How to make a Window Modal

Chris Kelleher

Administrator
Staff member
<BLOCKQUOTE><font size="1" face="Arial, Verdana">quote:</font><HR>
> ... I've used the second technique
> several times before to cater for screens which are normally modeless
> windows but which may occasionally need to be run from a modal dialog-box
> (and therefore must appear as dialogs themselves).
<HR></BLOCKQUOTE>

OK, I've had a couple of people ask me off-line how to go about doing this
kind of thing, so I'm posting some code I wrote a few years back which turns
a window into a dialog at run-time if the session has gone modal.

To use it:

1) copy the code (after signature) into a file and save it as 'modal.i'
2) create a new (standard) window in the AppBuilder/UIB
3) go into the property sheet for the window and each frame within it and
uncheck the 'View' option.
4) in the main block, replace the line
RUN enable_UI.
with
{modal.i}
RUN enable_UI.
VIEW {&CONTAINER-NAME}.
5) run the window stand-alone or from another window. It should appear as
normal (ie as a window).
6) run the window from a dialog-box. It should appear as another
dialog-box.

A few points:

* Don't blame me if it doesn't work properly on your screens! Although
'reasonably' generic it was designed for use on a particular project which
followed a certain set of standards in terms of screen design rules and
initialization code - yours may be different. However, the code is fairly
simple so it shouldn't be too difficult to pull apart and bend into a
useable form. In particular though, note that it won't work 'as is' for
SmartObjects.

* You can force the screen to be a dialog (whether the session is modal or
not) by using '{modal.i &forceModal=TRUE}'.

* An extra couple of preprocessor definitions are set up - {&CONTAINER-NAME}
and {&DIALOG-BOX-NAME}. The former will always be a valid handle and will
refer to whichever widget is being used as the container.
{&DIALOG-BOX-NAME} will only be a valid handle if the screen is running as a
dialog box.

* As ever, if you run a screen persistently which is rendered as a dialog
box, make sure your active WAIT-FOR is not waiting for any UI events on a
widget which will become disabled, or you'll get some unfriendly errors!

Regards,

Mark Mahieu
Software Consultant
Entier Ltd
markm@entier.co.uk


<BLOCKQUOTE><font size="1" face="Arial, Verdana">code:</font><HR><pre>
/* modal.i */

&IF DEFINED(modal_i) = 0 &THEN
&GLOBAL-DEFINE modal_i INCLUDED

/*
** Set up a Unique ID to embed into any 'global' variable and procedure
** names so they don't clash with names elsewhere
*/

&SCOPED-DEFINE UID UID#{&SEQUENCE}

&SCOPED-DEFINE setContainer {&UID}_setContainer
&SCOPED-DEFINE applyEvent {&UID}_applyEvent

&GLOBAL-DEFINE CONTAINER-NAME {&UID}_Container
&GLOBAL-DEFINE DIALOG-BOX-NAME {&UID}_Dialog-Box

DEFINE VARIABLE {&CONTAINER-NAME} AS HANDLE NO-UNDO.
DEFINE VARIABLE {&DIALOG-BOX-NAME} AS HANDLE NO-UNDO.


&IF DEFINED(forceModal) &THEN

IF CAN-DO("TRUE,YES", STRING({&forceModal})) THEN
RUN {&setContainer} (TRUE).
ELSE

&ENDIF

RUN {&setContainer} (FALSE).


PROCEDURE {&setContainer} :

DEFINE INPUT PARAMETER forceModal AS LOGICAL NO-UNDO.

DEFINE VARIABLE sessionIsModal AS LOGICAL NO-UNDO.
DEFINE VARIABLE windowHandle AS HANDLE NO-UNDO.
DEFINE VARIABLE childHandle AS HANDLE NO-UNDO.


/*
** If not forcing the container to be a dialog-box then check to see if
the
** system has already gone modal (ie there is already a dialog-box
open).
*/

IF NOT forceModal THEN
checkModalBlock:
DO :

windowHandle = SESSION:FIRST-CHILD.

DO WHILE VALID-HANDLE(windowHandle) :

childHandle = windowHandle:FIRST-CHILD.

DO WHILE VALID-HANDLE(childHandle) :

IF childHandle:TYPE = "DIALOG-BOX" AND childHandle:VISIBLE
THEN DO :
forceModal = TRUE.
LEAVE checkModalBlock.
END.
ELSE
childHandle = childHandle:NEXT-SIBLING.
END.

windowHandle = windowHandle:NEXT-SIBLING.
END.
END.


IF NOT forceModal THEN DO :

/*
** Just set {&CONTAINER-NAME} to refer to the Window handle
*/

{&CONTAINER-NAME} = {&WINDOW-NAME}.
END.
ELSE DO :

/*
** Make sure the window is HIDDEN so enable_UI doesn't implicitly
realise it.
** Then create a DIALOG-BOX, and parent the main frame to it
*/

{&WINDOW-NAME}:HIDDEN = TRUE.

CREATE DIALOG-BOX {&DIALOG-BOX-NAME}
ASSIGN HIDDEN = TRUE
THREE-D = {&WINDOW-NAME}:THREE-D
SENSITIVE = {&WINDOW-NAME}:SENSITIVE
SCROLLABLE = FALSE
TITLE = {&WINDOW-NAME}:TITLE
WIDTH-PIXELS = FRAME {&FRAME-NAME}:WIDTH-PIXELS
+ {&DIALOG-BOX-NAME}:BORDER-LEFT-PIXELS
+ {&DIALOG-BOX-NAME}:BORDER-RIGHT-PIXELS
HEIGHT-PIXELS = FRAME {&FRAME-NAME}:HEIGHT-PIXELS
+ {&DIALOG-BOX-NAME}:BORDER-TOP-PIXELS
+ {&DIALOG-BOX-NAME}:BORDER-BOTTOM-PIXELS
TRIGGERS :
ON WINDOW-CLOSE PERSISTENT RUN {&applyEvent} IN
THIS-PROCEDURE ("WINDOW-CLOSE", {&WINDOW-NAME}).
ON END-ERROR ANYWHERE PERSISTENT RUN {&applyEvent} IN
THIS-PROCEDURE ("END-ERROR", {&WINDOW-NAME}).
ON ENDKEY ANYWHERE PERSISTENT RUN {&applyEvent} IN
THIS-PROCEDURE ("ENDKEY", {&WINDOW-NAME}).
END TRIGGERS.

FRAME {&FRAME-NAME}:FRAME = {&DIALOG-BOX-NAME}.

{&CONTAINER-NAME} = {&DIALOG-BOX-NAME}.
END.

RETURN.

END PROCEDURE.


PROCEDURE {&applyEvent} :

DEFINE INPUT PARAMETER eventName AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER eventWidget AS HANDLE NO-UNDO.

APPLY eventName TO eventWidget.

RETURN NO-APPLY.

END PROCEDURE.


&ENDIF

[/code]
 
Top