~mniess/ubuntu/trusty/totem/fix-lp1292262

« back to all changes in this revision

Viewing changes to src/plugins/chapters/totem-chapters.c

  • Committer: Bazaar Package Importer
  • Author(s): Raphaël Hertzog
  • Date: 2011-04-10 18:12:25 UTC
  • mfrom: (1.1.14 upstream)
  • mto: (1.5.7 upstream)
  • mto: This revision was merged to the branch mainline in revision 19.
  • Revision ID: james.westby@ubuntu.com-20110410181225-u3x8yhsscjoe8ony
Tags: upstream-3.0.0
Import upstream version 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2010 Alexander Saprykin <xelfium@gmail.com>
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
 
17
 *
 
18
 *
 
19
 * The Totem project hereby grant permission for non-gpl compatible GStreamer
 
20
 * plugins to be used and distributed together with GStreamer and Totem. This
 
21
 * permission are above and beyond the permissions granted by the GPL license
 
22
 * Totem is covered by.
 
23
 */
 
24
 
 
25
#include "config.h"
 
26
 
 
27
#include <glib.h>
 
28
#include <glib-object.h>
 
29
#include <glib/gi18n-lib.h>
 
30
#include <glib/gprintf.h>
 
31
#include <gio/gio.h>
 
32
#include <gmodule.h>
 
33
#include <gdk-pixbuf/gdk-pixdata.h>
 
34
#include <gdk/gdkkeysyms.h>
 
35
#include <unistd.h>
 
36
#include <math.h>
 
37
 
 
38
#include "backend/bacon-video-widget.h"
 
39
#include "totem-plugin.h"
 
40
#include "totem-dirs.h"
 
41
#include "totem-interface.h"
 
42
#include "totem.h"
 
43
#include "totem-cmml-parser.h"
 
44
#include "totem-chapters-utils.h"
 
45
#include "totem-edit-chapter.h"
 
46
 
 
47
#define TOTEM_TYPE_CHAPTERS_PLUGIN              (totem_chapters_plugin_get_type ())
 
48
#define TOTEM_CHAPTERS_PLUGIN(o)                (G_TYPE_CHECK_INSTANCE_CAST ((o), TOTEM_TYPE_CHAPTERS_PLUGIN, TotemChaptersPlugin))
 
49
#define TOTEM_CHAPTERS_PLUGIN_CLASS(k)          (G_TYPE_CHECK_CLASS_CAST((k), TOTEM_TYPE_CHAPTERS_PLUGIN, TotemChaptersPluginClass))
 
50
#define TOTEM_IS_CHAPTERS_PLUGIN(o)             (G_TYPE_CHECK_INSTANCE_TYPE ((o), TOTEM_TYPE_CHAPTERS_PLUGIN))
 
51
#define TOTEM_IS_CHAPTERS_PLUGIN_CLASS(k)       (G_TYPE_CHECK_CLASS_TYPE ((k), TOTEM_TYPE_CHAPTERS_PLUGIN))
 
52
#define TOTEM_CHAPTERS_PLUGIN_GET_CLASS(o)      (G_TYPE_INSTANCE_GET_CLASS ((o), TOTEM_TYPE_CHAPTERS_PLUGIN, TotemChaptersPluginClass))
 
53
 
 
54
#define TOTEM_CHAPTERS_PLUGIN_GET_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TOTEM_TYPE_CHAPTERS_PLUGIN, TotemChaptersPluginPrivate))
 
55
 
 
56
#define CHAPTER_TOOLTIP(title, start) g_strdup_printf   ( _("<b>Title: </b>%s\n<b>Start time: </b>%s"), \
 
57
                                                         ( title ), ( start ) )
 
58
 
 
59
#define CHAPTER_TITLE(title, start) g_strdup_printf     ("<big>%s</big>\n"                              \
 
60
                                                         "<small><span foreground='grey'>%s"            \
 
61
                                                         "</span></small>",                             \
 
62
                                                         ( title ), ( start ) )
 
63
#define ICON_SCALE_RATIO 2
 
64
 
 
65
typedef struct {
 
66
        TotemObject     *totem;
 
67
        TotemEditChapter *edit_chapter;
 
68
        GtkWidget       *tree;
 
69
        GtkWidget       *add_button,
 
70
                        *remove_button,
 
71
                        *save_button,
 
72
                        *load_button,
 
73
                        *goto_button,
 
74
                        *continue_button,
 
75
                        *list_box,
 
76
                        *load_box;
 
77
        GtkActionGroup  *action_group;
 
78
        GtkUIManager    *ui_manager;
 
79
        gboolean        was_played;
 
80
        GdkPixbuf       *last_frame;
 
81
        gint64          last_time;
 
82
        gchar           *cmml_mrl;
 
83
        gboolean        autoload;
 
84
        GCancellable    *cancellable[2];
 
85
        GSettings       *settings;
 
86
        guint           autoload_handle_id;
 
87
} TotemChaptersPluginPrivate;
 
88
 
 
89
TOTEM_PLUGIN_REGISTER (TOTEM_TYPE_CHAPTERS_PLUGIN, TotemChaptersPlugin, totem_chapters_plugin)
 
90
 
 
91
enum {
 
92
        CHAPTERS_PIXBUF_COLUMN = 0,
 
93
        CHAPTERS_TITLE_COLUMN,
 
94
        CHAPTERS_TOOLTIP_COLUMN,
 
95
        CHAPTERS_TITLE_PRIV_COLUMN,
 
96
        CHAPTERS_TIME_PRIV_COLUMN,
 
97
        CHAPTERS_N_COLUMNS
 
98
};
 
99
 
 
100
static void totem_file_opened_async_cb (TotemObject *totem, const gchar *uri, TotemChaptersPlugin *plugin);
 
101
static void totem_file_opened_result_cb (gpointer data, gpointer user_data);
 
102
static void totem_file_closed_cb (TotemObject *totem, TotemChaptersPlugin *plugin);
 
103
static void add_chapter_to_the_list (gpointer data, gpointer user_data);
 
104
static void add_chapter_to_the_list_new (TotemChaptersPlugin *plugin, const gchar *title, gint64 time);
 
105
static gboolean check_available_time (TotemChaptersPlugin *plugin, gint64 time);
 
106
static GdkPixbuf * get_chapter_pixbuf (GdkPixbuf *src);
 
107
static void chapter_edit_dialog_response_cb (GtkDialog *dialog, gint response, TotemChaptersPlugin *plugin);
 
108
static void prepare_chapter_edit (GtkCellRenderer *renderer, GtkCellEditable *editable, gchar *path, gpointer user_data);
 
109
static void finish_chapter_edit (GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer user_data);
 
110
static void chapter_selection_changed_cb (GtkTreeSelection *tree_selection, TotemChaptersPlugin *plugin);
 
111
static void show_chapter_edit_dialog (TotemChaptersPlugin *plugin);
 
112
static void save_chapters_result_cb (gpointer data, gpointer user_data);
 
113
static GList * get_chapters_list (TotemChaptersPlugin *plugin);
 
114
static gboolean show_popup_menu (TotemChaptersPlugin *plugin, GdkEventButton *event);
 
115
static void autoload_changed_cb (GSettings *settigs, const gchar *key, TotemChaptersPlugin *plugin);
 
116
static void load_chapters_from_file (const gchar *uri, gboolean from_dialog, TotemChaptersPlugin *plugin);
 
117
static void set_no_data_visible (gboolean visible, gboolean show_buttons, TotemChaptersPlugin *plugin);
 
