'
' A minimal source file editor built around GtkSourceView. (c) PvE - April 2010.
'
' Version 1.0: initial release
' Version 1.1: compliancy to BaCon 1.0.18
' Version 1.2: many improvements
' Version 1.3: fix crash when syntaxfile is not available
' Version 1.4: added tabbing, FIND dialog, code improvements
'------------------------------------------------------------------------------------------------------------

TRAP LOCAL

' Set socket timeout to 1 second
OPTION SOCKET 1

' We will be using memory streams
OPTION MEMSTREAM TRUE

' Where to put the syntaxfile
CONST Config_Path$ = "/.local/share/gtksourceview-2.0/language-specs"

CONST GDK_SHIFT_MASK = 1 << 0
CONST GDK_CONTROL_MASK = 1 << 2
CONST GTK_RESPONSE_ACCEPT = -3
CONST GTK_RESPONSE_OK = -5
CONST GTK_RESPONSE_CANCEL = -6
CONST GTK_RESPONSE_YES = -8
CONST GTK_RESPONSE_APPLY = -10
CONST GTK_WIN_POS_CENTER_ON_PARENT = 4
CONST GTK_DIALOG_DESTROY_WITH_PARENT = 2
CONST GTK_MESSAGE_INFO = 0
CONST GTK_MESSAGE_WARNING = 1
CONST GTK_MESSAGE_ERROR = 3
CONST GTK_BUTTONS_CLOSE = 2
CONST GTK_BUTTONS_YES_NO = 4
CONST GTK_WRAP_NONE = 0
CONST GTK_WRAP_WORD = 2
CONST GTK_STATE_NORMAL = 0
CONST GTK_ACCEL_VISIBLE = 1
CONST GTK_WINDOW_TOPLEVEL = 0

' Memory size for GtkIter
CONST DEFAULT_WIDGET_SIZE = 96

' The notebook can have maximum pages
CONST MAXPAGES = 100

CONST DEFTITLE$ = "BaCon Source Code Editor"
CONST DEFVERSION$ = "1.4"

' Global ClipBoard object
DECLARE ClipBoard

' NULL pointer
DECLARE NUL TYPE STRING

' Hold widgets for the notebook
RECORD nbook[MAXPAGES]
    LOCAL view
    LOCAL buffer
    LOCAL label
    LOCAL title$
    LOCAL iter
END RECORD

' Allow icons in buttons - for now intentionally commented as some Linux distros have issues with 'gconftool-2'
'IF LEN(EXEC$(CONCAT$("which gconftool-2 2>/dev/null"))) THEN SYSTEM "gconftool-2 --type boolean --set /desktop/gnome/interface/buttons_have_icons 1"

' Check if syntax file exists
IF NOT(FILEEXISTS(CONCAT$(GETENVIRON$("HOME"), Config_Path$, "/bacon.lang"))) THEN
    PRINT "BaCon syntaxfile not found! Fetching from BaCon website... ";
    CALL Update_Syntaxfile(0)
    PRINT "done."
END IF

' Find libraries and import symbols
CALL Find_Gtk_Library
CALL Find_GtkSource_Library
CALL Find_Gdk_Library
CALL Find_Gobject_Library
CALL Find_Pango_Library

' Run the GUI
CALL Build_Gui

'------------------------------------------------------------------------------------------------------------

