User's Guide

Implementing the private instance methods

Implement the remaining methods by, essentially, repeating steps 7 and 8 for each of the methods. That is: mark the code displayed in the description pane; delete it; replace it with a new method; and then save the new method.

The remaining methods are shown below. You can implement them in any order.

Private methods for category Getters & Setters

Method rectangle

rectangle
   "Private - Answers the current rectangle"
   ^rectangle

Method rectangle:

rectangle: aRectangle
   "Private - Sets the current rectangle"
   rectangle := aRectangle

Private methods for category Initialization

Method initialize

initialize
   "Private - Sets intial values for some of the variable."
   gc := CgWindow default
      createGC: None
      values: nil.
   gc setFunction: GXhighlight.
   captureMode := #none.

Private methods for category Window Creation

Method createWindow

createWindow
   "Private - Creates the interface. The outside window is 'shell.'
	The form that manages layout of widgets is 'widgetManager.'
	The options are on the top. The buttons 'Capture' and 'Help'
	are on the bottom. The button 'Display Image' can be added, and
	CgImageViewer used to view the grabbed file."
   self createTopLevelShell.
   self createFrame.
   self createRadioRowColumn.
   self createRadioButton:  ' Entire screen with this window ' 
         clientData: #fullScreen.
   self createRadioButton: 'Entire screen without this window ' 
         clientData: #fullScreenWithout.
   self createRadioButton: 'Partial screen with delay'
         clientData: #partialWithDelay.
   self createRadioButton: 'Partial screen without delay'
         clientData: #partial.
   self createCountLabel.
   self createText.
   self createButtonsRowColumn.
   self createCaptureButton.
   self createHelpButton.
   countDown := text.
   self initialize.

Method createTopLevelShell

createTopLevelShell
   "Private - Creates a window."
   shell := CwTopLevelShell
      createApplicationShell: 'shell'
      argBlock: [:w | w title: 'Screen Capture'].
   "This is to ensure that the CgGraphicsContext (gc)
	 is freed when the window is closed."
   shell
      addCallback: XmNdestroyCallback
      receiver: self
      selector: #destroy:clientData:callData:
      clientData: nil.

Method createShell

createShell
   "Private - Creates a shell for aWidget."
   shell :=
      CwTopLevelShell
         appCreateShell: self class name
         applicationClass: CwAppContext defaultApplicationClass
         display: CgDisplay default
         argBlock: nil.

Method createFrame

createFrame
   "Private - Creates the frame and a manager for widgets."
   widgetMan := shell
      createForm: 'widgetManager'
      argBlock: nil.
   widgetMan manageChild.
   frame := widgetMan
      createFrame: 'frame'
      argBlock: [:w | w
         topAttachment: XmATTACHFORM;
         topOffset: 2;
         leftAttachment: XmATTACHFORM;
         leftOffset: 5;
         rightAttachment: XmATTACHFORM;
         rightOffset: 5].
   frame manageChild.

Method createRadioRowColumn

createRadioRowColumn
   "Private - Creates a row column to hold buttons."
   radio := frame
      createRowColumn: 'radio'
      argBlock: [:w | w radioBehavior: true].
   radio manageChild.

Method createRadioButton:clientData:

createRadioButton: aString clientData: data
   "Private - Creates a radio button."
   | r |
   r := radio
      createToggleButton: aString
      argBlock: nil.
   r
      addCallback: XmNarmCallback
      receiver: self
      selector: #mode:clientData:callData:
      clientData: data.
   r manageChild.
   ^r

Method createCountLabel

createCountLabel
   "Private - Creates a label."
   countLabel := widgetMan
      createLabel: 'countLabel'
      argBlock: [:w| w
         labelString: 'Countdown : ';
         leftAttachment: XmATTACHPOSITION;
         leftPosition: 25;
         topAttachment: XmATTACHWIDGET;
         topWidget: frame;
         topOffset: 10].
   countLabel manageChild.

Method createText

createText
   "Private - Creates a text field."
   text := widgetMan
      createText: 'text'
      argBlock: [:w | w
         topAttachment: XmATTACHWIDGET;
         topWidget: frame;
         leftAttachment: XmATTACHWIDGET;
         leftWidget: countLabel;
         topOffset: 10;
         columns: 2].
   text setString: '5'.
   text manageChild.

Method createButtonsRowColumn

