fpc/fv/outline.pas
peter ec3c805097 Merged revisions 3315-3317,3371,3397-3398,3430,3435,3437,3444,3450,3452,3455,3574,3594,3676-3678,3693,3710,3714,3717,3722-3723,3730-3732,3777-3778,3838-3840,3843 via svnmerge from
http://peter@svn.freepascal.org/svn/fpc/trunk

........
r3315 | daniel | 2006-04-22 21:53:02 +0200 (Sat, 22 Apr 2006) | 2 lines

  * Editor fixes by Wayne Sherman.

........
r3316 | daniel | 2006-04-22 23:56:38 +0200 (Sat, 22 Apr 2006) | 2 lines

  * Lazarus IDE config file

........
r3317 | daniel | 2006-04-23 09:32:51 +0200 (Sun, 23 Apr 2006) | 2 lines

  * Fix by Wayne Sherman

........
r3371 | daniel | 2006-04-29 22:18:44 +0200 (Sat, 29 Apr 2006) | 2 lines

  * Fix buttons that didn't show.

........
r3397 | daniel | 2006-05-01 13:53:36 +0200 (Mon, 01 May 2006) | 2 lines

  * New test program by Wayne Sherman

........
r3398 | daniel | 2006-05-01 13:57:46 +0200 (Mon, 01 May 2006) | 2 lines

  * Add hotkeys to test menu

........
r3430 | daniel | 2006-05-06 13:02:46 +0200 (Sat, 06 May 2006) | 2 lines

  * Removal of memory unit by Wayne Sherman

........
r3435 | daniel | 2006-05-06 20:40:21 +0200 (Sat, 06 May 2006) | 2 lines

  * Make Tframe codepage aware

........
r3437 | tom_at_work | 2006-05-06 22:03:43 +0200 (Sat, 06 May 2006) | 1 line

* re-added memory to buildfv.pas so that it gets built and a make install works.
........
r3444 | daniel | 2006-05-07 01:10:02 +0200 (Sun, 07 May 2006) | 2 lines

  * Newline for dos/unix shell

........
r3450 | daniel | 2006-05-07 16:02:06 +0200 (Sun, 07 May 2006) | 2 lines

  * it's May, not November :)

........
r3452 | daniel | 2006-05-07 17:56:11 +0200 (Sun, 07 May 2006) | 2 lines

  * Make dosshell work under Unix

........
r3455 | daniel | 2006-05-08 08:39:01 +0200 (Mon, 08 May 2006) | 2 lines

  * Fix by Wayne Sherman to make restore from dosshell work under Win32

........
r3574 | daniel | 2006-05-19 00:10:02 +0200 (Fri, 19 May 2006) | 2 lines

  * Some fixes by Onur Sayman

........
r3594 | florian | 2006-05-20 18:44:59 +0200 (Sat, 20 May 2006) | 2 lines

* removed tfileio from makefile

........
r3676 | daniel | 2006-05-25 23:06:00 +0200 (Thu, 25 May 2006) | 3 lines

  + New implementation of outline.pas.
    Contains some code contrinuted by Onur Sayman.

........
r3677 | daniel | 2006-05-25 23:22:04 +0200 (Thu, 25 May 2006) | 2 lines

  * Fix expandall

........
r3678 | daniel | 2006-05-26 12:13:14 +0200 (Fri, 26 May 2006) | 2 lines

  * Further development.

........
r3693 | daniel | 2006-05-27 15:54:28 +0200 (Sat, 27 May 2006) | 2 lines

  * Further development

........
r3710 | daniel | 2006-05-27 23:43:14 +0200 (Sat, 27 May 2006) | 2 lines

  * Further development

........
r3714 | daniel | 2006-05-28 11:28:32 +0200 (Sun, 28 May 2006) | 2 lines

  * Further development

........
r3717 | daniel | 2006-05-28 17:53:28 +0200 (Sun, 28 May 2006) | 2 lines

  * Further development

........
r3722 | daniel | 2006-05-28 22:34:19 +0200 (Sun, 28 May 2006) | 2 lines

  * Further development

........
r3723 | daniel | 2006-05-28 22:43:21 +0200 (Sun, 28 May 2006) | 2 lines

  + More assertions

........
r3730 | daniel | 2006-05-29 02:02:01 +0200 (Mon, 29 May 2006) | 3 lines

  * Fix color of highlighted item
  * Expand nodes by default

........
r3731 | daniel | 2006-05-29 02:05:59 +0200 (Mon, 29 May 2006) | 2 lines

  - Remove unnecessary drawview

........
r3732 | daniel | 2006-05-29 02:08:11 +0200 (Mon, 29 May 2006) | 2 lines

  * Fix mouse expand/retract

........
r3777 | daniel | 2006-06-03 20:53:00 +0200 (Sat, 03 Jun 2006) | 2 lines

  * Remove useless Unix specific hack.

........
r3778 | daniel | 2006-06-03 22:57:55 +0200 (Sat, 03 Jun 2006) | 2 lines

  * Do not redraw entire screen when Tgroup.draw is called.

........
r3838 | daniel | 2006-06-11 08:24:01 +0200 (Sun, 11 Jun 2006) | 2 lines

  * Fix disposenode bug.

........
r3839 | daniel | 2006-06-11 08:38:45 +0200 (Sun, 11 Jun 2006) | 2 lines

  * Add codepage directive to files.

........
r3840 | daniel | 2006-06-11 10:44:54 +0200 (Sun, 11 Jun 2006) | 2 lines

  * Typo

........
r3843 | daniel | 2006-06-11 12:11:34 +0200 (Sun, 11 Jun 2006) | 2 lines

  * Handle video initialization failure.

........

git-svn-id: branches/fixes_2_0@3956 -
2006-06-26 06:30:01 +00:00

686 lines
16 KiB
ObjectPascal
Raw Blame History

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,' <20><><EFBFBD><EFBFBD><EFBFBD>+<2B>');
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.