Launcher.st
author claus
Thu, 10 Aug 1995 15:14:54 +0200
changeset 110 570a38362ae1
parent 109 c23841df3616
child 111 b4ef3e799345
permissions -rw-r--r--
.

"
 COPYRIGHT (c) 1995 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

'From Smalltalk/X, Version:2.10.5 on 29-apr-1995 at 12:42:53 pm'!

ApplicationModel subclass:#NewLauncher
	 instanceVariableNames:'myMenu buttonPanel transcript infoView infoProcess helpIsOn'
	 classVariableNames:'CachedAboutIcon'
	 poolDictionaries:''
	 category:'Interface-Smalltalk'
!

!NewLauncher class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1995 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

version
"
$Header: /cvs/stx/stx/libtool/Launcher.st,v 1.21 1995-08-10 13:14:38 claus Exp $
$Revision: 1.21 $
"
!

documentation
"
    a new launcher, combining old Launchers menu and transcript into
    one view. (you no longer have to move two views around all the time ...).

    Also, this app makes first use of the new compatibility framework;
    you will notice, that this is a subclass of ApplicationModel.
    New applications will no longer be built as subclasses of standardSystemView.
"
!

customization
"
    Of course, it is possible to change the NewLauncher class itself
    as appropriate, however, we suggest you create a subclass (say MyLauncher),
    define it as autoloaded in the patches file, and redefined some methods
    there. That way, your changes are easier to reintroduce in case of
    an ST/X upgrade.


    adding more buttons to the button-panel:

      see the method #buttonPanelSpec;
	it defines a list of selectors and icons, which is used by 
	#setupButtonPanelIn:. 
	There, for each entry, a button with that icon is created, 
	which sends a selector-message to the launcher.

	Add entries to that list, and define appropriate methods.
	For example, to add a button which opens a drawTool,
	change #buttonPanelSpec to:

	buttonPanelSpec
	    ^ #(
		#(startSystemBrowser  'SBrowser32x32.xbm')
		#(startFileBrowser    'FBrowser32x32.xbm')
		#(nil nil)
		#(startChangesBrowser 'CBrowser32x32.xbm')
		#(nil nil)
		#(nil nil)
		#(startDrawTool       'DrawTool.xbm')
	     )

	the panel adjusts its height as appropriate - you may want to
	create new (small) icons for a good look.

    adding an entry to a menu:

	see the #setupMenu method; either add another top-menu, or
	add entries to an existing menu.
"
! !

!NewLauncher class methodsFor:'defaults'!

smallAboutIcon
    |image|

    image := self aboutIcon.
    image notNil ifTrue:[
	image := image magnifiedBy:0.4.
    ].
    ^ image

    "
     CachedAboutIcon := nil.
     NewLauncher aboutIcon.
     NewLauncher smallAboutIcon.
    "
!

aboutIcon
    |image|

    CachedAboutIcon notNil ifTrue:[^ CachedAboutIcon].

    image := Image fromFile:'SmalltalkX.xbm'.
    image notNil ifTrue:[
	|green dark|

	Display hasColors ifTrue:[
	    green := (Color red:0 green:80 blue:20) "darkened".
	    dark := Color grey:10.
	    image photometric:#palette.
	] ifFalse:[
	    dark := Color black.
	    green := Color white.
	    image photometric:#blackIs0.
	].
	image colorMap:(Array with:dark with:green).
	Display depth > 2 ifTrue:[
	    image := (Image implementorForDepth:Display depth) fromImage:image.
	]
    ].
    CachedAboutIcon := image.
    ^ image

    "
     CachedAboutIcon := nil.
     NewLauncher aboutIcon
    "
! !

!NewLauncher methodsFor:'queries'!

processName
    "for monitors only - my name"

    ^ 'ST/X Launcher'
! !

!NewLauncher methodsFor:'initialize / release'!

restarted
    "image restart - since WindowGroup recreates the process with
     default prio, we have to raise the prio again.
     Mhmh - this looks like a bug to me ..."

    Processor activeProcess priority:(Processor userSchedulingPriority + 1).
    super restarted
!

openInterface
    "sent by my superclass to open up my interface"

    |top icn w|

    top := StandardSystemView new.
    top label:'Smalltalk/X'.
    top extent:(400@300 ).

    "
     temporary kludge for SGI;
     this will be moved into StandardSystemView and be done
     automatically soon ...
    "
    Screen current serverVendor = 'Silicon Graphics' ifTrue:[
	icn := self class aboutIcon.
	icn notNil ifTrue:[
	    icn := icn magnifiedTo:86@68.
	    w := View extent:86@68. "/ icn extent.
	    w viewBackground:icn.
	    top iconView:w
	]
    ].

    self setupViewsIn:top.

    top application:self.   
    builder window:top.

    "
     open with higher prio to allow interaction even while things
     are running ...
    "
    top openWithPriority:(Processor userSchedulingPriority + 1).
!

addTopViewsToCurrentProject
    "ignored here - the launcher is always global (i.e. not project private)."

    ^ self
!

