~ubuntu-branches/ubuntu/precise/latexila/precise

« back to all changes in this revision

Viewing changes to src/file_browser.c

  • Committer: Bazaar Package Importer
  • Author(s): Tanguy Ortolo
  • Date: 2010-04-26 22:13:00 UTC
  • Revision ID: james.westby@ubuntu.com-20100426221300-6pa79a1yk5tino7y
Tags: upstream-0.2.0
ImportĀ upstreamĀ versionĀ 0.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This file is part of LaTeXila.
 
3
 *
 
4
 * Copyright Ā© 2009, 2010 SĆ©bastien Wilmet
 
5
 *
 
6
 * LaTeXila is free software: you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation, either version 3 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * LaTeXila is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with LaTeXila.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include <string.h> // for strcmp
 
21
#include <gtk/gtk.h>
 
22
 
 
23
#include "main.h"
 
24
#include "config.h"
 
25
#include "print.h"
 
26
#include "file_browser.h"
 
27
#include "callbacks.h"
 
28
#include "external_commands.h"
 
29
 
 
30
static void fill_list_store_with_dir (const gchar *directory);
 
31
static void cb_go_to_home_dir (GtkButton *button, gpointer user_data);
 
32
static void cb_go_to_parent_dir (GtkButton *button, gpointer user_data);
 
33
static void cb_jump_dir_current_doc (GtkButton *button, gpointer user_data);
 
34
static void cb_file_browser_row_activated (GtkTreeView *tree_view,
 
35
                GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
 
36
static gint sort_list_alphabetical_order (gconstpointer a, gconstpointer b);
 
37
static void cb_combo_parent_dir_changed (GtkComboBox *combo_box,
 
38
                gpointer user_data);
 
39
 
 
40
// list of files and directories of the current directory
 
41
static GtkListStore *list_store;
 
42
// for the list of directories showed in a combobox
 
43
static GtkListStore *parent_dir_store;
 
44
static GtkComboBox *combo;
 
45
 
 
46
void
 
47
init_file_browser (GtkWidget *vbox)
 
48
{
 
49
        /* mini-toolbar */
 
50
        GtkWidget *hbox = gtk_hbox_new (TRUE, 0);
 
51
        gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
 
52
 
 
53
        // go to the home user directory
 
54
        {
 
55
                GtkWidget *home_button = gtk_button_new ();
 
56
                gtk_button_set_relief (GTK_BUTTON (home_button), GTK_RELIEF_NONE);
 
57
                GtkWidget *home_icon = gtk_image_new_from_stock (GTK_STOCK_HOME,
 
58
                                GTK_ICON_SIZE_BUTTON);
 
59
                gtk_container_add (GTK_CONTAINER (home_button), home_icon);
 
60
                gtk_widget_set_tooltip_text (home_button, _("Go to the home directory"));
 
61
                g_signal_connect (G_OBJECT (home_button), "clicked",
 
62
                                G_CALLBACK (cb_go_to_home_dir), NULL);
 
63
                gtk_box_pack_start (GTK_BOX (hbox), home_button, TRUE, TRUE, 0);
 
64
        }
 
65
 
 
66
        // go to the parent directory
 
67
        {
 
68
                GtkWidget *parent_dir_button = gtk_button_new ();
 
69
                gtk_button_set_relief (GTK_BUTTON (parent_dir_button), GTK_RELIEF_NONE);
 
70
                GtkWidget *parent_dir_icon = gtk_image_new_from_stock (GTK_STOCK_GO_UP,
 
71
                                GTK_ICON_SIZE_BUTTON);
 
72
                gtk_container_add (GTK_CONTAINER (parent_dir_button), parent_dir_icon);
 
73
                gtk_widget_set_tooltip_text (parent_dir_button, _("Go to the parent directory"));
 
74
                g_signal_connect (G_OBJECT (parent_dir_button), "clicked",
 
75
                                G_CALLBACK (cb_go_to_parent_dir), NULL);
 
76
                gtk_box_pack_start (GTK_BOX (hbox), parent_dir_button, TRUE, TRUE, 0);
 
77
        }
 
78
 
 
79
        // jump to the directory of the current document
 
80
        {
 
81
                GtkWidget *jump_button = gtk_button_new ();
 
82
                gtk_button_set_relief (GTK_BUTTON (jump_button), GTK_RELIEF_NONE);
 
83
                GtkWidget *jump_icon = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO,
 
84
                                GTK_ICON_SIZE_BUTTON);
 
85
                gtk_container_add (GTK_CONTAINER (jump_button), jump_icon);
 
86
                gtk_widget_set_tooltip_text (jump_button,
 
87
                                _("Go to the directory of the current document"));
 
88
                g_signal_connect (G_OBJECT (jump_button), "clicked",
 
89
                                G_CALLBACK (cb_jump_dir_current_doc), NULL);
 
90
                gtk_box_pack_start (GTK_BOX (hbox), jump_button, TRUE, TRUE, 0);
 
91
        }
 
92
 
 
93
        // refresh
 
94
        {
 
95
                GtkWidget *refresh_button = gtk_button_new ();
 
96
                gtk_button_set_relief (GTK_BUTTON (refresh_button), GTK_RELIEF_NONE);
 
97
                GtkWidget *refresh_icon = gtk_image_new_from_stock (GTK_STOCK_REFRESH,
 
98
                                GTK_ICON_SIZE_BUTTON);
 
99
                gtk_container_add (GTK_CONTAINER (refresh_button), refresh_icon);
 
100
                gtk_widget_set_tooltip_text (refresh_button, _("Refresh"));
 
101
                g_signal_connect (G_OBJECT (refresh_button), "clicked",
 
102
                                G_CALLBACK (cb_file_browser_refresh), NULL);
 
103
                gtk_box_pack_start (GTK_BOX (hbox), refresh_button, TRUE, TRUE, 0);
 
104
        }
 
105
 
 
106
        /* list of parent directories */
 
107
        {
 
108
                parent_dir_store = gtk_list_store_new (N_COLS_PARENT_DIR,
 
109
                                G_TYPE_STRING,  // indentation (spaces)
 
110
                                G_TYPE_STRING,  // stock-id of a pixbuf
 
111
                                G_TYPE_STRING,  // directory name
 
112
                                G_TYPE_STRING   // full path
 
113
                                );
 
114
 
 
115
                GtkWidget *combo_box =
 
116
                        gtk_combo_box_new_with_model (GTK_TREE_MODEL (parent_dir_store));
 
117
                g_object_unref (parent_dir_store);
 
118
                combo = GTK_COMBO_BOX (combo_box);
 
119
 
 
120
                g_signal_connect (G_OBJECT (combo_box), "changed",
 
121
                                G_CALLBACK (cb_combo_parent_dir_changed), NULL);
 
122
 
 
123
                // indentation
 
124
                GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
 
125
                gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
 
126
                gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
 
127
                                "text", COL_PARENT_DIR_INDENT, NULL);
 
128
 
 
129
                // pixbuf
 
130
                renderer = gtk_cell_renderer_pixbuf_new ();
 
131
                gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
 
132
                gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
 
133
                                "stock-id", COL_PARENT_DIR_PIXBUF, NULL);
 
