REM
REM H.U.G. - Highlevel Universal GUI implementation for BaCon based on GTK
REM (c) Peter van Eerten - August 2009 / March 2010, GPL.
REM
REM Version 0.1 - Initial release
REM Version 0.2 - Fixed crash in OpenSolaris
REM Version 0.3 - Better library naming
REM Version 0.4 - GRAB functions returning text should contain '$'
REM Version 0.5 - Added spinbox and fileselector
REM Version 0.6 - Empty string clears widgets in TEXT method, added SYNC
REM Version 0.7 - Better casting to INT, changed DIALOG implementation
REM Version 0.8 - Simplified API even more, renamed TEXT to MARK
REM Version 0.9 - HUGOPTIONS to define HUG behaviour, TOGGLE button
REM Version 0.10 - Many requested the Notebook widget, here it is.
REM Version 0.11 - Re-implemented IMAGE() function, no API changes.
REM Version 0.12 - Minor bugfixes
REM Version 0.13 - IMAGE to define ImageWidget, PICTURE to load background pics into canvas
REM Version 0.14 - OpenBSD reveals wrong typecasting of some functions
REM Version 0.15 - Fixed issue with window placement
REM Version 0.16 - Password widget SET/GET methods to hide/show characters, bug in GET method for LIST fixed
REM Version 0.17 - Texts in labels selectable
REM Version 0.18 - CLIPBOARD object
REM Version 0.19 - SET/GET for label to set/get selectable, FILEDIALOG has CANCEL button (James Fuller)
REM Version 0.20 - Callback functions should have void* prototype (James Fuller)
REM
REM --------------------------------------------------------------------------------------------------

REM Check on the availability of GTK
TRAP LOCAL
CATCH GOTO hug_print_message

REM -------------------------- IMPORT FROM GTK ----------------------------------

CONST hug_Gtk$ = "libgtk-x11-2.0.so.0"
CONST hug_Gdk$ = "libgdk-x11-2.0.so.0"
CONST hug_Glib$ = "libglib-2.0.so.0"
CONST hug_Gobject$ = "libgobject-2.0.so.0"
CONST hug_Pango$ = "libpango-1.0.so.0"

IMPORT "gtk_init(int*,void*)" FROM hug_Gtk$ TYPE void
IMPORT "gdk_atom_intern(char*,int)" FROM hug_Gdk$ TYPE long
IMPORT "gdk_color_copy(long)" FROM hug_Gdk$ TYPE long
IMPORT "gdk_color_free(long)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_color_parse(char*,long)" FROM hug_Gdk$ TYPE int
IMPORT "gdk_draw_arc(long,long,int,int,int,int,int,int,int)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_draw_layout_with_colors(long,long,int,int,long,long,long)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_draw_line(long,long,int,int,int,int)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_draw_pixbuf(long,long,long,int,int,int,int,int,int,int,int,int)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_draw_point(long,long,int,int)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_draw_rectangle(long,long,int,int,int,int,int)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_gc_new(long)" FROM hug_Gdk$ TYPE long
IMPORT "gdk_gc_set_rgb_bg_color(long,long)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_gc_set_rgb_fg_color(long,long)" FROM hug_Gdk$ TYPE void
IMPORT "gdk_get_default_root_window" FROM hug_Gdk$ TYPE long
IMPORT "gdk_keymap_get_default" FROM hug_Gdk$ TYPE long
IMPORT "gdk_pixmap_new(long,int,int,int)" FROM hug_Gdk$ TYPE long
IMPORT "gdk_pixbuf_new_from_file(char*,void**)" FROM hug_Gdk$ TYPE long
IMPORT "gdk_window_at_pointer(long,long)" FROM hug_Gdk$ TYPE long
IMPORT "gdk_window_get_pointer(long,long,long,long)" FROM hug_Gdk$ TYPE long
IMPORT "g_main_context_iteration(long, int)" FROM hug_Glib$ TYPE int
IMPORT "g_object_set(long,char*,...)" FROM hug_Gobject$ TYPE void
IMPORT "g_object_unref(long)" FROM hug_Gobject$ TYPE void
IMPORT "g_signal_connect_data(long,char*,void*,long,long,int)" FROM hug_Gobject$ TYPE void
IMPORT "g_source_remove(int)" FROM hug_Glib$ TYPE int
IMPORT "g_timeout_add(int,void*,long)" FROM hug_Glib$ TYPE int
IMPORT "gtk_bin_get_child(long)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_button_get_label(long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_button_new_from_stock(char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_button_new_with_mnemonic(char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_button_set_label(long,char*)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_cell_renderer_text_new" FROM hug_Gtk$ TYPE long
IMPORT "gtk_check_button_new_with_label(char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_clipboard_get(long)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_clipboard_set_text(long,char*,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_clipboard_wait_for_text (long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_combo_box_append_text(long,char*)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_combo_box_get_active(long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_combo_box_get_active_text(long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_combo_box_new_text" FROM hug_Gtk$ TYPE long
IMPORT "gtk_combo_box_set_active(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_container_add(long,long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_entry_get_text(long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_entry_get_visibility(long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_entry_new" FROM hug_Gtk$ TYPE long
IMPORT "gtk_entry_set_text(long,char*)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_entry_set_visibility(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_event_box_new" FROM hug_Gtk$ TYPE long
IMPORT "gtk_events_pending" FROM hug_Gtk$ TYPE int
IMPORT "gtk_exit(int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_file_chooser_dialog_new(char*,long,int,char*,...)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_file_chooser_get_filename(long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_file_chooser_set_filename(long,char*)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_fixed_new" FROM hug_Gtk$ TYPE long
IMPORT "gtk_fixed_put(long,long,int,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_frame_get_label(long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_frame_get_label_widget(long)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_frame_new(char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_frame_set_label(long,char*)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_hseparator_new" FROM hug_Gtk$ TYPE long
IMPORT "gtk_image_new" FROM hug_Gtk$ TYPE long
IMPORT "gtk_image_new_from_file(char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_image_set_from_pixmap(long,long,long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_label_get_text(long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_label_new(char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_label_get_selectable(long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_label_set_selectable(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_label_set_text(long,char*)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_list_store_append(long,long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_list_store_clear(long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_list_store_new(int,...)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_list_store_set(long,long,...)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_main" FROM hug_Gtk$ TYPE void
IMPORT "gtk_main_iteration_do(int)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_message_dialog_new(long,int,int,int,char*,...)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_notebook_append_page(long,long,long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_notebook_get_tab_label_text(long,long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_notebook_get_current_page(long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_notebook_new" FROM hug_Gtk$ TYPE long
IMPORT "gtk_notebook_set_current_page(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_radio_button_new_with_label_from_widget(long,char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_scrolled_window_new(long,long)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_scrolled_window_set_policy(long,int,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_scrolled_window_set_shadow_type(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_spin_button_get_value_as_int(long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_spin_button_new_with_range(double,double,double)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_spin_button_set_value(long,double)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_table_new(int,int,int)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_table_attach_defaults(long,long,int,int,int,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_buffer_create_mark(long,char*,long,int)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_text_buffer_delete_mark(long,long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_buffer_get_end_iter(long,long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_buffer_get_iter_at_line(long,long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_buffer_get_line_count(long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_text_buffer_get_start_iter(long,long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_buffer_get_text(long,long,long,int)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_text_buffer_insert_at_cursor(long,char*,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_buffer_new(long)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_text_buffer_set_text(long,char*,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_view_new_with_buffer(long)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_text_view_scroll_to_mark(long,long,double,int,double,double)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_view_set_editable(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_text_view_set_wrap_mode(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_toggle_button_get_active(long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_toggle_button_new_with_mnemonic(char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_toggle_button_set_active(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_tree_model_get(long,long,...)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_tree_model_get_string_from_iter(long,long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_tree_path_free(long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_tree_path_new_from_string(char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_tree_selection_get_selected(long,long,long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_tree_selection_select_path(long,long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_tree_selection_set_mode(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_tree_view_append_column(long,long)" FROM hug_Gtk$ TYPE int
IMPORT "gtk_tree_view_column_new_with_attributes(char*,long,...)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_tree_view_get_selection(long)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_tree_view_new_with_model(long)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_tree_view_set_headers_visible(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_vseparator_new" FROM hug_Gtk$ TYPE long
IMPORT "gtk_widget_create_pango_layout(long,char*)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_widget_grab_focus(long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_hide(long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_modify_font(long,long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_queue_draw(long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_realize(long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_set_events(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_set_sensitive(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_set_size_request(long,int,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_show_all(long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_widget_show(long)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_window_get_title(long)" FROM hug_Gtk$ TYPE char*
IMPORT "gtk_window_new(int)" FROM hug_Gtk$ TYPE long
IMPORT "gtk_window_set_default_size(long,int,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_window_set_position(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_window_set_resizable(long,int)" FROM hug_Gtk$ TYPE void
IMPORT "gtk_window_set_title(long,char*)" FROM hug_Gtk$ TYPE void
IMPORT "pango_font_description_free(long)" FROM hug_Pango$ TYPE void
IMPORT "pango_font_description_from_string(char*)" FROM hug_Pango$ TYPE long