118
 
 
119
/* GtkBuilder callbacks */
 
120
void add_button_clicked_cb (GtkButton *button, TotemChaptersPlugin *plugin);
 
121
void remove_button_clicked_cb (GtkButton *button, TotemChaptersPlugin *plugin);
 
122
void save_button_clicked_cb (GtkButton *button, TotemChaptersPlugin *plugin);
 
123
void goto_button_clicked_cb (GtkButton *button, TotemChaptersPlugin *plugin);
 
124
void tree_view_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, TotemChaptersPlugin *plugin);
 
125
gboolean tree_view_button_press_cb (GtkTreeView *tree_view, GdkEventButton *event, TotemChaptersPlugin *plugin);
 
126
gboolean tree_view_key_press_cb (GtkTreeView *tree_view, GdkEventKey *event, TotemChaptersPlugin *plugin);
 
127
gboolean tree_view_popup_menu_cb (GtkTreeView *tree_view, TotemChaptersPlugin *plugin);
 
128
void popup_remove_action_cb (GtkAction *action, TotemChaptersPlugin *plugin);
 
129
void popup_goto_action_cb (GtkAction *action, TotemChaptersPlugin *plugin);
 
130
void load_button_clicked_cb (GtkButton *button, TotemChaptersPlugin *plugin);
 
131
void continue_button_clicked_cb (GtkButton *button, TotemChaptersPlugin *plugin);
 
132
 
 
133
static GdkPixbuf *
 
134
get_chapter_pixbuf (GdkPixbuf *src)
 
135
{
 
136
        GdkPixbuf       *pixbuf;
 
137
        gint            width, height;
 
138
        gfloat          pix_width, pix_height;
 
139
        gfloat          ratio, new_width;
 
140
 
 
141
        gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &width, &height);
 
142
        height *= ICON_SCALE_RATIO;
 
143
 
 
144
        if (src != NULL) {
 
145
                pix_width = (gfloat) gdk_pixbuf_get_width (src);
 
146
                pix_height = (gfloat) gdk_pixbuf_get_height (src);
 
147
 
 
148
                /* calc height ratio and apply it to width */
 
149
                ratio = pix_height / height;
 
150
                new_width = pix_width / ratio;
 
151
                width = ceil (new_width);
 
152
 
 
153
                /* scale video frame if need */
 
154
                pixbuf = gdk_pixbuf_scale_simple (src, width, height, GDK_INTERP_BILINEAR);
 
155
        } else {
 
156
                /* 16:10 aspect ratio by default */
 
157
                new_width = (gfloat) height * 1.6;
 
158
                width = ceil (new_width);
 
159
 
 
160
                pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height);
 
161
                gdk_pixbuf_fill (pixbuf, 0x00000000);
 
162
        }
 
163
 
 
164
        return pixbuf;
 
165
}
 
166
 
 
167
static void
 
168
add_chapter_to_the_list (gpointer       data,
 
169
                         gpointer       user_data)
 
170
{
 
171
        TotemChaptersPlugin     *plugin;
 
172
        GdkPixbuf               *pixbuf;
 
173
        GtkTreeIter             iter;
 
174
        GtkTreeStore            *store;
 
175
        TotemCmmlClip           *clip;
 
176
        gchar                   *text, *start, *tip;
 
177
 
 
178
        g_return_if_fail (data != NULL);
 
179
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (user_data));
 
180
 
 
181
        plugin = TOTEM_CHAPTERS_PLUGIN (user_data);
 
182
        store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (plugin->priv->tree)));
 
183
        clip = ((TotemCmmlClip *) data);
 
184
 
 
185
        /* prepare tooltip data */
 
186
        start = totem_cmml_convert_msecs_to_str (clip->time_start);
 
187
        tip = CHAPTER_TOOLTIP (clip->title, start);
 
188
 
 
189
        /* append clip to the sidebar list */
 
190
        gtk_tree_store_append (store, &iter, NULL);
 
191
        text = CHAPTER_TITLE (clip->title, start);
 
192
 
 
193
        if (G_LIKELY (clip->pixbuf != NULL))
 
194
                pixbuf = g_object_ref (clip->pixbuf);
 
195
        else
 
196
                pixbuf = get_chapter_pixbuf (NULL);
 
197
 
 
198
        gtk_tree_store_set (store, &iter,
 
199
                            CHAPTERS_TITLE_COLUMN, text,
 
200
                            CHAPTERS_TOOLTIP_COLUMN, tip,
 
201
                            CHAPTERS_PIXBUF_COLUMN, pixbuf,
 
202
                            CHAPTERS_TITLE_PRIV_COLUMN, clip->title,
 
203
                            CHAPTERS_TIME_PRIV_COLUMN, clip->time_start,
 
204
                            -1);
 
205
 
 
206
        g_object_unref (pixbuf);
 
207
        g_free (text);
 
208
        g_free (start);
 
209
        g_free (tip);
 
210
}
 
211
 
 
212
static void
 
213
add_chapter_to_the_list_new (TotemChaptersPlugin        *plugin,
 
214
                             const gchar                *title,
 
215
                             gint64                     _time)
 
216
{
 
217
        GdkPixbuf       *pixbuf;
 
218
        GtkTreeIter     iter, cur_iter, res_iter;
 
219
        GtkTreeModel    *store;
 
220
        gchar           *text, *start, *tip;
 
221
        gboolean        valid;
 
222
        gint64          cur_time, prev_time = 0;
 
223
        gint            iter_count = 0;
 
224
 
 
225
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
226
        g_return_if_fail (title != NULL);
 
227
        g_return_if_fail (_time >= 0);
 
228
 
 
229
        store = gtk_tree_view_get_model (GTK_TREE_VIEW (plugin->priv->tree));
 
230
        valid = gtk_tree_model_get_iter_first (store, &cur_iter);
 
231
 
 
232
        while (valid) {
 
233
                gtk_tree_model_get (store, &cur_iter,
 
234
                                    CHAPTERS_TIME_PRIV_COLUMN, &cur_time,
 
235
                                    -1);
 
236
 
 
237
                if (_time < cur_time && _time > prev_time)
 
238
                        break;
 
239
 
 
240
                iter_count += 1;
 
241
                res_iter = cur_iter;
 
242
                prev_time = cur_time;
 
243
 
 
244
                valid = gtk_tree_model_iter_next (store, &cur_iter);
 
245
        }
 
246
 
 
247
        /* prepare tooltip data */
 
248
        start = totem_cmml_convert_msecs_to_str (_time);
 
249
        tip = CHAPTER_TOOLTIP (title, start);
 
250
 
 
251
        /* insert clip into the sidebar list at proper position */
 
252
        if (iter_count > 0)
 
253
                gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter, NULL, &res_iter);
 
254
        else
 
255
                gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter, NULL, NULL);
 
256
 
 
257
        text = CHAPTER_TITLE (title, start);
 
258
        pixbuf = get_chapter_pixbuf (plugin->priv->last_frame);
 
259
 
 
260
        gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
 
261
                            CHAPTERS_TITLE_COLUMN, text,
 
262
                            CHAPTERS_TOOLTIP_COLUMN, tip,
 
263
                            CHAPTERS_PIXBUF_COLUMN, pixbuf,
 
264
                            CHAPTERS_TITLE_PRIV_COLUMN, title,
 
265
                            CHAPTERS_TIME_PRIV_COLUMN, _time,
 
266
                            -1);
 
267
 
 
268
        g_object_unref (pixbuf);
 
269
        g_free (text);
 
270
        g_free (start);
 
