276 lines
11 KiB
Rexx
Executable File
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
|