blob: bc123cb4db4d5915f13e94e380fbbe3d332ad4b5 [file] [log] [blame]
Stepan Salenikovich434b88f2015-02-19 17:49:08 -05001/*
2 * Copyright (C) 2015 Savoir-Faire Linux Inc.
3 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "ringmainwindow.h"
32
33#include <gtk/gtk.h>
Stepan Salenikovicha3557452015-02-20 14:14:12 -050034#include "models/gtkqtreemodel.h"
35#include <callmodel.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050036#include <call.h>
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050037#include <QtCore/QItemSelectionModel>
Stepan Salenikovich69771842015-02-24 18:11:45 -050038#include "incomingcallview.h"
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050039#include "currentcallview.h"
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050040#include <string.h>
Stepan Salenikovich15142182015-03-11 17:15:26 -040041#include <historymodel.h>
42#include <contactmethod.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050043
44#define DEFAULT_VIEW_NAME "placeholder"
Stepan Salenikovich15142182015-03-11 17:15:26 -040045#define VIEW_CONTACTS "contacts"
46#define VIEW_HISTORY "history"
47#define VIEW_PRESENCE "presence"
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050048
49struct _RingMainWindow
50{
51 GtkApplicationWindow parent;
52};
53
54struct _RingMainWindowClass
55{
56 GtkApplicationWindowClass parent_class;
57};
58
59typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
60
61struct _RingMainWindowPrivate
62{
63 GtkWidget *gears;
64 GtkWidget *gears_image;
Stepan Salenikovich15142182015-03-11 17:15:26 -040065 GtkWidget *stack_contacts_history_presence;
66 GtkWidget *radiobutton_contacts;
67 GtkWidget *radiobutton_history;
68 GtkWidget *radiobutton_presence;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050069 GtkWidget *treeview_call;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -050070 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -050071 GtkWidget *stack_main_view;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050072 GtkWidget *button_placecall;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050073};
74
75G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
76
77#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
78
Stepan Salenikovich69771842015-02-24 18:11:45 -050079static QModelIndex
80get_index_from_selection(GtkTreeSelection *selection)
81{
82 GtkTreeIter iter;
83 GtkTreeModel *model = NULL;
84
85 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
86 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
87 } else {
88 return QModelIndex();
89 }
90}
91
92static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050093update_call_model_selection(GtkTreeSelection *selection, G_GNUC_UNUSED gpointer user_data)
Stepan Salenikovich69771842015-02-24 18:11:45 -050094{
Stepan Salenikovich69771842015-02-24 18:11:45 -050095 QModelIndex current = get_index_from_selection(selection);
96 if (current.isValid())
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050097 CallModel::instance()->selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich69771842015-02-24 18:11:45 -050098 else
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050099 CallModel::instance()->selectionModel()->clearCurrentIndex();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500100}
101
102static void
103call_selection_changed(GtkTreeSelection *selection, gpointer win)
104{
105 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
106
107 /* get the current visible stack child */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500108 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_main_view));
Stepan Salenikovich69771842015-02-24 18:11:45 -0500109
110 QModelIndex idx = get_index_from_selection(selection);
111 if (idx.isValid()) {
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400112 QVariant state = CallModel::instance()->data(idx, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500113 GtkWidget *new_call_view = NULL;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500114 char* new_call_view_name = NULL;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500115
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500116 /* FIXME: change when fixed
117 * switch(state.value<Call::LifeCycleState>()) { */
118 Call::LifeCycleState lifecyclestate = (Call::LifeCycleState)state.toUInt();
119 switch(lifecyclestate) {
120 case Call::LifeCycleState::INITIALIZATION:
121 case Call::LifeCycleState::FINISHED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500122 new_call_view = incoming_call_view_new();
Stepan Salenikovich69771842015-02-24 18:11:45 -0500123 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), idx);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500124 /* use the pointer of the call as a unique name */
125 new_call_view_name = g_strdup_printf("%p_incoming", (void *)CallModel::instance()->getCall(idx));
126 break;
127 case Call::LifeCycleState::PROGRESS:
128 new_call_view = current_call_view_new();
129 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx);
130 /* use the pointer of the call as a unique name */
131 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx));
132 break;
133 case Call::LifeCycleState::COUNT__:
134 g_warning("LifeCycleState should never be COUNT");
135 break;
Stepan Salenikovich69771842015-02-24 18:11:45 -0500136 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500137
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500138 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), new_call_view, new_call_view_name);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500139 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), new_call_view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500140 g_free(new_call_view_name);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500141
Stepan Salenikovich69771842015-02-24 18:11:45 -0500142 } else {
143 /* nothing selected in the call model, so show the default screen */
144
145 /* TODO: replace stack paceholder view */
146 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
147 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), DEFAULT_VIEW_NAME);
148 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
149
150 }
151
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500152 /* check if we changed the visible child */
153 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_main_view));
154 if (current_call_view != old_call_view && old_call_view != NULL) {
155 /* if the previous child was a call view, then remove it from
Stepan Salenikovich69771842015-02-24 18:11:45 -0500156 * the stack; removing it should destory it since there should not
157 * be any other references to it */
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500158 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
159 gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), old_call_view);
160 }
161 }
162}
163
164static void
165call_state_changed(Call *call, gpointer win)
166{
167 g_debug("call state changed");
168 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
169
170 /* check if the call that changed state is the same as the selected call */
171 QModelIndex idx_selected = CallModel::instance()->selectionModel()->currentIndex();
172
173 if( idx_selected.isValid() && call == CallModel::instance()->getCall(idx_selected)) {
174 g_debug("selected call state changed");
175 /* check if we need to change the view */
176 GtkWidget *old_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_main_view));
177 GtkWidget *new_call_view = NULL;
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400178 QVariant state = CallModel::instance()->data(idx_selected, static_cast<int>(Call::Role::LifeCycleState));
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500179
180 /* check what the current state is vs what is displayed */
181
182 /* FIXME: change when fixed
183 * switch(state.value<Call::LifeCycleState>()) { */
184 Call::LifeCycleState lifecyclestate = (Call::LifeCycleState)state.toUInt();
185 switch(lifecyclestate) {
186 case Call::LifeCycleState::INITIALIZATION:
187 /* LifeCycleState cannot go backwards, so it should not be possible
188 * that the call is displayed as current (meaning that its in progress)
189 * but have the state 'initialization' */
190 if (IS_CURRENT_CALL_VIEW(old_call_view))
191 g_warning("call displayed as current, but is in state of initialization");
192 break;
193 case Call::LifeCycleState::PROGRESS:
194 if (IS_INCOMING_CALL_VIEW(old_call_view)) {
195 /* change from incoming to current */
196 new_call_view = current_call_view_new();
197 current_call_view_set_call_info(CURRENT_CALL_VIEW(new_call_view), idx_selected);
198 /* use the pointer of the call as a unique name */
199 char* new_call_view_name = NULL;
200 new_call_view_name = g_strdup_printf("%p_current", (void *)CallModel::instance()->getCall(idx_selected));
201 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), new_call_view, new_call_view_name);
202 g_free(new_call_view_name);
203 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
204 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), new_call_view);
205 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
206 }
207 break;
208 case Call::LifeCycleState::FINISHED:
209 /* do nothing, either call view is valid for this state */
210 break;
211 case Call::LifeCycleState::COUNT__:
212 g_warning("LifeCycleState should never be COUNT");
213 break;
214 }
215
216 /* check if we changed the visible child */
217 GtkWidget *current_call_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_main_view));
218 if (current_call_view != old_call_view && old_call_view != NULL) {
219 /* if the previous child was a call view, then remove it from
220 * the stack; removing it should destory it since there should not
221 * be any other references to it */
222 if (IS_INCOMING_CALL_VIEW(old_call_view) || IS_CURRENT_CALL_VIEW(old_call_view)) {
223 gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), old_call_view);
224 }
Stepan Salenikovich69771842015-02-24 18:11:45 -0500225 }
226 }
227}
228
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500229static void
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500230search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
231{
232 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
233
234 const gchar *number = gtk_entry_get_text(GTK_ENTRY(priv->search_entry));
235
236 if (number && strlen(number) > 0) {
237 g_debug("dialing to number: %s", number);
238 Call *call = CallModel::instance()->dialingCall();
239 call->setDialNumber(number);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500240 call->performAction(Call::Action::ACCEPT);
241
242 /* make this the currently selected call */
243 QModelIndex idx = CallModel::instance()->getIndex(call);
244 CallModel::instance()->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
245 }
246}
247
248static void
Stepan Salenikovich15142182015-03-11 17:15:26 -0400249call_history_item(GtkTreeView *tree_view,
250 GtkTreePath *path,
251 G_GNUC_UNUSED GtkTreeViewColumn *column,
252 G_GNUC_UNUSED gpointer user_data)
253{
254 GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
255
256 /* get iter */
257 GtkTreeIter iter;
258 if (gtk_tree_model_get_iter(model, &iter, path)) {
259 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
260
261 QVariant contact_method = idx.data(static_cast<int>(Call::Role::ContactMethod));
262 /* create new call */
263 if (contact_method.value<ContactMethod*>()) {
264 Call *call = CallModel::instance()->dialingCall();
265 call->setDialNumber(contact_method.value<ContactMethod*>());
266 call->performAction(Call::Action::ACCEPT);
267
268 /* make this the currently selected call */
269 QModelIndex call_idx = CallModel::instance()->getIndex(call);
270 CallModel::instance()->selectionModel()->setCurrentIndex(call_idx, QItemSelectionModel::ClearAndSelect);
271 } else
272 g_warning("contact method is empty");
273 }
274}
275
276static void
277navbutton_contacts_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
278{
279 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
280 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
281
282 if (gtk_toggle_button_get_active(navbutton)) {
283
284 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
285
286 if (visible) {
287 /* contacts is left of both history and presence, so always slide right to show it */
288 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
289 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
290 } else {
291 g_warning("should always have a visible child in the nav stack");
292 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
293 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_CONTACTS);
294 }
295 }
296}
297
298static void
299navbutton_presence_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
300{
301 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
302 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
303
304 if (gtk_toggle_button_get_active(navbutton)) {
305
306 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
307 if (visible) {
308 /* presence is right of both history and contacts, so always slide left to show it */
309 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
310 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
311 } else {
312 g_warning("should always have a visible child in the nav stack");
313 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
314 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_PRESENCE);
315 }
316 }
317}
318
319static void
320navbutton_history_toggled(GtkToggleButton *navbutton, RingMainWindow *win)
321{
322 g_return_if_fail(IS_RING_MAIN_WINDOW(win));
323 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
324
325 if (gtk_toggle_button_get_active(navbutton)) {
326
327 const gchar *visible = gtk_stack_get_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence));
328 if (visible) {
329 if (strcmp(visible, VIEW_CONTACTS) == 0) {
330 /* history is right of contacts, so slide left to show it */
331 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
332 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
333 } else if (strcmp(visible, VIEW_PRESENCE) == 0) {
334 /* history is left of presence, so slide right to show it */
335 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
336 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
337 }
338 } else {
339 g_warning("should always have a visible child in the nav stack");
340 gtk_stack_set_transition_type(GTK_STACK(priv->stack_contacts_history_presence), GTK_STACK_TRANSITION_TYPE_NONE);
341 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_contacts_history_presence), VIEW_HISTORY);
342 }
343 }
344}
345
346static void
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500347ring_main_window_init(RingMainWindow *win)
348{
349 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
350 gtk_widget_init_template(GTK_WIDGET(win));
351
352 /* set window icon */
353 GError *error = NULL;
354 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
355 if (icon == NULL) {
356 g_debug("Could not load icon: %s", error->message);
357 g_error_free(error);
358 } else
359 gtk_window_set_icon(GTK_WINDOW(win), icon);
360
361 /* set menu icon */
362 GdkPixbuf* ring_gears = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
363 -1, 22, TRUE, &error);
364 if (ring_gears == NULL) {
365 g_debug("Could not load icon: %s", error->message);
366 g_error_free(error);
367 } else
368 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->gears_image), ring_gears);
369
370 /* gears menu */
371 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
372 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
373 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->gears), menu);
374 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500375
376 /* call model */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500377 GtkQTreeModel *call_model;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500378 GtkCellRenderer *renderer;
379 GtkTreeViewColumn *column;
380
Stepan Salenikovich69771842015-02-24 18:11:45 -0500381 call_model = gtk_q_tree_model_new(CallModel::instance(), 4,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500382 Call::Role::Name, G_TYPE_STRING,
383 Call::Role::Number, G_TYPE_STRING,
384 Call::Role::Length, G_TYPE_STRING,
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400385 Call::Role::State, G_TYPE_STRING);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500386 gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_call), GTK_TREE_MODEL(call_model));
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500387
388 renderer = gtk_cell_renderer_text_new();
389 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400390 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500391 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
392
393 renderer = gtk_cell_renderer_text_new();
394 column = gtk_tree_view_column_new_with_attributes("Duration", renderer, "text", 2, NULL);
395 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
396
Stepan Salenikovich69771842015-02-24 18:11:45 -0500397 /* connect signals to and from UserActionModel to sync selection betwee
398 * the QModel and the GtkTreeView */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500399 QObject::connect(
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500400 CallModel::instance()->selectionModel(),
Stepan Salenikovich69771842015-02-24 18:11:45 -0500401 &QItemSelectionModel::currentChanged,
402 [=](const QModelIndex & current, const QModelIndex & previous) {
403 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
404
405 /* first unselect the previous */
406 if (previous.isValid()) {
407 GtkTreeIter old_iter;
408 if (gtk_q_tree_model_source_index_to_iter(call_model, previous, &old_iter)) {
409 gtk_tree_selection_unselect_iter(selection, &old_iter);
410 } else {
411 g_warning("Trying to unselect invalid GtkTreeIter");
412 }
413 }
414
415 /* select the current */
416 if (current.isValid()) {
417 GtkTreeIter new_iter;
418 if (gtk_q_tree_model_source_index_to_iter(call_model, current, &new_iter)) {
419 gtk_tree_selection_select_iter(selection, &new_iter);
420 } else {
421 g_warning("SelectionModel of CallModel changed to invalid QModelIndex?");
422 }
423 }
424 }
425 );
426
427 GtkTreeSelection *call_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500428 g_signal_connect(call_selection, "changed", G_CALLBACK(update_call_model_selection), NULL);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500429
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500430 /* connect to call state changes to update relevant view(s) */
431 QObject::connect(
432 CallModel::instance(),
433 &CallModel::callStateChanged,
434 [=](Call* call, G_GNUC_UNUSED Call::State previousState) {
435 call_state_changed(call, win);
436 }
437 );
438
Stepan Salenikovich15142182015-03-11 17:15:26 -0400439 /* contacts view/model */
440 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
441 GtkWidget *treeview_contacts = gtk_tree_view_new();
442 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_contacts), FALSE);
443 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_contacts);
444 gtk_widget_show_all(scrolled_window);
445 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
446 scrolled_window,
447 VIEW_CONTACTS);
448
449 /* history view/model */
450 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
451 GtkWidget *treeview_history = gtk_tree_view_new();
452 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_history), FALSE);
453 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_history);
454 gtk_widget_show_all(scrolled_window);
455 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
456 scrolled_window,
457 VIEW_HISTORY);
458 /* TODO: make this linked to the client settings so that the last shown view is the same on startup */
459 gtk_stack_set_visible_child(GTK_STACK(priv->stack_contacts_history_presence),
460 scrolled_window);
461
462 GtkQTreeModel *history_model;
463
464 history_model = gtk_q_tree_model_new(HistoryModel::instance(), 4,
465 Qt::DisplayRole, G_TYPE_STRING,
466 Call::Role::Number, G_TYPE_STRING,
467 Call::Role::FormattedDate, G_TYPE_STRING,
468 Call::Role::Direction, G_TYPE_INT);
469 gtk_tree_view_set_model( GTK_TREE_VIEW(treeview_history), GTK_TREE_MODEL(history_model) );
470
471 GtkCellArea *cellarea_name = gtk_cell_area_box_new();
472 gtk_orientable_set_orientation(GTK_ORIENTABLE(cellarea_name), GTK_ORIENTATION_HORIZONTAL);
473 GtkCellRenderer *cellrenderer_name = gtk_cell_renderer_text_new();
474 GtkCellRenderer *cellrenderer_number = gtk_cell_renderer_text_new();
475
476 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(cellarea_name),
477 cellrenderer_name,
478 FALSE, /* expand */
479 FALSE, /* align in adjacent rows */
480 FALSE); /* fixed size in all rows */
481
482 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(cellarea_name),
483 cellrenderer_number,
484 FALSE, /* expand */
485 FALSE, /* align in adjacent rows */
486 FALSE); /* fixed size in all rows */
487
488 column = gtk_tree_view_column_new_with_area(cellarea_name);
489 g_object_set(G_OBJECT(column), "max-width", 250, NULL);
490
491 gtk_tree_view_column_add_attribute(column, cellrenderer_name, "text", 0);
492 gtk_tree_view_column_add_attribute(column, cellrenderer_number, "text", 1);
493 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_history), column);
494
495 renderer = gtk_cell_renderer_text_new ();
496 column = gtk_tree_view_column_new_with_attributes ("Date", renderer, "text", 2, NULL);
497 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview_history), column);
498
499 gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview_history));
500
501 g_signal_connect(treeview_history, "row-activated", G_CALLBACK(call_history_item), NULL);
502
503 /* presence view/model */
504 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
505 GtkWidget *treeview_presence = gtk_tree_view_new();
506 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_presence), FALSE);
507 gtk_container_add(GTK_CONTAINER(scrolled_window), treeview_presence);
508 gtk_widget_show_all(scrolled_window);
509 gtk_stack_add_named(GTK_STACK(priv->stack_contacts_history_presence),
510 scrolled_window,
511 VIEW_PRESENCE);
512
513 /* connect signals to change the contacts/history/presence stack view */
514 g_signal_connect(priv->radiobutton_contacts, "toggled", G_CALLBACK(navbutton_contacts_toggled), win);
515 g_signal_connect(priv->radiobutton_history, "toggled", G_CALLBACK(navbutton_history_toggled), win);
516 g_signal_connect(priv->radiobutton_presence, "toggled", G_CALLBACK(navbutton_presence_toggled), win);
517
Stepan Salenikovich69771842015-02-24 18:11:45 -0500518 /* TODO: replace stack paceholder view */
519 GtkWidget *placeholder_view = gtk_tree_view_new();
520 gtk_widget_show(placeholder_view);
521 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), placeholder_view, DEFAULT_VIEW_NAME);
522
523 /* connect signals */
524 g_signal_connect(call_selection, "changed", G_CALLBACK(call_selection_changed), win);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500525 g_signal_connect(priv->button_placecall, "clicked", G_CALLBACK(search_entry_placecall), win);
526 g_signal_connect(priv->search_entry, "activate", G_CALLBACK(search_entry_placecall), win);
527
528 /* style of search entry */
529 gtk_widget_override_font(priv->search_entry, pango_font_description_from_string("monospace 15"));
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500530
531 /* connect to incoming call and focus */
532 QObject::connect(
533 CallModel::instance(),
534 &CallModel::incomingCall,
535 [=](Call* call) {
536 CallModel::instance()->selectionModel()->setCurrentIndex(
537 CallModel::instance()->getIndex(call), QItemSelectionModel::ClearAndSelect);
538 }
539 );
540}
541
542static void
543ring_main_window_finalize(GObject *object)
544{
545 G_OBJECT_CLASS(ring_main_window_parent_class)->finalize(object);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500546}
547
548static void
549ring_main_window_class_init(RingMainWindowClass *klass)
550{
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500551 G_OBJECT_CLASS(klass)->finalize = ring_main_window_finalize;
552
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500553 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
554 "/cx/ring/RingGnome/ringmainwindow.ui");
555
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500556 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, treeview_call);
Stepan Salenikovich15142182015-03-11 17:15:26 -0400557 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_contacts_history_presence);
558 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_contacts);
559 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_history);
560 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_presence);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500561 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, gears);
562 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, gears_image);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -0500563 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500564 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500565 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500566}
567
568GtkWidget *
569ring_main_window_new (GtkApplication *app)
570{
571 return (GtkWidget *)g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
572}