blob: 31bcb0088889b55ab2422fa3e933baa49e2659ae [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -04002 * Copyright (C) 2015 Savoir-faire Linux Inc.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040024 * terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050025 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "ringmainwindow.h"
32
33#include <gtk/gtk.h>
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040034#include <glib/gi18n.h>
Stepan Salenikovicha3557452015-02-20 14:14:12 -050035#include "models/gtkqtreemodel.h"
36#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050037#include <call.h>
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050038#include <QtCore/QItemSelectionModel>
Stepan Salenikovich69771842015-02-24 18:11:45 -050039#include "incomingcallview.h"
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050040#include "currentcallview.h"
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050041#include <string.h>
Stepan Salenikovich15142182015-03-11 17:15:26 -040042#include <contactmethod.h>
Stepan Salenikovich7ff47962015-03-16 15:10:14 -040043#include <QtCore/QSortFilterProxyModel>
44#include "models/gtkqsortfiltertreemodel.h"
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040045#include "accountview.h"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040046#include <accountmodel.h>
47#include <audio/codecmodel.h>
Stepan Salenikovichbd029582015-03-24 11:00:56 -040048#include "dialogs.h"
Stepan Salenikovich0bd53492015-05-11 14:28:52 -040049#include "mediasettingsview.h"
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040050#include <video/previewmanager.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040051#include <personmodel.h>
52#include "utils/drawing.h"
53#include <memory>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040054#include <globalinstances.h>
55#include "native/pixbufmanipulator.h"
Stepan Salenikovich6f687072015-03-26 10:43:37 -040056#include "models/activeitemproxymodel.h"
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -040057#include <numbercompletionmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040058#include "utils/calling.h"
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -040059#include "frequentcontactsview.h"
Stepan Salenikovich9816a942015-04-22 17:49:16 -040060#include "contactsview.h"
Stepan Salenikovich9d294492015-05-14 16:34:24 -040061#include <categorizedcontactmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040062#include "historyview.h"
63#include "utils/models.h"
Stepan Salenikovichde896112015-05-11 16:46:33 -040064#include "generalsettingsview.h"
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -040065#include "callsview.h"
Stepan Salenikovich75a39172015-07-10 13:21:08 -040066#include "utils/accounts.h"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040067
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040068#define CALL_VIEW_NAME "calls"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040069#define CREATE_ACCOUNT_1_VIEW_NAME "create1"
70#define CREATE_ACCOUNT_2_VIEW_NAME "create2"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040071#define GENERAL_SETTINGS_VIEW_NAME "general"
72#define AUDIO_SETTINGS_VIEW_NAME "audio"
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -040073#define MEDIA_SETTINGS_VIEW_NAME "media"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040074#define ACCOUNT_SETTINGS_VIEW_NAME "accounts"
Stepan Salenikovich69771842015-02-24 18:11:45 -050075#define DEFAULT_VIEW_NAME "placeholder"
Stepan Salenikovich15142182015-03-11 17:15:26 -040076#define VIEW_CONTACTS "contacts"
77#define VIEW_HISTORY "history"
78#define VIEW_PRESENCE "presence"
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050079
80struct _RingMainWindow
81{
82 GtkApplicationWindow parent;
83};
84
85struct _RingMainWindowClass
86{
87 GtkApplicationWindowClass parent_class;
88};
89
90typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
91
92struct _RingMainWindowPrivate
93{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040094 GtkWidget *ring_menu;
95 GtkWidget *image_ring;
96 GtkWidget *ring_settings;
97 GtkWidget *image_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040098 GtkWidget *hbox_settings;
Stepan Salenikovich15142182015-03-11 17:15:26 -040099 GtkWidget *stack_contacts_history_presence;
100 GtkWidget *radiobutton_contacts;
101 GtkWidget *radiobutton_history;
102 GtkWidget *radiobutton_presence;
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400103 GtkWidget *combobox_history_sort;
104 GtkWidget *combobox_contacts_sort;
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400105 GtkWidget *vbox_left_pane;
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400106 GtkWidget *vbox_contacts;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -0500107 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500108 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400109 GtkWidget *vbox_call_view;
110 GtkWidget *stack_call_view;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500111 GtkWidget *button_placecall;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400112 GtkWidget *account_settings_view;
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400113 GtkWidget *media_settings_view;
Stepan Salenikovichde896112015-05-11 16:46:33 -0400114 GtkWidget *general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400115 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400116 GtkWidget *radiobutton_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400117 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400118 GtkWidget *radiobutton_account_settings;
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400119 GtkWidget *label_ring_id;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400120
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400121 QMetaObject::Connection selection_updated;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400122
123 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400124
125 /* account creation */
126 GtkWidget *account_creation_1;
127 GtkWidget *image_ring_logo;
128 GtkWidget *label_enter_alias;
129 GtkWidget *entry_alias;
130 GtkWidget *label_generating_account;
131 GtkWidget *spinner_generating_account;
132 GtkWidget *button_account_creation_next;
133 GtkWidget *account_creation_2;
134 GtkWidget *entry_hash;
135 GtkWidget *button_account_creation_done;
136
137 QMetaObject::Connection hash_updated;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400138
139 /* allocd qmodels */
140 ActiveItemProxyModel *q_contact_model;
141 QSortFilterProxyModel *q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400142 NumberCompletionModel *q_completion_model;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500143};
144
145G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
146
147#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
148
Stepan Salenikovich69771842015-02-24 18:11:45 -0500149static void
Stepan Salenikovich69771842015-02-24 18:11:45 -0500150call_selection_changed(GtkTreeSelection *selection, gpointer win)
151{
152 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
153
154 /* get the current visible stack child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400155 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500156
157 QModelIndex idx = get_index_from_selection(selection);
158 if (idx.isValid()) {
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400159 QVariant state = idx.data(static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500160 GtkWidget *new_call_view = NULL;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500161 char* new_call_view_name = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500162
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400163 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400164 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500165 case Call::LifeCycleState::INITIALIZATION:
166 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500167 new_call_view = incoming_call_view_new();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500168 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500169 /* use the pointer of the call as a unique name */
170 new_call_view_name = g_strdup_printf("%p_incoming", (void *)CallModel::instance()->getCall(idx));
171 break;
172 case Call::LifeCycleState::PROGRESS:
173 new_call_view = current_call_view_new();
174 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx);
175 /* use the pointer of the call as a unique name */
176 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx));
177 break;
178 case Call::LifeCycleState::COUNT__:
179 g_warning("LifeCycleState should never be COUNT");
180 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500181 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500182
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400183 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
184 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500185 g_free(new_call_view_name);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500186 } else {
187 /* nothing selected in the call model, so show the default screen */
188
189 /* TODO: replace stack paceholder view */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400190 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
191 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_call_view), DEFAULT_VIEW_NAME);
192 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500193
194 }
195
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500196 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400197 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500198 if (current_call_view != old_call_view && old_call_view != NULL) {
199 /* if the previous child was a call view, then remove it from
Stepan Salenikovich69771842015-02-24 18:11:45 -0500200 * the stack; removing it should destory it since there should not
201 * be any other references to it */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500202 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400203 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500204 }
205 }
206}
207
208static void
209call_state_changed(Call *call, gpointer win)
210{
211 g_debug("call state changed");
212 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
213
214 /* check if the call that changed state is the same as the selected call */
215 QModelIndex idx_selected = CallModel::instance()->selectionModel()->currentIndex();
216
217 if( idx_selected.isValid() && call == CallModel::instance()->getCall(idx_selected)) {
218 g_debug("selected call state changed");
219 /* check if we need to change the view */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400220 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500221 GtkWidget *new_call_view = NULL;
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400222 QVariant state = CallModel::instance()->data(idx_selected, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500223
224 /* check what the current state is vs what is displayed */
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400225 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400226 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500227 case Call::LifeCycleState::INITIALIZATION:
228 /* LifeCycleState cannot go backwards, so it should not be possible
229 * that the call is displayed as current (meaning that its in progress)
230 * but have the state 'initialization' */
231 if (IS_CURRENT_CALL_VIEW(old_call_view))
232 g_warning("call displayed as current, but is in state of initialization");
233 break;
234 case Call::LifeCycleState::PROGRESS:
235 if (IS_INCOMING_CALL_VIEW(old_call_view)) {
236 /* change from incoming to current */
237 new_call_view = current_call_view_new();
238 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx_selected);
239 /* use the pointer of the call as a unique name */
240 char* new_call_view_name = NULL;
241 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx_selected));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400242 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500243 g_free(new_call_view_name);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400244 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
245 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
246 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500247 }
248 break;
249 case Call::LifeCycleState::FINISHED:
250 /* do nothing, either call view is valid for this state */
251 break;
252 case Call::LifeCycleState::COUNT__:
253 g_warning("LifeCycleState should never be COUNT");
254 break;
255 }
256
257 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400258 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500259 if (current_call_view != old_call_view && old_call_view != NULL) {
260 /* if the previous child was a call view, then remove it from
261 * the stack; removing it should destory it since there should not
262 * be any other references to it */
263 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400264 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500265 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500266 }
267 }
268}
269
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500270static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500271search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
272{
273 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
274
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400275 const gchar *number_entered = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500276
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400277 if (number_entered && strlen(number_entered) > 0) {
278 /* detect Ring hash */
279 gboolean is_ring_hash = FALSE;
280 if (strlen(number_entered) == 40) {
281 is_ring_hash = TRUE;
282 /* must be 40 chars long and alphanumeric */
283 for (int i = 0; i < 40 && is_ring_hash; ++i) {
284 if (!g_ascii_isalnum(number_entered[i]))
285 is_ring_hash = FALSE;
286 }
287 }
288
289 QString number = QString{number_entered};
290
291 if (is_ring_hash)
292 number = "ring:" + number;
293
294 g_debug("dialing to number: %s", number.toUtf8().constData());
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400295
296 Call *call = priv->q_completion_model->call();
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500297 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);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400303
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400304 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
305 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
306 /* clear the entry */
307 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
308 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500309}
310
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400311static gboolean
312grab_focus_on_widget(GtkWidget *widget)
Stepan Salenikovich15142182015-03-11 17:15:26 -0400313{
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400314 gtk_widget_grab_focus(widget);
315 return G_SOURCE_REMOVE;
Stepan Salenikovich15142182015-03-11 17:15:26 -0400316}
317
318static void
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400319navbutton_contacts_toggled(G_GNUC_UNUSED GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovich15142182015-03-11 17:15:26 -0400320{
321 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
322 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
323
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400324 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence),
325 VIEW_CONTACTS);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400326
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400327 /* if the stack transition is animated, it will take a few miliseconds;
328 * we want to grab focus of the newly displayed treeview only after the
329 * animation, to make sure we scroll to the top of the new view */
330 g_timeout_add_full(G_PRIORITY_DEFAULT,
331 gtk_stack_get_transition_duration(GTK_STACK(priv->stack_contacts_history_presence)),
332 (GSourceFunc)grab_focus_on_widget,
333 gtk_stack_get_visible_child(GTK_STACK(priv->stack_contacts_history_presence)),
334 NULL);
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400335
336 /* show the correct sorting combobox */
337 gtk_widget_show(priv->combobox_contacts_sort);
338 gtk_widget_hide(priv->combobox_history_sort);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400339}
340
341static void
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400342navbutton_presence_toggled(G_GNUC_UNUSED GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovich15142182015-03-11 17:15:26 -0400343{
344 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
345 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
346
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400347 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence),
348 VIEW_PRESENCE);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400349
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400350 /* if the stack transition is animated, it will take a few miliseconds;
351 * we want to grab focus of the newly displayed treeview only after the
352 * animation, to make sure we scroll to the top of the new view */
353 g_timeout_add_full(G_PRIORITY_DEFAULT,
354 gtk_stack_get_transition_duration(GTK_STACK(priv->stack_contacts_history_presence)),
355 (GSourceFunc)grab_focus_on_widget,
356 gtk_stack_get_visible_child(GTK_STACK(priv->stack_contacts_history_presence)),
357 NULL);
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400358
359 /* show the correct sorting combobox */
360 gtk_widget_hide(priv->combobox_contacts_sort);
361 gtk_widget_hide(priv->combobox_history_sort);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400362}
363
364static void
365navbutton_history_toggled(G_GNUC_UNUSED GtkToggleButton *navbutton, RingMainWindow *win)
366{
367 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
368 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
369
370 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence),
371 VIEW_HISTORY);
372
373 /* if the stack transition is animated, it will take a few miliseconds;
374 * we want to grab focus of the newly displayed treeview only after the
375 * animation, to make sure we scroll to the top of the new view */
376 g_timeout_add_full(G_PRIORITY_DEFAULT,
377 gtk_stack_get_transition_duration(GTK_STACK(priv->stack_contacts_history_presence)),
378 (GSourceFunc)grab_focus_on_widget,
379 gtk_stack_get_visible_child(GTK_STACK(priv->stack_contacts_history_presence)),
380 NULL);
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400381
382 /* show the correct sorting combobox */
383 gtk_widget_hide(priv->combobox_contacts_sort);
384 gtk_widget_show(priv->combobox_history_sort);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400385}
386
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400387static gboolean
388save_accounts(GtkWidget *working_dialog)
389{
390 /* save changes to accounts */
391 AccountModel::instance()->save();
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400392
393 if (working_dialog)
394 gtk_widget_destroy(working_dialog);
395
396 return G_SOURCE_REMOVE;
397}
398
Stepan Salenikovich15142182015-03-11 17:15:26 -0400399static void
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400400settings_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
401{
402 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
403 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
404
405 /* toggle show settings */
406 priv->show_settings = !priv->show_settings;
407
408 /* check which view to show */
409 if (priv->show_settings) {
410 /* show settings */
411 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-ok-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
412
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400413 gtk_widget_show(priv->hbox_settings);
414
Stepan Salenikovich41118912015-05-01 11:25:46 -0400415 /* make sure to start preview if we're showing the video settings */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400416 if (priv->last_settings_view == priv->media_settings_view)
417 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich41118912015-05-01 11:25:46 -0400418
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400419 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400420 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400421 } else {
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400422 /* show working dialog in case save operation takes time */
423 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
424 gtk_window_present(GTK_WINDOW(working));
425
426 /* now save after the time it takes to transition back to the call view (400ms)
427 * the save doesn't happen before the "working" dialog is presented
428 * the timeout function should destroy the "working" dialog when done saving
429 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400430 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400431
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400432 /* show calls */
433 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
434
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400435 gtk_widget_hide(priv->hbox_settings);
436
437 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
438 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400439
440 /* make sure video preview is stopped, in case it was started */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400441 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400442 }
443}
444
445static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400446show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400447{
448 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
449 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
450
451 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400452 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400453 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400454 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400455 priv->last_settings_view = priv->media_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400456 } else {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400457 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400458 }
459}
460
461static void
462show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
463{
464 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
465 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
466
467 if (gtk_toggle_button_get_active(navbutton)) {
468 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
469 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
470 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400471 }
472}
473
Stepan Salenikovichde896112015-05-11 16:46:33 -0400474static void
475show_general_settings(GtkToggleButton *navbutton, RingMainWindow *win)
476{
477 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
478 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
479
480 if (gtk_toggle_button_get_active(navbutton)) {
481 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
482 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), GENERAL_SETTINGS_VIEW_NAME);
483 priv->last_settings_view = priv->general_settings_view;
484 }
485}
486
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400487static gboolean
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400488create_ring_account(RingMainWindow *win)
489{
490 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
491 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
492
493 /* create account and set UPnP enabled, as its not by default in the daemon */
494 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
495 Account *account = AccountModel::instance()->add(alias, Account::Protocol::RING);
Stepan Salenikovich75a39172015-07-10 13:21:08 -0400496 account->setDisplayName(alias); // set the display name to the same as the alias
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400497 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
Stepan Salenikovich64505672015-09-24 10:46:07 -0400570 /* show the settings button*/
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400571 gtk_widget_show(priv->ring_settings);
572
573}
574
575static void
576entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
577{
578 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
579
580 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
581 if (strlen(alias) > 0)
582 gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
583}
584
585static void
586show_account_creation(RingMainWindow *win)
587{
588 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
589
590 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
591 priv->account_creation_1,
592 CREATE_ACCOUNT_1_VIEW_NAME);
593
594 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
595 priv->account_creation_2,
596 CREATE_ACCOUNT_2_VIEW_NAME);
597
Stepan Salenikovich64505672015-09-24 10:46:07 -0400598 /* hide settings button until account creation is complete */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400599 gtk_widget_hide(priv->ring_settings);
600
601 /* set ring logo */
602 GError *error = NULL;
603 GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
604 -1, 75, TRUE, &error);
605 if (logo_ring == NULL) {
606 g_debug("Could not load logo: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400607 g_clear_error(&error);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400608 } else
609 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
610
611 /* style of alias and hash entry; give them a larger font */
612 gtk_widget_override_font(priv->entry_alias, pango_font_description_from_string("15"));
613 gtk_widget_override_font(priv->entry_hash, pango_font_description_from_string("monospace 15"));
614
615 /* connect signals */
616 g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
617 g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
618 g_signal_connect(priv->button_account_creation_done, "clicked", G_CALLBACK(account_creation_done_clicked), win);
619 g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
620 g_signal_connect_swapped(priv->entry_hash, "activate", G_CALLBACK(gtk_button_clicked), priv->button_account_creation_done);
621
622 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_1_VIEW_NAME);
623}
624
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400625static void
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400626show_ring_id(RingMainWindow *win, Account *account) {
627 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
628
629 /* display the ring id, if we found a ring account */
630 if (account) {
631 if (!account->username().isEmpty()) {
Stepan Salenikovich2ea27302015-03-31 10:28:46 -0400632 QString hash = "<span fgcolor=\"black\">" + account->username() + "</span>";
633 gtk_label_set_label(GTK_LABEL(priv->label_ring_id), hash.toUtf8().constData());
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400634 } else {
635 g_warning("got ring account, but Ring id is empty");
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400636 gtk_label_set_label(GTK_LABEL(priv->label_ring_id),
637 g_strdup_printf("<span fgcolor=\"gray\">%s</span>",
638 _("fetching RingID...")));
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400639 }
640 } else {
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400641 gtk_label_set_label(GTK_LABEL(priv->label_ring_id),
642 g_strdup_printf("<span fgcolor=\"gray\">%s</span>",
643 _("no Ring account")));
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400644 }
645
646}
647
648static void
649get_active_ring_account(RingMainWindow *win)
650{
651 /* get the users Ring account
652 * if multiple accounts exist, get the first one which is registered,
653 * if none, then the first one which is enabled,
654 * if none, then the first one in the list of ring accounts
655 */
656 Account *registered_account = NULL;
657 Account *enabled_account = NULL;
658 Account *ring_account = NULL;
659 int a_count = AccountModel::instance()->rowCount();
660 for (int i = 0; i < a_count && !registered_account; ++i) {
661 QModelIndex idx = AccountModel::instance()->index(i, 0);
662 Account *account = AccountModel::instance()->getAccountByModelIndex(idx);
663 if (account->protocol() == Account::Protocol::RING) {
664 /* got RING account, check if active */
665 if (account->isEnabled()) {
666 /* got enabled account, check if connected */
667 if (account->registrationState() == Account::RegistrationState::READY) {
668 /* got registered account, use this one */
669 registered_account = enabled_account = ring_account = account;
670 // g_debug("got registered account: %s", ring_account->alias().toUtf8().constData());
671 } else {
672 /* not registered, but enabled, use if its the first one */
673 if (!enabled_account) {
674 enabled_account = ring_account = account;
675 // g_debug("got enabled ring accout: %s", ring_account->alias().toUtf8().constData());
676 }
677 }
678 } else {
679 /* not enabled, but a Ring account, use if its the first one */
680 if (!ring_account) {
681 ring_account = account;
682 // g_debug("got ring account: %s", ring_account->alias().toUtf8().constData());
683 }
684 }
685 }
686 }
687
688 show_ring_id(win, ring_account);
689}
690
691static void
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400692search_entry_text_changed(GtkEditable *search_entry, RingMainWindow *win)
693{
694 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
695
696 /* get the text from the entry */
697 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
698
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400699 if (text && strlen(text) > 0) {
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400700 /* edit the the dialing call (or create a new one) */
Guillaume Roguez6a478f22015-05-20 09:54:08 -0400701 if (auto call = CallModel::instance()->dialingCall()) {
702 call->setDialNumber(text);
703 priv->q_completion_model->setCall(call);
704 }
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400705 } else {
Guillaume Roguez6a478f22015-05-20 09:54:08 -0400706 if (auto call = priv->q_completion_model->call()) {
707 if (call->lifeCycleState() == Call::LifeCycleState::CREATION)
708 call->performAction(Call::Action::REFUSE);
709 }
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400710 }
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400711}
712
713static gboolean
714completion_match_func(G_GNUC_UNUSED GtkEntryCompletion *completion,
715 G_GNUC_UNUSED const gchar *key,
716 G_GNUC_UNUSED GtkTreeIter *iter,
717 G_GNUC_UNUSED RingMainWindow *win)
718{
719 /* the model is updated by lrc and should only every contain matching entries
720 * so always return TRUE */
721 return TRUE;
722}
723
724static QModelIndex
725get_qidx_from_filter_model(GtkTreeModelFilter *filter_model,
726 GtkTreeIter *filter_iter)
727{
728 GtkTreeModel *child_model = gtk_tree_model_filter_get_model(filter_model);
729 GtkTreeIter child_iter;
730 gtk_tree_model_filter_convert_iter_to_child_iter(
731 GTK_TREE_MODEL_FILTER(filter_model),
732 &child_iter,
733 filter_iter);
734
735 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(child_model), &child_iter);
736}
737
738static void
739autocompletion_photo_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
740 GtkCellRenderer *cell,
741 GtkTreeModel *model,
742 GtkTreeIter *iter,
743 G_GNUC_UNUSED gpointer user_data)
744{
745 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
746 if (idx.isValid()) {
747 QVariant photo_var = idx.sibling(idx.row(), 1).data(Qt::DecorationRole);
748 if (photo_var.isValid()) {
749 std::shared_ptr<GdkPixbuf> photo = photo_var.value<std::shared_ptr<GdkPixbuf>>();
750 GdkPixbuf *scaled = gdk_pixbuf_scale_simple(photo.get(),
751 20, 20,
752 GDK_INTERP_BILINEAR);
753
754 g_object_set(G_OBJECT(cell), "pixbuf", scaled, NULL);
755 g_object_unref(scaled);
756 return;
757 }
758 }
759
760 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
761}
762
763static void
764autocompletion_name_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
765 GtkCellRenderer *cell,
766 GtkTreeModel *model,
767 GtkTreeIter *iter,
768 G_GNUC_UNUSED gpointer user_data)
769{
770 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
771 if (idx.isValid()) {
772 QVariant name = idx.sibling(idx.row(), 1).data(Qt::DisplayRole);
773 gchar *text = g_strdup_printf("<span font=\"12\" weight=\"bold\">%s</span>",
774 name.value<QString>().toUtf8().constData());
775
776 g_object_set(G_OBJECT(cell), "markup", text, NULL);
777 g_free(text);
778 return;
779 }
780
781 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
782}
783
784static void
785autocompletion_number_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
786 GtkCellRenderer *cell,
787 GtkTreeModel *model,
788 GtkTreeIter *iter,
789 G_GNUC_UNUSED gpointer user_data)
790{
791 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
792 if (idx.isValid()) {
793 QVariant uri = idx.data(Qt::DisplayRole);
794 gchar *text = g_strdup_printf("<span font=\"12\">%s</span>",
795 uri.value<QString>().toUtf8().constData());
796
797 g_object_set(G_OBJECT(cell), "markup", text, NULL);
798 g_free(text);
799 return;
800 }
801
802 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
803}
804
805static void
806autocompletion_account_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
807 GtkCellRenderer *cell,
808 GtkTreeModel *model,
809 GtkTreeIter *iter,
810 G_GNUC_UNUSED gpointer user_data)
811{
812 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
813 if (idx.isValid()) {
814 QVariant alias = idx.sibling(idx.row(), 2).data(Qt::DisplayRole);
815 gchar *text = g_strdup_printf("<span font=\"12\" color=\"gray\">%s</span>",
816 alias.value<QString>().toUtf8().constData());
817
818 g_object_set(G_OBJECT(cell), "markup", text, NULL);
819 g_free(text);
820 return;
821 }
822
823 g_object_set(G_OBJECT(cell), "markup", NULL, NULL);
824}
825
826static gboolean
827select_autocompletion(G_GNUC_UNUSED GtkEntryCompletion *widget,
828 GtkTreeModel *model,
829 GtkTreeIter *iter,
830 RingMainWindow *win)
831{
832 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
833
834 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
835 if (idx.isValid()) {
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400836 priv->q_completion_model->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
837 Call *call = priv->q_completion_model->call();
838 priv->q_completion_model->callSelectedNumber();
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400839
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400840 /* make this the currently selected call */
841 QModelIndex idx = CallModel::instance()->getIndex(call);
842 CallModel::instance()->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400843
844 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
845 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400846
847 /* clear the entry */
848 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400849 } else {
850 g_warning("autocompletion selection is not a valid index!");
851 }
852 return TRUE;
853}
854
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400855static gboolean
856dtmf_pressed(RingMainWindow *win,
857 GdkEventKey *event,
858 G_GNUC_UNUSED gpointer user_data)
859{
860 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
861
862 /* we want to react to digit key presses, as long as a GtkEntry is not the
863 * input focus
864 */
865 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
866 if (GTK_IS_ENTRY(focus))
867 return GDK_EVENT_PROPAGATE;
868
869 /* make sure that a call is selected*/
870 QItemSelectionModel *selection = CallModel::instance()->selectionModel();
871 QModelIndex idx = selection->currentIndex();
872 if (!idx.isValid())
873 return GDK_EVENT_PROPAGATE;
874
875 /* make sure that the selected call is in progress */
876 Call *call = CallModel::instance()->getCall(idx);
877 Call::LifeCycleState state = call->lifeCycleState();
878 if (state != Call::LifeCycleState::PROGRESS)
879 return GDK_EVENT_PROPAGATE;
880
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400881 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
882 * does not result in a 'c' being played.
883 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
884 if ( event->state
885 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
886 return GDK_EVENT_PROPAGATE;
887
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400888 /* pass the character that was entered to be played by the daemon;
889 * the daemon will filter out invalid DTMF characters */
890 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
891 QString val = QString::fromUcs4(&unicode_val, 1);
892 call->playDTMF(val);
893 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400894
895 /* always propogate the key, so we don't steal accelerators/shortcuts */
896 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400897}
898
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400899static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500900ring_main_window_init(RingMainWindow *win)
901{
902 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
903 gtk_widget_init_template(GTK_WIDGET(win));
904
905 /* set window icon */
906 GError *error = NULL;
907 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
908 if (icon == NULL) {
909 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400910 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500911 } else
912 gtk_window_set_icon(GTK_WINDOW(win), icon);
913
914 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400915 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
916 -1, 24, TRUE, &error);
917 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500918 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400919 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500920 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400921 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500922
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400923 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500924 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
925 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400926 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500927 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500928
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400929 /* settings icon */
930 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
931
932 /* connect settings button signal */
933 g_signal_connect(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
934
935 /* add the call view to the main stack */
936 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
937 priv->vbox_call_view,
938 CALL_VIEW_NAME);
939
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400940 if (has_ring_account()) {
941 /* user has ring account, so show the call view right away */
942 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
943 } else {
944 /* user has to create the ring account */
945 show_account_creation(win);
946 }
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400947
948 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400949 priv->account_settings_view = account_view_new();
950 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
951
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400952 priv->media_settings_view = media_settings_view_new();
953 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 -0400954
Stepan Salenikovichde896112015-05-11 16:46:33 -0400955 priv->general_settings_view = general_settings_view_new();
956 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->general_settings_view, GENERAL_SETTINGS_VIEW_NAME);
957
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400958 /* make the setting we will show first the active one */
Stepan Salenikovichde896112015-05-11 16:46:33 -0400959 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_general_settings), TRUE);
960 priv->last_settings_view = priv->general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400961
962 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400963 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400964 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400965 g_signal_connect(priv->radiobutton_general_settings, "toggled", G_CALLBACK(show_general_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400966
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400967 /* calls view */
968 GtkWidget *calls_view = calls_view_new();
969 gtk_box_pack_start(GTK_BOX(priv->vbox_left_pane),
970 calls_view,
971 FALSE, TRUE, 0);
Stepan Salenikovich64505672015-09-24 10:46:07 -0400972 gtk_box_reorder_child(GTK_BOX(priv->vbox_left_pane), calls_view, 1);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500973
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500974 /* connect to call state changes to update relevant view(s) */
975 QObject::connect(
976 CallModel::instance(),
977 &CallModel::callStateChanged,
978 [=](Call* call, G_GNUC_UNUSED Call::State previousState) {
979 call_state_changed(call, win);
980 }
981 );
982
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400983 /* frequent contacts view */
984 GtkWidget *frequent_view = frequent_contacts_view_new();
985 gtk_box_pack_start(GTK_BOX(priv->vbox_contacts),
986 frequent_view,
987 FALSE, TRUE, 0);
988 gtk_box_reorder_child(GTK_BOX(priv->vbox_contacts), frequent_view, 0);
989
990 /* contacts view */
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400991 GtkWidget *contacts_view = contacts_view_new();
Stepan Salenikovich15142182015-03-11 17:15:26 -0400992 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400993 contacts_view,
Stepan Salenikovich15142182015-03-11 17:15:26 -0400994 VIEW_CONTACTS);
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400995 gtk_combo_box_set_qmodel(GTK_COMBO_BOX(priv->combobox_contacts_sort),
996 (QAbstractItemModel *)CategorizedContactModel::SortedProxy::instance()->categoryModel(),
997 CategorizedContactModel::SortedProxy::instance()->categorySelectionModel());
Stepan Salenikovich15142182015-03-11 17:15:26 -0400998
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400999 /* history view */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001000 GtkWidget *history_view = history_view_new();
Stepan Salenikovich15142182015-03-11 17:15:26 -04001001 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001002 history_view,
Stepan Salenikovich15142182015-03-11 17:15:26 -04001003 VIEW_HISTORY);
Stepan Salenikovich9d294492015-05-14 16:34:24 -04001004 gtk_combo_box_set_qmodel(GTK_COMBO_BOX(priv->combobox_history_sort),
1005 (QAbstractItemModel *)CategorizedHistoryModel::SortedProxy::instance()->categoryModel(),
1006 CategorizedHistoryModel::SortedProxy::instance()->categorySelectionModel());
Stepan Salenikovich15142182015-03-11 17:15:26 -04001007
Stepan Salenikovich15142182015-03-11 17:15:26 -04001008 /* presence view/model */
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001009 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001010 GtkWidget *treeview_presence = gtk_tree_view_new();
1011 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_presence), FALSE);
1012 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_presence);
1013 gtk_widget_show_all(scrolled_window);
1014 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
1015 scrolled_window,
1016 VIEW_PRESENCE);
1017
1018 /* connect signals to change the contacts/history/presence stack view */
1019 g_signal_connect(priv->radiobutton_contacts, "toggled", G_CALLBACK(navbutton_contacts_toggled), win);
1020 g_signal_connect(priv->radiobutton_history, "toggled", G_CALLBACK(navbutton_history_toggled), win);
1021 g_signal_connect(priv->radiobutton_presence, "toggled", G_CALLBACK(navbutton_presence_toggled), win);
1022
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001023 /* TODO: make this linked to the client settings so that the last shown view is the same on startup */
1024 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_contacts), TRUE);
Stepan Salenikovich9d294492015-05-14 16:34:24 -04001025 gtk_widget_show(priv->combobox_contacts_sort);
1026 gtk_widget_hide(priv->combobox_history_sort);
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001027
Stepan Salenikovich69771842015-02-24 18:11:45 -05001028 /* TODO: replace stack paceholder view */
1029 GtkWidget *placeholder_view = gtk_tree_view_new();
1030 gtk_widget_show(placeholder_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001031 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), placeholder_view, DEFAULT_VIEW_NAME);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001032
1033 /* connect signals */
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001034 GtkTreeSelection *call_selection = calls_view_get_selection(CALLS_VIEW(calls_view));
Stepan Salenikovich69771842015-02-24 18:11:45 -05001035 g_signal_connect(call_selection, "changed", G_CALLBACK(call_selection_changed), win);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001036 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
1037 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
1038
1039 /* style of search entry */
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001040 gtk_widget_override_font(priv->search_entry, pango_font_description_from_string("15"));
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001041
1042 /* autocompletion */
1043 priv->q_completion_model = new NumberCompletionModel();
1044
1045 /* autocompletion renderers */
1046 GtkCellArea *completion_area = gtk_cell_area_box_new();
1047
1048 /* photo renderer */
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001049 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001050 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1051 renderer,
1052 TRUE, /* expand */
1053 TRUE, /* align */
1054 TRUE); /* fixed size */
1055
1056 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1057 renderer,
1058 (GtkCellLayoutDataFunc)autocompletion_photo_render,
1059 NULL, NULL);
1060
1061 /* name renderer */
1062 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -04001063 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001064 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1065 renderer,
1066 TRUE, /* expand */
1067 TRUE, /* align */
1068 TRUE); /* fixed size */
1069
1070 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1071 renderer,
1072 (GtkCellLayoutDataFunc)autocompletion_name_render,
1073 NULL, NULL);
1074
1075 /* number renderer */
1076 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -04001077 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001078 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1079 renderer,
1080 TRUE, /* expand */
1081 TRUE, /* align */
1082 TRUE); /* fixed size */
1083
1084 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1085 renderer,
1086 (GtkCellLayoutDataFunc)autocompletion_number_render,
1087 NULL, NULL);
1088 /* account renderer */
1089 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -04001090 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001091 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1092 renderer,
1093 TRUE, /* expand */
1094 TRUE, /* align */
1095 TRUE); /* fixed size */
1096
1097 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1098 renderer,
1099 (GtkCellLayoutDataFunc)autocompletion_account_render,
1100 NULL, NULL);
1101
1102 GtkEntryCompletion *entry_completion = gtk_entry_completion_new_with_area(completion_area);
1103
1104 GtkQTreeModel *completion_model = gtk_q_tree_model_new(
1105 (QAbstractItemModel *)priv->q_completion_model,
1106 1,
1107 Qt::DisplayRole, G_TYPE_STRING);
1108
1109 gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(completion_model));
1110
1111 gtk_entry_set_completion(GTK_ENTRY(priv->search_entry), entry_completion);
1112 gtk_entry_completion_set_match_func(
1113 entry_completion,
1114 (GtkEntryCompletionMatchFunc) completion_match_func,
1115 NULL,
1116 NULL);
1117
1118 /* connect signal to when text is entered in the entry */
1119 g_signal_connect(priv->search_entry, "changed", G_CALLBACK(search_entry_text_changed), win);
1120 g_signal_connect(entry_completion, "match-selected", G_CALLBACK(select_autocompletion), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001121
1122 /* connect to incoming call and focus */
1123 QObject::connect(
1124 CallModel::instance(),
1125 &CallModel::incomingCall,
1126 [=](Call* call) {
1127 CallModel::instance()->selectionModel()->setCurrentIndex(
1128 CallModel::instance()->getIndex(call), QItemSelectionModel::ClearAndSelect);
1129 }
1130 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001131
1132 /* display ring id by first getting the active ring account */
Stepan Salenikovich2ea27302015-03-31 10:28:46 -04001133 gtk_widget_override_font(priv->label_ring_id, pango_font_description_from_string("monospace"));
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001134 get_active_ring_account(win);
1135 QObject::connect(
1136 AccountModel::instance(),
1137 &AccountModel::dataChanged,
1138 [=] () {
1139 /* check if the active ring account has changed,
1140 * eg: if it was deleted */
1141 get_active_ring_account(win);
1142 }
1143 );
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001144
1145 /* react to digit key press events */
1146 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001147}
1148
1149static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001150ring_main_window_dispose(GObject *object)
1151{
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001152 RingMainWindow *self = RING_MAIN_WINDOW(object);
1153 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1154
1155 QObject::disconnect(priv->selection_updated);
1156
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001157 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1158}
1159
1160static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001161ring_main_window_finalize(GObject *object)
1162{
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001163 RingMainWindow *self = RING_MAIN_WINDOW(object);
1164 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1165
1166 delete priv->q_contact_model;
1167 delete priv->q_history_model;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001168 delete priv->q_completion_model;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001169
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001170 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001171}
1172
1173static void
1174ring_main_window_class_init(RingMainWindowClass *klass)
1175{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001176 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001177 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001178
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001179 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1180 "/cx/ring/RingGnome/ringmainwindow.ui");
1181
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001182 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_left_pane);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -04001183 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_contacts);
Stepan Salenikovich15142182015-03-11 17:15:26 -04001184 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_contacts_history_presence);
1185 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_contacts);
1186 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_history);
1187 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_presence);
Stepan Salenikovich9d294492015-05-14 16:34:24 -04001188 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, combobox_history_sort);
1189 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, combobox_contacts_sort);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001190 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1191 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1192 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1193 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001194 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001195 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001196 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001197 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
1198 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_call_view);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001199 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001200 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001201 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001202 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Stepan Salenikovich2ea27302015-03-31 10:28:46 -04001203 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_ring_id);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001204
1205 /* account creation */
1206 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_1);
1207 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
1208 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_enter_alias);
1209 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
1210 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
1211 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
1212 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
1213
1214 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_2);
1215 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_hash);
1216 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_done);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001217}
1218
1219GtkWidget *
1220ring_main_window_new (GtkApplication *app)
1221{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001222 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1223
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001224 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001225}