271
        g_free (tip);
 
272
}
 
273
 
 
274
static gboolean
 
275
check_available_time (TotemChaptersPlugin       *plugin,
 
276
                      gint64                    _time)
 
277
{
 
278
        GtkTreeModel    *store;
 
279
        GtkTreeIter     iter;
 
280
        gboolean        valid;
 
281
        gint64          cur_time;
 
282
 
 
283
        g_return_val_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin), FALSE);
 
284
 
 
285
        store = gtk_tree_view_get_model (GTK_TREE_VIEW (plugin->priv->tree));
 
286
 
 
287
        valid = gtk_tree_model_get_iter_first (store, &iter);
 
288
        while (valid) {
 
289
                gtk_tree_model_get (store, &iter,
 
290
                                    CHAPTERS_TIME_PRIV_COLUMN, &cur_time,
 
291
                                    -1);
 
292
 
 
293
                if (cur_time == _time)
 
294
                        return FALSE;
 
295
 
 
296
                if (cur_time > _time)
 
297
                        return TRUE;
 
298
 
 
299
                valid = gtk_tree_model_iter_next (store, &iter);
 
300
        }
 
301
 
 
302
        return TRUE;
 
303
}
 
304
 
 
305
static void
 
306
totem_file_opened_result_cb (gpointer   data,
 
307
                             gpointer   user_data)
 
308
{
 
309
        TotemCmmlAsyncData      *adata;
 
310
        TotemChaptersPlugin     *plugin;
 
311
 
 
312
        g_return_if_fail (data != NULL);
 
313
 
 
314
        adata = (TotemCmmlAsyncData *) data;
 
315
        plugin = TOTEM_CHAPTERS_PLUGIN (adata->user_data);
 
316
 
 
317
        if (G_UNLIKELY (!adata->successful)) {
 
318
                if (G_UNLIKELY (g_cancellable_is_cancelled (adata->cancellable))) {
 
319
                        /* if operation was cancelled due to plugin deactivation only clean up */
 
320
                        g_object_unref (adata->cancellable);
 
321
                        g_free (adata->file);
 
322
                        g_free (adata->error);
 
323
                        g_list_foreach (adata->list, (GFunc) totem_cmml_clip_free, NULL);
 
324
                        g_list_free (adata->list);
 
325
                        g_free (adata);
 
326
                        return;
 
327
                } else
 
328
                        totem_action_error (plugin->priv->totem, _("Error while reading file with chapters"),
 
329
                                            adata->error);
 
330
        }
 
331
 
 
332
        if (adata->is_exists && adata->from_dialog) {
 
333
                g_free (plugin->priv->cmml_mrl);
 
334
                plugin->priv->cmml_mrl = g_strdup (adata->file);
 
335
        }
 
336
 
 
337
        g_list_foreach (adata->list, (GFunc) add_chapter_to_the_list, plugin);
 
338
        g_list_foreach (adata->list, (GFunc) totem_cmml_clip_free, NULL);
 
339
        g_list_free (adata->list);
 
340
 
 
341
        /* do not show tree if read operation failed */
 
342
        set_no_data_visible (!adata->successful || !adata->is_exists, TRUE, plugin);
 
343
 
 
344
        g_object_unref (adata->cancellable);
 
345
        g_free (adata->file);
 
346
        g_free (adata->error);
 
347
        g_free (adata);
 
348
}
 
349
 
 
350
static void
 
351
totem_file_opened_async_cb (TotemObject                 *totem,
 
352
                            const gchar                 *uri,
 
353
                            TotemChaptersPlugin         *plugin)
 
354
{
 
355
        gchar   *cmml_file;
 
356
 
 
357
        g_return_if_fail (TOTEM_IS_OBJECT (totem));
 
358
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
359
        g_return_if_fail (uri != NULL);
 
360
 
 
361
        if (g_str_has_prefix (uri, "http") != FALSE)
 
362
                return;
 
363
 
 
364
        cmml_file = totem_change_file_extension (uri, "cmml");
 
365
        /* if file has no extension - append it */
 
366
        if (cmml_file == NULL)
 
367
                cmml_file = g_strconcat (uri, ".cmml", NULL);
 
368
 
 
369
        plugin->priv->cmml_mrl = cmml_file;
 
370
 
 
371
        if (!plugin->priv->autoload)
 
372
                set_no_data_visible (TRUE, TRUE, plugin);
 
373
        else
 
374
                load_chapters_from_file (cmml_file, FALSE, plugin);
 
375
}
 
376
 
 
377
static void
 
378
totem_file_closed_cb (TotemObject               *totem,
 
379
                      TotemChaptersPlugin       *plugin)
 
380
{
 
381
        GtkTreeStore    *store;
 
382
 
 
383
        g_return_if_fail (TOTEM_IS_OBJECT (totem) && TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
384
 
 
385
        store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (plugin->priv->tree)));
 
386
 
 
387
        gtk_tree_store_clear (store);
 
388
 
 
389
        if (G_UNLIKELY (plugin->priv->edit_chapter != NULL))
 
390
                gtk_widget_destroy (GTK_WIDGET (plugin->priv->edit_chapter));
 
391
 
 
392
        if (G_UNLIKELY (plugin->priv->last_frame != NULL))
 
393
                g_object_unref (G_OBJECT (plugin->priv->last_frame));
 
394
 
 
395
        g_free (plugin->priv->cmml_mrl);
 
396
        plugin->priv->cmml_mrl = NULL;
 
397
 
 
398
        gtk_widget_set_sensitive (plugin->priv->remove_button, FALSE);
 
399
        gtk_widget_set_sensitive (plugin->priv->save_button, FALSE);
 
400
 
 
401
        set_no_data_visible (TRUE, FALSE, plugin);
 
402
}
 
403
 
 
404
static void
 
405
chapter_edit_dialog_response_cb (GtkDialog              *dialog,
 
406
                                 gint                   response,
 
407
                                 TotemChaptersPlugin    *plugin)
 
408
{
 
409
        gchar           *title;
 
410
 
 
411
        g_return_if_fail (TOTEM_IS_EDIT_CHAPTER (dialog));
 
412
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
413
 
 
414
        if (response != GTK_RESPONSE_OK) {
 
415
                gtk_widget_destroy (GTK_WIDGET (plugin->priv->edit_chapter));
 
416
 
 
417
                if (plugin->priv->last_frame != NULL)
 
418
                        g_object_unref (G_OBJECT (plugin->priv->last_frame));
 
419
 
 
420
                if (plugin->priv->was_played)
 
421
                        totem_action_play (plugin->priv->totem);
 
422
                return;
 
423
        }
 
424
 
 
425
        gtk_widget_hide (GTK_WIDGET (dialog));
 
426
 
 
427
        title = totem_edit_chapter_get_title (TOTEM_EDIT_CHAPTER (dialog));
 
428
        add_chapter_to_the_list_new (plugin, title, plugin->priv->last_time);
 
429
 
 
430
        gtk_widget_set_sensitive (plugin->priv->save_button, TRUE);
 
431
 
 
432
        if (G_LIKELY (plugin->priv->last_frame != NULL))
 
433
                g_object_unref (G_OBJECT (plugin->priv->last_frame));
 
434
 
 
435
        g_free (title);
 
436
        gtk_widget_destroy (GTK_WIDGET (plugin->priv->edit_chapter));
 
437
 
 
438
        if (plugin->priv->was_played)
 
439
                totem_action_play (plugin->priv->totem);
 
440
}
 
441
 
 
442
static void
 