REM --------------------------------------------------------------------------------------------------

REM If this include file is called, always initiate GTK
gtk_init(0, 0)

REM Some global constants
CONST G_CONNECT_AFTER = 1 << 0
CONST GDK_BUTTON_RELEASE_MASK = 1 << 9
CONST GDK_KEY_PRESS_MASK = 1 << 10
CONST GDK_POINTER_MOTION_MASK = 1 << 2
CONST GDK_SCROLL_MASK = 1 << 21
CONST GTK_POLICY_AUTOMATIC = 1
CONST GTK_SELECTION_SINGLE = 1
CONST GTK_SHADOW_ETCHED_IN = 3
CONST GTK_WINDOW_TOPLEVEL = 0
CONST GTK_WIN_POS_CENTER = 1
CONST GTK_WIN_POS_CENTER_ON_PARENT = 4
CONST GTK_WRAP_WORD = 2
CONST G_TYPE_STRING = 64
CONST GTK_FILE_CHOOSER_ACTION_OPEN = 0
CONST GTK_FILE_CHOOSER_ACTION_SAVE = 1
CONST GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER = 2
CONST GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER = 3
CONST GTK_RESPONSE_ACCEPT = -3
CONST GTK_RESPONSE_DELETE_EVENT = -4
CONST GTK_RESPONSE_OK     = -5
CONST GTK_RESPONSE_CANCEL = -6
CONST GTK_RESPONSE_CLOSE  = -7
CONST GTK_RESPONSE_YES    = -8
CONST GTK_RESPONSE_NO     = -9
CONST GTK_RESPONSE_APPLY  = -10
CONST GTK_RESPONSE_HELP   = -11

REM Some HUG limitations
CONST HUG_DEFAULT_WIDGET_SIZE = 64
CONST HUG_MAX_WIDGETS = 4096

REM Global properties of the HUG GUI
RECORD hug_gui_properties
    LOCAL last_key_pressed, timeout_id, canvas
    LOCAL mousex, mousey, scroll, button TYPE int
    LOCAL options$ TYPE STRING
END RECORD

REM Properties of the widgets
RECORD hug_widget_properties[HUG_MAX_WIDGETS]
    LOCAL widget, font, focus, attach, signal, s_widget, xsize, ysize
    REM only for canvas
    LOCAL pix, image, color
    REM Function pointers for methods
    LOCAL (*grab)(long) TYPE STRING
    LOCAL (*text)(long,char*) TYPE void
    LOCAL (*get)(long) TYPE int
    LOCAL (*set)(long,int) TYPE void
    REM only for Notebook, max 64 pages per notebook
    LOCAL page[64] TYPE int
END RECORD

GLOBAL hug_pointer

hug_gui_properties.timeout_id = -1
hug_pointer = 0

REM --------------------------------------------------------------------------------------------------

FUNCTION hug_key_press(NUMBER widget, void *data1, NUMBER data2)

    REM should work for GTK 1.x and 2.x
    USEC
        typedef struct {
            int type;
            void *window;
            signed char send_event;
            unsigned int time;
            unsigned int state;
            unsigned int keyval;
        } GdkEventKey;

        GdkEventKey *keypress;
        keypress = (GdkEventKey*)data1;

        hug_gui_properties.last_key_pressed = keypress->keyval;

    END USEC

    RETURN FALSE

END FUNCTION

REM --------------------------------------------------------------------------------------------------

SUB hug_mouse_event (NUMBER widget, NUMBER data, long user)

    LOCAL gdk, x, y

    REM Works for GTK 2.x
    USEC
        typedef struct {
            int type;
            void *window;
            signed char send_event;
            unsigned int time;
            double x;
            double y;
            unsigned int state;
            int direction;
        } GdkEventScroll;

        GdkEventScroll *mousescroll;
        mousescroll = (GdkEventScroll*)data;

        if(user == 10) hug_gui_properties.scroll = mousescroll->direction;
        else hug_gui_properties.scroll = -1;

        typedef struct {
            int type;
            void *window;
            signed char send_event;
            unsigned int time;
            double x;
            double y;
            double *axes;
            unsigned int state;
            unsigned int button;
        } GdkEventButton;

        GdkEventButton *mousebutton;
        mousebutton = (GdkEventButton*)data;

        if(user == 20) hug_gui_properties.button = mousebutton->button;
        else hug_gui_properties.button = -1;

    END USEC

    REM always get position
    gdk = gdk_window_at_pointer(ADDRESS(x), ADDRESS(y))
    gdk_window_get_pointer(gdk, ADDRESS(hug_gui_properties.mousex), ADDRESS(hug_gui_properties.mousey), 0)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB hug_notebook_hack(NUMBER widget, NUMBER page, int pnr)

    LOCAL i

    REM Get notebook widget properties
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            BREAK
        END IF
    NEXT

    hug_widget_properties[i].attach = hug_widget_properties[i].page[pnr]

