/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- * test-widget.c * * Copyright (C) 2001 * Mikael Hermansson<tyan@linux.se> * * Copyright (C) 2003 - Gustavo Giráldez <gustavo.giraldez@gmx.net> * If TEST_XML_MEM is defined the test program will try to detect memory * allocated by xmlMalloc() but not freed by xmlFree() or freed by xmlFree() * but not allocated by xmlMalloc(). cgtk test test.c -I/usr/include/gtksourceview-1.0 -I/usr/include/gnome-vfs-module-2.0 -lgtksourceview-1.0 && ./test */ /*#define TEST_XML_MEM*/ #include <stdio.h> #include <string.h> #include <gtk/gtk.h> #include <libgnomevfs/gnome-vfs-init.h> #include <libgnomevfs/gnome-vfs-mime-utils.h> #include <libgnomevfs/gnome-vfs-utils.h> #include <libgnomeprintui/gnome-print-job-preview.h> #include <gtksourceview/gtksourceview.h> #include <gtksourceview/gtksourcelanguage.h> #include <gtksourceview/gtksourcelanguagesmanager.h> #include <gtksourceview/gtksourceprintjob.h> #ifdef TEST_XML_MEM #include <libxml/xmlreader.h> #endif /* Global list of open windows */ static GList *windows = NULL; /* Private data structures */ #define READ_BUFFER_SIZE 4096 #define MARKER_TYPE_1 "one" #define MARKER_TYPE_2 "two" /* Private prototypes -------------------------------------------------------- */ static void open_file_cb (GtkAction *action, gpointer user_data); static void new_view_cb (GtkAction *action, gpointer user_data); static void numbers_toggled_cb (GtkAction *action, gpointer user_data); static void markers_toggled_cb (GtkAction *action,gpointer user_data); static void margin_toggled_cb (GtkAction *action,gpointer user_data); static void hl_line_toggled_cb (GtkAction *action, gpointer user_data); static void auto_indent_toggled_cb (GtkAction *action,gpointer user_data); static void insert_spaces_toggled_cb (GtkAction *action, gpointer user_data); static void tabs_toggled_cb (GtkAction *action,GtkAction *current,gpointer user_data); static void print_preview_cb (GtkAction *action,gpointer user_data); static GtkWidget *create_view_window (GtkSourceBuffer *buffer, GtkSourceView *from); /* Actions & UI definition ---------------------------------------------------- */ static GtkActionEntry buffer_action_entries[] = { { "Open", GTK_STOCK_OPEN, "_Open", "<control>O","Open a file" , G_CALLBACK (open_file_cb) }, { "Quit", GTK_STOCK_QUIT, "_Quit", "<control>Q","Exit the application", G_CALLBACK (gtk_main_quit) }, }; static GtkActionEntry view_action_entries[] = { { "FileMenu", NULL, "_File" }, { "ViewMenu", NULL, "_View" }, { "PrintPreview", GTK_STOCK_PRINT, "_Print Preview", "<control>P", "Preview printing of the file", G_CALLBACK (print_preview_cb) }, { "NewView", GTK_STOCK_NEW, "_New View", NULL, "Create a new view of the file", G_CALLBACK (new_view_cb) }, { "TabsWidth", NULL, "_Tabs Width" }, }; static GtkToggleActionEntry toggle_entries[] = { { "ShowNumbers", NULL, "Show _Line Numbers", NULL,"Toggle visibility of line numbers in the left margin", G_CALLBACK (numbers_toggled_cb), FALSE }, { "ShowMarkers", NULL, "Show _Markers", NULL,"Toggle visibility of markers in the left margin",G_CALLBACK (markers_toggled_cb), FALSE }, { "ShowMargin", NULL, "Show M_argin", NULL,"Toggle visibility of right margin indicator", G_CALLBACK (margin_toggled_cb), FALSE }, { "HlLine", NULL, "_Highlight Current Line", NULL,"Toggle highlighting of current line", G_CALLBACK (hl_line_toggled_cb), FALSE }, { "AutoIndent", NULL, "Enable _Auto Indent", NULL,"Toggle automatic auto indentation of text",G_CALLBACK (auto_indent_toggled_cb), FALSE }, { "InsertSpaces", NULL, "Insert _Spaces Instead of Tabs", NULL,"Whether to insert space characters when inserting tabulations",G_CALLBACK (insert_spaces_toggled_cb), FALSE } }; static GtkRadioActionEntry radio_entries[] = { { "TabsWidth4", NULL, "4", NULL, "Set tabulation width to 4 spaces", 4 }, { "TabsWidth6", NULL, "6", NULL, "Set tabulation width to 6 spaces", 6 }, { "TabsWidth8", NULL, "8", NULL, "Set tabulation width to 8 spaces", 8 }, { "TabsWidth10", NULL, "10", NULL, "Set tabulation width to 10 spaces", 10 }, { "TabsWidth12", NULL, "12", NULL, "Set tabulation width to 12 spaces", 12 } }; static const gchar *view_ui_description = "<ui>" " <menubar name=\"MainMenu\">" " <menu action=\"FileMenu\">" " <menuitem action=\"PrintPreview\"/>" " </menu>" " <menu action=\"ViewMenu\">" " <menuitem action=\"NewView\"/>" " <separator/>" " <menuitem action=\"ShowNumbers\"/>" " <menuitem action=\"ShowMarkers\"/>" " <menuitem action=\"ShowMargin\"/>" " <menuitem action=\"HlLine\"/>" " <separator/>" " <menuitem action=\"AutoIndent\"/>" " <menuitem action=\"InsertSpaces\"/>" " <separator/>" " <menu action=\"TabsWidth\">" " <menuitem action=\"TabsWidth4\"/>" " <menuitem action=\"TabsWidth6\"/>" " <menuitem action=\"TabsWidth8\"/>" " <menuitem action=\"TabsWidth10\"/>" " <menuitem action=\"TabsWidth12\"/>" " </menu>" " </menu>" " </menubar>" "</ui>"; static const gchar *buffer_ui_description = "<ui>" " <menubar name=\"MainMenu\">" " <menu action=\"FileMenu\">" " <menuitem action=\"Open\"/>" " <separator/>" " <menuitem action=\"Quit\"/>" " </menu>" " <menu action=\"ViewMenu\">" " </menu>" " </menubar>" "</ui>"; /* File loading code ----------------------------------------------------------------- */ static void error_dialog (GtkWindow *parent, const gchar *msg, ...) { va_list ap; gchar *tmp; GtkWidget *dialog; va_start (ap, msg); tmp = g_strdup_vprintf (msg, ap); va_end (ap); dialog = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, tmp); g_free (tmp); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static gboolean gtk_source_buffer_load_with_encoding (GtkSourceBuffer *source_buffer, const gchar *filename, const gchar *encoding, GError **error) { GIOChannel *io; GtkTextIter iter; gchar *buffer; gboolean reading; g_return_val_if_fail (source_buffer != NULL, FALSE); g_return_val_if_fail (filename != NULL, FALSE); g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (source_buffer), FALSE); *error = NULL; io = g_io_channel_new_file (filename, "r", error); if (!io) { error_dialog (NULL, "%s\nFile %s", (*error)->message, filename); return FALSE; } if (g_io_channel_set_encoding (io, encoding, error) != G_IO_STATUS_NORMAL) { error_dialog (NULL, "Failed to set encoding:\n%s\n%s", filename, (*error)->message); return FALSE; } gtk_source_buffer_begin_not_undoable_action (source_buffer); gtk_text_buffer_set_text (GTK_TEXT_BUFFER (source_buffer), "", 0); buffer = g_malloc (4096); reading = TRUE; while (reading) { gsize bytes_read; GIOStatus status; status = g_io_channel_read_chars (io, buffer, 4096, &bytes_read, error); switch (status) { case G_IO_STATUS_EOF: reading = FALSE; /* fall through */ case G_IO_STATUS_NORMAL: if (bytes_read == 0) { continue; } gtk_text_buffer_get_end_iter ( GTK_TEXT_BUFFER (source_buffer), &iter); gtk_text_buffer_insert (GTK_TEXT_BUFFER (source_buffer), &iter, buffer, bytes_read); break; case G_IO_STATUS_AGAIN: continue; case G_IO_STATUS_ERROR: default: error_dialog (NULL, "%s\nFile %s", (*error)->message, filename); /* because of error in input we clear already loaded text */ gtk_text_buffer_set_text (GTK_TEXT_BUFFER (source_buffer), "", 0); reading = FALSE; break; } } g_free (buffer); gtk_source_buffer_end_not_undoable_action (source_buffer); g_io_channel_unref (io); if (*error) return FALSE; gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (source_buffer), FALSE); /* move cursor to the beginning */ gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (source_buffer), &iter); gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (source_buffer), &iter); return TRUE; } static void remove_all_markers (GtkSourceBuffer *buffer) { GSList *markers; GtkTextIter begin, end; gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end); markers = gtk_source_buffer_get_markers_in_region (buffer, &begin, &end); while (markers) { GtkSourceMarker *marker = markers->data; gtk_source_buffer_delete_marker (buffer, marker); markers = g_slist_delete_link (markers, markers); } } static gboolean open_file (GtkSourceBuffer *buffer, const gchar *filename) { GtkSourceLanguagesManager *manager; GtkSourceLanguage *language = NULL; gchar *mime_type; GError *err = NULL; gchar *uri; /* get the new language for the file mimetype */ manager = g_object_get_data (G_OBJECT (buffer), "languages-manager"); /* I hate this! */ if (g_path_is_absolute (filename)) uri = gnome_vfs_get_uri_from_local_path (filename); else { gchar *curdir, *path; curdir = g_get_current_dir(); path = g_strconcat (curdir, "/", filename, NULL); g_free (curdir); uri = gnome_vfs_get_uri_from_local_path (path); g_free (path); } mime_type = gnome_vfs_get_mime_type (uri); g_free (uri); if (mime_type) { language = gtk_source_languages_manager_get_language_from_mime_type (manager, mime_type); g_print("mime [%s] Langage [%s]\n", mime_type, gtk_source_language_get_name(language)); if (language == NULL) { g_print ("No language found for mime type `%s'\n", mime_type); g_object_set (G_OBJECT (buffer), "highlight", FALSE, NULL); } else { g_object_set (G_OBJECT (buffer), "highlight", TRUE, NULL); gtk_source_buffer_set_language (buffer, language); } g_free (mime_type); } else { g_object_set (G_OBJECT (buffer), "highlight", FALSE, NULL); g_warning ("Couldn't get mime type for file `%s'", filename); } //g_print(" remove markers\n"); remove_all_markers (buffer); gtk_source_buffer_load_with_encoding (buffer, filename, "utf-8", &err); g_object_set_data_full (G_OBJECT (buffer),"filename", g_strdup (filename),(GDestroyNotify) g_free); if (err != NULL) { g_error_free (err); return FALSE; } return TRUE; } /* Printing callbacks --------------------------------------------------------- */ static void page_cb (GtkSourcePrintJob *job, gpointer user_data) { g_print ("Printing %.2f%% \r", 100.0 * gtk_source_print_job_get_page (job) / gtk_source_print_job_get_page_count (job)); } static void finished_cb (GtkSourcePrintJob *job, gpointer user_data) { GnomePrintJob *gjob; GtkWidget *preview; g_print ("\n"); gjob = gtk_source_print_job_get_print_job (job); preview = gnome_print_job_preview_new (gjob, (const guchar *)"test-widget print preview"); g_object_unref (gjob); g_object_unref (job); gtk_widget_show (preview); } /* View action callbacks -------------------------------------------------------- */ static void numbers_toggled_cb (GtkAction *action, gpointer user_data) { g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); gtk_source_view_set_show_line_numbers (GTK_SOURCE_VIEW (user_data), gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); } static void markers_toggled_cb (GtkAction *action, gpointer user_data) { g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); gtk_source_view_set_show_line_markers ( GTK_SOURCE_VIEW (user_data), gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); } static void margin_toggled_cb (GtkAction *action, gpointer user_data) { g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); gtk_source_view_set_show_margin ( GTK_SOURCE_VIEW (user_data), gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); } static void hl_line_toggled_cb (GtkAction *action, gpointer user_data) { g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); gtk_source_view_set_highlight_current_line ( GTK_SOURCE_VIEW (user_data), gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); } static void auto_indent_toggled_cb (GtkAction *action, gpointer user_data) { g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); gtk_source_view_set_auto_indent (GTK_SOURCE_VIEW (user_data), gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); } static void insert_spaces_toggled_cb (GtkAction *action, gpointer user_data) { g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); gtk_source_view_set_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (user_data), gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); } static void tabs_toggled_cb (GtkAction *action, GtkAction *current, gpointer user_data) { g_return_if_fail (GTK_IS_RADIO_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data)); gtk_source_view_set_tabs_width ( GTK_SOURCE_VIEW (user_data), gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action))); } static void new_view_cb (GtkAction *action, gpointer user_data) { GtkSourceBuffer *buffer; GtkSourceView *view; GtkWidget *window; g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data)); view = GTK_SOURCE_VIEW (user_data); buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); window = create_view_window (buffer, view); gtk_window_set_default_size (GTK_WINDOW (window), 400, 400); gtk_widget_show (window); } static void print_preview_cb (GtkAction *action, gpointer user_data) { GtkSourcePrintJob *job; GtkSourceView *view; GtkSourceBuffer *buffer; GtkTextIter start, end; gchar *filename; g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data)); view = GTK_SOURCE_VIEW (user_data); buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (user_data))); job = gtk_source_print_job_new (NULL); gtk_source_print_job_setup_from_view (job, view); gtk_source_print_job_set_wrap_mode (job, GTK_WRAP_CHAR); gtk_source_print_job_set_highlight (job, TRUE); gtk_source_print_job_set_print_numbers (job, 5); gtk_source_print_job_set_header_format (job,"Printed on %A",NULL,"%F",TRUE); filename = g_object_get_data (G_OBJECT (buffer), "filename"); gtk_source_print_job_set_footer_format (job,"%T",filename,"Page %N/%Q",TRUE); gtk_source_print_job_set_print_header (job, TRUE); gtk_source_print_job_set_print_footer (job, TRUE); gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end); if (gtk_source_print_job_print_range_async (job, &start, &end)) { g_signal_connect (job, "begin_page", (GCallback) page_cb, NULL); g_signal_connect (job, "finished", (GCallback) finished_cb, NULL); } else g_warning ("Async print failed"); } /* Buffer action callbacks ------------------------------------------------------------ */ static void open_file_cb (GtkAction *action, gpointer user_data) { GtkWidget *chooser; gint response; g_return_if_fail (GTK_IS_SOURCE_BUFFER (user_data)); chooser = gtk_file_chooser_dialog_new ("Open file...", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK,NULL); response = gtk_dialog_run (GTK_DIALOG (chooser)); if (response == GTK_RESPONSE_OK) { gchar *filename; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); if (filename != NULL) { open_file (GTK_SOURCE_BUFFER (user_data), filename); g_free (filename); } } gtk_widget_destroy (chooser); } /* View UI callbacks ------------------------------------------------------------------ */ static void update_cursor_position (GtkTextBuffer *buffer, gpointer user_data) { gchar *msg; gint row, col, chars, tabwidth; GtkTextIter iter, start; GtkSourceView *view; GtkLabel *pos_label; g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data)); view = GTK_SOURCE_VIEW (user_data); tabwidth = gtk_source_view_get_tabs_width (view); pos_label = GTK_LABEL (g_object_get_data (G_OBJECT (view), "pos_label")); gtk_text_buffer_get_iter_at_mark (buffer,&iter,gtk_text_buffer_get_insert (buffer)); chars = gtk_text_iter_get_offset (&iter); row = gtk_text_iter_get_line (&iter) + 1; start = iter; gtk_text_iter_set_line_offset (&start, 0); col = 0; while (!gtk_text_iter_equal (&start, &iter)) { if (gtk_text_iter_get_char (&start) == '\t') { col += (tabwidth - (col % tabwidth)); } else ++col; gtk_text_iter_forward_char (&start); } msg = g_strdup_printf ("char: %d, line: %d, column: %d", chars, row, col); gtk_label_set_text (pos_label, msg); g_free (msg); } static void move_cursor_cb (GtkTextBuffer *buffer,GtkTextIter *cursoriter, GtkTextMark *mark, gpointer user_data) { if (mark != gtk_text_buffer_get_insert (buffer)) return; update_cursor_position (buffer, user_data); } static gboolean window_deleted_cb (GtkWidget *widget, GdkEvent *ev, gpointer user_data) { g_return_val_if_fail (GTK_IS_SOURCE_VIEW (user_data), TRUE); if (g_list_nth_data (windows, 0) == widget) /* Main (first in the list) window was closed, so exit the application */ gtk_main_quit (); else { GtkSourceView *view = GTK_SOURCE_VIEW (user_data); GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); windows = g_list_remove (windows, widget); /* deinstall buffer motion signal handlers */ g_signal_handlers_disconnect_matched (buffer,G_SIGNAL_MATCH_DATA, 0, /* signal_id */ 0, /* detail */ NULL, /* closure */ NULL, /* func */ user_data); /* we return FALSE since we want the window destroyed */ return FALSE; } return TRUE; } static gboolean button_press_cb (GtkWidget *widget, GdkEventButton *ev, gpointer user_data) { GtkSourceView *view; GtkSourceBuffer *buffer; g_return_val_if_fail (GTK_IS_SOURCE_VIEW (widget), FALSE); view = GTK_SOURCE_VIEW (widget); buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); if (!gtk_source_view_get_show_line_markers (view)) return FALSE; /* check that the click was on the left gutter */ if (ev->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),GTK_TEXT_WINDOW_LEFT)) { gint y_buf; GtkTextIter line_start, line_end; GSList *marker_list, *list_iter; GtkSourceMarker *marker; const gchar *marker_type; if (ev->button == 1) marker_type = MARKER_TYPE_1; else marker_type = MARKER_TYPE_2; gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),GTK_TEXT_WINDOW_LEFT,ev->x, ev->y,NULL, &y_buf); /* get line bounds */ gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view), &line_start, y_buf, NULL); line_end = line_start; gtk_text_iter_forward_to_line_end (&line_end); /* get the markers already in the line */ marker_list = gtk_source_buffer_get_markers_in_region (buffer, &line_start, &line_end); /* search for the marker corresponding to the button pressed */ marker = NULL; for (list_iter = marker_list; list_iter && !marker; list_iter = g_slist_next (list_iter)) { GtkSourceMarker *tmp = list_iter->data; gchar *tmp_type = gtk_source_marker_get_marker_type (tmp); if (tmp_type && !strcmp (tmp_type, marker_type)) marker = tmp; g_free (tmp_type); } g_slist_free (marker_list); if (marker) /* a marker was found, so delete it */ gtk_source_buffer_delete_marker (buffer, marker); else /* no marker found -> create one */ marker = gtk_source_buffer_create_marker (buffer, NULL, marker_type, &line_start); } return FALSE; } /* Window creation functions -------------------------------------------------------- */ static GtkWidget *create_view_window (GtkSourceBuffer *buffer, GtkSourceView *from) { GtkWidget *window, *sw, *view, *vbox, *pos_label, *menu; PangoFontDescription *font_desc = NULL; GdkPixbuf *pixbuf; GtkAccelGroup *accel_group; GtkActionGroup *action_group; GtkUIManager *ui_manager; GError *error; g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL); g_return_val_if_fail (from == NULL || GTK_IS_SOURCE_VIEW (from), NULL); /* window */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_container_set_border_width (GTK_CONTAINER (window), 0); gtk_window_set_title (GTK_WINDOW (window), "GtkSourceView Demo"); windows = g_list_append (windows, window); /* view */ view = gtk_source_view_new_with_buffer (buffer); g_signal_connect (buffer, "mark_set", G_CALLBACK (move_cursor_cb), view); g_signal_connect (buffer, "changed", G_CALLBACK (update_cursor_position), view); g_signal_connect (view, "button-press-event", G_CALLBACK (button_press_cb), NULL); g_signal_connect (window, "delete-event", (GCallback) window_deleted_cb, view); /* action group and UI manager */ action_group = gtk_action_group_new ("ViewActions"); gtk_action_group_add_actions (action_group, view_action_entries, G_N_ELEMENTS (view_action_entries), view); gtk_action_group_add_toggle_actions (action_group, toggle_entries,G_N_ELEMENTS (toggle_entries), view); gtk_action_group_add_radio_actions (action_group, radio_entries,G_N_ELEMENTS (radio_entries),-1, G_CALLBACK (tabs_toggled_cb), view); ui_manager = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); /* save a reference to the ui manager in the window for later use */ g_object_set_data_full (G_OBJECT (window), "ui_manager",ui_manager, (GDestroyNotify) g_object_unref); accel_group = gtk_ui_manager_get_accel_group (ui_manager); gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); error = NULL; if (!gtk_ui_manager_add_ui_from_string (ui_manager, view_ui_description, -1, &error)) { g_message ("building view ui failed: %s", error->message); g_error_free (error); exit (1); } /* misc widgets */ vbox = gtk_vbox_new (0, FALSE); sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); pos_label = gtk_label_new ("Position"); g_object_set_data (G_OBJECT (view), "pos_label", pos_label); menu = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); /* layout widgets */ gtk_container_add (GTK_CONTAINER (window), vbox); gtk_box_pack_start (GTK_BOX (vbox), menu, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (sw), view); gtk_box_pack_start (GTK_BOX (vbox), pos_label, FALSE, FALSE, 0); /* setup view */ font_desc = pango_font_description_from_string ("monospace 10"); if (font_desc != NULL) { gtk_widget_modify_font (view, font_desc); pango_font_description_free (font_desc); } /* change view attributes to match those of from */ if (from) { gchar *tmp; GtkAction *action; action = gtk_action_group_get_action (action_group, "ShowNumbers"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), gtk_source_view_get_show_line_numbers (from)); action = gtk_action_group_get_action (action_group, "ShowMarkers"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), gtk_source_view_get_show_line_markers (from)); action = gtk_action_group_get_action (action_group, "ShowMargin"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), gtk_source_view_get_show_margin (from)); action = gtk_action_group_get_action (action_group, "HlLine"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), gtk_source_view_get_highlight_current_line (from)); action = gtk_action_group_get_action (action_group, "AutoIndent"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), gtk_source_view_get_auto_indent (from)); action = gtk_action_group_get_action (action_group, "InsertSpaces"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),gtk_source_view_get_insert_spaces_instead_of_tabs (from)); tmp = g_strdup_printf ("TabsWidth%d", gtk_source_view_get_tabs_width (from)); action = gtk_action_group_get_action (action_group, tmp); if (action) gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); g_free (tmp); } /* add marker pixbufs */ error = NULL; if ((pixbuf = gdk_pixbuf_new_from_file (DATADIR "/pixmaps/apple-green.png", &error))) { gtk_source_view_set_marker_pixbuf (GTK_SOURCE_VIEW (view), MARKER_TYPE_1, pixbuf); g_object_unref (pixbuf); } else { g_message ("could not load marker 1 image. Spurious messages might get triggered: %s", error->message); g_error_free (error); } error = NULL; if ((pixbuf = gdk_pixbuf_new_from_file (DATADIR "/pixmaps/apple-red.png", &error))) { gtk_source_view_set_marker_pixbuf (GTK_SOURCE_VIEW (view), MARKER_TYPE_2, pixbuf); g_object_unref (pixbuf); } else { g_message ("could not load marker 2 image. Spurious messages might get triggered: %s", error->message); g_error_free (error); } gtk_widget_show_all (vbox); return window; } static GtkWidget *create_main_window (GtkSourceBuffer *buffer) { GtkWidget *window; GtkAction *action; GtkUIManager *ui_manager; GtkActionGroup *action_group; GList *groups; GError *error; window = create_view_window (buffer, NULL); ui_manager = g_object_get_data (G_OBJECT (window), "ui_manager"); /* buffer action group */ action_group = gtk_action_group_new ("BufferActions"); gtk_action_group_add_actions (action_group, buffer_action_entries, G_N_ELEMENTS (buffer_action_entries), buffer); gtk_ui_manager_insert_action_group (ui_manager, action_group, 1); g_object_unref (action_group); /* merge buffer ui */ error = NULL; if (!gtk_ui_manager_add_ui_from_string (ui_manager, buffer_ui_description, -1, &error)) { g_message ("building buffer ui failed: %s", error->message); g_error_free (error); exit (1); } /* preselect menu checkitems */ groups = gtk_ui_manager_get_action_groups (ui_manager); /* retrieve the view action group at position 0 in the list */ action_group = g_list_nth_data (groups, 0); action = gtk_action_group_get_action (action_group, "ShowNumbers"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); action = gtk_action_group_get_action (action_group, "ShowMarkers"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); action = gtk_action_group_get_action (action_group, "ShowMargin"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE); action = gtk_action_group_get_action (action_group, "AutoIndent"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); action = gtk_action_group_get_action (action_group, "InsertSpaces"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE); action = gtk_action_group_get_action (action_group, "TabsWidth8"); gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); return window; } /* Buffer creation -------------------------------------------------------------- */ static GtkSourceBuffer *create_source_buffer (GtkSourceLanguagesManager *manager) { GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER (gtk_source_buffer_new (NULL)); g_object_ref (manager); g_object_set_data_full (G_OBJECT (buffer), "languages-manager",manager, (GDestroyNotify) g_object_unref); return buffer; } /* XML memory management and verification functions ----------------------------- */ #ifdef TEST_XML_MEM static GHashTable *xml_mem_table = NULL; static void xml_free (void *mem) { if (mem == NULL) return; if (g_hash_table_remove (xml_mem_table, mem)) g_free (mem); else g_warning ("Memory at %p (\"%s\") was not allocated by libxml", mem, (gchar*)mem); } static void *xml_malloc (size_t size) { void *allocated_mem = g_malloc (size); g_hash_table_insert (xml_mem_table, allocated_mem, GINT_TO_POINTER (TRUE)); return allocated_mem; } static void *xml_realloc (void *mem, size_t size) { void *allocated_mem; if (!g_hash_table_remove (xml_mem_table, mem)) g_warning ("Memory at %p (\"%s\") was not allocated by libxml",mem, (gchar*)mem); allocated_mem = g_realloc (mem, size); g_hash_table_insert (xml_mem_table, allocated_mem, GINT_TO_POINTER (TRUE)); return allocated_mem; } static char *xml_strdup (const char *str) { void *allocated_mem = g_strdup (str); g_hash_table_insert (xml_mem_table, allocated_mem, GINT_TO_POINTER (TRUE)); return allocated_mem; } static void xml_init () { xml_mem_table = g_hash_table_new (NULL, NULL); if (xmlMemSetup (xml_free, xml_malloc, xml_realloc, xml_strdup) != 0) g_warning ("xmlMemSetup() failed"); } static void xml_table_foreach_cb (gpointer key, gpointer value, gpointer user_data) { /* Some of this memory could be internally allocated by libxml. */ g_warning ("Memory at %p (\"%s\") was not freed, freed without using xmlFree() or allocated internally by libxml", key, (gchar*)key); } static void xml_finalize () { g_hash_table_foreach (xml_mem_table, xml_table_foreach_cb, NULL); g_hash_table_destroy (xml_mem_table); } #endif /* TEST_XML_MEM */ /* Program entry point ------------------------------------------------------------ */ int main (int argc, char *argv[]) { GtkWidget *window; GtkSourceLanguagesManager *lm; GtkSourceBuffer *buffer; /* initialization */ gtk_init (&argc, &argv); gnome_vfs_init (); #ifdef TEST_XML_MEM xml_init (); #endif /* create buffer */ lm = gtk_source_languages_manager_new (); buffer = create_source_buffer (lm); g_object_unref (lm); if (argc > 1) open_file (buffer, argv [1]); else open_file (buffer, "../gtksourceview/gtksourcebuffer.c"); /* create first window */ window = create_main_window (buffer); gtk_window_set_default_size (GTK_WINDOW (window), 500, 500); gtk_widget_show (window); /* ... and action! */ gtk_main (); /* cleanup */ g_list_foreach (windows, (GFunc) gtk_widget_destroy, NULL); g_list_free (windows); g_object_unref (buffer); gnome_vfs_shutdown (); #ifdef TEST_XML_MEM xml_finalize (); #endif return 0; }