443
prepare_chapter_edit (GtkCellRenderer   *renderer,
 
444
                      GtkCellEditable   *editable,
 
445
                      gchar             *path,
 
446
                      gpointer          user_data)
 
447
{
 
448
        TotemChaptersPlugin     *plugin;
 
449
        GtkTreeModel            *store;
 
450
        GtkTreeIter             iter;
 
451
        gchar                   *title;
 
452
        GtkEntry                *entry;
 
453
 
 
454
        g_return_if_fail (GTK_IS_ENTRY (editable));
 
455
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (user_data));
 
456
        g_return_if_fail (path != NULL);
 
457
 
 
458
        plugin = TOTEM_CHAPTERS_PLUGIN (user_data);
 
459
        entry = GTK_ENTRY (editable);
 
460
        store = gtk_tree_view_get_model (GTK_TREE_VIEW (plugin->priv->tree));
 
461
 
 
462
        if (G_UNLIKELY (!gtk_tree_model_get_iter_from_string (store, &iter, path)))
 
463
                return;
 
464
 
 
465
        gtk_tree_model_get (store, &iter, CHAPTERS_TITLE_PRIV_COLUMN, &title, -1);
 
466
        gtk_entry_set_text (entry, title);
 
467
 
 
468
        g_free (title);
 
469
        return;
 
470
}
 
471
 
 
472
static void
 
473
finish_chapter_edit (GtkCellRendererText        *renderer,
 
474
                     gchar                      *path,
 
475
                     gchar                      *new_text,
 
476
                     gpointer                   user_data)
 
477
{
 
478
        TotemChaptersPlugin     *plugin;
 
479
        GtkTreeModel            *store;
 
480
        GtkTreeIter             iter;
 
481
        gchar                   *time_str, *tip, *new_title, *old_title;
 
482
        gint64                  _time;
 
483
 
 
484
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (user_data));
 
485
        g_return_if_fail (new_text != NULL);
 
486
        g_return_if_fail (path != NULL);
 
487
 
 
488
        plugin = TOTEM_CHAPTERS_PLUGIN (user_data);
 
489
        store = gtk_tree_view_get_model (GTK_TREE_VIEW (plugin->priv->tree));
 
490
 
 
491
        if (G_UNLIKELY (!gtk_tree_model_get_iter_from_string (store, &iter, path)))
 
492
                return;
 
493
 
 
494
        gtk_tree_model_get (store, &iter,
 
495
                            CHAPTERS_TIME_PRIV_COLUMN, &_time,
 
496
                            CHAPTERS_TITLE_PRIV_COLUMN, &old_title,
 
497
                            -1);
 
498
 
 
499
        if (g_strcmp0 (old_title, new_text) == 0) {
 
500
                g_free (old_title);
 
501
                return;
 
502
        }
 
503
 
 
504
        time_str = totem_cmml_convert_msecs_to_str (_time);
 
505
        new_title = CHAPTER_TITLE (new_text, time_str);
 
506
        tip = CHAPTER_TOOLTIP (new_text, time_str);
 
507
 
 
508
        gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
 
509
                            CHAPTERS_TITLE_COLUMN, new_title,
 
510
                            CHAPTERS_TOOLTIP_COLUMN, tip,
 
511
                            CHAPTERS_TITLE_PRIV_COLUMN, new_text,
 
512
                            -1);
 
513
 
 
514
        gtk_widget_set_sensitive (plugin->priv->save_button, TRUE);
 
515
 
 
516
        g_free (old_title);
 
517
        g_free (new_title);
 
518
        g_free (tip);
 
519
        g_free (time_str);
 
520
}
 
521
 
 
522
static void
 
523
show_chapter_edit_dialog (TotemChaptersPlugin   *plugin)
 
524
{
 
525
        GtkWindow               *main_window;
 
526
        BaconVideoWidget        *bvw;
 
527
        gint64                  _time;
 
528
 
 
529
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
530
 
 
531
        if (G_UNLIKELY (plugin->priv->edit_chapter != NULL)) {
 
532
                gtk_window_present (GTK_WINDOW (plugin->priv->edit_chapter));
 
533
                return;
 
534
        }
 
535
 
 
536
        main_window = totem_get_main_window (plugin->priv->totem);
 
537
        plugin->priv->was_played = totem_is_playing (plugin->priv->totem);
 
538
        totem_action_pause (plugin->priv->totem);
 
539
 
 
540
        /* adding a new one, check if it's time available */
 
541
        g_object_get (G_OBJECT (plugin->priv->totem), "current-time", &_time, NULL);
 
542
        if (G_UNLIKELY (!check_available_time (plugin, _time))) {
 
543
                totem_interface_error_blocking (_("Chapter with the same time already exists"),
 
544
                                                _("Try another name or remove an existing chapter."),
 
545
                                                main_window);
 
546
                g_object_unref (main_window);
 
547
                if (plugin->priv->was_played)
 
548
                        totem_action_play (plugin->priv->totem);
 
549
                return;
 
550
        }
 
551
        plugin->priv->last_time = _time;
 
552
 
 
553
        /* capture frame */
 
554
        bvw = BACON_VIDEO_WIDGET (totem_get_video_widget (plugin->priv->totem));
 
555
        plugin->priv->last_frame = bacon_video_widget_get_current_frame (bvw);
 
556
        g_object_add_weak_pointer (G_OBJECT (plugin->priv->last_frame), (gpointer *) &plugin->priv->last_frame);
 
557
        g_object_unref (bvw);
 
558
 
 
559
        /* create chapter-edit dialog */
 
560
        plugin->priv->edit_chapter = TOTEM_EDIT_CHAPTER (totem_edit_chapter_new ());
 
561
        g_object_add_weak_pointer (G_OBJECT (plugin->priv->edit_chapter), (gpointer *) &(plugin->priv->edit_chapter));
 
562
 
 
563
        g_signal_connect (G_OBJECT (plugin->priv->edit_chapter), "delete-event",
 
564
                          G_CALLBACK (gtk_widget_destroy), NULL);
 
565
        g_signal_connect (G_OBJECT (plugin->priv->edit_chapter), "response",
 
566
                          G_CALLBACK (chapter_edit_dialog_response_cb), plugin);
 
567
 
 
568
        gtk_window_set_transient_for (GTK_WINDOW (plugin->priv->edit_chapter),
 
569
                                      main_window);
 
570
        gtk_widget_show (GTK_WIDGET (plugin->priv->edit_chapter));
 
571
 
 
572
        g_object_unref (main_window);
 
573
}
 
574
 
 
575
static void
 
576
chapter_selection_changed_cb (GtkTreeSelection          *tree_selection,
 
577
                              TotemChaptersPlugin       *plugin)
 
578
{
 
579
        gint            count;
 
580
        gboolean        allow_remove, allow_goto;
 
581
 
 
582
        g_return_if_fail (GTK_IS_TREE_SELECTION (tree_selection));
 
583
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
584
 
 
585
        count = gtk_tree_selection_count_selected_rows (tree_selection);
 
586
        allow_remove = (count > 0);
 
587
        allow_goto = (count == 1);
 
588
 
 
589
        gtk_widget_set_sensitive (plugin->priv->remove_button, allow_remove);
 
590
        gtk_widget_set_sensitive (plugin->priv->goto_button, allow_goto);
 
591
}
 
592
 
 
593
static void
 
594
autoload_changed_cb (GSettings                  *settings,
 
595
                     const gchar                *key,
 
596
                     TotemChaptersPlugin        *plugin)
 
