diff --git a/accessible.c b/accessible.c new file mode 100644 index 0000000..2dd99e0 --- /dev/null +++ b/accessible.c @@ -0,0 +1,865 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como accessible.c e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * + */ + + #include + + #define ENABLE_NLS + #define GETTEXT_PACKAGE PACKAGE_NAME + + #include + #include + #include + + #include + #include + #include "v3270.h" + #include "private.h" + #include "accessible.h" + +// References: +// +// http://git.gnome.org/browse/gtk+/tree/gtk/a11y/gtkwidgetaccessible.c +// http://git.gnome.org/browse/gtk+/tree/gtk/a11y/gtkentryaccessible.c +// + + +/*--[ Prototipes ]-----------------------------------------------------------------------------------*/ + +static void atk_component_interface_init (AtkComponentIface *iface); +static void atk_action_interface_init (AtkActionIface *iface); +static void v3270_accessible_class_init (v3270AccessibleClass *klass); +static void v3270_accessible_init (v3270Accessible *widget); + +static void atk_text_interface_init (AtkTextIface *iface); + +/*--[ Widget definition ]----------------------------------------------------------------------------*/ + +G_DEFINE_TYPE_WITH_CODE (v3270Accessible, v3270_accessible, GTK_TYPE_ACCESSIBLE, + G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init) + ) + +// G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init) + +/*--[ Implement ]------------------------------------------------------------------------------------*/ + +static const gchar * v3270_accessible_get_description (AtkObject *accessible) +{ + GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE (accessible)); + + if (widget == NULL) + return NULL; + + return _( "3270 screen" ); +} + +static void v3270_accessible_focus_event(AtkObject *obj, gboolean focus_in) +{ + AtkObject *focus_obj = g_object_get_data (G_OBJECT (obj), "gail-focus-object"); + + if(focus_obj == NULL) + focus_obj = obj; + + atk_object_notify_state_change(focus_obj, ATK_STATE_FOCUSED, focus_in); +} + +static AtkAttributeSet * v3270_accessible_get_attributes (AtkObject *obj) +{ + AtkAttributeSet * attributes; + AtkAttribute * toolkit = g_new(AtkAttribute, 1); + + toolkit->name = g_strdup("toolkit"); + toolkit->value = g_strdup("gtk"); + + attributes = g_slist_append (NULL, toolkit); + + return attributes; +} + +static void v3270_accessible_initialize (AtkObject *obj, gpointer data) +{ + obj->role = ATK_ROLE_TEXT; +} + +static void v3270_accessible_class_init(v3270AccessibleClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->get_description = v3270_accessible_get_description; + class->focus_event = v3270_accessible_focus_event; + class->get_attributes = v3270_accessible_get_attributes; + class->initialize = v3270_accessible_initialize; + +/* + klass->notify_gtk = gtk_widget_accessible_notify_gtk; + + class->get_parent = gtk_widget_accessible_get_parent; + class->ref_relation_set = gtk_widget_accessible_ref_relation_set; + class->ref_state_set = gtk_widget_accessible_ref_state_set; + class->get_index_in_parent = gtk_widget_accessible_get_index_in_parent; +*/ +} + +static gint v3270_accessible_get_n_actions(AtkAction *action) +{ + return 1; +} + +static const gchar* v3270_accessible_action_get_name (AtkAction *action, gint i) +{ + if (i != 0) + return NULL; + + return "activate"; +} + +static gboolean v3270_accessible_do_action(AtkAction *action, gint i) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action)); + + if(widget == NULL) + return FALSE; + + if(!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget)) + return FALSE; + + if (i != 0) + return FALSE; + + gtk_widget_activate(widget); + + return TRUE; +} + +static void atk_action_interface_init(AtkActionIface *iface) +{ + iface->get_name = v3270_accessible_action_get_name; + iface->get_n_actions = v3270_accessible_get_n_actions; + iface->do_action = v3270_accessible_do_action; + +/* + iface->get_keybinding = gtk_entry_accessible_get_keybinding; +*/ +} + +static gunichar v3270_accessible_get_character_at_offset(AtkText *atk_text, gint offset) +{ + GtkWidget * widget = gtk_accessible_get_widget(GTK_ACCESSIBLE (atk_text)); + + if(widget == NULL) + { + H3270 * host = v3270_get_session(widget); + gchar * text = lib3270_get_text(host,offset,1); + + if(text) + { + gunichar unichar; + gsize bytes_written; + GError * error = NULL; + gchar * utfstring = g_convert_with_fallback( text, + -1, + "UTF-8", + lib3270_get_charset(host), + " ", + NULL, + &bytes_written, + &error ); + + if(error) + { + g_warning("%s failed: %s",__FUNCTION__,error->message); + g_error_free(error); + } + unichar = *utfstring; + + g_free(utfstring); + + return unichar; + } + + } + + return '\0'; +} + +static gint v3270_accessible_get_caret_offset(AtkText *text) +{ + GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(text)); + + if (widget == NULL) + return 0; + + return lib3270_get_cursor_address(GTK_V3270(widget)->host); +} + +static gint v3270_accessible_get_character_count(AtkText *text) +{ + int rows,cols; + GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(text)); + + if(!widget) + return 0; + + return lib3270_get_length(GTK_V3270(widget)->host); +} + +static gint v3270_accessible_get_offset_at_point(AtkText *atk_text, gint x, gint y, AtkCoordType coords) +{ + gint x_window, + y_window, + x_widget, + y_widget; + GdkWindow * window; + GtkWidget * widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(atk_text)); + + if(!widget) + return -1; + + window = gtk_widget_get_window(widget); + gdk_window_get_origin(window, &x_widget, &y_widget); + + switch(coords) + { + case ATK_XY_SCREEN: + x -= x_widget; + y -= y_widget; + break; + + case ATK_XY_WINDOW: + window = gdk_window_get_toplevel(window); + gdk_window_get_origin (window, &x_window, &y_window); + x = x - x_widget + x_window; + y = y - y_widget + y_window; + break; + + default: + return -1; + + } + + return v3270_get_offset_at_point(GTK_V3270(widget),x,y); +} + +/* +text : an AtkText +offset : The offset of the text character for which bounding information is required. +x : Pointer for the x cordinate of the bounding box +y : Pointer for the y cordinate of the bounding box +width : Pointer for the width of the bounding box +height : Pointer for the height of the bounding box +coords : specify whether coordinates are relative to the screen or widget window +*/ + +static void v3270_accessible_get_character_extents( AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords ) +{ + v3270 * widget = (v3270 *) gtk_accessible_get_widget(GTK_ACCESSIBLE (text)); + int rows, cols; + GdkWindow * window; + + if (widget == NULL) + return; + + lib3270_get_screen_size(widget->host,&rows,&cols); + + // Get screen position + window = gtk_widget_get_window(GTK_WIDGET(widget)); + gdk_window_get_origin(window, x, y); + + // Get screen position + *x += widget->metrics.left + ((offset%cols) * widget->metrics.width); + *y += widget->metrics.top + ((offset/cols) * widget->metrics.spacing); + *width = widget->metrics.width; + *height = widget->metrics.spacing; + + if(coords == ATK_XY_WINDOW) + { + // Correct position based on toplevel + gint x_window, y_window; + + window = gdk_window_get_toplevel(window); + gdk_window_get_origin(window, &x_window, &y_window); + *x -= x_window; + *y -= y_window; + } + +// trace("%s: offset=%d x=%d y=%d %s",__FUNCTION__,offset,*x,*y,coords == ATK_XY_WINDOW ? "ATK_XY_WINDOW" : ""); + +} + +static gchar * v3270_accessible_get_text_at_offset(AtkText *atk_text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) +{ + GtkWidget * widget = gtk_accessible_get_widget(GTK_ACCESSIBLE (atk_text)); + H3270 * host; + char * text; + int rows,cols,pos; + + if(!widget) + return NULL; + + host = GTK_V3270(widget)->host; + lib3270_get_screen_size(host,&rows,&cols); + + switch(boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: // Boundary is the boundary between characters + // (including non-printing characters) + + text = lib3270_get_text(host,offset,1); + break; + + case ATK_TEXT_BOUNDARY_WORD_START: // Boundary is the start (i.e. first character) of a word. + return g_strdup("ATK_TEXT_BOUNDARY_WORD_START"); + break; + + case ATK_TEXT_BOUNDARY_WORD_END: // Boundary is the end (i.e. last character) of a word. + return g_strdup("ATK_TEXT_BOUNDARY_WORD_END"); + break; + + case ATK_TEXT_BOUNDARY_SENTENCE_START: // Boundary is the first character in a sentence. + return g_strdup("ATK_TEXT_BOUNDARY_SENTENCE_START"); + break; + + case ATK_TEXT_BOUNDARY_SENTENCE_END: // Boundary is the last (terminal) character in + // a sentence; in languages which use "sentence stop" punctuation such as English, the boundary is thus the '.', '?', or + // similar terminal punctuation character. + return g_strdup("ATK_TEXT_BOUNDARY_SENTENCE_END"); + break; + + + case ATK_TEXT_BOUNDARY_LINE_START: // Boundary is the initial character of the content or a character immediately following a newline, + // linefeed, or return character. + pos = (offset/cols)*cols; + if(pos == offset) + offset++; + text = lib3270_get_text(host,pos,(offset-pos)); + break; + + + case ATK_TEXT_BOUNDARY_LINE_END: // Boundary is the linefeed, or return character. + return g_strdup("ATK_TEXT_BOUNDARY_LINE_END"); + break; + + } + + if(text) + { + gsize bytes_written; + GError * error = NULL; + gchar * utfchar = g_convert_with_fallback( text, + -1, + "UTF-8", + lib3270_get_charset(host), + " ", + NULL, + &bytes_written, + &error ); + + if(error) + { + g_warning("%s failed: %s",__FUNCTION__,error->message); + g_error_free(error); + } + + free(text); + return utfchar; + } + + return NULL; +} + +static gchar * v3270_accessible_get_text(AtkText *atk_text, gint start_pos, gint end_pos) +{ + GtkWidget * widget = gtk_accessible_get_widget(GTK_ACCESSIBLE (atk_text)); + char * text; + H3270 * host; + gchar * utftext = NULL; + + if(widget == NULL) + return NULL; + + host = v3270_get_session(widget); + if(!host) + return NULL; + + if(!lib3270_connected(host)) + return g_strdup( "" ); + + text = lib3270_get_text(host,start_pos,end_pos < start_pos ? -1 : (end_pos - start_pos)); + + if(text) + { + gsize bytes_written; + GError * error = NULL; + + utftext = g_convert_with_fallback(text,-1,"UTF-8",lib3270_get_charset(host)," ",NULL,&bytes_written, &error); + + if(error) + { + g_warning("%s failed: %s",__FUNCTION__,error->message); + g_error_free(error); + } + + free(text); + +// trace("%s:\n%s\n",__FUNCTION__,utftext); + + } + + return utftext; +} + +static gboolean v3270_set_caret_offset(AtkText *text, gint offset) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return FALSE; + + trace("%s - offset=%d",__FUNCTION__,offset); + + lib3270_set_cursor_address(GTK_V3270(widget)->host,offset); + + return TRUE; +} + +static gboolean v3270_accessible_remove_selection(AtkText *text, gint selection_num) +{ + GtkWidget * widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + + if (widget == NULL || selection_num != 0) + return FALSE; + + v3270_unselect(widget); + + return TRUE; +} + +static gint v3270_accessible_get_n_selections (AtkText *text) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + + if(!widget) + return 0; + +// trace("%s: n_selections=%d",__FUNCTION__,v3270_get_selection_bounds(widget, NULL, NULL) ? 1 : 0); + + return v3270_get_selection_bounds(widget, NULL, NULL) ? 1 : 0; +} + +static gchar * v3270_accessible_get_selection(AtkText *atk_text, gint selection_num, gint *start_pos, gint *end_pos) +{ + GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE (atk_text)); + +// trace("%s: selection_num=%d",__FUNCTION__,selection_num); + if (widget == NULL ||selection_num != 0) + return NULL; + + if(v3270_get_selection_bounds(widget, start_pos, end_pos)) + { +// trace("%s: TRUE",__FUNCTION__); + return v3270_get_region(widget, *start_pos, *end_pos, FALSE); + } + +// trace("%s: FALSE",__FUNCTION__); + return NULL; +} + +static gboolean v3270_accessible_add_selection(AtkText *text, gint start_pos, gint end_pos) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + + if (widget == NULL) + return FALSE; + + if(!v3270_get_selection_bounds(widget,NULL,NULL)) + { + v3270_select_region(widget, start_pos, end_pos); + return TRUE; + } + + return FALSE; +} + +static gboolean v3270_accessible_set_selection(AtkText *text, gint selection_num, gint start_pos, gint end_pos) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + gint start, end; + + if(widget == NULL || selection_num != 0) + return FALSE; + + if(!v3270_get_selection_bounds(widget,NULL,NULL)) + { + v3270_select_region(widget, start_pos, end_pos); + return TRUE; + } + + return FALSE; +} + +static AtkAttributeSet *add_attribute(AtkAttributeSet * attributes, AtkTextAttribute attr, const gchar *value) +{ + AtkAttribute *at = g_new(AtkAttribute, 1); + + at->name = g_strdup (atk_text_attribute_get_name (attr)); + at->value = g_strdup (value); + + return g_slist_prepend (attributes, at); +} + +static AtkAttributeSet * v3270_accessible_get_default_attributes (AtkText *text) +{ + GtkWidget * widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + AtkAttributeSet * attributes = NULL; + + if(!widget) + return NULL; + + trace("%s is incomplete ***********************",__FUNCTION__); + + // http://developer.gnome.org/atk/stable/AtkText.html#AtkTextAttribute + + // The direction of the text, if set. Values are "none", "ltr" or "rtl" + attributes = add_attribute(attributes, ATK_TEXT_ATTR_DIRECTION,atk_text_attribute_get_value(ATK_TEXT_ATTR_DIRECTION,gtk_widget_get_direction(widget))); + + return attributes; +} + +/** + * Creates an AtkAttributeSet which consists of the attributes explicitly + * set at the position offset in the text. start_offset and end_offset are set + * to the start and end of the range around offset where the attributes are invariant. + * Note that end_offset is the offset of the first character after the range. + * See the enum AtkTextAttribute for types of text attributes that can be returned. + * Note that other attributes may also be returned. + * + * @param text an AtkText + * @param offset the offset at which to get the attributes, -1 means the offset of the character to be inserted at the caret location. + * @param start_offset the address to put the start offset of the range + * @param end_offset the address to put the end offset of the range + * + * @return an AtkAttributeSet which contains the attributes explicitly set at offset. + */ +static AtkAttributeSet * v3270_accessible_get_run_attributes(AtkText *text, gint offset, gint * start_offset, gint * end_offset) +{ + GtkWidget * widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + H3270 * host; + AtkAttributeSet * attributes = NULL; + int start, end; + + trace("%s is incomplete ***********************",__FUNCTION__); + + g_return_val_if_fail((widget && GTK_IS_V3270(widget)),NULL); + + host = v3270_get_session(widget); + + if(offset == -1) + offset = lib3270_get_cursor_address(host); + + if(!lib3270_get_field_bounds(host,offset,&start,&end)) + { + *start_offset = start; + *end_offset = end; + } + + // http://developer.gnome.org/atk/stable/AtkText.html#AtkTextAttribute + + // The direction of the text, if set. Values are "none", "ltr" or "rtl" + attributes = add_attribute(attributes, ATK_TEXT_ATTR_DIRECTION,atk_text_attribute_get_value(ATK_TEXT_ATTR_DIRECTION,gtk_widget_get_direction(widget))); + + // ATK_TEXT_ATTR_LEFT_MARGIN + // The pixel width of the left margin + + // ATK_TEXT_ATTR_RIGHT_MARGIN + // The pixel width of the right margin + + // ATK_TEXT_ATTR_INVISIBLE + // Either "true" or "false" indicating whether text is visible or not + + // Either "true" or "false" indicating whether text is editable or not + attributes = add_attribute( attributes, + ATK_TEXT_ATTR_EDITABLE, + lib3270_is_protected(host,offset) ? "false" : "true" ); + + // The background color. The value is an RGB value of the format "u,u,u" + // ATK_TEXT_ATTR_BG_COLOR + + // The foreground color. The value is an RGB value of the format "u,u,u" + // ATK_TEXT_ATTR_FG_COLOR + + // The font family name + attributes = add_attribute( attributes, + ATK_TEXT_ATTR_FAMILY_NAME, + GTK_V3270(widget)->font_family ); + + return attributes; +} + +/* +static gchar * v3270_accessible_get_text_after_offset(AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) +{ + // http://developer.gnome.org/atk/stable/AtkText.html#atk-text-get-text-after-offset + trace("WARNING: %s is incomplete",__FUNCTION__); + +} +*/ + +/* +static gchar * v3270_accessible_get_text_before_offset(AtkText *text,gint offset,AtkTextBoundary boundary_type,gint *start_offset,gint *end_offset) +{ + // http://developer.gnome.org/atk/stable/AtkText.html#atk-text-get-text-before-offset + trace("WARNING: %s is incomplete",__FUNCTION__); + +} +*/ + +static void atk_text_interface_init(AtkTextIface *iface) +{ + iface->get_text = v3270_accessible_get_text; + iface->get_character_at_offset = v3270_accessible_get_character_at_offset; + + iface->get_text_at_offset = v3270_accessible_get_text_at_offset; + + iface->get_character_count = v3270_accessible_get_character_count; + iface->get_caret_offset = v3270_accessible_get_caret_offset; + iface->set_caret_offset = v3270_set_caret_offset; + + iface->get_character_extents = v3270_accessible_get_character_extents; + iface->get_offset_at_point = v3270_accessible_get_offset_at_point; + + iface->get_n_selections = v3270_accessible_get_n_selections; + iface->add_selection = v3270_accessible_add_selection; + iface->remove_selection = v3270_accessible_remove_selection; + iface->set_selection = v3270_accessible_set_selection; + iface->get_selection = v3270_accessible_get_selection; + iface->get_run_attributes = v3270_accessible_get_run_attributes; + iface->get_default_attributes = v3270_accessible_get_default_attributes; +// iface->get_text_after_offset = v3270_accessible_get_text_after_offset; +// iface->get_text_before_offset = v3270_accessible_get_text_before_offset; + +} + +static void v3270_accessible_init(v3270Accessible *widget) +{ + AtkObject *obj = ATK_OBJECT(widget); + obj->role = ATK_ROLE_TEXT; +} + +void v3270_accessible_get_extents(AtkComponent *component, gint *x, gint *y,gint *width,gint *height, AtkCoordType coord_type) +{ + GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE (component)); + GdkWindow *window; + gint x_window, y_window; + GtkAllocation allocation; + + if (widget == NULL) + return; + + gtk_widget_get_allocation (widget, &allocation); + *width = allocation.width; + *height = allocation.height; + + if(gtk_widget_get_parent(widget)) + { + *x = allocation.x; + *y = allocation.y; + window = gtk_widget_get_parent_window (widget); + } + else + { + *x = 0; + *y = 0; + window = gtk_widget_get_window (widget); + } + + gdk_window_get_origin(window, &x_window, &y_window); + *x += x_window; + *y += y_window; + + if (coord_type == ATK_XY_WINDOW) + { + gint x_toplevel, y_toplevel; + + window = gdk_window_get_toplevel (gtk_widget_get_window (widget)); + gdk_window_get_origin (window, &x_toplevel, &y_toplevel); + + *x -= x_toplevel; + *y -= y_toplevel; + } +} + +static void v3270_accessible_get_size(AtkComponent *component,gint *width, gint *height) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component)); + + if (widget == NULL) + return; + + *width = gtk_widget_get_allocated_width (widget); + *height = gtk_widget_get_allocated_height (widget); +} + +static gboolean v3270_accessible_grab_focus(AtkComponent *component) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component)); + GtkWidget *toplevel; + + if (!widget) + return FALSE; + + gtk_widget_grab_focus (widget); + + toplevel = gtk_widget_get_toplevel (widget); + if (gtk_widget_is_toplevel (toplevel)) + { + #ifdef GDK_WINDOWING_X11 + gtk_window_present_with_time (GTK_WINDOW (toplevel),gdk_x11_get_server_time(gtk_widget_get_window(widget))); + #else + gtk_window_present (GTK_WINDOW (toplevel)); + #endif + } + + return TRUE; +} + +static AtkLayer v3270_accessible_get_layer (AtkComponent *component) +{ + return ATK_LAYER_WIDGET; +} + +static gboolean v3270_accessible_set_size(AtkComponent *component, gint width, gint height) +{ + GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE (component)); + + if (widget == NULL) + return FALSE; + + gtk_widget_set_size_request(widget, width, height); + return TRUE; +} + +static void atk_component_interface_init(AtkComponentIface *iface) +{ + iface->get_extents = v3270_accessible_get_extents; + iface->get_size = v3270_accessible_get_size; + iface->grab_focus = v3270_accessible_grab_focus; + iface->get_layer = v3270_accessible_get_layer; + iface->set_size = v3270_accessible_set_size; +} + +void v3270_acessible_set_state(GtkAccessible *obj, LIB3270_MESSAGE id) +{ + #ifdef DEBUG + #define STATE_MESSAGE(x,c) { #x, x, c } + #else + #define STATE_MESSAGE(x,c) { x, c } + #endif + + static const struct _state + { + #ifdef DEBUG + const gchar * dbg; + #endif + AtkStateType atkstate; + V3270_STATE flag; + } table[] = + { + STATE_MESSAGE(ATK_STATE_BUSY, V3270_STATE_BUSY ), + STATE_MESSAGE(ATK_STATE_EDITABLE, V3270_STATE_EDITABLE ), + STATE_MESSAGE(ATK_STATE_ENABLED, V3270_STATE_ENABLED ), + STATE_MESSAGE(ATK_STATE_INVALID_ENTRY, V3270_STATE_INVALID_ENTRY ), + }; + + V3270_STATE state = GTK_V3270_ACCESSIBLE(obj)->state; + V3270_STATE bits; + int f; + + switch(id) + { + case LIB3270_MESSAGE_NONE: + state = V3270_STATE_EDITABLE|V3270_STATE_ENABLED; + break; + + case LIB3270_MESSAGE_SYSWAIT: + case LIB3270_MESSAGE_TWAIT: + case LIB3270_MESSAGE_RESOLVING: + case LIB3270_MESSAGE_CONNECTING: + state = V3270_STATE_BUSY; + break; + + case LIB3270_MESSAGE_CONNECTED: + case LIB3270_MESSAGE_AWAITING_FIRST: + state = V3270_STATE_ENABLED; + break; + + case LIB3270_MESSAGE_DISCONNECTED: + state = 0; + break; + + case LIB3270_MESSAGE_MINUS: + case LIB3270_MESSAGE_INHIBIT: + case LIB3270_MESSAGE_X: + break; + + case LIB3270_MESSAGE_PROTECTED: + case LIB3270_MESSAGE_NUMERIC: + case LIB3270_MESSAGE_OVERFLOW: + case LIB3270_MESSAGE_KYBDLOCK: + state = V3270_STATE_INVALID_ENTRY|V3270_STATE_EDITABLE|V3270_STATE_ENABLED; + break; + + } + + if(state == GTK_V3270_ACCESSIBLE(obj)->state) + return; + + bits = GTK_V3270_ACCESSIBLE(obj)->state ^ state; + + trace("State change from %04x to %04x (bits=%04x)", + GTK_V3270_ACCESSIBLE(obj)->state, + state, bits ); + + for(f=0;fstate = state; +} + diff --git a/accessible.h b/accessible.h new file mode 100644 index 0000000..380ce73 --- /dev/null +++ b/accessible.h @@ -0,0 +1,73 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como accessible.h e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * + */ + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_V3270_ACCESSIBLE (v3270_accessible_get_type ()) +#define GTK_V3270_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_V3270_ACCESSIBLE, v3270Accessible)) +#define GTK_V3270_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_V3270_ACCESSIBLE, v3270AccessibleClass)) +#define GTK_IS_V3270_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_V3270_ACCESSIBLE)) +#define GTK_IS_V3270_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_V3270_ACCESSIBLE)) +#define GTK_V3270_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_V3270_ACCESSIBLE, v3270AccessibleClass)) + +typedef struct _v3270Accessible v3270Accessible; +typedef struct _v3270AccessibleClass v3270AccessibleClass; + +typedef enum _v3270_state +{ + V3270_STATE_NONE = 0x0000, + V3270_STATE_EDITABLE = 0x0001, + V3270_STATE_BUSY = 0x0002, + V3270_STATE_ENABLED = 0x0004, + V3270_STATE_INVALID_ENTRY = 0x0008, + +} V3270_STATE; + +struct _v3270Accessible +{ + GtkAccessible parent; + V3270_STATE state; + +// AtkLayer layer; +}; + +struct _v3270AccessibleClass +{ + GtkAccessibleClass parent_class; + + +}; + +GType v3270_accessible_get_type(void); + +void v3270_acessible_set_state(GtkAccessible *obj, LIB3270_MESSAGE id); + +G_END_DECLS diff --git a/draw.c b/draw.c new file mode 100644 index 0000000..c8a309c --- /dev/null +++ b/draw.c @@ -0,0 +1,397 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como draw.c e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * + */ + + #include + #include + #include + #include + #include "v3270.h" + #include "private.h" + +/*--[ Implement ]------------------------------------------------------------------------------------*/ + +gboolean v3270_draw(GtkWidget * widget, cairo_t * cr) +{ + v3270 * terminal = GTK_V3270(widget); + + cairo_set_source_surface(cr,terminal->surface,0,0); + cairo_paint(cr); + + if(lib3270_get_toggle(terminal->host,LIB3270_TOGGLE_CROSSHAIR) && (terminal->cursor.show&2)) + { + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + + gdk_cairo_set_source_color(cr,terminal->color+V3270_COLOR_CROSS_HAIR); + + cairo_rectangle(cr, 0,terminal->cursor.rect.y+terminal->metrics.height,allocation.width,1); + cairo_fill(cr); + + cairo_rectangle(cr, terminal->cursor.rect.x,0,1,terminal->oia_rect->y-3); + cairo_fill(cr); + } + + if(terminal->cursor.show == 3) + { + cairo_set_source_surface(cr,terminal->cursor.surface,terminal->cursor.rect.x,terminal->cursor.rect.y); + + if(lib3270_get_toggle(terminal->host,LIB3270_TOGGLE_INSERT)) + { + cairo_rectangle(cr, terminal->cursor.rect.x, + terminal->cursor.rect.y, + terminal->cursor.rect.width, + terminal->cursor.rect.height ); + } + else + { + cairo_rectangle(cr, terminal->cursor.rect.x, + terminal->cursor.rect.y+terminal->metrics.height, + terminal->cursor.rect.width, + terminal->metrics.descent ); + } + + cairo_fill(cr); + } + + return FALSE; +} + +#if( !GTK_CHECK_VERSION(3,0,0)) +gboolean v3270_expose(GtkWidget *widget, GdkEventExpose *event) +{ + cairo_t *cr = gdk_cairo_create(widget->window); + v3270_draw(widget,cr); + cairo_destroy(cr); + return FALSE; +} +#endif // GTk3 + + +static void get_element_colors(unsigned short attr, GdkColor **fg, GdkColor **bg, GdkColor *color) +{ + if(attr & LIB3270_ATTR_SELECTED) + { + *fg = color+V3270_COLOR_SELECTED_FG; + *bg = color+V3270_COLOR_SELECTED_BG; + } + else + { + *bg = color+((attr & 0x00F0) >> 4); + + if(attr & LIB3270_ATTR_FIELD) + *fg = color+(attr & 0x0003)+V3270_COLOR_FIELD; + else + *fg = color+(attr & 0x000F); + } +} + +void v3270_draw_element(cairo_t *cr, unsigned char chr, unsigned short attr, H3270 *session, guint height, GdkRectangle *rect, GdkColor *color) +{ + GdkColor *fg; + GdkColor *bg; + + get_element_colors(attr,&fg,&bg,color); + v3270_draw_char(cr,chr,attr,session,height,rect,fg,bg); +} + +void v3270_draw_char(cairo_t *cr, unsigned char chr, unsigned short attr, H3270 *session, guint height, GdkRectangle *rect, GdkColor *fg, GdkColor *bg) +{ + // Clear element area + gdk_cairo_set_source_color(cr,bg); + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + + // Set foreground color + gdk_cairo_set_source_color(cr,fg); + + // Draw char + if(attr & LIB3270_ATTR_CG) + { + switch(chr) + { + case 0xd3: // CG 0xab, plus + cairo_move_to(cr,rect->x+(rect->width/2),rect->y); + cairo_rel_line_to(cr,0,rect->height); + cairo_move_to(cr,rect->x,rect->y+(rect->height/2)); + cairo_rel_line_to(cr,rect->width,0); + break; + + case 0xa2: // CG 0x92, horizontal line + cairo_move_to(cr,rect->x,rect->y+(rect->height/2)); + cairo_rel_line_to(cr,rect->width,0); + break; + + case 0x85: // CG 0x184, vertical line + cairo_move_to(cr,rect->x+(rect->width/2),rect->y); + cairo_rel_line_to(cr,0,rect->height); + break; + + case 0xd4: // CG 0xac, LR corner + cairo_move_to(cr,rect->x, rect->y+(rect->height/2)); + cairo_rel_line_to(cr,rect->width/2,0); + cairo_rel_line_to(cr,0,-(rect->height/2)); + break; + + case 0xd5: // CG 0xad, UR corner + cairo_move_to(cr,rect->x, rect->y+(rect->height/2)); + cairo_rel_line_to(cr,rect->width/2,0); + cairo_rel_line_to(cr,0,rect->height/2); + break; + + case 0xc5: // CG 0xa4, UL corner + cairo_move_to(cr,rect->x+rect->width,rect->y+(rect->height/2)); + cairo_rel_line_to(cr,-(rect->width/2),0); + cairo_rel_line_to(cr,0,(rect->height/2)); + break; + + case 0xc4: // CG 0xa3, LL corner + cairo_move_to(cr,rect->x+rect->width,rect->y+(rect->height/2)); + cairo_rel_line_to(cr,-(rect->width/2),0); + cairo_rel_line_to(cr,0,-(rect->height/2)); + break; + + case 0xc6: // CG 0xa5, left tee + cairo_move_to(cr,rect->x+(rect->width/2),rect->y+(rect->height/2)); + cairo_rel_line_to(cr,rect->width/2,0); + cairo_move_to(cr,rect->x+(rect->width/2),rect->y); + cairo_rel_line_to(cr,0,rect->height); + break; + + case 0xd6: // CG 0xae, right tee + cairo_move_to(cr,rect->x+(rect->width/2),rect->y+(rect->height/2)); + cairo_rel_line_to(cr,-(rect->width/2),0); + cairo_move_to(cr,rect->x+(rect->width/2),rect->y); + cairo_rel_line_to(cr,0,rect->height); + break; + + case 0xc7: // CG 0xa6, bottom tee + cairo_move_to(cr,rect->x+(rect->width/2),rect->y+(rect->height/2)); + cairo_rel_line_to(cr,0,-(rect->height/2)); + cairo_move_to(cr,rect->x,rect->y+(rect->height/2)); + cairo_rel_line_to(cr,rect->width,0); + break; + + case 0xd7: // CG 0xaf, top tee + cairo_move_to(cr,rect->x+(rect->width/2),rect->y+(rect->height/2)); + cairo_rel_line_to(cr,0,rect->height/2); + cairo_move_to(cr,rect->x,rect->y+(rect->height/2)); + cairo_rel_line_to(cr,rect->width,0); + break; + + case 0x8c: // CG 0xf7, less or equal "≤" + cairo_move_to(cr,rect->x,rect->y+height); + cairo_show_text(cr, "≤"); + break; + + case 0xae: // CG 0xd9, greater or equal "≥" + cairo_move_to(cr,rect->x,rect->y+height); + cairo_show_text(cr, "≥"); + break; + + case 0xbe: // CG 0x3e, not equal "≠" + cairo_move_to(cr,rect->x,rect->y+height); + cairo_show_text(cr, "≠"); + break; + + case 0xad: // "[" + cairo_move_to(cr,rect->x,rect->y+height); + cairo_show_text(cr, "["); + break; + + case 0xbd: // "]" + cairo_move_to(cr,rect->x,rect->y+height); + cairo_show_text(cr, "]"); + break; + + default: + cairo_rectangle(cr, rect->x+1, rect->y+1, rect->width-2, rect->height-2); + } + } + else if(chr) + { + gchar *utf = g_convert((char *) &chr, 1, "UTF-8", lib3270_get_charset(session), NULL, NULL, NULL); + + if(utf) + { + cairo_move_to(cr,rect->x,rect->y+height); + cairo_show_text(cr, utf); + g_free(utf); + } + } + cairo_stroke(cr); +} + +void v3270_reload(GtkWidget *widget) +{ + v3270 * terminal = GTK_V3270(widget); + +#if GTK_CHECK_VERSION(3,0,0) + gint width = gtk_widget_get_allocated_width(widget); + gint height = gtk_widget_get_allocated_height(widget); +#else + gint width = terminal->width; + gint height = terminal->height; +#endif + + GdkRectangle rect; + int addr, cursor, r, rows, cols; + + cairo_t * cr; + + if(!gtk_widget_get_realized(widget)) + return; + + // Create new terminal image + if(terminal->surface) + cairo_surface_destroy(terminal->surface); + + terminal->surface = (cairo_surface_t *) gdk_window_create_similar_surface(gtk_widget_get_window(widget),CAIRO_CONTENT_COLOR,width,height); + + // Update the created image + cr = cairo_create(terminal->surface); + v3270_update_font_metrics(terminal, cr, width, height); + + gdk_cairo_set_source_color(cr,terminal->color+V3270_COLOR_BACKGROUND); + cairo_rectangle(cr, 0, 0, width, height); + cairo_fill(cr); + cairo_stroke(cr); + + // Draw terminal contents + lib3270_get_screen_size(terminal->host,&rows,&cols); + + memset(&rect,0,sizeof(rect)); + rect.y = terminal->metrics.top; + rect.width = terminal->metrics.width; + rect.height = terminal->metrics.spacing; + addr = 0; + cursor = lib3270_get_cursor_address(terminal->host); + + for(r = 0; r < rows; r++) + { + int c; + + rect.x = terminal->metrics.left; + + for(c=0;c < cols;c++) + { + unsigned char chr = 0; + unsigned short attr; + + lib3270_get_contents(terminal->host,addr,addr,&chr,&attr); + + if(addr == cursor) + v3270_update_cursor_rect(terminal,&rect,chr,attr); + + v3270_draw_element(cr,chr,attr,terminal->host,terminal->metrics.height,&rect,terminal->color); + + addr++; + rect.x += rect.width; + } + + rect.y += terminal->metrics.spacing; + + } + + v3270_draw_oia(cr, terminal->host, rect.y, cols, &terminal->metrics, terminal->color,terminal->oia_rect); + + cairo_destroy(cr); + +} + +void v3270_update_char(H3270 *session, int addr, unsigned char chr, unsigned short attr, unsigned char cursor) +{ + v3270 * terminal = GTK_V3270(session->widget); + cairo_t * cr; + GdkRectangle rect; + int rows,cols; + + if(!gtk_widget_get_realized(GTK_WIDGET(terminal))) + return; + + if(!terminal->surface) + { + v3270_reload(GTK_WIDGET(terminal)); + gtk_widget_queue_draw(GTK_WIDGET(terminal)); + return; + } + + lib3270_get_screen_size(terminal->host,&rows,&cols); + + memset(&rect,0,sizeof(rect)); + rect.x = terminal->metrics.left + ((addr % cols) * terminal->metrics.width); + rect.y = terminal->metrics.top + ((addr / cols) * terminal->metrics.spacing); + rect.width = terminal->metrics.width; + rect.height = terminal->metrics.spacing; + +// trace("%s: c=%c attr=%04x addr=%d pos=%d,%d x=%d y=%d w=%d h=%d",__FUNCTION__,chr,(int) attr,addr,(addr / cols),(addr % cols),rect.x,rect.y,rect.width,rect.height); + + cr = cairo_create(terminal->surface); + cairo_set_scaled_font(cr,terminal->font_scaled); + v3270_draw_element(cr, chr, attr, terminal->host, terminal->metrics.height, &rect,terminal->color); + cairo_destroy(cr); + + if(cursor) + v3270_update_cursor_rect(terminal,&rect,chr,attr); + + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),rect.x,rect.y,rect.width,rect.height); + +} + +void v3270_update_cursor_surface(v3270 *widget,unsigned char chr,unsigned short attr) +{ + if(widget->cursor.surface) + { + GdkRectangle rect = widget->cursor.rect; + cairo_t * cr = cairo_create(widget->cursor.surface); + GdkColor * fg; + GdkColor * bg; + + get_element_colors(attr,&fg,&bg,widget->color); + + cairo_set_scaled_font(cr,widget->font_scaled); + + rect.x = 0; + rect.y = 0; + v3270_draw_char(cr,chr,attr,widget->host,widget->metrics.height,&rect,bg,fg); + + cairo_destroy(cr); + } + + +} + +void v3270_update_cursor_rect(v3270 *widget, GdkRectangle *rect, unsigned char chr, unsigned short attr) +{ + widget->cursor.chr = chr; + widget->cursor.rect = *rect; + widget->cursor.attr = attr; + widget->cursor.rect.height = widget->metrics.height + widget->metrics.descent; + v3270_update_cursor_surface(widget,chr,attr); +} + diff --git a/genmarshal b/genmarshal new file mode 100644 index 0000000..d615b87 --- /dev/null +++ b/genmarshal @@ -0,0 +1,11 @@ +VOID:VOID +VOID:VOID,ENUM,BOOL,POINTER +VOID:VOID,ENUM +VOID:VOID,POINTER +VOID:POINTER +VOID:VOID,POINTER,POINTER +VOID:VOID,UINT,POINTER +BOOL:VOID,UINT,ENUM +VOID:VOID,BOOL +BOOL:VOID,BOOL,BOOL,POINTER +VOID:VOID,UINT,UINT diff --git a/iocallback.c b/iocallback.c new file mode 100644 index 0000000..ab42921 --- /dev/null +++ b/iocallback.c @@ -0,0 +1,349 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como iocallback.c e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * licinio@bb.com.br (Licínio Luis Branco) + * kraucer@bb.com.br (Kraucer Fernandes Mazuco) + * + */ + +#if defined(_WIN32) /*[*/ + #include +#elif defined(__APPLE__) + #include + #include +#else + #include + #include +#endif /*]*/ + +#include +#include +#include "../globals.h" + +#ifdef HAVE_MALLOC_H + #include +#endif + +static int static_CallAndWait(int(*callback)(H3270 *session, void *), H3270 *session, void *parm); +static unsigned long static_AddInput(int source, H3270 *session, void (*fn)(H3270 *session)); +static void static_RemoveSource(unsigned long id); + +#if !defined(_WIN32) /*[*/ +static unsigned long static_AddOutput(int source, H3270 *session, void (*fn)(H3270 *session)); +#endif + +static unsigned long static_AddExcept(int source, H3270 *session, void (*fn)(H3270 *session)); +static unsigned long static_AddTimeOut(unsigned long interval_ms, H3270 *session, void (*proc)(H3270 *session)); +static void static_RemoveTimeOut(unsigned long timer); +static int static_Sleep(int seconds); +static int static_RunPendingEvents(int wait); + +static gboolean IO_prepare(GSource *source, gint *timeout); +static gboolean IO_check(GSource *source); +static gboolean IO_dispatch(GSource *source, GSourceFunc callback, gpointer user_data); +static void IO_finalize(GSource *source); /* Can be NULL */ +static gboolean IO_closure(gpointer data); + +/*---[ Structs ]-------------------------------------------------------------------------------------------*/ + + typedef struct _IO_Source + { + GSource gsrc; + GPollFD poll; + int source; + void (*fn)(H3270 *session); + H3270 *session; + } IO_Source; + + typedef struct _timer + { + unsigned char remove; + void (*fn)(H3270 *session); + H3270 *session; + } TIMER; + + static GSourceFuncs IOSources = + { + IO_prepare, + IO_check, + IO_dispatch, + IO_finalize, + IO_closure, + NULL + }; + +/*---[ Implement ]-----------------------------------------------------------------------------------------*/ + +static unsigned long AddSource(int source, H3270 *session, gushort events, void (*fn)(H3270 *session)) +{ + IO_Source *src = (IO_Source *) g_source_new(&IOSources,sizeof(IO_Source)); + + src->source = source; + src->fn = fn; + src->poll.fd = source; + src->poll.events = events; + + g_source_attach((GSource *) src,NULL); + g_source_add_poll((GSource *) src,&src->poll); + + return (unsigned long) src; +} + +static unsigned long static_AddInput(int source, H3270 *session, void (*fn)(H3270 *session)) +{ + return AddSource(source,session,G_IO_IN|G_IO_HUP|G_IO_ERR,fn); +} + +static void static_RemoveSource(unsigned long id) +{ + if(id) + g_source_destroy((GSource *) id); +} + +#if !defined(_WIN32) /*[*/ +static unsigned long static_AddOutput(int source, H3270 *session, void (*fn)(H3270 *session)) +{ + return AddSource(source,session,G_IO_OUT|G_IO_HUP|G_IO_ERR,fn); +} +#endif /*]*/ + +static unsigned long static_AddExcept(int source, H3270 *session, void (*fn)(H3270 *session)) +{ +#if defined(_WIN32) /*[*/ + return 0; +#else + return AddSource(source,session,G_IO_HUP|G_IO_ERR,fn); +#endif +} + +static gboolean do_timer(TIMER *t) +{ + if(!t->remove) + t->fn(t->session); + return FALSE; +} + +static unsigned long static_AddTimeOut(unsigned long interval, H3270 *session, void (*proc)(H3270 *session)) +{ + TIMER *t = g_malloc(sizeof(TIMER)); + + t->remove = 0; + t->fn = proc; + t->session = session; + + g_timeout_add_full(G_PRIORITY_DEFAULT, (guint) interval, (GSourceFunc) do_timer, t, g_free); + + return (unsigned long) t; +} + +static void static_RemoveTimeOut(unsigned long timer) +{ + // FIXME (perry#2#): It this really necessary? The timeout is removed as soon as it ticks. + ((TIMER *) timer)->remove++; +} + +static gboolean IO_prepare(GSource *source, gint *timeout) +{ + /* + * Called before all the file descriptors are polled. + * If the source can determine that it is ready here + * (without waiting for the results of the poll() call) + * it should return TRUE. + * + * It can also return a timeout_ value which should be the maximum + * timeout (in milliseconds) which should be passed to the poll() call. + * The actual timeout used will be -1 if all sources returned -1, + * or it will be the minimum of all the timeout_ values + * returned which were >= 0. + * + */ + return 0; +} + +static gboolean IO_check(GSource *source) +{ + /* + * Called after all the file descriptors are polled. + * The source should return TRUE if it is ready to be dispatched. + * Note that some time may have passed since the previous prepare + * function was called, so the source should be checked again here. + * + */ +#if defined(_WIN32) /*[*/ + + if(WaitForSingleObject((HANDLE) ((IO_Source *) source)->source,0) == WAIT_OBJECT_0) + return TRUE; + +#else /*][*/ + + struct pollfd fds; + + memset(&fds,0,sizeof(fds)); + + fds.fd = ((IO_Source *) source)->poll.fd; + fds.events = ((IO_Source *) source)->poll.events; + + if(poll(&fds,1,0) > 0) + return TRUE; + +#endif /*]*/ + + return FALSE; +} + +static gboolean IO_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) +{ + /* + * Called to dispatch the event source, + * after it has returned TRUE in either its prepare or its check function. + * The dispatch function is passed in a callback function and data. + * The callback function may be NULL if the source was never connected + * to a callback using g_source_set_callback(). The dispatch function + * should call the callback function with user_data and whatever additional + * parameters are needed for this type of event source. + */ + ((IO_Source *) source)->fn(((IO_Source *) source)->session); + return TRUE; +} + +static void IO_finalize(GSource *source) +{ + +} + +static gboolean IO_closure(gpointer data) +{ + return 0; +} + +struct bgParameter +{ + gboolean running; + H3270 *session; + int rc; + int(*callback)(H3270 *session, void *); + void *parm; + +}; + +gpointer BgCall(struct bgParameter *p) +{ + trace("%s starts",__FUNCTION__); + p->rc = p->callback(p->session, p->parm); + p->running = FALSE; + trace("%s ends",__FUNCTION__); + return 0; +} + +static int static_CallAndWait(int(*callback)(H3270 *session, void *), H3270 *session, void *parm) +{ + struct bgParameter p = { TRUE, session, -1, callback, parm }; + GThread *thread; + + trace("Starting auxiliary thread for callback %p",callback); + + p.running = TRUE; + thread = g_thread_create( (GThreadFunc) BgCall, &p, 0, NULL); + + if(!thread) + { + g_error("Can't start background thread"); + return -1; + } + + while(p.running) + gtk_main_iteration(); + + return p.rc; +} + +static int static_Sleep(int seconds) +{ + time_t end = time(0) + seconds; + + while(time(0) < end) + gtk_main_iteration(); + + return 0; +} + +static int static_RunPendingEvents(int wait) +{ + int rc = 0; + while(gtk_events_pending()) + { + rc = 1; + gtk_main_iteration(); + } + + if(wait) + gtk_main_iteration(); + + return rc; +} + +static void beep(H3270 *session) +{ + gdk_beep(); +} + +void v3270_register_io_handlers(v3270Class *cls) +{ + static const struct lib3270_callbacks hdl = + { + sizeof(struct lib3270_callbacks), + + static_AddTimeOut, + static_RemoveTimeOut, + + static_AddInput, + static_RemoveSource, + + static_AddExcept, + +#if !defined(_WIN32) + static_AddOutput, +#endif + +#ifdef G_THREADS_ENABLED + static_CallAndWait, +#else + NULL, +#endif + + static_Sleep, + static_RunPendingEvents, + beep + + }; + + if(lib3270_register_handlers(&hdl)) + { + g_error("%s",_( "Can't set lib3270 I/O handlers" ) ); + } + + trace("%s: I/O handlers OK",__FUNCTION__); +} diff --git a/keyboard.c b/keyboard.c new file mode 100644 index 0000000..6fe3087 --- /dev/null +++ b/keyboard.c @@ -0,0 +1,241 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como keyboard.c e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * licinio@bb.com.br (Licínio Luis Branco) + * kraucer@bb.com.br (Kraucer Fernandes Mazuco) + * macmiranda@bb.com.br (Marco Aurélio Caldas Miranda) + * + */ + + #include + #include + #include + #include + #include + #include + + #include "v3270.h" + #include "private.h" + +#if GTK_CHECK_VERSION(3,0,0) + #include +#else + #include +#endif + +#ifndef GDK_ALT_MASK + #define GDK_ALT_MASK GDK_MOD1_MASK +#endif + +#ifndef GDK_NUMLOCK_MASK + #define GDK_NUMLOCK_MASK GDK_MOD2_MASK +#endif + + +/*--[ Implement ]------------------------------------------------------------------------------------*/ + + #define keyval_is_alt() (event->keyval == GDK_Alt_L || event->keyval == GDK_Meta_L || event->keyval == GDK_ISO_Level3_Shift) + + static void update_keyboard_state(v3270 *terminal, GdkEventKey *event, gboolean status) + { + if(event->keyval == GDK_Shift_R || event->keyval == GDK_Shift_L) + { + if(status) + terminal->keyflags |= KEY_FLAG_SHIFT; + else + terminal->keyflags &= ~KEY_FLAG_SHIFT; + v3270_draw_shift_status(terminal); + } + + if(keyval_is_alt()) + { + if(status) + terminal->keyflags |= KEY_FLAG_ALT; + else + terminal->keyflags &= ~KEY_FLAG_ALT; + v3270_draw_alt_status(terminal); + } + + } + + static gboolean check_keypress(v3270 *widget, GdkEventKey *event) + { + static const struct _keycode + { + guint keyval; + GdkModifierType state; + int (*exec)(H3270 *session); + } keycode[] = + { + { GDK_Left, 0, lib3270_cursor_left }, + { GDK_Up, 0, lib3270_cursor_up }, + { GDK_Right, 0, lib3270_cursor_right }, + { GDK_Down, 0, lib3270_cursor_down }, + { GDK_Tab, 0, lib3270_tab }, + { GDK_ISO_Left_Tab, GDK_SHIFT_MASK, lib3270_backtab }, + { GDK_KP_Left, 0, lib3270_cursor_left }, + { GDK_KP_Up, 0, lib3270_cursor_up }, + { GDK_KP_Right, 0, lib3270_cursor_right }, + { GDK_KP_Down, 0, lib3270_cursor_down }, + + { GDK_KP_Add, GDK_NUMLOCK_MASK, NULL }, + { GDK_KP_Subtract, GDK_NUMLOCK_MASK, NULL }, + + { GDK_3270_PrintScreen, 0, NULL }, + { GDK_Sys_Req, 0, lib3270_sysreq }, + + { GDK_Print, GDK_CONTROL_MASK, NULL }, + { GDK_Print, GDK_SHIFT_MASK, lib3270_sysreq }, + { GDK_Control_R, 0, NULL }, + { GDK_Control_L, 0, NULL }, + +#ifdef WIN32 + { GDK_Pause, 0, NULL }, +#endif + }; + + int f; + int state = event->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_ALT_MASK); + gboolean handled = FALSE; + +#ifdef WIN32 + // FIXME (perry#1#): Find a better way! + if( event->keyval == 0xffffff && event->hardware_keycode == 0x0013) + event->keyval = GDK_Pause; + + // Windows sets in left/right control + else if(state & GDK_CONTROL_MASK && (event->keyval == GDK_Control_R || event->keyval == GDK_Control_L)) + state &= ~GDK_CONTROL_MASK; +#endif + + g_signal_emit(GTK_WIDGET(widget), v3270_widget_signal[SIGNAL_KEYPRESS], 0, event->keyval, state, &handled); + if(handled) + return TRUE; + + if(event->keyval >= GDK_F1 && event->keyval <= GDK_F12 && !(state & (GDK_CONTROL_MASK|GDK_ALT_MASK))) + { + int pfcode = (event->keyval - GDK_F1) + ((state & GDK_SHIFT_MASK) ? 13 : 1); + if(pfcode > 0 && pfcode < 25) + { + lib3270_pfkey(widget->host,pfcode); + return TRUE; + } + } + + for(f=0; f < G_N_ELEMENTS(keycode);f++) + { + if(keycode[f].keyval == event->keyval && state == keycode[f].state) + { + if(keycode[f].exec) + { + keycode[f].exec(widget->host); + return TRUE; + } + } + } + + return FALSE; + } + + gboolean v3270_key_press_event(GtkWidget *widget, GdkEventKey *event) + { + v3270 * terminal = GTK_V3270(widget); + + update_keyboard_state(terminal,event,TRUE); + + if(gtk_im_context_filter_keypress(terminal->input_method,event)) + return TRUE; + + if(check_keypress(terminal,event)) + { + gtk_im_context_reset(terminal->input_method); + return TRUE; + } + + return FALSE; + + } + + gboolean v3270_key_release_event(GtkWidget *widget, GdkEventKey *event) + { + v3270 * terminal = GTK_V3270(widget); + + update_keyboard_state(terminal,event,FALSE); + + if(gtk_im_context_filter_keypress(terminal->input_method,event)) + return TRUE; + + return FALSE; + + } + + void v3270_tab(GtkWidget *widget) + { + g_return_if_fail(GTK_IS_V3270(widget)); + lib3270_tab(GTK_V3270(widget)->host); + } + + void v3270_backtab(GtkWidget *widget) + { + g_return_if_fail(GTK_IS_V3270(widget)); + lib3270_backtab(GTK_V3270(widget)->host); + } + + void v3270_set_string(GtkWidget *widget, const gchar *str) + { + H3270 *host; + gchar *utf; + + g_return_if_fail(GTK_IS_V3270(widget)); + + host = GTK_V3270(widget)->host; + + utf = g_convert((char *) str, -1, lib3270_get_charset(host), "UTF-8", NULL, NULL, NULL); + + if(utf) + { + lib3270_set_string(host, (const unsigned char *) utf); + g_free(utf); + } + + } + + void v3270_key_commit(GtkIMContext *imcontext, gchar *str, v3270 *widget) + { + gchar *utf = g_convert((char *) str, -1, lib3270_get_charset(widget->host), "UTF-8", NULL, NULL, NULL); + + if(utf) + { + lib3270_set_string(GTK_V3270(widget)->host, (const unsigned char *) utf); + g_free(utf); + } + else + { + lib3270_ring_bell(widget->host); + } + } + + diff --git a/locked.xbm b/locked.xbm new file mode 100644 index 0000000..039a355 --- /dev/null +++ b/locked.xbm @@ -0,0 +1,14 @@ +#define locked_width 32 +#define locked_height 32 +static unsigned char locked_bits[] = { + 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x07, 0xe0, 0x00, + 0x80, 0x03, 0xc0, 0x01, 0xc0, 0xf1, 0x8f, 0x03, 0xc0, 0xf8, 0x1f, 0x03, + 0xc0, 0x1c, 0x38, 0x03, 0xc0, 0x0c, 0x30, 0x03, 0xc0, 0x0c, 0x30, 0x03, + 0xc0, 0x0c, 0x30, 0x03, 0xc0, 0x0c, 0x30, 0x03, 0xc0, 0x0c, 0x30, 0x03, + 0xc0, 0x0c, 0x30, 0x03, 0xc0, 0x0c, 0x30, 0x03, 0xf0, 0xff, 0xff, 0x0f, + 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xcc, 0xcc, 0x0c, 0xf0, 0xcc, 0xcc, 0x0c, + 0x30, 0x33, 0x33, 0x0f, 0x30, 0x33, 0x33, 0x0f, 0xf0, 0xcc, 0xcc, 0x0c, + 0xf0, 0xcc, 0xcc, 0x0c, 0x30, 0x33, 0x33, 0x0f, 0x30, 0x33, 0x33, 0x0f, + 0xf0, 0xcc, 0xcc, 0x0c, 0xf0, 0xcc, 0xcc, 0x0c, 0x30, 0x33, 0x33, 0x0f, + 0x30, 0x33, 0x33, 0x0f, 0xf0, 0xcc, 0xcc, 0x0c, 0xf0, 0xcc, 0xcc, 0x0c, + 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f }; diff --git a/mouse.c b/mouse.c new file mode 100644 index 0000000..1318739 --- /dev/null +++ b/mouse.c @@ -0,0 +1,263 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como mouse.c e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * + */ + + #include + #include + #include + #include "v3270.h" + #include "private.h" + #include + #include + +/*--[ Implement ]------------------------------------------------------------------------------------*/ + +gint v3270_get_offset_at_point(v3270 *widget, gint x, gint y) +{ + GdkPoint point; + int r,c; + + if(x > 0 && y > 0) + { + point.x = ((x-widget->metrics.left)/widget->metrics.width); + point.y = ((y-widget->metrics.top)/widget->metrics.spacing); + + lib3270_get_screen_size(widget->host,&r,&c); + + if(point.x >= 0 && point.y >= 0 && point.x < c && point.y < r) + return (point.y * c) + point.x; + } + + return -1; +} + +static void single_click(v3270 *widget, int baddr) +{ + switch(lib3270_get_selection_flags(widget->host,baddr)) + { + case 0x00: + // Unselected area, move cursor and remove selection + lib3270_set_cursor_address(widget->host,baddr); + lib3270_unselect(widget->host); + widget->selecting = 1; + break; + + + default: + // Move selected area + trace("%s: default action",__FUNCTION__); + widget->selection_addr = baddr; + widget->moving = 1; + } + + +} + +static void button_1_press(GtkWidget *widget, GdkEventType type, int baddr) +{ + switch(type) + { + case GDK_BUTTON_PRESS: // Single click - set mode + single_click(GTK_V3270(widget),baddr); + break; + + case GDK_2BUTTON_PRESS: // Double click - Select word + if(lib3270_select_word_at(GTK_V3270(widget)->host,baddr)); + lib3270_ring_bell(GTK_V3270(widget)->host); + break; + + case GDK_3BUTTON_PRESS: // Triple clock - Select field + if(lib3270_select_field_at(GTK_V3270(widget)->host,baddr)) + lib3270_ring_bell(GTK_V3270(widget)->host); + break; + +#ifdef DEBUG + default: + trace("Unexpected button 1 type %d",type); +#endif + } +} + +void v3270_emit_popup(v3270 *widget, int baddr, GdkEventButton *event) +{ + unsigned char chr = 0; + unsigned short attr; + gboolean handled = FALSE; + + lib3270_get_contents(widget->host,baddr,baddr,&chr,&attr); + + g_signal_emit(GTK_WIDGET(widget), v3270_widget_signal[SIGNAL_POPUP], 0, + (attr & LIB3270_ATTR_SELECTED) ? TRUE : FALSE, + lib3270_connected(widget->host) ? TRUE : FALSE, + event, + &handled); + + if(handled) + return; + + gdk_beep(); +} + +gboolean v3270_button_press_event(GtkWidget *widget, GdkEventButton *event) +{ + int baddr = v3270_get_offset_at_point(GTK_V3270(widget),event->x,event->y); + + if(baddr < 0) + return FALSE; + +// trace("%s button=%d type=%d",__FUNCTION__,event->button,event->type); + + switch(event->button) + { + case 1: // Left button + button_1_press(widget,event->type,baddr); + break; + + case 3: // Right button + if(event->type == GDK_BUTTON_PRESS) + v3270_emit_popup(GTK_V3270(widget),baddr,event); + break; + + default: + trace("%s button=%d type=%d",__FUNCTION__,event->button,event->type); + } + + return FALSE; +} + +gboolean v3270_button_release_event(GtkWidget *widget, GdkEventButton*event) +{ + switch(event->button) + { + case 1: + GTK_V3270(widget)->selecting = 0; + GTK_V3270(widget)->moving = 0; + GTK_V3270(widget)->resizing = 0; + break; + + default: + trace("%s button=%d type=%d",__FUNCTION__,event->button,event->type); + } + + + return FALSE; +} + + +static void update_mouse_pointer(GtkWidget *widget, int baddr) +{ + v3270 *terminal = GTK_V3270(widget); + + if(baddr >= 0 && terminal->pointer_id == LIB3270_CURSOR_EDITABLE) + { + int id = terminal->pointer; + + switch(lib3270_get_selection_flags(terminal->host,baddr) & 0x8f) + { + case 0x80: + id = V3270_CURSOR_MOVE_SELECTION; + break; + + case 0x82: + id = V3270_CURSOR_SELECTION_TOP; + break; + + case 0x86: + id = V3270_CURSOR_SELECTION_TOP_RIGHT; + break; + + case 0x84: + id = V3270_CURSOR_SELECTION_RIGHT; + break; + + case 0x81: + id = V3270_CURSOR_SELECTION_LEFT; + break; + + case 0x89: + id = V3270_CURSOR_SELECTION_BOTTOM_LEFT; + break; + + case 0x88: + id = V3270_CURSOR_SELECTION_BOTTOM; + break; + + case 0x8c: + id = V3270_CURSOR_SELECTION_BOTTOM_RIGHT; + break; + + case 0x83: + id = V3270_CURSOR_SELECTION_TOP_LEFT; + break; + + default: + id = lib3270_is_protected(terminal->host,baddr) ? V3270_CURSOR_PROTECTED : V3270_CURSOR_UNPROTECTED; + + } + + gdk_window_set_cursor(gtk_widget_get_window(widget),v3270_cursor[id]); + } + +} + +void v3270_update_mouse_pointer(GtkWidget *widget) +{ + gint x, y; + +#if GTK_CHECK_VERSION(3,4,0) + #warning Implement gdk_window_get_device_position +#endif // GTK(3,4,0) + + gtk_widget_get_pointer(widget,&x,&y); + update_mouse_pointer(widget,v3270_get_offset_at_point(GTK_V3270(widget),x,y)); +} + +gboolean v3270_motion_notify_event(GtkWidget *widget, GdkEventMotion *event) +{ + v3270 * terminal = GTK_V3270(widget); + int baddr = v3270_get_offset_at_point(terminal,event->x,event->y); + + if(baddr >= 0) + { + if(terminal->selecting) // Select region + { + lib3270_select_to(terminal->host,baddr); + } + if(terminal->moving) // Move selected area + { + terminal->selection_addr = lib3270_drag_selection(terminal->host,terminal->pointer,terminal->selection_addr,baddr); + } + else + { + terminal->pointer = lib3270_get_selection_flags(terminal->host,baddr); + update_mouse_pointer(widget,baddr); + } + } + + return FALSE; +} diff --git a/oia.c b/oia.c new file mode 100644 index 0000000..9435051 --- /dev/null +++ b/oia.c @@ -0,0 +1,1032 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como oia.c e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * + */ + + #include + #include + #include + #include + #include + #include + + #ifdef HAVE_LIBM + #include + #endif // HAVE_LIBM + + #include "v3270.h" + #include "private.h" + +/*--[ Prototipes ]-----------------------------------------------------------------------------------*/ + +static void draw_cursor_position(cairo_t *cr, GdkRectangle *rect, struct v3270_metrics *metrics, int row, int col); + +/*--[ Statics ]--------------------------------------------------------------------------------------*/ + + #include "locked.xbm" + #include "unlocked.xbm" + +/*--[ Implement ]------------------------------------------------------------------------------------*/ + +static void short2string(char *ptr, unsigned short vlr, size_t sz) +{ + int f; + + for(f=sz-1;f>=0;f--) + { + ptr[f] = '0'+(vlr%10); + vlr /= 10; + } +} + + +#ifdef HAVE_LIBM +static gint draw_spinner(cairo_t *cr, GdkRectangle *r, GdkColor *color, gint step) +{ + static const guint num_steps = 10; + + gdouble dx = r->width/2; + gdouble dy = r->height/2; + gdouble radius = MIN (r->width / 2, r->height / 2); + gdouble half = num_steps / 2; + gint i; + + cairo_save(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + cairo_rectangle(cr, r->x, r->y, r->width, r->height); + cairo_clip(cr); + cairo_translate(cr, r->x, r->y); + + step++; + step %= num_steps; + + for (i = 0; i < num_steps; i++) + { + gint inset = 0.7 * radius; + + /* transparency is a function of time and intial value */ + gdouble t = (gdouble) ((i + num_steps - step) % num_steps) / num_steps; + + cairo_save(cr); + + cairo_set_source_rgba (cr, + color[V3270_COLOR_OIA_SPINNER].red / 65535., + color[V3270_COLOR_OIA_SPINNER].green / 65535., + color[V3270_COLOR_OIA_SPINNER].blue / 65535., + t); + + cairo_set_line_width (cr, 2.0); + cairo_move_to (cr, + dx + (radius - inset) * cos (i * G_PI / half), + dy + (radius - inset) * sin (i * G_PI / half)); + cairo_line_to (cr, + dx + radius * cos (i * G_PI / half), + dy + radius * sin (i * G_PI / half)); + cairo_stroke (cr); + + cairo_restore (cr); + } + + cairo_restore(cr); + + return step; +} +#endif // HAVE_LIBM + +static void setup_cursor_position(GdkRectangle *rect, struct v3270_metrics *metrics, cairo_t *cr, H3270 *host, int cols, GdkColor *color) +{ + rect->width = metrics->width * 8; + rect->x -= rect->width; + + if(lib3270_get_toggle(host,LIB3270_TOGGLE_CURSOR_POS)) + { + int addr = lib3270_get_cursor_address(host); + draw_cursor_position(cr,rect,metrics,addr/cols,addr%cols); + } +} + +static void setup_timer_position(GdkRectangle *rect, struct v3270_metrics *metrics, cairo_t *cr, H3270 *host, int cols, GdkColor *color) +{ + char buffer[7]; + cairo_text_extents_t extents; + + short2string(buffer,0,2); + buffer[2] = ':'; + short2string(buffer+3,0,2); + buffer[5] = 0; + + cairo_text_extents(cr,buffer,&extents); + rect->width = ((int) extents.width + 2); + rect->x -= rect->width; +} + +static void setup_spinner_position(GdkRectangle *rect, struct v3270_metrics *metrics, cairo_t *cr, H3270 *host, int cols, GdkColor *color) +{ + rect->width = rect->height; + rect->x -= rect->width; +// draw_spinner(cr,rect,color,0); +} + +static void setup_luname_position(GdkRectangle *rect, struct v3270_metrics *metrics, cairo_t *cr, H3270 *host, int cols, GdkColor *color) +{ + const char *luname = lib3270_get_luname(host); + + rect->width *= 16; + rect->x -= rect->width; + + cairo_save(cr); + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_clip(cr); + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); +#else + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_BACKGROUND); +#endif + + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + + if(luname) + { + cairo_move_to(cr,rect->x,rect->y+metrics->height); + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_LUNAME); + cairo_show_text(cr,luname); + cairo_stroke(cr); + } + + cairo_restore(cr); + +} + +static void setup_single_char_right(GdkRectangle *rect, struct v3270_metrics *metrics, cairo_t *cr, H3270 *host, int cols, GdkColor *color) +{ + rect->x -= rect->width; + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); +#endif + +} + +static void setup_insert_position(GdkRectangle *rect, struct v3270_metrics *metrics, cairo_t *cr, H3270 *host, int cols, GdkColor *color) +{ + if(rect->width > rect->height) + { + rect->width = rect->height; + } + else if(rect->height > rect->width) + { + rect->y += (rect->height - rect->width)/2; + rect->height = rect->width; + } + + rect->x -= rect->width; + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); +#endif + +} + + + +static void setup_double_char_position(GdkRectangle *rect, struct v3270_metrics *metrics, cairo_t *cr, H3270 *host, int cols, GdkColor *color) +{ + rect->width <<= 1; + rect->x -= rect->width; + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); +#endif + +} + +static void draw_undera(cairo_t *cr, H3270 *host, struct v3270_metrics *metrics, GdkColor *color, GdkRectangle *rect) +{ +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); +#else + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_BACKGROUND); +#endif + + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + + if(lib3270_get_undera(host)) + { + const gchar *chr = lib3270_in_e(host) ? "B" : "A"; + cairo_text_extents_t extents; + int x,y; + + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_BACKGROUND); + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_FOREGROUND); + + cairo_text_extents(cr,chr,&extents); + + x = rect->x + ((rect->width/2) - ((extents.width+extents.x_bearing)/2)); + y = rect->y + extents.height+((rect->height/2) - (extents.height/2)); + + cairo_move_to(cr,x,y); + cairo_show_text(cr,chr); + + cairo_move_to(cr,x+extents.x_bearing,y+2); + cairo_rel_line_to(cr,extents.width,0); + + cairo_stroke(cr); + + } + +} + +void v3270_draw_connection(cairo_t *cr, H3270 *host, struct v3270_metrics *metrics, GdkColor *color, GdkRectangle *rect) +{ + cairo_text_extents_t extents; + gchar *str = "?"; + + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_FOREGROUND); + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_stroke(cr); + + if(lib3270_get_oia_box_solid(host)) + { + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + return; + } + + if(lib3270_in_ansi(host)) + *str = 'N'; + else if(lib3270_in_sscp(host)) + *str = 'S'; + + cairo_text_extents(cr,str,&extents); + cairo_move_to(cr,rect->x+((rect->width/2)-(extents.width/2)),rect->y+extents.height+( (rect->height/2) - (extents.height/2))); + cairo_show_text(cr,str); + +} + +void v3270_draw_ssl_status(cairo_t *cr, H3270 *host, struct v3270_metrics *metrics, GdkColor *color, GdkRectangle *rect) +{ + cairo_surface_t * icon; + double sz = rect->width < rect->height ? rect->width : rect->height; + int idx = lib3270_get_ssl_state(host) ? 1 : 0; + + static const struct + { + unsigned short width; + unsigned short height; + unsigned char * bits; + } bitmap[] = + { + { unlocked_width, unlocked_height, unlocked_bits }, + { locked_width, locked_height, locked_bits }, + }; + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); +#else + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_BACKGROUND); +#endif + + cairo_translate(cr, rect->x, rect->y); + + cairo_rectangle(cr, 0, 0, rect->width, rect->height); + cairo_fill(cr); + + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_FOREGROUND); + + icon = cairo_image_surface_create_for_data( (unsigned char *) bitmap[idx].bits, + CAIRO_FORMAT_A1, + bitmap[idx].width,bitmap[idx].height, + cairo_format_stride_for_width(CAIRO_FORMAT_A1,locked_width)); + + cairo_scale(cr, sz / ((double) bitmap[idx].width), + sz / ((double) bitmap[idx].height)); + + cairo_mask_surface(cr,icon,(rect->width-sz)/2,(rect->height-sz)/2); + + cairo_surface_destroy(icon); + +} + +static void draw_status_message(cairo_t *cr, LIB3270_MESSAGE id, struct v3270_metrics *metrics, GdkColor *color, GdkRectangle *rect) +{ + #ifdef DEBUG + #define OIA_MESSAGE(x,c,y) { #x, c, y } + #else + #define OIA_MESSAGE(x,c,y) { c, y } + #endif + + static const struct _message + { + #ifdef DEBUG + const gchar * dbg; + #endif + enum V3270_COLOR + color; + const gchar * msg; + } message[] = + { + OIA_MESSAGE( LIB3270_MESSAGE_NONE, + V3270_COLOR_OIA_STATUS_OK, + NULL ), + + OIA_MESSAGE( LIB3270_MESSAGE_SYSWAIT, + V3270_COLOR_OIA_STATUS_OK, + N_( "X System" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_TWAIT, + V3270_COLOR_OIA_STATUS_OK, + N_( "X Wait" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_CONNECTED, + V3270_COLOR_OIA_STATUS_OK, + NULL ), + + OIA_MESSAGE( LIB3270_MESSAGE_DISCONNECTED, + V3270_COLOR_OIA_STATUS_INVALID, + N_( "X Not Connected" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_AWAITING_FIRST, + V3270_COLOR_OIA_STATUS_OK, + N_( "X" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_MINUS, + V3270_COLOR_OIA_STATUS_OK, + N_( "X -f" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_PROTECTED, + V3270_COLOR_OIA_STATUS_INVALID, + N_( "X Protected" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_NUMERIC, + V3270_COLOR_OIA_STATUS_INVALID, + N_( "X Numeric" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_OVERFLOW, + V3270_COLOR_OIA_STATUS_INVALID, + N_( "X Overflow" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_INHIBIT, + V3270_COLOR_OIA_STATUS_INVALID, + N_( "X Inhibit" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_KYBDLOCK, + V3270_COLOR_OIA_STATUS_INVALID, + N_( "X") ), + + OIA_MESSAGE( LIB3270_MESSAGE_X, + V3270_COLOR_OIA_STATUS_INVALID, + N_( "X" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_RESOLVING, + V3270_COLOR_OIA_STATUS_WARNING, + N_( "X Resolving" ) ), + + OIA_MESSAGE( LIB3270_MESSAGE_CONNECTING, + V3270_COLOR_OIA_STATUS_WARNING, + N_( "X Connecting" ) ), + + + }; + + const gchar *msg = message[0].msg; + +// trace("%s: id=%d",__FUNCTION__,id); + + if(id >= 0 && id < G_N_ELEMENTS(message)) + { + msg = message[id].msg; +#ifdef DEBUG + if(!msg) + msg = message[id].dbg; +#endif // DEBUG + } + + if(msg) + { + gdk_cairo_set_source_color(cr,color+message[id].color); + cairo_move_to(cr,rect->x,rect->y+metrics->height); + cairo_show_text(cr,gettext(msg)); + } + +} + +static void draw_insert(cairo_t *cr, H3270 *host, GdkColor *color, GdkRectangle *rect) +{ + if(lib3270_get_toggle(host,LIB3270_TOGGLE_INSERT)) + { + double y = rect->y+(rect->height-2); + + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_clip(cr); + + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_FOREGROUND); + + cairo_move_to(cr,rect->x,y); + cairo_rel_line_to(cr,rect->width/2,-(rect->height/1.7)); + cairo_line_to(cr,rect->x+rect->width,y); + cairo_stroke(cr); + } + +} + +void v3270_draw_oia(cairo_t *cr, H3270 *host, int row, int cols, struct v3270_metrics *metrics, GdkColor *color, GdkRectangle *rect) +{ + static const struct _right_fields + { + V3270_OIA_FIELD id; + void (*draw)(GdkRectangle *rect, struct v3270_metrics *metrics, cairo_t *cr, H3270 *host, int cols, GdkColor *color); + } right[] = + { + { V3270_OIA_CURSOR_POSITION, setup_cursor_position }, + { V3270_OIA_TIMER, setup_timer_position }, + { V3270_OIA_SPINNER, setup_spinner_position }, + { V3270_OIA_LUNAME, setup_luname_position }, +#ifdef X3270_PRINTER + { V3270_OIA_PRINTER, setup_single_char_right }, +#endif // X3270_PRINTER + { V3270_OIA_SCRIPT, setup_single_char_right }, + { V3270_OIA_INSERT, setup_insert_position }, + { V3270_OIA_TYPEAHEAD, setup_single_char_right }, + { V3270_OIA_SHIFT, setup_double_char_position }, +// { V3270_OIA_CAPS, setup_single_char_right }, + { V3270_OIA_ALT, setup_single_char_right }, + { V3270_OIA_SSL, setup_double_char_position }, + }; + + int f; + int rCol = metrics->left+(cols*metrics->width); + int lCol = metrics->left+1; + + row += OIA_TOP_MARGIN; + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_SEPARATOR); + cairo_rectangle(cr, metrics->left, row, cols*metrics->width, 1); + cairo_fill(cr); + + row += 2; + + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_BACKGROUND); + cairo_rectangle(cr, metrics->left, row, cols*metrics->width, metrics->spacing); + cairo_fill(cr); + + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_FOREGROUND); + + for(f=0;fx = rCol; + r->y = row; + r->width = metrics->width; + r->height = metrics->spacing; + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_FOREGROUND); + right[f].draw(r,metrics,cr,host,cols,color); + rCol = r->x - (metrics->width/3); + } + + gdk_cairo_set_source_color(cr,color+V3270_COLOR_OIA_FOREGROUND); + + const gchar *str = "4"; + cairo_text_extents_t extents; + + cairo_text_extents(cr,str,&extents); + cairo_move_to(cr,lCol+(((metrics->width+2)/2)-(extents.width/2)),row+extents.height+( (metrics->spacing/2) - (extents.height/2))); + cairo_show_text(cr,str); + + cairo_stroke(cr); + cairo_rectangle(cr, lCol, row, metrics->width+2, metrics->spacing); + cairo_stroke(cr); + + lCol += (metrics->width+5); + + // Undera indicator + rect[V3270_OIA_UNDERA].x = lCol; + rect[V3270_OIA_UNDERA].y = row; + rect[V3270_OIA_UNDERA].width = metrics->width+3; + rect[V3270_OIA_UNDERA].height = metrics->spacing; + draw_undera(cr,host,metrics,color,rect+V3270_OIA_UNDERA); + + lCol += (3 + rect[V3270_OIA_UNDERA].width); + + // Connection indicator + rect[V3270_OIA_CONNECTION].x = lCol; + rect[V3270_OIA_CONNECTION].y = row; + rect[V3270_OIA_CONNECTION].width = metrics->width+3; + rect[V3270_OIA_CONNECTION].height = metrics->spacing; + v3270_draw_connection(cr,host,metrics,color,rect+V3270_OIA_CONNECTION); + + lCol += (4 + rect[V3270_OIA_CONNECTION].width); + + memset(rect+V3270_OIA_MESSAGE,0,sizeof(GdkRectangle)); + + if(lCol < rCol) + { + GdkRectangle *r = rect+V3270_OIA_MESSAGE; + r->x = lCol; + r->y = row; + r->width = rCol - lCol; + r->height = metrics->spacing; + draw_status_message(cr,lib3270_get_program_message(host),metrics,color,r); + } + + cairo_save(cr); + v3270_draw_ssl_status(cr,host,metrics,color,rect+V3270_OIA_SSL); + cairo_restore(cr); + + cairo_save(cr); + draw_insert(cr,host,color,rect+V3270_OIA_INSERT); + cairo_restore(cr); +} + +/** + * Begin update of a specific OIA field. + * + * @param terminal 3270 terminal widget. + * @param r Rectangle to receive updated region. + * @param id Field id. + * + * @return cairo object for drawing. + * + */ +static cairo_t * set_update_region(v3270 * terminal, GdkRectangle **r, V3270_OIA_FIELD id) +{ + GdkRectangle * rect = terminal->oia_rect + id; + cairo_t * cr = cairo_create(terminal->surface); + + cairo_set_scaled_font(cr,terminal->font_scaled); + + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_clip(cr); + + *r = rect; + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); +#else + gdk_cairo_set_source_color(cr,terminal->color+V3270_COLOR_OIA_BACKGROUND); +#endif + + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + + gdk_cairo_set_source_color(cr,terminal->color+V3270_COLOR_OIA_FOREGROUND); + + return cr; +} + +void v3270_update_luname(GtkWidget *widget,const gchar *name) +{ + cairo_t * cr; + GdkRectangle * rect; + v3270 * terminal = GTK_V3270(widget); + + if(!terminal->surface) + return; + +// trace("%s",__FUNCTION__); + + cr = set_update_region(terminal,&rect,V3270_OIA_LUNAME); + + if(name) + { + cairo_move_to(cr,rect->x,rect->y+terminal->metrics.height); + gdk_cairo_set_source_color(cr,terminal->color+V3270_COLOR_OIA_LUNAME); + cairo_show_text(cr,name); + cairo_stroke(cr); + } + + cairo_destroy(cr); + + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),rect->x,rect->y,rect->width,rect->height); +} + +void v3270_update_message(v3270 *widget, LIB3270_MESSAGE id) +{ + cairo_t * cr; + GdkRectangle * rect; + + if(!widget->surface) + return; + + cr = set_update_region(widget,&rect,V3270_OIA_MESSAGE); + + draw_status_message(cr,id,&widget->metrics,widget->color,rect); + + cairo_destroy(cr); + + gtk_widget_queue_draw_area(GTK_WIDGET(widget),rect->x,rect->y,rect->width,rect->height); + + if(widget->accessible) + v3270_acessible_set_state(widget->accessible,id); +} + +static void draw_cursor_position(cairo_t *cr, GdkRectangle *rect, struct v3270_metrics *metrics, int row, int col) +{ + cairo_text_extents_t extents; + char buffer[10]; + + short2string(buffer,row+1,3); + buffer[3] = '/'; + short2string(buffer+4,col+1,3); + buffer[7] = 0; + + cairo_text_extents(cr,buffer,&extents); + + cairo_move_to(cr,(rect->x+rect->width)-(extents.width+2),rect->y+metrics->height); + cairo_show_text(cr, buffer); + cairo_stroke(cr); +} + +void v3270_update_cursor(H3270 *session, unsigned short row, unsigned short col, unsigned char c, unsigned short attr) +{ + v3270 * terminal = GTK_V3270(session->widget); + GdkRectangle saved; + + if(!terminal->surface) + return; + + // Update cursor rectangle + saved = terminal->cursor.rect; + + terminal->cursor.rect.x = terminal->metrics.left + (col * terminal->cursor.rect.width); + terminal->cursor.rect.y = terminal->metrics.top + (row * terminal->metrics.spacing); + terminal->cursor.rect.width = terminal->metrics.width; + terminal->cursor.rect.height = terminal->metrics.height+terminal->metrics.descent; + terminal->cursor.show |= 1; + + gtk_widget_queue_draw_area( GTK_WIDGET(terminal), saved.x, + saved.y, + saved.width, + saved.height); + + + v3270_update_cursor_surface(terminal,c,attr); + + gtk_widget_queue_draw_area( GTK_WIDGET(terminal), + terminal->cursor.rect.x,terminal->cursor.rect.y, + terminal->cursor.rect.width,terminal->cursor.rect.height); + + if(lib3270_get_toggle(session,LIB3270_TOGGLE_CROSSHAIR)) + { + GtkAllocation allocation; + gtk_widget_get_allocation(GTK_WIDGET(terminal), &allocation); + + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),0,saved.y+terminal->metrics.height,allocation.width,1); + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),saved.x,0,1,terminal->oia_rect->y-3); + + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),0,terminal->cursor.rect.y+terminal->metrics.height,allocation.width,1); + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),terminal->cursor.rect.x,0,1,terminal->oia_rect->y-3); + } + + if(lib3270_get_toggle(session,LIB3270_TOGGLE_CURSOR_POS)) + { + // Update OIA + GdkRectangle * rect; + cairo_t * cr; + + cr = set_update_region(terminal,&rect,V3270_OIA_CURSOR_POSITION); + + draw_cursor_position(cr,rect,&terminal->metrics,row,col); + + cairo_destroy(cr); + + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),rect->x,rect->y,rect->width,rect->height); + } + + if(terminal->accessible) + g_signal_emit_by_name(ATK_TEXT(terminal->accessible),"text-caret-moved",lib3270_get_cursor_address(session)); + +} + +struct timer_info +{ + time_t start; + time_t last; +#ifdef HAVE_LIBM + gint step; +#endif // HAVE_LIBM + v3270 * terminal; +}; + +static void release_timer(struct timer_info *info) +{ +// trace("Timer %p stops",info); + info->terminal->timer = NULL; + + if(info->terminal->surface) + { + // Erase timer info + static const int id[] = { V3270_OIA_TIMER, +#ifdef HAVE_LIBM + V3270_OIA_SPINNER +#endif // HAVE_LIBM + }; + int f; + + cairo_t *cr = cairo_create(info->terminal->surface); + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); +#else + gdk_cairo_set_source_color(cr,info->terminal->color+V3270_COLOR_OIA_BACKGROUND); +#endif + + for(f=0;fterminal->oia_rect + id[f]; + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + gtk_widget_queue_draw_area(GTK_WIDGET(info->terminal),rect->x,rect->y,rect->width,rect->height); + } + cairo_destroy(cr); + } + + g_free(info); +} + +void v3270_draw_shift_status(v3270 *terminal) +{ + GdkRectangle *r; + cairo_t *cr; + + if(!terminal->surface) + return; + + cr = set_update_region(terminal,&r,V3270_OIA_SHIFT); + cairo_translate(cr, r->x, r->y+1); + + trace("%s: %s",__FUNCTION__,(terminal->keyflags & KEY_FLAG_SHIFT) ? "Yes" : "No"); + + if(r->width > 2 && r->height > 7 && (terminal->keyflags & KEY_FLAG_SHIFT)) + { + int b,x,y,w,h,l; + int height = r->height-6; + + if(height > r->width) + { + w = r->width; + h = w*1.5; + } + else // width > height + { + h = height; + w = h/1.5; + } + + // Set image position + x = (r->width - w)/2; + y = (height - h)/2; + l = (w/3); + b = y+(w/1.5); + + cairo_move_to(cr,x+(w/2),y); + cairo_line_to(cr,x+w,b); + cairo_line_to(cr,(x+w)-l,b); + cairo_line_to(cr,(x+w)-l,y+h); + cairo_line_to(cr,x+l,y+h); + cairo_line_to(cr,x+l,b); + cairo_line_to(cr,x,b); + cairo_close_path(cr); + + cairo_stroke(cr); + + } + + cairo_destroy(cr); + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),r->x,r->y,r->width,r->height); + +} + +static void update_text_field(v3270 *terminal, gboolean flag, V3270_OIA_FIELD id, const gchar *text) +{ + GdkRectangle *r; + cairo_t *cr; + + if(!terminal->surface) + return; + + cr = set_update_region(terminal,&r,id); + cairo_translate(cr, r->x, r->y); + + if(flag) + { + cairo_move_to(cr,0,terminal->metrics.height); + cairo_show_text(cr, text); + cairo_stroke(cr); + } + + cairo_destroy(cr); + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),r->x,r->y,r->width,r->height); +} + +void v3270_draw_alt_status(v3270 *terminal) +{ + update_text_field(terminal,terminal->keyflags & KEY_FLAG_ALT,V3270_OIA_ALT,"A"); +} + +void v3270_draw_ins_status(v3270 *terminal) +{ + GdkRectangle *r; + cairo_t *cr; + + if(!terminal->surface) + return; + + cr = set_update_region(terminal,&r,V3270_OIA_INSERT); + + draw_insert(cr,terminal->host,terminal->color,r); + + cairo_destroy(cr); + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),r->x,r->y,r->width,r->height); + +} + +static gboolean update_timer(struct timer_info *info) +{ + cairo_t * cr; + time_t now = time(0); + GdkRectangle * rect; + + if(!info->terminal->surface) + return TRUE; + + cr = cairo_create(info->terminal->surface); + + if(now != info->last) + { + time_t seconds = now - info->start; + char buffer[7]; + + rect = info->terminal->oia_rect + V3270_OIA_TIMER; + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); +#else + gdk_cairo_set_source_color(cr,info->terminal->color+V3270_COLOR_OIA_BACKGROUND); +#endif + + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + + gdk_cairo_set_source_color(cr,info->terminal->color+V3270_COLOR_OIA_FOREGROUND); + + short2string(buffer,seconds/60,2); + buffer[2] = ':'; + short2string(buffer+3,seconds%60,2); + buffer[5] = 0; + + cairo_set_scaled_font(cr,info->terminal->font_scaled); + cairo_move_to(cr,rect->x,rect->y+info->terminal->metrics.height); + cairo_show_text(cr, buffer); + cairo_stroke(cr); + + info->last = now; + gtk_widget_queue_draw_area(GTK_WIDGET(info->terminal),rect->x,rect->y,rect->width,rect->height); + } + +#ifdef HAVE_LIBM + rect = info->terminal->oia_rect + V3270_OIA_SPINNER; + +#ifdef DEBUG + cairo_set_source_rgb(cr,0.1,0.1,0.1); +#else + gdk_cairo_set_source_color(cr,info->terminal->color+V3270_COLOR_OIA_BACKGROUND); +#endif + cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); + cairo_fill(cr); + + gdk_cairo_set_source_color(cr,info->terminal->color+V3270_COLOR_OIA_FOREGROUND); + + info->step = draw_spinner(cr, rect, info->terminal->color, info->step); + gtk_widget_queue_draw_area(GTK_WIDGET(info->terminal),rect->x,rect->y,rect->width,rect->height); +#endif // HAVE_LIBM + + cairo_destroy(cr); + + return TRUE; +} + +void v3270_start_timer(GtkWidget *widget) +{ + struct timer_info *info; + v3270 *terminal = GTK_V3270(widget); + + if(terminal->timer) + { + g_source_ref(terminal->timer); + return; + } + + info = g_new0(struct timer_info,1); + info->terminal = terminal; + info->start = time(0); + + terminal->timer = g_timeout_source_new(100); + g_source_set_callback(terminal->timer,(GSourceFunc) update_timer, info, (GDestroyNotify) release_timer); + + g_source_attach(terminal->timer, NULL); + g_source_unref(terminal->timer); + + trace("Timer %p starts",info); +} + +void v3270_stop_timer(GtkWidget *widget) +{ + v3270 *terminal = GTK_V3270(widget); + + if(!terminal->timer) + return; + +// trace("Timer=%p",terminal->timer); + if(terminal->timer->ref_count < 2) + g_source_destroy(terminal->timer); + + if(terminal->timer) + g_source_unref(terminal->timer); + +} + +void v3270_update_oia(H3270 *session, LIB3270_FLAG id, unsigned char on) +{ + cairo_t *cr; + GdkRectangle *r; + + v3270 *terminal = GTK_V3270(session->widget); + + if(!terminal->surface) + return; + + switch(id) + { + case LIB3270_FLAG_BOXSOLID: + cr = set_update_region(terminal,&r,V3270_OIA_CONNECTION); + v3270_draw_connection(cr,terminal->host,&terminal->metrics,terminal->color,r); + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),r->x,r->y,r->width,r->height); + cairo_destroy(cr); + break; + + case LIB3270_FLAG_UNDERA: + cr = set_update_region(terminal,&r,V3270_OIA_UNDERA); + draw_undera(cr,terminal->host,&terminal->metrics,terminal->color,r); + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),r->x,r->y,r->width,r->height); + cairo_destroy(cr); + break; + + case LIB3270_FLAG_SECURE: + cr = set_update_region(terminal,&r,V3270_OIA_SSL); + v3270_draw_ssl_status(cr,terminal->host,&terminal->metrics,terminal->color,r); + gtk_widget_queue_draw_area(GTK_WIDGET(terminal),r->x,r->y,r->width,r->height); + cairo_destroy(cr); + break; + + case LIB3270_FLAG_TYPEAHEAD: + update_text_field(terminal,on,V3270_OIA_TYPEAHEAD,"T"); + break; + +#if defined(LIB3270_FLAG_PRINTER) && defined(X3270_PRINTER) + case LIB3270_FLAG_PRINTER: + update_text_field(terminal,on,V3270_OIA_PRINTER,"P"); + break; +#endif // LIB3270_FLAG_PRINTER + + default: + return; + } +} diff --git a/private.h b/private.h new file mode 100644 index 0000000..49024ff --- /dev/null +++ b/private.h @@ -0,0 +1,236 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como private.h e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * licinio@bb.com.br (Licínio Luis Branco) + * kraucer@bb.com.br (Kraucer Fernandes Mazuco) + * + */ + +#include + +#define ENABLE_NLS +#define GETTEXT_PACKAGE PACKAGE_NAME + +#include +#include + +#ifndef V3270_H_INCLUDED + #include "v3270.h" +#endif + +G_BEGIN_DECLS + + + struct _v3270Class + { + GtkWidgetClass parent_class; + + /* Signals */ + void (*activate)(GtkWidget *widget); + void (*toggle_changed)(v3270 *widget,LIB3270_TOGGLE toggle_id,gboolean toggle_state,const gchar *toggle_name); + void (*message_changed)(v3270 *widget, LIB3270_MESSAGE id); + void (*luname_changed)(GtkWidget *widget,const gchar *luname); + gboolean (*keypress)(GtkWidget *widget,guint keyval,GdkModifierType state); + + }; + +/*--[ Defines]---------------------------------------------------------------------------------------*/ + + #define OIA_TOP_MARGIN 2 + + #define KEY_FLAG_SHIFT 0x0001 + #define KEY_FLAG_ALT 0x0002 + + enum + { + SIGNAL_TOGGLE_CHANGED, + SIGNAL_MESSAGE_CHANGED, + SIGNAL_LUNAME_CHANGED, + SIGNAL_KEYPRESS, + SIGNAL_CONNECTED, + SIGNAL_DISCONNECTED, + SIGNAL_UPDATE_CONFIG, + SIGNAL_MODEL_CHANGED, + SIGNAL_SELECTING, + SIGNAL_POPUP, + SIGNAL_PASTENEXT, + SIGNAL_CLIPBOARD, + SIGNAL_CHANGED, + + LAST_SIGNAL + }; + +/*--[ Globals ]--------------------------------------------------------------------------------------*/ + + #define V3270_CURSOR_UNPROTECTED LIB3270_CURSOR_EDITABLE + #define V3270_CURSOR_WAITING LIB3270_CURSOR_WAITING + #define V3270_CURSOR_LOCKED LIB3270_CURSOR_LOCKED + + #define V3270_CURSOR_PROTECTED LIB3270_CURSOR_USER + #define V3270_CURSOR_MOVE_SELECTION LIB3270_CURSOR_USER+1 + #define V3270_CURSOR_SELECTION_TOP_LEFT LIB3270_CURSOR_USER+2 + #define V3270_CURSOR_SELECTION_TOP_RIGHT LIB3270_CURSOR_USER+3 + #define V3270_CURSOR_SELECTION_TOP LIB3270_CURSOR_USER+4 + #define V3270_CURSOR_SELECTION_BOTTOM_LEFT LIB3270_CURSOR_USER+5 + #define V3270_CURSOR_SELECTION_BOTTOM_RIGHT LIB3270_CURSOR_USER+6 + #define V3270_CURSOR_SELECTION_BOTTOM LIB3270_CURSOR_USER+7 + #define V3270_CURSOR_SELECTION_LEFT LIB3270_CURSOR_USER+8 + #define V3270_CURSOR_SELECTION_RIGHT LIB3270_CURSOR_USER+9 + + #define V3270_CURSOR_COUNT LIB3270_CURSOR_USER+10 + + + struct v3270_metrics + { + guint width; + guint height; + guint ascent; + guint descent; + + guint spacing; + + guint left; + guint top; + }; + +/*--[ Widget data ]----------------------------------------------------------------------------------*/ + + struct _v3270 + { + GtkWidget parent; + + // flags + int selecting : 1; /**< Selecting region */ + int moving : 1; /**< Moving selected region */ + int resizing : 1; /**< Resizing selected region */ + int table : 1; /**< Copy mode is table */ + +#if GTK_CHECK_VERSION(3,0,0) + +#else + gint width; + gint height; +#endif // GTK_CHECK_VERSION(3,0,0) + + GSource * timer; + GtkIMContext * input_method; + unsigned short keyflags; + gchar * clipboard; /**< Clipboard contents (text only) */ + + LIB3270_CURSOR pointer_id; + unsigned char pointer; /** Mouse pointer ID */ + int selection_addr; /** Selection addr */ + + // Font info + gchar * font_family; + cairo_font_weight_t font_weight; + cairo_scaled_font_t * font_scaled; + cairo_surface_t * surface; + + struct v3270_metrics metrics; + + gint minimum_width; + gint minimum_height; + + // Colors + GdkColor color[V3270_COLOR_COUNT]; /**< Terminal widget colors */ + + // Regions + GdkRectangle oia_rect[V3270_OIA_FIELD_COUNT]; + + struct + { + unsigned char show; /**< Cursor flag */ + unsigned char chr; /**< Char at cursor position */ + unsigned short attr; /**< Attribute at cursor position */ + GdkRectangle rect; /**< Cursor rectangle */ + GSource * timer; /**< Cursor blinking timer */ + cairo_surface_t * surface; /**< Cursor image */ + } cursor; + + // Acessibility + GtkAccessible * accessible; + + // lib3270 stuff + H3270 * host; /**< Related 3270 session */ + + }; + +/*--[ Globals ]--------------------------------------------------------------------------------------*/ + + G_GNUC_INTERNAL guint v3270_widget_signal[LAST_SIGNAL]; + G_GNUC_INTERNAL GdkCursor * v3270_cursor[V3270_CURSOR_COUNT]; + +/*--[ Prototipes ]-----------------------------------------------------------------------------------*/ + +const GtkWidgetClass * v3270_get_parent_class(void); + +gboolean v3270_draw(GtkWidget * widget, cairo_t * cr); +void v3270_draw_oia(cairo_t *cr, H3270 *host, int row, int cols, struct v3270_metrics *metrics, GdkColor *color, GdkRectangle *rect); +void v3270_update_mouse_pointer(GtkWidget *widget); + +#if ! GTK_CHECK_VERSION(3,0,0) + gboolean v3270_expose(GtkWidget * widget, GdkEventExpose *event); +#endif // GTK 3 + +void v3270_draw_shift_status(v3270 *terminal); +void v3270_draw_alt_status(v3270 *terminal); +void v3270_draw_ins_status(v3270 *terminal); + +void v3270_update_cursor_surface(v3270 *widget,unsigned char chr,unsigned short attr); + +void v3270_register_io_handlers(v3270Class *cls); + +void v3270_draw_char(cairo_t *cr, unsigned char chr, unsigned short attr, H3270 *session, guint height, GdkRectangle *rect, GdkColor *fg, GdkColor *bg); + +void v3270_start_timer(GtkWidget *terminal); +void v3270_stop_timer(GtkWidget *terminal); + +void v3270_draw_connection(cairo_t *cr, H3270 *host, struct v3270_metrics *metrics, GdkColor *color, GdkRectangle *rect); +void v3270_draw_ssl_status(cairo_t *cr, H3270 *host, struct v3270_metrics *metrics, GdkColor *color, GdkRectangle *rect); + +void v3270_update_char(H3270 *session, int addr, unsigned char chr, unsigned short attr, unsigned char cursor); + +void v3270_update_font_metrics(v3270 *terminal, cairo_t *cr, int width, int height); + +void v3270_update_cursor_rect(v3270 *widget, GdkRectangle *rect, unsigned char chr, unsigned short attr); + +void v3270_update_luname(GtkWidget *widget,const gchar *name); +void v3270_update_message(v3270 *widget, LIB3270_MESSAGE id); +void v3270_update_cursor(H3270 *session, unsigned short row, unsigned short col, unsigned char c, unsigned short attr); +void v3270_update_oia(H3270 *session, LIB3270_FLAG id, unsigned char on); + +// Keyboard & Mouse +gboolean v3270_key_press_event(GtkWidget *widget, GdkEventKey *event); +gboolean v3270_key_release_event(GtkWidget *widget, GdkEventKey *event); +void v3270_key_commit(GtkIMContext *imcontext, gchar *str, v3270 *widget); +gboolean v3270_button_press_event(GtkWidget *widget, GdkEventButton *event); +gboolean v3270_button_release_event(GtkWidget *widget, GdkEventButton*event); +gboolean v3270_motion_notify_event(GtkWidget *widget, GdkEventMotion *event); +void v3270_emit_popup(v3270 *widget, int baddr, GdkEventButton *event); +gint v3270_get_offset_at_point(v3270 *widget, gint x, gint y); + +G_END_DECLS diff --git a/selection.c b/selection.c new file mode 100644 index 0000000..b73f211 --- /dev/null +++ b/selection.c @@ -0,0 +1,476 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como clipboard.c e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * + */ + + #include + #include + #include + #include "v3270.h" + #include "private.h" + #include + +/*--[ Globals ]--------------------------------------------------------------------------------------*/ + + enum + { + CLIPBOARD_TYPE_TEXT, + }; + + static const GtkTargetEntry targets[] = + { + { "COMPOUND_TEXT", 0, CLIPBOARD_TYPE_TEXT }, + { "UTF8_STRING", 0, CLIPBOARD_TYPE_TEXT }, + }; + +/*--[ Implement ]------------------------------------------------------------------------------------*/ + +static void clipboard_clear(GtkClipboard *clipboard, GObject *obj) +{ + trace("%s widget=%p",__FUNCTION__,obj); + +} + +static void clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection, guint target, GObject *obj) +{ + v3270 * widget = GTK_V3270(obj); + + trace("%s: widget=%p target=\"%s\"",__FUNCTION__,obj,targets[target].target); + + switch(target) + { + case CLIPBOARD_TYPE_TEXT: + if(!widget->clipboard) + lib3270_ring_bell(widget->host); + else + gtk_selection_data_set_text(selection,widget->clipboard,-1); + break; + + default: + g_warning("Unexpected clipboard type %d\n",target); + } +} + +gchar * v3270_get_text(GtkWidget *widget, int offset, int len) +{ + v3270 * terminal; + gchar * text; + char * str; + + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + + terminal = GTK_V3270(widget); + + str = lib3270_get_text(terminal->host, offset, len); + + if(!str) + return NULL; + + text = g_convert(str, -1, "UTF-8", lib3270_get_charset(terminal->host), NULL, NULL, NULL); + + free(str); + return text; +} + +/** + * Get lib3270 selection as a g_malloc buffer. + * + * @param widget Widget containing the desired section. + * + * @return NULL if error, otherwise the selected buffer contents (release with g_free). + * + */ +static gchar * v3270_get_selected(v3270 *widget) +{ + gchar *text = lib3270_get_selected(widget->host); + if(text) + { + gchar *str = g_strdup(text); + free(text); + return str; + } + return NULL; +} + +const gchar * v3270_get_selected_text(GtkWidget *widget) +{ + v3270 *terminal; + gchar *text; + + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + + terminal = GTK_V3270(widget); + + if(terminal->clipboard) + { + g_free(terminal->clipboard); + terminal->clipboard = NULL; + } + + text = v3270_get_selected(terminal); + + if(!text) + { + g_signal_emit(widget,v3270_widget_signal[SIGNAL_CLIPBOARD], 0, FALSE); + lib3270_ring_bell(terminal->host); + return NULL; + } + + if(terminal->table) + { + // Convert text to table + gchar **ln = g_strsplit(text,"\n",-1); + int width = lib3270_get_width(terminal->host); + gboolean cols[width]; + int l; + GString * buffer; + + memset(cols,0,sizeof(gboolean)*width); + + // Find column delimiters + for(l=0;ln[l];l++) + { + int c; + gchar * ptr = ln[l]; + GString * buffer; + + for(c=0;cclipboard = g_convert(text, -1, "UTF-8", lib3270_get_charset(terminal->host), NULL, NULL, NULL); + + g_free(text); + + + return terminal->clipboard; +} + +const gchar * v3270_get_copy(GtkWidget *widget) +{ + v3270 *terminal; + + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + + terminal = GTK_V3270(widget); + + return terminal->clipboard; +} + +const gchar * v3270_copy_append(GtkWidget *widget) +{ + v3270 * terminal; + char * str; + gchar * text; + gchar * clip; + + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + + terminal = GTK_V3270(widget); + + if(!terminal->clipboard) + return v3270_get_selected_text(widget); + + str = lib3270_get_selected(terminal->host); + + if(!str) + return terminal->clipboard; + + text = g_convert(str, -1, "UTF-8", lib3270_get_charset(terminal->host), NULL, NULL, NULL); + + free(str); + + clip = g_strconcat(terminal->clipboard,"\n",text,NULL); + + g_free(text); + g_free(terminal->clipboard); + + terminal->clipboard = clip; + + gtk_clipboard_set_text(gtk_widget_get_clipboard(widget,GDK_SELECTION_CLIPBOARD),terminal->clipboard,-1); + + g_signal_emit(widget,v3270_widget_signal[SIGNAL_CLIPBOARD], 0, TRUE); + + return terminal->clipboard; +} + +const gchar * v3270_copy(GtkWidget *widget, V3270_SELECT_FORMAT mode) +{ + const gchar * text; + GtkClipboard * clipboard = gtk_widget_get_clipboard(widget,GDK_SELECTION_CLIPBOARD); + + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + + GTK_V3270(widget)->table = (mode == V3270_SELECT_TABLE ? 1 : 0); + + text = v3270_get_selected_text(widget); + + if(text) + { + if(gtk_clipboard_set_with_owner( clipboard, + targets, + G_N_ELEMENTS(targets), + (GtkClipboardGetFunc) clipboard_get, + (GtkClipboardClearFunc) clipboard_clear, + G_OBJECT(widget) + )) + { + gtk_clipboard_set_can_store(clipboard,targets,1); + } + + g_signal_emit(widget,v3270_widget_signal[SIGNAL_CLIPBOARD], 0, TRUE); + } + + return text; + +} + +void v3270_paste_string(GtkWidget *widget, const gchar *text, const gchar *encoding) +{ + gchar * buffer = NULL; + H3270 * session = v3270_get_session(widget); + const gchar * charset = lib3270_get_charset(session); + gboolean next; + + if(!text) + return; + else if(g_strcasecmp(encoding,charset)) + buffer = g_convert(text, -1, charset, encoding, NULL, NULL, NULL); + else + buffer = g_strdup(text); + + if(!buffer) + { + /* Conversion failed, update special chars and try again */ + int f; + + static const struct _xlat + { + const gchar *from; + const gchar *to; + } xlat[] = + { + { "–", "-" }, + { "→", "->" }, + { "←", "<-" }, + { "©", "(c)" }, + { "↔", "<->" }, + { "™", "(TM)" }, + { "®", "(R)" }, + { "“", "\"" }, + { "”", "\"" }, + { "…", "..." }, + { "•", "*" }, + { "․", "." }, + { "·", "*" }, + + }; + + gchar *string = g_strdup(text); + + // FIXME (perry#1#): Is there any better way for a "sed" here? + for(f=0;fmessage, ln[f]); + + gtk_dialog_run(GTK_DIALOG (dialog)); + gtk_widget_destroy(dialog); + + break; + } + else + { + g_free(str); + } + + } + g_strfreev(ln); + + } + + g_free(string); + } + + if(buffer) + { + /* Remove TABS */ + gchar *ptr; + + for(ptr = buffer;*ptr;ptr++) + { + if(*ptr == '\t') + *ptr = ' '; + } + } + else + { + g_signal_emit(widget,v3270_widget_signal[SIGNAL_PASTENEXT], 0, FALSE); + return; + } + + next = lib3270_paste(session,(unsigned char *) buffer) ? TRUE : FALSE; + + trace("Pastenext is %s",next ? "On" : "Off"); + + g_free(buffer); + + g_signal_emit(widget,v3270_widget_signal[SIGNAL_PASTENEXT], 0, next); + +} + +static void text_received(GtkClipboard *clipboard, const gchar *text, GtkWidget *widget) +{ + v3270_paste_string(widget,text,"UTF-8"); +} + +void v3270_paste(GtkWidget *widget) +{ + gtk_clipboard_request_text(gtk_widget_get_clipboard(widget,GDK_SELECTION_CLIPBOARD),(GtkClipboardTextReceivedFunc) text_received,(gpointer) widget); +} + +void v3270_unselect(GtkWidget *widget) +{ + lib3270_unselect(v3270_get_session(widget)); +} + +gboolean v3270_get_selection_bounds(GtkWidget *widget, gint *start, gint *end) +{ + g_return_val_if_fail(GTK_IS_V3270(widget),FALSE); + return lib3270_get_selection_bounds(GTK_V3270(widget)->host,start,end) == 0 ? FALSE : TRUE; +} + +gchar * v3270_get_region(GtkWidget *widget, gint start_pos, gint end_pos, gboolean all) +{ + char * str; + gchar * utftext; + + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + + str = lib3270_get_region(GTK_V3270(widget)->host,start_pos,end_pos,all); + if(!str) + return NULL; + + utftext = g_convert(str, -1, "UTF-8", lib3270_get_charset(GTK_V3270(widget)->host), NULL, NULL, NULL); + + free(str); + + return utftext; +} + + void v3270_select_region(GtkWidget *widget, gint start, gint end) + { + g_return_if_fail(GTK_IS_V3270(widget)); + lib3270_select_region(GTK_V3270(widget)->host,start,end); + } + + diff --git a/sources.mak b/sources.mak new file mode 100644 index 0000000..f564843 --- /dev/null +++ b/sources.mak @@ -0,0 +1,2 @@ +V3270_SRC=marshal.c widget.c oia.c iocallback.c keyboard.c draw.c mouse.c selection.c accessible.c + diff --git a/unlocked.xbm b/unlocked.xbm new file mode 100644 index 0000000..6dcf3ee --- /dev/null +++ b/unlocked.xbm @@ -0,0 +1,14 @@ +#define unlocked_width 32 +#define unlocked_height 32 +static unsigned char unlocked_bits[] = { + 0x00, 0x00, 0xff, 0x0f, 0x00, 0x80, 0xff, 0x1f, 0x00, 0xc0, 0x01, 0x38, + 0x00, 0xe0, 0x00, 0x70, 0x00, 0x70, 0xfc, 0xe3, 0x00, 0x30, 0xfe, 0xc7, + 0x00, 0x30, 0x07, 0xce, 0x00, 0x30, 0x03, 0xcc, 0x00, 0x30, 0x03, 0xcc, + 0x00, 0x30, 0x03, 0xcc, 0x00, 0x30, 0x03, 0xcc, 0x00, 0x30, 0x03, 0xcc, + 0x00, 0x30, 0x03, 0xfc, 0x00, 0x30, 0x03, 0xfc, 0xff, 0xff, 0x3f, 0x00, + 0xff, 0xff, 0x3f, 0x00, 0x33, 0x33, 0x33, 0x00, 0x33, 0x33, 0x33, 0x00, + 0xcf, 0xcc, 0x3c, 0x00, 0xcf, 0xcc, 0x3c, 0x00, 0x33, 0x33, 0x33, 0x00, + 0x33, 0x33, 0x33, 0x00, 0xcf, 0xcc, 0x3c, 0x00, 0xcf, 0xcc, 0x3c, 0x00, + 0x33, 0x33, 0x33, 0x00, 0x33, 0x33, 0x33, 0x00, 0xcf, 0xcc, 0x3c, 0x00, + 0xcf, 0xcc, 0x3c, 0x00, 0x33, 0x33, 0x33, 0x00, 0x33, 0x33, 0x33, 0x00, + 0xff, 0xff, 0x3f, 0x00, 0xff, 0xff, 0x3f, 0x00 }; diff --git a/v3270.h b/v3270.h new file mode 100644 index 0000000..fb39726 --- /dev/null +++ b/v3270.h @@ -0,0 +1,190 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como v3270.h e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * licinio@bb.com.br (Licínio Luis Branco) + * kraucer@bb.com.br (Kraucer Fernandes Mazuco) + * + */ + +#ifndef V3270_H_INCLUDED + + #include + #include + + #define V3270_H_INCLUDED 1 + + G_BEGIN_DECLS + + #define GTK_TYPE_V3270 (v3270_get_type ()) + #define GTK_V3270(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_V3270, v3270)) + #define GTK_V3270_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_V3270, v3270Class)) + #define GTK_IS_V3270(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_V3270)) + #define GTK_IS_V3270_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_V3270)) + #define GTK_V3270_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_V3270, v3270Class)) + + + typedef struct _v3270 v3270; + typedef struct _v3270Class v3270Class; + + enum V3270_COLOR + { + V3270_COLOR_BACKGROUND, + V3270_COLOR_BLUE, + V3270_COLOR_RED, + V3270_COLOR_PINK, + V3270_COLOR_GREEN, + V3270_COLOR_TURQUOISE, + V3270_COLOR_YELLOW, + V3270_COLOR_WHITE, + V3270_COLOR_BLACK, + V3270_COLOR_DARK_BLUE, + V3270_COLOR_ORANGE, + V3270_COLOR_PURPLE, + V3270_COLOR_DARK_GREEN, + V3270_COLOR_DARK_TURQUOISE, + V3270_COLOR_MUSTARD, + V3270_COLOR_GRAY, + + V3270_COLOR_FIELD, + V3270_COLOR_FIELD_INTENSIFIED, + V3270_COLOR_FIELD_PROTECTED, + V3270_COLOR_FIELD_PROTECTED_INTENSIFIED, + + V3270_COLOR_SELECTED_BG, + V3270_COLOR_SELECTED_FG, + + V3270_COLOR_CROSS_HAIR, + + // Oia Colors (Must be the last block) + V3270_COLOR_OIA_BACKGROUND, + V3270_COLOR_OIA_FOREGROUND, + V3270_COLOR_OIA_SEPARATOR, + V3270_COLOR_OIA_STATUS_OK, + V3270_COLOR_OIA_STATUS_INVALID, + + V3270_COLOR_COUNT + }; + + #define V3270_COLOR_OIA_STATUS_WARNING V3270_COLOR_OIA_STATUS_OK + + typedef enum _v3270_oia_field + { + V3270_OIA_UNDERA, /**< "A" underlined */ + V3270_OIA_CONNECTION, /**< solid box if connected, "?" in a box if not */ + V3270_OIA_MESSAGE, /**< message area */ + V3270_OIA_SSL, /**< SSL Status */ + /**< Meta indication ("M" or blank) */ + V3270_OIA_ALT, /**< Alt indication ("A" or blank) */ + /**< Compose indication ("C" or blank) */ + /**< Compose first character */ + V3270_OIA_SHIFT, /**< Shift Status */ + V3270_OIA_TYPEAHEAD, /**< Typeahead indication ("T" or blank) */ + V3270_OIA_INSERT, /**< Insert mode indication (Special symbol/"I" or blank) */ + V3270_OIA_SCRIPT, /**< Script indication ("S" or blank) */ + V3270_OIA_LUNAME, /**< LU Name */ + V3270_OIA_SPINNER, /**< command timing spinner */ + V3270_OIA_TIMER, /**< command timing (mmm:ss, or blank) */ + V3270_OIA_CURSOR_POSITION, /**< cursor position (rrr/ccc or blank) */ + +// V3270_OIA_CAPS, /**< Caps indication ("A" or blank) */ + +#ifdef X3270_PRINTER + V3270_OIA_PRINTER, /**< Printer indication ("P" or blank) */ +#endif // X3270_PRINTER + + V3270_OIA_FIELD_COUNT + + } V3270_OIA_FIELD; + + + #define V3270_COLOR_OIA_SPINNER V3270_COLOR_OIA_FOREGROUND + #define V3270_COLOR_OIA_LUNAME V3270_COLOR_OIA_FOREGROUND + #define V3270_COLOR_OIA_INSERT V3270_COLOR_OIA_FOREGROUND + + #ifndef v3270char + #define v3270char void + #endif // v3270_char + + GtkWidget * v3270_new(void); + GType v3270_get_type(void); + + void v3270_reload(GtkWidget * widget); + + void v3270_set_font_family(GtkWidget *widget, const gchar *name); + const gchar * v3270_get_font_family(GtkWidget *widget); + + H3270 * v3270_get_session(GtkWidget *widget); + + int v3270_connect(GtkWidget *widget, const gchar *host); + void v3270_disconnect(GtkWidget *widget); + + // Clipboard + typedef enum _v3270_select_format + { + V3270_SELECT_TEXT, + V3270_SELECT_TABLE, + + V3270_SELECT_MAX + } V3270_SELECT_FORMAT; + + const gchar * v3270_copy(GtkWidget *widget, V3270_SELECT_FORMAT mode); + const gchar * v3270_copy_append(GtkWidget *widget); + + const gchar * v3270_get_selected_text(GtkWidget *widget); + const gchar * v3270_get_copy(GtkWidget *widget); + gchar * v3270_get_text(GtkWidget *widget,int offset, int len); + gchar * v3270_get_region(GtkWidget *widget, gint start_pos, gint end_pos, gboolean all); + + void v3270_set_string(GtkWidget *widget, const gchar *str); + void v3270_tab(GtkWidget *widget); + void v3270_backtab(GtkWidget *widget); + + // Cut & Paste + gboolean v3270_get_selection_bounds(GtkWidget *widget, gint *start, gint *end); + void v3270_unselect(GtkWidget *widget); + void v3270_paste(GtkWidget *widget); + void v3270_paste_string(GtkWidget *widget, const gchar *text, const gchar *encoding); + void v3270_select_region(GtkWidget *widget, gint start, gint end); + + // Colors + void v3270_set_colors(GtkWidget *widget, const gchar *); + void v3270_set_color_table(GdkColor *table, const gchar *colors); + const GdkColor * v3270_get_color_table(GtkWidget *widget); + void v3270_set_mono_color_table(GdkColor *table, const gchar *fg, const gchar *bg); + void v3270_draw_element(cairo_t *cr, unsigned char chr, unsigned short attr, H3270 *session, guint height, GdkRectangle *rect, GdkColor *color); + void v3270_set_color(GtkWidget *widget, enum V3270_COLOR id, GdkColor *color); + GdkColor * v3270_get_color(GtkWidget *widget, enum V3270_COLOR id); + + // Misc + GtkIMContext * v3270_get_im_context(GtkWidget *widget); + gboolean v3270_get_toggle(GtkWidget *widget, LIB3270_TOGGLE ix); + + void v3270_set_host(GtkWidget *widget, const gchar *uri); + + +G_END_DECLS + +#endif // V3270_H_INCLUDED diff --git a/widget.c b/widget.c new file mode 100644 index 0000000..81878a0 --- /dev/null +++ b/widget.c @@ -0,0 +1,1221 @@ +/* + * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 + * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a + * aplicativos mainframe. Registro no INPI sob o nome G3270. + * + * Copyright (C) <2008> + * + * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob + * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela + * Free Software Foundation. + * + * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER + * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO + * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para + * obter mais detalhes. + * + * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este + * programa; se não, escreva para a Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA, 02111-1307, USA + * + * Este programa está nomeado como widget.c e possui - linhas de código. + * + * Contatos: + * + * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) + * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) + * + */ + + #include + #include + #include + #include + #include + #include + #include + #include "v3270.h" + #include "private.h" + #include "accessible.h" + #include "marshal.h" + + #define WIDTH_IN_PIXELS(terminal,x) (x * cols) + #define HEIGHT_IN_PIXELS(terminal,x) (x * (rows+1)) + + #define CONTENTS_WIDTH(terminal) (cols * terminal->metrics.width) + #define CONTENTS_HEIGHT(terminal) (((rows+1) * terminal->metrics.spacing)+OIA_TOP_MARGIN+2) + +/*--[ Widget definition ]----------------------------------------------------------------------------*/ + + G_DEFINE_TYPE(v3270, v3270, GTK_TYPE_WIDGET); + +/*--[ Globals ]--------------------------------------------------------------------------------------*/ + + guint v3270_widget_signal[LAST_SIGNAL] = { 0 }; + GdkCursor * v3270_cursor[V3270_CURSOR_COUNT] = { 0 }; + +/*--[ Prototipes ]-----------------------------------------------------------------------------------*/ + + // http://git.gnome.org/browse/gtk+/tree/gtk/gtkdrawingarea.c?h=gtk-3-0 + +static void v3270_realize ( GtkWidget * widget) ; +static void v3270_size_allocate ( GtkWidget * widget, + GtkAllocation * allocation ); +static void v3270_send_configure ( v3270 * terminal ); +static AtkObject * v3270_get_accessible ( GtkWidget * widget ); + +// Signals +static void v3270_activate (GtkWidget *widget); + +gboolean v3270_focus_in_event(GtkWidget *widget, GdkEventFocus *event); +gboolean v3270_focus_out_event(GtkWidget *widget, GdkEventFocus *event); + +#if GTK_CHECK_VERSION(3,0,0) + +static void v3270_destroy (GtkWidget * object); + +#else + +static void v3270_destroy (GtkObject * object); + +#endif // gtk3 + +/*--[ Implement ]------------------------------------------------------------------------------------*/ + +static void v3270_cursor_draw(v3270 *widget) +{ + int pos = lib3270_get_cursor_address(widget->host); + unsigned char c; + unsigned short attr; + + lib3270_get_contents(widget->host,pos,pos,&c,&attr); + v3270_update_cursor_surface(widget,c,attr); + gtk_widget_queue_draw_area( GTK_WIDGET(widget), + widget->cursor.rect.x,widget->cursor.rect.y, + widget->cursor.rect.width,widget->cursor.rect.height); + +} + +static void v3270_toggle_changed(v3270 *widget,LIB3270_TOGGLE toggle_id, gboolean toggle_state,const gchar *toggle_name) +{ + trace("%s: toggle %d (%s)=%s",__FUNCTION__,toggle_id,toggle_name,toggle_state ? "Yes" : "No"); + + switch(toggle_id) + { + case LIB3270_TOGGLE_CURSOR_POS: + case LIB3270_TOGGLE_CROSSHAIR: + v3270_reload(GTK_WIDGET(widget)); + gtk_widget_queue_draw(GTK_WIDGET(widget)); + break; + + case LIB3270_TOGGLE_CURSOR_BLINK: + widget->cursor.show |= 1; + break; + + case LIB3270_TOGGLE_INSERT: + v3270_draw_ins_status(widget); + v3270_cursor_draw(widget); + break; + + case LIB3270_TOGGLE_BOLD: + v3270_reload(GTK_WIDGET(widget)); + gtk_widget_queue_draw(GTK_WIDGET(widget)); + break; + + default: + return; + + } + +} + +static void loghandler(H3270 *session, const char *module, int rc, const char *fmt, va_list args) +{ + g_logv(module,rc ? G_LOG_LEVEL_WARNING : G_LOG_LEVEL_MESSAGE, fmt, args); +} + +static gboolean v3270_popup_menu(GtkWidget * widget) +{ + GdkEventButton event; + + memset(&event,0,sizeof(event)); + + event.time = gtk_get_current_event_time(); + event.button = 3; + event.type = GDK_BUTTON_PRESS; + + v3270_emit_popup( GTK_V3270(widget), + lib3270_get_cursor_address(GTK_V3270(widget)->host), + &event ); + + return TRUE; +} + +#if GTK_CHECK_VERSION(3,0,0) + +static GtkSizeRequestMode get_request_mode(GtkWidget *widget) +{ + int rows, cols; + + trace("%s",__FUNCTION__); + + lib3270_get_screen_size(GTK_V3270(widget)->host,&rows,&cols); + + return rows > cols ? GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT : GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; + +} + +void get_preferred_height(GtkWidget *widget, gint *minimum_height, gint *natural_height) +{ + int height = GTK_V3270(widget)->minimum_height; + + if(minimum_height) + *minimum_height = height ? height : 10; + + if(natural_height) + *natural_height = 400; + +} + +void get_preferred_width(GtkWidget *widget, gint *minimum_width, gint *natural_width) +{ + int width = GTK_V3270(widget)->minimum_width; + + if(minimum_width) + *minimum_width = width ? width : 10; + + if(natural_width) + *natural_width = 600; +} + +/* +void get_preferred_height_for_width(GtkWidget *widget, gint width, gint *minimum_height, gint *natural_height) +{ + trace("%s",__FUNCTION__); + + if(minimum_height) + *minimum_height = 10; + + if(natural_height) + *natural_height = 10; +} + +static void get_preferred_width_for_height(GtkWidget *widget,gint height, gint *minimum_width, gint *natural_width) +{ + trace("%s",__FUNCTION__); + + if(minimum_width) + *minimum_width = 10; + + if(natural_width) + *natural_width = 10; + +} +*/ + +#endif // GTK(3,0,0) + +static void v3270_class_init(v3270Class *klass) +{ + GObjectClass * gobject_class = G_OBJECT_CLASS(klass); + GtkWidgetClass * widget_class = GTK_WIDGET_CLASS(klass); + + lib3270_set_log_handler(loghandler); + + widget_class->realize = v3270_realize; + widget_class->size_allocate = v3270_size_allocate; + widget_class->key_press_event = v3270_key_press_event; + widget_class->key_release_event = v3270_key_release_event; + widget_class->focus_in_event = v3270_focus_in_event; + widget_class->focus_out_event = v3270_focus_out_event; + widget_class->button_press_event = v3270_button_press_event; + widget_class->button_release_event = v3270_button_release_event; + widget_class->motion_notify_event = v3270_motion_notify_event; + widget_class->popup_menu = v3270_popup_menu; + + /* Accessibility support */ + widget_class->get_accessible = v3270_get_accessible; + + klass->activate = v3270_activate; + klass->toggle_changed = v3270_toggle_changed; + klass->message_changed = v3270_update_message; + klass->luname_changed = v3270_update_luname; + +#if GTK_CHECK_VERSION(3,0,0) + + widget_class->get_preferred_height = get_preferred_height; + widget_class->get_preferred_width = get_preferred_width; +// widget_class->get_preferred_width_for_height = get_preferred_width_for_height; +// widget_class->get_preferred_height_for_width = get_preferred_height_for_width; + + widget_class->destroy = v3270_destroy; + widget_class->draw = v3270_draw; + +#else + + { + GtkObjectClass *object_class = (GtkObjectClass*) klass; + + object_class->destroy = v3270_destroy; + } + + widget_class->expose_event = v3270_expose; + + +#endif // GTK3 + + v3270_register_io_handlers(klass); + + // Cursors + { +#ifdef WIN32 + static const gchar * cr[V3270_CURSOR_COUNT] = + { + "ibeam", // V3270_CURSOR_UNPROTECTED + "wait", // V3270_CURSOR_WAITING + "arrow", // V3270_CURSOR_LOCKED + "arrow", // V3270_CURSOR_PROTECTED + "hand", // V3270_CURSOR_MOVE_SELECTION + "sizenwse", // V3270_CURSOR_SELECTION_TOP_LEFT + "sizenesw", // V3270_CURSOR_SELECTION_TOP_RIGHT + "sizens", // V3270_CURSOR_SELECTION_TOP + "sizenesw", // V3270_CURSOR_SELECTION_BOTTOM_LEFT + "sizenwse", // V3270_CURSOR_SELECTION_BOTTOM_RIGHT + "sizens", // V3270_CURSOR_SELECTION_BOTTOM + "sizewe", // V3270_CURSOR_SELECTION_LEFT + "sizewe", // V3270_CURSOR_SELECTION_RIGHT + }; +#else + static const int cr[V3270_CURSOR_COUNT] = + { + GDK_XTERM, // V3270_CURSOR_UNPROTECTED + GDK_WATCH, // V3270_CURSOR_WAITING + GDK_X_CURSOR, // V3270_CURSOR_LOCKED + GDK_ARROW, // V3270_CURSOR_PROTECTED + GDK_HAND1, // V3270_CURSOR_MOVE_SELECTION + GDK_TOP_LEFT_CORNER, // V3270_CURSOR_SELECTION_TOP_LEFT + GDK_TOP_RIGHT_CORNER, // V3270_CURSOR_SELECTION_TOP_RIGHT + GDK_TOP_SIDE, // V3270_CURSOR_SELECTION_TOP + GDK_BOTTOM_LEFT_CORNER, // V3270_CURSOR_SELECTION_BOTTOM_LEFT + GDK_BOTTOM_RIGHT_CORNER, // V3270_CURSOR_SELECTION_BOTTOM_RIGHT + GDK_BOTTOM_SIDE, // V3270_CURSOR_SELECTION_BOTTOM + GDK_LEFT_SIDE, // V3270_CURSOR_SELECTION_LEFT + GDK_RIGHT_SIDE, // V3270_CURSOR_SELECTION_RIGHT + }; +#endif // WIN32 + + int f; + + for(f=0;factivate_signal = + g_signal_new( "activate", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (v3270Class, activate), + NULL, NULL, + pw3270_VOID__VOID, + G_TYPE_NONE, 0); + + v3270_widget_signal[SIGNAL_TOGGLE_CHANGED] = + g_signal_new( "toggle_changed", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (v3270Class, toggle_changed), + NULL, NULL, + pw3270_VOID__VOID_ENUM_BOOL_POINTER, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_BOOLEAN, G_TYPE_STRING); + + v3270_widget_signal[SIGNAL_MESSAGE_CHANGED] = + g_signal_new( "message_changed", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (v3270Class, message_changed), + NULL, NULL, + pw3270_VOID__VOID_ENUM, + G_TYPE_NONE, 1, G_TYPE_UINT); + + v3270_widget_signal[SIGNAL_LUNAME_CHANGED] = + g_signal_new( "luname_changed", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (v3270Class, luname_changed), + NULL, NULL, + pw3270_VOID__VOID_POINTER, + G_TYPE_NONE, 1, G_TYPE_STRING); + + v3270_widget_signal[SIGNAL_KEYPRESS] = + g_signal_new( "keypress", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (v3270Class, keypress), + NULL, NULL, + pw3270_BOOL__VOID_UINT_ENUM, + G_TYPE_BOOLEAN, 2, G_TYPE_UINT, G_TYPE_UINT); + + v3270_widget_signal[SIGNAL_CONNECTED] = + g_signal_new( "connected", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + pw3270_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_STRING); + + v3270_widget_signal[SIGNAL_DISCONNECTED] = + g_signal_new( "disconnected", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + pw3270_VOID__VOID, + G_TYPE_NONE, 0); + + v3270_widget_signal[SIGNAL_UPDATE_CONFIG] = + g_signal_new( "update_config", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + pw3270_VOID__VOID_POINTER_POINTER, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); + + v3270_widget_signal[SIGNAL_MODEL_CHANGED] = + g_signal_new( "model_changed", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + pw3270_VOID__VOID_UINT_POINTER, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); + + v3270_widget_signal[SIGNAL_SELECTING] = + g_signal_new( "selecting", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + pw3270_VOID__VOID_BOOL, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + v3270_widget_signal[SIGNAL_POPUP] = + g_signal_new( "popup", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + pw3270_BOOL__VOID_BOOL_BOOL_POINTER, + G_TYPE_BOOLEAN, 3, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER); + + v3270_widget_signal[SIGNAL_PASTENEXT] = + g_signal_new( "pastenext", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + pw3270_VOID__VOID_BOOL, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + v3270_widget_signal[SIGNAL_CLIPBOARD] = + g_signal_new( "has_text", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + pw3270_VOID__VOID_BOOL, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + v3270_widget_signal[SIGNAL_CHANGED] = + g_signal_new( "changed", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + pw3270_VOID__VOID_UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + +} + +void v3270_update_font_metrics(v3270 *terminal, cairo_t *cr, int width, int height) +{ + // update font metrics + static const int font_size[] = { 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28, 32, 36, 40, 48, 56, 64, 72, 0 }; + int f, rows, cols, hFont; + int size = font_size[0]; + + cairo_font_extents_t extents; + + lib3270_get_screen_size(terminal->host,&rows,&cols); + + terminal->font_weight = lib3270_get_toggle(terminal->host,LIB3270_TOGGLE_BOLD) ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL; + + cairo_select_font_face(cr, terminal->font_family, CAIRO_FONT_SLANT_NORMAL,terminal->font_weight); + + for(f=0;font_size[f];f++) + { + cairo_set_font_size(cr,font_size[f]); + cairo_font_extents(cr,&extents); + + if(f == 0) + { + terminal->minimum_width = (cols * extents.max_x_advance); + terminal->minimum_height = ((rows+1) * (extents.height + extents.descent)) + (OIA_TOP_MARGIN+2); + } + + if( HEIGHT_IN_PIXELS(terminal,(extents.height+extents.descent)) < height && WIDTH_IN_PIXELS(terminal,extents.max_x_advance) < width ) + size = font_size[f]; + } + + cairo_set_font_size(cr,size); + +#if !GTK_CHECK_VERSION(3,0,0) + gtk_widget_set_size_request(GTK_WIDGET(terminal),terminal->minimum_width,terminal->minimum_height); +#endif // !GTK(3,0,0) + +/* + double sx, sy; + cairo_matrix_t font_matrix; + + cairo_set_font_size(cr,10); + cairo_font_extents(cr,&extents); + + trace("font - extents.height=%f extents.width=%f",extents.height,extents.max_x_advance); + + sx = ((double) width) / (((double) terminal->cols) * extents.max_x_advance); + sy = ((double) height) / (((double) terminal->rows) * extents.height); + + trace("sy=%f sx=%f ",sy,sx); + + cairo_get_font_matrix(cr,&font_matrix); + cairo_matrix_scale(&font_matrix, sx, sy); + cairo_set_font_matrix(cr,&font_matrix); +*/ + + /* Save scaled font for use on next drawings */ + if(terminal->font_scaled) + cairo_scaled_font_destroy(terminal->font_scaled); + + terminal->font_scaled = cairo_get_scaled_font(cr); + cairo_scaled_font_reference(terminal->font_scaled); + + cairo_scaled_font_extents(terminal->font_scaled,&extents); + + terminal->metrics.width = (int) extents.max_x_advance; + terminal->metrics.height = (int) extents.height; + terminal->metrics.ascent = (int) extents.ascent; + terminal->metrics.descent = (int) extents.descent; + + hFont = terminal->metrics.height + terminal->metrics.descent; + + // Create new cursor surface + if(terminal->cursor.surface) + cairo_surface_destroy(terminal->cursor.surface); + + terminal->cursor.surface = gdk_window_create_similar_surface(gtk_widget_get_window(GTK_WIDGET(terminal)),CAIRO_CONTENT_COLOR,terminal->metrics.width,hFont); + + // Center image + size = CONTENTS_WIDTH(terminal); + terminal->metrics.left = (width >> 1) - ((size) >> 1); + + terminal->metrics.spacing = height / (rows+2); + if(terminal->metrics.spacing < hFont) + terminal->metrics.spacing = hFont; + + size = CONTENTS_HEIGHT(terminal); + + terminal->metrics.top = (height >> 1) - (size >> 1); + +} + +static void set_timer(H3270 *session, unsigned char on) +{ + GtkWidget *widget = GTK_WIDGET(session->widget); + + if(on) + v3270_start_timer(widget); + else + v3270_stop_timer(widget); + +} + +static void update_toggle(H3270 *session, LIB3270_TOGGLE ix, unsigned char value, LIB3270_TOGGLE_TYPE reason, const char *name) +{ + g_signal_emit(GTK_WIDGET(session->widget), v3270_widget_signal[SIGNAL_TOGGLE_CHANGED], 0, (guint) ix, (gboolean) (value != 0), (gchar *) name); +} + +static void update_message(H3270 *session, LIB3270_MESSAGE id) +{ + g_signal_emit(GTK_WIDGET(session->widget), v3270_widget_signal[SIGNAL_MESSAGE_CHANGED], 0, (gint) id); +} + +static void update_luname(H3270 *session, const char *name) +{ + g_signal_emit(GTK_WIDGET(session->widget), v3270_widget_signal[SIGNAL_LUNAME_CHANGED], 0, (gchar *) name); +} + +static void select_cursor(H3270 *session, LIB3270_CURSOR id) +{ + GtkWidget *widget = GTK_WIDGET(session->widget); + + if(gtk_widget_get_realized(widget) && gtk_widget_get_has_window(widget)) + { + GTK_V3270(widget)->pointer_id = id; + v3270_update_mouse_pointer(widget); + } +} + +static void ctlr_done(H3270 *session) +{ + GtkWidget *widget = GTK_WIDGET(session->widget); + + if(gtk_widget_get_realized(widget) && gtk_widget_get_has_window(widget)) + { + v3270_update_mouse_pointer(widget); + } + +} + +static void update_connect(H3270 *session, unsigned char connected) +{ + v3270 *widget = GTK_V3270(session->widget); + + if(connected) + { + widget->cursor.show |= 2; + g_signal_emit(GTK_WIDGET(widget), v3270_widget_signal[SIGNAL_CONNECTED], 0, session->full_current_host); + } + else + { + widget->cursor.show &= ~2; + g_signal_emit(GTK_WIDGET(widget), v3270_widget_signal[SIGNAL_DISCONNECTED], 0); + } + + gtk_widget_queue_draw(GTK_WIDGET(widget)); +} + +static void update_screen_size(H3270 *session,unsigned short rows, unsigned short cols) +{ +// trace("Widget %p changes to %dx%d",session->widget,cols,rows); + v3270_reload(GTK_WIDGET(session->widget)); + gtk_widget_queue_draw(GTK_WIDGET(session->widget)); +} + +static void update_model(H3270 *session, const char *name, int model, int rows, int cols) +{ + g_signal_emit(GTK_WIDGET(session->widget),v3270_widget_signal[SIGNAL_MODEL_CHANGED], 0, (guint) model, name); +} + +static void changed(H3270 *session, int offset, int len) +{ + GtkWidget * widget = session->widget; + GtkAccessible * obj = GTK_V3270(widget)->accessible; + + trace("%s: offset=%d len=%d",__FUNCTION__,offset,len) + + if(obj) + { + // Get new text, notify atk + gsize bytes_written = 0; + char * text = lib3270_get_text(session,offset,len); + + if(text) + { + GError * error = NULL; + gchar * utfchar = g_convert_with_fallback( text, + -1, + "UTF-8", + lib3270_get_charset(session), + " ", + NULL, + &bytes_written, + &error ); + + free(text); + + if(error) + { + g_warning("%s failed: %s",__FUNCTION__,error->message); + g_error_free(error); + } + + if(utfchar) + { + g_signal_emit_by_name(obj, "text-insert", offset, bytes_written, utfchar); + g_free(utfchar); + } + + } + } + + g_signal_emit(GTK_WIDGET(widget),v3270_widget_signal[SIGNAL_CHANGED], 0, (guint) offset, (guint) len); + +} + +static void set_selection(H3270 *session, unsigned char status) +{ + GtkWidget * widget = GTK_WIDGET(session->widget); + g_signal_emit(widget,v3270_widget_signal[SIGNAL_SELECTING], 0, status ? TRUE : FALSE); +} + +static void update_selection(H3270 *session, int start, int end) +{ + // Selected region changed + GtkWidget * widget = GTK_WIDGET(session->widget); + GtkAccessible * atk_obj = GTK_V3270(widget)->accessible; + + if(atk_obj) + g_signal_emit_by_name(atk_obj,"text-selection-changed"); + +} + +static void v3270_init(v3270 *widget) +{ + trace("%s",__FUNCTION__); + widget->host = lib3270_session_new(""); + + if(widget->host->sz != sizeof(H3270)) + { + g_error( _( "Unexpected signature in H3270 object, possible version mismatch in lib3270") ); + return; + } + + widget->host->widget = widget; + + widget->host->update = v3270_update_char; + widget->host->changed = changed; + widget->host->set_timer = set_timer; + + widget->host->set_selection = set_selection; + widget->host->update_selection = update_selection; + + widget->host->update_luname = update_luname; + widget->host->configure = update_screen_size; + widget->host->update_status = update_message; + widget->host->update_cursor = v3270_update_cursor; + widget->host->update_toggle = update_toggle; + widget->host->update_oia = v3270_update_oia; + widget->host->cursor = select_cursor; + widget->host->update_connect = update_connect; + widget->host->update_model = update_model; + widget->host->changed = changed; + widget->host->ctlr_done = ctlr_done; + + + // Setup input method + widget->input_method = gtk_im_multicontext_new(); + g_signal_connect(G_OBJECT(widget->input_method),"commit",G_CALLBACK(v3270_key_commit),widget); + + gtk_widget_set_can_default(GTK_WIDGET(widget),TRUE); + gtk_widget_set_can_focus(GTK_WIDGET(widget),TRUE); + + // Setup events + gtk_widget_add_events(GTK_WIDGET(widget),GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_MOTION_MASK|GDK_BUTTON_RELEASE_MASK|GDK_POINTER_MOTION_MASK|GDK_ENTER_NOTIFY_MASK); + +} + +GtkWidget * v3270_new(void) +{ + return g_object_new(GTK_TYPE_V3270, NULL); +} + +#if GTK_CHECK_VERSION(3,0,0) +static void v3270_destroy(GtkWidget *widget) +#else +static void v3270_destroy(GtkObject *widget) +#endif +{ + v3270 * terminal = GTK_V3270(widget); + + trace("%s %p",__FUNCTION__,widget); + + if(terminal->accessible) + { + gtk_accessible_set_widget(terminal->accessible, NULL); + g_object_unref(terminal->accessible); + terminal->accessible = NULL; + } + + if(terminal->host) + { + lib3270_session_free(terminal->host); + terminal->host = NULL; + } + + if(terminal->font_family) + { + g_free(terminal->font_family); + terminal->font_family = 0; + } + + if(terminal->font_scaled) + { + cairo_scaled_font_destroy(terminal->font_scaled); + terminal->font_scaled = NULL; + } + + if(terminal->surface) + { + cairo_surface_destroy(terminal->surface); + terminal->surface = NULL; + } + + if(terminal->cursor.surface) + { + cairo_surface_destroy(terminal->cursor.surface); + terminal->cursor.surface = NULL; + } + + if(terminal->timer) + { + g_source_destroy(terminal->timer); + while(terminal->timer) + g_source_unref(terminal->timer); + } + + if(terminal->cursor.timer) + { + g_source_destroy(terminal->cursor.timer); + while(terminal->cursor.timer) + g_source_unref(terminal->cursor.timer); + } + + if(terminal->input_method) + { + g_object_unref(terminal->input_method); + terminal->input_method = NULL; + } + + if(terminal->clipboard) + { + g_free(terminal->clipboard); + terminal->clipboard = NULL; + } + +#if GTK_CHECK_VERSION(3,0,0) + GTK_WIDGET_CLASS(v3270_parent_class)->destroy(widget); +#else + GTK_OBJECT_CLASS(v3270_parent_class)->destroy(widget); +#endif // GTK3 + +} + +static gboolean timer_tick(v3270 *widget) +{ + if(lib3270_get_toggle(widget->host,LIB3270_TOGGLE_CURSOR_BLINK)) + { + widget->cursor.show ^= 1; + gtk_widget_queue_draw_area(GTK_WIDGET(widget), widget->cursor.rect.x, + widget->cursor.rect.y, + widget->cursor.rect.width, + widget->cursor.rect.height ); + } + + return TRUE; +} + +static void release_timer(v3270 *widget) +{ + widget->cursor.timer = NULL; +} + +static void v3270_realize(GtkWidget * widget) +{ + if(!gtk_widget_get_has_window(widget)) + { + GTK_WIDGET_CLASS(v3270_parent_class)->realize(widget); + } + else + { + GtkAllocation allocation; + GdkWindow *window; + GdkWindowAttr attributes; + gint attributes_mask; + + + gtk_widget_set_realized (widget, TRUE); + + gtk_widget_get_allocation (widget, &allocation); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; + + window = gdk_window_new(gtk_widget_get_parent_window (widget),&attributes, attributes_mask); + gdk_window_set_user_data (window, widget); + gtk_widget_set_window(widget, window); + + gtk_im_context_set_client_window(GTK_V3270(widget)->input_method,window); + + } + +#if !GTK_CHECK_VERSION(3,0,0) + widget->style = gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); +#endif // !GTK3 + + v3270_reload(widget); + + v3270_send_configure(GTK_V3270(widget)); + + if(!GTK_V3270(widget)->cursor.timer) + { + // Setup cursor blink timer + v3270 *terminal = GTK_V3270(widget); + + terminal->cursor.timer = g_timeout_source_new(500); + g_source_set_callback(terminal->cursor.timer,(GSourceFunc) timer_tick, widget, (GDestroyNotify) release_timer); + + g_source_attach(terminal->cursor.timer, NULL); + g_source_unref(terminal->cursor.timer); + } + +} + +static void v3270_size_allocate(GtkWidget * widget, GtkAllocation * allocation) +{ + g_return_if_fail(GTK_IS_V3270(widget)); + g_return_if_fail(allocation != NULL); + +// trace("Widget size changes to %dx%d",allocation->width,allocation->height); + + gtk_widget_set_allocation(widget, allocation); + +#if !GTK_CHECK_VERSION(3,0,0) + { + v3270 *terminal = GTK_V3270(widget); + + terminal->width = allocation->width; + terminal->height = allocation->height; + } +#endif + + if(gtk_widget_get_realized(widget)) + { + if(gtk_widget_get_has_window(widget)) + gdk_window_move_resize(gtk_widget_get_window (widget),allocation->x, allocation->y,allocation->width, allocation->height); + + v3270_reload(widget); + v3270_send_configure(GTK_V3270(widget)); + } +} + +static void v3270_send_configure(v3270 * terminal) +{ + GtkAllocation allocation; + GtkWidget *widget; + GdkEvent *event = gdk_event_new(GDK_CONFIGURE); + + widget = GTK_WIDGET(terminal); + + gtk_widget_get_allocation(widget, &allocation); + + event->configure.window = g_object_ref(gtk_widget_get_window(widget)); + event->configure.send_event = TRUE; + event->configure.x = allocation.x; + event->configure.y = allocation.y; + event->configure.width = allocation.width; + event->configure.height = allocation.height; + +#if( !GTK_CHECK_VERSION(3,0,0)) + terminal->width = allocation.width; + terminal->height = allocation.height; +#endif + + gtk_widget_event(widget, event); + gdk_event_free(event); +} + +void v3270_set_colors(GtkWidget *widget, const gchar *colors) +{ + g_return_if_fail(GTK_IS_V3270(widget)); + + if(!colors) + { + colors = "#000000," // V3270_COLOR_BACKGROUND + "#7890F0," // V3270_COLOR_BLUE + "#FF0000," // V3270_COLOR_RED + "#FF00FF," // V3270_COLOR_PINK + "#00FF00," // V3270_COLOR_GREEN + "#00FFFF," // V3270_COLOR_TURQUOISE + "#FFFF00," // V3270_COLOR_YELLOW + "#FFFFFF," // V3270_COLOR_WHITE + "#000000," // V3270_COLOR_BLACK + "#000080," // V3270_COLOR_DARK_BLUE + "#FFA200," // V3270_COLOR_ORANGE + "#800080," // V3270_COLOR_PURPLE + "#008000," // V3270_COLOR_DARK_GREEN + "#008080," // V3270_COLOR_DARK_TURQUOISE + "#A0A000," // V3270_COLOR_MUSTARD + "#C0C0C0," // V3270_COLOR_GRAY + + "#00FF00," // V3270_COLOR_FIELD_DEFAULT + "#FF0000," // V3270_COLOR_FIELD_INTENSIFIED + "#00FFFF," // V3270_COLOR_FIELD_PROTECTED + "#FFFFFF," // V3270_COLOR_FIELD_PROTECTED_INTENSIFIED + + "#404040," // V3270_COLOR_SELECTED_BG + "#FFFFFF," // V3270_COLOR_SELECTED_FG, + + "#00FF00," // V3270_COLOR_CROSS_HAIR + + "#000000," // V3270_COLOR_OIA_BACKGROUND + "#00FF00," // V3270_COLOR_OIA + "#7890F0," // V3270_COLOR_OIA_SEPARATOR + "#FFFFFF," // V3270_COLOR_OIA_STATUS_OK + "#FF0000"; // V3270_COLOR_OIA_STATUS_INVALID + + } + + v3270_set_color_table(GTK_V3270(widget)->color,colors); + g_signal_emit(widget,v3270_widget_signal[SIGNAL_UPDATE_CONFIG], 0, "colors", colors); + v3270_reload(widget); + +} + +void v3270_set_color(GtkWidget *widget, enum V3270_COLOR id, GdkColor *color) +{ + g_return_if_fail(GTK_IS_V3270(widget)); + + GTK_V3270(widget)->color[id] = *color; + +#if !GTK_CHECK_VERSION(3,0,0) + gdk_colormap_alloc_color(gtk_widget_get_default_colormap(),color,TRUE,TRUE); +#endif // !GTK(3,0,0) + +} +GdkColor * v3270_get_color(GtkWidget *widget, enum V3270_COLOR id) +{ + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + return GTK_V3270(widget)->color+id; +} + +const GdkColor * v3270_get_color_table(GtkWidget *widget) +{ + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + return GTK_V3270(widget)->color; +} + +void v3270_set_mono_color_table(GdkColor *clr, const gchar *fg, const gchar *bg) +{ + int f; + + gdk_color_parse(bg,clr); + gdk_color_parse(fg,clr+1); + + for(f=2;ffont_family) + { + if(!g_strcasecmp(terminal->font_family,name)) + return; + g_free(terminal->font_family); + terminal->font_family = NULL; + } + + if(!name) + { + // TODO (perry#3#): Get default font family from currrent style + name = "courier new"; + } + + terminal->font_family = g_strdup(name); + terminal->font_weight = lib3270_get_toggle(terminal->host,LIB3270_TOGGLE_BOLD) ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL; + + trace("%s: %s (%p)",__FUNCTION__,terminal->font_family,terminal->font_family); + + g_signal_emit(widget,v3270_widget_signal[SIGNAL_UPDATE_CONFIG], 0, "font-family", name); + + v3270_reload(widget); + gtk_widget_queue_draw(widget); + + +} + +const gchar * v3270_get_font_family(GtkWidget *widget) +{ + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + return GTK_V3270(widget)->font_family; +} + +void v3270_disconnect(GtkWidget *widget) +{ + g_return_if_fail(GTK_IS_V3270(widget)); + lib3270_disconnect(GTK_V3270(widget)->host); +} + +H3270 * v3270_get_session(GtkWidget *widget) +{ + g_return_val_if_fail(GTK_IS_V3270(widget),NULL); + + return GTK_V3270(widget)->host; +} + +int v3270_connect(GtkWidget *widget, const gchar *host) +{ + v3270 * terminal; + int rc = -1; + + trace("%s widget=%p host=%p",__FUNCTION__,widget,host); + + g_return_val_if_fail(GTK_IS_V3270(widget),EINVAL); + + terminal = GTK_V3270(widget); + + rc = lib3270_connect(terminal->host,host,0); + + trace("%s exits with rc=%d (%s)",__FUNCTION__,rc,strerror(rc)); + + return rc; +} + +static gboolean notify_focus(GtkWidget *widget, GdkEventFocus *event) +{ + GtkAccessible *obj = GTK_V3270(widget)->accessible; + + if(obj) + g_signal_emit_by_name (obj, "focus-event", event->in); + + return FALSE; +} +gboolean v3270_focus_in_event(GtkWidget *widget, GdkEventFocus *event) +{ + v3270 * terminal = GTK_V3270(widget); + + gtk_im_context_focus_in(terminal->input_method); + + return notify_focus(widget,event); +} + +gboolean v3270_focus_out_event(GtkWidget *widget, GdkEventFocus *event) +{ + v3270 * terminal = GTK_V3270(widget); + + gtk_im_context_focus_out(terminal->input_method); + + return notify_focus(widget,event); +} + +static void v3270_activate(GtkWidget *widget) +{ + v3270 * terminal = GTK_V3270(widget); + + trace("%s: %p",__FUNCTION__,terminal); + + if(lib3270_connected(terminal->host)) + lib3270_enter(terminal->host); + else if(lib3270_get_host(terminal->host)) + v3270_connect(widget,NULL); + else + g_warning("Terminal widget %p activated without connection or valid hostname",terminal); +} + +const GtkWidgetClass * v3270_get_parent_class(void) +{ + return GTK_WIDGET_CLASS(v3270_parent_class); +} + +static AtkObject * v3270_get_accessible(GtkWidget * widget) +{ + v3270 * terminal = GTK_V3270(widget); + +// trace("%s acc=%p",__FUNCTION__,terminal->accessible); + + if(!terminal->accessible) + { + terminal->accessible = g_object_new(GTK_TYPE_V3270_ACCESSIBLE,NULL); + atk_object_initialize(ATK_OBJECT(terminal->accessible), widget); + gtk_accessible_set_widget(GTK_ACCESSIBLE(terminal->accessible),widget); + g_object_ref(terminal->accessible); + } + + return ATK_OBJECT(terminal->accessible); +} + +GtkIMContext * v3270_get_im_context(GtkWidget *widget) +{ + return GTK_V3270(widget)->input_method; +} + +gboolean v3270_get_toggle(GtkWidget *widget, LIB3270_TOGGLE ix) +{ + g_return_val_if_fail(GTK_IS_V3270(widget),FALSE); + + if(ix < LIB3270_TOGGLE_COUNT) + return lib3270_get_toggle(GTK_V3270(widget)->host,ix) ? TRUE : FALSE; + + return FALSE; +} + +void v3270_set_host(GtkWidget *widget, const gchar *uri) +{ + g_return_if_fail(GTK_IS_V3270(widget)); + g_return_if_fail(uri != NULL); + lib3270_set_host(GTK_V3270(widget)->host,uri); +} -- libgit2 0.21.2