setupMenu
    "setup the pulldown menu"

    |l s icon|

    myMenu labels:(resources array:#(
				     'about'
				     'file'
				     'classes'
				     'tools'
				     'projects'
				     'settings'
				     'demos'
				     'help')).
    "
     if there is a bitmap, change 'about' to the ST/X icon
    "
    icon := self class smallAboutIcon.
    icon notNil ifTrue:[
	myMenu labels at:1 put:icon.
	myMenu height:(myMenu height max:(icon height + (View viewSpacing * 2)))
    ].

    myMenu selectors:#(
				     #about
				     #file
				     #classes 
				     #tools 
				     #projects 
				     #settings
				     #demos
				     #help).

    myMenu at:#about 
	   putLabels:(resources array:#(
					'about Smalltalk/X ...'
				       ))
	   selectors:#(
					#about 
		      )
	   receiver:self.

    myMenu at:#file
	   putLabels:(resources array:#(
					'file browser'
					'-'
					'snapshot'
					'snapshot & exit'
					'exit smalltalk'
				       ))
	   selectors:#(
					#startFileBrowser
					nil
					#snapshot
					#snapshotAndExit
					#exit
		      )
	   receiver:self.

    myMenu at:#classes 
	   putLabels:(resources array:#(
					'system browser'
					'class browser'
					'full class browser'
					'class hierarchy browser'
					'class tree'
					'-'
					'implementors'
					'senders'
					'-'
					'change browser'
					))
	   selectors:#(
					#startSystemBrowser 
					#startClassBrowser 
					#startFullClassBrowser 
					#startClassHierarchyBrowser 
					#startClassTreeView 
					nil
					#browseImplementors 
					#browseSenders 
					nil
					#startChangesBrowser 
		      )
	   receiver:self.

    myMenu at:#projects 
	   putLabels:(resources array:#(
					'new project'
					'-'
					'select project ...'
					))
	   selectors:#(
					#newProject 
					nil
					#selectProject 
		      )
	   receiver:self.

    myMenu at:#settings 
	   putLabels:(resources array:#(
					'language'
					'show keyboard mappings'
					'view style'
					'messages'
					'compilation'
					'misc'
					))
	   selectors:#(
					#languageSetting 
					#keyboardSetting 
					#viewStyleSetting 
					#messageSettings 
					#compilerSettings 
					#miscSettings 
		      )
	   receiver:self.

    self setupToolsMenu.
    self setupDemoMenu.

    ActiveHelp notNil ifTrue:[
	l := #(
		'\c active help'
		'ST/X documentation'
	      ).
	s := #(
		#toggleActiveHelp:
		#startDocumentationTool
	      )
    ] ifFalse:[
	l := #(
		'ST/X documentation'
	      ).
	s := #(
		#startDocumentationTool
	      )
    ].

    myMenu at:#help 
	   putLabels:(resources array:l)
	   selectors:s
	   receiver:self.
!

setupTranscriptIn:aView 
    |v|

    (Transcript notNil and:[Transcript ~~ Stderr]) ifTrue:[
	Transcript topView destroy.
    ].

    v := HVScrollableView 
		for:TextCollector
		miniScrollerH:true 
		miniScrollerV:false 
		in:aView.

    v origin:(0.0 @ (buttonPanel corner y + View viewSpacing)) 
      corner:(1.0 @ 1.0).
    transcript := v scrolledView.
    transcript beTranscript.
!

setupInfoViewIn:topView 
    |spc halfSpc|

    spc := View viewSpacing.
    infoView := Label label:'' in:topView.
    infoView adjust:#left; borderWidth:0.
    infoView level:-1.
    transcript superView bottomInset:(infoView height + spc).

    infoView is3D ifTrue:[
	halfSpc := spc // 2.
    ] ifFalse:[
	halfSpc := 0
    ].
    infoView topInset:(infoView height negated - spc + transcript borderWidth);
	     bottomInset:halfSpc;
	     leftInset:halfSpc; 
	     rightInset:halfSpc.
    infoView origin:0.0 @ 1.0 corner:1.0 @ 1.0.
    infoView model:self; aspect:#info; labelMessage:#info.

    Project notNil ifTrue:[
	Project addDependent:self.
    ]

    "
     NewLauncher open
    "
!

focusSequence
    ^ (Array with:myMenu) 
      , 
      (buttonPanel subViews select:[:element | element isKindOf:Button])
"/      , (Array with:Transcript)
!

setupViewsIn:topView 
    "create the pulldown menu, buttonPanel and transcript view"

    |tFont|

    topView model:self.

    myMenu := PullDownMenu in:topView.
    myMenu origin:0.0 @ 0.0 corner:(1.0 @ myMenu height).

    self setupMenu.
    self setupButtonPanelIn:topView.
    self setupTranscriptIn:topView.
    self setupInfoViewIn:topView.
    self setupOtherViewsIn:topView.

    tFont := transcript font.
    topView extent:((tFont widthOf:'3')*60) 
		    @ 
		    ((tFont height) * 20).

    "
     NewLauncher open
    "
!