' Let's get the GTK library
SUB Find_Gtk_Library

    LOCAL lib$
    LOCAL seq, retry

    CATCH GOTO handle_error

    lib$ = "libgtk-x11-2.0.so"
    seq = -1

    LABEL import_retry
        INCR seq
        retry = FALSE
        IMPORT "gtk_init(int*,void*)" FROM lib$ TYPE void
        IF retry THEN GOTO import_retry

    GOTO contin

    LABEL handle_error
        ERROR = 0
        IF seq IS 0 THEN lib$ = "libgtk-x11-2.0.so.0"
        ELSE lib$ = CONCAT$(LEFT$(lib$, INSTRREV(lib$, ".")), STR$(seq))
        IF seq < 50 THEN
            retry = TRUE
        ELSE
            PRINT "Gtk library not found!"
            END
        END IF
        RESUME

    LABEL contin

    CATCH GOTO lookup_error

    IMPORT "gtk_accel_group_new" FROM lib$ TYPE long
    IMPORT "gtk_box_pack_end(long,long,int,int,int)" FROM lib$ TYPE void
    IMPORT "gtk_box_pack_start(long,long,int,int,int)" FROM lib$ TYPE void
    IMPORT "gtk_button_new_from_stock(char*)" FROM lib$ TYPE long
    IMPORT "gtk_check_menu_item_get_active(long)" FROM lib$ TYPE int
    IMPORT "gtk_check_menu_item_set_active(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_check_menu_item_new_with_mnemonic(char*)" FROM lib$ TYPE long
    IMPORT "gtk_clipboard_get(long)" FROM lib$ TYPE long
    IMPORT "gtk_color_selection_dialog_new(char*)" FROM lib$ TYPE long
    IMPORT "gtk_color_selection_get_current_color(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_color_selection_palette_from_string(char*,long,long)" FROM lib$ TYPE int
    IMPORT "gtk_color_selection_palette_to_string(long,int)" FROM lib$ TYPE char*
    IMPORT "gtk_color_selection_set_current_color(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_container_add(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_dialog_run(long)" FROM lib$ TYPE int
    IMPORT "gtk_entry_get_text(long)" FROM lib$ TYPE char*
    IMPORT "gtk_entry_new" FROM lib$ TYPE long
    IMPORT "gtk_events_pending" FROM lib$ TYPE int
    IMPORT "gtk_exit(int)" FROM lib$ TYPE void
    IMPORT "gtk_file_chooser_dialog_new(char*,long,int,char*,...)" FROM lib$ TYPE long
    IMPORT "gtk_file_chooser_get_filename(long)" FROM lib$ TYPE char*
    IMPORT "gtk_font_selection_dialog_get_font_name(long)" FROM lib$ TYPE char*
    IMPORT "gtk_font_selection_dialog_new(char*)" FROM lib$ TYPE long
    IMPORT "gtk_font_selection_dialog_set_font_name(long,char*)" FROM lib$ TYPE int
    IMPORT "gtk_hbox_new(int,int)" FROM lib$ TYPE long
    IMPORT "gtk_label_new(char*)" FROM lib$ TYPE long
    IMPORT "gtk_label_get_text(long)" FROM lib$ TYPE char*
    IMPORT "gtk_label_set_text(long,char*)" FROM lib$ TYPE void
    IMPORT "gtk_main" FROM lib$ TYPE void
    IMPORT "gtk_main_iteration_do(int)" FROM lib$ TYPE int
    IMPORT "gtk_menu_bar_new" FROM lib$ TYPE long
    IMPORT "gtk_menu_item_new" FROM lib$ TYPE long
    IMPORT "gtk_menu_item_new_with_mnemonic(char*)" FROM lib$ TYPE long
    IMPORT "gtk_menu_item_set_submenu(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_menu_new" FROM lib$ TYPE long
    IMPORT "gtk_menu_popup(long,long,long,void*,void*,int,int)" FROM lib$ TYPE void
    IMPORT "gtk_menu_set_title(long,char*)" FROM lib$ TYPE void
    IMPORT "gtk_menu_shell_append(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_message_dialog_new(long,int,int,int,char*,...)" FROM lib$ TYPE long
    IMPORT "gtk_notebook_append_page(long,long,long)" FROM lib$ TYPE int
    IMPORT "gtk_notebook_get_tab_label_text(long,long)" FROM lib$ TYPE char*
    IMPORT "gtk_notebook_get_current_page(long)" FROM lib$ TYPE int
    IMPORT "gtk_notebook_get_n_pages(long)" FROM lib$ TYPE int
    IMPORT "gtk_notebook_new" FROM lib$ TYPE long
    IMPORT "gtk_notebook_remove_page(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_notebook_set_current_page(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_scrolled_window_new(long,long)" FROM lib$ TYPE long
    IMPORT "gtk_scrolled_window_set_policy(long,int,int)" FROM lib$ TYPE void
    IMPORT "gtk_scrolled_window_set_shadow_type(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_show_about_dialog(long,char*,...)" FROM lib$ TYPE void
    IMPORT "gtk_statusbar_get_context_id(long,char*)" FROM lib$ TYPE int
    IMPORT "gtk_statusbar_new" FROM lib$ TYPE long
    IMPORT "gtk_statusbar_pop(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_statusbar_push(long,int,char*)" FROM lib$ TYPE int
    IMPORT "gtk_text_buffer_backspace(long,long,int,int)" FROM lib$ TYPE int
    IMPORT "gtk_text_buffer_copy_clipboard(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_text_buffer_cut_clipboard(long,long,int)" FROM lib$ TYPE void
    IMPORT "gtk_text_buffer_get_bounds(long,long,long)" FROM lib$ TYPE void
    IMPORT "gtk_text_buffer_get_end_iter(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_text_buffer_get_selection_bound(long)" FROM lib$ TYPE long
    IMPORT "gtk_text_buffer_get_start_iter(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_text_buffer_get_text(long,long,long,int)" FROM lib$ TYPE char*
    IMPORT "gtk_text_buffer_paste_clipboard(long,long,void*,int)" FROM lib$ TYPE void
    IMPORT "gtk_text_buffer_select_range(long,long,long)" FROM lib$ TYPE void
    IMPORT "gtk_text_buffer_set_text(long,char*,int)" FROM lib$ TYPE void
    IMPORT "gtk_text_iter_forward_search(long,char*,int,long,long,void*)" FROM lib$ TYPE int
    IMPORT "gtk_text_view_scroll_to_mark(long,long,double,int,double,double)" FROM lib$ TYPE void
    IMPORT "gtk_text_view_set_wrap_mode(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_vbox_new(int,int)" FROM lib$ TYPE long
    IMPORT "gtk_widget_add_accelerator(long,char*,long,int,int,int)" FROM lib$ TYPE void
    IMPORT "gtk_widget_destroy(long)" FROM lib$ TYPE void
    IMPORT "gtk_widget_grab_focus(long)" FROM lib$ TYPE void
    IMPORT "gtk_widget_hide(long)" FROM lib$ TYPE void
    IMPORT "gtk_widget_modify_base(long,int,long)" FROM lib$ TYPE void
    IMPORT "gtk_widget_modify_font(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_widget_set_sensitive(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_widget_set_size_request(long,int,int)" FROM lib$ TYPE void
    IMPORT "gtk_widget_show_all(long)" FROM lib$ TYPE void
    IMPORT "gtk_window_add_accel_group(long,long)" FROM lib$ TYPE void
    IMPORT "gtk_window_get_title(long)" FROM lib$ TYPE char*
    IMPORT "gtk_window_new(int)" FROM lib$ TYPE long
    IMPORT "gtk_window_set_default_size(long,int,int)" FROM lib$ TYPE void
    IMPORT "gtk_window_set_icon_name(long,char*)" FROM lib$ TYPE void
    IMPORT "gtk_window_set_position(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_window_set_title(long,char*)" FROM lib$ TYPE void
    IMPORT "gtk_window_set_transient_for(long,long)" FROM lib$ TYPE void

    GOTO Exit_Sub

    LABEL lookup_error
        PRINT ERR$(ERROR)
        END

    LABEL Exit_Sub
        CATCH RESET

END SUB

'------------------------------------------------------------------------------------------------------------

' Let's get the GtkSource library
SUB Find_GtkSource_Library

    LOCAL lib$
    LOCAL seq, retry

    CATCH GOTO handle_error

    lib$ = "libgtksourceview-2.0.so"
    seq = -1

    LABEL import_retry
        INCR seq
        retry = FALSE
        IMPORT "gtk_source_buffer_new_with_language(long)" FROM lib$ TYPE long
        IF retry THEN GOTO import_retry

    GOTO contin

    LABEL handle_error
        ERROR = 0
        IF seq IS 0 THEN lib$ = "libgtksourceview-2.0.so.0"
        ELSE lib$ = CONCAT$(LEFT$(lib$, INSTRREV(lib$, ".")), STR$(seq))
        IF seq < 50 THEN
            retry = TRUE
        ELSE
            PRINT "GtkSourceView library not found!"
            END
        END IF
        RESUME

    LABEL contin

    CATCH GOTO lookup_error

    IMPORT "gtk_source_buffer_can_undo(long)" FROM lib$ TYPE int
    IMPORT "gtk_source_buffer_set_highlight_matching_brackets(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_source_buffer_undo(long)" FROM lib$ TYPE void
    IMPORT "gtk_source_language_manager_get_default" FROM lib$ TYPE long
    IMPORT "gtk_source_language_manager_get_language(long,char*)" FROM lib$ TYPE long
    IMPORT "gtk_source_view_new_with_buffer(long)" FROM lib$ TYPE long
    IMPORT "gtk_source_view_set_auto_indent(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_source_view_set_show_line_numbers(long,int)" FROM lib$ TYPE void
    IMPORT "gtk_source_view_set_tab_width(long,int)" FROM lib$ TYPE void

    GOTO Exit_Sub

    LABEL lookup_error
        PRINT ERR$(ERROR)
        END

    LABEL Exit_Sub
        CATCH RESET

END SUB

'------------------------------------------------------------------------------------------------------------

' Let's get the Gdk library
SUB Find_Gdk_Library

    LOCAL lib$
    LOCAL seq, retry

    CATCH GOTO handle_error

    lib$ = "libgdk-x11-2.0.so"
    seq = -1

    LABEL import_retry
        INCR seq
        retry = FALSE
        IMPORT "gdk_atom_intern(char*,int)" FROM lib$ TYPE long
        IF retry THEN GOTO import_retry

    GOTO contin

    LABEL handle_error
        ERROR = 0
        IF seq IS 0 THEN lib$ = "libgdk-x11-2.0.so.0"
        ELSE lib$ = CONCAT$(LEFT$(lib$, INSTRREV(lib$, ".")), STR$(seq))
        IF seq < 50 THEN
            retry = TRUE
        ELSE
            PRINT "Gdk library not found!"
            END
        END IF
        RESUME

    LABEL contin

    CATCH GOTO lookup_error

    IMPORT "gdk_keyval_from_name(char*)" FROM lib$ TYPE int
    IMPORT "gdk_window_at_pointer(long,long)" FROM lib$ TYPE long
    IMPORT "gdk_window_get_pointer(long,long,long,long)" FROM lib$ TYPE long

    GOTO Exit_Sub

    LABEL lookup_error
        PRINT ERR$(ERROR)
        END

    LABEL Exit_Sub
        CATCH RESET