134
 
 
135
                // directory
 
136
                renderer = gtk_cell_renderer_text_new ();
 
137
                gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
 
138
                gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
 
139
                                "text", COL_PARENT_DIR_DIRECTORY, NULL);
 
140
                // omit characters at the end of the text if not enough place
 
141
                g_object_set (renderer, "ellipsize-set", TRUE,
 
142
                                "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 
143
 
 
144
                gtk_box_pack_start (GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
 
145
        }
 
146
 
 
147
        /* list of files and directories */
 
148
        {
 
149
                list_store = gtk_list_store_new (N_COLS_FILE_BROWSER,
 
150
                                G_TYPE_STRING, // stock-id of a pixbuf
 
151
                                G_TYPE_STRING  // file
 
152
                                );
 
153
 
 
154
                fill_list_store_with_dir (NULL);
 
155
 
 
156
                GtkWidget *tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
 
157
                g_object_unref (list_store);
 
158
                gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
 
159
 
 
160
                // dubble-click on a row will open the file
 
161
                g_signal_connect (G_OBJECT (tree_view), "row-activated",
 
162
                                G_CALLBACK (cb_file_browser_row_activated), NULL);
 
163
 
 
164
                GtkTreeViewColumn *column = gtk_tree_view_column_new ();
 
165
                gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
 
166
 
 
167
                // show the icon
 
168
                GtkCellRenderer *renderer1 = gtk_cell_renderer_pixbuf_new ();
 
169
                gtk_tree_view_column_pack_start (column, renderer1, FALSE);
 
170
                gtk_tree_view_column_set_attributes (column, renderer1,
 
171
                                "stock-id", COL_FILE_BROWSER_PIXBUF,
 
172
                                NULL);
 
173
 
 
174
                // show the file name
 
175
                GtkCellRenderer *renderer2 = gtk_cell_renderer_text_new ();
 
176
                gtk_tree_view_column_pack_start (column, renderer2, FALSE);
 
177
                gtk_tree_view_column_set_attributes (column, renderer2,
 
178
                                "text", COL_FILE_BROWSER_FILE,
 
179
                                NULL);
 
180
 
 
181
                // with a scrollbar
 
182
                GtkWidget *scrollbar = gtk_scrolled_window_new (NULL, NULL);
 
183
                gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollbar),
 
184
                                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
185
                gtk_container_add (GTK_CONTAINER (scrollbar), tree_view);
 
186
 
 
187
                gtk_box_pack_start (GTK_BOX (vbox), scrollbar, TRUE, TRUE, 0);
 
188
        }
 
