1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- |
---|
2 | * test-widget.c |
---|
3 | * |
---|
4 | * Copyright (C) 2001 |
---|
5 | * Mikael Hermansson<tyan@linux.se> |
---|
6 | * |
---|
7 | * Copyright (C) 2003 - Gustavo Giráldez <gustavo.giraldez@gmx.net> |
---|
8 | * |
---|
9 | * This program is free software; you can redistribute it and/or modify |
---|
10 | * it under the terms of the GNU General Public License as published by |
---|
11 | * the Free Software Foundation; either version 2 of the License, or |
---|
12 | * (at your option) any later version. |
---|
13 | * |
---|
14 | * This program is distributed in the hope that it will be useful, |
---|
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
17 | * GNU General Public License for more details. |
---|
18 | * |
---|
19 | * You should have received a copy of the GNU General Public License |
---|
20 | * along with this program; if not, write to the Free Software |
---|
21 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
---|
22 | */ |
---|
23 | |
---|
24 | #include <stdio.h> |
---|
25 | #include <string.h> |
---|
26 | #include <gtk/gtk.h> |
---|
27 | #include <libgnomevfs/gnome-vfs-init.h> |
---|
28 | #include <libgnomevfs/gnome-vfs-mime-utils.h> |
---|
29 | #include <libgnomevfs/gnome-vfs-utils.h> |
---|
30 | #include <libgnomeprintui/gnome-print-job-preview.h> |
---|
31 | #include <gtksourceview/gtksourceview.h> |
---|
32 | #include <gtksourceview/gtksourcelanguage.h> |
---|
33 | #include <gtksourceview/gtksourcelanguagesmanager.h> |
---|
34 | #include <gtksourceview/gtksourceprintjob.h> |
---|
35 | |
---|
36 | |
---|
37 | /* Global list of open windows */ |
---|
38 | |
---|
39 | static GList *windows = NULL; |
---|
40 | |
---|
41 | |
---|
42 | /* Private data structures */ |
---|
43 | |
---|
44 | #define READ_BUFFER_SIZE 4096 |
---|
45 | |
---|
46 | #define MARKER_TYPE_1 "one" |
---|
47 | #define MARKER_TYPE_2 "two" |
---|
48 | |
---|
49 | |
---|
50 | /* Private prototypes -------------------------------------------------------- */ |
---|
51 | |
---|
52 | static void open_file_cb (GtkAction *action, |
---|
53 | gpointer user_data); |
---|
54 | |
---|
55 | static void new_view_cb (GtkAction *action, |
---|
56 | gpointer user_data); |
---|
57 | static void numbers_toggled_cb (GtkAction *action, |
---|
58 | gpointer user_data); |
---|
59 | static void markers_toggled_cb (GtkAction *action, |
---|
60 | gpointer user_data); |
---|
61 | static void margin_toggled_cb (GtkAction *action, |
---|
62 | gpointer user_data); |
---|
63 | static void auto_indent_toggled_cb (GtkAction *action, |
---|
64 | gpointer user_data); |
---|
65 | static void insert_spaces_toggled_cb (GtkAction *action, |
---|
66 | gpointer user_data); |
---|
67 | static void tabs_toggled_cb (GtkAction *action, |
---|
68 | GtkAction *current, |
---|
69 | gpointer user_data); |
---|
70 | static void print_preview_cb (GtkAction *action, |
---|
71 | gpointer user_data); |
---|
72 | |
---|
73 | static GtkWidget *create_view_window (GtkSourceBuffer *buffer, |
---|
74 | GtkSourceView *from); |
---|
75 | |
---|
76 | |
---|
77 | /* Actions & UI definition ---------------------------------------------------- */ |
---|
78 | |
---|
79 | static GtkActionEntry buffer_action_entries[] = { |
---|
80 | { "Open", GTK_STOCK_OPEN, "_Open", "<control>O", |
---|
81 | "Open a file", G_CALLBACK (open_file_cb) }, |
---|
82 | { "Quit", GTK_STOCK_QUIT, "_Quit", "<control>Q", |
---|
83 | "Exit the application", G_CALLBACK (gtk_main_quit) }, |
---|
84 | }; |
---|
85 | |
---|
86 | static GtkActionEntry view_action_entries[] = { |
---|
87 | { "FileMenu", NULL, "_File" }, |
---|
88 | { "ViewMenu", NULL, "_View" }, |
---|
89 | { "PrintPreview", GTK_STOCK_PRINT, "_Print Preview", "<control>P", |
---|
90 | "Preview printing of the file", G_CALLBACK (print_preview_cb) }, |
---|
91 | { "NewView", GTK_STOCK_NEW, "_New View", NULL, |
---|
92 | "Create a new view of the file", G_CALLBACK (new_view_cb) }, |
---|
93 | { "TabsWidth", NULL, "_Tabs Width" }, |
---|
94 | }; |
---|
95 | |
---|
96 | static GtkToggleActionEntry toggle_entries[] = { |
---|
97 | { "ShowNumbers", NULL, "Show _Line Numbers", NULL, |
---|
98 | "Toggle visibility of line numbers in the left margin", |
---|
99 | G_CALLBACK (numbers_toggled_cb), FALSE }, |
---|
100 | { "ShowMarkers", NULL, "Show _Markers", NULL, |
---|
101 | "Toggle visibility of markers in the left margin", |
---|
102 | G_CALLBACK (markers_toggled_cb), FALSE }, |
---|
103 | { "ShowMargin", NULL, "Show M_argin", NULL, |
---|
104 | "Toggle visibility of right margin indicator", |
---|
105 | G_CALLBACK (margin_toggled_cb), FALSE }, |
---|
106 | { "AutoIndent", NULL, "Enable _Auto Indent", NULL, |
---|
107 | "Toggle automatic auto indentation of text", |
---|
108 | G_CALLBACK (auto_indent_toggled_cb), FALSE }, |
---|
109 | { "InsertSpaces", NULL, "Insert _Spaces Instead of Tabs", NULL, |
---|
110 | "Whether to insert space characters when inserting tabulations", |
---|
111 | G_CALLBACK (insert_spaces_toggled_cb), FALSE } |
---|
112 | }; |
---|
113 | |
---|
114 | static GtkRadioActionEntry radio_entries[] = { |
---|
115 | { "TabsWidth4", NULL, "4", NULL, "Set tabulation width to 4 spaces", 4 }, |
---|
116 | { "TabsWidth6", NULL, "6", NULL, "Set tabulation width to 6 spaces", 6 }, |
---|
117 | { "TabsWidth8", NULL, "8", NULL, "Set tabulation width to 8 spaces", 8 }, |
---|
118 | { "TabsWidth10", NULL, "10", NULL, "Set tabulation width to 10 spaces", 10 }, |
---|
119 | { "TabsWidth12", NULL, "12", NULL, "Set tabulation width to 12 spaces", 12 } |
---|
120 | }; |
---|
121 | |
---|
122 | static const gchar *view_ui_description = |
---|
123 | "<ui>" |
---|
124 | " <menubar name=\"MainMenu\">" |
---|
125 | " <menu action=\"FileMenu\">" |
---|
126 | " <menuitem action=\"PrintPreview\"/>" |
---|
127 | " </menu>" |
---|
128 | " <menu action=\"ViewMenu\">" |
---|
129 | " <menuitem action=\"NewView\"/>" |
---|
130 | " <separator/>" |
---|
131 | " <menuitem action=\"ShowNumbers\"/>" |
---|
132 | " <menuitem action=\"ShowMarkers\"/>" |
---|
133 | " <menuitem action=\"ShowMargin\"/>" |
---|
134 | " <separator/>" |
---|
135 | " <menuitem action=\"AutoIndent\"/>" |
---|
136 | " <menuitem action=\"InsertSpaces\"/>" |
---|
137 | " <separator/>" |
---|
138 | " <menu action=\"TabsWidth\">" |
---|
139 | " <menuitem action=\"TabsWidth4\"/>" |
---|
140 | " <menuitem action=\"TabsWidth6\"/>" |
---|
141 | " <menuitem action=\"TabsWidth8\"/>" |
---|
142 | " <menuitem action=\"TabsWidth10\"/>" |
---|
143 | " <menuitem action=\"TabsWidth12\"/>" |
---|
144 | " </menu>" |
---|
145 | " </menu>" |
---|
146 | " </menubar>" |
---|
147 | "</ui>"; |
---|
148 | |
---|
149 | static const gchar *buffer_ui_description = |
---|
150 | "<ui>" |
---|
151 | " <menubar name=\"MainMenu\">" |
---|
152 | " <menu action=\"FileMenu\">" |
---|
153 | " <menuitem action=\"Open\"/>" |
---|
154 | " <separator/>" |
---|
155 | " <menuitem action=\"Quit\"/>" |
---|
156 | " </menu>" |
---|
157 | " <menu action=\"ViewMenu\">" |
---|
158 | " </menu>" |
---|
159 | " </menubar>" |
---|
160 | "</ui>"; |
---|
161 | |
---|
162 | |
---|
163 | /* File loading code ----------------------------------------------------------------- */ |
---|
164 | |
---|
165 | static void |
---|
166 | error_dialog (GtkWindow *parent, const gchar *msg, ...) |
---|
167 | { |
---|
168 | va_list ap; |
---|
169 | gchar *tmp; |
---|
170 | GtkWidget *dialog; |
---|
171 | |
---|
172 | va_start (ap, msg); |
---|
173 | tmp = g_strdup_vprintf (msg, ap); |
---|
174 | va_end (ap); |
---|
175 | |
---|
176 | dialog = gtk_message_dialog_new (parent, |
---|
177 | GTK_DIALOG_DESTROY_WITH_PARENT, |
---|
178 | GTK_MESSAGE_ERROR, |
---|
179 | GTK_BUTTONS_OK, |
---|
180 | tmp); |
---|
181 | g_free (tmp); |
---|
182 | |
---|
183 | gtk_dialog_run (GTK_DIALOG (dialog)); |
---|
184 | gtk_widget_destroy (dialog); |
---|
185 | } |
---|
186 | |
---|
187 | static gboolean |
---|
188 | gtk_source_buffer_load_with_encoding (GtkSourceBuffer *source_buffer, |
---|
189 | const gchar *filename, |
---|
190 | const gchar *encoding, |
---|
191 | GError **error) |
---|
192 | { |
---|
193 | GIOChannel *io; |
---|
194 | GtkTextIter iter; |
---|
195 | gchar *buffer; |
---|
196 | gboolean reading; |
---|
197 | |
---|
198 | g_return_val_if_fail (source_buffer != NULL, FALSE); |
---|
199 | g_return_val_if_fail (filename != NULL, FALSE); |
---|
200 | g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (source_buffer), FALSE); |
---|
201 | |
---|
202 | *error = NULL; |
---|
203 | |
---|
204 | io = g_io_channel_new_file (filename, "r", error); |
---|
205 | if (!io) |
---|
206 | { |
---|
207 | error_dialog (NULL, "%s\nFile %s", (*error)->message, filename); |
---|
208 | return FALSE; |
---|
209 | } |
---|
210 | |
---|
211 | if (g_io_channel_set_encoding (io, encoding, error) != G_IO_STATUS_NORMAL) |
---|
212 | { |
---|
213 | error_dialog (NULL, "Failed to set encoding:\n%s\n%s", |
---|
214 | filename, (*error)->message); |
---|
215 | return FALSE; |
---|
216 | } |
---|
217 | |
---|
218 | gtk_source_buffer_begin_not_undoable_action (source_buffer); |
---|
219 | |
---|
220 | gtk_text_buffer_set_text (GTK_TEXT_BUFFER (source_buffer), "", 0); |
---|
221 | buffer = g_malloc (READ_BUFFER_SIZE); |
---|
222 | reading = TRUE; |
---|
223 | while (reading) |
---|
224 | { |
---|
225 | gsize bytes_read; |
---|
226 | GIOStatus status; |
---|
227 | |
---|
228 | status = g_io_channel_read_chars (io, buffer, |
---|
229 | READ_BUFFER_SIZE, &bytes_read, |
---|
230 | error); |
---|
231 | switch (status) |
---|
232 | { |
---|
233 | case G_IO_STATUS_EOF: |
---|
234 | reading = FALSE; |
---|
235 | /* fall through */ |
---|
236 | |
---|
237 | case G_IO_STATUS_NORMAL: |
---|
238 | if (bytes_read == 0) |
---|
239 | { |
---|
240 | continue; |
---|
241 | } |
---|
242 | |
---|
243 | gtk_text_buffer_get_end_iter ( |
---|
244 | GTK_TEXT_BUFFER (source_buffer), &iter); |
---|
245 | gtk_text_buffer_insert (GTK_TEXT_BUFFER (source_buffer), |
---|
246 | &iter, buffer, bytes_read); |
---|
247 | break; |
---|
248 | |
---|
249 | case G_IO_STATUS_AGAIN: |
---|
250 | continue; |
---|
251 | |
---|
252 | case G_IO_STATUS_ERROR: |
---|
253 | default: |
---|
254 | error_dialog (NULL, "%s\nFile %s", (*error)->message, filename); |
---|
255 | |
---|
256 | /* because of error in input we clear already loaded text */ |
---|
257 | gtk_text_buffer_set_text (GTK_TEXT_BUFFER (source_buffer), "", 0); |
---|
258 | |
---|
259 | reading = FALSE; |
---|
260 | break; |
---|
261 | } |
---|
262 | } |
---|
263 | g_free (buffer); |
---|
264 | |
---|
265 | gtk_source_buffer_end_not_undoable_action (source_buffer); |
---|
266 | |
---|
267 | g_io_channel_unref (io); |
---|
268 | |
---|
269 | if (*error) |
---|
270 | return FALSE; |
---|
271 | |
---|
272 | gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (source_buffer), FALSE); |
---|
273 | |
---|
274 | /* move cursor to the beginning */ |
---|
275 | gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (source_buffer), &iter); |
---|
276 | gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (source_buffer), &iter); |
---|
277 | |
---|
278 | return TRUE; |
---|
279 | } |
---|
280 | |
---|
281 | static void |
---|
282 | remove_all_markers (GtkSourceBuffer *buffer) |
---|
283 | { |
---|
284 | GSList *markers; |
---|
285 | GtkTextIter begin, end; |
---|
286 | |
---|
287 | gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end); |
---|
288 | markers = gtk_source_buffer_get_markers_in_region (buffer, &begin, &end); |
---|
289 | while (markers) |
---|
290 | { |
---|
291 | GtkSourceMarker *marker = markers->data; |
---|
292 | |
---|
293 | gtk_source_buffer_delete_marker (buffer, marker); |
---|
294 | markers = g_slist_delete_link (markers, markers); |
---|
295 | } |
---|
296 | } |
---|
297 | |
---|
298 | static gboolean |
---|
299 | open_file (GtkSourceBuffer *buffer, const gchar *filename) |
---|
300 | { |
---|
301 | GtkSourceLanguagesManager *manager; |
---|
302 | GtkSourceLanguage *language = NULL; |
---|
303 | gchar *mime_type; |
---|
304 | GError *err = NULL; |
---|
305 | gchar *uri; |
---|
306 | |
---|
307 | /* get the new language for the file mimetype */ |
---|
308 | manager = g_object_get_data (G_OBJECT (buffer), "languages-manager"); |
---|
309 | |
---|
310 | /* I hate this! */ |
---|
311 | if (g_path_is_absolute (filename)) |
---|
312 | { |
---|
313 | uri = gnome_vfs_get_uri_from_local_path (filename); |
---|
314 | } |
---|
315 | else |
---|
316 | { |
---|
317 | gchar *curdir, *path; |
---|
318 | |
---|
319 | curdir = g_get_current_dir (); |
---|
320 | path = g_strconcat (curdir, "/", filename, NULL); |
---|
321 | g_free (curdir); |
---|
322 | uri = gnome_vfs_get_uri_from_local_path (path); |
---|
323 | g_free (path); |
---|
324 | } |
---|
325 | |
---|
326 | mime_type = gnome_vfs_get_mime_type (uri); |
---|
327 | g_free (uri); |
---|
328 | if (mime_type) |
---|
329 | { |
---|
330 | language = gtk_source_languages_manager_get_language_from_mime_type (manager, |
---|
331 | mime_type); |
---|
332 | |
---|
333 | if (language == NULL) |
---|
334 | { |
---|
335 | g_print ("No language found for mime type `%s'\n", mime_type); |
---|
336 | g_object_set (G_OBJECT (buffer), "highlight", FALSE, NULL); |
---|
337 | } |
---|
338 | else |
---|
339 | { |
---|
340 | g_object_set (G_OBJECT (buffer), "highlight", TRUE, NULL); |
---|
341 | |
---|
342 | gtk_source_buffer_set_language (buffer, language); |
---|
343 | } |
---|
344 | |
---|
345 | g_free (mime_type); |
---|
346 | } |
---|
347 | else |
---|
348 | { |
---|
349 | g_object_set (G_OBJECT (buffer), "highlight", FALSE, NULL); |
---|
350 | |
---|
351 | g_warning ("Couldn't get mime type for file `%s'", filename); |
---|
352 | } |
---|
353 | |
---|
354 | remove_all_markers (buffer); |
---|
355 | gtk_source_buffer_load_with_encoding (buffer, filename, "utf-8", &err); |
---|
356 | g_object_set_data_full (G_OBJECT (buffer), |
---|
357 | "filename", g_strdup (filename), |
---|
358 | (GDestroyNotify) g_free); |
---|
359 | |
---|
360 | if (err != NULL) |
---|
361 | { |
---|
362 | g_error_free (err); |
---|
363 | return FALSE; |
---|
364 | } |
---|
365 | return TRUE; |
---|
366 | } |
---|
367 | |
---|
368 | |
---|
369 | /* Printing callbacks --------------------------------------------------------- */ |
---|
370 | |
---|
371 | static void |
---|
372 | page_cb (GtkSourcePrintJob *job, gpointer user_data) |
---|
373 | { |
---|
374 | g_print ("Printing %.2f%% \r", |
---|
375 | 100.0 * gtk_source_print_job_get_page (job) / |
---|
376 | gtk_source_print_job_get_page_count (job)); |
---|
377 | } |
---|
378 | |
---|
379 | static void |
---|
380 | finished_cb (GtkSourcePrintJob *job, gpointer user_data) |
---|
381 | { |
---|
382 | GnomePrintJob *gjob; |
---|
383 | GtkWidget *preview; |
---|
384 | |
---|
385 | g_print ("\n"); |
---|
386 | gjob = gtk_source_print_job_get_print_job (job); |
---|
387 | preview = gnome_print_job_preview_new (gjob, "test-widget print preview"); |
---|
388 | g_object_unref (gjob); |
---|
389 | g_object_unref (job); |
---|
390 | |
---|
391 | gtk_widget_show (preview); |
---|
392 | } |
---|
393 | |
---|
394 | |
---|
395 | /* View action callbacks -------------------------------------------------------- */ |
---|
396 | |
---|
397 | static void |
---|
398 | numbers_toggled_cb (GtkAction *action, gpointer user_data) |
---|
399 | { |
---|
400 | g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); |
---|
401 | gtk_source_view_set_show_line_numbers ( |
---|
402 | GTK_SOURCE_VIEW (user_data), |
---|
403 | gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); |
---|
404 | } |
---|
405 | |
---|
406 | static void |
---|
407 | markers_toggled_cb (GtkAction *action, gpointer user_data) |
---|
408 | { |
---|
409 | g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); |
---|
410 | gtk_source_view_set_show_line_markers ( |
---|
411 | GTK_SOURCE_VIEW (user_data), |
---|
412 | gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); |
---|
413 | } |
---|
414 | |
---|
415 | static void |
---|
416 | margin_toggled_cb (GtkAction *action, gpointer user_data) |
---|
417 | { |
---|
418 | g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); |
---|
419 | gtk_source_view_set_show_margin ( |
---|
420 | GTK_SOURCE_VIEW (user_data), |
---|
421 | gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); |
---|
422 | } |
---|
423 | |
---|
424 | static void |
---|
425 | auto_indent_toggled_cb (GtkAction *action, |
---|
426 | gpointer user_data) |
---|
427 | { |
---|
428 | g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); |
---|
429 | gtk_source_view_set_auto_indent ( |
---|
430 | GTK_SOURCE_VIEW (user_data), |
---|
431 | gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); |
---|
432 | } |
---|
433 | |
---|
434 | static void |
---|
435 | insert_spaces_toggled_cb (GtkAction *action, |
---|
436 | gpointer user_data) |
---|
437 | { |
---|
438 | g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); |
---|
439 | gtk_source_view_set_insert_spaces_instead_of_tabs ( |
---|
440 | GTK_SOURCE_VIEW (user_data), |
---|
441 | gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); |
---|
442 | } |
---|
443 | |
---|
444 | static void |
---|
445 | tabs_toggled_cb (GtkAction *action, |
---|
446 | GtkAction *current, |
---|
447 | gpointer user_data) |
---|
448 | { |
---|
449 | g_return_if_fail (GTK_IS_RADIO_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); |
---|
450 | gtk_source_view_set_tabs_width ( |
---|
451 | GTK_SOURCE_VIEW (user_data), |
---|
452 | gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action))); |
---|
453 | } |
---|
454 | |
---|
455 | static void |
---|
456 | new_view_cb (GtkAction *action, gpointer user_data) |
---|
457 | { |
---|
458 | GtkSourceBuffer *buffer; |
---|
459 | GtkSourceView *view; |
---|
460 | GtkWidget *window; |
---|
461 | |
---|
462 | g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data)); |
---|
463 | |
---|
464 | view = GTK_SOURCE_VIEW (user_data); |
---|
465 | buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); |
---|
466 | |
---|
467 | window = create_view_window (buffer, view); |
---|
468 | gtk_window_set_default_size (GTK_WINDOW (window), 400, 400); |
---|
469 | gtk_widget_show (window); |
---|
470 | } |
---|
471 | |
---|
472 | static void |
---|
473 | print_preview_cb (GtkAction *action, gpointer user_data) |
---|
474 | { |
---|
475 | GtkSourcePrintJob *job; |
---|
476 | GtkSourceView *view; |
---|
477 | GtkSourceBuffer *buffer; |
---|
478 | GtkTextIter start, end; |
---|
479 | gchar *filename; |
---|
480 | |
---|
481 | g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data)); |
---|
482 | |
---|
483 | view = GTK_SOURCE_VIEW (user_data); |
---|
484 | buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (user_data))); |
---|
485 | |
---|
486 | job = gtk_source_print_job_new (NULL); |
---|
487 | gtk_source_print_job_setup_from_view (job, view); |
---|
488 | gtk_source_print_job_set_wrap_mode (job, GTK_WRAP_CHAR); |
---|
489 | gtk_source_print_job_set_highlight (job, TRUE); |
---|
490 | gtk_source_print_job_set_print_numbers (job, 5); |
---|
491 | |
---|
492 | gtk_source_print_job_set_header_format (job, |
---|
493 | "Printed on %A", |
---|
494 | NULL, |
---|
495 | "%F", |
---|
496 | TRUE); |
---|
497 | |
---|
498 | filename = g_object_get_data (G_OBJECT (buffer), "filename"); |
---|
499 | |
---|
500 | gtk_source_print_job_set_footer_format (job, |
---|
501 | "%T", |
---|
502 | filename, |
---|
503 | "Page %N/%Q", |
---|
504 | TRUE); |
---|
505 | |
---|
506 | gtk_source_print_job_set_print_header (job, TRUE); |
---|
507 | gtk_source_print_job_set_print_footer (job, TRUE); |
---|
508 | |
---|
509 | gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end); |
---|
510 | if (gtk_source_print_job_print_range_async (job, &start, &end)) |
---|
511 | { |
---|
512 | g_signal_connect (job, "begin_page", (GCallback) page_cb, NULL); |
---|
513 | g_signal_connect (job, "finished", (GCallback) finished_cb, NULL); |
---|
514 | } |
---|
515 | else |
---|
516 | { |
---|
517 | g_warning ("Async print failed"); |
---|
518 | } |
---|
519 | } |
---|
520 | |
---|
521 | |
---|
522 | /* Buffer action callbacks ------------------------------------------------------------ */ |
---|
523 | |
---|
524 | static void |
---|
525 | open_file_cb (GtkAction *action, gpointer user_data) |
---|
526 | { |
---|
527 | GtkWidget *chooser; |
---|
528 | gint response; |
---|
529 | |
---|
530 | g_return_if_fail (GTK_IS_SOURCE_BUFFER (user_data)); |
---|
531 | |
---|
532 | chooser = gtk_file_chooser_dialog_new ("Open file...", |
---|
533 | NULL, |
---|
534 | GTK_FILE_CHOOSER_ACTION_OPEN, |
---|
535 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
---|
536 | GTK_STOCK_OPEN, GTK_RESPONSE_OK, |
---|
537 | NULL); |
---|
538 | |
---|
539 | response = gtk_dialog_run (GTK_DIALOG (chooser)); |
---|
540 | if (response == GTK_RESPONSE_OK) |
---|
541 | { |
---|
542 | gchar *filename; |
---|
543 | |
---|
544 | filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); |
---|
545 | if (filename != NULL) |
---|
546 | { |
---|
547 | open_file (GTK_SOURCE_BUFFER (user_data), filename); |
---|
548 | g_free (filename); |
---|
549 | } |
---|
550 | } |
---|
551 | |
---|
552 | gtk_widget_destroy (chooser); |
---|
553 | } |
---|
554 | |
---|
555 | |
---|
556 | /* View UI callbacks ------------------------------------------------------------------ */ |
---|
557 | |
---|
558 | static void |
---|
559 | update_cursor_position (GtkTextBuffer *buffer, gpointer user_data) |
---|
560 | { |
---|
561 | gchar *msg; |
---|
562 | gint row, col, chars, tabwidth; |
---|
563 | GtkTextIter iter, start; |
---|
564 | GtkSourceView *view; |
---|
565 | GtkLabel *pos_label; |
---|
566 | |
---|
567 | g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data)); |
---|
568 | |
---|
569 | view = GTK_SOURCE_VIEW (user_data); |
---|
570 | tabwidth = gtk_source_view_get_tabs_width (view); |
---|
571 | pos_label = GTK_LABEL (g_object_get_data (G_OBJECT (view), "pos_label")); |
---|
572 | |
---|
573 | gtk_text_buffer_get_iter_at_mark (buffer, |
---|
574 | &iter, |
---|
575 | gtk_text_buffer_get_insert (buffer)); |
---|
576 | |
---|
577 | chars = gtk_text_iter_get_offset (&iter); |
---|
578 | row = gtk_text_iter_get_line (&iter) + 1; |
---|
579 | |
---|
580 | start = iter; |
---|
581 | gtk_text_iter_set_line_offset (&start, 0); |
---|
582 | col = 0; |
---|
583 | |
---|
584 | while (!gtk_text_iter_equal (&start, &iter)) |
---|
585 | { |
---|
586 | if (gtk_text_iter_get_char (&start) == '\t') |
---|
587 | { |
---|
588 | col += (tabwidth - (col % tabwidth)); |
---|
589 | } |
---|
590 | else |
---|
591 | ++col; |
---|
592 | |
---|
593 | gtk_text_iter_forward_char (&start); |
---|
594 | } |
---|
595 | |
---|
596 | msg = g_strdup_printf ("char: %d, line: %d, column: %d", chars, row, col); |
---|
597 | gtk_label_set_text (pos_label, msg); |
---|
598 | g_free (msg); |
---|
599 | } |
---|
600 | |
---|
601 | static void |
---|
602 | move_cursor_cb (GtkTextBuffer *buffer, |
---|
603 | GtkTextIter *cursoriter, |
---|
604 | GtkTextMark *mark, |
---|
605 | gpointer user_data) |
---|
606 | { |
---|
607 | if (mark != gtk_text_buffer_get_insert (buffer)) |
---|
608 | return; |
---|
609 | |
---|
610 | update_cursor_position (buffer, user_data); |
---|
611 | } |
---|
612 | |
---|
613 | static gboolean |
---|
614 | window_deleted_cb (GtkWidget *widget, GdkEvent *ev, gpointer user_data) |
---|
615 | { |
---|
616 | g_return_val_if_fail (GTK_IS_SOURCE_VIEW (user_data), TRUE); |
---|
617 | |
---|
618 | if (g_list_nth_data (windows, 0) == widget) |
---|
619 | { |
---|
620 | /* Main (first in the list) window was closed, so exit |
---|
621 | * the application */ |
---|
622 | gtk_main_quit (); |
---|
623 | } |
---|
624 | else |
---|
625 | { |
---|
626 | GtkSourceView *view = GTK_SOURCE_VIEW (user_data); |
---|
627 | GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER ( |
---|
628 | gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); |
---|
629 | |
---|
630 | windows = g_list_remove (windows, widget); |
---|
631 | |
---|
632 | /* deinstall buffer motion signal handlers */ |
---|
633 | g_signal_handlers_disconnect_matched (buffer, |
---|
634 | G_SIGNAL_MATCH_DATA, |
---|
635 | 0, /* signal_id */ |
---|
636 | 0, /* detail */ |
---|
637 | NULL, /* closure */ |
---|
638 | NULL, /* func */ |
---|
639 | user_data); |
---|
640 | |
---|
641 | /* we return FALSE since we want the window destroyed */ |
---|
642 | return FALSE; |
---|
643 | } |
---|
644 | |
---|
645 | return TRUE; |
---|
646 | } |
---|
647 | |
---|
648 | static gboolean |
---|
649 | button_press_cb (GtkWidget *widget, GdkEventButton *ev, gpointer user_data) |
---|
650 | { |
---|
651 | GtkSourceView *view; |
---|
652 | GtkSourceBuffer *buffer; |
---|
653 | |
---|
654 | g_return_val_if_fail (GTK_IS_SOURCE_VIEW (widget), FALSE); |
---|
655 | |
---|
656 | view = GTK_SOURCE_VIEW (widget); |
---|
657 | buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); |
---|
658 | |
---|
659 | if (!gtk_source_view_get_show_line_markers (view)) |
---|
660 | return FALSE; |
---|
661 | |
---|
662 | /* check that the click was on the left gutter */ |
---|
663 | if (ev->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view), |
---|
664 | GTK_TEXT_WINDOW_LEFT)) |
---|
665 | { |
---|
666 | gint y_buf; |
---|
667 | GtkTextIter line_start, line_end; |
---|
668 | GSList *marker_list, *list_iter; |
---|
669 | GtkSourceMarker *marker; |
---|
670 | const gchar *marker_type; |
---|
671 | |
---|
672 | if (ev->button == 1) |
---|
673 | marker_type = MARKER_TYPE_1; |
---|
674 | else |
---|
675 | marker_type = MARKER_TYPE_2; |
---|
676 | |
---|
677 | gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), |
---|
678 | GTK_TEXT_WINDOW_LEFT, |
---|
679 | ev->x, ev->y, |
---|
680 | NULL, &y_buf); |
---|
681 | |
---|
682 | /* get line bounds */ |
---|
683 | gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view), |
---|
684 | &line_start, |
---|
685 | y_buf, |
---|
686 | NULL); |
---|
687 | |
---|
688 | line_end = line_start; |
---|
689 | gtk_text_iter_forward_to_line_end (&line_end); |
---|
690 | |
---|
691 | /* get the markers already in the line */ |
---|
692 | marker_list = gtk_source_buffer_get_markers_in_region (buffer, |
---|
693 | &line_start, |
---|
694 | &line_end); |
---|
695 | |
---|
696 | /* search for the marker corresponding to the button pressed */ |
---|
697 | marker = NULL; |
---|
698 | for (list_iter = marker_list; |
---|
699 | list_iter && !marker; |
---|
700 | list_iter = g_slist_next (list_iter)) |
---|
701 | { |
---|
702 | GtkSourceMarker *tmp = list_iter->data; |
---|
703 | gchar *tmp_type = gtk_source_marker_get_marker_type (tmp); |
---|
704 | |
---|
705 | if (tmp_type && !strcmp (tmp_type, marker_type)) |
---|
706 | { |
---|
707 | marker = tmp; |
---|
708 | } |
---|
709 | g_free (tmp_type); |
---|
710 | } |
---|
711 | g_slist_free (marker_list); |
---|
712 | |
---|
713 | if (marker) |
---|
714 | { |
---|
715 | /* a marker was found, so delete it */ |
---|
716 | gtk_source_buffer_delete_marker (buffer, marker); |
---|
717 | } |
---|
718 | else |
---|
719 | { |
---|
720 | /* no marker found -> create one */ |
---|
721 | marker = gtk_source_buffer_create_marker (buffer, NULL, |
---|
722 | marker_type, &line_start); |
---|
723 | } |
---|
724 | } |
---|
725 | |
---|
726 | return FALSE; |
---|
727 | } |
---|
728 | |
---|
729 | |
---|
730 | /* Window creation functions -------------------------------------------------------- */ |
---|
731 | |
---|
732 | static GtkWidget * |
---|
733 | create_view_window (GtkSourceBuffer *buffer, GtkSourceView *from) |
---|
734 | { |
---|
735 | GtkWidget *window, *sw, *view, *vbox, *pos_label, *menu; |
---|
736 | PangoFontDescription *font_desc = NULL; |
---|
737 | GdkPixbuf *pixbuf; |
---|
738 | GtkAccelGroup *accel_group; |
---|
739 | GtkActionGroup *action_group; |
---|
740 | GtkUIManager *ui_manager; |
---|
741 | GError *error; |
---|
742 | |
---|
743 | g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL); |
---|
744 | g_return_val_if_fail (from == NULL || GTK_IS_SOURCE_VIEW (from), NULL); |
---|
745 | |
---|
746 | /* window */ |
---|
747 | window = gtk_window_new (GTK_WINDOW_TOPLEVEL); |
---|
748 | gtk_container_set_border_width (GTK_CONTAINER (window), 0); |
---|
749 | gtk_window_set_title (GTK_WINDOW (window), "GtkSourceView Demo"); |
---|
750 | windows = g_list_append (windows, window); |
---|
751 | |
---|
752 | /* view */ |
---|
753 | view = gtk_source_view_new_with_buffer (buffer); |
---|
754 | |
---|
755 | g_signal_connect (buffer, "mark_set", G_CALLBACK (move_cursor_cb), view); |
---|
756 | g_signal_connect (buffer, "changed", G_CALLBACK (update_cursor_position), view); |
---|
757 | g_signal_connect (view, "button-press-event", G_CALLBACK (button_press_cb), NULL); |
---|
758 | g_signal_connect (window, "delete-event", (GCallback) window_deleted_cb, view); |
---|
759 | |
---|
760 | /* action group and UI manager */ |
---|
761 | action_group = gtk_action_group_new ("ViewActions"); |
---|
762 | gtk_action_group_add_actions (action_group, view_action_entries, |
---|
763 | G_N_ELEMENTS (view_action_entries), view); |
---|
764 | gtk_action_group_add_toggle_actions (action_group, toggle_entries, |
---|
765 | G_N_ELEMENTS (toggle_entries), view); |
---|
766 | gtk_action_group_add_radio_actions (action_group, radio_entries, |
---|
767 | G_N_ELEMENTS (radio_entries), |
---|
768 | -1, G_CALLBACK (tabs_toggled_cb), view); |
---|
769 | |
---|
770 | ui_manager = gtk_ui_manager_new (); |
---|
771 | gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); |
---|
772 | g_object_unref (action_group); |
---|
773 | |
---|
774 | /* save a reference to the ui manager in the window for later use */ |
---|
775 | g_object_set_data_full (G_OBJECT (window), "ui_manager", |
---|
776 | ui_manager, (GDestroyNotify) g_object_unref); |
---|
777 | |
---|
778 | accel_group = gtk_ui_manager_get_accel_group (ui_manager); |
---|
779 | gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); |
---|
780 | |
---|
781 | error = NULL; |
---|
782 | if (!gtk_ui_manager_add_ui_from_string (ui_manager, view_ui_description, -1, &error)) |
---|
783 | { |
---|
784 | g_message ("building view ui failed: %s", error->message); |
---|
785 | g_error_free (error); |
---|
786 | exit (1); |
---|
787 | } |
---|
788 | |
---|
789 | /* misc widgets */ |
---|
790 | vbox = gtk_vbox_new (0, FALSE); |
---|
791 | sw = gtk_scrolled_window_new (NULL, NULL); |
---|
792 | gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), |
---|
793 | GTK_SHADOW_IN); |
---|
794 | pos_label = gtk_label_new ("Position"); |
---|
795 | g_object_set_data (G_OBJECT (view), "pos_label", pos_label); |
---|
796 | menu = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); |
---|
797 | |
---|
798 | /* layout widgets */ |
---|
799 | gtk_container_add (GTK_CONTAINER (window), vbox); |
---|
800 | gtk_box_pack_start (GTK_BOX (vbox), menu, FALSE, FALSE, 0); |
---|
801 | gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); |
---|
802 | gtk_container_add (GTK_CONTAINER (sw), view); |
---|
803 | gtk_box_pack_start (GTK_BOX (vbox), pos_label, FALSE, FALSE, 0); |
---|
804 | |
---|
805 | /* setup view */ |
---|
806 | font_desc = pango_font_description_from_string ("monospace 10"); |
---|
807 | if (font_desc != NULL) |
---|
808 | { |
---|
809 | gtk_widget_modify_font (view, font_desc); |
---|
810 | pango_font_description_free (font_desc); |
---|
811 | } |
---|
812 | |
---|
813 | /* change view attributes to match those of from */ |
---|
814 | if (from) |
---|
815 | { |
---|
816 | gchar *tmp; |
---|
817 | GtkAction *action; |
---|
818 | |
---|
819 | action = gtk_action_group_get_action (action_group, "ShowNumbers"); |
---|
820 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), |
---|
821 | gtk_source_view_get_show_line_numbers (from)); |
---|
822 | |
---|
823 | action = gtk_action_group_get_action (action_group, "ShowMarkers"); |
---|
824 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), |
---|
825 | gtk_source_view_get_show_line_markers (from)); |
---|
826 | |
---|
827 | action = gtk_action_group_get_action (action_group, "ShowMargin"); |
---|
828 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), |
---|
829 | gtk_source_view_get_show_margin (from)); |
---|
830 | |
---|
831 | action = gtk_action_group_get_action (action_group, "AutoIndent"); |
---|
832 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), |
---|
833 | gtk_source_view_get_auto_indent (from)); |
---|
834 | |
---|
835 | action = gtk_action_group_get_action (action_group, "InsertSpaces"); |
---|
836 | gtk_toggle_action_set_active ( |
---|
837 | GTK_TOGGLE_ACTION (action), |
---|
838 | gtk_source_view_get_insert_spaces_instead_of_tabs (from)); |
---|
839 | |
---|
840 | |
---|
841 | tmp = g_strdup_printf ("TabsWidth%d", gtk_source_view_get_tabs_width (from)); |
---|
842 | action = gtk_action_group_get_action (action_group, tmp); |
---|
843 | if (action) |
---|
844 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); |
---|
845 | g_free (tmp); |
---|
846 | } |
---|
847 | |
---|
848 | /* add marker pixbufs */ |
---|
849 | error = NULL; |
---|
850 | if ((pixbuf = gdk_pixbuf_new_from_file (DATADIR "/pixmaps/apple-green.png", &error))) |
---|
851 | { |
---|
852 | gtk_source_view_set_marker_pixbuf (GTK_SOURCE_VIEW (view), MARKER_TYPE_1, pixbuf); |
---|
853 | g_object_unref (pixbuf); |
---|
854 | } |
---|
855 | else |
---|
856 | { |
---|
857 | g_message ("could not load marker 1 image. Spurious messages might get triggered: %s", |
---|
858 | error->message); |
---|
859 | g_error_free (error); |
---|
860 | } |
---|
861 | |
---|
862 | error = NULL; |
---|
863 | if ((pixbuf = gdk_pixbuf_new_from_file (DATADIR "/pixmaps/apple-red.png", &error))) |
---|
864 | { |
---|
865 | gtk_source_view_set_marker_pixbuf (GTK_SOURCE_VIEW (view), MARKER_TYPE_2, pixbuf); |
---|
866 | g_object_unref (pixbuf); |
---|
867 | } |
---|
868 | else |
---|
869 | { |
---|
870 | g_message ("could not load marker 2 image. Spurious messages might get triggered: %s", |
---|
871 | error->message); |
---|
872 | g_error_free (error); |
---|
873 | } |
---|
874 | |
---|
875 | gtk_widget_show_all (vbox); |
---|
876 | |
---|
877 | return window; |
---|
878 | } |
---|
879 | |
---|
880 | static GtkWidget * |
---|
881 | create_main_window (GtkSourceBuffer *buffer) |
---|
882 | { |
---|
883 | GtkWidget *window; |
---|
884 | GtkAction *action; |
---|
885 | GtkUIManager *ui_manager; |
---|
886 | GtkActionGroup *action_group; |
---|
887 | GList *groups; |
---|
888 | GError *error; |
---|
889 | |
---|
890 | window = create_view_window (buffer, NULL); |
---|
891 | ui_manager = g_object_get_data (G_OBJECT (window), "ui_manager"); |
---|
892 | |
---|
893 | /* buffer action group */ |
---|
894 | action_group = gtk_action_group_new ("BufferActions"); |
---|
895 | gtk_action_group_add_actions (action_group, buffer_action_entries, |
---|
896 | G_N_ELEMENTS (buffer_action_entries), buffer); |
---|
897 | gtk_ui_manager_insert_action_group (ui_manager, action_group, 1); |
---|
898 | g_object_unref (action_group); |
---|
899 | |
---|
900 | /* merge buffer ui */ |
---|
901 | error = NULL; |
---|
902 | if (!gtk_ui_manager_add_ui_from_string (ui_manager, buffer_ui_description, -1, &error)) |
---|
903 | { |
---|
904 | g_message ("building buffer ui failed: %s", error->message); |
---|
905 | g_error_free (error); |
---|
906 | exit (1); |
---|
907 | } |
---|
908 | |
---|
909 | /* preselect menu checkitems */ |
---|
910 | groups = gtk_ui_manager_get_action_groups (ui_manager); |
---|
911 | /* retrieve the view action group at position 0 in the list */ |
---|
912 | action_group = g_list_nth_data (groups, 0); |
---|
913 | |
---|
914 | action = gtk_action_group_get_action (action_group, "ShowNumbers"); |
---|
915 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); |
---|
916 | |
---|
917 | action = gtk_action_group_get_action (action_group, "ShowMarkers"); |
---|
918 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); |
---|
919 | |
---|
920 | action = gtk_action_group_get_action (action_group, "ShowMargin"); |
---|
921 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); |
---|
922 | |
---|
923 | action = gtk_action_group_get_action (action_group, "AutoIndent"); |
---|
924 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); |
---|
925 | |
---|
926 | action = gtk_action_group_get_action (action_group, "InsertSpaces"); |
---|
927 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE); |
---|
928 | |
---|
929 | action = gtk_action_group_get_action (action_group, "TabsWidth8"); |
---|
930 | gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); |
---|
931 | |
---|
932 | return window; |
---|
933 | } |
---|
934 | |
---|
935 | |
---|
936 | /* Buffer creation -------------------------------------------------------------- */ |
---|
937 | |
---|
938 | static GtkSourceBuffer * |
---|
939 | create_source_buffer (GtkSourceLanguagesManager *manager) |
---|
940 | { |
---|
941 | GtkSourceBuffer *buffer; |
---|
942 | |
---|
943 | buffer = GTK_SOURCE_BUFFER (gtk_source_buffer_new (NULL)); |
---|
944 | g_object_ref (manager); |
---|
945 | g_object_set_data_full (G_OBJECT (buffer), "languages-manager", |
---|
946 | manager, (GDestroyNotify) g_object_unref); |
---|
947 | |
---|
948 | return buffer; |
---|
949 | } |
---|
950 | |
---|
951 | |
---|
952 | /* Program entry point ------------------------------------------------------------ */ |
---|
953 | |
---|
954 | int |
---|
955 | main (int argc, char *argv[]) |
---|
956 | { |
---|
957 | GtkWidget *window; |
---|
958 | GtkSourceLanguagesManager *lm; |
---|
959 | GtkSourceBuffer *buffer; |
---|
960 | |
---|
961 | /* initialization */ |
---|
962 | gtk_init (&argc, &argv); |
---|
963 | gnome_vfs_init (); |
---|
964 | |
---|
965 | /* create buffer */ |
---|
966 | lm = gtk_source_languages_manager_new (); |
---|
967 | buffer = create_source_buffer (lm); |
---|
968 | g_object_unref (lm); |
---|
969 | |
---|
970 | if (argc > 1) |
---|
971 | open_file (buffer, argv [1]); |
---|
972 | else |
---|
973 | open_file (buffer, "../gtksourceview/gtksourcebuffer.c"); |
---|
974 | |
---|
975 | /* create first window */ |
---|
976 | window = create_main_window (buffer); |
---|
977 | gtk_window_set_default_size (GTK_WINDOW (window), 500, 500); |
---|
978 | gtk_widget_show (window); |
---|
979 | |
---|
980 | /* ... and action! */ |
---|
981 | gtk_main (); |
---|
982 | |
---|
983 | /* cleanup */ |
---|
984 | g_list_foreach (windows, (GFunc) gtk_widget_destroy, NULL); |
---|
985 | g_list_free (windows); |
---|
986 | g_object_unref (buffer); |
---|
987 | |
---|
988 | gnome_vfs_shutdown (); |
---|
989 | |
---|
990 | return 0; |
---|
991 | } |
---|