END SUB

'------------------------------------------------------------------------------------------------------------

' Let's get the GtkObject library
SUB Find_Gobject_Library

    LOCAL lib$
    LOCAL seq, retry

    CATCH GOTO handle_error

    lib$ = "libgobject-2.0.so"
    seq = -1

    LABEL import_retry
        INCR seq
        retry = FALSE
        IMPORT "g_signal_connect_data(long,char*,void*,long,long,int)" FROM lib$ TYPE void
        IF retry THEN GOTO import_retry

    GOTO contin

    LABEL handle_error
        ERROR = 0
        IF seq IS 0 THEN lib$ = "libgobject-2.0.so.0"
        ELSE lib$ = CONCAT$(LEFT$(lib$, INSTRREV(lib$, ".")), STR$(seq))
        IF seq < 50 THEN
            retry = TRUE
        ELSE
            PRINT "GtkObject library not found!"
            END
        END IF
        RESUME

    LABEL contin

    CATCH GOTO lookup_error

    IMPORT "g_object_get(long,char*,...)" FROM lib$ TYPE void

    GOTO Exit_Sub

    LABEL lookup_error
        PRINT ERR$(ERROR)
        END

    LABEL Exit_Sub
        CATCH RESET

END SUB

'------------------------------------------------------------------------------------------------------------

' Let's get the Pango library
SUB Find_Pango_Library

    LOCAL lib$
    LOCAL seq, retry

    CATCH GOTO handle_error

    lib$ = "libpango-1.0.so"
    seq = -1

    LABEL import_retry
        INCR seq
        retry = FALSE
        IMPORT "pango_font_description_from_string(char*)" FROM lib$ TYPE long
        IF retry THEN GOTO import_retry

    GOTO contin

    LABEL handle_error
        ERROR = 0
        IF seq IS 0 THEN lib$ = "libpango-1.0.so.0"
        ELSE lib$ = CONCAT$(LEFT$(lib$, INSTRREV(lib$, ".")), STR$(seq))
        IF seq < 50 THEN
            retry = TRUE
        ELSE
            PRINT "Pango library not found!"
            END
        END IF
        RESUME

    LABEL contin

    CATCH GOTO lookup_error

    IMPORT "pango_font_description_free(long)" FROM lib$ TYPE void

    GOTO Exit_Sub

    LABEL lookup_error
        PRINT ERR$(ERROR)
        END

    LABEL Exit_Sub
        CATCH RESET

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Update_Syntaxfile(NUMBER use_gui)

    LOCAL dat$, total$, home$
    LOCAL bacon, dialog
    LOCAL syntaxfile TYPE FILE*

    ' Update GUI
    IF use_gui THEN
        WHILE gtk_events_pending()
            gtk_main_iteration_do(FALSE)
        WEND
    END IF

    home$ = GETENVIRON$("HOME")

    ' Enable check on internet address
    CATCH GOTO error

    ' Fetch the latest syntaxfile
    OPEN "www.basic-converter.org:80" FOR NETWORK AS bacon

    SEND "GET /bacon.lang HTTP/1.1\r\nHost: www.basic-converter.org\r\n\r\n" TO bacon
    REPEAT
        RECEIVE dat$ FROM bacon
        total$ = CONCAT$(total$, dat$)
    UNTIL ISFALSE(WAIT(bacon, 500))

    CLOSE NETWORK bacon

    ' Create directories
    MAKEDIR CONCAT$(home$, "/.local/share/gtksourceview-2.0/language-specs")

    ' Save syntax file from BaCon website to correct location
    OPEN CONCAT$(home$, Config_Path$, "/bacon.lang") FOR WRITING AS syntaxfile
        WRITELN MID$(total$, INSTR(total$, "<?xml")) TO syntaxfile
    CLOSE FILE syntaxfile

    GOTO finish

    LABEL error
        dialog = gtk_message_dialog_new(win, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, \
        GTK_BUTTONS_CLOSE, "The BaCon website cannot be reached!\n\n\tCheck your network settings.", NUL)
        gtk_window_set_title(dialog, "Error")
        gtk_window_set_transient_for(dialog, win)
        gtk_window_set_position(dialog, GTK_WIN_POS_CENTER_ON_PARENT)
        gtk_dialog_run(dialog)
        gtk_widget_destroy(dialog)

    LABEL finish
        CATCH RESET

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Load_Defaults

    LOCAL txt$, name$
    LOCAL value, gdkcol, colselect, page, desc
    LOCAL amount TYPE int

    OPEN CONCAT$(GETENVIRON$("HOME"), "/.bacon.txt") FOR READING AS settings

    page = gtk_notebook_get_current_page(notebook)

    REPEAT
        READLN txt$ FROM settings
        IF INSTR(txt$, "indent") THEN
            value = VAL(MID$(txt$, INSTR(txt$, " ")+1))
            gtk_source_view_set_auto_indent(nbook[page].view, value)
            gtk_check_menu_item_set_active(indent_menu, value)
        END IF
        IF INSTR(txt$, "linenr") THEN
            value = VAL(MID$(txt$, INSTR(txt$, " ")+1))
            gtk_source_view_set_show_line_numbers(nbook[page].view, value)
            gtk_check_menu_item_set_active(linenr_menu, value)
        END IF
        IF INSTR(txt$, "wrap") THEN
            value = VAL(MID$(txt$, INSTR(txt$, " ")+1))
            gtk_text_view_set_wrap_mode(nbook[page].view, value)
            gtk_check_menu_item_set_active(wrap_menu, value)
        END IF
        IF INSTR(txt$, "font") THEN
            name$ = MID$(txt$, INSTR(txt$, " ")+1)
            gtk_font_selection_dialog_set_font_name(font_dialog, name$)
            desc = pango_font_description_from_string(name$)
            gtk_widget_modify_font(nbook[page].view, desc)
            pango_font_description_free(desc)
        END IF
        IF INSTR(txt$, "color") THEN
            name$ = MID$(txt$, INSTR(txt$, " ")+1)
            g_object_get(color_dialog, "color-selection", ADDRESS(colselect), NUL)
            gdkcol = MEMORY(DEFAULT_WIDGET_SIZE)
            gtk_color_selection_palette_from_string(name$, ADDRESS(gdkcol), ADDRESS(amount))
            gtk_color_selection_set_current_color(colselect, gdkcol)
            gtk_widget_modify_base(nbook[page].view, GTK_STATE_NORMAL, gdkcol)
            FREE gdkcol
        END IF
    UNTIL ENDFILE(settings)

    CLOSE FILE settings

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Save_Defaults

    ' String by reference needed for dialogs
    LOCAL ptr1, ptr2 TYPE STRING
    LOCAL colselect, gdkcol
    ' Temporary p$ for WRITELN
    LOCAL p$

    OPEN CONCAT$(GETENVIRON$("HOME"), "/.bacon.txt") FOR WRITING AS settings

        IF gtk_check_menu_item_get_active(indent_menu) THEN WRITELN "indent 1" TO settings
        ELSE WRITELN "indent 0" TO settings

        IF gtk_check_menu_item_get_active(linenr_menu) THEN WRITELN "linenr 1" TO settings
        ELSE WRITELN "linenr 0" TO settings

        IF gtk_check_menu_item_get_active(wrap_menu) THEN WRITELN "wrap 2" TO settings
        ELSE WRITELN "wrap 0" TO settings

        ptr1 = gtk_font_selection_dialog_get_font_name(font_dialog) : p$ = ptr1
        WRITELN "font ", p$ TO settings

        g_object_get(color_dialog, "color-selection", ADDRESS(colselect), NUL)
        gdkcol = MEMORY(DEFAULT_WIDGET_SIZE)
        gtk_color_selection_get_current_color(colselect, gdkcol)
        ptr2 = gtk_color_selection_palette_to_string(gdkcol, 1) : p$ = ptr2

        WRITELN "color ", p$ TO settings
        FREE gdkcol

    CLOSE FILE settings

