123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685 |
- unit outline;
- {$CODEPAGE cp437}
- {***************************************************************************}
- interface
- {***************************************************************************}
- uses drivers,objects,views;
- type Pnode=^Tnode;
- Tnode=record
- next:Pnode;
- text:Pstring;
- childlist:Pnode;
- expanded:boolean;
- end;
- Poutlineviewer=^Toutlineviewer;
- Toutlineviewer=object(Tscroller)
- foc:sw_integer;
- constructor init(var bounds:Trect;
- AHscrollbar,AVscrollbar:Pscrollbar);
- procedure adjust(node:pointer;expand:boolean);virtual;
- function creategraph(level:integer;lines:longint;
- flags:word;levwidth,endwidth:integer;
- const chars:string):string;
- procedure draw;virtual;
- procedure expandall(node:pointer);
- function firstthat(test:pointer):pointer;
- procedure focused(i:sw_integer);virtual;
- procedure foreach(action:pointer);
- function getchild(node:pointer;i:sw_integer):pointer;virtual;
- function getgraph(level:integer;lines:longint;flags:word):string;
- function getnode(i:sw_integer):pointer;virtual;
- function getnumchildren(node:pointer):sw_integer;virtual;
- function getpalette:Ppalette;virtual;
- function getroot:pointer;virtual;
- function gettext(node:pointer):string;virtual;
- procedure handleevent(var event:Tevent);virtual;
- function haschildren(node:pointer):boolean;virtual;
- function isexpanded(node:pointer):boolean;virtual;
- function isselected(i:sw_integer):boolean;virtual;
- procedure selected(i:sw_integer);virtual;
- procedure setstate(Astate:word;enable:boolean);virtual;
- procedure update;
- private
- procedure set_focus(Afocus:sw_integer);
- function do_recurse(action,callerframe:pointer;
- stop_if_found:boolean):pointer;
- end;
- Poutline=^Toutline;
- Toutline=object(Toutlineviewer)
- root:Pnode;
- constructor init(var bounds:Trect;
- AHscrollbar,AVscrollbar:Pscrollbar;
- Aroot:Pnode);
- procedure adjust(node:pointer;expand:boolean);virtual;
- function getchild(node:pointer;i:sw_integer):pointer;virtual;
- function getnumchildren(node:pointer):sw_integer;virtual;
- function getroot:pointer;virtual;
- function gettext(node:pointer):string;virtual;
- function haschildren(node:pointer):boolean;virtual;
- function isexpanded(node:pointer):boolean;virtual;
- destructor done;virtual;
- end;
- const ovExpanded = $1;
- ovChildren = $2;
- ovLast = $4;
- Coutlineviewer=Cscroller+#8#8;
- function newnode(const Atext:string;Achildren,Anext:Pnode):Pnode;
- procedure disposenode(node:Pnode);
- {***************************************************************************}
- implementation
- {***************************************************************************}
- type TMyFunc = function(_EBP: Pointer; Cur: Pointer;
- Level, Position: sw_integer; Lines: LongInt;
- Flags: Word): Boolean;
- function newnode(const Atext:string;Achildren,Anext:Pnode):Pnode;
- begin
- newnode:=new(Pnode);
- with newnode^ do
- begin
- next:=Anext;
- text:=newstr(Atext);
- childlist:=Achildren;
- expanded:=true;
- end;
- end;
- procedure disposenode(node:Pnode);
- var next:Pnode;
- begin
- while node<>nil do
- begin
- disposenode(node^.childlist);
- disposestr(node^.text);
- next:=node^.next;
- dispose(node);
- node:=next;
- end;
- end;
- {+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
- { Toutlineviewer object methods }
- {+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
- constructor Toutlineviewer.init(var bounds:Trect;
- AHscrollbar,AVscrollbar:Pscrollbar);
- begin
- inherited init(bounds,AHscrollbar,AVscrollbar);
- foc:=0;
- growmode:=gfGrowHiX+gfGrowHiY;
- end;
- procedure Toutlineviewer.adjust(node:pointer;expand:boolean);
- begin
- abstract;
- end;
- function TOutlineViewer.CreateGraph(Level: Integer; Lines: LongInt;
- Flags: Word; LevWidth, EndWidth: Integer;
- const Chars: String): String;
- const
- FillerOrBar = 0;
- YorL = 2;
- StraightOrTee= 4;
- Retracted = 6;
- var
- Last, Children, Expanded: Boolean;
- I , J : Byte;
- Graph : String;
- begin
- { Load registers }
- graph:=space(Level*LevWidth+EndWidth+1);
- { Write bar characters }
- J := 1;
- while (Level > 0) do
- begin
- Inc(J);
- if (Lines and 1) <> 0 then
- Graph[J] := Chars[FillerOrBar+2]
- else
- Graph[J] := Chars[FillerOrBar+1];
- for I := 1 to LevWidth - 1 do
- Graph[I]:= Chars[FillerOrBar+1];
- J := J + LevWidth - 1;
- Dec(Level);
- Lines := Lines shr 1;
- end;
- { Write end characters }
- Dec(EndWidth);
- if EndWidth > 0 then
- begin
- Inc(J);
- if Flags and ovLast <> 0 then
- Graph[J] := Chars[YorL+2]
- else
- Graph[J] := Chars[YorL+1];
- Dec(EndWidth);
- if EndWidth > 0 then
- begin
- Dec(EndWidth);
- for I := 1 to EndWidth do
- Graph[I]:= Chars[StraightOrTee+1];
- J := J + EndWidth;
- Inc(J);
- if (Flags and ovChildren) <> 0 then
- Graph[J] := Chars[StraightOrTee+2]
- else
- Graph[J] := Chars[StraightOrTee+1];
- end;
- Inc(J);
- if Flags and ovExpanded <> 0 then
- Graph[J] := Chars[Retracted+2]
- else
- Graph[J] := Chars[Retracted+1];
- end;
- Graph[0] := Char(J);
- CreateGraph := Graph;
- end;
- function Toutlineviewer.do_recurse(action,callerframe:pointer;
- stop_if_found:boolean):pointer;
- var position:sw_integer;
- r:pointer;
- function recurse(cur:pointer;level:integer;lines:longint;lastchild:boolean):pointer;
- var i,childcount:sw_integer;
- child:pointer;
- flags:word;
- children,expanded,found:boolean;
- begin
- inc(position);
- recurse:=nil;
- children:=haschildren(cur);
- expanded:=isexpanded(cur);
- {Determine flags.}
- flags:=0;
- if not children or expanded then
- inc(flags,ovExpanded);
- if children and expanded then
- inc(flags,ovChildren);
- if lastchild then
- inc(flags,ovLast);
- {Call the function.}
- found:=TMyFunc(action)(callerframe,cur,level,position,lines,flags);
- if stop_if_found and found then
- recurse:=cur
- else if children and expanded then {Recurse children?}
- begin
- if not lastchild then
- lines:=lines or (1 shl level);
- {Iterate all childs.}
- childcount:=getnumchildren(cur);
- for i:=0 to childcount-1 do
- begin
- child:=getchild(cur,i);
- if (child<>nil) and (level<31) then
- recurse:=recurse(child,level+1,lines,i=childcount-1);
- {Did we find a node?}
- if recurse<>nil then
- break;
- end;
- end;
- end;
- begin
- position:=-1;
- r:=getroot;
- if r<>nil then
- do_recurse:=recurse(r,0,0,true)
- else
- do_recurse:=nil;
- end;
- procedure Toutlineviewer.draw;
- var c_normal,c_normal_x,c_select,c_focus:byte;
- maxpos:sw_integer;
- b:Tdrawbuffer;
- function draw_item(cur:pointer;level,position:sw_integer;
- lines:longint;flags:word):boolean;
- var c,i:byte;
- s,t:string;
- begin
- draw_item:=position>=delta.y+size.y;
- if (position<delta.y) or draw_item then
- exit;
- maxpos:=position;
- s:=getgraph(level,lines,flags);
- t:=gettext(cur);
- {Determine text colour.}
- if (foc=position) and (state and sffocused<>0) then
- c:=c_focus
- else if isselected(position) then
- c:=c_select
- else if flags and ovexpanded<>0 then
- c:=c_normal_x
- else
- c:=c_normal;
- {Fill drawbuffer with graph and text to draw.}
- for i:=0 to size.x-1 do
- begin
- wordrec(b[i]).hi:=c;
- if i+delta.x<length(s) then
- wordrec(b[i]).lo:=byte(s[1+i+delta.x])
- else if 1+i+delta.x-length(s)<=length(t) then
- wordrec(b[i]).lo:=byte(t[1+i+delta.x-length(s)])
- else
- wordrec(b[i]).lo:=byte(' ');
- end;
- {Draw!}
- writeline(0,position-delta.y,size.x,1,b);
- end;
- begin
- c_normal:=getcolor(4);
- c_normal_x:=getcolor(1);
- c_focus:=getcolor(2);
- c_select:=getcolor(3);
- maxpos:=-1;
- foreach(@draw_item);
- movechar(b,' ',c_normal,size.x);
- writeline(0,maxpos+1,size.x,size.y-(maxpos-delta.y),b);
- end;
- procedure Toutlineviewer.expandall(node:pointer);
- var i:sw_integer;
- begin
- if haschildren(node) then
- begin
- for i:=0 to getnumchildren(node)-1 do
- expandall(getchild(node,i));
- adjust(node,true);
- end;
- end;
- function Toutlineviewer.firstthat(test:pointer):pointer;
- begin
- firstthat:=do_recurse(test,get_caller_frame(get_frame),true);
- end;
- procedure Toutlineviewer.focused(i:sw_integer);
- begin
- foc:=i;
- end;
- procedure Toutlineviewer.foreach(action:pointer);
- begin
- do_recurse(action,get_caller_frame(get_frame),false);
- end;
- function Toutlineviewer.getchild(node:pointer;i:sw_integer):pointer;
- begin
- abstract;
- end;
- function Toutlineviewer.getgraph(level:integer;lines:longint;
- flags:word):string;
- begin
- getgraph:=creategraph(level,lines,flags,3,3,' ³ÃÀÄÄ+Ä');
- end;
- function Toutlineviewer.getnode(i:sw_integer):pointer;
- function test_position(node:pointer;level,position:sw_integer;lines:longInt;
- flags:word):boolean;
- begin
- test_position:=position=i;
- end;
- begin
- getnode:=firstthat(@test_position);
- end;
- function Toutlineviewer.getnumchildren(node:pointer):sw_integer;
- begin
- abstract;
- end;
- function Toutlineviewer.getpalette:Ppalette;
- const p:string[length(Coutlineviewer)]=Coutlineviewer;
- begin
- getpalette:=@p;
- end;
- function Toutlineviewer.getroot:pointer;
- begin
- abstract;
- end;
- function Toutlineviewer.gettext(node:pointer):string;
- begin
- abstract;
- end;
- procedure Toutlineviewer.handleevent(var event:Tevent);
- var mouse:Tpoint;
- cur:pointer;
- new_focus:sw_integer;
- count:byte;
- handled,m,mouse_drag:boolean;
- graph:string;
- function graph_of_focus(var graph:string):pointer;
- var _level:sw_integer;
- _lines:longInt;
- _flags:word;
- function find_focused(cur:pointer;level,position:sw_integer;
- lines:longint;flags:word):boolean;
- begin
- find_focused:=position=foc;
- if find_focused then
- begin
- _level:=level;
- _lines:=lines;
- _flags:=flags;
- end;
- end;
- begin
- graph_of_focus:=firstthat(@find_focused);
- graph:=getgraph(_level,_lines,_flags);
- end;
- const skip_mouse_events=3;
- begin
- inherited handleevent(event);
- case event.what of
- evKeyboard:
- begin
- new_focus:=foc;
- handled:=true;
- case ctrltoarrow(event.keycode) of
- kbUp,kbLeft:
- dec(new_focus);
- kbDown,kbRight:
- inc(new_focus);
- kbPgDn:
- inc(new_focus,size.y-1);
- kbPgUp:
- dec(new_focus,size.y-1);
- kbCtrlPgUp:
- new_focus:=0;
- kbCtrlPgDn:
- new_focus:=limit.y-1;
- kbHome:
- new_focus:=delta.y;
- kbEnd:
- new_focus:=delta.y+size.y-1;
- kbCtrlEnter,kbEnter:
- selected(new_focus);
- else
- case event.charcode of
- '-','+':
- begin
- adjust(getnode(new_focus),event.charcode='+');
- update;
- end;
- '*':
- begin
- expandall(getnode(new_focus));
- update;
- end;
- else
- handled:=false;
- end;
- end;
- if new_focus<0 then
- new_focus:=0;
- if new_focus>=limit.y then
- new_focus:=limit.y-1;
- if foc<>new_focus then
- set_focus(new_focus);
- if handled then
- clearevent(event);
- end;
- evMouseDown:
- begin
- count:=1;
- mouse_drag:=false;
- repeat
- makelocal(event.where,mouse);
- if mouseinview(event.where) then
- new_focus:=delta.y+mouse.y
- else
- begin
- inc(count,byte(event.what=evMouseAuto));
- if count and skip_mouse_events=0 then
- begin
- if mouse.y<0 then
- dec(new_focus);
- if mouse.y>=size.y then
- inc(new_focus);
- end;
- end;
- if new_focus<0 then
- new_focus:=0;
- if new_focus>=limit.y then
- new_focus:=limit.y-1;
- if foc<>new_focus then
- set_focus(new_focus);
- m:=mouseevent(event,evMouseMove+evMouseAuto);
- if m then
- mouse_drag:=true;
- until not m;
- if event.double then
- selected(foc)
- else if not mouse_drag then
- begin
- cur:=graph_of_focus(graph);
- if mouse.x<length(graph) then
- begin
- adjust(cur,not isexpanded(cur));
- update;
- end;
- end;
- end;
- end;
- end;
- function Toutlineviewer.haschildren(node:pointer):boolean;
- begin
- abstract;
- end;
- function Toutlineviewer.isexpanded(node:pointer):boolean;
- begin
- abstract;
- end;
- function Toutlineviewer.isselected(i:sw_integer):boolean;
- begin
- isselected:=foc=i;
- end;
- procedure Toutlineviewer.selected(i:sw_integer);
- begin
- {Does nothing by default.}
- end;
- procedure Toutlineviewer.set_focus(Afocus:sw_integer);
- begin
- assert((Afocus>=0) and (Afocus<limit.y));
- focused(Afocus);
- if Afocus<delta.y then
- scrollto(delta.x,Afocus)
- else if Afocus-size.y>=delta.y then
- scrollto(delta.x,Afocus-size.y+1);
- drawview;
- end;
- procedure Toutlineviewer.setstate(Astate:word;enable:boolean);
- begin
- if Astate and sffocused<>0 then
- drawview;
- inherited setstate(Astate,enable);
- end;
- procedure Toutlineviewer.update;
- var count:sw_integer;
- maxwidth:byte;
- procedure check_item(cur:pointer;level,position:sw_integer;
- lines:longint;flags:word);
- var width:word;
- begin
- inc(count);
- width:=length(gettext(cur))+length(getgraph(level,lines,flags));
- if width>maxwidth then
- maxwidth:=width;
- end;
- begin
- count:=0;
- maxwidth:=0;
- foreach(@check_item);
- setlimit(maxwidth,count);
- set_focus(foc);
- end;
- {+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
- { Toutline object methods }
- {+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
- constructor Toutline.init(var bounds:Trect;
- AHscrollbar,AVscrollbar:Pscrollbar;
- Aroot:Pnode);
- begin
- inherited init(bounds,AHscrollbar,AVscrollbar);
- root:=Aroot;
- update;
- end;
- procedure Toutline.adjust(node:pointer;expand:boolean);
- begin
- assert(node<>nil);
- Pnode(node)^.expanded:=expand;
- end;
- function Toutline.getnumchildren(node:pointer):sw_integer;
- var p:Pnode;
- begin
- assert(node<>nil);
- p:=Pnode(node)^.childlist;
- getnumchildren:=0;
- while p<>nil do
- begin
- inc(getnumchildren);
- p:=p^.next;
- end;
- end;
- function Toutline.getchild(node:pointer;i:sw_integer):pointer;
- begin
- assert(node<>nil);
- getchild:=Pnode(node)^.childlist;
- while i<>0 do
- begin
- dec(i);
- getchild:=Pnode(getchild)^.next;
- end;
- end;
- function Toutline.getroot:pointer;
- begin
- getroot:=root;
- end;
- function Toutline.gettext(node:pointer):string;
- begin
- assert(node<>nil);
- gettext:=Pnode(node)^.text^;
- end;
- function Toutline.haschildren(node:pointer):boolean;
- begin
- assert(node<>nil);
- haschildren:=Pnode(node)^.childlist<>nil;
- end;
- function Toutline.isexpanded(node:pointer):boolean;
- begin
- assert(node<>nil);
- isexpanded:=Pnode(node)^.expanded;
- end;
- destructor Toutline.done;
- begin
- disposenode(root);
- inherited done;
- end;
- end.
|