setupOtherViewsIn:aTopView
    "a hook - allows redefinition in your personal subclass.
     For example, add a clock:"

"
    |sz clock space halfSpace|

    sz := buttonPanel innerHeight - (buttonPanel level abs*2).
    space := View viewSpacing.
    halfSpace := space // 2.

    buttonPanel rightInset:sz+(space  * 2).

    clock := ClockView in:buttonPanel topView.
    clock borderWidth:1.
    clock showSeconds:false.
    clock extent:(sz @ sz).
    clock origin:(1.0 @ (buttonPanel origin y + halfSpace)).
    clock leftInset:sz negated - 2 - halfSpace.
    clock rightInset:halfSpace.
    clock level:1.
"
!

setupButtonPanelIn:aTopView
    "create the buttonPanel"

    |spc mh|

    spc := View viewSpacing // 2.
    buttonPanel := HorizontalPanelView in:aTopView.
    buttonPanel level:-1; borderWidth:0.
    buttonPanel horizontalLayout:#leftSpace.

    self buttonPanelSpec do:[:entry |
	|sel b sep|

	sel := entry at:1.
	sel isNil ifTrue:[
	    sep := View in:buttonPanel.
	    sep extent:32@1; borderWidth:0.
	] ifFalse:[
	    b := Button in:buttonPanel.
	    b form:(Image fromFile:(entry at:2)).
	    b model:self; changeMessage:sel
	]
    ].

    mh := myMenu height.
    buttonPanel origin:0.0 @ (mh + spc)
		corner:(1.0 @ (mh + spc + buttonPanel preferredExtent y)).

    buttonPanel leftInset:spc; rightInset:spc.
!

setupDemoMenu
    "setup the demo pulldown menu"

    |m|

    myMenu at:#demos 
	   putLabels:(resources array:#(
					'goodies'
					'games'
					'geometric designs'
					'simple animations'
					'3D graphics'
					'graphic editors'
				       ))
	   selectors:#(
					goodies
					games
					geometricDesigns
					simpleAnimations
					#'3Dgraphics'
					#graphicEditors
		      )
	   receiver:self.

    "
     only to show two different ways of defining a popUpMenu,
     we use labels:selectors:receiver: here:
    "
    m := myMenu menuAt:#demos.
    m subMenuAt:#games
      put:(PopUpMenu
		labels:(resources array:#(
					   'Tetris'
					   'Tic Tac Toe'
					 ))
		selectors:#(
					   startTetris
					   startTicTacToe
			   )
		receiver:self).

    "
     and labels:selector:args:receiver: here:
    "
    m subMenuAt:#geometricDesigns
      put:(PopUpMenu
		labels:(resources array:#(
					   'Pen demo'
					   'Commander demo'
					 ))
		selector:#openDemo:
		args:#(
					   PenDemo
					   CommanderDemo
		      )
		receiver:self).

    m subMenuAt:#simpleAnimations 
      put:(PopUpMenu
		labels:(resources array:#(
					   'Animation'
					   'Globe demo'
					 ))
		selector:#openDemo:
		args:#(
					   Animation
					   GlobeDemo
		      )
		receiver:self).

    m subMenuAt:#'3Dgraphics' 
      put:(PopUpMenu
		labels:(resources 
			    array:#(
				'plane'
				'tetra'
				'cube (wireframe)'
				'cube (solid)'
				'cube (light)'
				'cube (light & texture)'
				'sphere (wireframe)'
				'sphere (light)'
				'planet'
				'teapot'
				'logo'
				'x/y graph'
			     ))
		selector:#openDemo:
		args:#(
				GLPlaneDemoView2
				GLTetraDemoView
				GLWireCubeDemoView
				GLCubeDemoView
				GLCubeDemoView2
				GLBrickCubeDemoView
				GLWireSphereDemoView
				GLSphereDemoView2
				GLPlanetDemoView
				GLTeapotDemo
				Logo3DView1
				GLXYGraph
		      )
		receiver:self).

    m subMenuAt:#graphicEditors 
      put:(PopUpMenu
		labels:(resources array:#(
					   'DrawTool'
					   'LogicTool'
					 ))
		selector:#openDemo:
		args:#(
					   DrawTool
					   LogicTool
		      )
		receiver:self).

    m subMenuAt:#goodies 
      put:(PopUpMenu
		labels:(resources array:#(
					'clock'
					'calendar'
					'-'
					'mail tool'
					'news tool'
					))
		selector:#openDemo:
		args:#(
					Clock 
					Calendar 
					nil
					MailView 
					NewsView
		      )
		receiver:self).
!

buttonPanelSpec
    "return a spec for the buttons in the panel;
     entries consists of selector and bitmap-filename.
     nil selectors are taken as separators (see setupButtonPanel)"

    ^ #(
	#(startSystemBrowser 'SBrowser32x32.xbm')
	#(startFileBrowser   'FBrowser32x32.xbm')
	#(nil nil)
	#(startChangesBrowser 'CBrowser32x32.xbm')
"/        #(nil nil)
"/        #(nil nil)
"/        #(startDocumentationTool 'book11.ico')
     )
!

