[comp.windows.news] A dialog box class

korp@TRIPOLI.EES.ANL.GOV (11/06/89)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%                       litedialog.ps
%                       -------------
%
%                        Version 1.0
%
%               A dialog box class implementation
%
%	Date: Sept. 5 1989
%	Author:
%		David G. Zawada
%		EAIS-VIS
%		Argonne National Laboratory
%		zawada@athens.ees.anl.gov
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%	This file contains the class definition for a DialogBox
%	and a subclass of it, called ModeLess, that allows the
%	use of buttons within a DialogBox.  Icons, similar to
%	those used on the M*cintosh, also are supported,
%	provided the flag Icon? is set to true and the
%	PaintIcon procedure is not null.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%	Examples:
%
%
%/inch {72 mul} def
%/Quit? false def
%/quit_notify { Quit? {/destroy qdia send} if} def
%/no_notify {/Quit? false store /unmap qdia send quit_notify} def
%/yes_notify {/Quit? true store /unmap qdia send quit_notify} def
%/mesg (Do you really want to Quit?) def
%
%/qdia [( YES ) ( NO )] [/yes_notify /no_notify] mesg framebuffer
%  /new ModeLess send def
%{
%        /Icon? true def
%        /PaintIcon { Asterisk } def
%        10 4 inch 6 inch 3.5 inch 1.5 inch sizeit 
%	map
%} qdia send
%
%/quit_popdia { /unmap popdia send } def
%/popdia [( OK )] [/quit_popdia] (Click on Ok!) framebuffer /new ModeLess send def
%{
%        /Icon? true def
%        /PaintIcon { Man } def
%        10 6 inch 7.5 inch 3.7 inch 1.5 inch sizeit
%	map
%} popdia send
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%Load liteitems for subclasses
systemdict /Item known not {($NEWSHOME/lib/NeWS/liteitem.ps) run} if
systemdict begin
/Dialog Object
dictbegin		%Instance vars
	/ParentCanvas		null def
	/DialogCanvas		null def
	/DialogX		0 def
	/DialogY		0 def
	/DialogHeight		0 def
	/DialogWidth		0 def
	/DialogRadius		10 def      % Corner radius for DialogBox frame
	/DialogInterests	null def    % Event interests for the DialogBox
	/DialogEventMgr		null def
	/DialogBorderWidth	6 def
	/DialogCanvasColor	1 .7 .7 rgbcolor def
	/DialogBorderColor	0 0 0 rgbcolor def
	/DialogFontColor	DialogBorderColor def
	/DialogFontSize		14 def
	/DialogFont 		/Helvetica-Bold findfont DialogFontSize scalefont def
	/PaintProcs		null def    % Damage repair procedure
	/PaintIcon		nullproc def
	/Icon?			false def   % Does the DialogBox display an icon in it?
	/IconSize		45 def
	/IconBorderWidth	3 def
	/IconBoxColor		1 1 1 rgbcolor def

	% The following variables govern the positioning
	% and wrapping of text within the DialogBox
	/DialogTextX		0 def
	/DialogTextY		{DialogHeight TopMargin 2 mul sub} def
	/WordBreak		( ) def    % Break the DialogBox text word breaks (ie. spaces)
	/BreakWidth		null def
	/CurWidth		0 def
	/LastBreak		0 def
	/FirstChar		0 def
	/LastChar		0 def
	/DialogText		() def
	/RestText		() def
	/NextWord		() def
	/WordWidth		0 def
	/LineSpacing		2 def
	/TopMargin		DialogFontSize def	% Margins defining the region within
	/BottomMargin		DialogFontSize def	% which text may be displayed
	/LeftMargin		DialogFontSize def
	/RightMargin		DialogFontSize def
	/SentenceLength		0 def
dictend
classbegin
	/DialogPath		{rrectpath} def				% Shape of the DialogBox
	/IconBox		{0 0 IconSize IconSize rectpath} def	% Border for the Icon
	/EOLproc		{DialogTextX DialogTextY moveto show 	% End of Line procedure executed after each
			 	/DialogTextY DialogTextY DialogFontSize	% word break (See breakline routine below)
				LineSpacing add sub store} def


% PROCEDURES

