blob: 6560b4ffba22fee947f4cefbdd5b9823c9e617c9 [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
2 * Copyright (C) 2015 Savoir-Faire Linux Inc.
3 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "ringmainwindow.h"
32
33#include <gtk/gtk.h>
Stepan Salenikovicha3557452015-02-20 14:14:12 -050034#include "models/gtkqtreemodel.h"
35#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050036#include <call.h>
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050037#include <QtCore/QItemSelectionModel>
Stepan Salenikovich69771842015-02-24 18:11:45 -050038#include "incomingcallview.h"
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050039#include "currentcallview.h"
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050040#include <string.h>
Stepan Salenikovichdd84cf92015-03-19 21:38:19 -040041#include <categorizedhistorymodel.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 Salenikovichf903d1b2015-03-25 14:51:45 -040049#include "videosettingsview.h"
50#include <video/previewmanager.h>
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040051
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040052#define CALL_VIEW_NAME "calls"
Stepan Salenikovichb8e41272015-03-27 14:31:54 -040053#define CREATE_ACCOUNT_1_VIEW_NAME "create1"
54#define CREATE_ACCOUNT_2_VIEW_NAME "create2"
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040055#define GENERAL_SETTINGS_VIEW_NAME "general"
56#define AUDIO_SETTINGS_VIEW_NAME "audio"
57#define VIDEO_SETTINGS_VIEW_NAME "video"
58#define ACCOUNT_SETTINGS_VIEW_NAME "accounts"
Stepan Salenikovich69771842015-02-24 18:11:45 -050059#define DEFAULT_VIEW_NAME "placeholder"
Stepan Salenikovich15142182015-03-11 17:15:26 -040060#define VIEW_CONTACTS "contacts"
61#define VIEW_HISTORY "history"
62#define VIEW_PRESENCE "presence"
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050063
64struct _RingMainWindow
65{
66 GtkApplicationWindow parent;
67};
68
69struct _RingMainWindowClass
70{
71 GtkApplicationWindowClass parent_class;
72};
73
74typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
75
76struct _RingMainWindowPrivate
77{
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040078 GtkWidget *ring_menu;
79 GtkWidget *image_ring;
80 GtkWidget *ring_settings;
81 GtkWidget *image_settings;
82 GtkWidget *hbox_search;
83 GtkWidget *hbox_settings;
Stepan Salenikovich15142182015-03-11 17:15:26 -040084 GtkWidget *stack_contacts_history_presence;
85 GtkWidget *radiobutton_contacts;
86 GtkWidget *radiobutton_history;
87 GtkWidget *radiobutton_presence;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050088 GtkWidget *treeview_call;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -050089 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -050090 GtkWidget *stack_main_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040091 GtkWidget *vbox_call_view;
92 GtkWidget *stack_call_view;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050093 GtkWidget *button_placecall;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040094 GtkWidget *account_settings_view;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -040095 GtkWidget *video_settings_view;
96 GtkWidget *last_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040097 GtkWidget *radiobutton_audio_settings;
98 GtkWidget *radiobutton_general_settings;
99 GtkWidget *radiobutton_video_settings;
100 GtkWidget *radiobutton_account_settings;
101
102 gboolean show_settings;
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400103
104 /* account creation */
105 GtkWidget *account_creation_1;
106 GtkWidget *image_ring_logo;
107 GtkWidget *label_enter_alias;
108 GtkWidget *entry_alias;
109 GtkWidget *label_generating_account;
110 GtkWidget *spinner_generating_account;
111 GtkWidget *button_account_creation_next;
112 GtkWidget *account_creation_2;
113 GtkWidget *entry_hash;
114 GtkWidget *button_account_creation_done;
115
116 QMetaObject::Connection hash_updated;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500117};
118
119G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
120
121#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
122
Stepan Salenikovich69771842015-02-24 18:11:45 -0500123static QModelIndex
124get_index_from_selection(GtkTreeSelection *selection)
125{
126 GtkTreeIter iter;
127 GtkTreeModel *model = NULL;
128
129 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
130 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
131 } else {
132 return QModelIndex();
133 }
134}
135
136static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500137update_call_model_selection(GtkTreeSelection *selection, G_GNUC_UNUSED gpointer user_data)
Stepan Salenikovich69771842015-02-24 18:11:45 -0500138{
Stepan Salenikovich69771842015-02-24 18:11:45 -0500139 QModelIndex current = get_index_from_selection(selection);
140 if (current.isValid())
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500141 CallModel::instance()->selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500142 else
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500143 CallModel::instance()->selectionModel()->clearCurrentIndex();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500144}
145
146static void
147call_selection_changed(GtkTreeSelection *selection, gpointer win)
148{
149 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
150
151 /* get the current visible stack child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400152 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500153
154 QModelIndex idx = get_index_from_selection(selection);
155 if (idx.isValid()) {
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400156 QVariant state = CallModel::instance()->data(idx, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500157 GtkWidget *new_call_view = NULL;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500158 char* new_call_view_name = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500159
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500160 /* FIXME: change when fixed
161 * switch(state.value<Call::LifeCycleState>()) { */
162 Call::LifeCycleState lifecyclestate = (Call::LifeCycleState)state.toUInt();
163 switch(lifecyclestate) {
164 case Call::LifeCycleState::INITIALIZATION:
165 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500166 new_call_view = incoming_call_view_new();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500167 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500168 /* use the pointer of the call as a unique name */
169 new_call_view_name = g_strdup_printf("%p_incoming", (void *)CallModel::instance()->getCall(idx));
170 break;
171 case Call::LifeCycleState::PROGRESS:
172 new_call_view = current_call_view_new();
173 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx);
174 /* use the pointer of the call as a unique name */
175 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx));
176 break;
177 case Call::LifeCycleState::COUNT__:
178 g_warning("LifeCycleState should never be COUNT");
179 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500180 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500181
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400182 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
183 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500184 g_free(new_call_view_name);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500185
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 */
225
226 /* FIXME: change when fixed
227 * switch(state.value<Call::LifeCycleState>()) { */
228 Call::LifeCycleState lifecyclestate = (Call::LifeCycleState)state.toUInt();
229 switch(lifecyclestate) {
230 case Call::LifeCycleState::INITIALIZATION:
231 /* LifeCycleState cannot go backwards, so it should not be possible
232 * that the call is displayed as current (meaning that its in progress)
233 * but have the state 'initialization' */
234 if (IS_CURRENT_CALL_VIEW(old_call_view))
235 g_warning("call displayed as current, but is in state of initialization");
236 break;
237 case Call::LifeCycleState::PROGRESS:
238 if (IS_INCOMING_CALL_VIEW(old_call_view)) {
239 /* change from incoming to current */
240 new_call_view = current_call_view_new();
241 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx_selected);
242 /* use the pointer of the call as a unique name */
243 char* new_call_view_name = NULL;
244 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx_selected));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400245 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), new_call_view, new_call_view_name);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500246 g_free(new_call_view_name);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400247 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
248 gtk_stack_set_visible_child(GTK_STACK(priv->stack_call_view), new_call_view);
249 gtk_stack_set_transition_type(GTK_STACK(priv->stack_call_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500250 }
251 break;
252 case Call::LifeCycleState::FINISHED:
253 /* do nothing, either call view is valid for this state */
254 break;
255 case Call::LifeCycleState::COUNT__:
256 g_warning("LifeCycleState should never be COUNT");
257 break;
258 }
259
260 /* check if we changed the visible child */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400261 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_call_view));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500262 if (current_call_view != old_call_view && old_call_view != NULL) {
263 /* if the previous child was a call view, then remove it from
264 * the stack; removing it should destory it since there should not
265 * be any other references to it */
266 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400267 gtk_container_remove(GTK_CONTAINER(priv->stack_call_view), old_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500268 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500269 }
270 }
271}
272
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500273static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500274search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
275{
276 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
277
278 const gchar *number = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
279
280 if (number && strlen(number) > 0) {
281 g_debug("dialing to number: %s", number);
282 Call *call = CallModel::instance()->dialingCall();
283 call->setDialNumber(number);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500284 call->performAction(Call::Action::ACCEPT);
285
286 /* make this the currently selected call */
287 QModelIndex idx = CallModel::instance()->getIndex(call);
288 CallModel::instance()->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
289 }
290}
291
292static void
Stepan Salenikovich15142182015-03-11 17:15:26 -0400293call_history_item(GtkTreeView *tree_view,
294 GtkTreePath *path,
295 G_GNUC_UNUSED GtkTreeViewColumn *column,
296 G_GNUC_UNUSED gpointer user_data)
297{
298 GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
299
300 /* get iter */
301 GtkTreeIter iter;
302 if (gtk_tree_model_get_iter(model, &iter, path)) {
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400303 QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(model), &iter);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400304
305 QVariant contact_method = idx.data(static_cast<int>(Call::Role::ContactMethod));
306 /* create new call */
307 if (contact_method.value<ContactMethod*>()) {
308 Call *call = CallModel::instance()->dialingCall();
309 call->setDialNumber(contact_method.value<ContactMethod*>());
310 call->performAction(Call::Action::ACCEPT);
311
312 /* make this the currently selected call */
313 QModelIndex call_idx = CallModel::instance()->getIndex(call);
314 CallModel::instance()->selectionModel()->setCurrentIndex(call_idx, QItemSelectionModel::ClearAndSelect);
315 } else
316 g_warning("contact method is empty");
317 }
318}
319
320static void
321navbutton_contacts_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
322{
323 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
324 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
325
326 if (gtk_toggle_button_get_active(navbutton)) {
327
328 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
329
330 if (visible) {
331 /* contacts is left of both history and presence, so always slide right to show it */
332 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
333 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
334 } else {
335 g_warning("should always have a visible child in the nav stack");
336 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
337 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
338 }
339 }
340}
341
342static void
343navbutton_presence_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
344{
345 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
346 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
347
348 if (gtk_toggle_button_get_active(navbutton)) {
349
350 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
351 if (visible) {
352 /* presence is right of both history and contacts, so always slide left to show it */
353 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
354 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
355 } else {
356 g_warning("should always have a visible child in the nav stack");
357 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
358 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
359 }
360 }
361}
362
363static void
364navbutton_history_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
365{
366 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
367 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
368
369 if (gtk_toggle_button_get_active(navbutton)) {
370
371 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
372 if (visible) {
373 if (strcmp(visible, VIEW_CONTACTS) == 0) {
374 /* history is right of contacts, so slide left to show it */
375 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
376 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
377 } else if (strcmp(visible, VIEW_PRESENCE) == 0) {
378 /* history is left of presence, so slide right to show it */
379 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
380 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
381 }
382 } else {
383 g_warning("should always have a visible child in the nav stack");
384 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
385 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
386 }
387 }
388}
389
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400390static gboolean
391save_accounts(GtkWidget *working_dialog)
392{
393 /* save changes to accounts */
394 AccountModel::instance()->save();
395 /* save changes to codecs */
396 for (int i = 0; i < AccountModel::instance()->rowCount(); i++) {
397 QModelIndex idx = AccountModel::instance()->index(i, 0);
398 AccountModel::instance()->getAccountByModelIndex(idx)->codecModel()->save();
399 }
400
401 if (working_dialog)
402 gtk_widget_destroy(working_dialog);
403
404 return G_SOURCE_REMOVE;
405}
406
Stepan Salenikovich15142182015-03-11 17:15:26 -0400407static void
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400408settings_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
409{
410 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
411 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
412
413 /* toggle show settings */
414 priv->show_settings = !priv->show_settings;
415
416 /* check which view to show */
417 if (priv->show_settings) {
418 /* show settings */
419 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-ok-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
420
421 gtk_widget_hide(priv->hbox_search);
422 gtk_widget_show(priv->hbox_settings);
423
424 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400425 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->last_settings_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400426 } else {
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400427 /* show working dialog in case save operation takes time */
428 GtkWidget *working = ring_dialog_working(GTK_WIDGET(win), NULL);
429 gtk_window_present(GTK_WINDOW(working));
430
431 /* now save after the time it takes to transition back to the call view (400ms)
432 * the save doesn't happen before the "working" dialog is presented
433 * the timeout function should destroy the "working" dialog when done saving
434 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400435 g_timeout_add_full(G_PRIORITY_DEFAULT, 400, (GSourceFunc)save_accounts, working, NULL);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400436
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400437 /* show calls */
438 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
439
440 gtk_widget_show(priv->hbox_search);
441 gtk_widget_hide(priv->hbox_settings);
442
443 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
444 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400445
446 /* make sure video preview is stopped, in case it was started */
447 Video::PreviewManager::instance()->stopPreview();
448 }
449}
450
451static void
452show_video_settings(GtkToggleButton *navbutton, RingMainWindow *win)
453{
454 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
455 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
456
457 if (gtk_toggle_button_get_active(navbutton)) {
458 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
459 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), VIDEO_SETTINGS_VIEW_NAME);
460 priv->last_settings_view = priv->video_settings_view;
461 }
462}
463
464static void
465show_account_settings(GtkToggleButton *navbutton, RingMainWindow *win)
466{
467 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
468 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
469
470 if (gtk_toggle_button_get_active(navbutton)) {
471 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
472 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_SETTINGS_VIEW_NAME);
473 priv->last_settings_view = priv->account_settings_view;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400474 }
475}
476
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400477static gboolean
478has_ring_account()
479{
480 /* check if a Ring account exists */
481 int a_count = AccountModel::instance()->rowCount();
482 for (int i = 0; i < a_count; ++i) {
483 QModelIndex idx = AccountModel::instance()->index(i, 0);
484 QVariant protocol = idx.data(static_cast<int>(Account::Role::Proto));
485 if ((Account::Protocol)protocol.toUInt() == Account::Protocol::RING)
486 return TRUE;
487 }
488
489 return FALSE;
490}
491
492static gboolean
493create_ring_account(RingMainWindow *win)
494{
495 g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
496 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
497
498 /* create account and set UPnP enabled, as its not by default in the daemon */
499 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
500 Account *account = AccountModel::instance()->add(alias, Account::Protocol::RING);
501 account->setUpnpEnabled(TRUE);
502
503 /* wait for hash to be generated to show the next view */
504 priv->hash_updated = QObject::connect(
505 account,
506 &Account::changed,
507 [=] (Account *a) {
508 QString hash = a->username();
509 if (!hash.isEmpty()) {
510 /* set the hash */
511 gtk_entry_set_text(GTK_ENTRY(priv->entry_hash), hash.toUtf8().constData());
512
513 /* show the next accont creation view */
514 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
515 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_2_VIEW_NAME);
516
517 /* select the hash text */
518 gtk_widget_grab_focus(priv->entry_hash);
519 gtk_editable_select_region(GTK_EDITABLE(priv->entry_hash), 0, -1);
520 }
521 }
522 );
523
524 account->performAction(Account::EditAction::SAVE);
525
526 return G_SOURCE_REMOVE;
527}
528
529static void
530alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
531{
532 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
533
534 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
535 if (strlen(alias) > 0) {
536 /* enable "next" button */
537 gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
538 } else {
539 /* disable "next" button, as we require an alias */
540 gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
541 }
542}
543
544static void
545account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
546{
547 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
548
549 /* show/hide relevant widgets */
550 gtk_widget_hide(priv->label_enter_alias);
551 gtk_widget_hide(priv->entry_alias);
552 gtk_widget_hide(priv->button_account_creation_next);
553 gtk_widget_show(priv->label_generating_account);
554 gtk_widget_show(priv->spinner_generating_account);
555
556 /* now create account after a short timeout so that the the save doesn't
557 * happen freeze the client before the widget changes happen;
558 * the timeout function should then display the next step in account creation
559 */
560 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
561}
562
563static void
564account_creation_done_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
565{
566 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
567
568 QObject::disconnect(priv->hash_updated);
569
570 /* show the call view */
571 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
572 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
573
574 /* show the search bar and settings */
575 gtk_widget_show(priv->hbox_search);
576 gtk_widget_show(priv->ring_settings);
577
578}
579
580static void
581entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
582{
583 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
584
585 const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
586 if (strlen(alias) > 0)
587 gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
588}
589
590static void
591show_account_creation(RingMainWindow *win)
592{
593 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
594
595 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
596 priv->account_creation_1,
597 CREATE_ACCOUNT_1_VIEW_NAME);
598
599 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
600 priv->account_creation_2,
601 CREATE_ACCOUNT_2_VIEW_NAME);
602
603 /* hide search bar and settings until account creation is complete */
604 gtk_widget_hide(priv->hbox_search);
605 gtk_widget_hide(priv->ring_settings);
606
607 /* set ring logo */
608 GError *error = NULL;
609 GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
610 -1, 75, TRUE, &error);
611 if (logo_ring == NULL) {
612 g_debug("Could not load logo: %s", error->message);
613 g_error_free(error);
614 } else
615 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
616
617 /* style of alias and hash entry; give them a larger font */
618 gtk_widget_override_font(priv->entry_alias, pango_font_description_from_string("15"));
619 gtk_widget_override_font(priv->entry_hash, pango_font_description_from_string("monospace 15"));
620
621 /* connect signals */
622 g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
623 g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
624 g_signal_connect(priv->button_account_creation_done, "clicked", G_CALLBACK(account_creation_done_clicked), win);
625 g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
626 g_signal_connect_swapped(priv->entry_hash, "activate", G_CALLBACK(gtk_button_clicked), priv->button_account_creation_done);
627
628 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_1_VIEW_NAME);
629}
630
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400631static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500632ring_main_window_init(RingMainWindow *win)
633{
634 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
635 gtk_widget_init_template(GTK_WIDGET(win));
636
637 /* set window icon */
638 GError *error = NULL;
639 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
640 if (icon == NULL) {
641 g_debug("Could not load icon: %s", error->message);
642 g_error_free(error);
643 } else
644 gtk_window_set_icon(GTK_WINDOW(win), icon);
645
646 /* set menu icon */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400647 GdkPixbuf* image_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-symbol-blue",
648 -1, 24, TRUE, &error);
649 if (image_ring == NULL) {
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500650 g_debug("Could not load icon: %s", error->message);
651 g_error_free(error);
652 } else
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400653 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring), image_ring);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500654
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400655 /* ring menu */
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500656 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
657 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400658 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->ring_menu), menu);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500659 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500660
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400661 /* settings icon */
662 gtk_image_set_from_icon_name(GTK_IMAGE(priv->image_settings), "emblem-system-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
663
664 /* connect settings button signal */
665 g_signal_connect(priv->ring_settings, "clicked", G_CALLBACK(settings_clicked), win);
666
667 /* add the call view to the main stack */
668 gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
669 priv->vbox_call_view,
670 CALL_VIEW_NAME);
671
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400672 if (has_ring_account()) {
673 /* user has ring account, so show the call view right away */
674 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
675 } else {
676 /* user has to create the ring account */
677 show_account_creation(win);
678 }
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400679
680 /* init the settings views */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400681 priv->account_settings_view = account_view_new();
682 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_settings_view, ACCOUNT_SETTINGS_VIEW_NAME);
683
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400684 priv->video_settings_view = video_settings_view_new();
685 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->video_settings_view, VIDEO_SETTINGS_VIEW_NAME);
686
687 /* make the setting we will show first the active one */
688 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->radiobutton_video_settings), TRUE);
689 priv->last_settings_view = priv->video_settings_view;
690
691 /* connect the settings button signals to switch settings views */
692 g_signal_connect(priv->radiobutton_video_settings, "toggled", G_CALLBACK(show_video_settings), win);
693 g_signal_connect(priv->radiobutton_account_settings, "toggled", G_CALLBACK(show_account_settings), win);
694
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500695 /* call model */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500696 GtkQTreeModel *call_model;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500697 GtkCellRenderer *renderer;
698 GtkTreeViewColumn *column;
699
Stepan Salenikovich69771842015-02-24 18:11:45 -0500700 call_model = gtk_q_tree_model_new(CallModel::instance(), 4,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500701 Call::Role::Name, G_TYPE_STRING,
702 Call::Role::Number, G_TYPE_STRING,
703 Call::Role::Length, G_TYPE_STRING,
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400704 Call::Role::State, G_TYPE_STRING);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500705 gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_call), GTK_TREE_MODEL(call_model));
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500706
707 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400708 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500709 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400710 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500711 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
712
713 renderer = gtk_cell_renderer_text_new();
714 column = gtk_tree_view_column_new_with_attributes("Duration", renderer, "text", 2, NULL);
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400715 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500716 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
717
Stepan Salenikovich69771842015-02-24 18:11:45 -0500718 /* connect signals to and from UserActionModel to sync selection betwee
719 * the QModel and the GtkTreeView */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500720 QObject::connect(
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500721 CallModel::instance()->selectionModel(),
Stepan Salenikovich69771842015-02-24 18:11:45 -0500722 &QItemSelectionModel::currentChanged,
723 [=](const QModelIndex & current, const QModelIndex & previous) {
724 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
725
726 /* first unselect the previous */
727 if (previous.isValid()) {
728 GtkTreeIter old_iter;
729 if (gtk_q_tree_model_source_index_to_iter(call_model, previous, &old_iter)) {
730 gtk_tree_selection_unselect_iter(selection, &old_iter);
731 } else {
732 g_warning("Trying to unselect invalid GtkTreeIter");
733 }
734 }
735
736 /* select the current */
737 if (current.isValid()) {
738 GtkTreeIter new_iter;
739 if (gtk_q_tree_model_source_index_to_iter(call_model, current, &new_iter)) {
740 gtk_tree_selection_select_iter(selection, &new_iter);
741 } else {
742 g_warning("SelectionModel of CallModel changed to invalid QModelIndex?");
743 }
744 }
745 }
746 );
747
748 GtkTreeSelection *call_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500749 g_signal_connect(call_selection, "changed", G_CALLBACK(update_call_model_selection), NULL);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500750
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500751 /* connect to call state changes to update relevant view(s) */
752 QObject::connect(
753 CallModel::instance(),
754 &CallModel::callStateChanged,
755 [=](Call* call, G_GNUC_UNUSED Call::State previousState) {
756 call_state_changed(call, win);
757 }
758 );
759
Stepan Salenikovich15142182015-03-11 17:15:26 -0400760 /* contacts view/model */
761 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
762 GtkWidget *treeview_contacts = gtk_tree_view_new();
763 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_contacts), FALSE);
764 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_contacts);
765 gtk_widget_show_all(scrolled_window);
766 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
767 scrolled_window,
768 VIEW_CONTACTS);
769
770 /* history view/model */
771 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
772 GtkWidget *treeview_history = gtk_tree_view_new();
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400773 /* make headers visible to allow column resizing */
774 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_history), TRUE);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400775 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_history);
776 gtk_widget_show_all(scrolled_window);
777 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
778 scrolled_window,
779 VIEW_HISTORY);
780 /* TODO: make this linked to the client settings so that the last shown view is the same on startup */
781 gtk_stack_set_visible_child(GTK_STACK(priv->stack_contacts_history_presence),
782 scrolled_window);
783
Stepan Salenikovich15142182015-03-11 17:15:26 -0400784
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400785 /* sort the history in descending order by date */
Stepan Salenikovichdd84cf92015-03-19 21:38:19 -0400786 QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(CategorizedHistoryModel::instance());
787 proxyModel->setSourceModel(CategorizedHistoryModel::instance());
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400788 proxyModel->setSortRole(static_cast<int>(Call::Role::Date));
789 proxyModel->sort(0,Qt::DescendingOrder);
790
791 GtkQSortFilterTreeModel *history_model = gtk_q_sort_filter_tree_model_new((QSortFilterProxyModel *)proxyModel, 4,
Stepan Salenikovich15142182015-03-11 17:15:26 -0400792 Qt::DisplayRole, G_TYPE_STRING,
793 Call::Role::Number, G_TYPE_STRING,
794 Call::Role::FormattedDate, G_TYPE_STRING,
795 Call::Role::Direction, G_TYPE_INT);
796 gtk_tree_view_set_model( GTK_TREE_VIEW(treeview_history), GTK_TREE_MODEL(history_model) );
797
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400798 /* name or time category column */
799 renderer = gtk_cell_renderer_text_new();
800 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
801 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400802 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_history), column);
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400803 gtk_tree_view_column_set_resizable(column, TRUE);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400804
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400805 /* "number" column */
806 renderer = gtk_cell_renderer_text_new();
807 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
808 column = gtk_tree_view_column_new_with_attributes("Number", renderer, "text", 1, NULL);
809 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_history), column);
810 gtk_tree_view_column_set_resizable(column, TRUE);
811
812 /* date column */
Stepan Salenikovich15142182015-03-11 17:15:26 -0400813 renderer = gtk_cell_renderer_text_new ();
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400814 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400815 column = gtk_tree_view_column_new_with_attributes ("Date", renderer, "text", 2, NULL);
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400816 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_history), column);
817 gtk_tree_view_column_set_resizable(column, TRUE);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400818
Stepan Salenikovich2efd1982015-03-22 12:10:23 -0400819 /* expand the first row, which should be the most recent calls */
820 gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview_history),
821 gtk_tree_path_new_from_string("0"),
822 FALSE);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400823
824 g_signal_connect(treeview_history, "row-activated", G_CALLBACK(call_history_item), NULL);
825
826 /* presence view/model */
827 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
828 GtkWidget *treeview_presence = gtk_tree_view_new();
829 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_presence), FALSE);
830 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_presence);
831 gtk_widget_show_all(scrolled_window);
832 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
833 scrolled_window,
834 VIEW_PRESENCE);
835
836 /* connect signals to change the contacts/history/presence stack view */
837 g_signal_connect(priv->radiobutton_contacts, "toggled", G_CALLBACK(navbutton_contacts_toggled), win);
838 g_signal_connect(priv->radiobutton_history, "toggled", G_CALLBACK(navbutton_history_toggled), win);
839 g_signal_connect(priv->radiobutton_presence, "toggled", G_CALLBACK(navbutton_presence_toggled), win);
840
Stepan Salenikovich69771842015-02-24 18:11:45 -0500841 /* TODO: replace stack paceholder view */
842 GtkWidget *placeholder_view = gtk_tree_view_new();
843 gtk_widget_show(placeholder_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400844 gtk_stack_add_named(GTK_STACK(priv->stack_call_view), placeholder_view, DEFAULT_VIEW_NAME);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500845
846 /* connect signals */
847 g_signal_connect(call_selection, "changed", G_CALLBACK(call_selection_changed), win);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500848 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
849 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
850
851 /* style of search entry */
852 gtk_widget_override_font(priv->search_entry, pango_font_description_from_string("monospace 15"));
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500853
854 /* connect to incoming call and focus */
855 QObject::connect(
856 CallModel::instance(),
857 &CallModel::incomingCall,
858 [=](Call* call) {
859 CallModel::instance()->selectionModel()->setCurrentIndex(
860 CallModel::instance()->getIndex(call), QItemSelectionModel::ClearAndSelect);
861 }
862 );
863}
864
865static void
866ring_main_window_finalize(GObject *object)
867{
868 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500869}
870
871static void
872ring_main_window_class_init(RingMainWindowClass *klass)
873{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500874 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
875
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500876 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
877 "/cx/ring/RingGnome/ringmainwindow.ui");
878
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500879 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, treeview_call);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400880 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_contacts_history_presence);
881 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_contacts);
882 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_history);
883 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_presence);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400884 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_menu);
885 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring);
886 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, ring_settings);
887 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_settings);
888 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_search);
889 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, hbox_settings);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -0500890 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500891 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400892 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_call_view);
893 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_call_view);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500894 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400895 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_audio_settings);
896 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
897 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_video_settings);
898 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
Stepan Salenikovichb8e41272015-03-27 14:31:54 -0400899
900 /* account creation */
901 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_1);
902 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
903 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_enter_alias);
904 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
905 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
906 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
907 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
908
909 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation_2);
910 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_hash);
911 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_done);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500912}
913
914GtkWidget *
915ring_main_window_new (GtkApplication *app)
916{
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400917 gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
918
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400919 return (GtkWidget *)win;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500920}