END SUB

'------------------------------------------------------------------------------------------------------------

FUNCTION Get_Contents$

    LOCAL iter1, iter2, page
    LOCAL text$

    iter1 = MEMORY(DEFAULT_WIDGET_SIZE)
    iter2 = MEMORY(DEFAULT_WIDGET_SIZE)

    page = gtk_notebook_get_current_page(notebook)
    gtk_text_buffer_get_bounds(nbook[page].buffer, iter1, iter2)
    text$ = gtk_text_buffer_get_text(nbook[page].buffer, iter1, iter2, 1)

    FREE iter1
    FREE iter2

    RETURN text$

END FUNCTION

'------------------------------------------------------------------------------------------------------------

' Update statusbar with message
SUB Statusbar_Msg(STRING txt)

    gtk_statusbar_pop(statusbar, msg_cid)
    gtk_statusbar_push(statusbar, msg_cid, txt)

END SUB

'------------------------------------------------------------------------------------------------------------

' Callback for OPEN file
SUB Open_File

    LOCAL page, dialog, response
    LOCAL title$

    page = gtk_notebook_get_current_page(notebook)

    title$ = gtk_label_get_text(nbook[page].label)

    IF EQUAL(LEFT$(title$, 1), "*") THEN
        dialog = gtk_message_dialog_new(win, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, CONCAT$("Save program '", MID$(title$, 2), "' ?"), NUL)
        gtk_window_set_title(dialog, "Warning!")
        response = gtk_dialog_run(dialog)
        gtk_widget_destroy(dialog)
        IF response IS GTK_RESPONSE_YES THEN CALL Save_File
    END IF

    gtk_widget_show_all(file_dialog)

END SUB

'------------------------------------------------------------------------------------------------------------

' Callback to hide a widget
SUB Hide_Dialog(NUMBER widget)

    gtk_widget_hide(widget)

END SUB

'------------------------------------------------------------------------------------------------------------

' Handle dialog actions
SUB Handle_Open_Dialog(NUMBER widget, int action)

    LOCAL str$
    LOCAL buffer, filelen, iter, page

    ' Define by reference
    LOCAL name TYPE STRING

    gtk_widget_hide(file_dialog)

    IF action IS GTK_RESPONSE_ACCEPT THEN
        name = gtk_file_chooser_get_filename(file_dialog)
        filelen = FILELEN(name)

        buffer = MEMORY(filelen)

        OPEN name FOR READING AS fh
            GETBYTE buffer FROM fh SIZE filelen
        CLOSE FILE fh

        ' Get current page
        page = gtk_notebook_get_current_page(notebook)
        OPEN buffer FOR MEMORY AS str$
            gtk_text_buffer_set_text(nbook[page].buffer, str$, -1)
        CLOSE MEMORY str$

        FREE buffer

        iter = MEMORY(DEFAULT_WIDGET_SIZE)

        ' Remove last CR from buffer
        gtk_text_buffer_get_end_iter(nbook[page].buffer, iter)
        gtk_text_buffer_backspace(nbook[page].buffer, iter, 0, 1)

        gtk_label_set_text(nbook[page].label, MID$(name, INSTRREV(name, "/")+1) )
        Statusbar_Msg(CONCAT$("File '", name, "' loaded successfully."))

        nbook[page].title$ = name
        FREE iter
    END IF

END SUB

'------------------------------------------------------------------------------------------------------------

' Callback for SAVE file
SUB Save_File

    LOCAL page, response

    page = gtk_notebook_get_current_page(notebook)

    IF LEN(nbook[page].title$) > 0 THEN
        OPEN nbook[page].title$ FOR WRITING AS sourcefile
            WRITELN Get_Contents$() TO sourcefile
        CLOSE FILE sourcefile

        gtk_label_set_text(nbook[page].label, MID$(nbook[page].title$, INSTRREV(nbook[page].title$, "/")+1) )
        Statusbar_Msg(CONCAT$("Current code saved to '", nbook[page].title$, "'."))
    ELSE
        response = gtk_dialog_run(save_dialog)
    END IF

END SUB

'------------------------------------------------------------------------------------------------------------

' Callback for SAVE AS
SUB Save_Fileas

    gtk_widget_show_all(save_dialog)

END SUB

'------------------------------------------------------------------------------------------------------------

' Handle SAVE AS dialog actions
SUB Handle_Save_Dialog(NUMBER widget, int action)

    LOCAL file_overwrite, page
    LOCAL response TYPE int

    ' Define by reference
    LOCAL name TYPE STRING

    gtk_widget_hide(save_dialog)

    IF action IS GTK_RESPONSE_ACCEPT THEN
        name = gtk_file_chooser_get_filename(widget)

        IF FILEEXISTS(name) THEN
            file_overwrite = gtk_message_dialog_new(win, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, \
                GTK_BUTTONS_YES_NO, CONCAT$("Overwrite existing '", MID$(name, INSTRREV(name, "/")+1), "'?"), NUL)
            gtk_window_set_title(file_overwrite, "Warning!")
            response = gtk_dialog_run(file_overwrite)
            gtk_widget_destroy(file_overwrite)
        ELSE
            response = GTK_RESPONSE_YES
        END IF

        IF response IS GTK_RESPONSE_YES THEN
            OPEN name FOR WRITING AS sourcefile
                WRITELN Get_Contents$() TO sourcefile
            CLOSE FILE sourcefile

            page = gtk_notebook_get_current_page(notebook)

            gtk_label_set_text(nbook[page].label, MID$(name, INSTRREV(name, "/")+1) )
            Statusbar_Msg(CONCAT$("Current code saved to '", name, "'."))

            nbook[page].title$ = name
        ELSE
            Statusbar_Msg(CONCAT$("Save cancelled."))
        END IF
    END IF

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Set_Indent

    LOCAL x

    FOR x = 0 TO gtk_notebook_get_n_pages(notebook)-1
        gtk_source_view_set_auto_indent(nbook[x].view, gtk_check_menu_item_get_active(indent_menu))
    NEXT

    Save_Defaults

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Set_Linenr

    LOCAL x

    FOR x = 0 TO gtk_notebook_get_n_pages(notebook)-1
        gtk_source_view_set_show_line_numbers(nbook[x].view, gtk_check_menu_item_get_active(linenr_menu))
    NEXT

    Save_Defaults