597
{
 
598
        g_return_if_fail (G_IS_SETTINGS (settings));
 
599
        g_return_if_fail (key != NULL);
 
600
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
601
 
 
602
        plugin->priv->autoload = g_settings_get_boolean (settings, key);
 
603
}
 
604
 
 
605
static void
 
606
load_chapters_from_file (const gchar            *uri,
 
607
                         gboolean               from_dialog,
 
608
                         TotemChaptersPlugin    *plugin)
 
609
{
 
610
        TotemCmmlAsyncData      *data;
 
611
 
 
612
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
613
 
 
614
        if (plugin->priv->cancellable[0] != NULL) {
 
615
                g_cancellable_cancel (plugin->priv->cancellable[0]);
 
616
                g_object_unref (plugin->priv->cancellable[0]);
 
617
        }
 
618
 
 
619
        data = g_new0 (TotemCmmlAsyncData, 1);
 
620
        /* do not forget to save this pointer in the result function */
 
621
        data->file = g_strdup (uri);
 
622
        data->final = totem_file_opened_result_cb;
 
623
        data->user_data = (gpointer) plugin;
 
624
        if (from_dialog)
 
625
                data->from_dialog = TRUE;
 
626
        /* cancellable object shouldn't be finalized during result func */
 
627
        plugin->priv->cancellable[0] = g_cancellable_new ();
 
628
        g_object_add_weak_pointer (G_OBJECT (plugin->priv->cancellable[0]),
 
629
                                   (gpointer *) &(plugin->priv->cancellable[0]));
 
630
        data->cancellable = plugin->priv->cancellable[0];
 
631
 
 
632
        if (G_UNLIKELY (totem_cmml_read_file_async (data) < 0)) {
 
633
                g_warning ("chapters: wrong parameters for reading CMML file, may be a bug");
 
634
 
 
635
                set_no_data_visible (TRUE, TRUE, plugin);
 
636
 
 
637
                g_object_unref (plugin->priv->cancellable[0]);
 
638
                g_free (data);
 
639
        }
 
640
}
 
641
 
 
642
static void
 
643
set_no_data_visible (gboolean                   visible,
 
644
                     gboolean                   show_buttons,
 
645
                     TotemChaptersPlugin        *plugin)
 
646
{
 
647
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
648
 
 
649
        if (visible) {
 
650
                gtk_widget_hide (plugin->priv->list_box);
 
651
                gtk_widget_show (plugin->priv->load_box);
 
652
        } else {
 
653
                gtk_widget_hide (plugin->priv->load_box);
 
654
                gtk_widget_show (plugin->priv->list_box);
 
655
        }
 
656
 
 
657
        gtk_widget_set_sensitive (plugin->priv->add_button, !visible);
 
658
        gtk_widget_set_sensitive (plugin->priv->tree, !visible);
 
659
 
 
660
        gtk_widget_set_visible (plugin->priv->load_button, show_buttons);
 
661
        gtk_widget_set_visible (plugin->priv->continue_button, show_buttons);
 
662
}
 
663
 
 
664
void
 
665
add_button_clicked_cb (GtkButton                *button,
 
666
                       TotemChaptersPlugin      *plugin)
 
667
{
 
668
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
669
 
 
670
        show_chapter_edit_dialog (plugin);
 
671
}
 
672
 
 
673
void
 
674
remove_button_clicked_cb (GtkButton             *button,
 
675
                          TotemChaptersPlugin   *plugin)
 
676
{
 
677
        GtkTreeSelection        *selection;
 
678
        GtkTreeIter             iter;
 
679
        GtkTreeModel            *store;
 
680
        GList                   *list;
 
681
 
 
682
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
683
 
 
684
        store = gtk_tree_view_get_model (GTK_TREE_VIEW (plugin->priv->tree));
 
685
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (plugin->priv->tree));
 
686
        list = gtk_tree_selection_get_selected_rows (selection, NULL);
 
687
 
 
688
        g_return_if_fail (g_list_length (list) != 0);
 
689
 
 
690
        list = g_list_last (list);
 
691
        while (list != NULL) {
 
692
                gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, (GtkTreePath *) list->data);
 
693
                gtk_tree_store_remove (GTK_TREE_STORE (store), &iter);
 
694
 
 
695
                list = list->prev;
 
696
        }
 
697
 
 
698
        gtk_widget_set_sensitive (plugin->priv->save_button, TRUE);
 
699
 
 
700
        g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
 
701
        g_list_free (list);
 
702
}
 
703
 
 
704
static void
 
705
save_chapters_result_cb (gpointer       data,
 
706
                         gpointer       user_data)
 
707
{
 
708
        TotemCmmlAsyncData      *adata;
 
709
        TotemChaptersPlugin     *plugin;
 
710
 
 
711
        g_return_if_fail (data != NULL);
 
712
 
 
713
        adata = (TotemCmmlAsyncData *) data;
 
714
        plugin = TOTEM_CHAPTERS_PLUGIN (adata->user_data);
 
715
 
 
716
        if (G_UNLIKELY (!adata->successful && !g_cancellable_is_cancelled (adata->cancellable))) {
 
717
                totem_action_error (plugin->priv->totem, _("Error while writing file with chapters"),
 
718
                                    adata->error);
 
719
                gtk_widget_set_sensitive (plugin->priv->save_button, TRUE);
 
720
        }
 
721
 
 
722
        g_object_unref (adata->cancellable);
 
723
        g_list_foreach (adata->list, (GFunc) totem_cmml_clip_free, NULL);
 
724
        g_list_free (adata->list);
 
725
        g_free (adata->error);
 
726
        g_free (adata);
 
727
}
 
728
 
 
729
static GList *
 
730
get_chapters_list (TotemChaptersPlugin  *plugin)
 
731
{
 
732
        GList           *list = NULL;
 
733
        TotemCmmlClip   *clip;
 
734
        GtkTreeModel    *store;
 
735
        GtkTreeIter     iter;
 
736
        gchar           *title;
 
737
        gint64          _time;
 
738
        GdkPixbuf       *pixbuf;
 
739
        gboolean        valid;
 
740
 
 
741
        g_return_val_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin), NULL);
 
742
 
 
743
        store = gtk_tree_view_get_model (GTK_TREE_VIEW (plugin->priv->tree));
 
744
 
 
745
        valid = gtk_tree_model_get_iter_first (store, &iter);
 
746
        while (valid) {
 
747
                gtk_tree_model_get (store, &iter,
 
748
                                    CHAPTERS_TITLE_PRIV_COLUMN, &title,
 
749
                                    CHAPTERS_TIME_PRIV_COLUMN, &_time,
 
750
                                    CHAPTERS_PIXBUF_COLUMN, &pixbuf,
 
751
                                    -1);
 
752
                clip = totem_cmml_clip_new (title, NULL, _time, pixbuf);
 
753
                list = g_list_prepend (list, clip);
 
754
 
 
755
                g_free (title);
 
756
                g_object_unref (pixbuf);
 
757
 
 
758
                valid = gtk_tree_model_iter_next (store, &iter);
 
759
        }
 
760
        list = g_list_reverse (list);
 
761
 
 
762
        return list;
 
763
}
 
764
 
 
765
static gboolean
 
766
show_popup_menu (TotemChaptersPlugin    *plugin,
 
767
                 GdkEventButton         *event)
 
768
{
 
769
        guint                   button = 0;
 
770
        guint32                 _time;
 
771
        GtkTreePath             *path;
 
772
        gint                    count;
 
773
        GtkWidget               *menu;
 
774
        GtkAction               *remove_act, *goto_act;
 
775
        GtkTreeSelection        *selection;
 
776
 
 
777
        g_return_val_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin), FALSE);
 
