blob: d4456eeabe760d61f240d2fa930fcda0c67cec7d [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>
37#include <QItemSelectionModel>
38#include "incomingcallview.h"
39
40#define DEFAULT_VIEW_NAME "placeholder"
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050041
42struct _RingMainWindow
43{
44 GtkApplicationWindow parent;
45};
46
47struct _RingMainWindowClass
48{
49 GtkApplicationWindowClass parent_class;
50};
51
52typedef struct _RingMainWindowPrivate RingMainWindowPrivate;
53
54struct _RingMainWindowPrivate
55{
56 GtkWidget *gears;
57 GtkWidget *gears_image;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050058 GtkWidget *treeview_call;
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -050059 GtkWidget *search_entry;
Stepan Salenikovich69771842015-02-24 18:11:45 -050060 GtkWidget *stack_main_view;
Stepan Salenikovich434b88f2015-02-19 17:49:08 -050061};
62
63G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
64
65#define RING_MAIN_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RING_MAIN_WINDOW_TYPE, RingMainWindowPrivate))
66
Stepan Salenikovich69771842015-02-24 18:11:45 -050067static QModelIndex
68get_index_from_selection(GtkTreeSelection *selection)
69{
70 GtkTreeIter iter;
71 GtkTreeModel *model = NULL;
72
73 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
74 return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
75 } else {
76 return QModelIndex();
77 }
78}
79
80static void
81update_call_model_selection(GtkTreeSelection *selection, gpointer win)
82{
83 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
84 GtkQTreeModel *call_model = GTK_Q_TREE_MODEL(gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview_call)));
85 CallModel *call_qmodel = (CallModel *)gtk_q_tree_model_get_qmodel(call_model);
86 QModelIndex current = get_index_from_selection(selection);
87 if (current.isValid())
88 call_qmodel->selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect);
89 else
90 call_qmodel->selectionModel()->clearCurrentIndex();
91}
92
93static void
94call_selection_changed(GtkTreeSelection *selection, gpointer win)
95{
96 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
97
98 /* get the current visible stack child */
99 GtkWidget *child_old = gtk_stack_get_visible_child(GTK_STACK(priv->stack_main_view));
100
101 QModelIndex idx = get_index_from_selection(selection);
102 if (idx.isValid()) {
103 QVariant state = idx.model()->data(idx, Call::Role::CallState);
104
105 switch(state.value<Call::State>()) {
106 case Call::State::INCOMING:
107 {
108 QVariant name = idx.model()->data(idx, Call::Role::Name);
109 QByteArray ba_name = name.toString().toLocal8Bit();
110 GtkWidget *new_call_view = incoming_call_view_new();
111 incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_call_view), idx);
112 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), new_call_view, ba_name.data());
113 gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), new_call_view);
114 }
115 break;
116
117 /* TODO: implement view(s) for other call states */
118 case Call::State::RINGING:
119 case Call::State::CURRENT:
120 case Call::State::DIALING:
121 case Call::State::HOLD:
122 case Call::State::FAILURE:
123 case Call::State::BUSY:
124 case Call::State::TRANSFERRED:
125 case Call::State::TRANSF_HOLD:
126 case Call::State::OVER:
127 case Call::State::ERROR:
128 case Call::State::CONFERENCE:
129 case Call::State::CONFERENCE_HOLD:
130 case Call::State::INITIALIZATION:
131 case Call::State::COUNT__:
132 break;
133 }
134 } else {
135 /* nothing selected in the call model, so show the default screen */
136
137 /* TODO: replace stack paceholder view */
138 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
139 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), DEFAULT_VIEW_NAME);
140 gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
141
142 }
143
144 /* check if we changed the visible child */
145 GtkWidget *child_current = gtk_stack_get_visible_child(GTK_STACK(priv->stack_main_view));
146 if (child_current != child_old && child_old != NULL) {
147 /* if the previous child was an "incoming call", then remove it from
148 * the stack; removing it should destory it since there should not
149 * be any other references to it */
150 if (IS_INCOMING_CALL_VIEW(child_old)) {
151 gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), child_old);
152 }
153 }
154}
155
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500156static void
157ring_main_window_init(RingMainWindow *win)
158{
159 RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
160 gtk_widget_init_template(GTK_WIDGET(win));
161
162 /* set window icon */
163 GError *error = NULL;
164 GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/cx/ring/RingGnome/ring-symbol-blue", &error);
165 if (icon == NULL) {
166 g_debug("Could not load icon: %s", error->message);
167 g_error_free(error);
168 } else
169 gtk_window_set_icon(GTK_WINDOW(win), icon);
170
171 /* set menu icon */
172 GdkPixbuf* ring_gears = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
173 -1, 22, TRUE, &error);
174 if (ring_gears == NULL) {
175 g_debug("Could not load icon: %s", error->message);
176 g_error_free(error);
177 } else
178 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->gears_image), ring_gears);
179
180 /* gears menu */
181 GtkBuilder *builder = gtk_builder_new_from_resource("/cx/ring/RingGnome/ringgearsmenu.ui");
182 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
183 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(priv->gears), menu);
184 g_object_unref(builder);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500185
186 /* call model */
Stepan Salenikovich69771842015-02-24 18:11:45 -0500187 GtkQTreeModel *call_model;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500188 GtkCellRenderer *renderer;
189 GtkTreeViewColumn *column;
190
Stepan Salenikovich69771842015-02-24 18:11:45 -0500191 call_model = gtk_q_tree_model_new(CallModel::instance(), 4,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500192 Call::Role::Name, G_TYPE_STRING,
193 Call::Role::Number, G_TYPE_STRING,
194 Call::Role::Length, G_TYPE_STRING,
195 Call::Role::CallState, G_TYPE_STRING);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500196 gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_call), GTK_TREE_MODEL(call_model));
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500197
198 renderer = gtk_cell_renderer_text_new();
199 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500200 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
201
202 renderer = gtk_cell_renderer_text_new();
203 column = gtk_tree_view_column_new_with_attributes("Duration", renderer, "text", 2, NULL);
204 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_call), column);
205
Stepan Salenikovich69771842015-02-24 18:11:45 -0500206 /* connect signals to and from UserActionModel to sync selection betwee
207 * the QModel and the GtkTreeView */
208 CallModel *call_qmodel = (CallModel *)gtk_q_tree_model_get_qmodel(call_model);
209
210 QObject::connect(
211 call_qmodel->selectionModel(),
212 &QItemSelectionModel::currentChanged,
213 [=](const QModelIndex & current, const QModelIndex & previous) {
214 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
215
216 /* first unselect the previous */
217 if (previous.isValid()) {
218 GtkTreeIter old_iter;
219 if (gtk_q_tree_model_source_index_to_iter(call_model, previous, &old_iter)) {
220 gtk_tree_selection_unselect_iter(selection, &old_iter);
221 } else {
222 g_warning("Trying to unselect invalid GtkTreeIter");
223 }
224 }
225
226 /* select the current */
227 if (current.isValid()) {
228 GtkTreeIter new_iter;
229 if (gtk_q_tree_model_source_index_to_iter(call_model, current, &new_iter)) {
230 gtk_tree_selection_select_iter(selection, &new_iter);
231 } else {
232 g_warning("SelectionModel of CallModel changed to invalid QModelIndex?");
233 }
234 }
235 }
236 );
237
238 GtkTreeSelection *call_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_call));
239 g_signal_connect(call_selection, "changed", G_CALLBACK(update_call_model_selection), win);
240
241 /* TODO: replace stack paceholder view */
242 GtkWidget *placeholder_view = gtk_tree_view_new();
243 gtk_widget_show(placeholder_view);
244 gtk_stack_add_named(GTK_STACK(priv->stack_main_view), placeholder_view, DEFAULT_VIEW_NAME);
245
246 /* connect signals */
247 g_signal_connect(call_selection, "changed", G_CALLBACK(call_selection_changed), win);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500248}
249
250static void
251ring_main_window_class_init(RingMainWindowClass *klass)
252{
253 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
254 "/cx/ring/RingGnome/ringmainwindow.ui");
255
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500256 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, treeview_call);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500257 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, gears);
258 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, gears_image);
Stepan Salenikovich45d25aa2015-02-20 17:39:09 -0500259 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, search_entry);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500260 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, stack_main_view);
Stepan Salenikovich434b88f2015-02-19 17:49:08 -0500261}
262
263GtkWidget *
264ring_main_window_new (GtkApplication *app)
265{
266 return (GtkWidget *)g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
267}