189
}
 
190
 
 
191
void
 
192
cb_file_browser_refresh (GtkButton *button, gpointer user_data)
 
193
{
 
194
        fill_list_store_with_dir (NULL);
 
195
}
 
196
 
 
197
 
 
198
static void
 
199
fill_list_store_with_dir (const gchar *directory)
 
200
{
 
201
        // if directory is NULL, open the current directory
 
202
        GError *error = NULL;
 
203
        GDir *dir;
 
204
        if (directory == NULL)
 
205
                dir = g_dir_open (latexila.prefs.file_browser_dir, 0, &error);
 
206
        else
 
207
                dir = g_dir_open (directory, 0, &error);
 
208
 
 
209
        if (error != NULL)
 
210
        {
 
211
                print_warning ("File browser: %s", error->message);
 
212
 
 
213
                // warning dialog
 
214
                GtkWidget *dialog = gtk_message_dialog_new (latexila.main_window,
 
215
                                GTK_DIALOG_DESTROY_WITH_PARENT,
 
216
                                GTK_MESSAGE_WARNING,
 
217
                                GTK_BUTTONS_CLOSE,
 
218
                                _("File Browser"));
 
219
                gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
 
220
                                "%s", error->message);
 
221
                gtk_dialog_run (GTK_DIALOG (dialog));
 
222
                gtk_widget_destroy (dialog);
 
223
 
 
224
                g_error_free (error);
 
225
                return;
 
226
        }
 
227
 
 
228
        gtk_list_store_clear (list_store);
 
229
        gtk_list_store_clear (parent_dir_store);
 
230
 
 
231
        if (directory != NULL)
 
232
        {
 
233
                g_free (latexila.prefs.file_browser_dir);
 
234
                latexila.prefs.file_browser_dir = g_strdup (directory);
 
235
        }
 
236
 
 
237
        /* append all the files contained in the directory */
 
238
        const gchar *read_name = NULL;
 
239
        GList *directory_list = NULL;
 
240
        GList *file_list = NULL;
 
241
        while ((read_name = g_dir_read_name (dir)) != NULL)
 
242
        {
 
243
                // not show hidden files
 
244
                if (read_name[0] == '.' && (! latexila.prefs.file_browser_show_all_files
 
245
                                        || ! latexila.prefs.file_browser_show_hidden_files))
 
246
                        continue;
 
247
 
 
248
                gchar *full_path = g_build_filename (latexila.prefs.file_browser_dir,
 
249
                                read_name, NULL);
 
250
 
 
251
                // make a copy of read_name, else there are memory errors if the
 
252
                // directory is very big (/usr/bin for example)
 
253
                gchar *tmp = g_strdup (read_name);
 
254
 
 
255
                if (g_file_test (full_path, G_FILE_TEST_IS_DIR))
 
256
                        directory_list = g_list_prepend (directory_list, (gpointer) tmp);
 
257
 
 
258
                // show all files or only *.tex, *.pdf, *.dvi, *.ps and *.bib
 
259
                else if (latexila.prefs.file_browser_show_all_files
 
260
                                || g_str_has_suffix (full_path, ".tex")
 
261
                                || g_str_has_suffix (full_path, ".pdf")
 
262
                                || g_str_has_suffix (full_path, ".dvi")
 
263
                                || g_str_has_suffix (full_path, ".ps")
 
264
                                || g_str_has_suffix (full_path, ".bib"))
 
265
                {
 
266
                        file_list = g_list_prepend (file_list, (gpointer) tmp);
 
267
                }
 
268
 
 
269
                g_free (full_path);
 
270
        }
 