END SUB

REM --------------------------------------------------------------------------------------------------

SUB HUGOPTIONS (STRING arg$)

    hug_gui_properties.options$ = CONCAT$(hug_gui_properties.options$, " ", arg$)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB QUIT

    END

END SUB

REM --------------------------------------------------------------------------------------------------

SUB DRAW (NUMBER widget)

    LOCAL i

    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            hug_gui_properties.canvas = hug_widget_properties[i].pix
            BREAK
        END IF
    NEXT

END SUB

REM --------------------------------------------------------------------------------------------------

SUB HIDE (NUMBER widget)

    gtk_widget_hide(widget)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB SHOW (NUMBER widget)

    gtk_widget_show_all(widget)

END SUB

REM --------------------------------------------------------------------------------------------------

FUNCTION DUMMY_GRAB$ (NUMBER widget)

    RETURN ""

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION WINDOW_GRAB$ (NUMBER widget)

    LOCAL text$ TYPE STRING

    text$ = gtk_window_get_title(widget)
    RETURN text$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION NOTEBOOK_GRAB$ (NUMBER widget)

    LOCAL text$ TYPE STRING
    LOCAL i

    REM Get notebook widget properties
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            BREAK
        END IF
    NEXT

    text$ = gtk_notebook_get_tab_label_text(widget, hug_widget_properties[i].attach)
    RETURN text$

END FUNCTION


REM --------------------------------------------------------------------------------------------------

FUNCTION BUTTON_GRAB$ (NUMBER widget)

    LOCAL text$ TYPE STRING

    text$ = gtk_button_get_label(widget)
    RETURN text$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION ENTRY_GRAB$ (NUMBER widget)

    LOCAL text$ TYPE STRING

    text$ = gtk_entry_get_text(widget)
    RETURN text$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION MARK_GRAB$ (NUMBER widget)

    LOCAL text$ TYPE STRING

    text$ = gtk_label_get_text(widget)
    RETURN text$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION COMBO_GRAB$ (NUMBER widget)

    LOCAL text$ TYPE STRING

    text$ = gtk_combo_box_get_active_text(widget)
    RETURN text$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION FRAME_GRAB$ (NUMBER widget)

    LOCAL text$ TYPE STRING

    text$ = gtk_frame_get_label(widget)
    RETURN text$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION EDIT_GRAB$ (NUMBER widget)

    LOCAL buffer, i, iter1, iter2
    LOCAL text$

    REM Get buffer
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            buffer = hug_widget_properties[i].font
            BREAK
        END IF
    NEXT

    iter1 = MEMORY(HUG_DEFAULT_WIDGET_SIZE)
    iter2 = MEMORY(HUG_DEFAULT_WIDGET_SIZE)

    gtk_text_buffer_get_start_iter(buffer, iter1)
    gtk_text_buffer_get_end_iter(buffer, iter2)
    text$ = gtk_text_buffer_get_text(buffer, iter1, iter2, 1)

    FREE iter1
    FREE iter2

    RETURN text$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION LIST_GRAB$ (NUMBER widget)

    LOCAL sel, i, result, lst
    LOCAL get$ TYPE STRING

    REM Get widgets from LIST
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            sel = hug_widget_properties[i].attach
            lst = hug_widget_properties[i].focus
            BREAK
        END IF
    NEXT

    RECORD iter
        LOCAL stamp TYPE int
        LOCAL user_data TYPE void*
        LOCAL user_data2 TYPE void*
        LOCAL user_data3 TYPE void*
    END RECORD

    result = gtk_tree_selection_get_selected(sel, 0, ADDRESS(iter))

    IF ISTRUE(result) THEN
        gtk_tree_model_get(lst, ADDRESS(iter), 0, ADDRESS(get$), -1)
    ELSE
        get$ = ""
    END IF

    RETURN get$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION FILEDIALOG_GRAB$(NUMBER widget)

    LOCAL txt TYPE STRING

    txt = gtk_file_chooser_get_filename(widget)

    IF txt EQ 0 THEN
        RETURN ""
    END IF

    RETURN txt

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION CLIPBOARD_GRAB$ (NUMBER widget)

    LOCAL text$ TYPE STRING

    text$ = gtk_clipboard_wait_for_text(widget)
    RETURN text$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION GRAB$(NUMBER widget)

    LOCAL i
    LOCAL txt$

    txt$ = ""

    REM Get widgets from LIST
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            txt$ = hug_widget_properties[i].grab(widget)
            BREAK
        END IF
    NEXT

    RETURN txt$

END FUNCTION

REM --------------------------------------------------------------------------------------------------

SUB DUMMY_TEXT (NUMBER widget, STRING text$)

    ' We do nothing here

END SUB

REM --------------------------------------------------------------------------------------------------

SUB WINDOW_TEXT (NUMBER widget, STRING text$)

    gtk_window_set_title(widget, text$)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB NOTEBOOK_TEXT(NUMBER widget, STRING text$)

    LOCAL lab, dim, i, layer, which

    IF INSTR(hug_gui_properties.options$, "TABLE") THEN
        SPLIT hug_gui_properties.options$ BY " " TO opt$ SIZE dim
        FOR i = 0 TO dim - 1
            IF INSTR(opt$[i], "TABLE") AND i+2 <= dim THEN
                layer = gtk_table_new(VAL(opt$[i+2]), VAL(opt$[i+1]), 1)
                BREAK
            END IF
        NEXT
    ELSE
        layer = gtk_fixed_new()
    END IF

    gtk_widget_show(layer)

    lab = gtk_label_new(text$)
    which = gtk_notebook_append_page(widget, layer, lab)

    gtk_notebook_set_current_page(widget, which)

    REM Get notebook widget properties
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            BREAK
        END IF
    NEXT

    WITH hug_widget_properties[i]
        .attach = layer
        .page[which] = layer
    END WITH

END SUB

REM --------------------------------------------------------------------------------------------------

SUB BUTTON_TEXT (NUMBER widget, STRING text$)

    gtk_button_set_label(widget, text$)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB ENTRY_TEXT (NUMBER widget, STRING text$)

    gtk_entry_set_text(widget, text$)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB MARK_TEXT (NUMBER widget, STRING text$)

    gtk_label_set_text(widget, text$)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB COMBO_TEXT (NUMBER widget, STRING text$)

    gtk_combo_box_append_text(widget, text$)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB FRAME_TEXT (NUMBER widget, STRING text$)

    LOCAL i

    gtk_frame_set_label(widget, text$)

    REM Get label of frame
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            hug_widget_properties[i].font = gtk_frame_get_label_widget(widget)
            BREAK
        END IF
    NEXT

