{ /*************************************************************************** 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 ShortCutToShortCutText(ShortCut: TShortCut): string; function ShortCutTextToShortCut(const ShortCutText: string): TShortCut; // Hooks used to prevent unit circles type TSendApplicationMessageFunction = function(Msg: Cardinal; WParam: WParam; LParam: LParam):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: WParam; LParam: LParam):Longint; procedure OwnerFormDesignerModified(AComponent: TComponent); function OffsetRect(var ARect: TRect; dx,dy: Integer): Boolean; procedure FreeThenNil(var AnObject: TObject); procedure MakeMinMax(var i1, i2: integer); procedure CalculateLeftTopWidthHeight(X1,Y1,X2,Y2: integer; var Left,Top,Width,Height: integer); function DeleteAmpersands(var Str : String) : Longint; function BreakString(const s: string; MaxLineLength, Indent: integer): string; function ComparePointers(p1, p2: Pointer): integer; function RoundToInt(const e: Extended): integer; function RoundToCardinal(const e: Extended): cardinal; function TruncToInt(const e: Extended): integer; function TruncToCardinal(const e: Extended): cardinal; function StrToDouble(const s: string): double; // debugging procedure RaiseGDBException(const Msg: string); procedure DebugLn; procedure DebugLn(const s: string); procedure DebugLn(const s1,s2: string); procedure DebugLn(const s1,s2,s3: string); procedure DebugLn(const s1,s2,s3,s4: string); procedure DebugLn(const s1,s2,s3,s4,s5: string); procedure DebugLn(const s1,s2,s3,s4,s5,s6: string); procedure DebugLn(const s1,s2,s3,s4,s5,s6,s7: string); procedure DebugLn(const s1,s2,s3,s4,s5,s6,s7,s8: string); procedure DebugLn(const s1,s2,s3,s4,s5,s6,s7,s8,s9: string); procedure DebugLn(const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10: string); procedure DebugLn(const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11: string); procedure DebugLn(const s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12: string); procedure DbgOut(const s: string); procedure DbgOut(const s1,s2: string); function DbgS(const c: cardinal): string; function DbgS(const i: integer): string; function DbgS(const r: TRect): string; function DbgS(const p: TPoint): string; function DbgS(const p: pointer): string; function DbgS(const e: extended): string; function DbgS(const b: boolean): string; function DbgS(const i1,i2,i3,i4: 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'&') and (Result<1) then Result:=DestPos; end; if DestPos 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: WParam; LParam: LParam ): Longint; begin if SendApplicationMessageFunction<>nil then Result:=SendApplicationMessageFunction(Msg, WParam, LParam) else Result:=0; end; procedure OwnerFormDesignerModified(AComponent: TComponent); begin if ([csDesigning,csLoading,csDestroying]*AComponent.ComponentState =[csDesigning]) then begin if OwnerFormDesignerModifiedProc<>nil then OwnerFormDesignerModifiedProc(AComponent); end; 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 IndexNewIndex 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 debugln(rsERRORInLCL, Msg); // creates an exception, that gdb catches: debugln(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; 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)+LineEnding; 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; function ComparePointers(p1, p2: Pointer): integer; begin if p1>p2 then Result:=1 else if p1fmClosed then writeln(s); end; procedure DebugLn(const s1, s2: string); begin DebugLn(s1+s2); end; procedure DebugLn(const s1, s2, s3: string); begin DebugLn(s1+s2+s3); end; procedure DebugLn(const s1, s2, s3, s4: string); begin DebugLn(s1+s2+s3+s4); end; procedure DebugLn(const s1, s2, s3, s4, s5: string); begin DebugLn(s1+s2+s3+s4+s5); end; procedure DebugLn(const s1, s2, s3, s4, s5, s6: string); begin DebugLn(s1+s2+s3+s4+s5+s6); end; procedure DebugLn(const s1, s2, s3, s4, s5, s6, s7: string); begin DebugLn(s1+s2+s3+s4+s5+s6+s7); end; procedure DebugLn(const s1, s2, s3, s4, s5, s6, s7, s8: string); begin DebugLn(s1+s2+s3+s4+s5+s6+s7+s8); end; procedure DebugLn(const s1, s2, s3, s4, s5, s6, s7, s8, s9: string); begin DebugLn(s1+s2+s3+s4+s5+s6+s7+s8+s9); end; procedure DebugLn(const s1, s2, s3, s4, s5, s6, s7, s8, s9, s10: string); begin DebugLn(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10); end; procedure DebugLn(const s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11: string); begin DebugLn(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10+s11); end; procedure DebugLn(const s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12: string); begin DebugLn(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10+s11+s12); end; procedure DBGOut(const s: string); begin if TextRec(Output).Mode<>fmClosed then write(s); end; procedure DBGOut(const s1, s2: string); begin DbgOut(s1+s2); end; function DbgS(const c: cardinal): string; begin Result:=IntToStr(c); end; function DbgS(const i: integer): string; begin Result:=IntToStr(i); end; function DbgS(const r: TRect): string; begin Result:=' l='+IntToStr(r.Left)+',t='+IntToStr(r.Top) +',r='+IntToStr(r.Right)+',b='+IntToStr(r.Bottom); end; function DbgS(const p: TPoint): string; begin Result:=' x='+IntToStr(p.x)+',y='+IntToStr(p.y); end; function DbgS(const p: pointer): string; begin Result:=HexStr(Cardinal(p),8); end; function DbgS(const e: extended): string; begin Result:=FloatToStr(e); end; function DbgS(const b: boolean): string; begin if b then Result:='True' else Result:='False'; end; function DbgS(const i1, i2, i3, i4: integer): string; begin Result:=dbgs(i1)+','+dbgs(i2)+','+dbgs(i3)+','+dbgs(i4); end; initialization SendApplicationMessageFunction:=nil; OwnerFormDesignerModifiedProc:=nil; end.