release
    infoProcess notNil ifTrue:[
	infoProcess terminate.
	infoProcess := nil.
    ].
    super release
!

closeRequest
    (self confirm:(resources string:'really close %1 ?' with:self class name)) ifTrue:[
	super closeRequest
    ]
!

saveAndTerminate
    "
     some windowManagers can send this, to shutDown an application
     but let it save its state before, for restart. We are already
     prepared for this ;-)"

    self snapshot.
    super saveAndTerminate
!

setupToolsMenu
    "setup the tools pulldown menu"

    |m|

    myMenu at:#tools 
	   putLabels:(resources array:#(
					'workspace'
					'-'
					'monitors'
					'-'
					'view tree (all views)'
					'view tree'
					'inspect view'
					'destroy view'
					'-'
					'hardcopy'
					))
	   selectors:#(
					#startWorkspace 
					nil
					#monitors
					nil
					#startFullWindowTreeView 
					#startWindowTreeView 
					#viewInspect 
					#viewDestroy 
					nil
					#hardcopy 
		      )
	   receiver:self.


    m := myMenu menuAt:#tools.
    m subMenuAt:#monitors 
      put:(PopUpMenu
		labels:(resources array:#(
					   'process'
					   'memory'
					   'event'
					   '-'
					   'memory usage'
					 ))
		selectors:#(
					#startProcessMonitor
					#startMemoryMonitor 
					#startEventMonitor 
					nil
					#startMemoryUsageView 
			   )
		receiver:self).

    m subMenuAt:#hardcopy 
      put:(PopUpMenu
		labels:(resources array:#(
					   'screen'
					   'area'
					   'view'
					 ))
		selectors:#(
					#fullScreenHardcopy
					#screenHardcopy
					#viewHardcopy
			   )
		receiver:self).

! !

!NewLauncher methodsFor:'actions - classes'!

startFileBrowser
    self withWaitCursorDo:[FileBrowser open]
!

startChangesBrowser
    self withWaitCursorDo:[ChangesBrowser open]
!

startSystemBrowser
    self withWaitCursorDo:[SystemBrowser open]
!

startWorkspace
    Workspace open
!

startClassBrowser
    SystemBrowser askThenBrowseClass
!

startFullClassBrowser
    SystemBrowser askThenBrowseFullClassProtocol
!

startClassHierarchyBrowser
    SystemBrowser askThenBrowseClassHierarchy
!

browseImplementors
    |enterBox|

    enterBox := EnterBox title:(resources at:'Browse implementors of:') withCRs.
    enterBox okText:(resources at:'browse').
    enterBox action:[:selectorName |
	|cls|

	self withWaitCursorDo:[SystemBrowser browseImplementorsOf:selectorName]  
    ].
    enterBox showAtPointer
!

browseSenders
    |enterBox|

    enterBox := EnterBox title:(resources at:'Browse senders of:') withCRs.
    enterBox okText:(resources at:'browse').
    enterBox action:[:selectorName |
	|cls|

	self withWaitCursorDo:[SystemBrowser browseAllCallsOn:selectorName]  
    ].
    enterBox showAtPointer
!

startClassTreeView
    self withWaitCursorDo:[ClassTreeGraphView open]
! !

!NewLauncher methodsFor:'actions - settings'!

viewStyleSetting 
    |listOfStyles resourceDir dir box 
     list listView infoLabel infoForwarder newStyle|

    "
     search resources directory for a list of .style files ...
    "

    resourceDir := Smalltalk getSystemFileName:'resources'.
    resourceDir isNil ifTrue:[
	self warn:'no styles found (missing ''resources'' directory)'.
	^ self
    ].
    dir := FileDirectory directoryNamed:resourceDir.

    listOfStyles := dir select:[:aFileName | aFileName endsWith:'.style'].
    listOfStyles := listOfStyles collect:[:aFileName | aFileName copyWithoutLast:6].
    listOfStyles sort.

"/ old code: used a standard ListSelectionBox
"/ changed to intercept selection and add info-output.

"/    box := ListSelectionBox title:(resources string:'STYLE_MSG') withCRs.
"/    box label:(resources string:'Style selection').
"/    box list:listOfStyles.
"/    box initialText:View defaultStyle.
"/    box selectAction:[:newStyle |
"/    ].    
"/    box action:[:newStyle |
"/        transcript topView withCursor:Cursor wait do:[
"/            Transcript showCr:'change style to ' , newStyle , ' ...'.
"/            View defaultStyle:newStyle asSymbol.
"/        ]
"/    ].    
"/    box showAtPointer