/DrawDialog	% - => -  Paint the DialogBox
{
	gsave
		DialogCanvas setcanvas
		DialogCanvasColor fillcanvas
		/DialogTextX 0 def
		/DialogTextY { DialogHeight TopMargin 2 mul sub } def

		Icon? 	% Calculate the margins for the text display region
		{
			/SentenceLength DialogWidth IconSize LeftMargin RightMargin add add sub store
			/DialogTextX IconSize LeftMargin add store
			gsave
				LeftMargin 2 div DialogHeight TopMargin IconSize add sub translate
				IconBox
				gsave
					IconBoxColor setcolor fill
				grestore 
				DialogBorderColor setcolor IconBorderWidth setlinewidth stroke 1 setlinewidth
				gsave
					IconBox clip PaintIcon
				grestore
			grestore
		} 
		{
			/SentenceLength DialogWidth LeftMargin RightMargin add sub store
			/DialogTextX LeftMargin store
		}
		ifelse

		DialogBorderColor setcolor DialogBorderWidth setlinewidth
		DialogRadius 0 0 DialogWidth DialogHeight DialogPath stroke
		DialogFont setfont
		DialogFontColor setcolor
		breakline	% Position the text and wrap it if necessary
	grestore
} def


% Possible PaintIcon Routines (Asterisk and Man)

/wedge {0 0 moveto -10 40 rlineto 20 0 rlineto -10 -40 rlineto} def
/angle 45 def

/Asterisk % Need to notify user of something urgent
{
gsave
   gsave
      0 0 0 setrgbcolor IconBox fill
   grestore
         IconSize 2 div dup translate .45 .45 scale
         360 angle div round { wedge angle rotate} repeat 
         gsave 1 0 0 setrgbcolor fill grestore stroke
grestore
} def

/space {7 0 rmoveto} def
/speech {5 0 rlineto space 6 0 rlineto space 3 0 rlineto} def

/Man % Give user some general type of information
{
gsave
   IconBox gsave 0 setgray fill grestore stroke
	.45 .45 scale
	0 0 moveto 30 0 rlineto 0 40 rlineto 10 0 rlineto
	0 60 rlineto -40 0 rlineto 0 -100 rlineto 
	1 setgray gsave fill grestore
	55 80 rmoveto 35 0 rlineto 0 -40 rlineto 
	-45 -20 rlineto 10 20 rlineto 0 40 rlineto
	gsave fill grestore 
	0 setgray 
	4 -5 rmoveto speech 5 {-28 -5 rmoveto speech} repeat
	-58 -30 rmoveto -20 0 rlineto 10 20 rmoveto 10 0 rlineto
	stroke
	newpath 20 80 5 0 360 arc fill
grestore
} def


% METHODS
/new	 % statictext parentcanvas => dialogcanvas
{   
	/new super send begin
		/ParentCanvas exch store
		/DialogText exch ( ) append store
		/DialogCanvas ParentCanvas newcanvas store
		DialogCanvas /Transparent false put
		DialogCanvas /SaveBehind true put
		DialogCanvas /EventsConsumed /AllEvents put
		% Specify DialogBox event interests
		/DialogInterests 5 dict dup begin
			/DialogToTopEvent
				PointButton { DialogCanvas canvastotop }
				DownTransition DialogCanvas eventmgrinterest def
			/DialogDamageEvent
				/Damaged /RepairDialog
				null DialogCanvas eventmgrinterest def
		end store
		/DialogEventMgr DialogInterests forkeventmgr store
		currentdict
	end
} def

/destroy { DialogCanvas /Mapped false put currentprocess killprocessgroup } def

/sizeit  % r x y w h => -	Size the DialogBox
{  	
	/DialogHeight exch store
	/DialogWidth exch store
	/DialogY exch store
	/DialogX exch store
	/DialogRadius exch store

	gsave
		ParentCanvas setcanvas
		DialogX DialogY translate DialogRadius 0 0 DialogWidth DialogHeight DialogPath
		DialogCanvas reshapecanvas
	grestore
} def

/RepairDialog 	% - => - 	 Fork off process to repair any damage
{
	PaintProcs null ne 
	{
		gsave
			PaintProcs killprocessgroup
			DialogCanvas setcanvas newpath clipcanvas damagepath
		grestore
	} if
	/PaintProcs { newprocessgroup CallPaintProcs } fork def
} def

/CallPaintProcs 
{
	gsave
		DialogCanvas setcanvas
		damagepath clipcanvas
		DrawDialog
		newpath clipcanvas
		/PaintProcs null store
	grestore
} def