END SUB

REM --------------------------------------------------------------------------------------------------

SUB EDIT_TEXT (NUMBER widget, STRING text$)

    LOCAL buffer, i

    REM Get buffer
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            buffer = hug_widget_properties[i].font
            BREAK
        END IF
    NEXT

    REM Delete contents when text$ is empty
    IF LEN(text$) EQ 0 THEN
        gtk_text_buffer_set_text(buffer, "", 0)
    ELSE
        gtk_text_buffer_insert_at_cursor(buffer, text$, -1)
    END IF

END SUB

REM --------------------------------------------------------------------------------------------------

SUB LIST_TEXT (NUMBER widget, STRING text$)

    LOCAL lst, i

    REM Get list
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            lst = hug_widget_properties[i].focus
            BREAK
        END IF
    NEXT

    RECORD iter
        LOCAL stamp TYPE int
        LOCAL user_data TYPE void*
        LOCAL user_data2 TYPE void*
        LOCAL user_data3 TYPE void*
    END RECORD

    REM Delete contents when text$ is empty
    IF LEN(text$) EQ 0 THEN
        gtk_list_store_clear(lst)
    ELSE
        gtk_list_store_append(lst, ADDRESS(iter))
        gtk_list_store_set(lst, ADDRESS(iter), 0, text$, -1)
    END IF

END SUB

REM --------------------------------------------------------------------------------------------------

SUB FILEDIALOG_TEXT (NUMBER widget, STRING txt$)

    gtk_file_chooser_set_filename (widget, txt$)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB CLIPBOARD_TEXT(NUMBER widget, STRING text$)

    gtk_clipboard_set_text(widget, text$, -1)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB TEXT(NUMBER widget, STRING txt)

    LOCAL i

    REM Get widgets from LIST
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            USEC
                hug_widget_properties[i].text(widget, txt);
            ENDUSEC
            BREAK
        END IF
    NEXT

END SUB

REM --------------------------------------------------------------------------------------------------

FUNCTION DUMMY_GET (NUMBER widget)

    LOCAL dum TYPE int

    dum = 0

    RETURN dum

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION NOTEBOOK_GET (NUMBER widget)

    LOCAL get TYPE int

    get = gtk_notebook_get_current_page(widget)
    RETURN get

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION BUTTON_GET (NUMBER widget)

    LOCAL get TYPE int

    get = gtk_toggle_button_get_active(widget)
    RETURN get

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION PASSWORD_GET (NUMBER widget)

    LOCAL get TYPE int

    get = gtk_entry_get_visibility(widget)
    RETURN get

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION MARK_GET (NUMBER widget)

    LOCAL get TYPE int

    get = gtk_label_get_selectable(widget)
    RETURN get

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION COMBO_GET (NUMBER widget)

    LOCAL get TYPE int

    get = gtk_combo_box_get_active(widget)
    RETURN get

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION EDIT_GET (NUMBER widget)

    LOCAL i
    LOCAL get TYPE int

    REM Get buffer
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            get = gtk_text_buffer_get_line_count(hug_widget_properties[i].font)
            BREAK
        END IF
    NEXT

    RETURN get

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION LIST_GET (NUMBER widget)

    LOCAL sel, lst
    LOCAL get TYPE int

    REM Get widgets from LIST
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            sel = hug_widget_properties[i].attach
            lst = hug_widget_properties[i].focus
            BREAK
        END IF
    NEXT

    RECORD iter
        LOCAL stamp TYPE int
        LOCAL user_data TYPE void*
        LOCAL user_data2 TYPE void*
        LOCAL user_data3 TYPE void*
    END RECORD

    IF ISTRUE(gtk_tree_selection_get_selected(sel, 0, ADDRESS(iter))) THEN
        get = VAL(gtk_tree_model_get_string_from_iter(lst, ADDRESS(iter)))
    ELSE
        get = -1
    END IF

    RETURN get

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION SPIN_GET (NUMBER widget)

    LOCAL get TYPE int

    get = gtk_spin_button_get_value_as_int(widget)
    RETURN get

END FUNCTION


REM --------------------------------------------------------------------------------------------------

FUNCTION GET (NUMBER widget)

    LOCAL i, j

    REM Get widgets from LIST
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            j = hug_widget_properties[i].get(widget)
            BREAK
        END IF
    NEXT

    RETURN j

END FUNCTION

REM --------------------------------------------------------------------------------------------------

SUB DUMMY_SET (NUMBER widget, int value)

    ' We do nothing here

END SUB

REM --------------------------------------------------------------------------------------------------

SUB NOTEBOOK_SET (NUMBER widget, int value)

    gtk_notebook_set_current_page(widget, value)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB BUTTON_SET (NUMBER widget, int value)

    gtk_toggle_button_set_active(widget, value)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB PASSWORD_SET (NUMBER widget, int value)

    gtk_entry_set_visibility(widget, value)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB MARK_SET (NUMBER widget, int value)

    gtk_label_set_selectable(widget, value)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB COMBO_SET (NUMBER widget, int value)

    gtk_combo_box_set_active(widget, value)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB EDIT_SET (NUMBER widget, int value)

    LOCAL buffer, view, iter, i, mark

    REM Get buffer
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            buffer = hug_widget_properties[i].font
            view = hug_widget_properties[i].focus
            BREAK
        END IF
    NEXT

    iter = MEMORY(HUG_DEFAULT_WIDGET_SIZE)

    gtk_text_buffer_get_iter_at_line(buffer, iter, value)
    mark = gtk_text_buffer_create_mark(buffer, "mark", iter, 0)
    gtk_text_view_scroll_to_mark(view, mark, 0, 1, 0.0, 1.0)
    gtk_text_buffer_delete_mark(buffer, mark)

    FREE iter

END SUB

REM --------------------------------------------------------------------------------------------------

SUB LIST_SET (NUMBER widget, int value)

    LOCAL path, i, sel

    REM Get widgets from LIST
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            sel = hug_widget_properties[i].attach
            BREAK
        END IF
    NEXT

    path = gtk_tree_path_new_from_string(STR$(value))
    gtk_tree_selection_select_path(sel, path)
    gtk_tree_path_free(path)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB SPIN_SET (NUMBER widget, int value)

    LOCAL d TYPE FLOATING

    d = (double)value

    gtk_spin_button_set_value(widget, d)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB SET(NUMBER widget, int value)

    LOCAL i

    REM Get widgets from LIST
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            USEC
                hug_widget_properties[i].set(widget, value);
            ENDUSEC
            BREAK
        END IF
    NEXT

END SUB

REM --------------------------------------------------------------------------------------------------

