blob: d7961161e494bf8fd2e874c2abeccf6e46b3d4f5 [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 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.
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050018 */
19
20#include "ringmainwindow.h"
21
22#include <gtk/gtk.h>
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040023#include <glib/gi18n.h>
Stepan Salenikovicha3557452015-02-20 14:14:12 -050024#include "models/gtkqtreemodel.h"
25#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050026#include <call.h>
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050027#include <QtCore/QItemSelectionModel>
Stepan Salenikovich69771842015-02-24 18:11:45 -050028#include "incomingcallview.h"
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050029#include "currentcallview.h"
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050030#include <string.h>
Stepan Salenikovich15142182015-03-11 17:15:26 -040031#include <contactmethod.h>
Stepan Salenikovich7ff47962015-03-16 15:10:14 -040032#include <QtCore/QSortFilterProxyModel>
33#include "models/gtkqsortfiltertreemodel.h"
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040034#include "accountview.h"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040035#include <accountmodel.h>
Edric Milaret58e71072016-03-21 12:16:37 -040036#include <codecmodel.h>
Stepan Salenikovichbd029582015-03-24 11:00:56 -040037#include "dialogs.h"
Stepan Salenikovich0bd53492015-05-11 14:28:52 -040038#include "mediasettingsview.h"
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040039#include <video/previewmanager.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040040#include <personmodel.h>
41#include "utils/drawing.h"
42#include <memory>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040043#include <globalinstances.h>
44#include "native/pixbufmanipulator.h"
Stepan Salenikovich6f687072015-03-26 10:43:37 -040045#include "models/activeitemproxymodel.h"
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -040046#include <numbercompletionmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040047#include "utils/calling.h"
48#include "contactsview.h"
Stepan Salenikovich9d294492015-05-14 16:34:24 -040049#include <categorizedcontactmodel.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040050#include "historyview.h"
51#include "utils/models.h"
Stepan Salenikovichde896112015-05-11 16:46:33 -040052#include "generalsettingsview.h"
Stepan Salenikovich75a39172015-07-10 13:21:08 -040053#include "utils/accounts.h"
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040054#include "ringwelcomeview.h"
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -040055#include "recentcontactsview.h"
Stepan Salenikovichc1323422016-01-06 10:54:44 -050056#include <recentmodel.h>
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050057#include "chatview.h"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040058
Nicolas Jagerb413b302016-05-06 11:41:32 -040059/*TODO : sorting headers */
60/* client */
61#include "avatarmanipulation.h"
62/* lrc */
63#include <profilemodel.h>
64#include <profile.h>
65
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040066static constexpr const char* CALL_VIEW_NAME = "calls";
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -050067static constexpr const char* CREATE_ACCOUNT_VIEW_NAME = "wizard";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040068static constexpr const char* GENERAL_SETTINGS_VIEW_NAME = "general";
69static constexpr const char* AUDIO_SETTINGS_VIEW_NAME = "audio";
70static constexpr const char* MEDIA_SETTINGS_VIEW_NAME = "media";
71static constexpr const char* ACCOUNT_SETTINGS_VIEW_NAME = "accounts";
Stepan Salenikovich2cde7612015-09-25 10:44:01 -040072static constexpr const char* DEFAULT_VIEW_NAME = "welcome";
Stepan Salenikovich9ffad5e2015-09-25 13:16:50 -040073static constexpr const char* VIEW_CONTACTS = "contacts";
74static constexpr const char* VIEW_HISTORY = "history";
75static constexpr const char* VIEW_PRESENCE = "presence";
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050076
77struct _RingMainWindow
78{
79 GtkApplicationWindow parent;
80};
81
82struct _RingMainWindowClass
83{
84 GtkApplicationWindowClass parent_class;
85};
86
87typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
88
89struct _RingMainWindowPrivate
90{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040091 GtkWidget *ring_menu;
92 GtkWidget *image_ring;
93 GtkWidget *ring_settings;
94 GtkWidget *image_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040095 GtkWidget *hbox_settings;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -040096 GtkWidget *scrolled_window_smartview;
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -040097 GtkWidget *scrolled_window_contacts;
98 GtkWidget *scrolled_window_history;
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -040099 GtkWidget *vbox_left_pane;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -0500100 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500101 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400102 GtkWidget *vbox_call_view;
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400103 GtkWidget *frame_call;
104 GtkWidget *welcome_view;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500105 GtkWidget *button_placecall;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400106 GtkWidget *account_settings_view;
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400107 GtkWidget *media_settings_view;
Stepan Salenikovichde896112015-05-11 16:46:33 -0400108 GtkWidget *general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400109 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400110 GtkWidget *radiobutton_general_settings;
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400111 GtkWidget *radiobutton_media_settings;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400112 GtkWidget *radiobutton_account_settings;
Stepan Salenikovich5eab3032015-03-28 10:50:51 -0400113
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400114 QMetaObject::Connection selection_updated;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400115
116 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400117
118 /* account creation */
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500119 GtkWidget *account_creation;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400120 GtkWidget *image_ring_logo;
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500121 GtkWidget *vbox_account_creation_entry;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400122 GtkWidget *entry_alias;
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500123 GtkWidget *label_default_name;
124 GtkWidget *label_paceholder;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400125 GtkWidget *label_generating_account;
126 GtkWidget *spinner_generating_account;
127 GtkWidget *button_account_creation_next;
Nicolas Jagerb413b302016-05-06 11:41:32 -0400128 GtkWidget *box_avatarselection;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400129
130 QMetaObject::Connection hash_updated;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400131
132 /* allocd qmodels */
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400133 NumberCompletionModel *q_completion_model;
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500134
135 /* fullscreen */
136 gboolean is_fullscreen;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500137};
138
139G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
140
141#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
142
Stepan Salenikovich69771842015-02-24 18:11:45 -0500143static void
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500144enter_full_screen(RingMainWindow *self)
145{
146 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
147 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
148
149 if (!priv->is_fullscreen) {
150 gtk_widget_hide(priv->vbox_left_pane);
151 gtk_window_fullscreen(GTK_WINDOW(self));
152 priv->is_fullscreen = TRUE;
153 }
154}
155
156static void
157leave_full_screen(RingMainWindow *self)
158{
159 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
160 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
161
162 if (priv->is_fullscreen) {
163 gtk_widget_show(priv->vbox_left_pane);
164 gtk_window_unfullscreen(GTK_WINDOW(self));
165 priv->is_fullscreen = FALSE;
166 }
167}
168
169static void
Stepan Salenikovich5e431cf2015-12-24 14:09:01 -0500170video_double_clicked(G_GNUC_UNUSED CurrentCallView *view, RingMainWindow *self)
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500171{
172 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
173 auto priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
174
175 if (priv->is_fullscreen) {
176 leave_full_screen(self);
177 } else {
178 enter_full_screen(self);
179 }
180}
181
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400182static void
183hide_view_clicked(G_GNUC_UNUSED GtkWidget *view, RingMainWindow *self)
184{
185 g_return_if_fail(IS_RING_MAIN_WINDOW(self));
186
187 /* clear selection */
188 RecentModel::instance().selectionModel()->clear();
189}
190
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500191/**
192 * This takes the RecentModel index as the argument and displays the corresponding view:
193 * - incoming call view
194 * - current call view
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500195 * - chat view
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500196 * - welcome view (if no index is selected)
197 */
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500198static void
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500199selection_changed(const QModelIndex& recent_idx, RingMainWindow *win)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500200{
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500201 // g_debug("selection changed");
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400202 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500203 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
204
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500205 /* if we're showing the settings, then nothing needs to be done as the call
206 view is not shown */
207 if (priv->show_settings) return;
208
Stepan Salenikovich69771842015-02-24 18:11:45 -0500209 /* get the current visible stack child */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400210 GtkWidget *old_call_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500211
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500212 /* make sure we leave full screen, since the call selection is changing */
213 leave_full_screen(win);
214
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500215 /* check which object type is selected */
216 auto type = recent_idx.data(static_cast<int>(Ring::Role::ObjectType)).value<Ring::ObjectType>();
217 auto object = recent_idx.data(static_cast<int>(Ring::Role::Object));
218 /* try to get the call model index, in case its a call, since we're still using the CallModel as well */
219 auto call_idx = CallModel::instance().getIndex(RecentModel::instance().getActiveCall(recent_idx));
220
221 /* we prioritize showing the call view */
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500222 if (call_idx.isValid()) {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500223 /* show the call view */
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500224 QVariant state = call_idx.data(static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500225 GtkWidget *new_call_view = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500226
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400227 switch(state.value<Call::LifeCycleState>()) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400228 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500229 case Call::LifeCycleState::INITIALIZATION:
230 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500231 new_call_view = incoming_call_view_new();
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500232 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), call_idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500233 break;
234 case Call::LifeCycleState::PROGRESS:
235 new_call_view = current_call_view_new();
Stepan Salenikovich5e431cf2015-12-24 14:09:01 -0500236 g_signal_connect(new_call_view, "video-double-clicked", G_CALLBACK(video_double_clicked), win);
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500237 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), call_idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500238 break;
239 case Call::LifeCycleState::COUNT__:
240 g_warning("LifeCycleState should never be COUNT");
241 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500242 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500243
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400244 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
245 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_call_view);
246 gtk_widget_show(new_call_view);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500247 } else if (type == Ring::ObjectType::Person && object.isValid()) {
248 /* show chat view constructed from Person object */
249 auto new_chat_view = chat_view_new_person(object.value<Person *>());
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400250 g_signal_connect(new_chat_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500251 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
252 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_chat_view);
253 gtk_widget_show(new_chat_view);
254 } else if (type == Ring::ObjectType::ContactMethod && object.isValid()) {
255 /* show chat view constructed from CM */
256 auto new_chat_view = chat_view_new_cm(object.value<ContactMethod *>());
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400257 g_signal_connect(new_chat_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500258 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
259 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_chat_view);
260 gtk_widget_show(new_chat_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500261 } else {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500262 /* nothing selected that we can display, show the welcome view */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400263 gtk_container_remove(GTK_CONTAINER(priv->frame_call), old_call_view);
264 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500265 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500266}
267
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500268static gboolean
269selected_item_changed(RingMainWindow *win)
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500270{
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500271 // g_debug("item changed");
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500272 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
273
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500274 /* if we're showing the settings, then nothing needs to be done as the call
275 view is not shown */
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500276 if (priv->show_settings) return G_SOURCE_REMOVE;
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500277
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500278 auto idx_selected = RecentModel::instance().selectionModel()->currentIndex();
279
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500280 /* we prioritize showing the call view; but if the call is over we go back to showing the chat view */
281 if(auto call = RecentModel::instance().getActiveCall(idx_selected)) {
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500282 /* check if we need to change the view */
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500283 auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
Stepan Salenikovichfe87a8d2016-01-15 10:39:09 -0500284 auto state = call->lifeCycleState();
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500285
286 /* check what the current state is vs what is displayed */
Stepan Salenikovichfe87a8d2016-01-15 10:39:09 -0500287 switch(state) {
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400288 case Call::LifeCycleState::CREATION:
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500289 case Call::LifeCycleState::FINISHED:
290 /* go back to incoming call view;
291 * it will show that the call failed and offer to hang it up */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500292 case Call::LifeCycleState::INITIALIZATION:
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500293 {
294 /* show the incoming call view */
295 if (!IS_INCOMING_CALL_VIEW(current_view)) {
296 auto new_view = incoming_call_view_new();
297 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_view), CallModel::instance().getIndex(call));
298 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
299 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
300 gtk_widget_show(new_view);
301 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500302 }
303 break;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500304 case Call::LifeCycleState::PROGRESS:
305 {
306 /* show the current call view */
307 if (!IS_CURRENT_CALL_VIEW(current_view)) {
308 auto new_view = current_call_view_new();
309 g_signal_connect(new_view, "video-double-clicked", G_CALLBACK(video_double_clicked), win);
310 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_view), CallModel::instance().getIndex(call));
311 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
312 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
313 gtk_widget_show(new_view);
314 }
315 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500316 break;
317 case Call::LifeCycleState::COUNT__:
318 g_warning("LifeCycleState should never be COUNT");
319 break;
320 }
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500321 } else if (idx_selected.isValid()) {
322 /* otherwise, the call is over and is already removed from the RecentModel */
323 auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call));
324 leave_full_screen(win);
325
326 /* show the chat view */
327 if (!IS_CHAT_VIEW(current_view)) {
328 auto type = idx_selected.data(static_cast<int>(Ring::Role::ObjectType)).value<Ring::ObjectType>();
329 auto object = idx_selected.data(static_cast<int>(Ring::Role::Object));
330 if (type == Ring::ObjectType::Person && object.isValid()) {
331 /* show chat view constructed from Person object */
332 auto new_view = chat_view_new_person(object.value<Person *>());
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400333 g_signal_connect(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500334 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
335 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
336 gtk_widget_show(new_view);
337 } else if (type == Ring::ObjectType::ContactMethod && object.isValid()) {
338 /* show chat view constructed from CM */
339 auto new_view = chat_view_new_cm(object.value<ContactMethod *>());
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400340 g_signal_connect(new_view, "hide-view-clicked", G_CALLBACK(hide_view_clicked), win);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500341 gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view);
342 gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view);
343 gtk_widget_show(new_view);
344 }
345 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500346 }
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500347
348 return G_SOURCE_REMOVE;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500349}
350
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500351static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500352search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
353{
354 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
355
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400356 const gchar *number_entered = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500357
Stepan Salenikovich895a70b2015-03-27 16:02:27 -0400358 if (number_entered && strlen(number_entered) > 0) {
359 /* detect Ring hash */
360 gboolean is_ring_hash = FALSE;
361 if (strlen(number_entered) == 40) {
362 is_ring_hash = TRUE;
363 /* must be 40 chars long and alphanumeric */
364 for (int i = 0; i < 40 && is_ring_hash; ++i) {
365 if (!g_ascii_isalnum(number_entered[i]))
366 is_ring_hash = FALSE;
367 }
368 }
369
370 QString number = QString{number_entered};
371
372 if (is_ring_hash)
373 number = "ring:" + number;
374
375 g_debug("dialing to number: %s", number.toUtf8().constData());
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400376
377 Call *call = priv->q_completion_model->call();
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500378 call->setDialNumber(number);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500379 call->performAction(Call::Action::ACCEPT);
380
381 /* make this the currently selected call */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400382 QModelIndex idx = CallModel::instance().getIndex(call);
383 CallModel::instance().selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400384
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400385 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
386 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
387 /* clear the entry */
388 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
389 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500390}
391
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400392static gboolean
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400393save_accounts(GtkWidget *working_dialog)
394{
395 /* save changes to accounts */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400396 AccountModel::instance().save();
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400397
398 if (working_dialog)
399 gtk_widget_destroy(working_dialog);
400
401 return G_SOURCE_REMOVE;
402}
403
Stepan Salenikovich15142182015-03-11 17:15:26 -0400404static void
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400405settings_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
406{
407 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
408 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
409
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400410 /* check which view to show */
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500411 if (!priv->show_settings) {
412 /* show the settings */
413
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500414 /* make sure we are not showing a call view so we don't have more than one clutter stage at a time */
415 selection_changed(QModelIndex(), win);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500416
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400417 /* show settings */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400418 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-ok-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400419
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400420 gtk_widget_show(priv->hbox_settings);
421
Stepan Salenikovich41118912015-05-01 11:25:46 -0400422 /* make sure to start preview if we're showing the video settings */
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400423 if (priv->last_settings_view == priv->media_settings_view)
424 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovich41118912015-05-01 11:25:46 -0400425
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400426 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400427 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500428
429 priv->show_settings = TRUE;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400430 } else {
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500431 /* hide the settings */
432 priv->show_settings = FALSE;
433
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400434 /* show working dialog in case save operation takes time */
435 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
436 gtk_window_present(GTK_WINDOW(working));
437
438 /* now save after the time it takes to transition back to the call view (400ms)
439 * the save doesn't happen before the "working" dialog is presented
440 * the timeout function should destroy the "working" dialog when done saving
441 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400442 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400443
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400444 /* show calls */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400445 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400446
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400447 gtk_widget_hide(priv->hbox_settings);
448
Stepan Salenikovichbd4b3772015-12-13 23:28:06 -0500449 /* make sure video preview is stopped, in case it was started */
450 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
451
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400452 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
453 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400454
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500455 /* show the view which was selected previously */
456 selection_changed(RecentModel::instance().selectionModel()->currentIndex(), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400457 }
458}
459
460static void
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400461show_media_settings(GtkToggleButton *navbutton, RingMainWindow *win)
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400462{
463 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
464 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
465
466 if (gtk_toggle_button_get_active(navbutton)) {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400467 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), TRUE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400468 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400469 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), MEDIA_SETTINGS_VIEW_NAME);
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400470 priv->last_settings_view = priv->media_settings_view;
Stepan Salenikovich41118912015-05-01 11:25:46 -0400471 } else {
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400472 media_settings_view_show_preview(MEDIA_SETTINGS_VIEW(priv->media_settings_view), FALSE);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400473 }
474}
475
476static void
477show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
478{
479 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
480 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
481
482 if (gtk_toggle_button_get_active(navbutton)) {
483 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
484 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
485 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400486 }
487}
488
Stepan Salenikovichde896112015-05-11 16:46:33 -0400489static void
490show_general_settings(GtkToggleButton *navbutton, RingMainWindow *win)
491{
492 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
493 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
494
495 if (gtk_toggle_button_get_active(navbutton)) {
496 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
497 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), GENERAL_SETTINGS_VIEW_NAME);
498 priv->last_settings_view = priv->general_settings_view;
499 }
500}
501
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400502static gboolean
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400503create_ring_account(RingMainWindow *win)
504{
505 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
506 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
507
508 /* create account and set UPnP enabled, as its not by default in the daemon */
509 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500510 Account *account = nullptr;
Nicolas Jagerb413b302016-05-06 11:41:32 -0400511
512 /* get profile (if so) */
513 auto profile = ProfileModel::instance().selectedProfile();
514
515 if (profile) {
516 if (alias && strlen(alias) > 0) {
517 account = AccountModel::instance().add(alias, Account::Protocol::RING);
518 profile->person()->setFormattedName(alias);
519 } else {
520 auto unknown_alias = C_("The default username / account alias, if none is set by the user", "Unknown");
521 account = AccountModel::instance().add(unknown_alias, Account::Protocol::RING);
522 profile->person()->setFormattedName(unknown_alias);
523 }
524 }
525
Stepan Salenikovich75a39172015-07-10 13:21:08 -0400526 account->setDisplayName(alias); // set the display name to the same as the alias
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400527 account->setUpnpEnabled(TRUE);
528
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500529 /* wait for hash to be generated to show the main view */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400530 priv->hash_updated = QObject::connect(
531 account,
532 &Account::changed,
533 [=] (Account *a) {
534 QString hash = a->username();
535 if (!hash.isEmpty()) {
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500536 /* show the call view */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400537 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500538 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400539
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500540 /* show the settings button*/
541 gtk_widget_show(priv->ring_settings);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400542 }
543 }
544 );
545
546 account->performAction(Account::EditAction::SAVE);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400547 profile->save();
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400548
549 return G_SOURCE_REMOVE;
550}
551
552static void
553alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
554{
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500555 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400556 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
557
558 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500559 if (!alias || strlen(alias) == 0) {
560 gtk_widget_show(priv->label_default_name);
561 gtk_widget_hide(priv->label_paceholder);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400562 } else {
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500563 gtk_widget_hide(priv->label_default_name);
564 gtk_widget_show(priv->label_paceholder);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400565 }
566}
567
568static void
569account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
570{
571 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
572
573 /* show/hide relevant widgets */
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500574 gtk_widget_hide(priv->vbox_account_creation_entry);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400575 gtk_widget_hide(priv->button_account_creation_next);
576 gtk_widget_show(priv->label_generating_account);
577 gtk_widget_show(priv->spinner_generating_account);
578
579 /* now create account after a short timeout so that the the save doesn't
580 * happen freeze the client before the widget changes happen;
581 * the timeout function should then display the next step in account creation
582 */
583 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
584}
585
586static void
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400587entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
588{
589 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
590
591 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
592 if (strlen(alias) > 0)
593 gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
594}
595
596static void
597show_account_creation(RingMainWindow *win)
598{
599 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
600
601 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500602 priv->account_creation,
603 CREATE_ACCOUNT_VIEW_NAME);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400604
Stepan Salenikovich64505672015-09-24 10:46:07 -0400605 /* hide settings button until account creation is complete */
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400606 gtk_widget_hide(priv->ring_settings);
607
608 /* set ring logo */
609 GError *error = NULL;
610 GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500611 -1, 50, TRUE, &error);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400612 if (logo_ring == NULL) {
613 g_debug("Could not load logo: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400614 g_clear_error(&error);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400615 } else
616 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
617
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500618 /* use the real name / username of the logged in user as the default */
Nicolas Jager5e604d52016-05-13 11:09:12 -0400619 const char* real_name = g_get_real_name();
620 const char* user_name = g_get_user_name();
621 g_debug("real_name = %s",real_name);
622 g_debug("user_name = %s",user_name);
623
624 /* check first if the real name was determined */
625 if (g_strcmp0 (real_name,"Unknown") != 0)
626 gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), real_name);
627 else
628 gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), user_name);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400629
Nicolas Jagerb413b302016-05-06 11:41:32 -0400630 /* avatar manipulation widget */
631 gtk_box_pack_start(GTK_BOX(priv->box_avatarselection), avatar_manipulation_new_from_wizard(), true, true, 0);
632
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400633 /* connect signals */
634 g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
635 g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400636 g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400637
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -0500638 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_VIEW_NAME);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400639}
640
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400641static void
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400642search_entry_text_changed(GtkEditable *search_entry, RingMainWindow *win)
643{
644 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
645
646 /* get the text from the entry */
647 const gchar *text = gtk_entry_get_text(GTK_ENTRY(search_entry));
648
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400649 if (text && strlen(text) > 0) {
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400650 /* edit the the dialing call (or create a new one) */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400651 if (auto call = CallModel::instance().dialingCall()) {
Guillaume Roguez6a478f22015-05-20 09:54:08 -0400652 call->setDialNumber(text);
653 priv->q_completion_model->setCall(call);
654 }
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400655 } else {
Guillaume Roguez6a478f22015-05-20 09:54:08 -0400656 if (auto call = priv->q_completion_model->call()) {
657 if (call->lifeCycleState() == Call::LifeCycleState::CREATION)
658 call->performAction(Call::Action::REFUSE);
659 }
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400660 }
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400661}
662
663static gboolean
664completion_match_func(G_GNUC_UNUSED GtkEntryCompletion *completion,
665 G_GNUC_UNUSED const gchar *key,
666 G_GNUC_UNUSED GtkTreeIter *iter,
667 G_GNUC_UNUSED RingMainWindow *win)
668{
669 /* the model is updated by lrc and should only every contain matching entries
670 * so always return TRUE */
671 return TRUE;
672}
673
674static QModelIndex
675get_qidx_from_filter_model(GtkTreeModelFilter *filter_model,
676 GtkTreeIter *filter_iter)
677{
678 GtkTreeModel *child_model = gtk_tree_model_filter_get_model(filter_model);
679 GtkTreeIter child_iter;
680 gtk_tree_model_filter_convert_iter_to_child_iter(
681 GTK_TREE_MODEL_FILTER(filter_model),
682 &child_iter,
683 filter_iter);
684
685 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(child_model), &child_iter);
686}
687
688static void
689autocompletion_photo_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
690 GtkCellRenderer *cell,
691 GtkTreeModel *model,
692 GtkTreeIter *iter,
693 G_GNUC_UNUSED gpointer user_data)
694{
695 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
696 if (idx.isValid()) {
697 QVariant photo_var = idx.sibling(idx.row(), 1).data(Qt::DecorationRole);
698 if (photo_var.isValid()) {
699 std::shared_ptr<GdkPixbuf> photo = photo_var.value<std::shared_ptr<GdkPixbuf>>();
700 GdkPixbuf *scaled = gdk_pixbuf_scale_simple(photo.get(),
701 20, 20,
702 GDK_INTERP_BILINEAR);
703
704 g_object_set(G_OBJECT(cell), "pixbuf", scaled, NULL);
705 g_object_unref(scaled);
706 return;
707 }
708 }
709
710 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
711}
712
713static void
714autocompletion_name_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
715 GtkCellRenderer *cell,
716 GtkTreeModel *model,
717 GtkTreeIter *iter,
718 G_GNUC_UNUSED gpointer user_data)
719{
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400720 gchar *text = nullptr;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400721 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
722 if (idx.isValid()) {
723 QVariant name = idx.sibling(idx.row(), 1).data(Qt::DisplayRole);
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400724 text = g_markup_printf_escaped("<span weight=\"bold\">%s</span>",
725 name.value<QString>().toUtf8().constData());
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400726 }
727
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400728 g_object_set(G_OBJECT(cell), "markup", text, NULL);
729 g_free(text);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400730}
731
732static void
733autocompletion_number_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
734 GtkCellRenderer *cell,
735 GtkTreeModel *model,
736 GtkTreeIter *iter,
737 G_GNUC_UNUSED gpointer user_data)
738{
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400739 gchar *text = nullptr;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400740 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
741 if (idx.isValid()) {
742 QVariant uri = idx.data(Qt::DisplayRole);
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400743 text = g_markup_escape_text(uri.value<QString>().toUtf8().constData(), -1);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400744 }
745
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400746 g_object_set(G_OBJECT(cell), "markup", text, NULL);
747 g_free(text);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400748}
749
750static void
751autocompletion_account_render(G_GNUC_UNUSED GtkCellLayout *cell_layout,
752 GtkCellRenderer *cell,
753 GtkTreeModel *model,
754 GtkTreeIter *iter,
755 G_GNUC_UNUSED gpointer user_data)
756{
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400757 gchar *text = nullptr;
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400758 QModelIndex idx = get_qidx_from_filter_model(GTK_TREE_MODEL_FILTER(model), iter);
759 if (idx.isValid()) {
760 QVariant alias = idx.sibling(idx.row(), 2).data(Qt::DisplayRole);
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400761 text = g_markup_printf_escaped("<span color=\"gray\">%s</span>",
762 alias.value<QString>().toUtf8().constData());
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400763 }
764
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400765 g_object_set(G_OBJECT(cell), "markup", text, NULL);
766 g_free(text);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400767}
768
769static gboolean
770select_autocompletion(G_GNUC_UNUSED GtkEntryCompletion *widget,
771 GtkTreeModel *model,
772 GtkTreeIter *iter,
773 RingMainWindow *win)
774{
775 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
776
777 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), iter);
778 if (idx.isValid()) {
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400779 priv->q_completion_model->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
780 Call *call = priv->q_completion_model->call();
781 priv->q_completion_model->callSelectedNumber();
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400782
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400783 /* make this the currently selected call */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400784 QModelIndex idx = CallModel::instance().getIndex(call);
785 CallModel::instance().selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich1564bba2015-04-29 11:28:43 -0400786
787 /* move focus away from entry so that DTMF tones can be entered via the keyboard */
788 gtk_widget_child_focus(GTK_WIDGET(win), GTK_DIR_TAB_FORWARD);
Stepan Salenikovich2e6ab562015-05-07 12:33:07 -0400789
790 /* clear the entry */
791 gtk_entry_set_text(GTK_ENTRY(priv->search_entry), "");
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400792 } else {
793 g_warning("autocompletion selection is not a valid index!");
794 }
795 return TRUE;
796}
797
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400798static gboolean
799dtmf_pressed(RingMainWindow *win,
800 GdkEventKey *event,
801 G_GNUC_UNUSED gpointer user_data)
802{
803 g_return_val_if_fail(event->type == GDK_KEY_PRESS, GDK_EVENT_PROPAGATE);
804
805 /* we want to react to digit key presses, as long as a GtkEntry is not the
806 * input focus
807 */
808 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(win));
809 if (GTK_IS_ENTRY(focus))
810 return GDK_EVENT_PROPAGATE;
811
812 /* make sure that a call is selected*/
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400813 QItemSelectionModel *selection = CallModel::instance().selectionModel();
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400814 QModelIndex idx = selection->currentIndex();
815 if (!idx.isValid())
816 return GDK_EVENT_PROPAGATE;
817
818 /* make sure that the selected call is in progress */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400819 Call *call = CallModel::instance().getCall(idx);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400820 Call::LifeCycleState state = call->lifeCycleState();
821 if (state != Call::LifeCycleState::PROGRESS)
822 return GDK_EVENT_PROPAGATE;
823
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400824 /* filter out cretain MOD masked key presses so that, for example, 'Ctrl+c'
825 * does not result in a 'c' being played.
826 * we filter Ctrl, Alt, and SUPER/HYPER/META keys */
827 if ( event->state
828 & ( GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK ))
829 return GDK_EVENT_PROPAGATE;
830
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400831 /* pass the character that was entered to be played by the daemon;
832 * the daemon will filter out invalid DTMF characters */
833 guint32 unicode_val = gdk_keyval_to_unicode(event->keyval);
834 QString val = QString::fromUcs4(&unicode_val, 1);
835 call->playDTMF(val);
836 g_debug("attemptingto play DTMF tone during ongoing call: %s", val.toUtf8().constData());
Stepan Salenikovich447f4a72015-04-29 11:09:28 -0400837
838 /* always propogate the key, so we don't steal accelerators/shortcuts */
839 return GDK_EVENT_PROPAGATE;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400840}
841
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400842static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500843ring_main_window_init(RingMainWindow *win)
844{
845 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
846 gtk_widget_init_template(GTK_WIDGET(win));
847
848 /* set window icon */
849 GError *error = NULL;
850 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
851 if (icon == NULL) {
852 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400853 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500854 } else
855 gtk_window_set_icon(GTK_WINDOW(win), icon);
856
857 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400858 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400859 -1, 16, TRUE, &error);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400860 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500861 g_debug("Could not load icon: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400862 g_clear_error(&error);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500863 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400864 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500865
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400866 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500867 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
868 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400869 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500870 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500871
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400872 /* settings icon */
Stepan Salenikovichd61974b2016-03-18 16:36:27 -0400873 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400874
875 /* connect settings button signal */
876 g_signal_connect(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
877
878 /* add the call view to the main stack */
879 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
880 priv->vbox_call_view,
881 CALL_VIEW_NAME);
882
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400883 if (has_ring_account()) {
884 /* user has ring account, so show the call view right away */
885 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
886 } else {
887 /* user has to create the ring account */
888 show_account_creation(win);
889 }
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400890
891 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400892 priv->account_settings_view = account_view_new();
893 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
894
Stepan Salenikovich0bd53492015-05-11 14:28:52 -0400895 priv->media_settings_view = media_settings_view_new();
896 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 -0400897
Stepan Salenikovichde896112015-05-11 16:46:33 -0400898 priv->general_settings_view = general_settings_view_new();
899 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->general_settings_view, GENERAL_SETTINGS_VIEW_NAME);
900
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400901 /* make the setting we will show first the active one */
Stepan Salenikovichde896112015-05-11 16:46:33 -0400902 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_general_settings), TRUE);
903 priv->last_settings_view = priv->general_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400904
905 /* connect the settings button signals to switch settings views */
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400906 g_signal_connect(priv->radiobutton_media_settings, "toggled", G_CALLBACK(show_media_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400907 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
Stepan Salenikovichde896112015-05-11 16:46:33 -0400908 g_signal_connect(priv->radiobutton_general_settings, "toggled", G_CALLBACK(show_general_settings), win);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400909
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400910 /* populate the notebook */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400911 auto smart_view = recent_contacts_view_new();
912 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_smartview), smart_view);
Stepan Salenikovich7c71bfe2015-05-13 18:08:09 -0400913
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400914 auto contacts_view = contacts_view_new();
915 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_contacts), contacts_view);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400916
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -0400917 auto history_view = history_view_new();
918 gtk_container_add(GTK_CONTAINER(priv->scrolled_window_history), history_view);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400919
Stepan Salenikovich2cde7612015-09-25 10:44:01 -0400920 /* welcome/default view */
Stepan Salenikovich3034d922015-10-09 10:11:42 -0400921 priv->welcome_view = ring_welcome_view_new();
922 g_object_ref(priv->welcome_view);
923 // gtk_stack_add_named(GTK_STACK(priv->stack_call_view), welcome_view, DEFAULT_VIEW_NAME);
924 gtk_container_add(GTK_CONTAINER(priv->frame_call), priv->welcome_view);
925 gtk_widget_show(priv->welcome_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500926
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500927 /* call/chat selection */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400928 QObject::connect(
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500929 RecentModel::instance().selectionModel(),
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400930 &QItemSelectionModel::currentChanged,
931 [win](const QModelIndex current, G_GNUC_UNUSED const QModelIndex & previous) {
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500932 if (auto call = RecentModel::instance().getActiveCall(current)) {
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400933 /* if the call is on hold, we want to put it off hold automatically
934 * when switching to it */
935 if (call->state() == Call::State::HOLD)
936 call << Call::Action::HOLD;
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400937 }
Stepan Salenikovichc1323422016-01-06 10:54:44 -0500938 selection_changed(current, win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400939 }
940 );
941
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500942 /* connect to dataChanged of the RecentModel to see if we need to change the view */
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400943 QObject::connect(
Stepan Salenikovich9c9a0492016-01-13 10:55:06 -0500944 &RecentModel::instance(),
945 &RecentModel::dataChanged,
946 [win](const QModelIndex & topLeft, G_GNUC_UNUSED const QModelIndex & bottomRight, G_GNUC_UNUSED const QVector<int> & roles) {
Stepan Salenikovich504f7722016-01-20 17:10:45 -0500947 /* it is possible for dataChanged to be emitted inside of a dataChanged handler or
948 * some other signal; since the connection is via a lambda, Qt would cause the
949 * handler to be called directly. This is not behaviour we usually want, so we call our
950 * function via g_idle so that it gets called after the initial handler is done.
951 */
952 if (topLeft == RecentModel::instance().selectionModel()->currentIndex())
953 g_idle_add((GSourceFunc)selected_item_changed, win);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -0400954 }
955 );
956
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500957 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
958 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
959
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400960 /* autocompletion */
961 priv->q_completion_model = new NumberCompletionModel();
962
963 /* autocompletion renderers */
964 GtkCellArea *completion_area = gtk_cell_area_box_new();
965
966 /* photo renderer */
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -0400967 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400968 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
969 renderer,
970 TRUE, /* expand */
971 TRUE, /* align */
972 TRUE); /* fixed size */
973
974 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
975 renderer,
976 (GtkCellLayoutDataFunc)autocompletion_photo_render,
977 NULL, NULL);
978
979 /* name renderer */
980 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -0400981 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400982 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
983 renderer,
984 TRUE, /* expand */
985 TRUE, /* align */
986 TRUE); /* fixed size */
987
988 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
989 renderer,
990 (GtkCellLayoutDataFunc)autocompletion_name_render,
991 NULL, NULL);
992
993 /* number renderer */
994 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -0400995 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -0400996 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
997 renderer,
998 TRUE, /* expand */
999 TRUE, /* align */
1000 TRUE); /* fixed size */
1001
1002 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1003 renderer,
1004 (GtkCellLayoutDataFunc)autocompletion_number_render,
1005 NULL, NULL);
1006 /* account renderer */
1007 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich19e581d2015-06-18 11:44:45 -04001008 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001009 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(completion_area),
1010 renderer,
1011 TRUE, /* expand */
1012 TRUE, /* align */
1013 TRUE); /* fixed size */
1014
1015 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(completion_area),
1016 renderer,
1017 (GtkCellLayoutDataFunc)autocompletion_account_render,
1018 NULL, NULL);
1019
1020 GtkEntryCompletion *entry_completion = gtk_entry_completion_new_with_area(completion_area);
1021
1022 GtkQTreeModel *completion_model = gtk_q_tree_model_new(
1023 (QAbstractItemModel *)priv->q_completion_model,
1024 1,
aviau9bbb19b2016-05-16 15:53:44 -04001025 0, Qt::DisplayRole, G_TYPE_STRING);
Stepan Salenikovicheafc3dd2015-04-16 18:38:01 -04001026
1027 gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(completion_model));
1028
1029 gtk_entry_set_completion(GTK_ENTRY(priv->search_entry), entry_completion);
1030 gtk_entry_completion_set_match_func(
1031 entry_completion,
1032 (GtkEntryCompletionMatchFunc) completion_match_func,
1033 NULL,
1034 NULL);
1035
1036 /* connect signal to when text is entered in the entry */
1037 g_signal_connect(priv->search_entry, "changed", G_CALLBACK(search_entry_text_changed), win);
1038 g_signal_connect(entry_completion, "match-selected", G_CALLBACK(select_autocompletion), win);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001039
1040 /* connect to incoming call and focus */
1041 QObject::connect(
Guillaume Roguez5d1514b2015-10-22 15:55:31 -04001042 &CallModel::instance(),
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001043 &CallModel::incomingCall,
1044 [=](Call* call) {
Guillaume Roguez5d1514b2015-10-22 15:55:31 -04001045 CallModel::instance().selectionModel()->setCurrentIndex(
1046 CallModel::instance().getIndex(call), QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001047 }
1048 );
Stepan Salenikovich5eab3032015-03-28 10:50:51 -04001049
Stepan Salenikovichb01d7362015-04-27 23:02:00 -04001050 /* react to digit key press events */
1051 g_signal_connect(win, "key-press-event", G_CALLBACK(dtmf_pressed), NULL);
Stepan Salenikovich48b45c12015-09-25 15:49:51 -04001052
1053 /* set the search entry placeholder text */
1054 gtk_entry_set_placeholder_text(GTK_ENTRY(priv->search_entry),
1055 C_("Please try to make the translation 50 chars or less so that it fits into the layout", "Search contacts or enter number"));
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001056}
1057
1058static void
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001059ring_main_window_dispose(GObject *object)
1060{
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001061 RingMainWindow *self = RING_MAIN_WINDOW(object);
1062 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1063
1064 QObject::disconnect(priv->selection_updated);
1065
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001066 G_OBJECT_CLASS(ring_main_window_parent_class)->dispose(object);
1067}
1068
1069static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001070ring_main_window_finalize(GObject *object)
1071{
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001072 RingMainWindow *self = RING_MAIN_WINDOW(object);
1073 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(self);
1074
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001075 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001076}
1077
1078static void
1079ring_main_window_class_init(RingMainWindowClass *klass)
1080{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001081 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001082 G_OBJECT_CLASS(klass)->dispose = ring_main_window_dispose;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001083
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001084 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1085 "/cx/ring/RingGnome/ringmainwindow.ui");
1086
Stepan Salenikovichbbb10d82015-05-13 12:26:44 -04001087 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_left_pane);
Stepan Salenikovich2f8b4492015-09-21 17:10:36 -04001088 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_smartview);
Stepan Salenikovicha7a19bc2015-09-24 16:16:28 -04001089 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contacts);
1090 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_history);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001091 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
1092 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
1093 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
1094 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001095 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -05001096 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -05001097 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001098 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
Stepan Salenikovich3034d922015-10-09 10:11:42 -04001099 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, frame_call);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -05001100 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001101 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -04001102 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -04001103 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001104
1105 /* account creation */
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -05001106 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001107 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -05001108 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_account_creation_entry);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001109 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
Stepan Salenikovichbabe26f2016-02-12 16:47:07 -05001110 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_default_name);
1111 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_paceholder);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -04001112 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
1113 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
1114 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
Nicolas Jagerb413b302016-05-06 11:41:32 -04001115 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, box_avatarselection);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001116}
1117
1118GtkWidget *
1119ring_main_window_new (GtkApplication *app)
1120{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001121 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
1122
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001123 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001124}