271
 
 
272
        g_dir_close (dir);
 
273
 
 
274
        // sort the lists in alphabetical order
 
275
        directory_list = g_list_sort (directory_list, sort_list_alphabetical_order);
 
276
        file_list = g_list_sort (file_list, sort_list_alphabetical_order);
 
277
 
 
278
        GtkTreeIter iter;
 
279
 
 
280
        // traverse the directory list
 
281
        GList *current = directory_list;
 
282
        while (current != NULL)
 
283
        {
 
284
                gchar *directory = current->data;
 
285
 
 
286
                // append the directory to the list store
 
287
                gtk_list_store_append (list_store, &iter);
 
288
                gtk_list_store_set (list_store, &iter,
 
289
                                COL_FILE_BROWSER_PIXBUF, GTK_STOCK_DIRECTORY,
 
290
                                COL_FILE_BROWSER_FILE, directory,
 
291
                                -1);
 
292
 
 
293
                g_free (directory);
 
294
                current = g_list_next (current);
 
295
        }
 
296
 
 
297
        // traverse the file list
 
298
        current = file_list;
 
299
        while (current != NULL)
 
300
        {
 
301
                gchar *file = current->data;
 
302
 
 
303
                gchar *stock_id;
 
304
                if (g_str_has_suffix (file, ".tex"))
 
305
                        stock_id = GTK_STOCK_EDIT;
 
306
                else if (g_str_has_suffix (file, ".pdf"))
 
307
                        stock_id = "view_pdf";
 
308
                else if (g_str_has_suffix (file, ".dvi"))
 
309
                        stock_id = "view_dvi";
 
310
                else if (g_str_has_suffix (file, ".ps"))
 
311
                        stock_id = "view_ps";
 
312
                else
 
313
                        stock_id = GTK_STOCK_FILE;
 
314
 
 
315
 
 
316
                // append the file to the list store
 
317
                gtk_list_store_append (list_store, &iter);
 
318
                gtk_list_store_set (list_store, &iter,
 
319
                                COL_FILE_BROWSER_PIXBUF, stock_id,
 
320
                                COL_FILE_BROWSER_FILE, file,
 
321
                                -1);
 
322
 
 
323
                g_free (file);
 
324
                current = g_list_next (current);
 
325
        }
 
326
 
 
327
        g_list_free (directory_list);
 
328
        g_list_free (file_list);
 
329
 
 
330
        /* fill the parent directory list */
 
331
        if (! g_path_is_absolute (latexila.prefs.file_browser_dir))
 
332
                return;
 
333
 
 
334
        GList *parent_dirs = NULL;
 
335
        parent_dirs = g_list_append (parent_dirs,
 
336
                        g_strdup (latexila.prefs.file_browser_dir));
 
337
        gchar *path = latexila.prefs.file_browser_dir;
 
338
 
 
339
        while (TRUE)
 
340
        {
 
341
                gchar *parent = g_path_get_dirname (path);
 
342
                parent_dirs = g_list_prepend (parent_dirs, parent);
 
343
                path = parent;
 
344
                if (strcmp (parent, "/") == 0)
 
345
                        break;
 
346
        }
 
347
 
 
348
        current = parent_dirs;
 
349
        gint i = 0;
 
350
        while (current != NULL)
 
351
        {
 
352
                gchar *current_dir = current->data;
 
353
 
 
354
                // basename
 
355
                gchar *basename;
 
356
                if (i == 0)
 
357
                        basename = g_strdup (_("File System"));
 
358
                else
 
359
                        basename = g_path_get_basename (current_dir);
 
360
 
 
361
                // indentation
 
362
                gchar *indent = g_strnfill (i * 2, ' ');
 
363
 
 
364
                // pixbuf
 
365
                gchar *stock_id;
 
366
                if (i == 0)
 
367
                        stock_id = GTK_STOCK_HARDDISK;
 
368
                else if (strcmp (current_dir, g_get_home_dir ()) == 0)
 
369
                        stock_id = GTK_STOCK_HOME;
 
370
                else
 
371
                        stock_id = GTK_STOCK_DIRECTORY;
 
372
 
 
373
                gtk_list_store_append (parent_dir_store, &iter);
 
374
                gtk_list_store_set (parent_dir_store, &iter,
 
375
                                COL_PARENT_DIR_INDENT, indent,
 
376
                                COL_PARENT_DIR_PIXBUF, stock_id,
 
377
                                COL_PARENT_DIR_DIRECTORY, basename,
 
378
                                COL_PARENT_DIR_PATH, current_dir,
 
379
                                -1);
 
380
 
 
381
                g_free (current_dir);
 
382
                g_free (basename);
 
383
                g_free (indent);
 
384
                current = g_list_next (current);
 
385
                i++;
 
386
        }
 