createButtonsRowColumn
   "Private - Creates a row column to hold buttons."
   buttons := widgetMan
      createRowColumn: 'buttons'
      argBlock: [:w | w
         orientation: XmVERTICAL;
         packing: XmPACKCOLUMN;
         numColumns: 2;
         topOffset: 10;
         leftOffset: 5;
         bottomOffset: 5;
         rightOffset: 5;
         topAttachment: XmATTACHWIDGET;
         topWidget: countLabel;
         leftAttachment: XmATTACHFORM;
         bottomAttachment: XmATTACHFORM].
   buttons manageChild.

Method createCaptureButton

createCaptureButton
   "Private - Creates a button."
   | captureButton |
   captureButton := buttons
      createPushButton: 'capture'
      argBlock: [:w | w labelString: ' Capture '].
   captureButton
      addCallback: XmNactivateCallback
      receiver: self
      selector: #captureCallback:clientData:callData:
      clientData: widgetMan.
   captureButton manageChild.

Method createHelpButton

createHelpButton
   "Private - Creates a button."
   | helpButton |
   helpButton := buttons
      createPushButton: 'help'
      argBlock: [:w | w
         labelString: ' Help ';
         marginTop: 3;
         marginBottom: 3].
   helpButton
      addCallback: XmNactivateCallback
      receiver: self
      selector: #help:clientData:callData:
      clientData: nil.
   helpButton manageChild.

Method realizeWindow

realizeWindow
   "Private - Realizes the receiver's widget hierarchy."
   shell realizeWidget.

Private methods for category Event Handlers

Method buttonPress:clientData:event:

buttonPress: aWidget clientData: widgetManager event: event
   "Private - The user has started to draw a rectangle."
   | point |
   point := widgetManager translateCoords: (event point).
   self rectangle: (Rectangle
      origin: (point x) @ (point y)
      extent: 0 @ 0 ).
   widgetManager
      removeEventHandler: ButtonPressMask
      receiver: self
      selector: #buttonPress:clientData:event:
      clientData: widgetManager.

Method buttonRelease:clientData:event:

buttonRelease: aWidget clientData: widgetManager event: event
   "Private - The user has finished drawing a rectangle."
   | point xE yE |
   point := widgetManager translateCoords: (event point).
   xE := (point x) - (self rectangle origin x).
   yE := (point y) - (self rectangle origin y).
   self rectangle extent: xE @ yE.
   widgetManager ungrabPointer.
   widgetManager
      removeEventHandler: ButtonReleaseMask
      receiver: self
      selector: #buttonRelease:clientData:event:
      clientData: widgetManager.
   self adjustRectangle.
   captureMode = #partial
      ifTrue: [
         self eraseCurrentRectangle.
         self saveGraphicImage]
      ifFalse: [self delayAndGrab]

Method captureCallback:clientData:callData:

captureCallback: aWidget clientData: widgetManager callData: callData
   "Private - The Capture button has been pressed. Processes
    the capture according to the capture mode."
   captureMode = #fullScreen ifTrue: [
      self setFullRectangle.
      ^self saveGraphicImage].
   captureMode = #fullScreenWithout ifTrue: [
      widgetManager shell unmapWidget.
      widgetManager updateDisplay.
      self setFullRectangle.
      self saveGraphicImage.
      ^widgetManager shell mapWidget].
   captureMode = #none ifFalse: [
      self getPartialFrom: widgetManager]

Method destroy:clientData:callData:

destroy: data1 clientData: data2 callData: data3
   "Private - Frees the graphics context."
   gc == nil ifFalse: [gc freeGC]

Method help:clientData:callData:

help: aWidget clientData: clientData callData: callData
   "Private - Displays the help message."
   System message:
'Select the capture mode, then press capture to start.
To draw a partial screen rectangle, point to any corner 
of the rectangle that you wish to capture. Press mouse 
button one and drag the rectangle to the correct size. 
Release the mouse button to capture.
 
The delay option gives you a chance to arrange the 
windows AFTER you draw the rectangle, but BEFORE 
the image capture. You can capture window menus 
this way. You may also hide the capture window during 
the delay. You can change the delay from 0 to 9 seconds.'

Method mode:clientData:callData:

mode: aButton clientData: clientData callData: callData
   "Private - A capture mode has been selected."
   captureMode := clientData

Method pointerMotion:clientData:event:

pointerMotion: aWidget clientData: widgetManager event: event
   "Private - Erases the old rectangle and replaces it with the new rectangle."
   | point xE yE |
   point := widgetManager translateCoords: (event point).
   state = #initial ifFalse: [self eraseCurrentRectangle].
   xE := (point x) - (self rectangle origin x).
   yE := (point y) - (self rectangle origin y).
   self rectangle extent: xE @ yE.
   CgWindow default
      drawRectangle: gc
      x: (self rectangle origin x)
      y: (self rectangle origin y)
      width: xE
      height: yE.
   state := #drawing.