END SUB

'------------------------------------------------------------------------------------------------------------

' At each keystroke set title to 'changed'
SUB Set_Changed

    LOCAL title$
    LOCAL page

    page = gtk_notebook_get_current_page(notebook)
    title$ = gtk_label_get_text(nbook[page].label)

    IF NOT(EQUAL(LEFT$(title$, 1), "*")) THEN

        gtk_label_set_text(nbook[page].label, CONCAT$("* ", title$) )

    END IF

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Set_Wrapmode

    LOCAL x

    IF gtk_check_menu_item_get_active(wrap_menu) THEN
        FOR x = 0 TO gtk_notebook_get_n_pages(notebook)-1
            gtk_text_view_set_wrap_mode(nbook[x].view, GTK_WRAP_WORD)
        NEXT
    ELSE
        FOR x = 0 TO gtk_notebook_get_n_pages(notebook)-1
            gtk_text_view_set_wrap_mode(nbook[x].view, GTK_WRAP_NONE)
        NEXT
    ENDIF

    Save_Defaults

END SUB

'------------------------------------------------------------------------------------------------------------

' Callback for SELECT FONT
SUB Select_Font

    gtk_widget_show_all(font_dialog)

END SUB

'------------------------------------------------------------------------------------------------------------

' Handle FONT selection
SUB Handle_Font_Dialog(NUMBER widget, int action)

    LOCAL name TYPE STRING
    LOCAL desc, x

    IF action IS GTK_RESPONSE_CANCEL OR action IS GTK_RESPONSE_OK THEN gtk_widget_hide(font_dialog)

    IF action IS GTK_RESPONSE_OK OR action IS GTK_RESPONSE_APPLY THEN
        name = gtk_font_selection_dialog_get_font_name(font_dialog)
        desc = pango_font_description_from_string(name)

        FOR x = 0 TO gtk_notebook_get_n_pages(notebook)-1
            gtk_widget_modify_font(nbook[x].view, desc)
        NEXT

        pango_font_description_free(desc)
        Statusbar_Msg(CONCAT$("Font has been changed to '", name, "'."))

        Save_Defaults
    END IF

END SUB

'------------------------------------------------------------------------------------------------------------

' Callback for SELECT COLOR
SUB Select_Color

    gtk_widget_show_all(color_dialog)

END SUB

'------------------------------------------------------------------------------------------------------------

' Handle COLOR selection
SUB Handle_Color_Dialog(NUMBER widget, int action)

    LOCAL colselect, gdkcol, x

    gtk_widget_hide(color_dialog)

    IF action IS GTK_RESPONSE_OK THEN
        g_object_get(color_dialog, "color-selection", ADDRESS(colselect), NUL)
        gdkcol = MEMORY(DEFAULT_WIDGET_SIZE)
        gtk_color_selection_get_current_color(colselect, gdkcol)

        FOR x = 0 TO gtk_notebook_get_n_pages(notebook)-1
            gtk_widget_modify_base(nbook[x].view, GTK_STATE_NORMAL, gdkcol)
        NEXT

        Statusbar_Msg(CONCAT$("Color was successfully changed."))

        FREE gdkcol

        Save_Defaults
    END IF

END SUB

'------------------------------------------------------------------------------------------------------------

' Update syntax file
SUB Get_Syntax

    Update_Syntaxfile(1)
    Statusbar_Msg(CONCAT$("Updated BaCon language file!"))

END SUB

'------------------------------------------------------------------------------------------------------------

' Info dialog
SUB Show_Info

    gtk_show_about_dialog(win, "program-name", DEFTITLE$, "version", DEFVERSION$, "copyright", "(c) Peter van Eerten", \
            "comments", "An editor built around GtkSourceView.", "website", "http://www.basic-converter.org/", NUL)

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Cut_Text

    LOCAL page

    page = gtk_notebook_get_current_page(notebook)
    gtk_text_buffer_cut_clipboard(nbook[page].buffer, ClipBoard, 1)

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Copy_Text

    LOCAL page

    page = gtk_notebook_get_current_page(notebook)
    gtk_text_buffer_copy_clipboard(nbook[page].buffer, ClipBoard)

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Paste_Text

    LOCAL page

    page = gtk_notebook_get_current_page(notebook)
    gtk_text_buffer_paste_clipboard(nbook[page].buffer, ClipBoard, NUL, 1)

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Undo_Action

    LOCAL page

    page = gtk_notebook_get_current_page(notebook)
    IF gtk_source_buffer_can_undo(nbook[page].buffer) THEN gtk_source_buffer_undo(nbook[page].buffer)

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Select_All

    LOCAL page, start_iter, end_iter

    start_iter = MEMORY(DEFAULT_WIDGET_SIZE)
    end_iter = MEMORY(DEFAULT_WIDGET_SIZE)

    page = gtk_notebook_get_current_page(notebook)

    gtk_text_buffer_get_bounds(nbook[page].buffer, start_iter, end_iter)

    gtk_text_buffer_select_range(nbook[page].buffer, start_iter, end_iter)

    FREE start_iter
    FREE end_iter

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Close_Tab

    LOCAL title$
    LOCAL dialog, page, x
    LOCAL response TYPE int

    page = gtk_notebook_get_current_page(notebook)

    IF page >= 0 THEN

        title$ = gtk_label_get_text(nbook[page].label)

        IF EQUAL(LEFT$(title$, 1), "*") THEN
            dialog = gtk_message_dialog_new(win, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, CONCAT$("Save program '", MID$(title$, 2), "' ?"), NUL)
            gtk_window_set_title(dialog, "Warning!")
            response = gtk_dialog_run(dialog)
            gtk_widget_destroy(dialog)
            IF response IS GTK_RESPONSE_YES THEN Save_File
        END IF

        gtk_notebook_remove_page(notebook, page)

        ' Scroll record array
        FOR x = page TO gtk_notebook_get_n_pages(notebook)-1
            nbook[x].view = nbook[x+1].view
            nbook[x].buffer = nbook[x+1].buffer
            nbook[x].label = nbook[x+1].label
            nbook[x].title$ = nbook[x+1].title$
        NEXT
    END IF

    ' If there are no tabs, disable some of the menus
    IF gtk_notebook_get_n_pages(notebook) IS 0 THEN
        gtk_widget_set_sensitive(file_menu, FALSE)
        gtk_widget_set_sensitive(save_menu, FALSE)
        gtk_widget_set_sensitive(saveas_menu, FALSE)
        gtk_widget_set_sensitive(closetab_menu, FALSE)
        gtk_widget_set_sensitive(cut_menu, FALSE)
        gtk_widget_set_sensitive(copy_menu, FALSE)
        gtk_widget_set_sensitive(paste_menu, FALSE)
        gtk_widget_set_sensitive(select_menu, FALSE)
        gtk_widget_set_sensitive(undo_menu, FALSE)
        gtk_widget_set_sensitive(find_menu, FALSE)
        gtk_widget_set_sensitive(indent_menu, FALSE)
        gtk_widget_set_sensitive(linenr_menu, FALSE)
        gtk_widget_set_sensitive(wrap_menu, FALSE)
        gtk_widget_set_sensitive(font_menu, FALSE)
        gtk_widget_set_sensitive(color_menu, FALSE)
    END IF

