Mai számunkban olyan kontrollokat írok le, amit az előző számokban még nem tettem meg, de fontosnak tartok. Az előző számban a scrollbar-okról (gördítősáv) volt szó, így adott minden feltétel ahhoz, hogy először is egy editáló – "szöveg-szerkesztő" ablakocskát készítsünk.
A TEditWindow átírása đ TPCXEditWindow
Mindenek előtt helyesen be kell állítani a TPCXDesktop elhelyezkedését, mert ezt már rég elmulasztottuk, amikor a TTitleBar-t deklaráltuk. Ezt most a PCX_APP.pas unit-ban a TPCXApplication InitDeskTop() metódusában kell megtennünk. Ez a régi forráskód:
procedure TPCXApplication.InitDeskTop;
var R: TRect;
begin
GetExtent(R);
Inc(R.A.Y); {Jó változat: Inc(R.A.Y, 2); }
Dec(R.B.Y);
DeskTop:=New(PPCXDeskTop, Init(R));
end;
S az Inc(R.A.Y); utasítást kell kijavítanunk Inc(R.A.Y, 2);-re, hogy a desktop a második sorban kezdődjön, s ne az elsőben. Ezáltal már jó helyre helyezhetjük az ablakokat, ha a Desktop^.GetExtent() metódust használjuk mely – mint tanultuk – az adott view kiterjedését adja vissza az Owner-ének koordináta-rendszeréhez képest.
Most, hogy kijavítottuk ezt a TFileEditor view-objektumnak kell valami tisztességes háttérszínt adni a megszokott GetPalette() metódussal.
type
PPCXFileEditor = ^TPCXFileEditor;
TPCXFileEditor = Object(TFileEditor)
function GetPalette: PPalette; virtual;
end;
function TPCXFileEditor.GetPalette: PPalette;
const P: String[Length(CPCXFileEditor)] = CPCXFileEditor;
begin
GetPalette:=@P;
end;
Hogy ez az objektum, hogy csatlakozik a TEditWindow-hoz ? Ez annak egy mezője, s ez végül is egy view, egy TEditor ős view mely, hivatott egy szöveg ablak – ill. TXT file tartalmának kiíratására. Maga a TWindow-ból lesz a TEditorWindow, ez a keretfelület, s ebbe jön bele a TFileEditor.
A TEditWindow objektumot azért kell átírnunk, hogy szép kerete (frame) ill. saját, új scrollbar-jainkat használja. Lássuk a származtatás ill. az implementálás menetét:
type
PPCXEditWindow = ^TPCXEditWindow;
TPCXEditWindow = Object(TEditWindow)
constructor Init(var Bounds: TRect;
FileName: FNameStr; ANumber: Integer);
procedure InitFrame; virtual;
function GetPalette: PPalette;
virtual;
procedure HandleEvent(var Event: TEvent);
virtual;
private
ControlBoxWin: PPCXControlBoxWin;
end;
constructor TPCXEditWindow.Init(var Bounds: TRect;
FileName: FNameStr; ANumber: Integer);
var
P: TPCXPoint;
HScrollBar, VScrollBar: PPCXScrollBar;
Indicator: PPCXIndicator;
R: TRect;
begin
Inherited Init(Bounds, '', ANumber);
Options := Options or ofTileable;
R.Assign(18, Size.Y - 1, Size.X - 2, Size.Y);
HScrollBar := New(PPCXScrollBar, Init(R));
HScrollBar^.Hide;
Insert(HScrollBar);
if IsPCXGraphCharsOn
then R.Assign(Size.X - 2, 1, Size.X, Size.Y - 1)
else R.Assign(Size.X - 1, 1, Size.X, Size.Y - 1);
VScrollBar := New(PPCXScrollBar, Init(R));
VScrollBar^.Hide;
Insert(VScrollBar);
R.Assign(2, Size.Y - 1, 16, Size.Y);
Indicator := New(PPCXIndicator, Init(R));
Indicator^.Hide;
Insert(Indicator);
GetExtent(R);
R.Grow(-2, -1);
Editor := New(PPCXFileEditor, Init(R, HScrollBar, VScrollBar,
Indicator, FileName));
Insert(Editor);
P.Assign(0,0);
New(ControlBoxWin, Init(P));
Insert(ControlBoxWin);
end;
Lássuk szép sorjában, hogy mi is történik:
procedure TPCXEditWindow.InitFrame;
var
R: TRect;
begin
GetExtent(R);
Frame := New(PPCXFrame, Init(R));
end;
A TPCXEditWindow HandleEvent()-je így néz ki:
procedure TPCXEditWindow.HandleEvent(var Event: TEvent);
begin
if Event.KeyCode=kbAltSpace then ControlBoxWin^.ExecControlMenuBox;
Inherited HandleEvent(Event);
if Event.Command = cmNewLine then
begin
Event.KeyCode:=Event.KeyCode and (Not
kbAltSpace);
Event.Command:= Event.Command and (Not
cmNewLine);
end;
end;
Itt csak azért kell variálni, mert az Alt+Space-nek a Turbo Vision billentyű konvertálása után az lesz a kódja, mintha új sort nyomtunk volna. Így egy faramuci módon kell lekezelni.
Ennek figyelése úgy néz ki, hogy még mielőtt
az ősök HandleEvent()-je lekezelné a ControllBox előhívásához szükséges
Alt+Space-t azelőtt mi magunk tesszük meg. Ha ez megtörtént, s az Alt+Space-ből
cmNewLine lett akkor az így keletkezett új sor parancsot meg kiiktatjuk.
Ha ügyesek voltunk, akkor egy ilyen editáló ablakot kaptunk:
További nyalánkságok, MessageBox()
Gondolom, észrevettétek hogy a TVDEMO programot futva az elmentésre felszólító dialógus, pontosabban messagebox (üzenetablak) még a régi, sima text formátumú. Ennek elkerülése végett saját MessageBox()-ot kell létrehoznunk. Ez persze megint egyszerű móka, hisz a MessageBox() forráskódjában lévő PDialog-okat, ill. PButton-okat kell a PCX-es megfelelőkre kicserélni. Ugyanakkor feldobhatjuk programunkat a Norton-ból megismert figyelemfelhívó piros, figyelmeztető dialógusokkal. A messagebox két típusból áll, a MessageBoxRect()-ből, mely megvalósítja magát a dialógust a gombokkal, a szövegmezővel és oda rakja a képernyőn ahova a bemenő R: TRect megadta, ill. a MessageBox() rutinból mely úgy futtatja le a MessageBoxRect()-et, hogy előtte minden képen a képernyő közepére helyezi azt (Vagyis a bemenő R: TRect-et maga számolja !). Így mi is a MessageBox()-ot használjuk későbbiekben.
Mi rövidebb neveket használunk: PCXMsgBoxRect() ill. PCXMsgBox().
function PCXMsgBoxRect(var R: TRect; const
Msg: String; Params: Pointer; AOptions: Word): Word;
{var változó deklaráció}
begin
{... itt a gombokon látható szövegeket hozzuk
létre}
if ((AOptions and 3)<>mfError) and ((AOptions
and 3)<>mfWarning)
then Dialog := New(PPCXBlueDialog,Init(R, Titles[AOptions and $3]))
else Dialog := New(PPCXRedDialog,Init(R, Titles[AOptions and $3]));
with Dialog^ do
begin
R.Assign(3, 2, Size.X - 2, Size.Y - 3);
FormatStr(S, Msg, Params^);
Control := New(PStaticText, Init(R, S));
Insert(Control);
X := -2;
ButtonCount := 0;
for I := 0 to 3 do
if AOptions and ($0100
shl I) <> 0 then
begin
R.Assign(0, 0, 10, 2);
Control := New(PPCXButton,
Init(R, ButtonName[I], Commands[i], bfNormal));
Inc(X, Control^.Size.X
+ 2);
ButtonList[ButtonCount]
:= Control;
Inc(ButtonCount);
end;
X := (Size.X - X) shr 1;
for I := 0 to ButtonCount - 1
do
begin
Control := ButtonList[I];
Insert(Control);
Control^.MoveTo(X, Size.Y - 3);
Inc(X, Control^.Size.X + 2);
end;
SelectNext(False);
end;
if AOptions and mfInsertInApp = 0 then
PCXMsgBoxRect := DeskTop^.ExecView(Dialog)
else PCXMsgBoxRect := Application^.ExecView(Dialog);
Dispose(Dialog, Done);
end;
A rutin működése egyszerű, a fenti forráskód elejéből kivágtam az a string inicializációt amelyben a gombokon lévő feliratokat rendeljük a string-hez, hisz ez egyértelmű.
begin
R.Assign(0, 0, 40, 9);
if AOptions and mfInsertInApp = 0 then
R.Move((Desktop^.Size.X
- R.B.X) div 2, (Desktop^.Size.Y - R.B.Y) div 2)
else R.Move((Application^.Size.X - R.B.X) div 2,
(Application^.Size.Y - R.B.Y) div 2);
PCXMsgBox := PCXMsgBoxRect(R, Msg, Params, AOptions);
end;
És itt a PCXMsgBox() aminél már láthatjuk, hogy nincs szükség az elhelyezkedés (R: TRect) megadására, hisz azt maga számolja, s így a képernyő közepére helyezi a dialógust. Valahogy így:
Nos hogy ezzel végeztünk, tegyük szebbé a FileOpenDialog-ot mely a file megnyitásakor hívódik meg. Itt se kell meggebednünk csak a frame-et, meg egy apróságokat kell átírnunk.
TPCXFileDialog
Ezen view felépítésekor fogunk pár olyan view-ot használni amit nem ismertetek, csak említés szintjén foglalkozunk velük. Ilyenek a TFileInputLine, TPCXFrameLine3D, TFileList ...
Lássuk az átírt, szépített forráskódot, s közben a magyarázatot:
constructor TPCXFileDialog.Init(AWildCard: TWildStr; const ATitle, InputName: String; AOptions: Word; HistoryId: Byte);
var
Control: PView;
R: TRect;
Opt: Word;
begin
R.Assign(15,1,64,20);
TDialog.Init(R, ATitle);
Options := Options or ofCentered;
WildCard := AWildCard;
Nos először a változók deklarációja, majd
function TPCXFileDialog.GetPalette: PPalette;
const P: String[Length(CPCXBlueDialog)] = CPCXBlueDialog;
begin
GetPalette:=@P;
end;
GetPalette: Ez a jó ismert szín-paletta megadásához szükséges
rutin.
procedure TPCXFileDialog.InitFrame;
var R: TRect;
begin
GetExtent(R);
Frame:=New(PPCXFrame, Init(R));
end;
InitFrame: A keret átrajzolásához szükséges rutin.
procedure TPCXFileDialog.ReadDirectory;
begin
FileList^.ReadDirectory(WildCard);
Directory := NewStr(GetCurDir);
end;
S az ominózus ReadDirectory mely az aktuális könyvtár file-jait ill. al-könyvtár bejegyzéseit olvassa be. Munkánk gyümölcsét az alábbi kép mutatja:
Végül, de nem utolsó sorban a CheckBox-ok, ill. a RadioButton-ok kerülnek terítékre
Hogy mi is az a CheckBox, hát az a be-x-elhető
négyzetecske, valahogy így: x S a RadioButton
? Emmeg ez: ¤ . Nos komolyra fordítva a szót
a Turbo Vision-nek természetesen van CheckBox-a csak az a sima text mód
miatt így néz ki: [X]. Sőt RadioButton
is van ez meg még szörnyebben néz ki: (*). Valami szebbet is készíthetnénk
ezeknél, ha már karakterátírással foglalkozunk. Most se lesz nagyon nehéz
dolgunk csak az eredeti forráskódot kell alakítgatnunk.
Kezdjük a CheckBox-al. Milyen metódusokat módosítunk ?
type
PPCXCheckBoxes = ^TPCXCheckBoxes;
TPCXCheckBoxes = Object(TCheckBoxes)
constructor Init(var Bounds: TRect; AStrings:
PSItem; IsRightOn: Boolean);
procedure DrawMultiBox(const Icon, Marker:
String);
procedure Draw; virtual;
function GetPalette: PPalette;
virtual;
procedure Press(Item: Integer); virtual;
private
IsRight: Boolean;
function Column(Item: Integer): Integer;
function FindSel(P: TPoint): Integer;
function Row(Item: Integer): Integer;
end;
Először is elmondanám, hogy egy újítást kell végzünk a CheckBox-on. Sokszor előfordul, hogy nem a bal oldalra szeretnénk tenni a be-x-elhető kis négyzetet, hanem jobbra. Ahhoz, hogy ezt nyugodtan megtehessük a később átirandő Draw; metódust kell megfelelően kialakítani. Viszont az objektumnak tudnia kell, hogy bal ill. jobb oldalra kívánjuk –e tenni a négyzetet. Ezért kell az Init() metódust módosítani. Így:
constructor TPCXCheckBoxes.Init(var Bounds: TRect;
AStrings: PSItem; IsRightOn: Boolean);
begin
Inherited Init(Bounds, AStrings);
IsRight:=IsRightOn;
end;
Egy lokális változóban mentjük el, hogy jobbra van –e igazítva, miután meghívtuk az örökölt Init() metódust.
A következő metódus ami a leglényegesebb, ez a DrawMultiBox(), ez végzi magát a kirajzolást, míg a Draw; metódus pedig ezt a DrawMultiBox rutint vezérli. Lássuk a DrawMuliBox() rutin forráskódját, s vele együtt a magyarázatot:
procedure TPCXCheckBoxes.DrawMultiBox(const Icon,
Marker: String);
var
I,J,Cur,Col: Integer;
CNorm, CSel, CDis, Color: Word;
B: TDrawBuffer;
SCOff: Byte;
begin
CNorm := GetColor($0301);
CSel := GetColor($0402);
CDis := GetColor($0505);
begin
WordRec(B[Col]).Lo := Byte(SpecialChars[0]);
WordRec(B[Column(Cur+Size.Y)-1]).Lo := Byte(SpecialChars[1]);
end;
WriteBuf(0, I, Size.X, 1, B);
Most pedig maga a Draw; metódus következik mely vezérli a checkbox-t. Lehetőségünk van a Draw ill. a DrawMuliBox szétválastásának eredménye képen, hogy különböző checkbox-aink legyenek. Most a sima text, ill. a grafikus-text módtól függően rajzolunk ki különböző checkbox-ot.
procedure TPCXCheckBoxes.Draw;
const
GraphButton = #32+CheckBoxLeft+#32+CheckBoxRight+#32; {'
[ ] '}
TextButton = ' [ ] ';
begin
if IsPCXGraphCharsOn
then DrawMultiBox(GraphButton, CheckBoxCenterN+CheckBoxCenterX)
{' X'}
else DrawMultiBox(TextButton, ' X');
end;
Ha átírt karaktereket használunk akkor az átírt karakterek ASCII kódjából összeállított ikont adjuk át, ill. a hasonló karakterekből kialakított üres négyzetbelsőt ill. a be-x-eltet.
Ha sima text módban dolgozunk akkor, pedig a sima ASCII karakterből összeállított ikont adjuk át, (’ [ ] ’) ill. ennek a belsejét vagy üres: ’ X’ vagy a második karakter az X.
Láthatjuk, hogy bal ill. jobb oldalra illesztett checkbox-okat sikerült
produkálnunk. Lássuk a létrehozáshoz szükséges forráskódot:
Balra illesztett, | Jobbra illesztet checkbox: |
R.Assign(3, 8, 37, 12);
Insert(New(PPCXCheckBoxes, Init(R, NewSItem('~C~ase sensitive', NewSItem('~W~hole words only', NewSItem('~P~rompt on replace', NewSItem('~R~eplace all', nil)))), False))); |
R.Assign(3, 8, 37, 12);
Insert(New(PPCXCheckBoxes, Init(R, NewSItem('~C~ase sensitive', NewSItem('~W~hole words only', NewSItem('~P~rompt on replace', NewSItem('~R~eplace all', nil)))), True))); |
Nos úgy érzem ennyi elég volt mára, így zárul az OOP móka-tára, de végső elkeseredéstek előtt megnyugtatástok végett közlöm véletek, hogy az PC-XUSER\OOP\R4sGLTV könyvtárban található R4sGLTV.ZIP-ben az általam átírt összes Turbo Vision-ös view. (Sajna csak TPU-ban, de akinek ez olyan lelkiproblémát jelent eMail-ezzen, s majd meglátjuk mit tehetünk.)
Minden fontosabb view-ot megvalósítottunk, így azt hiszem az OOP rovatot ezen, view-átirogató formájában kivégzem, avagy megszüntetem, s az OOP rovat – feltehetőleg – ezek után a C nyelv objektum-orientáltságával lesz hivatott foglalkozni. De amint említettem volt, (tiszta ómagyar a stílus ... ?) amit még nem írtam le az megtalálható az R4sGLTV.zip-ben, továbbá bármi kérdésteket eMail-en továbbíthatjátok hozzám.