"/ new code: build box 'by 'hand'
"/
    infoForwarder := Plug new.
    infoForwarder respondTo:#showInfo
		  with:[
			|nm sheet comment|
			nm := list selection.
			sheet := ViewStyle fromFile:(nm , '.style').
			comment := sheet at:#comment ifAbsent:''.
			infoLabel label:comment withCRs asStringCollection
		       ].

    list := SelectionInList with:listOfStyles.
    list onChangeSend:#showInfo to:infoForwarder.

    box := Dialog new.
    box label:(resources string:'Style selection').

    box addTextLabel:(resources string:'STYLE_MSG') withCRs.
    listView := SelectionInListView on:list.
    listView doubleClickAction:[:sel | box accept value:true. box hide].
    listView height:200.
    box addComponent:(ScrollableView forView:listView) tabable:true.
    box addVerticalSpace.

    (infoLabel := box addTextLabel:'\\' withCRs) adjust:#centerLeft.

    box addAbortButton; addOkButton.
    list selection:(View defaultStyle).
    box extent:(box preferredExtent).
    box minExtent:box extent.
    box maxExtent:box extent.

    box showAtPointer.

    box accepted ifTrue:[
	newStyle := list selection.
	newStyle notNil ifTrue:[
	    self withWaitCursorDo:[
		Transcript showCr:'change style to ' , newStyle , ' ...'.
		View defaultStyle:newStyle asSymbol.
	    ].
	    self reopenLauncher.
	]
    ]
!

languageSetting 
    |listOfLanguages box|

    "
     hardwiring here is stupid - need a resource file
     which lists languagenames which have all texts available
     in resource files ...
    "
    listOfLanguages := #('english'
			 'german'
			 'french'
			 'spanish'
			 'italian'
			).

    box := ListSelectionBox title:(resources string:'LANG_MSG') withCRs.
    box label:(resources string:'Language selection').
    box list:listOfLanguages.
    box initialText:(Language).
    box action:[:newLanguage |
	self withWaitCursorDo:[
	    Transcript showCr:'change language to ' , newLanguage , ' ...'.
	    Smalltalk at:#Language put:newLanguage asSymbol.
	    Smalltalk changed:#Language.
	    ResourcePack flushCachedResourcePacks
	].
	self reopenLauncher.
    ].    
    box showAtPointer
!

keyboardSetting 
    |mappings listOfRawKeys listOfFunctions
     box l
     list1 list2 listView1 listView2 
     frame infoLabel selectionForwarder macroForwarder macroTextView y|

    mappings := Screen current keyboardMap.

    listOfRawKeys := (mappings keys asArray collect:[:key | key asString]) sort.
    listOfFunctions := (mappings values asSet asArray collect:[:key | key asString]) sort.

    selectionForwarder := Plug new.
    selectionForwarder respondTo:#showFunction
		  with:[
			|raw|
			raw := list1 selection.
			list2 retractInterrestFor:selectionForwarder.
			list2 selection:(mappings at:raw asSymbol) asString.
			list2 onChangeSend:#showRawKey to:selectionForwarder.
		       ].
    selectionForwarder respondTo:#showRawKey
		  with:[
			|f raw|

			f := list2 selection.
			list1 retractInterrestFor:selectionForwarder.
			raw := mappings keyAtValue:f asString.
			raw isNil ifTrue:[
			    raw := mappings keyAtValue:f first.
			    raw isNil ifTrue:[
				raw := mappings keyAtValue:f asSymbol.
			    ]
			].
			list1 selection:raw.
			list1 onChangeSend:#showFunction to:selectionForwarder.
		       ].

    macroForwarder := Plug new.
    macroForwarder respondTo:#showMacro
		  with:[
			|f macro indent|
			f := list2 selection.
			(f startsWith:'Cmd') ifTrue:[
			    f := f copyFrom:4
			].
			macro := FunctionKeySequences at:(f asSymbol) ifAbsent:nil.
			macro notNil ifTrue:[
			    macro := macro asStringCollection.
			    indent := macro
					 inject:99999 into:[:min :element |
					     |stripped|

					     stripped := element withoutLeadingSeparators.
					     stripped size == 0 ifTrue:[
						 min
					     ] ifFalse:[
						 min min:(element size - stripped size)
					     ]
					 ].
			    indent ~~ 0 ifTrue:[
				macro := macro collect:[:line | 
					     line size > indent ifTrue:[
						line copyFrom:indent+1
					     ] ifFalse:[
						line
					     ].
					]
			    ].                        
			].
			macroTextView contents:macro.
		       ].

    list1 := SelectionInList with:listOfRawKeys.
    list1 onChangeSend:#showFunction to:selectionForwarder.
    list2 := SelectionInList with:listOfFunctions.
    list2 onChangeSend:#showRawKey to:selectionForwarder.
    list2 onChangeSend:#showMacro to:macroForwarder.

    box := Dialog new.
    box label:(resources string:'Keyboard mappings').

    l := box addTextLabel:(resources string:'KEY_MSG') withCRs.
    l adjust:#left.

    frame := View new.
    frame extent:300 @ 300.

    listView1 := ScrollableView for:SelectionInListView in:frame.
    listView1 model:list1.
    listView1 origin:0.0@0.0 corner:0.5@1.0; inset:2.

    listView2 := ScrollableView for:SelectionInListView in:frame.
    listView2 model:list2.
    listView2 origin:0.5@0.0 corner:1.0@1.0; inset:2.

    frame topInset:box yPosition.
    box addComponent:frame withExtent:350@200.
    box makeTabable:listView1. 
    box makeTabable:listView2. 
    frame origin:0.0@0.0 corner:1.0@0.6.

    box addVerticalSpace.

    l := box addTextLabel:(resources string:'Macro text (if any):') withCRs.
    l adjust:#left.
    l origin:0.0@0.6 corner:1.0@0.6.
    l topInset:(View viewSpacing).
    l bottomInset:(l preferredExtent y negated - View viewSpacing).

    macroTextView := HVScrollableView for:TextView miniScroller:true.
    box addComponent:macroTextView tabable:true.
    macroTextView origin:0.0@0.6 corner:1.0@1.0.
    y := box yPosition.

    box "addAbortButton;" addOkButton.
    macroTextView topInset:(l preferredExtent y).
    macroTextView bottomInset:(box preferredExtent y - y).

    box showAtPointer.

    box accepted ifTrue:[
    ]
