note description: "Eiffel Vision widget. GTK implementation.%N% %See ev_widget.e" legal: "See notice at end of class." status: "See notice at end of class." date: "$Date$" revision: "$Revision$" deferred class EV_WIDGET_IMP inherit EV_WIDGET_I redefine interface end EV_PICK_AND_DROPABLE_IMP redefine interface, make, destroy, x_position, y_position end EV_SENSITIVE_IMP redefine interface end EV_COLORIZABLE_IMP redefine interface end EV_WIDGET_ACTION_SEQUENCES_I export {EV_INTERMEDIARY_ROUTINES} focus_in_actions_internal, focus_out_actions_internal, pointer_motion_actions_internal, pointer_button_release_actions, pointer_leave_actions, pointer_leave_actions_internal, pointer_enter_actions_internal end EV_DOCKABLE_SOURCE_IMP redefine interface end feature {NONE} -- Initialization make -- Show non window widgets. -- Initialize default options, colors and sizes. -- Connect action sequences to GTK signals. do Precursor {EV_PICK_AND_DROPABLE_IMP} set_is_initialized (True) end initialize_file_drop (a_widget: POINTER) external "C inline use " alias "[ GtkTargetEntry target_entry[1]; target_entry[0].target = "STRING"; target_entry[0].flags = 0; target_entry[0].info = 0; gtk_drag_dest_set ( (GtkWidget*) $a_widget, GTK_DEST_DEFAULT_DROP, target_entry, sizeof (target_entry) / sizeof (GtkTargetEntry), GDK_ACTION_LINK ); ]" end feature -- Event handling init_resize_actions (a_resize_actions: like resize_actions) -- local l_app_imp: like app_implementation do if not {GTK}.gtk_is_window (c_object) then -- Window resize events are connected separately l_app_imp := app_implementation l_app_imp.gtk_marshal.signal_connect (c_object, l_app_imp.size_allocate_event_string, agent (l_app_imp.gtk_marshal).on_size_allocate_intermediate (internal_id, ?, ?, ?, ?), l_app_imp.gtk_marshal.size_allocate_translate_agent, False) end end init_dpi_changed_actions (a_dpi_changed_actions: like dpi_changed_actions) do -- TODO end init_file_drop_actions (a_file_drop_actions: like file_drop_actions) -- do end feature {EV_WINDOW_IMP, EV_INTERMEDIARY_ROUTINES, EV_ANY_I, EV_APPLICATION_IMP} -- Implementation on_key_event (a_key: detachable EV_KEY; a_key_string: detachable STRING_32; a_key_press: BOOLEAN) -- Used for key event actions sequences. do if a_key_press then if a_key /= Void and then key_press_actions_internal /= Void then key_press_actions_internal.call ([a_key]) end if key_press_string_actions_internal /= Void then if a_key_string /= Void then key_press_string_actions_internal.call ([a_key_string]) end end else if a_key /= Void then if key_release_actions_internal /= Void then key_release_actions_internal.call ([a_key]) end end end end on_size_allocate (a_x, a_y, a_width, a_height: INTEGER) -- Gtk_Widget."size-allocate" happened. local l_x_y_offset: INTEGER l_x, l_y: INTEGER do if a_width /= previous_width or else a_height /= previous_height then if attached parent_imp as l_parent_imp then l_x_y_offset := l_parent_imp.internal_x_y_offset end l_x := a_x - l_x_y_offset l_y := a_y - l_x_y_offset previous_width := a_width.to_integer_16 previous_height := a_height.to_integer_16 if resize_actions_internal /= Void then resize_actions_internal.call (app_implementation.gtk_marshal.dimension_tuple (l_x, l_y, a_width, a_height)) end end if attached parent_imp as l_parent_imp then l_parent_imp.child_has_resized (Current) end end on_focus_changed (a_has_focus: BOOLEAN) -- Called from focus intermediary agents when focus for `Current' has changed. -- if `a_has_focus' then `Current' has just received focus. do if a_has_focus then if app_implementation.focus_in_actions_internal /= Void then app_implementation.focus_in_actions.call ([attached_interface]) end if focus_in_actions_internal /= Void then focus_in_actions_internal.call (Void) end else if app_implementation.focus_out_actions_internal /= Void then app_implementation.focus_out_actions.call ([attached_interface]) end if focus_out_actions_internal /= Void then focus_out_actions_internal.call (Void) end end end on_pointer_enter_leave (a_pointer_enter: BOOLEAN) -- Called from pointer enter leave intermediary agents when the mouse pointer either enters or leaves `Current'. do if a_pointer_enter then -- The mouse pointer has entered `Current'. if pointer_enter_actions_internal /= Void then pointer_enter_actions_internal.call (Void) end else -- The mouse pointer has left `Current'. if pointer_leave_actions_internal /= Void then pointer_leave_actions_internal.call (Void) end end end feature {EV_ANY_I, EV_INTERMEDIARY_ROUTINES} -- Implementation call_button_event_actions ( a_type: INTEGER; a_x, a_y, a_button: INTEGER; a_x_tilt, a_y_tilt, a_pressure: DOUBLE; a_screen_x, a_screen_y: INTEGER) -- Call pointer_button_press_actions or pointer_double_press_actions -- depending on event type in first position of `event_data'. --| GTK sends both GDK_BUTTON_PRESS and GDK_2BUTTON_PRESS events --| when a handler is attached to "button-press-event". --| We attach the signal to this switching feature to look at the --| event type and pass the event data to the appropriate action --| sequence. local t : TUPLE [INTEGER, INTEGER, INTEGER, DOUBLE, DOUBLE, DOUBLE, INTEGER, INTEGER] mouse_wheel_delta: INTEGER do t := [a_x, a_y, a_button, a_x_tilt, a_y_tilt, a_pressure, a_screen_x, a_screen_y] -- Mouse Wheel implementation. if a_type /= {GTK}.GDK_BUTTON_RELEASE_ENUM then -- A button press must have occurred if a_type = {GTK}.GDK_BUTTON_PRESS_ENUM then mouse_wheel_delta := 1 elseif a_type = {GTK}.GDK_2BUTTON_PRESS_ENUM then mouse_wheel_delta := 1 elseif a_type = {GTK}.GDK_3BUTTON_PRESS_ENUM then mouse_wheel_delta := 1 end if a_button = 4 and mouse_wheel_delta > 0 then -- This is for scrolling up if app_implementation.mouse_wheel_actions_internal /= Void then app_implementation.mouse_wheel_actions.call ([attached_interface, mouse_wheel_delta]) end if mouse_wheel_actions_internal /= Void then mouse_wheel_actions_internal.call ([mouse_wheel_delta]) end elseif a_button = 5 and mouse_wheel_delta > 0 then -- This is for scrolling down if app_implementation.mouse_wheel_actions_internal /= Void then app_implementation.mouse_wheel_actions.call ([attached_interface, -mouse_wheel_delta]) end if mouse_wheel_actions_internal /= Void then mouse_wheel_actions_internal.call ([-mouse_wheel_delta]) end end if a_button >= 1 and then a_button <= 3 then if a_type = {GTK}.GDK_BUTTON_PRESS_ENUM then if app_implementation.pointer_button_press_actions_internal /= Void then app_implementation.pointer_button_press_actions.call ([attached_interface, a_button, a_screen_x, a_screen_y]) end if pointer_button_press_actions_internal /= Void then pointer_button_press_actions_internal.call (t) end elseif a_type = {GTK}.GDK_2BUTTON_PRESS_ENUM then if app_implementation.pointer_double_press_actions_internal /= Void then app_implementation.pointer_double_press_actions.call ([attached_interface, a_button, a_screen_x, a_screen_y]) end if pointer_double_press_actions_internal /= Void then pointer_double_press_actions_internal.call (t) end end end else -- We have a button release event if a_button >= 1 and a_button <= 3 then if app_implementation.pointer_button_release_actions_internal /= Void then app_implementation.pointer_button_release_actions.call ([attached_interface, a_button, a_screen_x, a_screen_y]) end if pointer_button_release_actions_internal /= Void then pointer_button_release_actions_internal.call (t) end end end end feature -- Access parent: detachable EV_CONTAINER -- Container widget that contains `Current'. -- (Void if `Current' is not in a container) local a_par_imp: detachable EV_CONTAINER_IMP do a_par_imp := parent_imp if a_par_imp /= Void then Result := a_par_imp.interface end end feature -- Status setting hide -- Request that `Current' not be displayed even when its parent is. do {GTK}.gtk_widget_hide (c_object) end feature -- Element change set_minimum_width (a_minimum_width: INTEGER) -- Set the minimum horizontal size to `a_minimum_width'. do {GTK2}.gtk_widget_set_minimum_size (c_object, a_minimum_width, height_request) -- If the parent is a fixed or scrollable area we need to update the item size. if attached {EV_VIEWPORT_IMP} parent_imp as l_viewport_parent then l_viewport_parent.set_item_width (a_minimum_width.max (width)) elseif attached {EV_FIXED_IMP} parent_imp as l_fixed_parent then l_fixed_parent.set_item_width (attached_interface, a_minimum_width.max (width)) end end set_minimum_height (a_minimum_height: INTEGER) -- Set the minimum vertical size to `a_minimum_height'. do {GTK2}.gtk_widget_set_minimum_size (c_object, width_request, a_minimum_height) -- If the parent is a fixed or scrollable area we need to update the item size. if attached {EV_VIEWPORT_IMP} parent_imp as l_viewport_parent then l_viewport_parent.set_item_height (a_minimum_height.max (height)) elseif attached {EV_FIXED_IMP} parent_imp as l_fixed_parent then l_fixed_parent.set_item_height (attached_interface, a_minimum_height.max (height)) end end set_minimum_size (a_minimum_width, a_minimum_height: INTEGER) -- Set the minimum horizontal size to `a_minimum_width'. -- Set the minimum vertical size to `a_minimum_height'. do {GTK2}.gtk_widget_set_minimum_size (c_object, a_minimum_width, a_minimum_height) -- If the parent is a fixed or scrollable area we need to update the item size. if attached {EV_VIEWPORT_IMP} parent_imp as l_viewport_parent then l_viewport_parent.set_item_size (a_minimum_width.max (width), a_minimum_height.max (height)) elseif attached {EV_FIXED_IMP} parent_imp as l_fixed_parent then l_fixed_parent.set_item_size (attached_interface, a_minimum_width.max (width), a_minimum_height.max (height)) end end feature -- Measurement x_position: INTEGER -- Horizontal offset relative to parent `x_position'. -- Unit of measurement: screen pixels. do if attached {EV_FIXED_IMP} parent_imp as l_fixed then Result := l_fixed.x_position_of_child (Current) else Result := Precursor {EV_PICK_AND_DROPABLE_IMP} end end y_position: INTEGER -- Vertical offset relative to parent `y_position'. -- Unit of measurement: screen pixels. do if attached {EV_FIXED_IMP} parent_imp as l_fixed then Result := l_fixed.y_position_of_child (Current) else Result := Precursor {EV_PICK_AND_DROPABLE_IMP} end end feature {EV_ANY_I} -- Implementation refresh_now -- Flush any pending redraws due for `Current'. do if {GTK}.gtk_widget_get_window (c_object) /= default_pointer then {GTK2}.gdk_window_process_updates ( {GTK}.gtk_widget_get_window (c_object), False ) end end feature {EV_CONTAINER_IMP} -- Implementation set_parent_imp (a_container_imp: detachable EV_CONTAINER_IMP) -- Set `parent_imp' to `a_container_imp'. do parent_imp := a_container_imp end feature {EV_ANY_IMP, EV_GTK_DEPENDENT_INTERMEDIARY_ROUTINES} -- Implementation destroy -- Destroy `Current' local l_window: POINTER do if not is_destroyed then -- Remove previously set pointer. l_window := {GTK}.gtk_widget_get_window (c_object) if l_window /= default_pointer then {GTK}.gdk_window_set_cursor (l_window, default_pointer) end if attached parent_imp as l_parent_imp then l_parent_imp.attached_interface.prune_all (attached_interface) end Precursor {EV_PICK_AND_DROPABLE_IMP} end end parent_imp: detachable EV_CONTAINER_IMP -- Container widget that contains `Current'. -- (Void if `Current' is not in a container) feature {EV_INTERMEDIARY_ROUTINES, EV_APPLICATION_IMP} -- Implementation on_widget_mapped -- `Current' has been mapped on to the screen. do -- Make sure that the pointer style is correctly set when the widget is mapped. -- This is needed for gtkwidgets that have not yet been realized. if previously_set_pointer_style = Void and then attached pointer_style as l_pointer_style then internal_set_pointer_style (l_pointer_style) end end on_widget_unmapped -- `Current' has been unmapped from the screen do end feature {EV_WIDGET_I} -- Implementation internal_x_y_offset: INTEGER -- Internal offset added to x/y coordinates of children. do -- Redefined by GtkLayout containers. Result := 0 end feature {NONE} -- Implementation propagate_foreground_color_internal (a_color: EV_COLOR; a_c_object: POINTER) -- Propagate `a_color' to the foreground color of `a_c_object's children. local l: POINTER child: POINTER fg: detachable EV_COLOR a_child_list: POINTER do if {GTK}.gtk_is_container (a_c_object) then from fg := a_color a_child_list := {GTK}.gtk_container_get_children (a_c_object) l := a_child_list until l = NULL loop child := {GTK}.glist_struct_data (l) real_set_foreground_color (child, fg) if {GTK}.gtk_is_container (child) then propagate_foreground_color_internal (fg, child) end l := {GTK}.glist_struct_next (l) end {GTK}.g_list_free (a_child_list) else real_set_foreground_color (a_c_object, fg) end end propagate_background_color_internal (a_color: EV_COLOR; a_c_object: POINTER) -- Propagate `a_color' to the background color of `a_c_object's children. local l: POINTER child: POINTER bg: detachable EV_COLOR a_child_list: POINTER do if {GTK}.gtk_is_container (a_c_object) then from bg := a_color a_child_list := {GTK}.gtk_container_get_children (a_c_object) l := a_child_list until l = NULL loop child := {GTK}.glist_struct_data (l) real_set_background_color (child, bg) if {GTK}.gtk_is_container (child) then propagate_background_color_internal (bg, child) end l := {GTK}.glist_struct_next (l) end {GTK}.g_list_free (a_child_list) else real_set_background_color (a_c_object, bg) end end feature {EV_INTERMEDIARY_ROUTINES} -- Implementation call_draw_actions (a_cairo_context: POINTER) -- Call the expose actions for the drawing area. do -- Redefined by descendents. end feature {NONE} -- Implementation set_box_child_expandable (a_parent_box, a_child: POINTER; flag: BOOLEAN) -- Set whether `child' expands to fill available spare space. local old_expand, fill, pad, pack_type: INTEGER do {GTK}.gtk_box_query_child_packing ( a_parent_box, a_child, $old_expand, $fill, $pad, $pack_type ) {GTK}.gtk_box_set_child_packing ( a_parent_box, a_child, flag, fill.to_boolean, pad, pack_type ) end feature {EV_APPLICATION_IMP} -- Implementation previous_width, previous_height: INTEGER_16 -- Dimensions during last "size-allocate". feature {EV_ANY, EV_ANY_I, EV_INTERMEDIARY_ROUTINES} -- Implementation interface: detachable EV_WIDGET note option: stable attribute end; note copyright: "Copyright (c) 1984-2019, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software 5949 Hollister Ave., Goleta, CA 93117 USA Telephone 805-685-1006, Fax 805-685-6869 Website http://www.eiffel.com Customer support http://support.eiffel.com ]" end -- class EV_WIDGET_IMP