indexing description: "[ Message client scene ]" date: "$Date$" revision: "$Revision$" class MESSAGE_CLIENT_SCENE inherit EM_COMPONENT_SCENE EM_SHARED_ERROR_HANDLER export {NONE} all end create make feature {NONE} -- Initialisation make is -- Initialise scene. local a_panel: EM_PANEL a_label: EM_LABEL a_scrollpanel: EM_SCROLLPANEL do make_component_scene -- Panel for remote address and TCP connect/disconnect button create a_panel.make_from_dimension (width, 75) a_panel.set_position (0, 0) a_panel.set_border (create {EM_NAMED_BORDER}.make_from_text ("TCP and UDP destination")) add_component (a_panel) create a_label.make_from_text ("Remote host:") a_label.set_position (5, 12) a_panel.add_widget (a_label) create remote_hostname_textbox.make_from_text ("localhost") remote_hostname_textbox.set_position (100, 12) remote_hostname_textbox.set_dimension (width-105, 20) remote_hostname_textbox.focus_received_event.subscribe (agent handle_hostname_changed) a_panel.add_widget (remote_hostname_textbox) create a_label.make_from_text ("Remote IP:") a_label.set_position (5, 32) a_panel.add_widget (a_label) create remote_ip_textbox.make_from_text ("") remote_ip_textbox.disable remote_ip_textbox.set_position (100, 32) remote_ip_textbox.set_dimension (width-235, 20) a_panel.add_widget (remote_ip_textbox) create resolve_button.make_from_text ("Resolve") resolve_button.set_position (width-125, 32) resolve_button.set_dimension (120, 20) resolve_button.clicked_event.subscribe (agent handle_resolve_button_clicked) a_panel.add_widget (resolve_button) create a_label.make_from_text ("Remote port:") a_label.set_position (5, 52) a_panel.add_widget (a_label) create remote_port_textbox.make_from_text ("6666") remote_port_textbox.set_position (100, 52) remote_port_textbox.set_dimension (width-235, 20) a_panel.add_widget (remote_port_textbox) create tcp_button.make_from_text ("") tcp_button.set_position (width-125, 52) tcp_button.set_dimension (120, 20) tcp_button.clicked_event.subscribe (agent handle_tcp_button_clicked) a_panel.add_widget (tcp_button) -- Panel for local port and UDP open/close button create a_panel.make_from_dimension (width, 35) a_panel.set_position (0, 75) a_panel.set_border (create {EM_NAMED_BORDER}.make_from_text ("UDP")) add_component (a_panel) create a_label.make_from_text ("Local port:") a_label.set_position (5, 12) a_panel.add_widget (a_label) create local_port_textbox.make_from_text ("6789") local_port_textbox.set_position (100, 12) local_port_textbox.set_dimension (width-235, 20) a_panel.add_widget (local_port_textbox) create udp_button.make_from_text ("") udp_button.set_position (width-125, 12) udp_button.set_dimension (120, 20) udp_button.clicked_event.subscribe (agent handle_udp_button_clicked) a_panel.add_widget (udp_button) -- Input box, send buttons and message display create input_textbox.make_empty input_textbox.set_position (0, 110) input_textbox.set_dimension (width-200, 20) input_textbox.return_pressed_event.subscribe (agent handle_return_pressed) add_component (input_textbox) create send_tcp_button.make_from_text ("Send (TCP)") send_tcp_button.set_position (width-200, 110) send_tcp_button.set_dimension (100, 20) send_tcp_button.disable send_tcp_button.clicked_event.subscribe (agent handle_tcp_send_button_clicked) add_component (send_tcp_button) create send_udp_button.make_from_text ("Send (UDP") send_udp_button.set_position (width-100, 110) send_udp_button.set_dimension (100, 20) send_udp_button.disable send_udp_button.clicked_event.subscribe (agent handle_udp_send_button_clicked) add_component (send_udp_button) create message_textarea.make_empty message_textarea.disable create a_scrollpanel.make_from_widget (message_textarea) a_scrollpanel.set_position (0, 130) a_scrollpanel.set_dimension (width, height-150) add_component (a_scrollpanel) -- Status message labels create tcp_state_label.make_empty tcp_state_label.set_position (0, height-20) tcp_state_label.set_dimension (width-170, 20) tcp_state_label.set_background_color (theme_colors.window_background) add_component (tcp_state_label) create udp_state_label.make_empty udp_state_label.set_position (width-170, height-20) udp_state_label.set_dimension (170, 20) udp_state_label.set_background_color (theme_colors.window_background) add_component (udp_state_label) -- Create sockets create udp_socket.make udp_socket.data_received_event.subscribe (agent handle_udp_data_received (?)) create tcp_socket.make tcp_socket.connection_established_event.subscribe (agent handle_connection_established) tcp_socket.connection_closed_event.subscribe (agent handle_connection_closed) tcp_socket.connection_failed_event.subscribe (agent handle_connection_failed) tcp_socket.data_received_event.subscribe (agent handle_tcp_data_received (?)) -- Subscribe exit handler event_loop.quit_event.subscribe (agent handle_quit (?)) -- Initialise widgets update_udp_state update_tcp_state end feature -- Access (Widgest) tcp_state_label: EM_LABEL -- TCP state label udp_state_label: EM_LABEL -- UDP state label tcp_button: EM_BUTTON -- Connect/Disconnect TCP button udp_button: EM_BUTTON -- Open/Close UDP button resolve_button: EM_BUTTON -- Resolve hostname to IP button send_tcp_button: EM_BUTTON -- TCP send button send_udp_button: EM_BUTTON -- UDP send button local_port_textbox: EM_TEXTBOX -- Local port textbox remote_hostname_textbox: EM_TEXTBOX -- Remote host textbox remote_ip_textbox: EM_TEXTBOX -- Remote IP textbox remote_port_textbox: EM_TEXTBOX -- Remote port textbox input_textbox: EM_TEXTBOX -- Input textbox message_textarea: EM_TEXTAREA -- Message textarea feature -- Access (Network) udp_socket: EM_UDP_SOCKET -- UDP socket tcp_socket: EM_TCP_CLIENT_SOCKET -- TCP socket current_address: EM_INET_SOCKET_ADDRESS -- Current IP address feature -- Event management (UDP) update_udp_state is -- Update UDP state label and udp buttons. do if udp_socket.is_open then udp_state_label.set_text ("UDP: open on port "+udp_socket.port.out) udp_button.set_text ("Close (UDP)") send_udp_button.enable else udp_state_label.set_text ("UDP: socket closed") udp_button.set_text ("Open (UDP)") send_udp_button.disable end end handle_udp_button_clicked is -- Handle udp button clicked. local error_occured: BOOLEAN error_dialog: EM_MESSAGE_DIALOG port: INTEGER do if error_occured then -- If an error occured it has to be on 'udp_socket.open' because 'close' doesn't raise an exception create error_dialog.make_from_error ("Unable to open UDP socket%NTry another port") error_dialog.set_modal (True) error_dialog.show else if udp_socket.is_open then udp_socket.close else port := -1 if local_port_textbox.text.is_integer then port := local_port_textbox.text.to_integer end if 1024 < port and port < 65535 then udp_socket.set_port (port) udp_socket.open else create error_dialog.make_from_error ("Local port invalid") error_dialog.set_modal (True) error_dialog.show end end end update_udp_state rescue error_occured := True retry end update_message_text (an_address: EM_INET_SOCKET_ADDRESS; a_message: STRING) is -- Update message text require an_address_not_void: an_address /= Void a_message_not_void: a_message /= Void do if an_address.is_resolved then append_line ("UDP sent to "+an_address.host_ip_port_as_string+":") append_line (a_message) else append_line ("UDP sending to '"+an_address.hostname+"' failed!") append_line (" (" + a_message + ")") end end handle_udp_send_button_clicked is -- Handle udp send button clicked. local a_port: INTEGER error_dialog: EM_MESSAGE_DIALOG do a_port := -1 if remote_port_textbox.text.is_integer then a_port := remote_port_textbox.text.to_integer end if 0 < a_port and a_port < 65535 then if current_address = Void then create current_address.make_by_hostname_port (remote_hostname_textbox.text, a_port) current_address.resolve_finished_event.subscribe_kamikaze (agent handle_resolve_finished (current_address)) elseif a_port /= current_address.port then create current_address.make_by_inet_address_port (current_address, a_port) end if current_address.is_resolved then -- Address is resolved and thus the upd sending will work instantly update_message_text (current_address, input_textbox.text) else -- Address is not resolved and the udp sending will not work instantly but first resolve the address -- When the resolving finished the event will fire and we know if it worked if the resolving was a success current_address.resolve_finished_event.subscribe_kamikaze (agent update_message_text (current_address,input_textbox.text.twin)) end udp_socket.send_direct (current_address, input_textbox.text + "%N") input_textbox.set_text ("") else create error_dialog.make_from_error ("Remote port invalid") error_dialog.set_modal (True) error_dialog.show end end handle_udp_data_received (a_packet: EM_UDP_PACKET) is -- Handle udp packet received. do append_line ("UDP received from "+a_packet.address.host_ip_port_as_string+" ("+a_packet.count.out+" bytes):") append_line (a_packet.item) end feature -- Event management (TCP) update_tcp_state is -- Update TCP state label and tcp buttons. do if tcp_socket.is_connected then tcp_state_label.set_text ("TCP: connected to "+tcp_socket.address.host_ip_port_as_string) tcp_button.set_text ("Disconnect (TCP)") tcp_button.enable send_tcp_button.enable elseif tcp_socket.is_trying_to_connect then tcp_state_label.set_text ("TCP: trying to connect") tcp_button.set_text ("... connecting ...") tcp_button.disable send_tcp_button.disable else tcp_state_label.set_text ("TCP: not connected") tcp_button.set_text ("Connect (TCP)") tcp_button.enable send_tcp_button.disable end end handle_tcp_button_clicked is -- Handle tcp button clicked. local error_dialog: EM_MESSAGE_DIALOG a_port: INTEGER do if tcp_socket.is_connected then tcp_socket.disconnect else a_port := -1 if remote_port_textbox.text.is_integer then a_port := remote_port_textbox.text.to_integer end if 0 < a_port and a_port < 65535 then if current_address = Void then create current_address.make_by_hostname_port (remote_hostname_textbox.text, a_port) current_address.resolve_finished_event.subscribe_kamikaze (agent handle_resolve_finished (current_address)) elseif a_port /= current_address.port then create current_address.make_by_inet_address_port (current_address, a_port) end tcp_socket.set_address (current_address) tcp_socket.connect else create error_dialog.make_from_error ("Remote port invalid") error_dialog.set_modal (True) error_dialog.show end end update_tcp_state end handle_connection_established is -- Handle connection established. do update_tcp_state end handle_connection_closed is -- Handle connection closed. local message_dialog: EM_MESSAGE_DIALOG do update_tcp_state create message_dialog.make_from_message ("TCP connection closed") message_dialog.set_modal (True) message_dialog.show end handle_connection_failed (an_error: INTEGER) is -- Handle connection failed. local error_dialog: EM_MESSAGE_DIALOG do update_tcp_state if an_error = Error_handler.Em_error_resolve_ip then create error_dialog.make_from_error ("TCP conenction attempt failed%NHost couldn't be resolved") elseif an_error = Error_handler.Em_error_connection_failed then create error_dialog.make_from_error ("TCP conenction attempt failed%NConnection refused%NThere is maybe no server on this port") else create error_dialog.make_from_error ("TCP conenction attempt failed") end error_dialog.set_modal (True) error_dialog.show end handle_tcp_send_button_clicked is -- Handle tcp send button clicked. do tcp_socket.send_string (input_textbox.text + "%N") append_line ("TCP sent to "+tcp_socket.address.host_ip_port_as_string+":") append_line (input_textbox.text) input_textbox.set_text ("") end handle_tcp_data_received (a_data: STRING) is -- Handle tcp data received. do append_line ("TCP received:") append_line (a_data) end feature -- Event management (Resolving) handle_hostname_changed is -- Handle hostname changed do remote_ip_textbox.set_text ("") current_address := Void end handle_resolve_finished (an_inet_socket_address: EM_INET_SOCKET_ADDRESS) is -- Handle resolve finished. do -- Only display resolved name if the resolved address is the same as the current address if an_inet_socket_address = current_address then if current_address.is_resolved then remote_ip_textbox.set_text (current_address.host_ip_as_string) remote_hostname_textbox.set_text (current_address.hostname) remote_port_textbox.set_text (current_address.port.out) else remote_ip_textbox.set_text ("Resolve failed") end end end handle_resolve_button_clicked is -- Handle resolve button clicked. local a_port: INTEGER error_dialog: EM_MESSAGE_DIALOG do a_port := -1 if remote_port_textbox.text.is_integer then a_port := remote_port_textbox.text.to_integer end if 0 < a_port and a_port < 65535 then create current_address.make_by_hostname_port (remote_hostname_textbox.text, a_port) current_address.resolve_finished_event.subscribe_kamikaze (agent handle_resolve_finished (current_address)) current_address.resolve_non_blocking else create error_dialog.make_from_error ("Remote port invalid") error_dialog.set_modal (True) error_dialog.show end end feature {NONE} -- Event management (Miscellaneous) handle_return_pressed is -- Handle return pressed in input box. local a_line: STRING do a_line := input_textbox.text if udp_socket.is_open then handle_udp_send_button_clicked end if tcp_socket.is_connected then -- Reset text in case udp send has cleared it input_textbox.set_text (a_line) handle_tcp_send_button_clicked end end handle_quit (a_quit_event: EM_QUIT_EVENT) is -- Handle quit event. do if tcp_socket.is_connected then tcp_socket.disconnect end if udp_socket.is_open then udp_socket.close end end feature {NONE} -- Implementation append_line (a_line: STRING) is -- Append `a_line' to `message_textarea'. do message_textarea.text.append_string (a_line) message_textarea.text.append_string ("%N") if message_textarea.text.count > 5000 then message_textarea.text.keep_tail (5000) end message_textarea.resize_to_optimal_dimension end end