!

compilerSettings
    |box warnings warnSTX warnUnderscore warnOldStyle allowUnderscore immutableArrays
     warnSTXBox warnUnderscoreBox warnOldStyleBox 
     stcCompilation compilationList stcCompilationOptions
     enabler|

    warnings := Compiler warnings asValue.

    warnSTX := Compiler warnSTXSpecials asValue.
    warnUnderscore := Compiler warnUnderscoreInIdentifier asValue.
    warnOldStyle := Compiler warnOldStyleAssignment asValue.
    allowUnderscore := Compiler allowUnderscoreInIdentifier asValue.
    immutableArrays := Compiler arraysAreImmutable asValue.
    stcCompilationOptions := #( always default never).
    stcCompilation := SelectionInList new list:(resources array:#('always' 'primitive code only' 'never')).
    stcCompilation selectionIndex:2.

    enabler := Plug new.
    enabler 
	respondTo:#check 
	with:[warnings value ifTrue:[
		warnSTXBox enable. 
		warnOldStyleBox enable.
		allowUnderscore value ifTrue:[
		    warnUnderscoreBox enable.
		] ifFalse:[
		    warnUnderscoreBox disable.
		].
	      ] ifFalse:[
		warnSTXBox disable. 
		warnUnderscoreBox disable.
		warnOldStyleBox disable.
	      ]].

    warnings onChangeSend:#check to:enabler.
    allowUnderscore onChangeSend:#check to:enabler.

    box := DialogBox new.
    box label:(resources string:'Compiler settings').
"/    box extent:200@300.

    box addCheckBox:(resources string:'allow underscore in identifiers') on:allowUnderscore.
    box addVerticalSpace.
    box addCheckBox:(resources string:'literal arrays are immutable') on:immutableArrays.
"/    box addVerticalSpace.
    box addHorizontalLine.
"/    box addVerticalSpace.

    "/ kludge for now (to get size computation right)

    compilationList := box addPopUpList:(resources string:'compilation to machine code') on:stcCompilation.
    stcCompilation selectionIndex:( stcCompilationOptions indexOf:(Compiler stcCompilation) ifAbsent:2).
    box addVerticalSpace.
"/    box addVerticalSpace.
    box addHorizontalLine.
"/    box addVerticalSpace.

    "/ if there is no compiler around,
    "/ change to compile nothing, and disable the checkBoxes
    Compiler canCreateMachineCode ifFalse:[
	stcCompilation selectionIndex:3.
	compilationList disable.
    ].

    box addCheckBox:(resources string:'warnings') on:warnings.
    box addVerticalSpace.
    box leftIndent:30.
    warnSTXBox := box addCheckBox:(resources string:'ST/X language extensions') on:warnSTX.
    box addVerticalSpace.
    warnUnderscoreBox := box addCheckBox:(resources string:'underscores in identifiers') on:warnUnderscore.
    box addVerticalSpace.
    warnOldStyleBox := box addCheckBox:(resources string:'oldStyle assignment') on:warnOldStyle.
    box addVerticalSpace.
    box leftIndent:0.

    box addAbortButton; addOkButton.

    enabler check.
    box showAtPointer.

    box accepted ifTrue:[
	Compiler warnings:warnings value.
	Compiler warnSTXSpecials:warnSTX value.
	Compiler warnOldStyleAssignment:warnOldStyle value.
	Compiler warnUnderscoreInIdentifier:warnUnderscore value.
	Compiler allowUnderscoreInIdentifier:allowUnderscore value.
	Compiler arraysAreImmutable:immutableArrays value.
	Compiler stcCompilation:(stcCompilationOptions at:stcCompilation selectionIndex)
    ]
!

messageSettings
    |box vmInfo vmErrors classInfos|

    vmInfo := ObjectMemory infoPrinting asValue.
    vmErrors := ObjectMemory debugPrinting asValue.
    classInfos := Object infoPrinting asValue.

    box := DialogBox new.
    box label:(resources string:'Messages').
"/    box extent:200@300.

    box addCheckBox:(resources string:'VM info messages') on:vmInfo.
    box addCheckBox:(resources string:'VM error messages') on:vmErrors.
    box addHorizontalLine.

    box addCheckBox:(resources string:'Other info messages') on:classInfos.

    box addAbortButton; addOkButton.
    box showAtPointer.

    box accepted ifTrue:[
	ObjectMemory infoPrinting:vmInfo value.
	ObjectMemory debugPrinting:vmErrors value.
	Object infoPrinting:classInfos value.
    ]
!

miscSettings
    |box logDoits compileLazy shadows updChanges catchRedefs|

    logDoits := Smalltalk logDoits asValue.
    compileLazy := Autoload compileLazy asValue.
    shadows := PopUpView shadows asValue.
    updChanges := Class updatingChanges asValue.
    catchRedefs := Class catchMethodRedefinitions asValue.

    box := DialogBox new.
    box label:(resources string:'Other settings').
"/    box extent:200@300.

    box addCheckBox:(resources string:'log compiles in changes file') on:updChanges.
    box addCheckBox:(resources string:'log doIts in changes file') on:logDoits.
    box addHorizontalLine.

    box addCheckBox:(resources string:'lazy compilation when autoloading') on:compileLazy.
    box addCheckBox:(resources string:'catch method redefinitions') on:catchRedefs.

    box addHorizontalLine.
    box addCheckBox:(resources string:'shadows under popup views') on:shadows.

    box addAbortButton; addOkButton.
    box showAtPointer.

    box accepted ifTrue:[
	Smalltalk logDoits:logDoits value.
	Autoload compileLazy:compileLazy value.
	PopUpView shadows:shadows value.
	Class updateChanges:updChanges value.
	Class catchMethodRedefinitions:catchRedefs value.
    ]
! !

!NewLauncher methodsFor:'infoview update'!

info
    |project projectName projectDir packageName|

    (Project isNil or:[(project := Project current) isNil]) ifTrue:[
	projectName := '* none *'.
	projectDir := '.'.
	packageName := '* none *'.
    ] ifFalse:[
	projectName := project name.
	projectDir := project directory.
	packageName := project packageName.
    ].

    ^ resources
	string:'project: ''%1''  fileOut to: ''%3''  package: ''%2'''
	  withArgs:(Array 
			with:projectName
			with:packageName 
			with:(projectDir contractTo:30))
! !

!NewLauncher methodsFor:'actions - about & help'!

toggleActiveHelp:aBoolean
    ActiveHelp notNil ifTrue:[
	helpIsOn := aBoolean.
	helpIsOn ifTrue:[
	    ActiveHelp start
	] ifFalse:[
	    ActiveHelp stop
	]
    ].
!

about
    |box|

    box := AboutBox new.
    box autoHideAfter:10 with:[].
    box showAtCenter
!

startDocumentationTool
    "
     although that one is not yet finished,
     its better than nothing ...
    "
    XtHTML notNil ifTrue:[
	"
	 temporary kludge;
	 not all machines can autoload binaries;
	 however, on my SGI (which can) we want it
	 to load automatically.
	"
	XtHTML isLoaded ifFalse:[
	    (OperatingSystem getSystemType = 'iris') ifTrue:[
		XtHTML autoload
	    ]
	].
	XtHTML isLoaded ifTrue:[
	    XtHTML openFull.
	    ^ self
	].
    ].

    self warn:'Sorry, the ST/X HTML reader is not (yet) 
included in this architectures release

Please use Mosaic, netscape, chimera or any
other HTML viewer to see the documentation.'.
! !

!NewLauncher methodsFor:'actions - tools'!

startProcessMonitor
    ProcessMonitor open
!

startMemoryMonitor
    MemoryMonitor open
!

startEventMonitor
    EventMonitor open
!

startMemoryUsageView
    MemoryUsageView open
!

startWindowTreeView
    |v|

    v := self pickAView.
    v notNil ifTrue:[
	WindowTreeView openOn:v topView
    ]
!

fullScreenHardcopy
    "after a second (to allow redraw of views under menu ...),
     save the contents of the whole screen."

    Processor addTimedBlock:[
	self saveScreenImage:(Image fromScreen) defaultName:'screen'
    ] afterSeconds:1
!

startFullWindowTreeView
    WindowTreeView open
!

viewInspect
    |v|

    v := self pickAView.
    v notNil ifTrue:[
	v topView inspect
    ]

!

viewDestroy
    |v|

    v := self pickAView.
    v notNil ifTrue:[
	v topView destroy
    ]
!

screenHardcopy
    "after a second (to allow redraw of views under menu ...),
     let user specify a rectangular area on the screen
     and save its contents."

    |area|

    Processor addTimedBlock:[
	area := Rectangle fromUser.
	(area width > 0 and:[area height > 0]) ifTrue:[
	    self saveScreenImage:(Image fromScreen:area) defaultName:'hardcopy'
	]
    ] afterSeconds:1
!

viewHardcopy
    "after a second (to allow redraw of views under menu ...),
     let user specify a view and save its contents."

    Processor addTimedBlock:[
	|v|

	v := Screen current viewFromUser.
	v notNil ifTrue:[
	    self saveScreenImage:(Image fromView:(v topView)) defaultName:'hardcopy'
	]
    ] afterSeconds:1
! !

!NewLauncher methodsFor:'actions - goodies'!

startNewsTool
    NewsView open
!

startMailTool
    MailView open
!

startClock
    RoundClock open
!

startCalendar
    Calendar open
! !

!NewLauncher methodsFor:'change & update'!

update:something with:aParameter from:changedObject
    ((something == #currentProject)
    or:[changedObject == Project]) ifTrue:[
	self changed:#info.
	^ self
    ].
! !

!NewLauncher methodsFor:'actions - file'!

snapshot
    |saveBox|

    saveBox := EnterBox title:(resources at:'filename for image:') withCRs.
    saveBox okText:(resources at:'save').
    saveBox action:[:fileName | 
	(ObjectMemory snapShotOn:fileName) ifFalse:[
	    "
	     snapshot failed for some reason (disk full, no permission etc.)
	    "
	    self warn:(resources string:'failed to save snapshot image (disk full or not writable)').
	]
    ].

    saveBox initialText:(ObjectMemory nameForSnapshot).
    saveBox label:'save image'.
    saveBox showAtPointer

!

exit
    (self confirm:(resources string:'Are you certain you want to exit without saving ?'))
    ifTrue:[
	self saveAllViews.
	Smalltalk exit
    ]
!

snapshotAndExit
    |saveBox|

    saveBox := EnterBox title:(resources at:'filename for image:') withCRs.
    saveBox okText:(resources at:'save & exit').
    saveBox action:[:fileName | 
	(ObjectMemory snapShotOn:fileName) ifFalse:[
	    "
	     snapshot failed for some reason (disk full, no permission etc.)
	     Do NOT exit in this case.
	    "
	    self warn:(resources string:'failed to save snapshot image (disk full or not writable)').
	] ifTrue:[
	    "
	     saveAllViews tells all views to shutdown neatly 
	     (i.e. offer a chance to save the contents to a file).

	     This is NOT required - all data should be in the snapshot ...
	     ... however, if remote disks/mountable filesystems are involved,
	     which may not be present the next time, it may make sense to 
	     uncomment it and query for saving - time will show which is better.
	    "
"
	    self saveAllViews.
"
	    Smalltalk exit
	]
    ].

    saveBox initialText:(ObjectMemory nameForSnapshot).
    saveBox label:'save image & exit'.
    saveBox showAtPointer

! !

!NewLauncher methodsFor:'actions - projects'!

newProject 
    Project notNil ifTrue:[
	(ProjectView for:(Project new)) open
    ]
!

selectProject
    |list box|

    Project notNil ifTrue:[
	list := Project allInstances.
	box := ListSelectionBox new.
	box list:(list collect:[:p | p name]).
	box title:(resources string:'select a project').
	box action:[:selection |
	    |project|

	    project := list detect:[:p | p name = selection] ifNone:[nil].
	    project isNil ifTrue:[
		Transcript showCr:'no such project.'
	    ] ifFalse:[
		project showViews.
		Project current:project
	    ]
	].
	box showAtPointer
    ]
! !

!NewLauncher methodsFor:'private'!

reopenLauncher
    "for now (since style & language settings currently do
     not affect living views ...)"

    |contents|

    contents := Transcript endEntry; contents.
    self class open.
    Transcript contents:contents; hideCursor; scrollToBottom; cursorToEnd; showCursor
!

saveAllViews
    "tell each topview that we are going to terminate and give it chance
     to save its contents."

    ObjectMemory changed:#aboutToExit
!

pickAView
    |v|

    (Delay forSeconds:1) wait.
    v := Screen current viewFromUser.
    v isNil ifTrue:[
	self warn:'sorry, this is not a smalltalk view'.
	^ nil
    ].
    ^ v
!

saveScreenImage:anImage defaultName:defaultName
    "save an image into a file 
     - ask user for filename using a fileSelectionBox."

    |fileName|

    fileName := Dialog
		    requestFileName:'save image in:'
		    default:(defaultName , '.tiff')
		    ok:'save'
		    abort:'abort'
		    pattern:'*.tiff'.

    fileName notNil ifTrue:[
	anImage saveOn:fileName
    ].
! !

!NewLauncher methodsFor:'actions - demos'!

openDemo:className
    (Smalltalk at:className) open

!

startTicTacToe
    TicTacToe open
!

startTetris
    Tetris open
! !

!NewLauncher methodsFor:'help'!

helpTextFor:aComponent
    |sel|

    aComponent == transcript ifTrue:[
	^ resources string:'this is the Transcript.\It is used to send\messages to the user.'
    ].

    aComponent == infoView ifTrue:[
	^ resources string:'this shows the name of the current\project and the directory\where a fileOut operation writes the file.'
    ].

    (buttonPanel subViews includes:aComponent) ifTrue:[
	"kludge: look for its change selector"
	sel := aComponent changeMessage.
	sel == #startSystemBrowser ifTrue:[
	    ^ resources string:'opens a\SystemBrowser'
	].
	sel == #startFileBrowser ifTrue:[
	    ^ resources string:'opens a\FileBrowser'
	].
    ].
    ^ nil
! !