REM The widgets
FUNCTION WINDOW (STRING title$, int xsize, int ysize)

    LOCAL win, layer, dim, i

    win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
    gtk_window_set_title(win, title$)
    gtk_window_set_position(win, GTK_WIN_POS_CENTER)

    g_signal_connect_data(win, "delete-event", QUIT, 0, 0, 0)
    g_signal_connect_data(win, "key-press-event", hug_key_press, 0, 0, 0)

    IF INSTR(hug_gui_properties.options$, "TABLE") THEN
        SPLIT hug_gui_properties.options$ BY " " TO opt$ SIZE dim
        FOR i = 0 TO dim - 1
            IF INSTR(opt$[i], "TABLE") AND i+2 <= dim THEN
                layer = gtk_table_new(VAL(opt$[i+2]), VAL(opt$[i+1]), 1)
                BREAK
            END IF
        NEXT
    ELSE
        gtk_window_set_resizable(win, 0)
        layer = gtk_fixed_new()
    END IF

    gtk_container_add(win, layer)
    gtk_widget_set_size_request(win, xsize, ysize)
    gtk_widget_show_all(win)

    WITH hug_widget_properties[hug_pointer]
        .widget = win
        .font = win
        .focus = win
        .attach = layer
        .signal = 1
        .s_widget = win
        .xsize = xsize
        .ysize = ysize
        .grab = WINDOW_GRAB$
        .text = WINDOW_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN win

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION NOTEBOOK(STRING title$, int xsize, int ysize)

    LOCAL notebook, layer, dim, i, lab, which

    notebook = gtk_notebook_new()
    
    IF INSTR(hug_gui_properties.options$, "TABLE") THEN
        SPLIT hug_gui_properties.options$ BY " " TO opt$ SIZE dim
        FOR i = 0 TO dim - 1
            IF INSTR(opt$[i], "TABLE") AND i+2 <= dim THEN
                layer = gtk_table_new(VAL(opt$[i+2]), VAL(opt$[i+1]), 1)
                BREAK
            END IF
        NEXT
    ELSE
        layer = gtk_fixed_new()
    END IF

    gtk_widget_show(layer)

    lab = gtk_label_new(title$)
    which = gtk_notebook_append_page(notebook, layer, lab)
    gtk_widget_show(notebook)

    g_signal_connect_data(notebook, "switch-page", hug_notebook_hack, 0, 0, 0)

    WITH hug_widget_properties[hug_pointer]
        .widget = notebook
        .font = lab
        .focus = notebook
        .attach = layer
        .signal = 0
        .s_widget = notebook
        .xsize = xsize
        .ysize = ysize
        .grab = NOTEBOOK_GRAB$
        .text = NOTEBOOK_TEXT
        .get = NOTEBOOK_GET
        .set = NOTEBOOK_SET
        .page[which] = layer
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN notebook

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION BUTTON (STRING text$, int xsize, int ysize)

    LOCAL but

    but = gtk_button_new_with_mnemonic(text$)

    gtk_widget_show(but)

    WITH hug_widget_properties[hug_pointer]
        .widget = but
        .font = gtk_bin_get_child(but)
        .focus = but
        .attach = but
        .signal = 2
        .s_widget = but
        .xsize = xsize
        .ysize = ysize
        .grab = BUTTON_GRAB$
        .text = BUTTON_TEXT
        .get = BUTTON_GET
        .set = BUTTON_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN but

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION STOCK (STRING text$, int xsize, int ysize)

    LOCAL but

    but = gtk_button_new_from_stock(text$)

    gtk_widget_show(but)

    WITH hug_widget_properties[hug_pointer]
        .widget = but
        .font = gtk_bin_get_child(but)
        .focus = but
        .attach = but
        .signal = 2
        .s_widget = but
        .xsize = xsize
        .ysize = ysize
        .grab = BUTTON_GRAB$
        .text = BUTTON_TEXT
        .get = BUTTON_GET
        .set = BUTTON_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN but

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION TOGGLE (STRING text$, int xsize, int ysize)

    LOCAL but

    but = gtk_toggle_button_new_with_mnemonic(text$)

    gtk_widget_show(but)

    WITH hug_widget_properties[hug_pointer]
        .widget = but
        .font = gtk_bin_get_child(but)
        .focus = but
        .attach = but
        .signal = 2
        .s_widget = but
        .xsize = xsize
        .ysize = ysize
        .grab = BUTTON_GRAB$
        .text = BUTTON_TEXT
        .get = BUTTON_GET
        .set = BUTTON_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN but

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION CHECK (STRING text$, int xsize, int ysize)

    LOCAL but

    but = gtk_check_button_new_with_label(text$)

    gtk_widget_show(but)

    WITH hug_widget_properties[hug_pointer]
        .widget = but
        .font = gtk_bin_get_child(but)
        .focus = but
        .attach = but
        .signal = 2
        .s_widget = but
        .xsize = xsize
        .ysize = ysize
        .grab = BUTTON_GRAB$
        .text = BUTTON_TEXT
        .get = BUTTON_GET
        .set = BUTTON_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN but

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION RADIO (STRING text$, int xsize, int ysize, NUMBER group)

    LOCAL but

    but = gtk_radio_button_new_with_label_from_widget(group, text$)

    gtk_widget_show(but)

    WITH hug_widget_properties[hug_pointer]
        .widget = but
        .font = gtk_bin_get_child(but)
        .focus = but
        .attach = but
        .signal = 2
        .s_widget = but
        .xsize = xsize
        .ysize = ysize
        .grab = BUTTON_GRAB$
        .text = BUTTON_TEXT
        .get = BUTTON_GET
        .set = BUTTON_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN but

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION ENTRY (STRING text$, int xsize, int ysize)

    LOCAL ent

    ent = gtk_entry_new()
    gtk_entry_set_text(ent, text$)
    gtk_widget_show(ent)

    WITH hug_widget_properties[hug_pointer]
        .widget = ent
        .font = ent
        .focus = ent
        .attach = 0
        .signal = 3
        .s_widget = ent
        .xsize = xsize
        .ysize = ysize
        .grab = ENTRY_GRAB$
        .text = ENTRY_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN ent

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION PASSWORD (int xsize, int ysize)

    LOCAL passwd

    passwd = gtk_entry_new()
    gtk_entry_set_visibility(passwd, 0)
    gtk_widget_show(passwd)

    WITH hug_widget_properties[hug_pointer]
        .widget = passwd
        .font = passwd
        .focus = passwd
        .attach = 0
        .signal = 3
        .s_widget = passwd
        .xsize = xsize
        .ysize = ysize
        .grab = ENTRY_GRAB$
        .text = ENTRY_TEXT
        .get = PASSWORD_GET
        .set = PASSWORD_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN passwd

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION MARK (STRING text$, int xsize, int ysize)

    LOCAL label

    label = gtk_label_new(text$)
    gtk_widget_show(label)

    WITH hug_widget_properties[hug_pointer]
        .widget = label
        .font = label
        .focus = 0
        .attach = 0
        .signal = 0
        .s_widget = label
        .xsize = xsize
        .ysize = ysize
        .grab = MARK_GRAB$
        .text = MARK_TEXT
        .get = MARK_GET
        .set = MARK_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN label

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION COMBO (STRING text$, int xsize, int ysize)

    LOCAL combo, but

    combo = gtk_combo_box_new_text()
    gtk_combo_box_append_text(combo, text$)
    gtk_combo_box_set_active(combo, 0)
    gtk_widget_show(combo)

    but = gtk_bin_get_child(combo)

    WITH hug_widget_properties[hug_pointer]
        .widget = combo
        .font = but
        .focus = but
        .attach = 0
        .signal = 5
        .s_widget = combo
        .xsize = xsize
        .ysize = ysize
        .grab = COMBO_GRAB$
        .text = COMBO_TEXT
        .get = COMBO_GET
        .set = COMBO_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN combo

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION HSEPARATOR (int xsize)

    LOCAL separator

    separator = gtk_hseparator_new()
    gtk_widget_show(separator)

    WITH hug_widget_properties[hug_pointer]
        .widget = separator
        .font = 0
        .focus = 0
        .attach = 0
        .signal = 0
        .s_widget = separator
        .xsize = xsize
        .ysize = 0
        .grab = DUMMY_GRAB$
        .text = DUMMY_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN separator

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION VSEPARATOR (int ysize)

    LOCAL separator

    separator = gtk_vseparator_new()
    gtk_widget_show(separator)

    WITH hug_widget_properties[hug_pointer]
        .widget = separator
        .font = 0
        .focus = 0
        .attach = 0
        .signal = 0
        .s_widget = separator
        .xsize = 0
        .ysize = ysize
        .grab = DUMMY_GRAB$
        .text = DUMMY_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN separator

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION FRAME (int xsize, int ysize)

    LOCAL myframe

    myframe = gtk_frame_new(0)
    gtk_widget_show(myframe)

    WITH hug_widget_properties[hug_pointer]
        .widget = myframe
        .font = 0
        .focus = 0
        .attach = 0
        .signal = 0
        .s_widget = myframe
        .xsize = xsize
        .ysize = ysize
        .grab = FRAME_GRAB$
        .text = FRAME_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN myframe

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION EDIT (int xsize, int ysize)

    LOCAL buffer, view, scrolled

    buffer = gtk_text_buffer_new(0)
    view = gtk_text_view_new_with_buffer(buffer)
    scrolled = gtk_scrolled_window_new(0, 0)
    gtk_scrolled_window_set_policy(scrolled, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC)
    gtk_scrolled_window_set_shadow_type(scrolled, GTK_SHADOW_ETCHED_IN)
    gtk_container_add(scrolled, view)
    gtk_text_view_set_editable(view, 1)
    gtk_text_view_set_wrap_mode(view, GTK_WRAP_WORD)
    gtk_widget_show_all(scrolled)

    WITH hug_widget_properties[hug_pointer]
        .widget = scrolled
        .font = buffer
        .focus = view
        .attach = 0
        .signal = 0
        .s_widget = scrolled
        .xsize = xsize
        .ysize = ysize
        .grab = EDIT_GRAB$
        .text = EDIT_TEXT
        .get = EDIT_GET
        .set = EDIT_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN scrolled

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION LIST (int xsize, int ysize)

    LOCAL lst, tree, sel, cell, tc, win

    lst = gtk_list_store_new(1, G_TYPE_STRING)
    tree = gtk_tree_view_new_with_model(lst)
    gtk_tree_view_set_headers_visible(tree, 0)

    sel = gtk_tree_view_get_selection(tree)
    gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE)
    cell = gtk_cell_renderer_text_new()

    tc = gtk_tree_view_column_new_with_attributes("dummy", cell, "text", 0, 0)
    gtk_tree_view_append_column(tree, tc)

    win = gtk_scrolled_window_new(0, 0)
    gtk_scrolled_window_set_policy(win, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC)
    gtk_scrolled_window_set_shadow_type(win, GTK_SHADOW_ETCHED_IN)
    gtk_container_add(win, tree)

    gtk_widget_show_all(win)

    WITH hug_widget_properties[hug_pointer]
        .widget = win
        .font = tree
        .focus = lst
        .attach = sel
        .signal = 5
        .s_widget = sel
        .xsize = xsize
        .ysize = ysize
        .grab = LIST_GRAB$
        .text = LIST_TEXT
        .get = LIST_GET
        .set = LIST_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN win

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION MSGDIALOG (STRING text$, int xsize, int ysize, int type, int buttons)

    LOCAL win, table, lbl, but
    LOCAL dialog

    IF type > 4 THEN type = 4
    IF type < 0 THEN type = 0

    text$ = CONCAT$(NL$, text$)
    dialog = gtk_message_dialog_new(0, 2, type, buttons, text$, 0)

    gtk_window_set_title(dialog, "HUG message")
    gtk_window_set_position(dialog, GTK_WIN_POS_CENTER)
    gtk_window_set_default_size(dialog, xsize, ysize)

    WITH hug_widget_properties[hug_pointer]
        .widget = dialog
        .font = dialog
        .focus = 0
        .attach = 0
        .signal = 7
        .s_widget = dialog
        .xsize = xsize
        .ysize = ysize
        .grab = DUMMY_GRAB$
        .text = DUMMY_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    g_signal_connect_data(dialog, "delete-event", HIDE, 0, 0, 0)

    RETURN dialog

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION FILEDIALOG (STRING title$, STRING text$, int xsize, int ysize, int type)

    LOCAL dialog
    LOCAL terminator$

    IF type > 3 THEN type = 3
    IF type < 0 THEN type = 0

    ' Improved by James Fuller
    dialog = gtk_file_chooser_dialog_new(title$, 0, type, "gtk-cancel", GTK_RESPONSE_CANCEL, text$, GTK_RESPONSE_ACCEPT, terminator$)

    gtk_window_set_title(dialog, "HUG selector")
    gtk_window_set_default_size(dialog, xsize, ysize)
    gtk_window_set_position(dialog, GTK_WIN_POS_CENTER)
    gtk_widget_realize(dialog)

    WITH hug_widget_properties[hug_pointer]
        .widget = dialog
        .font = dialog
        .focus = 0
        .attach = 0
        .signal = 7
        .s_widget = dialog
        .xsize = xsize
        .ysize = ysize
        .grab = FILEDIALOG_GRAB$
        .text = FILEDIALOG_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    g_signal_connect_data(dialog, "delete-event", HIDE, 0, 0, 0)

    RETURN dialog

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION SPIN (int xsize, int ysize, FLOATING start, FLOATING end, FLOATING step)

    LOCAL but, lbl

    but = gtk_spin_button_new_with_range(start, end, step)

    gtk_widget_show(but)

    WITH hug_widget_properties[hug_pointer]
        .widget = but
        .font = but
        .focus = but
        .attach = but
        .signal = 6
        .s_widget = but
        .xsize = xsize
        .ysize = ysize
        .grab = DUMMY_GRAB$
        .text = DUMMY_TEXT
        .get = SPIN_GET
        .set = SPIN_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN but

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION IMAGE(STRING file$, int xsize, int ysize)

    LOCAL image, ebox

    image = gtk_image_new_from_file(file$)
    ebox = gtk_event_box_new()
    gtk_container_add(ebox, image)

    gtk_widget_queue_draw(image)
    gtk_widget_show_all(ebox)

    WITH hug_widget_properties[hug_pointer]
        .widget = ebox
        .font = image
        .focus = image
        .attach = 0
        .signal = 4
        .s_widget = ebox
        .xsize = xsize
        .ysize = ysize
        .grab = DUMMY_GRAB$
        .text = DUMMY_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN ebox

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION CANVAS (int xsize, int ysize)

    LOCAL image, ebox, gdkwin, pix, context, gdkcol

    REM Setup the image
    image = gtk_image_new()
    ebox = gtk_event_box_new()
    gtk_widget_set_events (ebox, GDK_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK)
    g_signal_connect_data(ebox, "button-press-event", hug_mouse_event, 20, 0, 0)
    g_signal_connect_data(ebox, "button-release-event", hug_mouse_event, 20, 0, 0)
    g_signal_connect_data(ebox, "motion-notify-event", hug_mouse_event, 0, 0, 0)
    g_signal_connect_data(ebox, "scroll-event", hug_mouse_event, 10, 0, G_CONNECT_AFTER)

    gtk_container_add(ebox, image)
    gdkwin = gdk_get_default_root_window()
    pix = gdk_pixmap_new(gdkwin, xsize, ysize, -1)
    context = gdk_gc_new(pix)
    gtk_image_set_from_pixmap(image, pix, 0)

    REM Determine colors
    gdkcol = MEMORY(HUG_DEFAULT_WIDGET_SIZE)
    gdk_color_parse("#FFFFFF", gdkcol)
    gdk_gc_set_rgb_bg_color(context, gdkcol)
    gdk_gc_set_rgb_fg_color(context, gdkcol)

    gdk_draw_rectangle(pix, context, 1, 0, 0, xsize, ysize)

    gtk_widget_queue_draw(image)
    gtk_widget_show_all(ebox)

    WITH hug_widget_properties[hug_pointer]
        .widget = ebox
        .font = 0
        .focus = 0
        .pix = pix
        .image = image
        .color = gdkcol
        .attach = 0
        .signal = 4
        .s_widget = ebox
        .xsize = xsize
        .ysize = ysize
        .grab = DUMMY_GRAB$
        .text = DUMMY_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    REM Define this as default drawing canvas
    DRAW(ebox)

    RETURN ebox