END SUB

'------------------------------------------------------------------------------------------------------------

' Callback for exit
SUB Exit_Prog

    REPEAT
        Close_Tab
    UNTIL gtk_notebook_get_n_pages(notebook) IS 0

    Save_Defaults

    ' Use normal 'exit' as gtk_exit is deprecated
    END

ENDSUB

'------------------------------------------------------------------------------------------------------------

SUB Create_Page

    LOCAL lm, lang, buffer, view, scrolled, label, page

    ' Create language manager
    lm = gtk_source_language_manager_get_default()

    ' Define language
    lang = gtk_source_language_manager_get_language(lm, "bacon")

    ' Setup buffer and viewer using BaCon as language
    buffer = gtk_source_buffer_new_with_language(lang)
    g_signal_connect_data(buffer, "changed", Set_Changed, 0, 0, 0)
    gtk_source_buffer_set_highlight_matching_brackets(buffer, 1)
    view = gtk_source_view_new_with_buffer(buffer)

    ' Tab space to 4 characters
    gtk_source_view_set_tab_width(view, 4)

    ' Create scrolled window
    scrolled = gtk_scrolled_window_new(0, 0)
    gtk_scrolled_window_set_policy(scrolled, 1, 1)
    gtk_scrolled_window_set_shadow_type(scrolled, 3)
    gtk_container_add(scrolled, view)

    ' Create the tab
    label = gtk_label_new("New")
    page = gtk_notebook_append_page(notebook, scrolled, label)

    ' Set new tab as active
    gtk_widget_show_all(notebook)
    gtk_notebook_set_current_page(notebook, page)

    ' Store new widgets
    nbook[page].view = view
    nbook[page].buffer = buffer
    nbook[page].label = label
    nbook[page].title$ = ""

    IF gtk_notebook_get_n_pages(notebook) IS 1 THEN
        gtk_widget_set_sensitive(file_menu, TRUE)
        gtk_widget_set_sensitive(save_menu, TRUE)
        gtk_widget_set_sensitive(saveas_menu, TRUE)
        gtk_widget_set_sensitive(closetab_menu, TRUE)
        gtk_widget_set_sensitive(cut_menu, TRUE)
        gtk_widget_set_sensitive(copy_menu, TRUE)
        gtk_widget_set_sensitive(paste_menu, TRUE)
        gtk_widget_set_sensitive(select_menu, TRUE)
        gtk_widget_set_sensitive(undo_menu, TRUE)
        gtk_widget_set_sensitive(find_menu, TRUE)
        gtk_widget_set_sensitive(indent_menu, TRUE)
        gtk_widget_set_sensitive(linenr_menu, TRUE)
        gtk_widget_set_sensitive(wrap_menu, TRUE)
        gtk_widget_set_sensitive(font_menu, TRUE)
        gtk_widget_set_sensitive(color_menu, TRUE)
    END IF

    gtk_widget_grab_focus(view)

    ' Get the default settings for the new page
    IF FILEEXISTS(CONCAT$(GETENVIRON$("HOME"), "/.bacon.txt")) THEN Load_Defaults

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Open_Page

    Create_Page
    Open_File

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Start_Find

    gtk_widget_show_all(find)
    gtk_widget_grab_focus(find_entry)

END SUB

'------------------------------------------------------------------------------------------------------------

SUB Hide_Find

    LOCAL page

    gtk_widget_hide(find)
    page = gtk_notebook_get_current_page(notebook)

    IF nbook[page].iter ISNOT 0 THEN FREE nbook[page].iter
    nbook[page].iter = 0

END SUB

'------------------------------------------------------------------------------------------------------------

' Action to find term and scroll text_view to that place
SUB Find_Term

    LOCAL iter, start_iter, end_iter, page, result, find_warning, response, mark
    LOCAL text$

    text$ = gtk_entry_get_text(find_entry)

    page = gtk_notebook_get_current_page(notebook)

    IF nbook[page].iter IS 0 THEN
        nbook[page].iter = MEMORY(DEFAULT_WIDGET_SIZE)
        gtk_text_buffer_get_start_iter(nbook[page].buffer, nbook[page].iter)
    END IF

    start_iter = MEMORY(DEFAULT_WIDGET_SIZE)
    end_iter = MEMORY(DEFAULT_WIDGET_SIZE)

    result = gtk_text_iter_forward_search(nbook[page].iter, text$, 0, start_iter, end_iter, NUL)

    IF NOT(result) THEN
        find_warning = gtk_message_dialog_new(win, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, \
                GTK_BUTTONS_CLOSE, CONCAT$(NL$, "Term '", text$, "' not found!"), NUL)
        gtk_window_set_title(find_warning, "For your information")
        response = gtk_dialog_run(find_warning)
        gtk_widget_destroy(find_warning)
        FREE end_iter
        FREE nbook[page].iter
        nbook[page].iter = 0
    ELSE
        gtk_text_buffer_select_range(nbook[page].buffer, start_iter, end_iter)
        mark = gtk_text_buffer_get_selection_bound(nbook[page].buffer)
        gtk_text_view_scroll_to_mark(nbook[page].view, mark, 0, 0, 0, 0)
        FREE nbook[page].iter
        nbook[page].iter = end_iter
    END IF

    FREE start_iter

END SUB

'------------------------------------------------------------------------------------------------------------

FUNCTION Show_Popup

    LOCAL gdkwin, mask
    LOCAL x, y TYPE int

    gdkwin = gdk_window_at_pointer(ADDRESS(x), ADDRESS(y))
    gdk_window_get_pointer(gdkwin, ADDRESS(x), ADDRESS(y), ADDRESS(mask))

    ' Only right mouse button should activate popup
    IF (mask & (1 << 10)) THEN
        gtk_menu_popup(cmenu, 0, 0, NUL, NUL, 3, 0)
        RETURN TRUE
    END IF

    ' If other button is pressed, resume default event handling
    RETURN FALSE

END FUNCTION

'------------------------------------------------------------------------------------------------------------

