home *** CD-ROM | disk | FTP | other *** search
- POPEDIT - A TSR Editor Based on Borland's TVEDIT Demonstration Program
- ----------------------------------------------------------------------
- Prepared by Richard Sadowsky, TurboPower Software, 4/91
-
- This document describes how to turn Borland's TVEDIT program into a useful
- TSR. Borland's license agreement prohibits the distribution of their demo
- program. By following these instructions, you can build a popup editor from
- their demo. We will call the popup editor POPEDIT.
-
- POPEDIT can be compiled as either a swappable or non-swappable TSR. A file
- named POPEDIT.INC will control whether POPEDIT is swappable or not. To create
- a swappable TSR, define the conditional Swappable in POPEDIT.INC as shown:
-
- {$DEFINE Swappable}
-
- To create a standard TSR, POPEDIT.INC should not define Swappable. Placing a
- period between the { and the $ will cause the text to be taken as a comment,
- not a conditional define, as in:
-
- {.$DEFINE Swappable}
-
- Now, we create the main program file for POPEDIT. The following is the
- contents of POPEDIT.PAS:
-
-
- {$M 8192,8192,655360}
- {$X+,S-,R-}
- program PopEdit;
- {-A popup version of Borland's TVEDIT program}
-
- {$I POPEDIT.INC}
- uses Dos, Objects, Drivers, Memory, Views, Menus, Dialogs,
- StdDlg, MsgBox, App, Buffers, Editors, PopEditM,
- {$IFDEF Swappable}
- OpSwap
- {$ELSE}
- OpTsr
- {$ENDIF}
- ;
-
- begin
- TsrInit;
- end.
-
-
- Now comes the hard part. We need to create a file called POPEDITM.PAS to
- implement the application code. The easiest way to do this is to copy
- TVEDIT.PAS to POPEDITM.PAS. Now follow the instructions below to create a TSR
- out of TVEDIT:
-
- 1) Delete all text up to the first occurance of the word const (except
- Borland's copyright). The first constant definition looks like this:
-
- const
- HeapSize = 32 * (1024 div 16)
-
- That means the program statement, the $M and other compiler directives,
- and the uses list all need to be deleted.
-
- 2) Above the word const, add the following lines:
-
- {$X+,S-,R-,V-}
-
- {$I OPDEFINE.INC}
- {$I POPEDIT.INC}
-
- unit PopEditM;
-
- interface
-
- uses Dos, Objects, Drivers, Memory, Views, Menus, Dialogs,
- StdDlg, MsgBox, App, Calc, Buffers, Editors,
- TvScreen,
- {$IFDEF Swappable}
- OpSwap1
- {$ELSE}
- OpInt,
- OpTsr
- {$ENDIF}
- ;
-
- 3) Scroll down until you come to the definition of the variable ClipWindow.
- After this declaration add the following lines:
-
- procedure TsrInit;
- {-Initialize our TSR and go resident}
-
- implementation
-
- 4) Executing a program within a TSR is quite a difficult task. TVEDIT
- contains a menu option for a DOS Shell. You need to comment out this
- feature. The easiest way to do this is just to comment out the body of
- the procedure DosShell. It may also be desirable to remove the Dos Shell
- option from the Files submenu (be careful with your parentheses if you do
- so).
-
- 5) Now go to the bottom of the file, and delete the main program block. This
- is the code that says:
-
-
- begin
- EditorApp.Init;
- EditorApp.Run;
- EditorApp.Done;
- end.
-
-
- 6) Now add the following large block of code (approximately 360 lines) to
- the end of the file after the definition of TEditorApp.OutOfMemory:
-
- {=====================================================================}
- const
- {$IFDEF Swappable}
- ResidentHeap = 256 * 1024;
- {$ELSE}
- ResidentHeap = 128 * 1024;
- {$ENDIF}
-
- MaxParas : Word = ResidentHeap div 16;
-
- ModuleName : String[7] = 'POPEDIT'; {module name for standard interface}
- OurHotKey : Word = $0844; {Alt + F10}
-
- {$IFDEF Swappable}
- SwapPathName : String[64] = 'C:\';
- SwapName1 : String[12] = 'POPEDIT1.$$$';
- SwapName2 : String[12] = 'POPEDIT2.$$$';
- {$ENDIF}
- FirstTimeInPopup : Boolean = True;
-
- procedure StartupDialog;
- var
- Control : Word;
- begin
- Control := MessageBox(^C'POPEDIT 1.0' +
- ^M^C'Installing as a TSR' +
- ^M^C'Press Alt-F10 to popup',
- Nil, mfInformation + mfOKCancel);
- if Control = cmCancel then begin
- EditorApp.Done;
- Halt;
- end;
- end;
-
- function ShutDownDialog : Boolean;
- {-Returns True if OK to shut down}
- var
- Control : Word;
- begin
- Control := MessageBox(^C'Unloading resident POPEDIT',
- Nil, mfInformation + mfOKCancel);
- ShutDownDialog := Control = cmOK;
- end;
-
- procedure ReadyBuffers;
- var
- H : Word;
- begin
- if FirstTimeInPopup then begin
- {if we've never been in popup, we need to init Buffers unit}
- H := PtrRec(HeapEnd).Seg - PtrRec(HeapPtr).Seg;
- if H > HeapSize then
- BufHeapSize := H - HeapSize
- else
- BufHeapSize := 0;
- InitBuffers;
- ClipWindow := OpenEditor('', False);
- if ClipWindow <> nil then begin
- Clipboard := ClipWindow^.Editor;
- Clipboard^.CanUndo := False;
- end;
- FirstTimeInPopup := False;
- end;
- end;
-
- procedure TurnOffTurboVision;
- begin
- Drivers.HideMouse;
- DoneEvents;
- DoneVideo;
- DoneMemory;
- end;
-
- procedure TurnOnTurboVision;
- begin
- InitMemory;
- InitVideo;
- InitEvents;
- (* InitSysError; *) {!! do not call in a popup !!}
- EditorApp.Redraw;
- Drivers.ShowMouse;
- end;
-
- procedure SaveMouse(var MSP : MouseStatePtr);
- var
- MStateSize : Word;
- begin
- MSP := nil;
- if TvScreen.MouseInstalled then begin
- MStateSize := TvScreen.MouseStateBufferSize;
- if (MStateSize = 0) or (MStateSize > MaxAvail) then
- Exit;
- TvScreen.SaveMouseState(MSP);
- TvScreen.InitializeMouse;
- end
- end;
-
- procedure RestoreMouse(MSP : MouseStatePtr);
- begin
- if (MSP <> nil) then
- TvScreen.RestoreMouseState(MSP);
- end;
-
- {$IFDEF Swappable}
- procedure PopEditMain; Far;
- {$ELSE}
- procedure PopEditMain(var Regs : Registers); Far;
- {$ENDIF}
- var
- MSP : MouseStatePtr;
- ScreenBuf : Pointer;
- XY, SL : Word;
-
- begin
- {save mouse, screen and cursor state}
- SaveMouse(MSP);
- ReinitVideo;
- if not InTextMode then begin
- RestoreMouse(MSP);
- Exit;
- end;
- if not SaveScreen(ScreenBuf) then begin
- RestoreMouse(MSP);
- Exit;
- end;
- GetCursorState(XY, SL);
- ReadyBuffers;
-
- {Turn TV back on}
- TurnOnTurboVision;
-
- {call application's Run method}
- EditorApp.Run;
-
- {Shut down TV again}
- TurnOffTurboVision;
-
- {restore mouse, screen and cursor state}
- RestoreScreen(ScreenBuf);
- RestoreCursorState(XY, SL);
- RestoreMouse(MSP);
- end;
-
- function AttemptDisable : Byte;
- begin
- if SafeToDisable then begin
- {turn TV on}
- ReadyBuffers;
- TurnOnTurboVision;
- {display unload dialog box, allow user to cancel unload}
- if ShutDownDialog then begin
- Drivers.HideMouse;
- {call the application's Done destructor}
- EditorApp.Done;
- {attempt to disable the TSR}
- AttemptDisable := Ord(DisableTSR);
- end
- else begin
- {user cancelled unload attempt}
- TurnOffTurboVision;
- AttemptDisable := 2;
- end;
- end;
- end;
-
- {$IFDEF Swappable}
- procedure OurDisablePopup; Far;
- begin
- LongInt(CSSwapData^.ThisIFC.UserData) := LongInt(AttemptDisable);
- end;
- {$ELSE}
- procedure OurDisablePopup(BP : Word); Interrupt;
- var
- Regs : IntRegisters absolute BP;
- SavePSP : Word;
- begin
- SavePSP := GetPSP;
- SetPSP(PrefixSeg);
- Regs.AL := AttemptDisable;
- SetPSP(SavePSP);
- end;
- {$ENDIF}
-
- procedure UnloadFromCommandLine;
- {-Unload resident copy of POPEDIT (if possible) and report results}
- var
- IFC : IfcPtr;
- {$IFDEF Swappable}
- SaveSwapMsg : Boolean;
- {$ELSE}
- Regs : IntRegisters;
- {$ENDIF}
- Result : Byte;
- begin
- RestoreAllVectors;
- IFC := ModulePtrByName(ModuleName); {get the IFCPtr for this module}
- if IFC <> nil then begin {make sure it is already installed}
- {$IFDEF Swappable}
- SaveSwapMsg := IFC^.CSDataPtr^.SwapMsgOn; {save state of swap messages}
- IFC^.CSDataPtr^.SwapMsgOn := False; {disable swap messages}
- IFC^.CmdEntryPtr; {call the CmdEntryPtr}
- Result := Byte(LongInt(IFC^.UserData));
- IFC^.CSDataPtr^.SwapMsgOn := SaveSwapMsg; {restore state of swap messages}
- {$ELSE}
- Regs.AH := Ord('U');
- EmulateInt(Regs, IFC^.CmdEntryPtr);
- Result := Regs.AL;
- {$ENDIF}
- case Result of
- 0 : WriteLn('Unable to unload POPEDIT');
- 1 : WriteLn('POPEDIT unloaded');
- 2 : WriteLn('Unload cancelled by user');
- else
- WriteLn('Serious error attempting to unload POPEDIT');
- end;
- end
- else
- WriteLn('POPEDIT not installed.');
- Halt;
- end;
-
- procedure Abort(S : String);
- begin
- WriteLn(S);
- Halt(1);
- end;
-
- procedure TsrInit;
- var
- CmdLine : String;
-
- begin
- if ParamCount > 0 then begin
- CmdLine := ParamStr(1);
- if (Length(CmdLine) = 2) and (CmdLine[1] in ['/','-']) and
- (UpCase(CmdLine[2]) = 'U') then begin
- UnloadFromCommandLine;
- Halt;
- end;
- end;
- {check to see if we're already installed}
- if ModuleInstalled(ModuleName) then
- Abort('POPEDIT is already loaded. Aborting...');
-
- {install the module with special external interface to allow disabling}
- {$IFDEF Swappable}
- InstallModule(ModuleName,OurDisablePopup);
- {$ELSE}
- InstallModule(ModuleName, @OurDisablePopup);
- {$ENDIF}
-
- {check to see if SideKick is loaded}
- if SideKickLoaded then
- Abort('Can''t be loaded after SideKick!');
-
- if not DefinePop(OurHotKey, PopEditMain,
- Ptr(SSeg, SPtr)
- {$IFNDEF Swappable}
- , True
- {$ENDIF}
- ) then
- Abort('Unable to define popup');
-
- EditorApp.Init;
-
- {display a dialog box, if user cancels, application will not go resident}
- StartUpDialog;
-
- {TEditorApp.Init initializes the clipboard window. It must be destroyed,
- since it uses Buffers and we are about to call DoneBuffers. The clipboard
- will be reinitialized by the popup the first time it is called. It would
- have been easy to modify TEditorApp.Init not to initialize the clipboard,
- but we wanted to avoid changing as much of the application code as possible
- to make it easier to describe how to make this TSR.}
- DeskTop^.Delete(ClipWindow);
- Dispose(ClipWindow, Done);
-
- {Shut down TV's system error handlers}
- DRIVERS.DoneSysError;
-
- {Shut down TV's usage of video, memory and events}
- TurnOffTurboVision;
-
- {Shut down Buffers unit}
- BUFFERS.DoneBuffers;
-
- {$IFDEF Swappable}
- {$IFDEF SupportXms}
- SwapUseXms := True;
- {$ENDIF}
- {$ENDIF}
-
- PopupsOn;
- {$IFDEF Swappable}
- {terminate and stay resident}
- StayResSwap(ParagraphsToKeep + MaxParas, 0,
- SwapPathName + SwapName1,
- SwapPathName + SwapName2, True);
- {if we get here we failed}
- {$ELSE}
- StayRes(ParagraphsToKeep + MaxParas, 0);
- {$ENDIF}
- WriteLn('Error going resident - press return');
- ReadLn;
- end;
-
- end.
-
-
- 7) This step is optional. A well behaved TSR should call interrupt 28h while
- waiting for keystrokes. If it fails to do so, no other TSR (that needs
- DOS services) will be able to popup if you are popped up over the DOS
- command line. The code Borland uses within Turbo Vision to read the
- keyboard does not call int 28h. A perfect place to call int 28h is within
- the TApplication's Idle method. To do so, you'll need to add the line
- marked with {!!} to the definition of the TEditorApp object:
-
- type
- PEditorApp = ^TEditorApp;
- TEditorApp = object(TApplication)
- constructor Init;
- destructor Done; virtual;
- procedure HandleEvent(var Event: TEvent); virtual;
- procedure InitMenuBar; virtual;
- procedure InitStatusLine; virtual;
- procedure OutOfMemory; virtual;
- procedure Idle; Virtual; {!!}
- end;
-
- Now we need to add an implementation for the Idle method. Somewhere after
- the definition of TEditorApp, add the following:
-
-
- procedure TEditorApp.Idle;
- begin
- TApplication.Idle;
- asm
- int 28h
- end;
- end;
-
-
- 8) Be sure you've created the file POPEDIT.INC and set it to the desired
- state (with Swappable defined or not). Now compile POPEDIT.PAS. In
- addition to the units used by TVEDIT, POPEDIT will use TVSCREEN, and
- either OPSWAP and OPSWAP1 (for swappable TSRs) or OPINT and OPTSR (for
- standard TSRs).
-
-
- Swappable versus Standard TSRs
- ------------------------------
- This program is most useful if turned into a swappable TSR. As a swapper, it
- will take less then 7K of RAM away from other applications. When its hotkey is
- invoked, it will swap out whatever is above it (to EMS, XMS, or disk, based on
- availability), and swap itself in. TurboPower's documentation explains the
- pros and cons of swappable TSRs.
-
- If you choose to make POPEDIT a standard TSR, it will take a good chunk of
- RAM. As coded above, it will take 240K bytes of RAM.
-
-
- Running POPEDIT
- ---------------
- To load POPEDIT as a RAM resident program (a TSR), simply type POPEDIT.
- POPEDIT will present you with a dialog box. If you select OK, then POPEDIT
- will attempt to go resident. If you select Cancel, POPEDIT will terminate
- without remaining resident.
-
- Note that for a swappable TSR the program shown above will swap to C:\ if EMS
- or XMS is not available. Therefore there would have to be sufficient hard disk
- space on C: for the swap files. See the TurboPower demonstration swappable
- TSRs DESKPOP or PSCREEN for examples of more flexible control over the
- resource used to swap to and from.
-
- The code above defines Alt-F10 as the hotkey. When the hotkey is pressed, the
- application will popup. It is then essentially the same as TVEDIT. To exit the
- popup and return to the underlying application (or DOS command line), press
- Alt-X or select Exit from the POPEDIT Files menu. When you press the hotkey
- again, POPEDIT will pop back up in the same state it was when you last exited.
-
- If you have any files opened and modified when you exit the popup, POPEDIT
- will ask if you wish to save them. It is a good idea when using a text editor
- implemented as a TSR to save your files before popping down (exiting the
- popup), so you don't forget to save them before turning the machine off or
- rebooting.
-
- POPEDIT has the ability to unload itself. This is done by running POPEDIT
- again with the command line switch /u. If it is safe to unload POPEDIT, you
- will be prompted by a dialog box. If you select OK, then POPEDIT will be
- unloaded. If you select CANCEL, then POPEDIT will not be unloaded. See the
- TurboPower documentation for information on unloading TSRs.
-
- POPEDIT can popup only over text video modes. If you try to popup over
- graphics, POPEDIT will politely refuse to popup.
-
-
- Technical Issues
- ----------------
- Turning TVEDIT into a TSR proved to be quite a chore. In addition to the
- typical problems encountered writing any TSR that uses Turbo Vision, TVEDIT
- introduced some interesting problems.
-
- In addition to calling TApplication.Init, TEditorApp.Init calls InitBuffers
- from the BUFFERS unit, and instantiates a TEditWindow object for its
- clipboard. The TEditWindow uses the facilities of the BUFFERS unit. BUFFERS is
- a unit that implements a simple moveable memory manager. In a TSR application,
- BUFFERS presents a problem, since the actual size of the heap isn't known
- until the application goes resident. Therefore, DoneBuffers has to be called
- before going resident (or alternatively, never call InitBuffers or instantiate
- the TEditWindow in the first place, we chose the DoneBuffers technique to
- limit the number of changes needed to Borland's source code). Before calling
- DoneBuffers, we must destruct any objects using memory from the BUFFERS unit.
- The code:
-
- DeskTop^.Delete(ClipWindow);
- Dispose(ClipWindow, Done);
-
- from the TSRInit procedure disposes of the PEditWindow instantiated in
- TEditorApp.Init, clearing the way for DoneBuffers to be called before going
- resident.
-
- Of course, this means that the first time we popup, the BUFFERS unit is
- uninitialized and no clipboard has been instantiated. The typed constant
- FirstTimeInPopup allows the popup to properly detect when it is popped up for
- the first time so InitBuffers can be called and the clipboard instantiated.
-
- The BUFFERS unit is tightly intertwined with the EDITORS unit. While objects
- from the EDITORS unit are instantiated, it would be a serious error to call
- DoneBuffers. Therefore the BUFFERS unit can not be turned on and off like many
- of the other Turbo Vision facilities. Since BUFFERS contains no event handlers,
- there is no need to turn it off once it has been initialized the first time
- the popup is called.
-
- Due to the use of the BUFFERS unit, code within the popup and to unload the
- TSR is more complicated then it is for simpler Turbo Vision programs.