lazarus/lcl/lclproc.pas
mattias 8ab6a60b7e many clipping fixes
git-svn-id: trunk@2662 -
2002-08-17 23:41:35 +00:00

535 lines
14 KiB
ObjectPascal

{
/***************************************************************************
lclproc.pas
-----------
Component Library Code
***************************************************************************/
*****************************************************************************
* *
* This file is part of the Lazarus Component Library (LCL) *
* *
* See the file COPYING.LCL, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
Useful lower level helper functions and classes.
}
unit LCLProc;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LCLStrConsts, LCLType;
type
TMethodList = class
private
FItems: ^TMethod;
FCount: integer;
function GetItems(Index: integer): TMethod;
procedure SetItems(Index: integer; const AValue: TMethod);
public
destructor Destroy; override;
function Count: integer;
function NextDownIndex(var Index: integer): boolean;
function IndexOf(const AMethod: TMethod): integer;
procedure Delete(Index: integer);
procedure Remove(const AMethod: TMethod);
procedure Add(const AMethod: TMethod);
procedure Add(const AMethod: TMethod; AsLast: boolean);
procedure Insert(Index: integer; const AMethod: TMethod);
procedure Move(OldIndex, NewIndex: integer);
procedure RemoveAllMethodsOfObject(const AnObject: TObject);
public
property Items[Index: integer]: TMethod read GetItems write SetItems; default;
end;
Function DeleteAmpersands(var Str : String) : Longint;
function ShortCutToShortCutText(ShortCut: TShortCut): string;
function ShortCutTextToShortCut(const ShortCutText: string): TShortCut;
// Hooks used to prevent unit circles
type
TSendApplicationMessageFunction =
function(Msg: Cardinal; WParam, LParam: Longint):Longint;
TOwnerFormDesignerModifiedProc =
procedure(AComponent: TComponent);
TSendMessageToInterfaceFunction =
function(LM_Message: Integer; Sender: TObject; data: pointer): integer
of object;
var
SendApplicationMessageFunction: TSendApplicationMessageFunction;
OwnerFormDesignerModifiedProc: TOwnerFormDesignerModifiedProc;
// SendMsgToInterface is set in interfacebase.pp
SendMsgToInterface: TSendMessageToInterfaceFunction;
function SendApplicationMessage(Msg: Cardinal; WParam, LParam: Longint):Longint;
procedure OwnerFormDesignerModified(AComponent: TComponent);
function OffsetRect(var ARect: TRect; dx,dy: Integer): Boolean;
procedure FreeThenNil(var AnObject: TObject);
procedure RaiseGDBException(const Msg: string);
procedure MakeMinMax(var i1, i2: integer);
procedure CalculateLeftTopWidthHeight(X1,Y1,X2,Y2: integer;
var Left,Top,Width,Height: integer);
function BreakString(const s: string; MaxLineLength, Indent: integer): string;
implementation
Function DeleteAmpersands(var Str : String) : Longint;
// Replace all &x with x
// and return the position of the first ampersand letter in the resulting Str.
// double ampersands && are converted to a single & and are ignored.
var
SrcPos, DestPos, SrcLen: Integer;
begin
Result:=-1;
SrcLen:=length(Str);
SrcPos:=1;
DestPos:=1;
while SrcPos<=SrcLen do begin
if (Str[SrcPos]='&') and (SrcPos<SrcLen) then begin
// & found
inc(SrcPos); // skip &
if (Str[SrcPos]<>'&') and (Result<1) then
Result:=DestPos;
end;
if DestPos<SrcPos then
Str[DestPos]:=Str[SrcPos];
inc(SrcPos);
inc(DestPos);
end;
if DestPos<SrcPos then
SetLength(Str,DestPos-1);
end;
//-----------------------------------------------------------------------------
// Keys and shortcuts
type
TMenuKeyCap = (mkcBkSp, mkcTab, mkcEsc, mkcEnter, mkcSpace, mkcPgUp,
mkcPgDn, mkcEnd, mkcHome, mkcLeft, mkcUp, mkcRight, mkcDown, mkcIns,
mkcDel, mkcShift, mkcCtrl, mkcAlt);
const
SmkcBkSp = 'BkSp';
SmkcTab = 'Tab';
SmkcEsc = 'Esc';
SmkcEnter = 'Enter';
SmkcSpace = 'Space';
SmkcPgUp = 'PgUp';
SmkcPgDn = 'PgDn';
SmkcEnd = 'End';
SmkcHome = 'Home';
SmkcLeft = 'Left';
SmkcUp = 'Up';
SmkcRight = 'Right';
SmkcDown = 'Down';
SmkcIns = 'Ins';
SmkcDel = 'Del';
SmkcShift = 'Shift+';
SmkcCtrl = 'Ctrl+';
SmkcAlt = 'Alt+';
MenuKeyCaps: array[TMenuKeyCap] of string = (
SmkcBkSp, SmkcTab, SmkcEsc, SmkcEnter, SmkcSpace, SmkcPgUp,
SmkcPgDn, SmkcEnd, SmkcHome, SmkcLeft, SmkcUp, SmkcRight,
SmkcDown, SmkcIns, SmkcDel, SmkcShift, SmkcCtrl, SmkcAlt);
function GetSpecialShortCutName(ShortCut: TShortCut): string;
{var
ScanCode: Integer;
KeyName: array[0..255] of Char;}
begin
Result := '';
// ToDo:
{
ScanCode := MapVirtualKey(WordRec(ShortCut).Lo, 0) shl 16;
if ScanCode <> 0 then
begin
GetKeyNameText(ScanCode, KeyName, SizeOf(KeyName));
Result := KeyName;
end;
}
end;
function ShortCutToShortCutText(ShortCut: TShortCut): string;
var
Name: string;
begin
case WordRec(ShortCut).Lo of
$08, $09:
Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + WordRec(ShortCut).Lo - $08)];
$0D: Name := MenuKeyCaps[mkcEnter];
$1B: Name := MenuKeyCaps[mkcEsc];
$20..$28:
Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcSpace) + WordRec(ShortCut).Lo - $20)];
$2D..$2E:
Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcIns) + WordRec(ShortCut).Lo - $2D)];
$30..$39: Name := Chr(WordRec(ShortCut).Lo - $30 + Ord('0'));
$41..$5A: Name := Chr(WordRec(ShortCut).Lo - $41 + Ord('A'));
$60..$69: Name := Chr(WordRec(ShortCut).Lo - $60 + Ord('0'));
$70..$87: Name := 'F' + IntToStr(WordRec(ShortCut).Lo - $6F);
else
Name := GetSpecialShortCutName(ShortCut);
end;
if Name <> '' then
begin
Result := '';
if ShortCut and scShift <> 0 then Result := Result + MenuKeyCaps[mkcShift];
if ShortCut and scCtrl <> 0 then Result := Result + MenuKeyCaps[mkcCtrl];
if ShortCut and scAlt <> 0 then Result := Result + MenuKeyCaps[mkcAlt];
Result := Result + Name;
end
else Result := '';
end;
function ShortCutTextToShortCut(const ShortCutText: string): TShortCut;
function CompareFront(var StartPos: integer; const Front: string): Boolean;
begin
if (Front<>'') and (StartPos+length(Front)-1<=length(ShortCutText))
and (AnsiStrLIComp(@ShortCutText[StartPos], PChar(Front), Length(Front))= 0)
then begin
Result:=true;
inc(StartPos,length(Front));
end else
Result:=false;
end;
var
Key: TShortCut;
Shift: TShortCut;
StartPos: integer;
Name: string;
begin
Result := 0;
Shift := 0;
StartPos:=1;
while True do
begin
if CompareFront(StartPos, MenuKeyCaps[mkcShift]) then
Shift := Shift or scShift
else if CompareFront(StartPos, '^') then
Shift := Shift or scCtrl
else if CompareFront(StartPos, MenuKeyCaps[mkcCtrl]) then
Shift := Shift or scCtrl
else if CompareFront(StartPos, MenuKeyCaps[mkcAlt]) then
Shift := Shift or scAlt
else
Break;
end;
if ShortCutText = '' then Exit;
for Key := $08 to $255 do begin { Copy range from table in ShortCutToText }
Name:=ShortCutToShortCutText(Key);
if (Name<>'') and (length(Name)=length(ShortCutText)-StartPos+1)
and (AnsiStrLIComp(@ShortCutText[StartPos], PChar(Name), length(Name)) = 0)
then begin
Result := Key or Shift;
Exit;
end;
end;
end;
function SendApplicationMessage(Msg: Cardinal; WParam, LParam: Longint
): Longint;
begin
if SendApplicationMessageFunction<>nil then
Result:=SendApplicationMessageFunction(Msg,WParam,LParam)
else
Result:=0;
end;
procedure OwnerFormDesignerModified(AComponent: TComponent);
begin
if OwnerFormDesignerModifiedProc<>nil then
OwnerFormDesignerModifiedProc(AComponent);
end;
function OffSetRect(var ARect: TRect; dx,dy: Integer): Boolean;
Begin
with ARect do
begin
Left := Left + dx;
Right := Right + dx;
Top := Top + dy;
Bottom := Bottom + dy;
end;
if (ARect.Left >= 0) and (ARect.Top >= 0) then
Result := True
else
Result := False;
end;
procedure FreeThenNil(var AnObject: TObject);
begin
if AnObject<>nil then begin
AnObject.Free;
AnObject:=nil;
end;
end;
{ TMethodList }
function TMethodList.GetItems(Index: integer): TMethod;
begin
Result:=FItems[Index];
end;
procedure TMethodList.SetItems(Index: integer; const AValue: TMethod);
begin
FItems[Index]:=AValue;
end;
destructor TMethodList.Destroy;
begin
ReAllocMem(FItems,0);
inherited Destroy;
end;
function TMethodList.Count: integer;
begin
if Self<>nil then
Result:=FCount
else
Result:=0;
end;
function TMethodList.NextDownIndex(var Index: integer): boolean;
begin
if Self<>nil then begin
dec(Index);
if (Index>=FCount) then
Index:=FCount-1;
end else
Index:=-1;
Result:=(Index>=0);
end;
function TMethodList.IndexOf(const AMethod: TMethod): integer;
begin
if Self<>nil then begin
Result:=FCount-1;
while Result>=0 do begin
if (FItems[Result].Code=AMethod.Code)
and (FItems[Result].Data=AMethod.Data) then exit;
dec(Result);
end;
end else
Result:=-1;
end;
procedure TMethodList.Delete(Index: integer);
begin
dec(FCount);
if FCount>Index then
System.Move(FItems[Index+1],FItems[Index],(FCount-Index)*SizeOf(TMethod));
ReAllocMem(FItems,FCount*SizeOf(TMethod));
end;
procedure TMethodList.Remove(const AMethod: TMethod);
var
i: integer;
begin
if Self<>nil then begin
i:=IndexOf(AMethod);
if i>=0 then Delete(i);
end;
end;
procedure TMethodList.Add(const AMethod: TMethod);
begin
inc(FCount);
ReAllocMem(FItems,FCount*SizeOf(TMethod));
FItems[FCount-1]:=AMethod;
end;
procedure TMethodList.Add(const AMethod: TMethod; AsLast: boolean);
begin
if AsLast then
Add(AMethod)
else
Insert(0,AMethod);
end;
procedure TMethodList.Insert(Index: integer; const AMethod: TMethod);
begin
if Index<FCount then
System.Move(FItems[Index],FItems[Index+1],(FCount-Index)*SizeOf(TMethod));
FItems[Index]:=AMethod;
end;
procedure TMethodList.Move(OldIndex, NewIndex: integer);
var
MovingMethod: TMethod;
begin
if OldIndex=NewIndex then exit;
MovingMethod:=FItems[OldIndex];
if OldIndex>NewIndex then
System.Move(FItems[NewIndex],FItems[NewIndex+1],
SizeOf(TMethod)*(OldIndex-NewIndex))
else
System.Move(FItems[NewIndex+1],FItems[NewIndex],
SizeOf(TMethod)*(NewIndex-OldIndex));
FItems[NewIndex]:=MovingMethod;
end;
procedure TMethodList.RemoveAllMethodsOfObject(const AnObject: TObject);
var
i: Integer;
begin
if Self=nil then exit;
i:=FCount-1;
while i>=0 do begin
if TObject(FItems[i].Data)=AnObject then Delete(i);
dec(i);
end;
end;
{------------------------------------------------------------------------------
procedure RaiseGDBException(const Msg: string);
Raises an exception.
gdb does not catch fpc Exception objects, therefore this procedure raises
a standard AV which is catched by gdb.
------------------------------------------------------------------------------}
procedure RaiseGDBException(const Msg: string);
begin
writeln(rsERRORInLCL, Msg);
// creates an exception, that gdb catches:
writeln(rsCreatingGdbCatchableError);
if (length(Msg) div (length(Msg) div 10000))=0 then ;
end;
procedure MakeMinMax(var i1, i2: integer);
var
h: Integer;
begin
if i1>i2 then begin
h:=i1;
i1:=i2;
i2:=h;
end;
end;
procedure CalculateLeftTopWidthHeight(X1, Y1, X2, Y2: integer;
var Left, Top, Width, Height: integer);
begin
if X1<=X2 then begin
Left:=X1;
Width:=X2 - X1;
end else begin
Left:=X2;
Width:=X1 - X2;
end;
if Y1<=Y2 then begin
Top:=Y1;
Height:=Y2 - Y1;
end else begin
Top:=Y2;
Height:=Y1 - Y2;
end;
end;
function BreakString(const s: string; MaxLineLength, Indent: integer): string;
const
NewLine = {$IfDef win32}#13+{$EndIf}#10;
var
SrcLen: Integer;
APos: Integer;
Src: String;
SplitPos: Integer;
CurMaxLineLength: Integer;
begin
Result:='';
Src:=s;
CurMaxLineLength:=MaxLineLength;
if Indent>MaxLineLength-2 then Indent:=MaxLineLength-2;
if Indent<0 then MaxLineLength:=0;
repeat
SrcLen:=length(Src);
if SrcLen<=CurMaxLineLength then begin
Result:=Result+Src;
break;
end;
// split line
SplitPos:=0;
// search new line chars
APos:=1;
while (APos<=CurMaxLineLength) do begin
if Src[APos] in [#13,#10] then begin
SplitPos:=APos;
break;
end;
inc(APos);
end;
// search a space boundary
if SplitPos=0 then begin
APos:=CurMaxLineLength;
while APos>1 do begin
if (Src[APos-1] in [' ',#9])
and (not (Src[APos] in [' ',#9])) then begin
SplitPos:=APos;
break;
end;
dec(APos);
end;
end;
// search a word boundary
if SplitPos=0 then begin
APos:=CurMaxLineLength;
while APos>1 do begin
if (Src[APos] in ['A'..'Z','a'..'z'])
and (not (Src[APos-1] in ['A'..'Z','a'..'z'])) then begin
SplitPos:=APos;
break;
end;
dec(APos);
end;
end;
if SplitPos=0 then begin
// no word boundary found -> split chars
SplitPos:=CurMaxLineLength;
end;
// append part and newline
if (SplitPos<=SrcLen) and (Src[SplitPos] in [#10,#13]) then begin
// there is already a new line char at position
inc(SplitPos);
if (SplitPos<=SrcLen) and (Src[SplitPos] in [#10,#13])
and (Src[SplitPos]<>Src[SplitPos-1]) then
inc(SplitPos);
Result:=Result+copy(Src,1,SplitPos-1);
end else begin
Result:=Result+copy(Src,1,SplitPos-1)+NewLine;
end;
// append indent
if Indent>0 then
Result:=Result+StringOfChar(' ',Indent);
// calculate new LineLength
CurMaxLineLength:=MaxLineLength-Indent;
// cut string
Src:=copy(Src,SplitPos,length(Src)-SplitPos+1);
until false;
end;
initialization
SendApplicationMessageFunction:=nil;
OwnerFormDesignerModifiedProc:=nil;
end.