778
 
 
779
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (plugin->priv->tree));
 
780
 
 
781
        if (event != NULL) {
 
782
                button = event->button;
 
783
                _time = event->time;
 
784
 
 
785
                if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (plugin->priv->tree),
 
786
                                                   event->x, event->y, &path, NULL, NULL, NULL)) {
 
787
                        if (!gtk_tree_selection_path_is_selected (selection, path)) {
 
788
                                gtk_tree_selection_unselect_all (selection);
 
789
                                gtk_tree_selection_select_path (selection, path);
 
790
                        }
 
791
                        gtk_tree_path_free (path);
 
792
                } else
 
793
                        gtk_tree_selection_unselect_all (selection);
 
794
        } else
 
795
                _time = gtk_get_current_event_time ();
 
796
 
 
797
        count = gtk_tree_selection_count_selected_rows (selection);
 
798
 
 
799
        if (count == 0)
 
800
                return FALSE;
 
801
 
 
802
        remove_act = gtk_action_group_get_action (plugin->priv->action_group, "remove");
 
803
        goto_act = gtk_action_group_get_action (plugin->priv->action_group, "goto");
 
804
        gtk_action_set_sensitive (remove_act, count > 0);
 
805
        gtk_action_set_sensitive (goto_act, count == 1);
 
806
 
 
807
        menu = gtk_ui_manager_get_widget (plugin->priv->ui_manager, "/totem-chapters-popup");
 
808
 
 
809
        gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
 
810
 
 
811
        gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
 
812
                        button, _time);
 
813
 
 
814
        return TRUE;
 
815
}
 
816
 
 
817
void
 
818
save_button_clicked_cb (GtkButton               *button,
 
819
                        TotemChaptersPlugin     *plugin)
 
820
{
 
821
        TotemCmmlAsyncData      *data;
 
822
 
 
823
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
824
 
 
825
        if (plugin->priv->cancellable[1] != NULL) {
 
826
                g_cancellable_cancel (plugin->priv->cancellable[1]);
 
827
                g_object_unref (plugin->priv->cancellable[1]);
 
828
        }
 
829
 
 
830
        data = g_new0 (TotemCmmlAsyncData, 1);
 
831
        data->file = plugin->priv->cmml_mrl;
 
832
        data->list = get_chapters_list (plugin);
 
833
        data->final = save_chapters_result_cb;
 
834
        data->user_data = (gpointer) plugin;
 
835
        /* cancellable object shouldn't be finalized during result func */
 
836
        data->cancellable = g_cancellable_new ();
 
837
        plugin->priv->cancellable[1] = data->cancellable;
 
838
        g_object_add_weak_pointer (G_OBJECT (plugin->priv->cancellable[1]),
 
839
                                   (gpointer *) &(plugin->priv->cancellable[1]));
 
840
 
 
841
        if (G_UNLIKELY (totem_cmml_write_file_async (data) < 0)) {
 
842
                totem_action_error (plugin->priv->totem, _("Error occurred while saving chapters"),
 
843
                                    _("Please check you have permission to write to the folder containing the movie."));
 
844
                g_free (data);
 
845
        } else
 
846
                gtk_widget_set_sensitive (plugin->priv->save_button, FALSE);
 
847
}
 
848
 
 
849
void
 
850
tree_view_row_activated_cb (GtkTreeView                 *tree_view,
 
851
                            GtkTreePath                 *path,
 
852
                            GtkTreeViewColumn           *column,
 
853
                            TotemChaptersPlugin         *plugin)
 
854
{
 
855
        GtkTreeModel    *store;
 
856
        GtkTreeIter     iter;
 
857
        gboolean        seekable;
 
858
        gint64          _time;
 
859
 
 
860
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
861
        g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
 
862
        g_return_if_fail (path != NULL);
 
863
 
 
864
        store = gtk_tree_view_get_model (tree_view);
 
865
        seekable = totem_is_seekable (plugin->priv->totem);
 
866
        if (!seekable) {
 
867
                g_warning ("chapters: unable to seek stream!");
 
868
                return;
 
869
        }
 
870
 
 
871
        gtk_tree_model_get_iter (store, &iter, path);
 
872
        gtk_tree_model_get (store, &iter, CHAPTERS_TIME_PRIV_COLUMN, &_time, -1);
 
873
 
 
874
        totem_action_seek_time (plugin->priv->totem, _time, TRUE);
 
875
}
 
876
 
 
877
gboolean
 
878
tree_view_button_press_cb (GtkTreeView                  *tree_view,
 
879
                           GdkEventButton               *event,
 
880
                           TotemChaptersPlugin          *plugin)
 
881
{
 
882
        g_return_val_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin), FALSE);
 
883
        g_return_val_if_fail (event != NULL, FALSE);
 
884
 
 
885
        if (event->type == GDK_BUTTON_PRESS && event->button == 3)
 
886
                return show_popup_menu (plugin, event);
 
887
 
 
888
        return FALSE;
 
889
}
 
890
 
 
891
gboolean
 
892
tree_view_key_press_cb (GtkTreeView             *tree_view,
 
893
                        GdkEventKey             *event,
 
894
                        TotemChaptersPlugin     *plugin)
 
895
{
 
896
        GtkTreeSelection        *selection;
 
897
 
 
898
        g_return_val_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin), FALSE);
 
899
        g_return_val_if_fail (event != NULL, FALSE);
 
900
 
 
901
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (plugin->priv->tree));
 
902
 
 
903
        /* Special case some shortcuts */
 
904
        if (event->state != 0) {
 
905
                if ((event->state & GDK_CONTROL_MASK) &&
 
906
                    event->keyval == GDK_KEY_a) {
 
907
                        gtk_tree_selection_select_all (selection);
 
908
                        return TRUE;
 
909
                }
 
910
        }
 
911
 
 
912
        /* If we have modifiers, and either Ctrl, Mod1 (Alt), or any
 
913
         * of Mod3 to Mod5 (Mod2 is num-lock...) are pressed, we
 
914
         * let Gtk+ handle the key */
 
915
        if (event->state != 0 &&
 
916
            ((event->state & GDK_CONTROL_MASK)
 
917
            || (event->state & GDK_MOD1_MASK)
 
918
            || (event->state & GDK_MOD3_MASK)
 
919
            || (event->state & GDK_MOD4_MASK)
 
920
            || (event->state & GDK_MOD5_MASK)))
 
921
                return FALSE;
 
922
 
 
923
        if (event->keyval == GDK_KEY_Delete) {
 
924
                if (gtk_tree_selection_count_selected_rows (selection) > 0)
 
925
                        remove_button_clicked_cb (GTK_BUTTON (plugin->priv->remove_button), plugin);
 
926
                return TRUE;
 
927
        }
 
928
 
 
929
        return FALSE;
 
930
}
 
931
 
 
932
gboolean
 
933
tree_view_popup_menu_cb (GtkTreeView            *tree_view,
 
934
                         TotemChaptersPlugin    *plugin)
 
935
{
 
936
        g_return_val_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin), FALSE);
 
937
 
 
938
        return show_popup_menu (plugin, NULL);
 
939
}
 
940
 
 
941
void
 
942
popup_remove_action_cb (GtkAction               *action,
 
943
                        TotemChaptersPlugin     *plugin)
 
944
{
 
945
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
946
 
 
947
        remove_button_clicked_cb (GTK_BUTTON (plugin->priv->remove_button), plugin);
 
948
}
 