END FUNCTION

REM --------------------------------------------------------------------------------------------------

FUNCTION CLIPBOARD ()

    LOCAL clip, atom

    atom = gdk_atom_intern("CLIPBOARD", 0)
    clip = gtk_clipboard_get(atom)

    WITH hug_widget_properties[hug_pointer]
        .widget = clip
        .font = 0
        .focus = 0
        .attach = 0
        .signal = 0
        .s_widget = 0
        .xsize = 0
        .ysize = 0
        .grab = CLIPBOARD_GRAB$
        .text = CLIPBOARD_TEXT
        .get = DUMMY_GET
        .set = DUMMY_SET
    END WITH

    hug_pointer = hug_pointer + 1

    RETURN clip

END FUNCTION

REM --------------------------------------------------------------------------------------------------

SUB CALLBACK (NUMBER widget, void* addr)

    LOCAL i

    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            SELECT hug_widget_properties[i].signal
                CASE 1
                    g_signal_connect_data(hug_widget_properties[i].s_widget, "show", addr, 0, 0, 0)
                CASE 2
                    g_signal_connect_data(hug_widget_properties[i].s_widget, "clicked", addr, 0, 0, 0)
                CASE 3
                    g_signal_connect_data(hug_widget_properties[i].s_widget, "activate", addr, 0, 0, 0)
                CASE 4
                    g_signal_connect_data(hug_widget_properties[i].s_widget, "button-press-event", addr, 0, 0, 0)
                CASE 5
                    g_signal_connect_data(hug_widget_properties[i].s_widget, "changed", addr, 0, 0, 0)
                CASE 6
                    g_signal_connect_data(hug_widget_properties[i].s_widget, "value-changed", addr, 0, 0, 0)
                CASE 7
                    g_signal_connect_data(hug_widget_properties[i].s_widget, "response", addr, 0, 0, 0)
                DEFAULT
                    PRINT "WARNING: Cannot setup callback for widget!"
                    END
            END SELECT
            BREAK
        END IF
    NEXT

