note
description: "Eiffel Vision application. MS Windows implementation."
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
EV_APPLICATION_IMP
inherit
EV_APPLICATION_I
redefine
make,
wake_up_gui_thread
end
WEL_APPLICATION
rename
make as wel_make,
main_window as silly_main_window
export
{NONE} silly_main_window
redefine
init_application,
message_loop
end
WEL_CONSTANTS
export
{NONE} all
end
WEL_ICC_CONSTANTS
export
{NONE} all
end
WEL_TOOLTIP_CONSTANTS
export
{NONE} all
end
WEL_WORD_OPERATIONS
export
{NONE} all
end
WEL_VK_CONSTANTS
WEL_WINDOWS_VERSION
export
{NONE} all
end
WEL_SHARED_METRICS
create
make
feature -- Initialization
make
-- Create the application with `an_interface' interface.
local
l_result: INTEGER
l_process: POINTER
do
(create {WEL_SCALING_EXTERNALS}).set_process_per_monitor_dpi_aware
create reusable_message.make
init_instance
init_application
create blocking_windows_stack.make (5)
-- This is a hack to ensure that `silly_main_window' exists before
-- we create any widgets. If this is not the case, then `themes_active' fails
-- during creation of the widgets, and those widgets created before a window
-- end up with a null theme handle.
silly_main_window.do_nothing
cwin_disable_xp_ghosting
-- Initialize the theme drawer to the correct version for
-- the current platform.
update_theme_drawer
Precursor
tooltip_delay := no_tooltip_delay_assigned
dispatcher.set_exception_callback (agent on_exception_action)
set_capture_type ({EV_APPLICATION_IMP}.capture_heavy)
set_application_main_window (silly_main_window)
-- Set the main application window handle for thread communication facilities.
main_application_window_handle := silly_main_window.item
-- Get HANDLE to current process.
-- 0x2 stands for `DUPLICATE_SAME_ACCESS'.
l_process := {WEL_API}.get_current_process
l_result := {WEL_API}.duplicate_handle (l_process, l_process, l_process, $l_process, 0, False, 0x2)
check l_result_good: l_result /= 0 end
process_handle := l_process
set_application (Current)
end
feature -- Access
key_pressed (virtual_key: INTEGER): BOOLEAN
-- Is `virtual_key' currently pressed?
do
Result := (cwin_get_keyboard_state (virtual_key) & 0xF000) = 0xF000
end
key_toggled (virtual_key: INTEGER): BOOLEAN
-- Is `virtual_key' currently toggled?
do
Result := (cwin_get_keyboard_state (virtual_key) & 0x0001) = 0x0001
end
ctrl_pressed: BOOLEAN
-- Is ctrl key currently pressed?
do
Result := key_pressed (vk_control)
end
alt_pressed: BOOLEAN
-- Is alt key currently pressed?
do
Result := key_pressed (vk_lmenu) or
key_pressed (vk_rmenu)
end
shift_pressed: BOOLEAN
-- Is shift key currently pressed?
do
Result := key_pressed (vk_shift)
end
caps_lock_on: BOOLEAN
-- Is the caps lock key currently on?
do
Result := key_toggled (vk_capital)
end
is_display_remote: BOOLEAN
-- Is display for application remote?
do
Result := metrics.is_remote_session
end
feature -- Basic operation
process_graphical_events
-- Process any pending paint messages.
--| Pass control to the GUI toolkit so that it can
--| handle any paint events that may be in its queue.
local
msg: WEL_MSG
do
from
create msg.make
msg.peek_paint_messages
until
not msg.last_boolean_result
loop
process_message (msg)
msg.peek_paint_messages
end
end
sleep (msec: INTEGER)
-- Wait for `msec' milliseconds and return.
do
c_sleep (msec)
end
feature -- Root window
Silly_main_window: EV_INTERNAL_SILLY_WINDOW_IMP
-- Current main window of the application.
once
--| Previously this would return the first window created
--| by the user. This forced a window to be created before the
--| application was launched. Now we set the main window
--| to an EV_INTERNAL_SILLY_WINDOW_IMP which is never seen by the
--| User. The application still ends when the last of the user
--| created windows is destroyed. This now allows an application
--| to create it's windows from within post_launch_actions and
--| provides more flexibility.
create Result.make_top ("Main Window")
Result.move (0, 0)
end
feature -- Element change
add_root_window (w: WEL_FRAME_WINDOW)
-- Add `w' to the list of root windows.
do
-- Initialize the theme drawer to the correct version for
-- the current platform. This needs to be performed after a window
-- is added to the system as otherwise a call to `themes_active' is False.
update_theme_drawer
Application_windows_id.extend (w.item)
end
remove_root_window (w: WEL_FRAME_WINDOW)
-- Remove `w' from the root windows list.
do
Application_windows_id.prune_all (w.item)
end
window_with_focus: detachable EV_WINDOW_IMP
-- `Result' is EV_WINDOW with current focus.
set_window_with_focus (a_window: detachable EV_WINDOW_IMP)
-- Assign implementation of `a_window' to `window_with_focus'.
do
window_with_focus := a_window
end
feature {NONE} -- Implementation
Application_windows_id: ARRAYED_LIST [POINTER]
-- All user created windows in the application.
--| For internal use only.
once
create {ARRAYED_LIST [POINTER]} Result.make (5)
ensure
not_void: Result /= Void
end
feature {EV_ANY_I}-- Status report
tooltip_delay: INTEGER
-- Time in milliseconds before tooltips pop up.
no_tooltip_delay_assigned: INTEGER = -1
-- Constant for use with tooltip_delay.
windows: LINEAR [EV_WINDOW]
-- List of current EV_WINDOWs.
--| This was introduced to allow the previous internal
--| implementation to be kept although changing the interface.
do
Result := windows_internal (True)
end
feature {EV_WIDGET_IMP} -- Implementation
windows_internal (a_include_hidden: BOOLEAN): LINEAR [EV_WINDOW]
-- Implementation for `windows', use `a_include_hidden' to include hidden Windows in result.
local
ev_win: detachable EV_WINDOW_IMP
res: ARRAYED_LIST [EV_WINDOW]
do
create res.make (Application_windows_id.count)
Result := res
from
Application_windows_id.start
until
Application_windows_id.after
loop
if is_window (Application_windows_id.item) then
ev_win ?= window_of_item (Application_windows_id.item)
if ev_win /= Void then
if (not a_include_hidden) implies ev_win.is_displayed then
-- If not including hidden then we only want displayed windows.
res.extend (ev_win.attached_interface)
end
Application_windows_id.forth
else
-- Object has been collected, we remove it
-- from `Application_windows_id'.
Application_windows_id.remove
end
else
-- Object has been collected, we remove it
-- from `Application_windows_id'.
Application_windows_id.remove
end
end
end
feature {EV_ANY_HANDLER, EV_WEL_CONTROL_CONTAINER_IMP, EV_WIDGET_IMP, WEL_ANY} -- Theme drawing
theme_drawer: EV_THEME_DRAWER_IMP
-- `Result' is object suitable for drawing using the
-- currently selected themes.
update_theme_drawer
-- Updated `theme_drawer' to use current Windows settings.
do
if themes_active then
create {EV_XP_THEME_DRAWER_IMP} theme_drawer
else
create {EV_CLASSIC_THEME_DRAWER_IMP} theme_drawer
end
end
set_theme_drawer (drawer: EV_THEME_DRAWER_IMP)
-- Assign `drawer' to `theme_drawer'.
require
drawer_not_void: drawer /= Void
do
theme_drawer := drawer
ensure
drawer_set: theme_drawer = drawer
end
themes_active: BOOLEAN
-- Are themes currently active?
do
Result := uxtheme_dll_available and then cwin_is_theme_active and then cwin_is_app_themed and then comctl32_version >= version_600
end
uxtheme_dll_available: BOOLEAN
-- Is the "uxtheme.dll" required for theme support available on the current platform?
local
dll: WEL_DLL
once
create dll.make ("uxtheme.dll")
Result := dll.exists
end
feature {EV_ANY_I, EV_ANY_HANDLER, EV_PICK_AND_DROPABLE_IMP, EV_INTERNAL_COMBO_FIELD_IMP} -- Status Report
pick_and_drop_source: detachable EV_PICK_AND_DROPABLE_IMP
-- The current pick and drop source.
--| If `Void' then no pick and drop is currently executing.
--| This allows us to globally check whether a pick and drop
--| is executing, and if so, the source.
drop_actions_executing: BOOLEAN
-- Are the `drop_actions' for a pick and dropable object currently executing?
dockable_source: detachable EV_DOCKABLE_SOURCE_IMP
-- The current dockable source if a dock is executing.
feature {EV_PICK_AND_DROPABLE_IMP, EV_DOCKABLE_SOURCE_IMP} -- Status Report
enable_drop_actions_executing
-- Assign `True' to `drop_actions_executing'.
do
drop_actions_executing := True
ensure
drop_actions_executing: drop_actions_executing
end
disable_drop_actions_executing
-- Assign `False' to `drop_actions_executing'.
do
drop_actions_executing := False
ensure
drop_actions_not_executing: not drop_actions_executing
end
dock_started (source: EV_DOCKABLE_SOURCE_IMP)
-- Assign `source' to `dockable_source'.
require
source_not_void: source /= Void
do
dockable_source := source
ensure
source_set: dockable_source = source
end
dock_ended
-- Ensure `dockable_source' is Void.
do
dockable_source := Void
ensure
dockable_source = Void
end
transport_started (widget: EV_PICK_AND_DROPABLE_IMP)
-- Assign `widget' to `pick_and_drop_source'.
require
widget_not_void: widget /= Void
do
pick_and_drop_source := widget
ensure
source_set: pick_and_drop_source = widget
end
transport_ended
-- Assign `Void' to `pick_and_drop_source'.
do
pick_and_drop_source := Void
ensure
pick_and_drop_source = Void
end
awaiting_movement: BOOLEAN
-- Is there a drag and drop awaiting movement, before the transport
-- really starts?
--| This allows us to check globally.
start_awaiting_movement
-- Assign `True' to `awaiting_movement'.
do
-- Update screen dc of pnd screen in case it has changed.
pnd_screen.implementation.refresh_graphics_context
awaiting_movement := True
end
end_awaiting_movement
-- Assign `False' to `awaiting_movement'.
do
awaiting_movement := False
end
transport_just_ended: BOOLEAN
-- Has a pick/drag and drop just ended and we have not
-- yet received the Wm_ncactivate message in the window
-- where the pick/drag was ended?
--| When we cancel a pick/drag, we must reset override_movement
--| ready for the next pick/drag. However, we still want to override
--| the default processing for the Wm_ncativate message in the window.
--| This flag has been added only for this case.
set_transport_just_ended
-- Assign `True' to `transport_just_ended'.
do
transport_just_ended := True
end
clear_transport_just_ended
-- Assign `False' to `transport_just_ended'.
do
transport_just_ended := False
end
override_from_mouse_activate: BOOLEAN
-- The default_windows behaviour is being overridden from a
-- the on_wm_mouse_activate windows message.
-- This should be reset to False at the start of `on_wm_mouse_activate'
-- and to true when we know we must override the windows movement
-- within `on_wm_mouse_activate'.
set_override_from_mouse_activate
-- Assign `True' to override_from_mouse_activate.
do
override_from_mouse_activate := True
end
clear_override_from_mouse_activate
-- Assign `False' to override_from_mouse_activate.
do
override_from_mouse_activate := False
end
feature -- Status reports
capture_type: INTEGER
-- Type of capture to use when capturing the mouse.
-- See constants Capture_xxxx at the end of the class.
do
Result := internal_capture_type.item
ensure
valid_result: Result = Capture_normal or
Result = Capture_heavy
end
feature -- Status setting
set_tooltip_delay (a_delay: INTEGER)
-- Assign `a_delay' to `tooltip_delay'.
do
tooltip_delay := a_delay
internal_tooltip.set_initial_delay_time (a_delay)
end
set_capture_type (a_capture_type: INTEGER)
-- Set the type of capture to use when capturing the
-- mouse to `a_capture_type'.
-- See constants Capture_xxxx at the end of the class
require
valid_capture_type: a_capture_type = Capture_normal or
a_capture_type = Capture_heavy
do
internal_capture_type.put (a_capture_type)
ensure
valid_capture: capture_type = Capture_normal or
capture_type = Capture_heavy
end
feature -- Basic operation
destroy
-- Destroy `Current' (End the application).
local
l_result: INTEGER
do
cwin_post_quit_message (0)
set_is_destroyed (True)
window_with_focus := Void
destroy_actions.call (Void)
-- Destroy `process_handle'
l_result := {WEL_API}.close_handle (process_handle)
check l_result_ok: l_result /= 0 end
process_handle := default_pointer
end
feature -- Tooltips
internal_tooltip: WEL_TOOLTIP
-- WEL_TOOLTIP used internally by current.
once
create Result.make (silly_main_window, -1)
Result.set_max_tip_width (32000)
Result.activate
-- Set the inital time delay for the tooltip.
Result.set_initial_delay_time (tooltip_delay)
ensure
internal_tooltip_not_void: Result /= Void
end
feature {NONE} -- WEL Implementation
controls_dll: WEL_INIT_COMMCTRL_EX
-- Needed for loading the common controls dlls.
rich_edit_dll: WEL_RICH_EDIT_DLL
-- DLL needed for rich edit control.
init_application
-- Load the dll needed sometimes.
do
create controls_dll.make_with_flags (Icc_win95_classes |
Icc_date_classes | Icc_userex_classes | Icc_cool_classes)
create rich_edit_dll.make
end
feature {NONE} -- Implementation
wake_up_gui_thread
-- Wake up the GUI thread if sleeping.
local
l_success: BOOLEAN
do
-- Post a null message to the main application window.
l_success := {WEL_API}.post_message_result (main_application_window_handle, {WEL_WM_CONSTANTS}.wm_null, to_wparam (0), to_lparam (0))
end
theme_window: EV_THEME_WINDOW
-- Window with responsibility for notify `theme_changed_actions'.
once
create Result.make
end
blocking_windows_stack: ARRAYED_STACK [EV_WINDOW_IMP]
-- Windows that are blocking window. The top
-- window represent the window that is the
-- real current blocking window.
message_loop
-- Windows message loop.
do
-- Not applicable with Vision2
end
reusable_message: WEL_MSG
-- Reusable message object.
process_underlying_toolkit_event_queue
-- Process event queue from underlying toolkit.
local
msg: WEL_MSG
l_any_event, l_user_event: BOOLEAN
do
from
msg := reusable_message
msg.peek_all
until
not msg.last_boolean_result or else is_destroyed
loop
l_any_event := True
l_user_event := l_user_event or else msg.user_generated
process_message (msg)
msg.peek_all
end
events_processed_from_underlying_toolkit := l_any_event
user_events_processed_from_underlying_toolkit := l_user_event
end
process_message (msg: WEL_MSG)
-- Dispatch `msg'.
--| Different from WEL because of accelerators.
require
msg_not_void: msg /= Void
local
l_process_events: BOOLEAN
do
if msg.last_boolean_result then
if msg.quit then
set_is_destroyed (True)
else
l_process_events := True
if attached window_with_focus as l_window_with_focus and then l_window_with_focus.exists and then is_dialog (l_window_with_focus.wel_item) then
msg.process_dialog_message (l_window_with_focus.wel_item)
l_process_events := not msg.last_boolean_result
-- Only process events if the event was not a dialog message.
end
if l_process_events then
-- Dispatch message.
msg.translate
msg.dispatch
end
end
end
end
internal_capture_type: CELL [INTEGER]
-- System wide once, in order to always get the
-- same value.
once
create Result.put (0)
ensure
internal_capture_type_not_void: Result /= Void
end
process_handle: POINTER
-- HANDLE for current process.
wait_for_input (msec: INTEGER)
-- Wait for at most `msec' milliseconds for an input.
local
l_result: INTEGER
l_process, l_null: POINTER
do
l_process := process_handle
if l_process /= l_null then
l_result := {WEL_API}.msg_wait_for_multiple_objects_ex (1, $l_process, msec, {WEL_QS_CONSTANTS}.qs_allinput | {WEL_QS_CONSTANTS}.qs_allpostmessage, mwmo_inputavailable)
check l_result_ok: l_result /= -1 end
end
end
mwmo_inputavailable: INTEGER_32 = 0x4
-- MWMO_INPUTAVAILABLE Flag for MsgWaitForMultipleObjects to return if input is available.
main_application_window_handle: POINTER
-- HANDLE for main application window.
feature -- Public constants
Capture_heavy: INTEGER = 1
-- The mouse [has been/should be] captured through
-- a call to `set_heavy_capture'
Capture_normal: INTEGER = 0
-- The mouse [has been/should be] captured through
-- a call to `set_capture'
--
-- Default value.
feature {NONE} -- Externals
cwin_disable_xp_ghosting
-- Disable XP ghosting.
external
"C inline use <windows.h>"
alias
"[
{
FARPROC disable_ghosting = NULL;
HMODULE user32_module = LoadLibrary (L"user32.dll");
if (user32_module) {
disable_ghosting = GetProcAddress (user32_module, "DisableProcessWindowsGhosting");
if (disable_ghosting) {
(FUNCTION_CAST_TYPE(void,WINAPI,(void)) disable_ghosting) ();
}
}
}
]"
end
cwin_register_window_message (message_name: POINTER): INTEGER
-- Register a custom window message named `message_name'.
-- `Result' is id of new message.
external
"C [macro <windows.h>] (LPCTSTR): EIF_INTEGER"
alias
"RegisterWindowMessage"
end
c_sleep (v: INTEGER)
-- Sleep for `v' milliseconds.
external
"C [macro <windows.h>] (DWORD)"
alias
"Sleep"
end
cwin_post_quit_message (exit_code: INTEGER)
-- SDK PostQuitMessage.
external
"C [macro <wel.h>] (int)"
alias
"PostQuitMessage"
end
cwin_get_keyboard_state (virtual_key: INTEGER): INTEGER_16
-- `Result' is state of `virtual_key'.
external
"C [macro <windows.h>] (int): EIF_INTEGER_16"
alias
"GetKeyState"
end
frozen cwel_integer_to_pointer (i: INTEGER): POINTER
-- Converts an integer `i' to a pointer
external
"C [macro <wel.h>] (EIF_INTEGER): EIF_POINTER"
end
cwin_is_theme_active: BOOLEAN
-- SDK's Open
external
"dllwin %"uxtheme.dll%" signature (): EIF_BOOLEAN use <windows.h>"
alias
"IsThemeActive"
end
cwin_is_app_themed: BOOLEAN
-- SDK's Open
external
"dllwin %"uxtheme.dll%" signature (): EIF_BOOLEAN use <windows.h>"
alias
"IsAppThemed"
end
invariant
idle_action_mutex_valid: {PLATFORM}.is_thread_capable implies idle_action_mutex /= Void
process_handle_valid: not is_destroyed implies process_handle /= default_pointer
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