rexx-things/modules/windows/oodialog/winsystem/displayWindowTree.rex
2025-03-12 20:50:48 +00:00

276 lines
11 KiB
Rexx
Executable File

/*----------------------------------------------------------------------------*/
/* */
/* Copyright (c) 2008-2014 Rexx Language Association. All rights reserved. */
/* */
/* This program and the accompanying materials are made available under */
/* the terms of the Common Public License v1.0 which accompanies this */
/* distribution. A copy is also available at the following address: */
/* https://www.oorexx.org/license.html */
/* */
/* Redistribution and use in source and binary forms, with or */
/* without modification, are permitted provided that the following */
/* conditions are met: */
/* */
/* Redistributions of source code must retain the above copyright */
/* notice, this list of conditions and the following disclaimer. */
/* Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in */
/* the documentation and/or other materials provided with the distribution. */
/* */
/* Neither the name of Rexx Language Association nor the names */
/* of its contributors may be used to endorse or promote products */
/* derived from this software without specific prior written permission. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* */
/*----------------------------------------------------------------------------*/
/**
* Name: displayWindowTree.rex
* Type: Example ooRexx program
*
* Description: Allows the user to pick an open top-level window and then
* displays the window hiearchy for that window. The program
* uses both ooDialog and winsystm classes.
*
* This example uses some of the public functions provided by
* the windowsSystem.frm package. That framework contains
* functions shared with other example programs shipped with
* ooRexx that use winsystm.cls.
*
* External function Source
* ---------------------------------------------------
* errorDialog() ooDialog
* getWindowTree() windowsSystem.frm
* showWindowTree() windowsSystem.frm
*
*/
sd = locate()
-- Use the global .constDir for symbolic IDs and turn auto detection off.
.application~setDefaults("O", sd"winSystemDlgs.h", .false)
-- Create and show our ooDialog dialog. The logic of the program is contained
-- within the WindowListDlg class.
dlg = .WindowListDlg~new(sd"winSystemDlgs.rc", IDD_WINDOW_List)
if dlg~initCode == 0 then do
dlg~execute("SHOWTOP")
return 0
end
else do
msg = "Failed to instantiate the .WindowListDlg object." || '0d0a0d0a'x || -
"Aborting on error."
j = errorDialog(msg)
return 99
end
::requires "windowsSystem.frm"
::class 'WindowListDlg' public subclass RcDialog
/** initdialog()
* Initialize the state of our dialog controls.
*/
::method initDialog
expose listView windows inError
-- If some un-anticipated error happens we will change the behaviour of the
-- dialog. inError is used to flag an error.
inError = .false
-- Instantiate an array that will be used to hold all the top-level window
-- objects.
windows = .array~new
-- If we can't get the list view control, something is wrong.
listView = self~newListView(IDC_LV_WINDOWS)
if listView == .nil then return self~putInErrorState("NOLISTVIEW")
-- The extended list view styles can only be added after the list view control
-- has been created.
--
-- The FULLROWSELECT style highlights the entire row when row is selected
-- rather than just part of the row.
--
-- The LABLETIP style will cause a tool-tip like label to appear that displays
-- the entire text of the column, if the text is longer than will fit in the
-- column, when the user puts their mouse over the row.
listView~addExtendedStyle("FULLROWSELECT LABELTIP")
-- We want to set the width of column 0, (the first column,) to the width of
-- the list view control, minus the width of a scroll bar if there is one.
--
-- We can find out the width of a scroll bar from the system metrics and can
-- query the width of the list view control directly.
--
-- Note that the system metric figure is in pixels and the column width needs
-- to be in dialog units. Therefore the width of the scroll bar is more than
-- we actually need, but it is close enough.
parse value listView~getSize with width height
SM_CXVSCROLL = 20
width -= SystemMetrics(SM_CXVSCROLL)
-- Insert our one and only column for the list view control.
listView~insertColumn(0, "Window Title", width)
-- Connect the push buttons to our event handling methods.
self~connectButton(IDC_PB_SHOW, onShow)
self~connectButton(IDC_PB_QUIT, onQuit)
self~connectButton(IDC_PB_REFRESH, onRefresh)
-- The logic / code to fill the list view control is in the onRefresh()
-- method. So, we simply invoke the method ourselves to populate the list
-- view.
self~onRefresh
/** onRefresh()
* This method contains the logic to populate the list view control with the
* titles of the current top-level windows. The method is connected to the
* Refresh button, so it is invoked when the user pushes that button.
*
* However, it can also be invoked internally to populate the list view. This
* is done when the dialog is initialized, from the initDialog() method. And
* it is also invokde if an invalid window handle is detected in the onShow()
* method.
*/
::method onRefresh
expose listView windows inError
-- Be sure the windows array is empty
windows~empty
-- Be sure the list view control is empty
listView~deleteAll
-- Instantiate a WindowManager object and get the desktop window.
mgr = .WindowsManager~new
deskTop = mgr~desktopWindow
-- Build an array of all the children windows of the desktop. Skip any window
-- that does not have a title or that is invisible.
child = deskTop~firstChild
do while child <> .nil
if child~title~strip \== "", child~state~caselessWordpos("invisible") == 0 then windows~append(child)
child = child~next
end
-- If we don't have any windows, something is wrong.
if windows~isEmpty then return self~putInErrorState("NOWINDOWS")
-- Now, for each window, add a row using the window title as the text for the
-- row.
do wnd over windows
listView~addRow( , , wnd~title)
end
-- Select the first row, so that we are guaranteed that a row is selected.
-- Note that the row numbers are zero-indexed.
listView~select(0)
/** onShow()
* The user pushed the 'Show' button. In the unlikely event that we are in an
* error state we will quit. Otherwise, we will display the window hiearchy of
* the window the user selected.
*/
::method onShow
expose listView inError windows
if inError then return self~cancel
-- Get the row number selected. Note that we are guarenteed that a row is
-- selected because we selected a row to begin with and we have provided no
-- way for the user to deselect a row. Because of this we do not have to
-- check for -1. Since the rows are zero-indexed, we add 1 to the selected
-- row number, to produce the index into our array of windows.
wnd = windows[listView~selected + 1]
-- It is conceivable / possible that the user has closed a window since the
-- time the list view was populated. This can be detected because the window
-- state will be 'Illegal Handle.'
if wnd~state == "Illegal Handle" then return self~badHandle
-- Display the window hierarchy of the selected window.
tree = getWindowTree(wnd)
success = showWindowTree(tree)
/** onQuit()
* The user pushed the 'Quit' button. This is easy, just quit the dialog.
*/
::method onQuit
return self~cancel
/** badHandle()
* This private method is invoked when the user picks a window that has been
* closed since the list view was originally populated. We tell the user the
* problem and refresh the list view.
*/
::method badHandle private
expose listView
-- Get the text of the selected item to use in the error message.
title = listView~itemText(listView~selected)
msg = "The window you selected appears to have been closed." || '0d0a0d0a'x || -
"Window title:" title || '0d0a0d0a'x || -
"The window list is being refreshed. Select a new window."
j = infoDialog(msg)
self~onRefresh
return 0
/** putInErrorState()
* This private method is used when some un-recoverable error is detected.
*
* It displays an error message to the user based on the reason for the error,
* sets the error state flag, and tries to disable the Show push button.
*/
::method putInErrorState private
expose inError
use strict arg reason
inError = .true
select
when reason == "NOWINDOWS" then do
msg = "For some reason no top-level windows were located." || '0d0a'x || -
"This is not correct, the sample program is not" || '0d0a'x || -
"executing correctly."
end
when reason == "NOLISTVIEW" then do
msg = "An internal error is preventing this sample program" || '0d0a'x || -
"from executing correctly. The ListControl object" || '0d0a'x || -
"could not be obtained."
end
otherwise do
msg = "The sample program is not executing correctly," || '0d0a'x || -
"some unknown error."
end
end
-- End select
msg = msg || '0d0a0d0a'x || "Please just quit."
j = errorDialog(msg)
-- We will try to disable the 'Show' push button. But, if the dialog is not
-- working correctly, it could be that this won't work.
pb = self~newPushButton(IDC_PB_SHOW)
if pb <> .nil then pb~disable
return 0