949
 
 
950
void
 
951
popup_goto_action_cb (GtkAction                 *action,
 
952
                      TotemChaptersPlugin       *plugin)
 
953
{
 
954
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
955
 
 
956
        goto_button_clicked_cb (GTK_BUTTON (plugin->priv->goto_button), plugin);
 
957
}
 
958
 
 
959
void
 
960
load_button_clicked_cb (GtkButton               *button,
 
961
                        TotemChaptersPlugin     *plugin)
 
962
{
 
963
        GtkWindow       *main_window;
 
964
        GtkWidget       *dialog;
 
965
        GFile           *cur, *parent;
 
966
        GtkFileFilter   *filter_supported, *filter_all;
 
967
        gchar           *filename, *mrl, *dir;
 
968
 
 
969
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
970
 
 
971
        plugin->priv->was_played = totem_is_playing (plugin->priv->totem);
 
972
        totem_action_pause (plugin->priv->totem);
 
973
 
 
974
        mrl = totem_get_current_mrl (plugin->priv->totem);
 
975
        main_window = totem_get_main_window (plugin->priv->totem);
 
976
        dialog = gtk_file_chooser_dialog_new (_("Open Chapter File"), main_window, GTK_FILE_CHOOSER_ACTION_OPEN,
 
977
                                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
978
                                              GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
 
979
                                              NULL);
 
980
        gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
 
981
 
 
982
        cur = g_file_new_for_uri (mrl);
 
983
        parent = g_file_get_parent (cur);
 
984
 
 
985
        if (parent != NULL)
 
986
                dir = g_file_get_uri (parent);
 
987
        else
 
988
                dir = g_strdup (G_DIR_SEPARATOR_S);
 
989
 
 
990
        filter_supported = gtk_file_filter_new ();
 
991
        filter_all = gtk_file_filter_new ();
 
992
 
 
993
        gtk_file_filter_add_pattern (filter_supported, "*.cmml");
 
994
        gtk_file_filter_add_pattern (filter_supported, "*.CMML");
 
995
        gtk_file_filter_set_name (filter_supported, _("Supported files"));
 
996
 
 
997
        gtk_file_filter_add_pattern (filter_all, "*");
 
998
        gtk_file_filter_set_name (filter_all, _("All files"));
 
999
 
 
1000
        gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter_supported);
 
1001
        gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filter_all);
 
1002
 
 
1003
        gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog), dir);
 
1004
 
 
1005
        if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
 
1006
                filename = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
 
1007
 
 
1008
                load_chapters_from_file (filename, TRUE, plugin);
 
1009
 
 
1010
                g_free (filename);
 
1011
        }
 
1012
 
 
1013
        if (plugin->priv->was_played)
 
1014
                totem_action_play (plugin->priv->totem);
 
1015
 
 
1016
        gtk_widget_destroy (dialog);
 
1017
        g_object_unref (main_window);
 
1018
        g_object_unref (cur);
 
1019
        g_object_unref (parent);
 
1020
        g_free (mrl);
 
1021
        g_free (dir);
 
1022
}
 
1023
 
 
1024
void
 
1025
continue_button_clicked_cb (GtkButton           *button,
 
1026
                            TotemChaptersPlugin *plugin)
 
1027
{
 
1028
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
1029
 
 
1030
        set_no_data_visible (FALSE, FALSE, plugin);
 
1031
}
 
1032
 
 
1033
void
 
1034
goto_button_clicked_cb (GtkButton               *button,
 
1035
                        TotemChaptersPlugin     *plugin)
 
1036
{
 
1037
        GtkTreeView             *tree;
 
1038
        GtkTreeModel            *store;
 
1039
        GtkTreeSelection        *selection;
 
1040
        GList                   *list;
 
1041
 
 
1042
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
1043
 
 
1044
        tree = GTK_TREE_VIEW (plugin->priv->tree);
 
1045
        store = gtk_tree_view_get_model (tree);
 
1046
        selection = gtk_tree_view_get_selection (tree);
 
1047
 
 
1048
        list = gtk_tree_selection_get_selected_rows (selection, &store);
 
1049
 
 
1050
        tree_view_row_activated_cb (tree, (GtkTreePath *) list->data, NULL, plugin);
 
1051
 
 
1052
        g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
 
1053
        g_list_free (list);
 
1054
}
 
1055
 
 
1056
static void
 
1057
impl_activate (PeasActivatable *plugin)
 
1058
{
 
1059
        TotemObject             *totem;
 
1060
        GtkWindow               *main_window;
 
1061
        GtkBuilder              *builder;
 
1062
        GtkWidget               *main_box;
 
1063
        GtkTreeSelection        *selection;
 
1064
        TotemChaptersPlugin     *cplugin;
 
1065
        GtkCellRenderer         *renderer;
 
1066
        GtkTreeViewColumn       *column;
 
1067
        gchar                   *mrl;
 
1068
 
 
1069
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
1070
 
 
1071
        cplugin = TOTEM_CHAPTERS_PLUGIN (plugin);
 
1072
        totem = g_object_get_data (G_OBJECT (plugin), "object");
 
1073
        main_window = totem_get_main_window (totem);
 
1074
 
 
1075
        builder = totem_plugin_load_interface ("chapters", "chapters-list.ui", TRUE,
 
1076
                                               main_window, cplugin);
 
1077
        g_object_unref (main_window);
 
1078
 
 
1079
        if (builder == NULL)
 
1080
                return;
 
1081
 
 
1082
        cplugin->priv->settings = g_settings_new (TOTEM_GSETTINGS_SCHEMA);
 
1083
        cplugin->priv->autoload = g_settings_get_boolean (cplugin->priv->settings, "autoload-chapters");
 
1084
        g_signal_connect (cplugin->priv->settings, "changed::autoload-chapters", (GCallback) autoload_changed_cb, cplugin);
 
1085
 
 
1086
        cplugin->priv->tree = GTK_WIDGET (gtk_builder_get_object (builder, "chapters_tree_view"));
 
1087
        cplugin->priv->action_group = GTK_ACTION_GROUP (gtk_builder_get_object (builder, "chapters-action-group"));
 
1088
        g_object_ref (cplugin->priv->action_group);
 
1089
        cplugin->priv->ui_manager = GTK_UI_MANAGER (gtk_builder_get_object (builder, "totem-chapters-ui-manager"));
 
1090
        g_object_ref (cplugin->priv->ui_manager);
 
1091
 
 
1092
        renderer = gtk_cell_renderer_pixbuf_new ();
 
1093
        column = gtk_tree_view_column_new_with_attributes (_("Chapter Screenshot"), renderer, "pixbuf", CHAPTERS_PIXBUF_COLUMN, NULL);
 
1094
        gtk_tree_view_append_column (GTK_TREE_VIEW (cplugin->priv->tree), column);
 
1095
 
 
1096
        renderer = gtk_cell_renderer_text_new ();
 
1097
 
 
1098
        g_object_set (renderer, "editable", TRUE, NULL);
 
1099
        g_signal_connect (G_OBJECT (renderer), "editing-started",
 
1100
                          G_CALLBACK (prepare_chapter_edit), cplugin);
 
1101
        g_signal_connect (G_OBJECT (renderer), "edited",
 
1102
                          G_CALLBACK (finish_chapter_edit), cplugin);
 
1103
 
 
1104
        column = gtk_tree_view_column_new_with_attributes (_("Chapter Title"), renderer,
 
1105
                                                           "markup", CHAPTERS_TITLE_COLUMN, NULL);
 
1106
        gtk_tree_view_append_column (GTK_TREE_VIEW (cplugin->priv->tree), column);
 
1107
 
 
1108
        cplugin->priv->totem = g_object_ref (totem);
 
1109
        /* for read operation */
 
1110
        cplugin->priv->cancellable[0] = NULL;
 
1111
        /* for write operation */
 
1112
        cplugin->priv->cancellable[1] = NULL;
 
1113
        cplugin->priv->edit_chapter = NULL;
 
1114
        cplugin->priv->last_frame = NULL;
 
1115
        cplugin->priv->cmml_mrl = NULL;
 
1116
        cplugin->priv->last_time = 0;
 
1117
 
 
1118
        cplugin->priv->add_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_button"));
 
1119
        cplugin->priv->remove_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove_button"));
 
1120
        cplugin->priv->save_button = GTK_WIDGET (gtk_builder_get_object (builder, "save_button"));
 
1121
        cplugin->priv->goto_button = GTK_WIDGET (gtk_builder_get_object (builder, "goto_button"));
 
1122
        cplugin->priv->load_button = GTK_WIDGET (gtk_builder_get_object (builder, "load_button"));
 
1123
        cplugin->priv->continue_button = GTK_WIDGET (gtk_builder_get_object (builder, "continue_button"));
 
1124
 
 
1125
        gtk_widget_hide (cplugin->priv->load_button);
 
1126
        gtk_widget_hide (cplugin->priv->continue_button);
 
1127
 
 
1128
        cplugin->priv->list_box = GTK_WIDGET (gtk_builder_get_object (builder, "main_vbox"));
 
1129
        cplugin->priv->load_box = GTK_WIDGET (gtk_builder_get_object (builder, "load_vbox"));
 
1130
 
 
1131
        main_box = gtk_vbox_new (FALSE, 6);
 
1132
        gtk_box_pack_start (GTK_BOX (main_box), cplugin->priv->list_box, TRUE, TRUE, 0);
 
1133
        gtk_box_pack_start (GTK_BOX (main_box), cplugin->priv->load_box, TRUE, TRUE, 0);
 
1134
 
 
1135
        set_no_data_visible (TRUE, FALSE, cplugin);
 
1136
 
 
1137
        totem_add_sidebar_page (totem, "chapters", _("Chapters"), main_box);
 
1138
 
 
1139
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cplugin->priv->tree));
 