END SUB

REM --------------------------------------------------------------------------------------------------

FUNCTION MOUSE (NUMBER arg)

    SELECT arg
        CASE 0
            RETURN hug_gui_properties.mousex
        CASE 1
            RETURN hug_gui_properties.mousey
        CASE 2
            RETURN hug_gui_properties.button
        CASE 3
            RETURN hug_gui_properties.scroll
        DEFAULT
            RETURN 0
    END SELECT

END FUNCTION

REM --------------------------------------------------------------------------------------------------

SUB CIRCLE (STRING color$, int x, int y, int xsize, int ysize, NUMBER fill)

    LOCAL pix, context, i

    REM Get the associated canvas
    pix = hug_gui_properties.canvas

    REM Get the internal widgetID
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].pix EQ pix THEN
            BREAK
        END IF
    NEXT

    gdk_color_parse(color$, hug_widget_properties[i].color)

    context = gdk_gc_new(pix)
    gdk_gc_set_rgb_fg_color(context, hug_widget_properties[i].color)

    gdk_draw_arc(pix, context, fill, x, y, xsize, ysize, 0, 23040)

    REM Refresh image
    gtk_widget_queue_draw(hug_widget_properties[i].image)
    g_main_context_iteration(0, TRUE)

    g_object_unref(context)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB PIXEL (STRING color$, int x, int y)

    LOCAL pix, context, i

    REM Get the associated canvas
    pix = hug_gui_properties.canvas

    REM Get the internal widgetID
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].pix EQ pix THEN
            BREAK
        END IF
    NEXT

    gdk_color_parse(color$, hug_widget_properties[i].color)

    context = gdk_gc_new(pix)
    gdk_gc_set_rgb_fg_color(context, hug_widget_properties[i].color)

    gdk_draw_point(pix, context, x, y)

    REM Refresh image
    gtk_widget_queue_draw(hug_widget_properties[i].image)
    g_main_context_iteration(0, TRUE)

    g_object_unref(context)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB LINE (STRING color$, int xstart, int ystart, int xend, int yend)

    LOCAL pix, context, i

    REM Get the associated canvas
    pix = hug_gui_properties.canvas

    REM Get the internal widgetID
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].pix EQ pix THEN
            BREAK
        END IF
    NEXT

    gdk_color_parse(color$, hug_widget_properties[i].color)

    context = gdk_gc_new(pix)
    gdk_gc_set_rgb_fg_color(context, hug_widget_properties[i].color)

    gdk_draw_line(pix, context, xstart, ystart, xend, yend)

    REM Refresh image
    gtk_widget_queue_draw(hug_widget_properties[i].image)
    g_main_context_iteration(0, TRUE)

    g_object_unref(context)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB SQUARE (STRING color$, int x, int y, int xsize, int ysize, int fill)

    LOCAL pix, context, i

    REM Get the associated canvas
    pix = hug_gui_properties.canvas

    REM Get the internal widgetID
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].pix EQ pix THEN
            BREAK
        END IF
    NEXT

    gdk_color_parse(color$, hug_widget_properties[i].color)

    context = gdk_gc_new(pix)
    gdk_gc_set_rgb_fg_color(context, hug_widget_properties[i].color)

    gdk_draw_rectangle(pix, context, fill, x, y, xsize, ysize)

    REM Refresh image
    gtk_widget_queue_draw(hug_widget_properties[i].image)
    g_main_context_iteration(0, TRUE)

    g_object_unref(context)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB OUT (STRING text$, STRING fcolor$, STRING bcolor$, int x, int y)

    LOCAL image, pix, context, layout, gdkcol, i

    REM Get the associated canvas
    pix = hug_gui_properties.canvas

    REM Get the internal widgetID
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].pix EQ pix THEN
            BREAK
        END IF
    NEXT

    gdk_color_parse(fcolor$, hug_widget_properties[i].color)

    context = gdk_gc_new(pix)
    gdk_gc_set_rgb_fg_color(context, hug_widget_properties[i].color)

    image = hug_widget_properties[i].image

    layout = gtk_widget_create_pango_layout(image, text$)

    gdkcol = gdk_color_copy(hug_widget_properties[i].color)

    IF LEN(bcolor$) NE 0 THEN
        gdk_color_parse(bcolor$, gdkcol)
        gdk_draw_layout_with_colors(pix, context, x, y, layout, hug_widget_properties[i].color, gdkcol)
    ELSE
        gdk_draw_layout_with_colors(pix, context, x, y, layout, hug_widget_properties[i].color, 0)
    END IF

    REM Refresh image
    gtk_widget_queue_draw(image)
    g_main_context_iteration(0, TRUE)

    REM Free memory
    gdk_color_free(gdkcol)
    g_object_unref(layout)
    g_object_unref(context)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB PICTURE (STRING file$, int xpos, int ypos, int xsize, int ysize)

    LOCAL pix, context, buf
    LOCAL xs, ys TYPE int

    REM Get the associated canvas
    pix = hug_gui_properties.canvas
    context = gdk_gc_new(pix)

    buf = gdk_pixbuf_new_from_file(file$, 0)

    REM Get canvas properties
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].pix EQ pix THEN
            BREAK
        END IF
    NEXT

    gdk_draw_pixbuf(pix, context, buf, 0, 0, xpos, ypos, xsize, ysize, 0, 0, 0)

    gtk_widget_queue_draw(hug_widget_properties[i].image)
    g_main_context_iteration(0, TRUE)

    g_object_unref(context)
    g_object_unref(buf)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB ATTACH (NUMBER widget, NUMBER child, NUMBER xpos, NUMBER ypos)

    LOCAL i, layer, dim

    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            layer = hug_widget_properties[i].attach
            BREAK
        END IF
    NEXT

    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ child THEN
            BREAK
        END IF
    NEXT

    IF INSTR(hug_gui_properties.options$, "TABLE") THEN
        gtk_table_attach_defaults(layer, child, xpos, xpos+hug_widget_properties[i].xsize, ypos, ypos+hug_widget_properties[i].ysize)
    ELSE
        gtk_widget_set_size_request(child, hug_widget_properties[i].xsize, hug_widget_properties[i].ysize)
        gtk_fixed_put(layer, child, xpos, ypos)
    END IF

