home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
pctchnqs
/
1991
/
number5
/
tvtsr
/
popedit.doc
next >
Wrap
Text File
|
1991-04-12
|
17KB
|
546 lines
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.