Private methods for category Image Operations

Method adjustRectangle

adjustRectangle
   "Private - Makes sure that the rectangle has positive width and height."
   | xMin yMin |
   xMin := self rectangle origin x.
   yMin := self rectangle origin y.
   self rectangle width < 0 ifTrue: [
      xMin := self rectangle origin x + self rectangle width].
   self rectangle height < 0 ifTrue: [
      yMin := self rectangle origin y + self rectangle height].
   self rectangle: (xMin @ yMin
      extent: (self rectangle width abs) @ (self rectangle height abs)).
   self rectangle width < 1 ifTrue: [self rectangle width: 1].
   self rectangle height < 1 ifTrue: [self rectangle height: 1].

Method delayAndGrab

delayAndGrab
   "Private - Captures the rectangle after seven seconds.
	Forks this process so the user can adjust the windows during
	the countdown."
   | delay |
   delay := countDown value first digitValue.
   delay = -1 ifTrue: [delay := 7].
   [ delay to: 0 by: -1 do: [:count |
      (Delay forSeconds: 1) wait.
      countDown setString: count printString ].
      self eraseCurrentRectangle.
      self saveGraphicImage.
      countDown setString: delay printString.
   ] forkAt: Processor timingPriority

Method eraseCurrentRectangle

eraseCurrentRectangle
   "Private - Erases the capture rectangle. Since the GCFunction
	was set to highlight, a re-draw will erase it."
   CgWindow default
      drawRectangle: gc
      x: self rectangle origin x
      y: self rectangle origin y
      width: self rectangle width
      height: self rectangle height

Method getPartialFrom:

getPartialFrom: widgetManager
   "Private - Sets up the event handlers for a partial screen grab.
	Also grabs the pointer."
   state := #initial.
   widgetManager
      addEventHandler: ButtonPressMask
      receiver: self
      selector: #buttonPress:clientData:event:
      clientData: widgetManager.
   widgetManager
      addEventHandler: ButtonReleaseMask
      receiver: self
      selector: #buttonRelease:clientData:event:
      clientData: widgetManager.
   widgetManager
      addEventHandler: Button1MotionMask
      receiver: self
      selector: #pointerMotion:clientData:event:
      clientData: widgetManager.
   widgetManager grabPointer:
      (widgetManager display createFontCursor: XCCrosshair).

Method promptForFileName

promptForFileName
   "Private - Prompts the user to name the target BMP file."
   ^CwFileSelectionPrompter new
      title: 'Graphic image filename: ';
      prompt

Method saveGraphicImage

saveGraphicImage
   "Private - Captures the image in the current rectangle
    and unloads the image into the file named by the user."
   | image file name fileFormat success |
   image := (CgScreen default) rootWindow
      getDeviceIndependentImage: self rectangle.
   (name := self promptForFileName) == nil
      ifTrue: [ ^self ].
   "The following statement uses a class that defines a Windows-specific file
    format, CgWinBMPFileFormat. For an OS/2 file format, use in the following 
    statement the class CgPMBMPFileFormat. The system offers TIFF, GIFF,
    IPEG, and ICO file formats as well. You can use whatever file format your 
    graphics tools support, and capture screens in any format on any platform."
   fileFormat := CgWinBMPFileFormat new.
   (file := CfsFileDescriptor
      open: name
      oflag: OCREAT | OTRUNC | OWRONLY) isCfsError
         ifTrue: [
            ^(CwMessagePrompter new
               iconType: XmICONERROR;
               buttonType: XmOK;
               messageString: file message;
               prompt)].
   success := fileFormat
      unload: image
      intoFileHandle: file
      atOffset: 0.
   file close.
   success
      ifFalse: [
         ^(CwMessagePrompter new
            iconType: XmICONERROR;
            buttonType: XmOK;
            messageString: 'Error saving graphic image';
            prompt)]
      ifTrue: [
         CwMessagePrompter new
            messageString: 'Graphic image saved';
            buttonType: XmOK;
            prompt ]

Method setFullRectangle

setFullRectangle
   "Private - Sets rectangle for a full screen grab."
   self rectangle: ( 0@0 corner:
      (CgScreen default width) @ (CgScreen default height))


[ Top of Page | Previous Page | Next Page | Table of Contents | Index ]