/map		% Map the DialogBox and bring it to the top of its siblings
{
	DialogCanvas /Mapped true put
	DialogCanvas canvastotop
} def

/unmap { DialogCanvas /Mapped false put } def	% Hide the DialogBox

/move  % x y => -	Move the DialogBox to the specified coordinates
{ 	gsave
		DialogCanvas setcanvas /DialogY exch store /DialogX exch store
		DialogX DialogY movecanvas
	grestore
} def

/printtext % string => -	Change the DialogBox' current message to the specified string
{
	/DialogText exch ( ) append store
	DrawDialog
} def

/breakline  % - => -		Adapted from the BREAKLINES program in Adobe's (Blue) PostScript CookBook
{  

	/BreakWidth WordBreak stringwidth pop store
	/RestText DialogText store
	/CurWidth 0 store
	/FirstChar 0 store
	/LastBreak 0 store
	/NextWord 0 store

	{
		RestText WordBreak search
		{
			/NextWord exch store pop
		 	/RestText exch store
		 	/WordWidth NextWord stringwidth pop store

		 	CurWidth WordWidth add SentenceLength gt
		  	{ 
				DialogText FirstChar LastBreak FirstChar sub getinterval EOLproc
		  	 	/FirstChar LastBreak store /CurWidth WordWidth BreakWidth add store
			}
		 	{
				/CurWidth CurWidth WordWidth add BreakWidth add store
			} 
		  	ifelse

			/LastBreak LastBreak NextWord length add 1 add store
		}
		{
			pop exit
		}
		ifelse
	} loop

	/LastChar DialogText length store
	DialogText FirstChar LastChar FirstChar sub getinterval EOLproc
} def
		  
classend def
/DefaultDialog Dialog def

%_______________________________________________________________________________
% ModeLess is a subclass of Dialog which facilitates the incorporation of any
% number of ButtonItems into a DialogBox

/ModeLess Dialog
dictbegin
	/ButtonLabels [] def		% Labels for the buttons
	/ButtonNotifies [] def		% Notify procedures for the buttons
	/DialogItems null def		% Array of ButtonItems
dictend
classbegin

% PROCEDURES
/CreateButton	% - => -	Create a button for each entry in ButtonLabels and store it in DialogItems
{
	/DialogItems
	[
		0 1 ButtonLabels length 1 sub
		{
			ButtonLabels 1 index get ButtonNotifies 3 -1 roll get
			DialogCanvas 60 0 /new ButtonItem send
			dup /ItemFrame 3 put
		} for
	] def

	DialogItems forkitems pop	% Activate the buttons
} def

/MoveButtons	% - => -	Evenly distribute the buttons along the bottom of the DialogBox
{
	0 1 DialogItems length 1 sub
	{
		DialogWidth DialogItems length 1 add div
		1 index 1 add mul DialogItems 2 index get /ItemWidth get 2 div sub
		BottomMargin 2 div /move
		DialogItems 5 -1 roll get send
	} for
} def

/DrawDialog	% - => -	Paint the DialogBox and its buttons
{
	/DrawDialog super send
	DialogItems paintitems
} def


% METHODS
/new 	% labels notifyprocs statictext parentcanvas => dialog_instance
{
	/new super send
	begin
		/ButtonNotifies exch def
		/ButtonLabels exch def
		CreateButton

		currentdict
	end
} def

/sizeit 	% r x y w h	Size the DialogBox and position its buttons
{
	/sizeit super send
	MoveButtons
} def

classend def
end

bwong@cbnewsc.ATT.COM (bruce.f.wong) (11/06/89)

Can someone help me understand why /destroy is designed the way it is
in the Lite toolkit ?

	destroy in Dialog:

/destroy { DialogCanvas /Mapped false put currentprocess killprocessgroup } def

	DestroyClient, called by /destroy, in LiteWindow:

/DestroyClient { % - => - (Destroy client canvas &c.)
%	currentprocess killprocessgroup
	FrameEventMgr null ne {FrameEventMgr killprocessgroup} if
} def

o Why would you want to do a killprocessgroup instead of a killprocess ?
  A killprocess will get rid of the event manager for that window but a
  killprocessgroup will get rid of other unrelated event managers.

o In Dialog, why unmap the DialogCanvas ?
-- 
Bruce F. Wong		ATT Bell Laboratories
att!iexist!bwong	200 Park Plaza, Rm 1B-232
708-713-5111		Naperville, Ill 60566-7050