Chapter 10 of the Turbo Pascal Reference The Turbo Vision Reference (continued) This chapter is part of the Turbo Pascal Reference electronic freeware book (C) Copyright 1992 by Ed Mitchell. This freeware book contains supplementary material to Borland Pascal Developer's Guide, published by Que Corporation, 1992. However, Que Corporation has no affiliation with nor responsibility for the content of this free book. Please see Chapter 1 of the Turbo Pascal Reference for important information about your right to distribute and use this material freely. If you find this material of use, I would appreciate your purchase of one my books, such as the Borland Pascal Developer's Guide or Secrets of the Borland C++ Masters, Sams Books, 1992. Thank you. For additional information on using Turbo Vision, including a detailed tutorial, please see Chapters 11 through 16 of the Borland Pascal Developer's Guide. TScrollBar object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TView TScrollBar Discussion Use TScrollBar to create scroll bar objects for list boxes, list viewers and anywhere else, such as text editors, where the user can scroll through text or lists. When scroll bars are linked to a TListBox or TListViewer, these objects automatically update the TScrollBar fields. As a result, when using TScrollBar in conjunction with the list viewers, all you need to do is Init a new scrollbar are the respective fields of TScrollBar are automatically set so that the scroll bar moves in tandem with movements in the list box. Conversely, whenever the scroll bar is adjusted with the mouse, the scrollbar uses the Message function to broadcast the changes to the owner of the scroll bar. Example { Define the location of the scroll bar relative to the List viewer } BarBounds := Bounds; BarBounds.B.X := BarBounds.B.X + 1; BarBounds.A.X := Bounds.B.X; { Create a new scroll bar at the specified location } ListScroller := New (PScrollBar, Init(BarBounds)); { Now create the list viewer (PDirList is derived from TListViewer) and link the list viewer to its scroll bar } ListObject := New(PDirList, Init(Bounds, 1, NIL, ListScroller )); { Insert both objects into the owning group window } Insert(ListScroller); Insert(ListObject); Commonly Used Features If you are using TScrollBar in conjunction with TListViewer or TListBox, you will mostly use only the Init method. However, if you are using scroll bars for other purposes, you will likely access the SetParams, SetRange, SetStep and SetValue methods. Fields ArStep: Integer; { Read only } This field holds the amount to change Value by when the up or down arrow keys are pressed, or if a mouse click is made in the up or down arrow icons. The default value is 1. See: TScrollBar.SetParams Max: Integer; { Read only } Max contains the upper limit for the scroll bar range of values. See: Min, Value, TScrollBar.SetParams, TScrollBar.SetRange Min: Integer; { Read only } Holds the minimum value for the scroll bar range. See: Max, TScrollBar.SetParams, TScrollBar.SetRange PgStep: Integer; { Read only } When the page up or down keys are pressed, or a mouse click is made in the paging area either side of the scroll bar marker, PgStep is the amount by which Value should be adjusted (PgStep is added or substracted to Value depending upon the direction). Value: Integer; { Read only } Value keeps track of the current location of the scroll bar marker (what the Macintosh people call a "thumb") that you can drag along the scroll bar with the mouse to perform quick scrolling manuevers. Values ranges from Min up to the maximum value, Max, set by calling TScrollBar.SetRange. See: Max, Min, TScrollBar.SetParams, TScrollBar.SetRange, TScrollBar.SetValue Methods procedure Draw; virtual; Draws the scroll bar. function GetPalette: PPalette; virtual; Returns a pointer to the CScrollBar palette, but this function may be overriden to return a pointer to a new palette. procedure HandleEvent(var Event: TEvent); virtual; Like most views, this is the central location for processing of the mouse and keyboard events that cause the scroll bar to change its position. constructor Init(var Bounds: TRect); Use Init to create the scroll bar object and to specify the Bounds where the scroll bar should be position. If the X width specifed by Bounds.A and Bounds.B equals 1, then a vertical scroll bar is created. For all other X widths, a horizontal scroll bar is created. constructor Load(var S: TStream); Creates and reads a scroll bar object from stream S. procedure ScrollDraw; virtual; Every time Value changes, ScrollDraw is called to send evBroadcast message to the owner of this scroll bar. See: cmXXXX constants, Message function ScrollStep(Part: Integer): Integer; virtual; Part is one of the sbXXXX constants, such as sbUpArrow or sbPageDown. ScrollStep determines by how much the Value field should change if one of these items was selected by a mouse click, and returns the result. See: sbXXXX constants procedure SetParams(AValue, AMin, AMax, APgStep, AArStep : Integer ); Use SetParams to initialize all of the various TScrollBar fields. See the field definitions provided above for more information. procedure SetRange(AMin, AMax: Integer); Call SetRange to set the Min and Max fields to the parameter values. Thesespecify the range over which the scroll bar will operate. For example, in a list of 100 items, you would write, SetRange(0, 100); procedure SetStep(APgStep, AArStep: Integer); PgStep and ArStep define how far to move the scroll bar marker for paging and single movements, respectively. Call SetStep to set the PgStep and ArStep fields to the parameter values. See: TScrollBar.SetParams, ArStep, PgStep fields procedure SetValue(AValue: Integer); Use SetValue to change the position of the scroll bar marker. This will cause the scroll bar to be redrawn with the marker at the new positions. procedure Store(var S: TStream); Writes the TScrollBar object to stream S. TScrollChars type ------------------------------------------------------------ Declaration: TScrollChars = array[0..4] of Char; Unit: Views Purpose: This is an internal type used inside TScrollBar to store the characters used to draw a TSCrollBar object on the display. TScroller object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TView TScroller TTextDevice TTerminal Discussion TScroller is primarily of use in providing functionality, ultimately for the descendant TTerminal type. Example See TTerminal. Fields Delta : TPoint; { Read only } The X and Y values of the TPoint record contain the relative positions of the scrolled positions. HScrollBar: PScrollBar; { Read only } Points to the horizontal scroll bar linked to this scroller, or is nil if there is no horizontal scroll bar. Limit: TPoint; { Read only } The X and Y fields of the TPoint record contain the maximum values for Delta's X and Y fields. VScrollBar: PScrollBar; { Read only } Points to the vertical scroll bar linked to this scroller, or is nil if there is no vertical scroll bar. Methods procedure ChangeBounds(var Bounds: TRect); virtual; Use ChangeBounds to change the size of the scroller. function GetPalette: PPalette; virtual; Returns a pointer to the default scroller color palette, CScroller, and may be overridden to provide other color mappings. procedure HandleEvent(var Event: TEvent); virtual; Handles TScroller events, most of which are passed to TView.HandleEvent. constructor Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar); Performs initialization of the TScroller object, giving it the size and position specified by Bounds, and linking it to the horizontal and vertical scrollbars passed to it as parameters. If there is no horizontal or vertical scrollbar, the respective parameters may be set to nil. constructor Load(var S: TStream); Creates and reads a TScroller object from stream S. procedure ScrollDraw; virtual; Tests the value of Delta to the current scrollbar positions. If the scrollbars have changed (or conversely, Delta has changed), then Delta is updated and the scroller is redrawn. procedure ScrollTo(X, Y: Integer); Sets the horizontal scroll bar to the positions specified by X, and the vertical scrollbar to the position specified by Y. procedure SetLimit(X, Y: Integer); Set's the Limit field to the values passed in X and Y, and updates the scrollbars. procedure SetState(AState: Word; Enable: Boolean); virtual; Passes its parameters to TView.SetState (see TView.SetState), and then if necessary, displays the scroll bars. procedure Store(var S: TStream); Writes this TScroller object to stream S. TSItem type ------------------------------------------------------------ Declaration: TSItem = record Value : PString; Next : PSItem; end; Purpose: TSItem is used in conjunction with NewSItem for creating a linked list of strings. See NewSItem for an example using this data type. TSortedCollection object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TCollection TSortedCollection TStringCollection Discussion When you want to have your collection sorted into some order (that you specify), use the TSortedCollection type. Items added to a TSortedCollection are always ordered by the order you specify with a custom, overridden TSortedCollection.Compare function. Chapter 14, "Collections" contains a tutorial on the use of collections and sorted collections and includes examples of the use of TSortedCollection. Because the important features are described in Chapter 14, some of the descriptions below are kept fairly brief. Commonly Used Features In addition to the inherited TCollection.Init, you'll use Insert to add new items to the collection, Done to dispose of the collection, and finally, you must override KeyOf and Compare. Example See Chapter 14, "Collections", in the Borland Pascal Developer's Guide, for numerous examples. Fields Duplicates: Boolean; { Read/Write } The default setting of Duplicates is False and this prohibits the addition of duplicate entries to the collection. If you change Duplicates to True, new duplicate entries will be inserted just before any other items having the same key. Methods function Compare(Key1, Key2: Pointer): Integer; virtual; Override: You must override Compare for your data object type. Write a new Compare function to compare Key1^ to Key2^ and return a value according to this: if Key1^ < Key2^ then return -1 if Key1^ = Key2^ then return 0 if Key1^ > Key2^ then return 1 See Chapter 14, "Collections". function IndexOf(Item: Pointer): Integer; virtual; Where Item is a pointer to an object type that is stored in the collection, IndexOf returns the Index position where it is located, or -1 if the Item is not found. procedure Insert(Item: Pointer); virtual; Adds the Item to the collection. If Item already exists in the collection, Insert checks the value of the Duplicates field. If Duplicates is False then the Item is not added, but if Duplicates is True, then the Item is inserted just prior to any duplicate entries. function KeyOf(Item: Pointer): Pointer; virtual; Override: You must override this function. KeyOf returns a pointer to the specific field within the object that is to be used as the sort key. For example, function TPersonCollection.KeyOf( Item: Pointer): Pointer; begin KeyOf := @PPersonInfo(Item)^.Name; end; Here, the Item pointer is recast to the data object data type so that you can access its fields. The address of the field to be used as the sort key is returned as the result. function Search(Key: Pointer; var Index: Integer): Boolean; virtual; Use Search to look for specific items in the collection. Search returns True if the item was found, or False otherwise. If found, Index is set to the location in the collection where the item resides. See the example in Chapter 14, "Collections" for how to set up the parameter values to use the Search function. TStaticText object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TView TStaticText Discussion Use TStaticText to display string messages within dialog boxes or windows. See: TParamText and TLabel. Commonly Used Features Init is used to create and initialize the static text area. None of the other methods are likely to be used. Example { The following code centers the status string in the message box. The Max(1, ) is to insure that in case the expression becomes negative, we still pass at least a 1 as the X coordinate } Bounds.Assign ( Max(1,30 - ((Length(Source) + Length(Dest)) div 2) ), 2, 58, 5); Dialog^.Insert( New(PStaticText, Init( Bounds, Source+ ' to '+Dest))); Desktop^.Insert( Dialog ); Fields Text: PString; Holds a pointer to the string that is displayed as the static text item. Methods constructor Init(var Bounds: TRect; AText: String ); Creates the TStaticText object at the location specified by Bounds, and copies the AText parameter to the Text^ string. If the Bounds defines a multi-line area, AText will be word wrapped within the rectangle. If AText begins with a Ctrl-C character (Chr(03)) the text will be centered within the Bounds area. You can break the text into multiple lines by inserting Ctrl-M (Chr(13)) characters at the beginning of each line break. constructor Load(var S: TStream); Creates and reads a TStaticText object from stream S. destructor Done; virtual; Disposes of the object. Is normally called automatically by the owning group, such as a dialog or window. procedure Draw; virtual; Displays Text^. function GetPalette: PPalette; virtual; Returns a pointer to CStaticText. Override this function if you wish to map to a different location in the color palettes. procedure GetText(var S: String); virtual; Returns the string contained in S. Equivalent to S:= Text^. procedure Store(var S: TStream); Writes this object to stream S. TStatusDef type ------------------------------------------------------------ Declaration: TStatusDef = record Next: PStatusDef; Min, Max : Word; Items: PStatusItem; end; Unit: Menus Purpose: TStatusDef records are used internally for storing the status line definition that is created with the NewStatusDef function. The various components of the record contain: Next pointers to the next TStatusDef record in a linked list of status line items. For the last item in the list, Next is set to nil. Min and Max are the minimum and maximum values of the help context constants for the items on the status line. Items points to a TStatusItem record, creating a list of the items displayed on the status line, or nil, if there are no items on the status line. TStatusItem type ------------------------------------------------------------ Declaration: TStatusItem = record Next: PStatusItem; Text: PString; KeyCode: Word; Command: Word; end; Unit: Menus Purpose: TStatusItem is an internal record structure used to hold each status line item, and is created by calling the NewStatusKey function. Next points to the next TStatusItem record, forming a linked list. Text points to the string label of the item, or is nil for invisible items. KeyCode stores the hot key scan code specified to activate the particular command. Command contains the cmXXXX constant that is sent in an event record when the hot key is pressed or the status line item is selected with a mouse. See: TStatusLine, NewStatusKey TStatusLine object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TView TStatusLine Discussion The TStatusLine is the object used to represent the status line displayed at the bottom of Turbo Vision applications. You create the status line with a call to it's Init constructor, and store a pointer to the status line into the global variable StatusLine. You can also use the TStatusLine object to define context senstivie hints that are displayed on the status line during the program's execution. Example See Chapter 11, "Turbo Vision Tutorial" for examples of status line creation and see the example given for the TStatusLine.Update procedure below, showing how to add "hint" context sensitive prompts to the status line. Fields Defs: PStatusDef; Points to the linked list of TStatusDef records. Items: PStatusItem; Points to the linked list of TStatusItems. Methods destructor Done; virtual; Disposes of the TStatusLine object. procedure Draw; virtual; Draws the status line at the bottom of the desktop. function GetPalette: PPalette; virtual; Returns a pointer to CStatusLine, but can be overridden to produce a different color mapping. procedure HandleEvent(var Event: TEvent); virtual; Handles mouse clicks on the status line, and generates the corresponding cmXXXX constant when status line items are selected. function Hint(AHelpCtx: Word): String; virtual; Hint translates a help context constant into a string that will be displayed by Draw to the right of the keys shown on the status line.. Generally it must be overridden to perform the desired translation for your application. See TStatusLine.Update for an example using the Hint context sensitive help facility. constructor Init(var Bounds: TRect; ADefs: PStatusDef); Creates a status line object at the location specified by Bounds, and sets up the linked list of status line items. See Chapter 11, "Turbo Vision Tutorial" for a discussion on the creation and use of the status line objects. constructor Load(var S: TStream); Creates and reads a status line object from stream S. procedure Store(var S: TStream); Writes the status line object to stream S, including all of the items defined for the status line. procedure Update; While an application has only one status line, the contents of that status line can change during the program's execution. In particular, when the menu bar is active or a pull down menu is visible, the status line can display a brief help message. The Update procedure is called internally by the status line object to determine which items should currently appear on the status line. To create "hints" that vary according to the currently active pulldown selection requires that a new TStatusLine-derived object be declared. The example code shown below is adapted from TVSHELL8 and begins with the overridden Hint function. PNewStatusLine = ^TNewStatusLine; TNewStatusLine = object(TStatusLine) function Hint(AHelpCtx: Word): String; virtual; end; In InitStatusLine, the StatusLine global variable should be created from the TNewStatusLine object, like this: StatusLine := New(PNewStatusLine, Init(Bounds, NewStatusDef(0, $FFFF, NewStatusKey('', kbF10, cmMenu, NewStatusKey('~Alt-X~ Exit', kbAltX, cmQuit, NewStatusKey('~F2~ Copy', kbF2, cmCopy, NewStatusKey('~F3~ Close', kbF3, cmClose, ... InitMenuBar, as part of the menu initialization, must set up appropriate help contexts for each item on the menu which will have an associated hint. Then, in the Hint function, the AHelpCtx parameter is compared to the help contexts for which a Hint is defined, and the appropriate help context string is returned. procedure TShell.InitMenuBar; { Purpose: Defines the pulldown menus used in Shell } var Bounds: TRect; begin TShell.GetExtent(Bounds); Bounds.B.Y := Bounds.A.Y + 1; MenuBar := New(PMenuBar, Init(Bounds, NewMenu( NewSubMenu('~R~un', hcNoContext, NewMenu( NewItem('~R~un', '', 0, cmRunProgram, hcRun, NewItem('~E~dit', 'Alt-E', kbAltE, cmEdit, hcEdit, NewItem('~V~iew', 'Alt-V', kbAltV, cmView, hcView, NewItem('~U~se DOS','Alt-D', kbAltS, cmUseDOS, hcNoContext, NewItem('E~x~it', 'Alt-X', kbAltX, cmQuit, hcNoContext, nil)))))), ... nil))) ))); end; { TShell.InitMenuBar } And finally, the Hint function itself is declared to produce a short hint text for each item on the pulldown menu, like this: function TNewStatusLine.Hint(AHelpCtx: Word): String; begin if AHelpCtx = hcRun then Hint:= 'Run an executable program' else if AHelpCtx = hcEdit then Hint:='Edit a text file' else if AHelpCtx = hcView then Hint := 'View a text file' else Hint := ''; end; { TNewStatusLine.Hint } Now, when you run the TVSHELL program, each time you drop down the run menu and move the cursor from item to item, the first 3 items will display a context sensitive hint or prompt on the status line. TStream object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TStream TDosStream TBufStream TEmsStream Discussion TStream is the root of the various stream objects. For your applications, you must use the descendants, TDosStream, TBufStream or TEmsStream. Example See Chapter 15, "Streams" in the Borland Pascal Developer's Guide, for numerous examples. Fields ErrorInfo: Integer; Whenever an error has occurred (indicated by Status <> StOk), the ErrorInfo field contains additional information. For some values these will be additional stXXXX constants, although for DOS-level file errors, ErrorInfo will contain the DOS error code. Status: Integer; Holds either stOk or the last error code. See the stXXXX constants for possible Status values. Methods procedure CopyFrom(var S: TStream; Count: Longint); This procedure copies Count bytes from stream S to the current stream object. procedure Error(Code, Info: Integer); virtual; The various stream methods call Error when a problem is encountered. Error, in turn, calls the procedure pointed to by the global variable StreamError (if its not nil). Once an error condition has been raised, you must call Reset before performing additional stream reads or writes. procedure Flush; virtual; TStream.Flush has no operation, but is overridden in by descendant objects, such as TBufStream, in order to write the buffer to disk. function Get: PObject; Reads an object from the stream, interpreting the first bytes as the ObjType field of a registeration record. The ObjType is used to determined exactly what type of object this is, and then to call that object's Load constructor to create a new instance. function GetPos: Longint; virtual; GetPos is overridden in all descendants to compute the current byte location within the stream. function GetSize: Longint; virtual; GetSize is overridden in all descendants to computer the total number of bytes in the stream. procedure Put(P: PObject); Writes the ObjType field of the registration record to the stream, and calls the object's Store method to write the appropriate data fields. procedure Read(var Buf; Count: Word); virtual; All descendants of TStream override this procedure to copy Count bytes from the stream into Buf, and to move the current stream position to the next object position. function ReadStr: PString; Reads a string from the stream and returns a pointer to that string. This function is used by TStringCollection.GetItem to read a string from the stream. procedure Reset; Clears any existing error conditions. procedure Seek(Pos: Longint); virtual; All descendants must override Seek such that it positions the stream's current position to the byte offset specified by Pos. procedure Truncate; virtual; All descendants must override Truncate to delete all data in the stream after the current position. procedure Write(var Buf; Count: Word); virtual; All descendants must override this procedure to copy Count bytes from Buf and write them to the stream. procedure WriteStr(P: PString); Outputs string P to the current stream. TStreamRec type ------------------------------------------------------------ Declaration: TStreamRec = record ObjType: Word; VmtLink: Word; Load: Pointer; Store: Pointer; Next: Word; end; Unit: Objects Purpose: The TStreamRec record type is used in registering objects prior to storing to or loading from a TStream. You register an object for stream I/O by initializing the fields of the TStreamRec and then passing the record as a parameter to the RegisterType procedure. Chapter 15, "Streams", contains complete details on performing stream I/O, including initializing and using this record type. The record's fields are used as follows: ObjType is a unique number from 1,000 to 65,535 that you choose to identify your object. VmtLink is an internal link to the object's virtual method table. Normally, you set this field equal to Ofs(TypeOf(TSomeObject)^) where TSomeObject is any object type. Load is a pointer to the object's Load constructor. Store is a pointer to the object's Store method. Next is a pointer to the next TStreamRec. You do not need to initialize this value. When you define your own variables as TStreamRec types, it is recommended that you begin each registration record variable name with the letter R as an aid to identifying registration records. Turbo Vision predefines registration records for all of its objects, each beginning with an 'R' instead of a 'T'. For example, TCollection's registration record is RCollection. See: Chapter 15, "Streams," RegisterType TStringCollection object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TCollection TSortedCollection TStringCollection Discussion TStringCollection is a special version of TSortedCollection that is prebuilt to produce sorted collections of strings. The use of TStringCollection is fully explained in Chapter 14, "Collections" in the Borland Pascal Developer's Guide. See TCollections, TSortedCollection Fields None Methods function Compare(Key1, Key2: Pointer): Integer; virtual; Compares Key1^ to Key2^ as strings and returns -1, 0 or 1. See TSortedCollection.Compare for details. procedure FreeItem(Item: Pointer); virtual; Where Item points to a string object, FreeItem deletes Item from the collection. function GetItem(var S: TStream): Pointer; virtual; Uses the TStream.ReadStr function to read a string from stream S and return a pointer to a PString containing the result. procedure PutItem(var S: TStream; Item: Pointer); virtual; Writes Item^ to stream S. TStrIndex type ------------------------------------------------------------ Declaration: TStrIndex = array[0..9999] of TStrIndexRec; Unit: Objects Purpose: This internal type is used by TStringList and TStrListMaker. TStrIndexRec type ------------------------------------------------------------ Declaration: TStrIndexRec = record Key, Count, Offset: Word; end; Unit: Objects Purpose: This internal type is used by TStringList and TStrListMaker. TStringList object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TStringList TStrListMaker Discussion TStringList and TStrListMaker are used to create string resource files. TStrListMaker should be used in a separate program to create the string resources, and TStringList should be used to access the previously created string resources. Because both objects have the same ObjType value in their stream registration record, its very important that these object types not appear in the same program, but should be used in separate programs. See Chapter 16, "Resources," TStrListMaker Fields None Methods destructor Done; virtual; Disposes of the string list. constructor Load (var S: TStream); Creates and reads the string list index from stream S. See Chapter 16, "Resources." function Get(Key: Word): String; Get is the primary function used to access individual strings in the string list. Key is a numeric value, typically a predefined constant, used to access particular strings in the resource file. See Chapter 16, "Resources." TStrListMaker object ------------------------------------------------------------ Turbo Vision Hierarchy Discussion TStrListMaker creates the string resources to be subsequently access by TStringList. See Chapter 16, "Resources," TStringList Methods destructor Done; virtual; Disposes of the string list. constructor Init(AStrSize, AIndexSize: Word); Init allocates memory for a string buffer having AStrSize bytes and index of AIndexSize elements. All of the strings that you add to the list are added into the string buffer, so AStrSize must be large enough to hold all the strings. procedure Put(Key: Word; S: String); Use Put to add each string into the string list, where Key is the index value to assign to the string, and S is the string value. The TStrListMaker appears to have no error checking so make sure that you do not add more elements than were specified by AIndexSize and which will fit into the string buffer. procedure Store(var S: TStream); Writes the string list to stream S. TSysErrorFunc type ------------------------------------------------------------ Declaration: TSysErrorFunc = function (ErrorCode: Integer; Drive: Byte) : Integer; Unit: Drivers Purpose: TSysErrorFunc defines what the system error handler function looks like. See SysErrorFunc and SystemError for details of intercepting system level errors. TTerminal object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TView TScroller TTextDevice TTerminal Discussion TTerminal stores byte values into a large circular queue buffer, and provides all the routines to manage and access the queue. TTerminal is normally used for creating terminal program-like scroll buffers that let you page back through the data that has come in over a modem. The scroll buffer is automatically displayed in a scrollable view on the screen, with attached horizontal and vertical scroll buffers. ASCII 10 characters in the text are used to delineate one line from the next. Commonly Used Features Other than Init, to construct and initialize the object, and Done to dispose of the object, you'll are likely to use only the CanInsert function, for determining if there is room to add more data to the scroll buffer without having to throwaway the oldest data. Example Borland provides a good example use of TTerminal in their sample program TVTXTDMO.PAS, contained in the TVDEMOS subdirectory (example \TP\TVDEMOS\TVTXTDMO.PAS as used in Turbo Pascal 6.0). The main body of that program creates a TTerminal object by instantiating a window (TTerminalWindow), which in turn inserts the TTerminal object into itself. The simple program then proceeds to read lines of text from a file and place them into the TTerminal scroll buffer, using, Writeln( T, St ); This looks pretty peculiar since T is a Text file. How is the data getting written to the scroll buffer? Earlier in the program, the statements, AssignDevice (T, PTerminal(Interior)); Rewrite( T ); created a link between the TTerminal scrolling buffer and the T text file so that subsequent reads or writes to T are redirected to the scroll buffer. Fields BufSize : Word; { Read only } Contains the size of the terminal's scroll buffer area, in bytes. It is set by TTerminal.Init. Buffer: PTerminalBuffer; { Read only } Points to the start of the buffer memory allocation. PTerminalBuffer is a pointer to an array of up to 64k bytes in size. QueBack : Word; { Read only } Together with QueFront, these variables are indexes into the buffer, with QueFront indexing the first character currently in the queue, and QueBack indexing the location of the last character stored in the scroll buffer. QueFront : Word; { Read only } See QueBack. Methods procedure BufDec ( var Val: Word ); This is an internal procedure that decrements Val 1, unless Val is at 0, in which case this sets Val to BufSize -1. BufDec, with BufInc, is used for moving the QueFront and QueBack indexes. procedure BufInc ( var Val : Word ); Like BufDec, increments Val by 1. If Val is at the end of the buffer, then resets Val to 0. function CalcWidth : Integer; Computes and returns the length of the longest line in the scroll buffer. This is useful for knowing how far it may need to scroll horizontally. function CanInsert ( Amount : Word ): Boolean; Use this function to see if there is space remaining in the scroll buffer. If adding Amount bytes to the buffer will require that the oldest data be deleted to make room, then CanInsert returns False. If there is enough room to avoid a deletion, CanInsert returns True. destructor Done; virtual; Cleans up this object instance, throwing away the buffer and calling TTextDevice.Done; procedure Draw; virtual; Displays the contents of the scroll buffer on the screen. constructor Init ( var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar; ABufSize : Word ); Creates and initializes an instance of TTerminal, assigning its location as determined by Bounds, and having the horizontal and vertical scroll bars passed to it. The size of the scroll buffer is set to ABufSize, which can be up to a maximum of 65,520 bytes. To determine if TTerminal.Init was unable to allocate the buffer, check to see if Buffer is non-nil after the call to Init. A nil value would mean that the allocation failed. function NextLine ( Pos : Word ) : Word; Where Pos is an index into the scroll buffer, NextLine scans forwards (logically, that is, since it may have to wrap around) to find the start of the next line, returning that position as its result. function PrevLines ( Pos : Word; Lines : Word ) : Word; Where Pos is an index into the scroll buffer, PrevLine scans backwards to return the start of the line that is Lines back from the current line. function StrRead ( var S : TextBuf ) : Byte; virtual; As implemented, StrRead returns 0 as its result but performs no other action. It needs to be overwritten if you wish to be able to read data from the scroll buffer. If you plan on overwritting this method, you definitely should refer to the source code for TTerminal.StrWrite, available in the Run Time Library Source package, available separately from Borland. function StrWrite ( var S : TextBuf; Count : Byte ); virtual; TextBuf is an array of character bytes. StrWrite adds Count copies of TextBuf to the scroll buffer queue. Embedded ASCII 13 characters are ignored; embedded ASCII 10 (line feed) characters are treated as the start of a new line and result in the start of a new line within the scroll buffer. function QueEmpty : Boolean; Returns True if the que is completely empty, False otherwise. TTerminalBuffer type ------------------------------------------------------------ Declaration: TTerminalBuffer = array[0..65519] of Char; Unit: TextView Purpose: An internal type declaration for use by Turbo Vision or Turbo Vision applications. TTextDevice object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TView TScroller TTextDevice Discussion TTextDevice is an abstract object type used by descendant objects. See TTerminal. Fields Dummy : Word; The purpose of this field is unknown. Methods function StrRead ( var S : TextBuf ): Byte; virtual; This is an abstract method that must be overridden in order to support reading data from a text device buffer. procedure StrWrite ( var S : TextBuf; Count : Byte ); virtual; This is an abstract method for sending output to the a text device buffer. It must be overridden to be useful. TTitleStr type ------------------------------------------------------------ Declaration: TTitleStr = string[80]; Unit: Views Purpose: Defines a type used by TWindow for window title strings. TVideoBuf type ------------------------------------------------------------ Declaration: TVideoBuf = array[0..3999] of Word; Unit: Views Purpose: This defines the internal type used in video buffer declarations in TGroup. Video buffers are used to store screen images in cache memory (see GetBufMem) for rapid screen update. TView object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TView Discussion TView is the parent of all visible objects in Turbo Vision. Generally, you will directly instantiate and use one of the descendants: TBackground, TButton, TCluster, TFrame, TGroup, THistory, TInputLine, TListViewer, TMenuView, TStatusLine, TStatusText, and their respective descendants. See the descriptions for objects listed above and also see Chapter 11, "Turbo Vision Tutorial" which describes many of these features. Commonly Used Features TView provides much of the functionality of all visible objects in Turbo Vision. As a consequence, TView has the most methods of any Turbo Vision object. While many of the methods are primarly used internally and to create new descendant-type objects, most applications will encounter at least the following TView methods: GrowMode, DragMode, HelpCtx, State, Options and EventMask. Commonly used methods include BlockCursor, ClearEvent, CommandEnabled, DataSize, DisableCommands, Draw, DrawView, EnableCommands, GetColor, GetCommands, GetHelpCtx, GetPalette, GetState, HideCursor, NormalCursor, Select, SetCommands, SetState, Show, ShowCursor, Valid, WriteLine and WriteStr. Fields Cursor: TPoint; { Read only } Records the position of the display cursor. DragMode: Byte; { Read/Write } The bits in DragMode indicate the view's dragging characterisitcs when dragged with the mouse. You must directly set a value to DragMode using the dmXXXX constants. See dmXXXX constants for more information on these settings. EventMask: Word; { Read/Write } Set EventMask to mask off or on the classes of messages that are accepted for processing by this view. A value of $FFFF means that the view will accept all messages. You set EventMask to a combination of the evXXXX constants. See evXXXX constants GrowMode: Byte; { Read/Write } The bit settings in GrowMode indicate how the view will change shape when it is resized. You must explicitly assign values to GrowMode. See gfXXXX constants for more information on these bit settings. HelpCtx: Word; { Read/Write } Holds the view's help context setting. You must explicitly store a value here unless there is no help, in which case HelpCtx will have a default of hcNoContext. Next: PView; { Read only } Next maintains a c ircular list pointing to the next view, in Z-order. Options: Word; { Read/Write } Set Options to determine event processing order (ofPreProcess, ofPostProcess) and to set other attributes defined by the ofXXXX constants. Origin: TPoint; { Read only } Describes the upper left corner of the view. Owner: PGroup; { Read only } Points to the TGroup that owns this view. Size: TPoint; { Read only } Contains the size of the view. State: Word; { Read only } The State bits retain information about many view options, including the cursor shape, if the cursor is visible or if the view is selected. See sfXXXX constants, TView.SetState, TView.GetState Methods procedure BlockCursor; Override: Do not override. Changes the cursor to the solid block cursor by setting the sfCursorIns bit in the State field. See TView.NormalCursor procedure CalcBounds(var Bounds: TRect; Delta: TPoint); virtual; CalcBounds is used internally to resize and shape this view in the case that the Owner's view was changed in size. procedure ChangeBounds(var Bounds: TRect); virtual; This internal procedure repositions the view. procedure ClearEvent(var Event: TEvent); In your HandleEvent methods or overridden HandleEvent methods, whenever you have finished processing an event, you must signal that the event is finished by calling ClearEvent, which sets Event.What := evNothing; and Event.InfoPtr := @Self so that other views can determine who it was that process the event. function CommandEnabled(Command: Word): Boolean; Use CommandEnabled to check if a specific command is currently enabled or allowed. Pass the cmXXXX value as the Command parameter and CommandEnabled will return True if the command is available now, or False if the command has been disabled. function DataSize: Word; virtual; Used in conjunction with GetData and SetData to copy the views data to and from a data record. See GetData, SetData procedure DisableCommands(Commands: TCommandSet); Commands is a set variable containing a set of commands, specified by their cmXXXX constant values, to be disabled. Calling DisableCommands causes these commands to become greyed out on the menus and status line. See: TView.DisableCommands destructor Done; virtual; Disposes of the view after erasing it from the screen. procedure DragView(Event: TEvent; Mode: Byte; var Limits: TRect; MinSize, MaxSize: TPoint); DragView handles redrawing the view while it is being dragged across the string. Limits defines the rectangle in which the view can be dragged, and MinSize and MaxSize set the minimum and maximum sizes to which the view can be resized. procedure Draw; virtual; The Draw method is the only method that should write data to the view's screen area. Any view that you create, including descendants such as TWindow or TApplication, must override the Draw method in order to display their output on the screen. Generally, your Draw method must keep track of where the screen image is relative to its internal data structure. For example, if your code implements a text editor, you do not need to draw all 500 lines that are in the current file. Instead, Draw would update only the lines that are actually visible. Sometimes you do not need to redraw the entire view because, perhaps, only a portion of the screen was overwritten by another view such as a dialog. Call GetClipRect to fetch the coordinates of the minimum area that needs updating. The use of GetClipRect can noticeably improve performance by minimizing the amount of time spent updating the screen. See TView.DrawView, TView.GetClipRect procedure DrawView; DrawView is the preferred method to call when you need to update the view. That's because DrawView makes a check to determine if the view is exposed (not hidden behind another view) before attempting to call Draw. Draw doesn't care if the view is visible since Turbo Vision will automatically clip away text that doesn't currently appear in a view. See TView.Draw procedure EnableCommands(Commands: TCommandSet); Commands is a set variable containing a set of commands, specified by their cmXXXX constant values, to be enabled. EnableCommands is the inverse of DisableCommands and restores commands to an operable state. procedure EndModal(Command: Word); virtual; Used internally in conjunction with ExecView for displaying modal views, such as dialogs, to terminate the modal view. See TGroup.EndModal, TGroup.Execute, TGroup.ExecView function EventAvail: Boolean; Returns True if an event is available. function Execute: Word; virtual; Execute is overridden in TGroup descendants to provide the event loop that makes the view a modal view. See TGroup.EndModal, TGroup.Execute, TGroup.ExecView function Exposed: Boolean; If at least some part of the view can be seen on the screen, then Exposed returns True. If the view is completely hidden, then Exposed returns False. procedure GetBounds(var Bounds: TRect); Returns the upper left and lower right corners of this view in Bounds, relative to the owner of the view. procedure GetClipRect(var Clip: TRect); Returns the upper left and lower right corners in Clip of the minimum sized area that needs to be redrawn. Uses this procedure in Draw to help locate only the area on the screen that needs to be updated. See TView.Draw function GetColor(Color: Word): Word; Color contains two color indexes, one in the high byte and one in the low byte. GetColor maps these indexes into the each color palette, in turn, going all the way back to the palette containing the video color attributes. These values are then returned in the corresponding high and low bytes of the result. procedure GetCommands(var Commands: TCommandSet); Use GetCommands to fetch a set containing all of the currently enabled commands. procedure GetData(var Rec); virtual; This procedure is overridden in descendants to copy the appropriate amount of view data to Rec. This procedure is primarily of interest to dialog box controls. procedure GetEvent(var Event: TEvent); virtual; Returns the next event from the event queue (typically called after calling TView.EventAvail) procedure GetExtent(var Extent: TRect); Similar to GetBounds, except that GetExtent sets Extent.A := (0,0) such that Extent.B, which is set to TView.Size, reflects the total extent of the view relative to the upper left corner. function GetHelpCtx: Word; virtual; Returns the TView.HelpCtx value. function GetPalette: PPalette; virtual; The default GetPalette returns nil, so most views will elect to override this function such that it returns a pointer to the color palette for this view. See Chapter 13, "More Turbo Vision Features" for an in depth discussion of color usage in Turbo Vision. procedure GetPeerViewPtr(var S: TStream; var P); Used by Load when certain objects need to load a peer view, P, from stream S, such as a list box needing to load it scroll bar object. See TView.Store, Chapter 15, "Streams" and Chapter 16, "Resources". function GetState(AState: Word): Boolean; AState can be set to multiple combinations of the sfXXXX constants and returns True if the the indicated bits are set in the TView.State variable. See TView.State, sfXXXX constants procedure GrowTo(X, Y: Integer); Calls TView.Locate to adjust the size of the view. procedure HandleEvent(var Event: TEvent); virtual; Every view must override the HandleEvent method. This is where events are recognized and parceled out to make the view come alive. For an example, see TVSHELL8.PAS, TShell.HandleEvent, in the Borland Pascal Developer's Guide. See evXXXX constants, cmXXXX constants, Chapter 11, "Turbo Vision Tutorial" procedure Hide; Hides the view. See TView.Show procedure HideCursor; Hides the cursor. See TV iew.ShowCursor constructor Init(var Bounds: TRect); Creates an initializes a TView object and places it according to the Bounds parameter. You may wish to directly assign values other than defaults, to State, Options, EventMask, GrowMode and DragMode. procedure KeyEvent(var Event: TEvent); KeyEvent ignores all incoming events until it gets an evKeyDown event, and it returns that event. constructor Load(var S: TStream); Creates and reads a view from stream S. procedure Locate(var Bounds: TRect); Changes the boundaries of the view and redisplays the view on the screen. procedure MakeFirst; Moves this view to the first position (closest to the screen) in the owner's Z-ordered list of views. procedure MakeGlobal(Source: TPoint; var Dest: TPoint); Use MakeGlobal (see also MakeLocal) to convert Source, which contains view relative coordinates, to screen coordinates. The converted value is returned in Dest. procedure MakeLocal(Source: TPoint; var Dest: TPoint); Converts the screen coordinates into view relative coordinates. See MakeGlobal. function MouseEvent(var Event: TEvent; Mask: Word): Boolean; Mask is an evMouse event. MouseEvent sets Event to the next mouse event and returns True if the Event.What field matches Mask, and False if when evMouseUp occurs. Typically, this function is called when a mouse is being dragged. To process the dragging operation, keep calling this routine until it returns False, meaning that the mouse button has been let up. function MouseInView(Mouse: TPoint) : Boolean; If the point described by Mouse (in screen coordinates) lies within this view, then MouseInView returns True. procedure MoveTo(X,Y: Integer); Keeping the view the same size, MoveTo moves the upper left corner, and hence the entire view, to the new point (X,Y). function NextView: PView; NextView returns a pointer to the next view, in sequence, in the owner's list of views, or nil if it has reached the end of the list. procedure NormalCursor; Sets the screen cursor to an underscore-style cursor. See TView.BlockCursor. function Prev: PView; Prev returns a pointer to the previous view, in sequence, in the owner's list of views, and cycles back to the beginning of the list if it has reached the end. function PrevView: PView; Identical to TView.Prev except that if PrevView reaches the beginning of the view list, PrevView returns nil. procedure PutEvent(var Event: TEvent); virtual; Using PutEvent, you can force one and only one event to be inserted as the next event in the event queue. Event will become the next event retrieved by GetEvent. procedure PutInFrontOf(Target: PView); Where Target is any view in this view's owner's view list, PutInFrontOf moves this view to be placed directly in front of Target. procedure PutPeerViewPtr(var S: TStream; P: PView); The Store method calls this routine to write the "peer" view pointer P to stream S. procedure Select; Select makes this view the active view in the group, and if the owning group is on the focused chain, then this view becomes the focused view. procedure SetBounds(var Bounds: TRect); This is an internal routine called by ChangeBounds. procedure SetCommands(Commands: TCommandSet); Sets the currently list of enabled commands to the set passed in the Commands parameter. See TView.EnableCommands, TView.DisableCommands procedure SetCursor(X, Y: Integer); Moves the screen cursor to (X,Y) where X and Y are local coordinates. procedure SetData(var Rec); virtual; Copies TView.DataSize bytes from Rec to the view's data fields. See TView.Datasize, TView.GetData procedure SetState(AState: Word; Enable: Boolean); virtual; Use SetState to either set or clear bits in the TView.State variable. If Enable is True, then the bits specified by AState are set, and if Enable is False, then the bits specified by AState are cleared. procedure Show; Causes the view to be displayed. See TView.Hide. procedure ShowCursor; Makes the screen cursor visible (the default condition is a hidden cursor). procedure SizeLimits(var Min, Max: TPoint); virtual; Sets Min to (0,0) and Max to Owner^.Size. procedure Store(var S: TStream); Writes this view to stream S. function TopView: PView; Returns a pointer to the view modal view that is on top. function Valid(Command: Word): Boolean; virtual; Each view contains aValid method which should be overridden to check for specific error conditions appropriate to your views. When Command equals cmValid, then the view should return False if some problem has occurred when instantiating the view. Use Valid to check for any type of errors - its up to you to define what those error conditions might be. An example might be a view that after initialization is unable to find its data file. Valid might call an error handling routine to display the error, and return False. If Command is not equal to cmValid, then Command equals the returns result from a modal dialog and the view can make specific checks based on this parameter. An example usage of Valid is given in Chapter 13, "More Turbo Vision Features". procedure WriteBuf(X, Y, W, H: Integer; var Buf); WriteBuf is called from with TView.Draw to copy the bytes from Buf to the location on the view specified by the coordinates (X,Y) and extending over W characters and down H lines. Buf is an array of words, with each word containing both a character to output and its corresponding video memory color attribute (See TDrawBuffer) procedure WriteChar(X, Y: Integer; Ch: Char; Color: Byte; Count: Integer); From within a TView.Draw method, WriteChar copies the character Ch, having the color index Color, to the screen, starting at (X,Y) and continuing for Count number of characters. procedure WriteLine(X, Y, W, H: Integer; var Buf); From within a TView.Draw method, WriteLine outputs Buf (which is an array of Words; see TDrawBuffer), starting at the view's coordinates (X,Y), and copies W bytes from Buf. Important! W should be exactly equal to the number of characters to copy from Buf. If larger than the contents of the string represented in Buf, then WriteBuf will copy data beyond the end of Buf and random data will appear at those locations. If H is more than 1, then WriteLine displays the contents of Buf on each of the succeeding lines. procedure WriteStr(X, Y: Integer; Str: String; Color: Byte); WriteStr is probably the most commonly used method for performing output within Draw methods. Use WriteStr to output the string Str to the screen, starting at (X,Y) and having the color index specified by Color. TWindow object ------------------------------------------------------------ Turbo Vision Hierarchy TObject TView TGroup TWindow Discussion TWindow is one of the core TView-derived objects, used for all types of windows including dialog boxes. Windows can have optional titles, window numbers, close and zoom icons. The TWindow object encapsulates and provides all the functionality need to perform window resizing, dragging, closing and includes features for maintaining scroll bars. Fields Flags: Byte; { Read/Write } Bit positions in the Flags byte are set using the wfXXXX constants to indicate if the window can grow, close, move or zoom. See wfXXXX constants Frame: PFrame; { Read only } Points to the TFrame object linked to this window. Number: Integer; { Read/Write } Contains the window's number which if in the range of 1 to 9, appears in the upper right corner of the window and allows the window to be directly selected with the Alt-n key combination. Palette: Integer; { Read/Write } Windows can choose from one of three different palettes, CBlueWindow, CCyanWindow or CGrayWindow depending upon the window's usage. Palette contains the vlaue of one of the constants wpBlueWindow, wpCyanWindow or wpGrayWindow, with wpBlueWindow as the default value. Title: PString; { Read/Write } Points to a string holding the window's title (if any). ZoomRect: TRect; { Read only } Specifies the normal window boundaries for the window, as specified by the Bounds passed to the Init constructor. During program execution, the window can be resized or shrunk; however ZoomRect always holds the original window coordinates so that if you choose the zoom icon, the window can be resized to its original coordinates. Methods procedure Close; virtual; Equivalent to calling TWindow.Done, and is normally called by the HandleEvent method when cmClose is received. destructor Done; virtual; Disposes of the window. function GetPalette: PPalette; virtual; Returns a pointer to one of the palettes, CBlueWindow, CCyanWindow or CGrayWindow, depending on the value of TWindow.Palette. function GetTitle(MaxSize: Integer): TTitleStr; virtual; This is an overridable function that returns Title^. However, you can override this function to truncate Title^ if its length exceeds MaxSize characters, or you can have it produce an abbreviated title string. procedure HandleEvent(Event: TEvent); virtual; You will probably override this method (before or after calling TWindow.HandleEvent), to handle your application specific event processing. You can intercept the cmClose and cmZoom commands here, as well has trap keyboard events, as in a word processor. constructor Init(var Bounds: TRect; ATitle: TTitleStr; ANumber: Integer); Call Init to create a new window having the size and position specified by Bounds, the title specified by ATitle, and a window number specified by ANumber. If ANumber is in the range 1 to 9, then the window can be selected directly from the keyboard by pressing Alt-n. procedure InitFrame; virtual; InitFrame is called by TWindow.Init to create the window's TFrame object border. If you wish to instantiate a custom TFrame object, override InitFrame to create your TFrame-derived object. constructor Load(var S: TStream); Creates and reads a window object from stream S. procedure SetState(AState: Word; Enable: boolean); virtual; Calls TView.SetState to set the inherited State bits, specified by AState, to True if Enable is True, or cleared if Enable is False. TWindow.SetStatehandles the window becoming the selected view, and enabling or disabling window-related commands such as cmNext and cmPrev, as needed. procedure SizeLimits(var Min, Max: TPoint); virtual; Calls TView.SizeLimits and sets TView.Min := MinWinSize, where MinWinSize is Turbo Vision global variable. See MinWinSize, TView.SizeLimits function StandardScrollBar(AOptions: Word): PScrollBar; By calling TWindow.StandardScrollBar, you can create and insert a scroll bar into the window; StandardScrollBar returns a pointer to the new scroll bar object. Set AOptions to sbVertical to create a vertical scroll bar that is automatically sized to just fit in the window at the right hand side, or set AOptions to sbHorizontal to create a perfectly sized scroll bar across the bottom of the window. If you OR sbHandleKeyboard with either sbVertical or sbHorizontal, the scroll bar will be created so that it will also handle up an down arrow keys, page up and down keys and other navigation keys. See sbXXXX constants procedure Store(var S: TStream); Writes the TWindow object to stream S. procedure Zoom; virtual; Zooms the window back to its ZoomRect specified size. TWordArray type ------------------------------------------------------------ Declaration: TWordArray = array[0..16383] of Word; Unit: Objects Purpose: Defines an array of words type for use by any application. wfXXXX constants ------------------------------------------------------------ TWindow flags Constant Value Usage wfMove $01 When bit is set, the window can be moved. wfGrow $02 When the bit is set, the window can be resized and has a "grow" icon in the lower right corner. If you clear this bit you can prevent a window from being resized. wfClose $04 Set this bit to add a close icon in the upper left corner of a window. Clear the bit to eliminate the icon. wfZoom $08 When this bit is set, the window contains a zoom icon for zooming a window to full size. See: TWindow, TWindog.Flags wnNoNumber constant ------------------------------------------------------------ Declaration: WnNoNumber = 0; Unit: Views Purpose: When ever a TWindow.Number field contains this value, it means that the window has no number. Windows that have no number do not display any number in the upper right corner, nor can they be selected with the Alt-number quick key selection for switching between windows numbered 1 through 9. WordRec type ------------------------------------------------------------ Declaration: WordRec = record Lo, Hi : Byte; end; Unit: Objects Purpose: You can use this type declaration to recast a word value so that you can access the low and high bytes. wpXXXX constants ------------------------------------------------------------ Window Palette selection contstants TWindows have 3 separate color palettes, as shown in the table below. CBlueWindow (indicated by wpBlueWindow) is normally used for text windows, CCyanWindow (indicated by wpCyanWindow) is normally used for messages, and CGrayWindow (indicated by wpGrayWindow) is normally used for dialog boxes. The wpXXXX constant, indicating the window color scheme, is stored in TWindow.Palette. You can alter a window's default color scheme, by assigning one of the wpXXXX constants to TWindow.Palette, typically after calling the window's Init constructor. Example, where DirWindow is TWindow-derived object pointer: DirWindow^.Palette := wpCyanWindow; Constant Value Usage wpBlueWindow 0 Yellow on blue color scheme wpCyanWindow 1 Blue on cyan color scheme wpGrayWindow 2 Black on gray color scheme