1140
        gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
 
1141
 
 
1142
        g_signal_connect (G_OBJECT (totem),
 
1143
                          "file-opened",
 
1144
                          G_CALLBACK (totem_file_opened_async_cb),
 
1145
                          plugin);
 
1146
        g_signal_connect (G_OBJECT (totem),
 
1147
                          "file-closed",
 
1148
                          G_CALLBACK (totem_file_closed_cb),
 
1149
                          plugin);
 
1150
        g_signal_connect (G_OBJECT (selection),
 
1151
                          "changed",
 
1152
                          G_CALLBACK (chapter_selection_changed_cb),
 
1153
                          plugin);
 
1154
 
 
1155
        mrl = totem_get_current_mrl (cplugin->priv->totem);
 
1156
        if (mrl != NULL)
 
1157
                totem_file_opened_async_cb (cplugin->priv->totem, mrl, cplugin);
 
1158
 
 
1159
        g_object_unref (builder);
 
1160
        g_free (mrl);
 
1161
}
 
1162
 
 
1163
static void
 
1164
impl_deactivate (PeasActivatable *plugin)
 
1165
{
 
1166
        TotemObject             *totem;
 
1167
        TotemChaptersPlugin     *cplugin;
 
1168
 
 
1169
        g_return_if_fail (TOTEM_IS_CHAPTERS_PLUGIN (plugin));
 
1170
 
 
1171
        totem = g_object_get_data (G_OBJECT (plugin), "object");
 
1172
        cplugin = TOTEM_CHAPTERS_PLUGIN (plugin);
 
1173
 
 
1174
        /* If there are unsaved changes to the chapter data, ask the user if they'd like to save them. */
 
1175
        if (gtk_widget_get_sensitive (cplugin->priv->save_button) == TRUE) {
 
1176
                GtkWidget *dialog;
 
1177
                GtkWindow *main_window;
 
1178
 
 
1179
                main_window = totem_object_get_main_window (totem);
 
1180
                dialog = gtk_message_dialog_new (main_window, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 
1181
                                                 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
 
1182
                                                 _("Save changes to chapter list before closing?"));
 
1183
                g_object_unref (main_window);
 
1184
 
 
1185
                gtk_dialog_add_buttons (GTK_DIALOG (dialog),
 
1186
                                        /* Translators: close Totem without saving changes to the chapter list of the current movie. */
 
1187
                                        _("Close without Saving"), GTK_RESPONSE_CLOSE,
 
1188
                                        /* Translators: save changes to the chapter list of the current movie before closing Totem. */
 
1189
                                        _("Save"), GTK_RESPONSE_OK,
 
1190
                                        NULL);
 
1191
                gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
 
1192
                                                          _("If you don't save, changes to the chapter list will be lost."));
 
1193
 
 
1194
                if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
 
1195
                        /* Save the chapter list before closing */
 
1196
                        save_button_clicked_cb (GTK_BUTTON (cplugin->priv->save_button), cplugin);
 
1197
 
 
1198
                        /* Prevent it being cancelled further down in this function */
 
1199
                        cplugin->priv->cancellable[1] = NULL;
 
1200
                }
 
1201
 
 
1202
                gtk_widget_destroy (dialog);
 
1203
        }
 
1204
 
 
1205
        /* FIXME: do not cancel async operation if any */
 
1206
 
 
1207
        g_signal_handlers_disconnect_by_func (G_OBJECT (totem),
 
1208
                                              totem_file_opened_async_cb,
 
1209
                                              plugin);
 
1210
        g_signal_handlers_disconnect_by_func (G_OBJECT (totem),
 
1211
                                              totem_file_closed_cb,
 
1212
                                              plugin);
 
1213
        if (cplugin->priv->settings != NULL)
 
1214
                g_object_unref (cplugin->priv->settings);
 
1215
 
 
1216
        if (G_UNLIKELY (cplugin->priv->last_frame != NULL))
 
1217
                g_object_unref (G_OBJECT (cplugin->priv->last_frame));
 
1218
 
 
1219
        if (G_UNLIKELY (cplugin->priv->edit_chapter != NULL))
 
1220
                gtk_widget_destroy (GTK_WIDGET (cplugin->priv->edit_chapter));
 
1221
 
 
1222
        if (G_LIKELY (cplugin->priv->action_group != NULL))
 
1223
                g_object_unref (cplugin->priv->action_group);
 
1224
 
 
1225
        if (G_LIKELY (cplugin->priv->ui_manager != NULL))
 
1226
                g_object_unref (cplugin->priv->ui_manager);
 
1227
 
 
1228
        if (G_LIKELY (cplugin->priv->cancellable[0] != NULL))
 
1229
                g_cancellable_cancel (cplugin->priv->cancellable[0]);
 
1230
 
 
1231
        if (G_LIKELY (cplugin->priv->cancellable[1] != NULL))
 
1232
                g_cancellable_cancel (cplugin->priv->cancellable[1]);
 
1233
 
 
1234
 
 
1235
        g_object_unref (cplugin->priv->totem);
 
1236
        g_free (cplugin->priv->cmml_mrl);
 
1237
 
 
1238
        totem_remove_sidebar_page (totem, "chapters");
 
1239
}