SUB Build_Gui

    LOCAL menubar, fmenu, sep1, sep5, fmenu_item1
    LOCAL emenu, sep4, sep6, sep7, emenu_item1
    LOCAL omenu, sep2, sep3, fmenu_item2
    LOCAL hmenu, fmenu_item3
    LOCAL cnewtab_menu, copentab_menu, cclosetab_menu
    LOCAL vbox, accel_group, find_ok, find_get, find_vbox, find_hbox

    ' Initialize GTK
    gtk_init(0, 0)

    ' Define main window
    win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
    gtk_window_set_title(win, DEFTITLE$)
    gtk_window_set_position(win, 1)
    gtk_window_set_default_size(win, 700, 600)
    gtk_window_set_icon_name(win, "gtk-edit")

    ' Define callback when deleting window
    g_signal_connect_data(win, "delete-event", Exit_Prog, 0, 0, 0)

    ' Create FILE OPEN
    file_dialog = gtk_file_chooser_dialog_new("Open file...", win, 0, "gtk-cancel", GTK_RESPONSE_CANCEL, "gtk-open", GTK_RESPONSE_ACCEPT, NUL)
    gtk_window_set_transient_for(file_dialog, win)
    gtk_window_set_position(file_dialog, GTK_WIN_POS_CENTER_ON_PARENT)
    gtk_window_set_default_size(file_dialog, 300, 300)
    g_signal_connect_data(file_dialog, "delete-event", Hide_Dialog, 0, 0, 0)
    g_signal_connect_data(file_dialog, "response", Handle_Open_Dialog, 0, 0, 0)

    ' Create SAVE AS
    save_dialog = gtk_file_chooser_dialog_new("Save file as...", win, 1, "gtk-cancel", GTK_RESPONSE_CANCEL, "gtk-save", GTK_RESPONSE_ACCEPT, NUL)
    gtk_window_set_transient_for(save_dialog, win)
    gtk_window_set_position(save_dialog, GTK_WIN_POS_CENTER_ON_PARENT)
    gtk_window_set_default_size(save_dialog, 300, 300)
    g_signal_connect_data(save_dialog, "delete-event", Hide_Dialog, 0, 0, 0)
    g_signal_connect_data(save_dialog, "response", Handle_Save_Dialog, 0, 0, 0)

    ' Create FONT SELECTION
    font_dialog = gtk_font_selection_dialog_new("Select font...")
    gtk_window_set_transient_for(font_dialog, win)
    gtk_window_set_position(font_dialog, GTK_WIN_POS_CENTER_ON_PARENT)
    gtk_window_set_default_size(font_dialog, 300, 300)
    g_signal_connect_data(font_dialog, "delete-event", Hide_Dialog, 0, 0, 0)
    g_signal_connect_data(font_dialog, "response", Handle_Font_Dialog, 0, 0, 0)

    ' Create COLOR SELECTION
    color_dialog = gtk_color_selection_dialog_new("Select color...")
    gtk_window_set_transient_for(color_dialog, win)
    gtk_window_set_position(color_dialog, GTK_WIN_POS_CENTER_ON_PARENT)
    gtk_window_set_default_size(color_dialog, 300, 300)
    g_signal_connect_data(color_dialog, "delete-event", Hide_Dialog, 0, 0, 0)
    g_signal_connect_data(color_dialog, "response", Handle_Color_Dialog, 0, 0, 0)

    ' Define the global clipboard
    ClipBoard = gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", 0))

    ' Create menubar
    menubar = gtk_menu_bar_new()

    ' Create FILE submenu
    fmenu = gtk_menu_new()
    file_menu = gtk_menu_item_new_with_mnemonic("_Open...")
    save_menu = gtk_menu_item_new_with_mnemonic("_Save")
    saveas_menu = gtk_menu_item_new_with_mnemonic("S_ave as...")
    sep1 = gtk_menu_item_new()
    newtab_menu = gtk_menu_item_new_with_mnemonic("_New tab")
    opentab_menu = gtk_menu_item_new_with_mnemonic("O_pen tab...")
    closetab_menu = gtk_menu_item_new_with_mnemonic("_Close tab")
    sep5 = gtk_menu_item_new()
    exit_menu = gtk_menu_item_new_with_mnemonic("_Exit")
    gtk_menu_shell_append(fmenu, file_menu)
    gtk_menu_shell_append(fmenu, save_menu)
    gtk_menu_shell_append(fmenu, saveas_menu)
    gtk_menu_shell_append(fmenu, sep1)
    gtk_menu_shell_append(fmenu, newtab_menu)
    gtk_menu_shell_append(fmenu, opentab_menu)
    gtk_menu_shell_append(fmenu, closetab_menu)
    gtk_menu_shell_append(fmenu, sep5)
    gtk_menu_shell_append(fmenu, exit_menu)
    fmenu_item1 = gtk_menu_item_new_with_mnemonic("_File")
    gtk_menu_item_set_submenu(fmenu_item1, fmenu)
    gtk_menu_shell_append(menubar, fmenu_item1)

    ' Create EDIT submenu
    emenu = gtk_menu_new()
    cut_menu = gtk_menu_item_new_with_mnemonic("_Cut")
    copy_menu = gtk_menu_item_new_with_mnemonic("C_opy")
    paste_menu = gtk_menu_item_new_with_mnemonic("_Paste")
    sep4 = gtk_menu_item_new()
    undo_menu = gtk_menu_item_new_with_mnemonic("_Undo")
    sep6 = gtk_menu_item_new()
    select_menu = gtk_menu_item_new_with_mnemonic("_Select all")
    sep7 = gtk_menu_item_new()
    find_menu = gtk_menu_item_new_with_mnemonic("_Find...")
    gtk_menu_shell_append(emenu, cut_menu)
    gtk_menu_shell_append(emenu, copy_menu)
    gtk_menu_shell_append(emenu, paste_menu)
    gtk_menu_shell_append(emenu, sep4)
    gtk_menu_shell_append(emenu, undo_menu)
    gtk_menu_shell_append(emenu, sep6)
    gtk_menu_shell_append(emenu, select_menu)
    gtk_menu_shell_append(emenu, sep7)
    gtk_menu_shell_append(emenu, find_menu)
    emenu_item1 = gtk_menu_item_new_with_mnemonic("_Edit")
    gtk_menu_item_set_submenu(emenu_item1, emenu)
    gtk_menu_shell_append(menubar, emenu_item1)

    ' Create OPTIONS submenu
    omenu = gtk_menu_new()
    indent_menu = gtk_check_menu_item_new_with_mnemonic("_Auto indent")
    linenr_menu = gtk_check_menu_item_new_with_mnemonic("_Line numbers")
    wrap_menu = gtk_check_menu_item_new_with_mnemonic("_Wrap lines")
    sep2 = gtk_menu_item_new()
    font_menu = gtk_menu_item_new_with_mnemonic("_Select font...")
    color_menu = gtk_menu_item_new_with_mnemonic("_Background color...")
    sep3 = gtk_menu_item_new()
    syntax_menu = gtk_menu_item_new_with_mnemonic("_Update syntaxfile...")
    gtk_menu_shell_append(omenu, indent_menu)
    gtk_menu_shell_append(omenu, linenr_menu)
    gtk_menu_shell_append(omenu, wrap_menu)
    gtk_menu_shell_append(omenu, sep2)
    gtk_menu_shell_append(omenu, font_menu)
    gtk_menu_shell_append(omenu, color_menu)
    gtk_menu_shell_append(omenu, sep3)
    gtk_menu_shell_append(omenu, syntax_menu)
    fmenu_item2 = gtk_menu_item_new_with_mnemonic("_Options")
    gtk_menu_item_set_submenu(fmenu_item2, omenu)
    gtk_menu_shell_append(menubar, fmenu_item2)

    ' Create HELP submenu
    hmenu = gtk_menu_new()
    about_menu = gtk_menu_item_new_with_mnemonic("_About...")
    gtk_menu_shell_append(hmenu, about_menu)
    fmenu_item3 = gtk_menu_item_new_with_mnemonic("_Info")
    gtk_menu_item_set_submenu(fmenu_item3, hmenu)
    gtk_menu_shell_append(menubar, fmenu_item3)

    ' Create contextmenu
    cmenu = gtk_menu_new()
    cnewtab_menu = gtk_menu_item_new_with_mnemonic("New tab")
    copentab_menu = gtk_menu_item_new_with_mnemonic("Open tab...")
    cclosetab_menu = gtk_menu_item_new_with_mnemonic("Close tab")
    gtk_menu_shell_append(cmenu, cnewtab_menu)
    gtk_menu_shell_append(cmenu, copentab_menu)
    gtk_menu_shell_append(cmenu, cclosetab_menu)
    g_signal_connect_data(cnewtab_menu, "activate", Create_Page, 0, 0, 0)
    g_signal_connect_data(copentab_menu, "activate", Open_Page, 0, 0, 0)
    g_signal_connect_data(cclosetab_menu, "activate", Close_Tab, 0, 0, 0)
    gtk_widget_show_all(cmenu)

    ' Create notebook
    notebook = gtk_notebook_new()
    g_signal_connect_data(notebook, "button-press-event", Show_Popup, 0, 0, 0)

    Create_Page

    ' Create statusbar
    statusbar = gtk_statusbar_new()
    msg_cid = gtk_statusbar_get_context_id(statusbar, "bacon")
    gtk_statusbar_push(statusbar, msg_cid, CONCAT$("Compiled with BaCon ", VERSION$))

    ' Setup vbox
    vbox = gtk_vbox_new(0, 0)
    gtk_box_pack_start(vbox, menubar, 0, 0, 1)
    gtk_box_pack_start(vbox, notebook, 1, 1, 1)
    gtk_box_pack_start(vbox, statusbar, 0, 0, 1)

    ' Finalize GUI
    gtk_container_add(win, vbox)
    gtk_widget_show_all(win)

    ' Define some accelerator keys
    accel_group = gtk_accel_group_new()
    gtk_window_add_accel_group(win, accel_group)
    gtk_widget_add_accelerator(file_menu, "activate", accel_group, gdk_keyval_from_name("o"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(save_menu, "activate", accel_group, gdk_keyval_from_name("s"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(saveas_menu, "activate", accel_group, gdk_keyval_from_name("s"), GDK_CONTROL_MASK|GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(newtab_menu, "activate", accel_group, gdk_keyval_from_name("n"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(opentab_menu, "activate", accel_group, gdk_keyval_from_name("n"), GDK_CONTROL_MASK|GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(closetab_menu, "activate", accel_group, gdk_keyval_from_name("t"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(exit_menu, "activate", accel_group, gdk_keyval_from_name("q"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(cut_menu, "activate", accel_group, gdk_keyval_from_name("x"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(copy_menu, "activate", accel_group, gdk_keyval_from_name("c"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(paste_menu, "activate", accel_group, gdk_keyval_from_name("v"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(undo_menu, "activate", accel_group, gdk_keyval_from_name("z"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(select_menu, "activate", accel_group, gdk_keyval_from_name("a"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)
    gtk_widget_add_accelerator(find_menu, "activate", accel_group, gdk_keyval_from_name("f"), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE)

    ' Get the default settings for the new page
    IF FILEEXISTS(CONCAT$(GETENVIRON$("HOME"), "/.bacon.txt")) THEN Load_Defaults

    ' Create FIND dialog
    find = gtk_window_new(GTK_WINDOW_TOPLEVEL)
    gtk_window_set_title(find, "Find term")
    gtk_window_set_transient_for(find, win)
    gtk_window_set_position(find, GTK_WIN_POS_CENTER_ON_PARENT)
    gtk_window_set_default_size(find, 300, 70)
    gtk_window_set_icon_name(find, "gtk-find")
    find_entry = gtk_entry_new()
    find_ok = gtk_button_new_from_stock("gtk-close")
    find_get = gtk_button_new_from_stock("gtk-find")
    ' Now arrange FIND widgets on window using boxes
    find_vbox = gtk_vbox_new(0, 0)
    find_hbox = gtk_hbox_new(0, 0)
    gtk_box_pack_start(find_vbox, find_entry, 1, 1, 1)
    gtk_box_pack_start(find_hbox, find_ok, 0, 0, 1)
    gtk_box_pack_end(find_hbox, find_get, 0, 0, 1)
    gtk_box_pack_start(find_vbox, find_hbox, 0, 0, 1)
    gtk_container_add(find, find_vbox)
    g_signal_connect_data(find, "delete-event", Hide_Find, 0, 0, 0)
    g_signal_connect_data(find_ok, "clicked", Hide_Find, 0, 0, 0)
    g_signal_connect_data(find_get, "clicked", Find_Term, 0, 0, 0)
    g_signal_connect_data(find_entry, "activate", Find_Term, 0, 0, 0)

    ' Callbacks for the menu items
    g_signal_connect_data(file_menu, "activate", Open_File, 0, 0, 0)
    g_signal_connect_data(save_menu, "activate", Save_File, 0, 0, 0)
    g_signal_connect_data(saveas_menu, "activate", Save_Fileas, 0, 0, 0)
    g_signal_connect_data(newtab_menu, "activate", Create_Page, 0, 0, 0)
    g_signal_connect_data(opentab_menu, "activate", Open_Page, 0, 0, 0)
    g_signal_connect_data(closetab_menu, "activate", Close_Tab, 0, 0, 0)
    g_signal_connect_data(exit_menu, "activate", Exit_Prog, 0, 0, 0)
    g_signal_connect_data(cut_menu, "activate", Cut_Text, 0, 0, 0)
    g_signal_connect_data(copy_menu, "activate", Copy_Text, 0, 0, 0)
    g_signal_connect_data(paste_menu, "activate", Paste_Text, 0, 0, 0)
    g_signal_connect_data(undo_menu, "activate", Undo_Action, 0, 0, 0)
    g_signal_connect_data(select_menu, "activate", Select_All, 0, 0, 0)
    g_signal_connect_data(find_menu, "activate", Start_Find, 0, 0, 0)
    g_signal_connect_data(indent_menu, "activate", Set_Indent, 0, 0, 0)
    g_signal_connect_data(linenr_menu, "activate", Set_Linenr, 0, 0, 0)
    g_signal_connect_data(wrap_menu, "activate", Set_Wrapmode, 0, 0, 0)
    g_signal_connect_data(font_menu, "activate", Select_Font, 0, 0, 0)
    g_signal_connect_data(color_menu, "activate", Select_Color, 0, 0, 0)
    g_signal_connect_data(syntax_menu, "activate", Get_Syntax, 0, 0, 0)
    g_signal_connect_data(about_menu, "activate", Show_Info, 0, 0, 0)

    'Endless mainloop
    gtk_main

END SUB

'------------------------------------------------------------------------------------------------------------