END SUB

REM --------------------------------------------------------------------------------------------------

SUB TIMEOUT(int seconds, void* addr)

    IF hug_gui_properties.timeout_id NE -1 THEN
        g_source_remove(hug_gui_properties.timeout_id)
    END IF

    hug_gui_properties.timeout_id = g_timeout_add(seconds, addr, 0)

    REM Return TRUE in callbacked function if it needs to be called again

END SUB

REM --------------------------------------------------------------------------------------------------

SUB FONT (NUMBER widget, STRING which$)

    LOCAL desc, i

    desc = pango_font_description_from_string(which$)

    REM Get widget and focus
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            gtk_widget_modify_font(hug_widget_properties[i].font, desc)
            BREAK
        END IF
    NEXT

    pango_font_description_free(desc)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB DISABLE (NUMBER widget)

    gtk_widget_set_sensitive(widget, 0)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB ENABLE (NUMBER widget)

    gtk_widget_set_sensitive(widget, 1)

END SUB

REM --------------------------------------------------------------------------------------------------

SUB FOCUS (NUMBER widget)

    LOCAL i

    REM Get widget and focus
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            g_object_set(hug_widget_properties[i].focus, "can-focus", 1, 0)
            gtk_widget_grab_focus(hug_widget_properties[i].focus)
            BREAK
        END IF
    NEXT

END SUB

REM --------------------------------------------------------------------------------------------------

SUB UNFOCUS (NUMBER widget)

    LOCAL i

    REM Get widget and focus
    FOR i = 0 TO hug_pointer
        IF hug_widget_properties[i].widget EQ widget THEN
            g_object_set(hug_widget_properties[i].focus, "can-focus", 0, 0)
            BREAK
        END IF
    NEXT

END SUB

REM --------------------------------------------------------------------------------------------------

FUNCTION KEY

    RETURN hug_gui_properties.last_key_pressed

END FUNCTION

REM --------------------------------------------------------------------------------------------------

SUB SYNC

    WHILE gtk_events_pending() DO
        gtk_main_iteration_do(FALSE)
    WEND

END SUB

REM --------------------------------------------------------------------------------------------------

SUB DISPLAY

    gtk_main

END SUB

REM Goto end of include file
GOTO hug_end

REM ---------------------------------------------------------- Error handling

LABEL hug_print_message
    PRINT "The GTK library '", hug_Gtk$, "' is not available on this platform!"
    END

LABEL hug_end
    TRAP SYSTEM