387
 
 
388
        // select the last parent directory
 
389
        gtk_combo_box_set_active_iter (combo, &iter);
 
390
}
 
391
 
 
392
static void
 
393
cb_go_to_home_dir (GtkButton *button, gpointer user_data)
 
394
{
 
395
        fill_list_store_with_dir (g_get_home_dir ());
 
396
}
 
397
 
 
398
static void
 
399
cb_go_to_parent_dir (GtkButton *button, gpointer user_data)
 
400
{
 
401
        gchar *path = g_path_get_dirname (latexila.prefs.file_browser_dir);
 
402
        fill_list_store_with_dir (path);
 
403
        g_free (path);
 
404
}
 
405
 
 
406
static void
 
407
cb_jump_dir_current_doc (GtkButton *button, gpointer user_data)
 
408
{
 
409
        if (latexila.active_doc == NULL || latexila.active_doc->path == NULL)
 
410
                return;
 
411
 
 
412
        gchar *path = g_path_get_dirname (latexila.active_doc->path);
 
413
        fill_list_store_with_dir (path);
 
414
        g_free (path);
 
415
}
 
416
 
 
417
static void
 
418
cb_file_browser_row_activated (GtkTreeView *tree_view, GtkTreePath *path,
 
419
                GtkTreeViewColumn *column, gpointer user_data)
 
420
{
 
421
        GtkTreeIter iter;
 
422
        GtkTreeModel *model = GTK_TREE_MODEL (list_store);
 
423
        gtk_tree_model_get_iter (model, &iter, path);
 
424
        
 
425
        gchar *file = NULL;
 
426
        gtk_tree_model_get (model, &iter,
 
427
                        COL_FILE_BROWSER_FILE, &file,
 
428
                        -1);
 
429
 
 
430
        gchar *full_path = g_build_filename (latexila.prefs.file_browser_dir,
 
431
                        file, NULL);
 
432
 
 
433
        // open the directory
 
434
        if (g_file_test (full_path, G_FILE_TEST_IS_DIR))
 
435
                fill_list_store_with_dir (full_path);
 
436
 
 
437
        // view the document
 
438
        else if (g_str_has_suffix (full_path, ".pdf"))
 
439
                view_document (_("View PDF"), full_path);
 
440
        else if (g_str_has_suffix (full_path, ".dvi"))
 
441
                view_document (_("View DVI"), full_path);
 
442
        else if (g_str_has_suffix (full_path, ".ps"))
 
443
                view_document (_("View PS"), full_path);
 
444
 
 
445
        // open the file
 
446
        else
 
447
                open_new_document_without_uri (full_path);
 
448
 
 
449
        g_free (full_path);
 
450
}
 
451
 
 
452
static gint
 
453
sort_list_alphabetical_order (gconstpointer a, gconstpointer b)
 
454
{
 
455
        return g_utf8_collate ((char *) a, (char *) b);
 
456
}
 
457
 
 
458
static void
 
459
cb_combo_parent_dir_changed (GtkComboBox *combo_box, gpointer user_data)
 
460
{
 
461
        GtkTreeIter iter;
 
462
        if (gtk_combo_box_get_active_iter (combo_box, &iter))
 
463
        {
 
464
                gchar *path;
 
465
                GtkTreeModel *model = gtk_combo_box_get_model (combo_box);
 
466
                gtk_tree_model_get (model, &iter,
 
467
                                COL_PARENT_DIR_PATH, &path,
 
468
                                -1);
 
469
 
 
470
                // avoid infinite loop (this function is called in
 
471
                // fill_list_store_with_dir())
 
472
                if (strcmp (path, latexila.prefs.file_browser_dir) != 0)
 
473
                        fill_list_store_with_dir (path);
 
474
                g_free (path);
 
475
        }
 
476
}