blob: 9e3a6553a513a0869b84033f565f2e111cf42c7a [file] [log] [blame]
Stepan Salenikovich9816a942015-04-22 17:49:16 -04001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Stepan Salenikovich9816a942015-04-22 17:49:16 -04003 * 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 Salenikovich9816a942015-04-22 17:49:16 -040018 */
19
20#include "historyview.h"
21
22#include <gtk/gtk.h>
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040023#include <glib/gi18n.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040024#include "models/gtkqsortfiltertreemodel.h"
25#include <categorizedhistorymodel.h>
26#include <QtCore/QSortFilterProxyModel>
27#include <personmodel.h>
28#include "utils/calling.h"
29#include <memory>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040030#include <globalinstances.h>
31#include "native/pixbufmanipulator.h"
Stepan Salenikovich9816a942015-04-22 17:49:16 -040032#include "defines.h"
33#include "utils/models.h"
34#include <contactmethod.h>
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -040035#include <QtCore/QDateTime> // for date time formatting
Stepan Salenikovich9d294492015-05-14 16:34:24 -040036#include <QtCore/QItemSelectionModel>
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -040037#include "utils/menus.h"
Stepan Salenikovich9816a942015-04-22 17:49:16 -040038
39struct _HistoryView
40{
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -040041 GtkTreeView parent;
Stepan Salenikovich9816a942015-04-22 17:49:16 -040042};
43
44struct _HistoryViewClass
45{
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -040046 GtkTreeViewClass parent_class;
Stepan Salenikovich9816a942015-04-22 17:49:16 -040047};
48
49typedef struct _HistoryViewPrivate HistoryViewPrivate;
50
51struct _HistoryViewPrivate
52{
Stepan Salenikovich9d294492015-05-14 16:34:24 -040053 CategorizedHistoryModel::SortedProxy *q_sorted_proxy;
54 QMetaObject::Connection category_changed;
Stepan Salenikovich9816a942015-04-22 17:49:16 -040055};
56
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -040057G_DEFINE_TYPE_WITH_PRIVATE(HistoryView, history_view, GTK_TYPE_TREE_VIEW);
Stepan Salenikovich9816a942015-04-22 17:49:16 -040058
59#define HISTORY_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HISTORY_VIEW_TYPE, HistoryViewPrivate))
60
61static void
62render_call_direction(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
63 GtkCellRenderer *cell,
64 GtkTreeModel *tree_model,
65 GtkTreeIter *iter,
66 G_GNUC_UNUSED gpointer data)
67{
68 /* check if this is a top level item (the fuzzy date item),
69 * in this case we don't want to show a call direction */
70 gchar *render_direction = NULL;
71 GtkTreeIter parent;
72 if (gtk_tree_model_iter_parent(tree_model, &parent, iter)) {
73 /* get direction and missed values */
74 GValue value = G_VALUE_INIT;
75 gtk_tree_model_get_value(tree_model, iter, 3, &value);
76 Call::Direction direction = (Call::Direction)g_value_get_int(&value);
77 g_value_unset(&value);
78
79 gtk_tree_model_get_value(tree_model, iter, 4, &value);
80 gboolean missed = g_value_get_boolean(&value);
81 g_value_unset(&value);
82
83 switch (direction) {
84 case Call::Direction::INCOMING:
85 if (missed)
86 render_direction = g_strdup_printf("<span fgcolor=\"red\" font=\"monospace\">&#8601;</span>");
87 else
88 render_direction = g_strdup_printf("<span fgcolor=\"green\" font=\"monospace\">&#8601;</span>");
89 break;
90 case Call::Direction::OUTGOING:
91 if (missed)
92 render_direction = g_strdup_printf("<span fgcolor=\"red\" font=\"monospace\">&#8599;</span>");
93 else
94 render_direction = g_strdup_printf("<span fgcolor=\"green\" font=\"monospace\">&#8599;</span>");
95 break;
96 }
97 }
98 g_object_set(G_OBJECT(cell), "markup", render_direction, NULL);
99 g_free(render_direction);
100}
101
102static void
103activate_history_item(GtkTreeView *tree_view,
104 GtkTreePath *path,
105 G_GNUC_UNUSED GtkTreeViewColumn *column,
106 G_GNUC_UNUSED gpointer user_data)
107{
108 GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
109
110 /* expand / collapse row */
111 if (gtk_tree_view_row_expanded(tree_view, path))
112 gtk_tree_view_collapse_row(tree_view, path);
113 else
114 gtk_tree_view_expand_row(tree_view, path, FALSE);
115
116 /* get iter */
117 GtkTreeIter iter;
118 if (gtk_tree_model_get_iter(model, &iter, path)) {
119 QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(model), &iter);
120
121 QVariant contact_method = idx.data(static_cast<int>(Call::Role::ContactMethod));
122 /* create new call */
123 if (contact_method.value<ContactMethod*>()) {
124 place_new_call(contact_method.value<ContactMethod*>());
125 }
126 }
127}
128
129static void
130copy_history_item(G_GNUC_UNUSED GtkWidget *item, GtkTreeView *treeview)
131{
132 GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
133 QModelIndex idx = get_index_from_selection(selection);
134
135 if (idx.isValid()) {
136 GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
137
138 const gchar* number = idx.data(static_cast<int>(Call::Role::Number)).toString().toUtf8().constData();
139 gtk_clipboard_set_text(clip, number, -1);
140 }
141}
142
143/* TODO: can't seem to delete just one item for now, add when supported in backend
144 * static void
145 * delete_history_item(G_GNUC_UNUSED GtkWidget *item, GtkTreeView *treeview)
146 * {
147 * GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
148 * QModelIndex idx = get_index_from_selection(selection);
149 *
150 * if (idx.isValid()) {
151 * g_debug("deleting history item");
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400152 * CategorizedHistoryModel::instance().removeRow(idx.row(), idx.parent());
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400153 * }
154 * }
155 */
156
157static gboolean
158history_popup_menu(G_GNUC_UNUSED GtkWidget *widget, GdkEventButton *event, GtkTreeView *treeview)
159{
160 /* build popup menu when right clicking on history item
161 * user should be able to copy the "number",
162 * delete history item or all of the history,
163 * and eventualy add the number to a contact
164 */
165
166 /* check for right click */
167 if (event->button != BUTTON_RIGHT_CLICK || event->type != GDK_BUTTON_PRESS)
168 return FALSE;
169
170 GtkWidget *menu = gtk_menu_new();
171
172 /* copy */
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400173 GtkWidget *item = gtk_menu_item_new_with_mnemonic(_("_Copy"));
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400174 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
175 g_signal_connect(item, "activate", G_CALLBACK(copy_history_item), treeview);
176
177 /* TODO: delete history entry
178 * gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
179 * item = gtk_menu_item_new_with_mnemonic("_Delete entry");
180 * gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
181 * g_signal_connect(item, "activate", G_CALLBACK(delete_history_item), treeview);
182 */
183
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400184 /* check if the selected item is a call, if so get the contact method and
185 * check if it is already linked to a person, if not, then offer to either
186 * add to a new or existing contact */
187 auto selection = gtk_tree_view_get_selection(treeview);
188 const auto& idx = get_index_from_selection(selection);
189 const auto& var_c = idx.data(static_cast<int>(Call::Role::Object));
190 if (idx.isValid() && var_c.isValid()) {
191 if (auto call = var_c.value<Call *>()) {
192 auto contactmethod = call->peerContactMethod();
193 if (!contact_method_has_contact(contactmethod)) {
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400194 GtkTreeIter iter;
195 GtkTreeModel *model;
196 gtk_tree_selection_get_selected(selection, &model, &iter);
197 auto path = gtk_tree_model_get_path(model, &iter);
198 auto column = gtk_tree_view_get_column(treeview, 0);
199 GdkRectangle rect;
200 gtk_tree_view_get_cell_area(treeview, path, column, &rect);
201 gtk_tree_view_convert_bin_window_to_widget_coords(treeview, rect.x, rect.y, &rect.x, &rect.y);
202 gtk_tree_path_free(path);
203 auto add_to = menu_item_add_to_contact(contactmethod, GTK_WIDGET(treeview), &rect);
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400204 gtk_menu_shell_append(GTK_MENU_SHELL(menu), add_to);
205 }
206 }
207 }
208
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400209 /* show menu */
210 gtk_widget_show_all(menu);
211 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
212
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400213 return TRUE;
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400214}
215
216static void
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400217render_call_photo(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
218 GtkCellRenderer *cell,
219 GtkTreeModel *tree_model,
220 GtkTreeIter *iter,
221 G_GNUC_UNUSED gpointer data)
222{
223 /* check if this is a top level item (category),
224 * in this case we don't want to show a photo */
225 GtkTreePath *path = gtk_tree_model_get_path(tree_model, iter);
226 int depth = gtk_tree_path_get_depth(path);
227 gtk_tree_path_free(path);
228 if (depth == 2) {
229 /* get person */
230 QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(tree_model), iter);
231 if (idx.isValid()) {
232 QVariant var_c = idx.data(static_cast<int>(Call::Role::Object));
233 Call *c = var_c.value<Call *>();
234 /* get photo */
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400235 QVariant var_p = GlobalInstances::pixmapManipulator().callPhoto(c, QSize(50, 50), false);
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400236 std::shared_ptr<GdkPixbuf> photo = var_p.value<std::shared_ptr<GdkPixbuf>>();
237 g_object_set(G_OBJECT(cell), "pixbuf", photo.get(), NULL);
238 return;
239 }
240 }
241
242 /* otherwise, make sure its an empty pixbuf */
243 g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
244}
245
246static void
247render_name_and_contact_method(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
248 GtkCellRenderer *cell,
249 GtkTreeModel *tree_model,
250 GtkTreeIter *iter,
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400251 GtkTreeView *treeview)
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400252{
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400253 // check if this iter is selected
254 gboolean is_selected = FALSE;
255 if (GTK_IS_TREE_VIEW(treeview)) {
256 auto selection = gtk_tree_view_get_selection(treeview);
257 is_selected = gtk_tree_selection_iter_is_selected(selection, iter);
258 }
259
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400260 /**
261 * If call, show the name and the contact method (number) underneath;
262 * otherwise just display the category.
263 */
264 GtkTreePath *path = gtk_tree_model_get_path(tree_model, iter);
265 int depth = gtk_tree_path_get_depth(path);
266 gtk_tree_path_free(path);
267
268 gchar *text = NULL;
269
270 QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(tree_model), iter);
271 if (idx.isValid()) {
272 QVariant var = idx.data(Qt::DisplayRole);
273 if (depth == 1) {
274 /* category */
275 text = g_strdup_printf("<b>%s</b>", var.value<QString>().toUtf8().constData());
276 } else if (depth == 2) {
277 /* call item */
278 QVariant var_name = idx.data(static_cast<int>(Call::Role::Name));
279 QVariant var_number = idx.data(static_cast<int>(Call::Role::Number));
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400280
281 /* we want the color of the status text to be the default color if this iter is
282 * selected so that the treeview is able to invert it against the selection color */
283 if (is_selected) {
284 text = g_strdup_printf("%s\n %s",
285 var_name.value<QString>().toUtf8().constData(),
286 var_number.value<QString>().toUtf8().constData());
287 } else {
288 text = g_strdup_printf("%s\n <span fgcolor=\"gray\">%s</span>",
289 var_name.value<QString>().toUtf8().constData(),
290 var_number.value<QString>().toUtf8().constData());
291 }
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400292 }
293 }
294
295 g_object_set(G_OBJECT(cell), "markup", text, NULL);
296 g_free(text);
297}
298
299static void
Stepan Salenikovich501a7f82015-12-23 11:06:47 -0500300render_date_time(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
301 GtkCellRenderer *cell,
302 GtkTreeModel *tree_model,
303 GtkTreeIter *iter,
304 GtkTreeView *treeview)
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400305{
Stepan Salenikovich501a7f82015-12-23 11:06:47 -0500306 // check if this iter is selected
307 gboolean is_selected = FALSE;
308 if (GTK_IS_TREE_VIEW(treeview)) {
309 auto selection = gtk_tree_view_get_selection(treeview);
310 is_selected = gtk_tree_selection_iter_is_selected(selection, iter);
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400311 }
312
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400313 gchar *text = NULL;
314
315 QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(tree_model), iter);
Stepan Salenikovich501a7f82015-12-23 11:06:47 -0500316 QVariant var_d = idx.data(static_cast<int>(Call::Role::DateTime));
317 if (idx.isValid() && var_d.isValid()) {
Stepan Salenikovich803562b2015-12-23 10:51:01 -0500318 QDateTime date_time = var_d.value<QDateTime>();
Stepan Salenikovich501a7f82015-12-23 11:06:47 -0500319
320 /* we want the color of the text to be the default color if this iter is
321 * selected so that the treeview is able to invert it against the selection color */
322 if (is_selected) {
323 text = g_strdup_printf("%s\n%s",
324 date_time.time().toString().toUtf8().constData(),
325 date_time.date().toString().toUtf8().constData()
326 );
327 } else {
328 text = g_strdup_printf("%s\n<span fgcolor=\"gray\">%s</span>",
329 date_time.time().toString().toUtf8().constData(),
330 date_time.date().toString().toUtf8().constData()
331 );
332 }
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400333 }
334
335 g_object_set(G_OBJECT(cell), "markup", text, NULL);
336 g_free(text);
337}
338
339static void
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400340history_view_init(HistoryView *self)
341{
342 HistoryViewPrivate *priv = HISTORY_VIEW_GET_PRIVATE(self);
343
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400344 /* make headers visible to allow column resizing */
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -0400345 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self), TRUE);
346 /* disable default search, we will handle it ourselves;
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400347 * otherwise the search steals input focus on key presses */
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -0400348 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(self), FALSE);
Stepan Salenikovichb01d7362015-04-27 23:02:00 -0400349
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400350 /* instantiate history proxy model */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400351 priv->q_sorted_proxy = &CategorizedHistoryModel::SortedProxy::instance();
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400352
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400353 /* select default category (the first one, which is by date) */
354 priv->q_sorted_proxy->categorySelectionModel()->setCurrentIndex(
355 priv->q_sorted_proxy->categoryModel()->index(0, 0),
356 QItemSelectionModel::ClearAndSelect);
Stepan Salenikovich803562b2015-12-23 10:51:01 -0500357 /* make sure it is sorted so that newest calls are at the top */
358 priv->q_sorted_proxy->model()->sort(0, Qt::AscendingOrder);
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400359
360 GtkQSortFilterTreeModel *history_model = gtk_q_sort_filter_tree_model_new(
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400361 priv->q_sorted_proxy->model(),
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400362 5,
363 Qt::DisplayRole, G_TYPE_STRING,
364 Call::Role::Number, G_TYPE_STRING,
365 Call::Role::FormattedDate, G_TYPE_STRING,
366 Call::Role::Direction, G_TYPE_INT,
367 Call::Role::Missed, G_TYPE_BOOLEAN);
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -0400368 gtk_tree_view_set_model( GTK_TREE_VIEW(self), GTK_TREE_MODEL(history_model) );
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400369
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400370 /* call direction, photo, name/number column */
371 GtkCellArea *area = gtk_cell_area_box_new();
372 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_area(area);
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400373 gtk_tree_view_column_set_title(column, C_("Call history", "Call"));
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400374
375 /* call direction */
376 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400377 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400378
379 /* display the call direction with arrows */
380 gtk_tree_view_column_set_cell_data_func(
381 column,
382 renderer,
383 (GtkTreeCellDataFunc)render_call_direction,
384 NULL,
385 NULL);
386
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400387 /* photo renderer */
388 renderer = gtk_cell_renderer_pixbuf_new();
389 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400390
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400391 /* get the photo */
392 gtk_tree_view_column_set_cell_data_func(
393 column,
394 renderer,
395 (GtkTreeCellDataFunc)render_call_photo,
396 NULL,
397 NULL);
398
399 /* name and contact method renderer */
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400400 renderer = gtk_cell_renderer_text_new();
401 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400402 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
403
404 gtk_tree_view_column_set_cell_data_func(
405 column,
406 renderer,
407 (GtkTreeCellDataFunc)render_name_and_contact_method,
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -0400408 self,
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400409 NULL);
410
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -0400411 gtk_tree_view_append_column(GTK_TREE_VIEW(self), column);
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400412 gtk_tree_view_column_set_resizable(column, TRUE);
Stepan Salenikoviche7c4e282015-06-11 17:22:08 -0400413 gtk_tree_view_column_set_expand(column, TRUE);
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400414
Stepan Salenikovich501a7f82015-12-23 11:06:47 -0500415 /* date time column */
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400416 area = gtk_cell_area_box_new();
417 column = gtk_tree_view_column_new_with_area(area);
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400418 gtk_tree_view_column_set_title(column, C_("Call history", "Date"));
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400419
Stepan Salenikovich501a7f82015-12-23 11:06:47 -0500420 /* date time renderer */
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400421 renderer = gtk_cell_renderer_text_new ();
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400422 gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
423 /* format the time*/
424 gtk_tree_view_column_set_cell_data_func(
425 column,
426 renderer,
Stepan Salenikovich501a7f82015-12-23 11:06:47 -0500427 (GtkTreeCellDataFunc)render_date_time,
428 self,
Stepan Salenikovich82b1acf2015-05-12 12:33:51 -0400429 NULL);
430
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -0400431 gtk_tree_view_append_column(GTK_TREE_VIEW(self), column);
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400432 gtk_tree_view_column_set_resizable(column, TRUE);
Stepan Salenikoviche7c4e282015-06-11 17:22:08 -0400433 gtk_tree_view_column_set_expand(column, FALSE);
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400434
Stepan Salenikovich4a5dd7f2015-10-29 16:35:02 -0400435 g_signal_connect(self, "row-activated", G_CALLBACK(activate_history_item), NULL);
436 g_signal_connect(self, "button-press-event", G_CALLBACK(history_popup_menu), self);
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400437
438 gtk_widget_show_all(GTK_WIDGET(self));
439}
440
441static void
442history_view_dispose(GObject *object)
443{
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400444 HistoryViewPrivate *priv = HISTORY_VIEW_GET_PRIVATE(object);
445
446 QObject::disconnect(priv->category_changed);
447
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400448 G_OBJECT_CLASS(history_view_parent_class)->dispose(object);
449}
450
451static void
452history_view_finalize(GObject *object)
453{
Stepan Salenikovich9816a942015-04-22 17:49:16 -0400454 G_OBJECT_CLASS(history_view_parent_class)->finalize(object);
455}
456
457static void
458history_view_class_init(HistoryViewClass *klass)
459{
460 G_OBJECT_CLASS(klass)->finalize = history_view_finalize;
461 G_OBJECT_CLASS(klass)->dispose = history_view_dispose;
462}
463
464GtkWidget *
465history_view_new()
466{
467 gpointer self = g_object_new(HISTORY_VIEW_TYPE, NULL);
468
469 return (GtkWidget *)self;
Stepan Salenikovich9d294492015-05-14 16:34:24 -0400470}