/* -*- 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;
}