/* Glimmer - gdsfile.c * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "gdsfile.h" #include "gdstearaway.h" #include "../main.h" #include "../menus.h" #include "../signals.h" #include "xpm/file-selector-file.xpm" static void gds_file_init (GdsFile * gds_file); static void gds_file_class_init (GdsFileClass * klass); static void gds_file_destroy (GtkObject * object); static void gds_file_construct_labels (GdsFile * gds_file, gboolean full); static gint button_press_cb (GtkWidget * widget, GdkEventButton * event); static gint key_press_cb (GtkWidget * widget, GdkEventKey * event, GdsFile * gds_file); static gchar *make_spaces_string (gint spaces); void bracket_match_cb (GtkWidget * widget, gint pos, GdsFile * gds_file); static GtkHBoxClass *parent_class = NULL; static gint global_undomax = 0; static gint global_spaces = 0; static gboolean global_indent = 0; static gboolean global_bracketmatch = 0; static gboolean global_tab_stops; GtkType gds_file_get_type (void) { static GtkType gds_file_type = 0; if (!gds_file_type) { static const GtkTypeInfo gds_file_info = { "GdsFile", sizeof (GdsFile), sizeof (GdsFileClass), (GtkClassInitFunc) gds_file_class_init, (GtkObjectInitFunc) gds_file_init, NULL, NULL, (GtkClassInitFunc) NULL, }; gds_file_type = gtk_type_unique (GTK_TYPE_HBOX, &gds_file_info); } return (gds_file_type); } static void gds_file_class_init (GdsFileClass * klass) { GtkObjectClass *object_class; object_class = (GtkObjectClass *) klass; parent_class = gtk_type_class (GTK_TYPE_HBOX); object_class->destroy = gds_file_destroy; } static void gds_file_init (GdsFile * gds_file) { gds_file->uri = NULL; gds_file->filename = NULL; gds_file->need_pass = FALSE; gds_file->xfering = FALSE; gds_file->xfer_finish = (GtkSignalFunc) NULL; gds_file->xfer_data = NULL; gds_file->tables = NULL; gds_file->default_lang = TRUE; gds_file->changed_set = FALSE; gds_file->modtime = 0; gds_file->props_dialog = NULL; gds_file->props.over_ride = FALSE; gds_file->props.dir = NULL; gds_file->props.auto_indent = FALSE; gds_file->props.use_spaces = FALSE; gds_file->props.spaces = 0; gds_file->props.compiler = NULL; gds_file->props.debugger = NULL; gds_file->props.execution = NULL; gds_file->compile_pid = 0; gds_file->debug_pid = 0; gds_file->exec_pid = 0; } GtkWidget * gds_file_new (GnomeVFSURI * uri, gboolean full) { GdsFile *gds_file; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *hscroll; gds_file = gtk_type_new (GDS_TYPE_FILE); gds_file->tear_away = gds_tear_away_new (GTK_POS_LEFT); gtk_widget_show (gds_file->tear_away); gds_file_set_uri (gds_file, uri, full); gds_file->paned = gtk_vpaned_new (); gtk_paned_set_gutter_size (GTK_PANED (gds_file->paned), 12); gds_tear_away_set_child (GDS_TEAR_AWAY (gds_file->tear_away), gds_file->paned); gtk_widget_show (gds_file->paned); vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); gtk_widget_show (hbox); gds_file->text = gds_editor_new (); gtk_box_pack_start (GTK_BOX (hbox), gds_file->text, TRUE, TRUE, 0); gtk_widget_show (gds_file->text); gtk_extext_undo_set_max (GTK_EXTEXT (gds_file->text), global_undomax); gds_file->vscroller = gtk_vscrollbar_new (GTK_EXTEXT (gds_file->text)->vadj); gtk_box_pack_start (GTK_BOX (hbox), gds_file->vscroller, FALSE, FALSE, 0); gtk_widget_show (gds_file->vscroller); gtk_signal_connect (GTK_OBJECT (gds_file->text), "button_press_event", GTK_SIGNAL_FUNC (button_press_cb), 0); gtk_signal_connect (GTK_OBJECT (gds_file->text), "key_press_event", GTK_SIGNAL_FUNC (key_press_cb), gds_file); hscroll = gtk_hscrollbar_new (GTK_EXTEXT (gds_file->text)->hadj); gtk_box_pack_start (GTK_BOX (vbox), hscroll, FALSE, TRUE, 0); gtk_widget_show (hscroll); gds_file->file_data = gtk_entry_new (); gtk_box_pack_end (GTK_BOX (vbox), gds_file->file_data, FALSE, TRUE, 0); gtk_widget_show (gds_file->file_data); gtk_editable_set_editable (GTK_EDITABLE (gds_file->file_data), FALSE); gtk_paned_pack1 (GTK_PANED (gds_file->paned), vbox, TRUE, TRUE); gtk_signal_connect (GTK_OBJECT (gds_file->text), "move_to_column", GTK_SIGNAL_FUNC (bracket_match_cb), (gpointer) gds_file); gds_file->script_box = gtk_vbox_new (FALSE, 0); gtk_paned_pack2 (GTK_PANED (gds_file->paned), gds_file->script_box, FALSE, FALSE); gtk_widget_show (gds_file->script_box); return GTK_WIDGET (gds_file); } static void gds_file_destroy (GtkObject * object) { GdsFile *gds_file; g_return_if_fail (object != NULL); g_return_if_fail (GDS_IS_FILE (object)); gds_file = GDS_FILE (object); if (gds_file->props_dialog) gtk_widget_destroy (gds_file->props_dialog); gtk_widget_destroy (gds_file->pixmap); gtk_widget_destroy (gds_file->menu_item); gtk_widget_destroy (gds_file->nb_label); gtk_widget_destroy (gds_file->text); gtk_widget_destroy (gds_file->tear_away); if (gds_file->uri) gnome_vfs_uri_unref (gds_file->uri); if (gds_file->filename) g_free (gds_file->filename); g_free (gds_file->props.dir); g_free (gds_file->props.compiler); g_free (gds_file->props.debugger); g_free (gds_file->props.execution); if (gds_file->compile_pid) kill (gds_file->compile_pid, 9); if (gds_file->debug_pid) kill (gds_file->debug_pid, 9); if (gds_file->exec_pid) kill (gds_file->exec_pid, 9); GTK_OBJECT_CLASS (parent_class)->destroy (object); } void gds_file_set_uri (GdsFile * gds_file, GnomeVFSURI * uri, gboolean full) { g_return_if_fail (gds_file != NULL); g_return_if_fail (GDS_IS_FILE (gds_file)); if (gds_file->uri && gds_file->uri != uri) { gnome_vfs_uri_unref (gds_file->uri); } gds_file->uri = uri; gnome_vfs_uri_ref (gds_file->uri); if (gds_file->filename) g_free (gds_file->filename); gds_file->filename = gds_file_display_filename (uri, TRUE); gds_file_construct_labels (gds_file, full); } static void gds_file_construct_labels (GdsFile * gds_file, gboolean full) { GtkWidget *label; gchar *str = NULL; gchar *pixstr = NULL; str = gds_file_display_filename (gds_file->uri, full); if (gds_file->pixmap) gtk_widget_destroy (gds_file->pixmap); if ((pixstr = (gchar *) gnome_vfs_mime_get_value ((gchar *) gnome_vfs_mime_type_from_name (str), "icon-filename"))) { gds_file->pixmap = gnome_pixmap_new_from_file_at_size (pixstr, 18, 18); } else { gds_file->pixmap = gnome_pixmap_new_from_xpm_d (file_selector_file_xpm); } if (!gds_file->menu_item) { gds_file->menu_item = gtk_pixmap_menu_item_new (); label = gtk_label_new (str); gtk_container_add (GTK_CONTAINER (gds_file->menu_item), label); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_widget_show (label); gtk_widget_show (gds_file->menu_item); gds_file->nb_label = gtk_label_new (str); gtk_widget_show (gds_file->nb_label); } else { gtk_label_set (GTK_LABEL (GTK_BIN (gds_file->menu_item)->child), str); gtk_label_set (GTK_LABEL (gds_file->nb_label), str); } gds_tear_away_set_title (GDS_TEAR_AWAY (gds_file->tear_away), str); gtk_pixmap_menu_item_set_pixmap (GTK_PIXMAP_MENU_ITEM (gds_file->menu_item), gds_file->pixmap); gtk_widget_show (gds_file->pixmap); g_free (str); } void gds_file_set_title (GdsFile * gds_file, gboolean full) { gchar *str = NULL; g_return_if_fail (gds_file != NULL); g_return_if_fail (GDS_IS_FILE (gds_file)); str = gds_file_display_filename (gds_file->uri, full); gtk_label_set (GTK_LABEL (GTK_BIN (gds_file->menu_item)->child), str); gtk_label_set (GTK_LABEL (gds_file->nb_label), str); gds_tear_away_set_title (GDS_TEAR_AWAY (gds_file->tear_away), str); g_free (str); } static gint button_press_cb (GtkWidget * widget, GdkEventButton * event) { GtkExText *text; gboolean handled = FALSE; text = (GtkExText *) widget; if (event->type == GDK_BUTTON_PRESS && event->button == 3) { if (!GTK_WIDGET_HAS_FOCUS (widget)) gtk_widget_grab_focus (widget); gtk_menu_popup (GTK_MENU (edit_popup_menu), NULL, NULL, NULL, NULL, event->button, event->time); handled = TRUE; } if (handled) gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "button_press_event"); return (handled); } static gint key_press_cb (GtkWidget * widget, GdkEventKey * event, GdsFile * gds_file) { GtkExText *text; gint curpos; gboolean handled = FALSE; gint i = 0; gchar *chars = NULL, *spaces = NULL; gboolean shift_state; gboolean control_state; gboolean alt_state; gint key; gint abscol; gint tabstop; control_state = event->state & GDK_CONTROL_MASK; shift_state = event->state & GDK_SHIFT_MASK; alt_state = event->state & GDK_MOD1_MASK; key = event->keyval; text = (GtkExText *) widget; if (!gtk_extext_get_editable (text)) return (handled); curpos = text->current_pos; if (event->keyval == GDK_Tab || event->keyval == ' ' || (event->keyval == GDK_Delete && control_state)) { abscol = gtk_extext_get_abs_column (text, curpos); tabstop = gtk_extext_get_next_tab_stop (text, curpos); if (text->has_selection && text->selection_start_pos != text->selection_end_pos) { gint start = 0; gint end = 0; gint first_line = 0; gint last_line = 0; gint current_line = 0; GtkExTextLineData *lineptr = NULL; start = text->selection_start_pos; end = text->selection_end_pos; lineptr = gtk_extext_get_line_by_char_pos (text, start, NULL); first_line = lineptr->line_number; g_free (lineptr); lineptr = gtk_extext_get_line_by_char_pos (text, end, NULL); last_line = lineptr->line_number; if (end == lineptr->startpos && first_line < last_line) last_line--; g_free (lineptr); current_line = first_line; if (first_line == last_line) { if (event->keyval == GDK_Tab && !control_state && !shift_state) { #ifdef WITH_PYTHON if (!file_emit_scripting_signal ("tab-pressed", gds_file)) { #endif gtk_extext_freeze (text); gtk_extext_delete_text (text, start, end); text->has_selection = FALSE; text->selection_start_pos = -1; text->selection_end_pos = -1; spaces = make_spaces_string (tabstop); if ((gds_file->props.over_ride && gds_file->props.use_spaces) || (!gds_file->props.over_ride && global_spaces)) gtk_extext_insert_text (text, spaces, strlen (spaces), &start); else gtk_extext_insert_text (text, "\t", 1, &start); g_free (spaces); gtk_extext_thaw (text); handled = TRUE; #ifdef WITH_PYTHON } #endif } else if (event->keyval == ' ' && !control_state && !shift_state) { gtk_extext_freeze (text); gtk_extext_delete_text (text, start, end); text->has_selection = FALSE; text->selection_start_pos = -1; text->selection_end_pos = -1; gtk_extext_insert_text (text, " ", 1, &start); gtk_extext_thaw (text); handled = TRUE; } } else { gtk_extext_freeze (text); text->has_selection = FALSE; text->selection_start_pos = -1; text->selection_end_pos = -1; tabstop = text->default_tab_width; spaces = make_spaces_string (tabstop); while (current_line <= last_line) { gtk_extext_set_column (text, 0); gtk_extext_set_line (text, current_line); chars = gtk_extext_get_chars (text, text->current_pos, text->current_pos + text->default_tab_width); if (event->keyval == GDK_Tab && (control_state || shift_state)) { g_print ("Blah, blah, blah, here we are!\n"); if (chars && !strcmp (chars, spaces)) gtk_extext_delete_text (text, text->current_pos, text->current_pos + text->default_tab_width); else if (chars && chars[0] == '\t') gtk_extext_delete_text (text, text->current_pos, text->current_pos + 1); } else if (event->keyval == GDK_Tab) { if ((gds_file->props.over_ride && gds_file->props.use_spaces) || (!gds_file->props.over_ride && global_spaces)) gtk_extext_insert_text (text, spaces, strlen (spaces), &text->current_pos); else gtk_extext_insert_text (text, "\t", 1, &text->current_pos); } else if (event->keyval == ' ' && (control_state || shift_state)) { if (chars[0] == ' ') gtk_extext_delete_text (text, text->current_pos, text->current_pos + 1); } else if (event->keyval == GDK_Delete && control_state) { gtk_extext_delete_text (text, text->current_pos, text->current_pos + 1); } else if (event->keyval == ' ') { gtk_extext_insert_text (text, " ", 1, &text->current_pos); } g_free (chars); current_line++; } lineptr = gtk_extext_get_line_data (text, first_line, NULL); start = lineptr->startpos; g_free (lineptr); lineptr = gtk_extext_get_line_data (text, last_line, NULL); if (GTK_EXTEXT_INDEX (text, lineptr->endpos) == '\n') end = lineptr->endpos - 1; else end = lineptr->endpos; g_free (lineptr); gtk_extext_set_position (text, end); gtk_extext_thaw (text); gtk_extext_select_region (text, start, end); g_free (spaces); handled = TRUE; } } #ifdef WITH_PYTHON else if (event->keyval == GDK_Tab && (control_state || shift_state)) { handled = file_emit_scripting_signal ("anti-tab-pressed", gds_file); } #endif else if (event->keyval == GDK_Tab) { #ifdef WITH_PYTHON if (!file_emit_scripting_signal ("tab-pressed", gds_file)) { #endif spaces = make_spaces_string (tabstop); if ((gds_file->props.over_ride && gds_file->props.use_spaces) || (!gds_file->props.over_ride && global_spaces)) gtk_extext_insert_text (text, spaces, strlen (spaces), &text->current_pos); else gtk_extext_insert_text (text, "\t", 1, &text->current_pos); g_free (spaces); #ifdef WITH_PYTHON } #endif handled = TRUE; } } else if (event->keyval == GDK_KP_Enter || event->keyval == GDK_Return) { if ((gds_file->props.over_ride && gds_file->props.auto_indent) || (!gds_file->props.over_ride && global_indent)) { #ifdef WITH_PYTHON if (!file_emit_scripting_signal ("enter-pressed", gds_file)) { #endif gtk_extext_set_column (text, 0); chars = gtk_extext_get_chars (text, text->current_pos, curpos); if (chars) { for (i = 0; i < strlen (chars); i++) { if (chars[i] == '\t' || chars[i] == ' ') continue; else break; } } if (i) { gchar *insert; insert = g_new0 (char, i + 2); insert[0] = '\n'; strncpy (&insert[1], chars, i); gtk_extext_insert_text (text, insert, i + 1, &curpos); g_free (insert); } else { gtk_extext_insert_text (text, "\n", 1, &curpos); } g_free (chars); #ifdef WITH_PYTHON } #endif handled = TRUE; } } if (handled) gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event"); #ifdef WITH_PYTHON else if (key > 0 && key <= 127 && gds_file->tables && gds_file->tables->key_pressed_callback) call_scripting_function_extended (gds_file->tables-> key_pressed_callback, control_state ? TRUE : FALSE, shift_state ? TRUE : FALSE, alt_state ? TRUE : FALSE, key, curpos); #endif return (handled); } static gchar * make_spaces_string (gint spaces) { gchar *string = NULL; if (spaces > 10) spaces = 10; else if (spaces < 0) spaces = 1; string = g_new (char, spaces + 1); if (string) { memset (string, ' ', spaces); string[spaces] = '\0'; } return (string); } void bracket_match_cb (GtkWidget * widget, gint pos, GdsFile * gds_file) { GdsEditor *gds_editor; GtkExText *text; gboolean found = FALSE; g_return_if_fail (widget != NULL); gds_editor = GDS_EDITOR (widget); text = GTK_EXTEXT (widget); gtk_extext_set_pseudo_select (text, -1, -1); if (gds_file->props.over_ride && !gds_file->props.bracketmatch) return; else if (!gds_file->props.over_ride && !global_bracketmatch) return; pos = text->current_pos - 1; if (pos < 0) return; found = find_bracket_match (widget, &pos); if (found) { gtk_extext_set_pseudo_select (text, pos, pos + 1); } } gboolean find_bracket_match (GtkWidget * widget, gint * search) { GdsEditor *gds_editor; GtkExText *text; gchar base_char = 0; gchar search_char = 0; gchar cur_char = 0; gint addition = -1; gint counter = 0; gboolean found = FALSE; gint pos = 0; g_return_val_if_fail (widget != NULL, FALSE); if (!search) return (FALSE); gds_editor = GDS_EDITOR (widget); text = GTK_EXTEXT (widget); pos = *search; cur_char = GTK_EXTEXT_INDEX (text, pos); if (text->property_current && text->property_current->user_data == GINT_TO_POINTER (SYNTAX_TABLE)) return (FALSE); base_char = cur_char; switch ((int) base_char) { case '{': addition = 1; search_char = '}'; break; case '(': addition = 1; search_char = ')'; break; case '[': addition = 1; search_char = ']'; break; case '<': addition = 1; search_char = '>'; break; case '}': addition = -1; search_char = '{'; break; case ')': addition = -1; search_char = '('; break; case ']': addition = -1; search_char = '['; break; case '>': addition = -1; search_char = '<'; break; default: addition = 0; break; } if (!addition) return (FALSE); while (pos >= 0 && pos <= text->length) { pos += addition; cur_char = GTK_EXTEXT_INDEX (text, pos); if (cur_char == search_char && !counter) { found = TRUE; break; } if (cur_char == base_char) counter++; else if (cur_char == search_char) counter--; } if (found) *search = pos; return (found); } void gds_file_set_global_props (gint undo, gint spaces, gboolean indent, gboolean bracket, gboolean tab_stops) { global_undomax = undo; global_spaces = spaces; global_indent = indent; global_bracketmatch = bracket; global_tab_stops = tab_stops; } gchar * gds_file_display_filename (GnomeVFSURI * uri, gboolean full) { gchar *filename; if (full) filename = (gchar *) gnome_vfs_uri_get_path (uri); else filename = (gchar *) gnome_vfs_uri_get_basename (uri); filename = gnome_vfs_unescape_string (filename, "/"); return (filename); } gchar * gds_file_get_path (GdsFile * file) { gchar *path; path = (gchar *) gnome_vfs_uri_extract_dirname (file->uri); return (path); } gchar * gds_file_get_filename (GdsFile * file) { gchar *filename; filename = gnome_vfs_uri_extract_short_name (file->uri); return (filename); } gchar * gds_file_get_filename_nix_extension (GdsFile * file) { gchar *filename; gchar *extension; gchar *retval; filename = gnome_vfs_uri_extract_short_name (file->uri); extension = strrchr (filename, '.'); if (extension) { *extension = '\0'; } retval = g_strdup (filename); g_free (filename); return (retval); }