Good night friends,
I'm currently working on a project that involves enumeration of visible windows and their descendents (also visible).
And I can transfer all nodes of TreeView for a Memo (in text format) one by one, but now I'm trying to do the opposite (of necessity of the project).
Someone could help me with this here in StackOverflow?
Here is all the code that lists the windows on TreeView and after transfers it to one Memo.
function GetWindowTitle(hwnd: HWND): string;
begin
SetLength(Result, 255);
SetLength(Result, GetWindowText(hwnd, PChar(Result), 255));
end;
function GetWindowClass(hwnd: HWND): string;
begin
SetLength(Result, 255);
SetLength(Result, GetClassName(hwnd, PChar(Result), 255));
end;
function GetWindowInfo(hwnd: HWND): string;
begin
Result := GetWindowTitle(hwnd) + ' [' + GetWindowClass(hwnd) +
'] (' + {IntToStr}IntToHex(hwnd, 8) + ')';
end;
function EnumChildProc(hwnd: HWND; lParam: Integer): BOOL; stdcall;
var
NewNode, ParentNode: TTreeNode;
begin
Result := True;
ParentNode := TTreeNode(lParam);
if IsWindowVisible(hwnd) then
NewNode := ParentNode.Owner.AddChild(ParentNode,
GetWindowInfo(hwnd));
EnumChildWindows(hwnd, #EnumChildProc, Integer(NewNode));
end;
function EnumWindowsProc(hwnd: HWND; lParam: Integer): BOOL; stdcall;
var
NewNode: TTreeNode;
begin
Result := True;
if IsWindowVisible(hwnd) then
NewNode := TTreeView(lParam).Items.Add(nil, GetWindowInfo(hwnd));
EnumChildWindows(hwnd, #EnumChildProc, Integer(NewNode));
end;
procedure EnumWindowsTree(Tree: TTreeView);
begin
EnumWindows(#EnumWindowsProc, Integer(Tree));
end;
// Listing all windows in TreeView
procedure TForm2.Button1Click(Sender: TObject);
begin
TreeView1.Items.Clear;
EnumWindowsTree(TreeView1);
end;
//Tranfers all nodes of TreeView for a Memo (one by one)
procedure TForm2.Button3Click(Sender: TObject);
var I,P,Cnt : Integer;
ParentNode, ChildNode: TTreeNode;
begin
P := 65;
ParentNode := TreeView1.Items[0];
While ParentNode<>nil do
begin
if (ParentNode <> nil) then
begin
Memo1.Lines.Add(ParentNode.Text);
Cnt := 1;
ChildNode := ParentNode.GetFirstChild;
while (ChildNode <> nil) do
begin
Memo1.Lines.Add(ChildNode.Text);
if ChildNode.HasChildren then
begin
ParentNode:= ChildNode.GetFirstChild;
break;
end;
ChildNode := ChildNode.GetNextSibling;
Inc(Cnt);
end;
end;
if ChildNode=nil then
begin
if ParentNode.GetNextSibling<>nil then
ParentNode:=ParentNode.GetNextSibling
else
begin
while ParentNode.GetNextSibling=nil do
begin
if ParentNode.Parent<>nil then ParentNode:=ParentNode.Parent else break;
end;
if ParentNode<>nil then ParentNode:=ParentNode.GetNextSibling;
end;
end;
Inc(P);
end;
end;
It would better to use inbuilt methods of TreeView contents storing:
// Tranfers all nodes of TreeView for a Memo (one by one)
var
MS: TMemoryStream;
begin
MS := TMemoryStream.Create;
try
TreeView1.SaveToStream(MS);
MS.Position := 0;
Memo1.Lines.LoadFromStream(MS);
finally
Ms.Free;
end;
end;
// Tranfers all nodes to TreeView from a Memo
var
MS: TMemoryStream;
begin
MS := TMemoryStream.Create;
try
Memo1.Lines.SaveToStream(MS);
MS.Position := 0;
TreeView1.LoadFromStream(MS);
finally
Ms.Free;
end;
end;
Note that unnamed windows break formatting needed for correct restoring, so I've changed string format a bit: '.[' instead of space.
function GetWindowInfo(hwnd: HWND): string;
begin
Result := GetWindowTitle(hwnd) + '.[' + GetWindowClass(hwnd) + '] (' +
{ IntToStr } IntToHex(hwnd, 8) + ')';
end;
Related
I made a directory TreeView in Delphi FMX, but when I expand 'C:\Windows\WinSxS' it contains 15000 folders. It took a lot of time, but it doesn't expand. On the other hand, when I tried to do it with a VCL TreeView, it worked fine, as it should. Is there any way to make it fast?
Here is my code:
function SlashSep(const Path, S: String): String;
begin
{$IF DEFINED(CLR)}
if Path[Length(Path)] <> '\' then
{$ELSE}
if AnsiLastChar(Path)^ <> '\' then
{$ENDIF}
Result := Path + '\' + S
else
Result := Path + S;
end;
procedure GetDir(const ParentDirectory: string; ParentItem: TTreeViewItem);
var
Status: Integer;
SearchRec: TSearchRec;
Node: TTreeViewItem;
begin
Status := FindFirst(SlashSep(ParentDirectory, '*.*'), faDirectory, SearchRec);
try
while Status = 0 do
begin
if (SearchRec.Attr and faDirectory = faDirectory) then
begin
if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
begin
Node := AddChild(ParentItem,ExtractFileName(SearchRec.Name));
Node.HasChildren := True;
end;
end;
Status := FindNext(SearchRec);
end;
finally
FindClose(SearchRec);
end;
end;
procedure TForm1.ItemOnExpanding(Sender: TObject; Node: TTreeViewItem);
var
i: Integer;
begin
for i := Node.Count - 1 downto 0 do
TreeView1.RemoveObject(Node.Items[i]);
Node.BeginUpdate;
GetDir(GetPathItem(node),node);
Node.EndUpdate;
end;
It's expanding from here:
procedure TCustomTreeView.ItemExpanded(const Item: TTreeViewItem);
var
I: Integer;
Child: TTreeViewItem;
AllowExpansion: Boolean;
begin
InvalidateGlobalList;
if Item.IsExpanded then
for I := 0 to Item.Count - 1 do
begin
Child := Item.Items[I];
if not Child.IsInflated then
Child.Inflate;
end;
RealignContent;
//end;
if Assigned(FOnExpanding) then
if Item.IsExpanded then
FOnExpanding(Self, Item, AllowExpansion)
else
if Assigned(FOnCollapsing) then
if not Item.IsExpanded then
FOnCollapsing(Self, Item, AllowExpansion)
end;
I want to get and show the name and extension of selected file in explorer by delphi7.
I use below code for show caption of active window but i need selected file name in active window.
function ActiveCaption: string;
var
Handle: THandle;
Len: LongInt;
Title: string;
begin
Result := '';
Handle := GetForegroundWindow;
if Handle <> 0 then
begin
Len := GetWindowTextLength(Handle) + 1;
SetLength(Title, Len);
GetWindowText(Handle, PChar(Title), Len);
ActiveCaption := TrimRight(Title);
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Label1.Caption := ActiveCaption;
end;
The only way I know of is to use the Active-X IShellWindows and IWebBrowser Interfaces to to that.
First, you have to import the "Microsoft Internet Controls" Active-X (via the Component Menu). By that you will get a unit called "SHDocVW_TLB". Put this unit and the ActiveX unit in your uses clause.
Than you can use the following two functions to retrieve the selected file from the window handle provided:
The first function does a rough test if the given handle belongs to an explorer window
function isexplorerwindow(exwnd: hwnd): boolean;
var
p: array[0..max_path] of Char;
begin
GetClassName(exwnd, p, SizeOf(p));
result := ((strcomp(p, 'CabinetWClass') = 0) or (strcomp(p, 'ExploreWClass') = 0));
end;
And the second function retrieves the name of the nth selected file:
function getexplorerselectedfile(exwnd: hwnd; nr: integer): string;
var
pvShell: IShellWindows;
pvWeb2: IWebBrowser2;
ovIE: OleVariant;
psp: IServiceProvider;
psb: IShellBrowser;
isw: IShellView;
ido: IDataObject;
FmtEtc: TFormatEtc;
Medium: TStgMedium;
dwcount: integer;
n: integer;
p: array[0..max_path] of Char;
s: string;
found: boolean;
begin
found := false;
result := '';
s :='';
try
pvShell := CoShellWindows.Create;
for dwcount := 0 to Pred(pvShell.count) do
begin
ovIE := pvShell.Item(dwcount);
if (ovIE.hwnd = exwnd) or ((exwnd = 0) and isexplorerwindow(ovIE.hwnd)) then
begin
found := true;
if (IDispatch(ovIE).QueryInterface(IWebBrowser2, pvWeb2) = S_OK) then
begin
psp := (pvWeb2 as IServiceProvider);
psp.QueryService(IID_IShellBrowser, IID_IShellBrowser, psb);
psb.QueryActiveShellView(isw);
if isw.GetItemObject(SVGIO_SELECTION, IDataObject, pointer(ido)) = S_OK then
begin
try
FmtEtc.cfFormat := CF_HDROP;
FmtEtc.ptd := nil;
FmtEtc.dwAspect := DVASPECT_CONTENT;
FmtEtc.lindex := -1;
FmtEtc.tymed := TYMED_HGLOBAL;
ido.GetData(FmtEtc, Medium);
GlobalLock(Medium.hGlobal);
try
n := DragQueryFile(Medium.hGlobal, $FFFFFFFF, nil, 0);
if nr < n then
begin
DragQueryFile(Medium.hGlobal, nr, p, max_path);
s := strpas(p);
end;
finally
DragFinish(Medium.hGlobal);
GlobalUnLock(Medium.hGlobal);
end;
finally
ReleaseStgMedium(Medium);
end;
end;
pvWeb2 := nil;
end;
end;
ovIE := Unassigned;
if found then
break;
end;
pvShell := nil;
finally
result := s;
end;
end;
To test this code create a new project and place a button and a memo on the form.
Add the following units to the uses clause:
USES SHDocVW_TLB, ShlObj, activex, shellapi;
And add this code to the button event handler:
PROCEDURE TForm2.Button1Click(Sender: TObject);
VAR
wnd, exwnd: hwnd;
n: integer;
s: STRING;
BEGIN
exwnd := 0;
wnd := getwindow(getdesktopwindow, gw_child);
REPEAT
IF isexplorerwindow(wnd) THEN
BEGIN
exwnd := wnd;
break;
END;
wnd := getwindow(wnd, gw_hwndnext);
UNTIL (wnd = 0) OR (exwnd <> 0);
IF exwnd <> 0 THEN
BEGIN
n := 0;
REPEAT
s := getexplorerselectedfile(exwnd, n);
memo1.Lines.Add(s);
inc(n);
UNTIL s = '';
END;
END;
If you press the button, the memo will contain the selected files of the first open explorer window it finds. Of course you should have an explorer window open with at least one file selected.
I am looking at contributing to the Delphi SynEdit project with a syntax-highlighter for the Health Level 7 (HL7) v2 messaging Standard. I have no experience of creating a highlighter from scratch and there are two quirks that I have stumbled upon that differ from existing highlighters:
Fixed position keywords - first three characters of every line
Delimiters are defined in the begining of the message
Is there anyone out there who has any SynEdit experice with HL7 or similar syntaxes e.g. Edifact, X12?
Prototype
I've created a crude prototype using the OnPaintTransient event-handler which in fact works better than I anticipated :-) Basically it does the following:
Highlight each Segment ID navy-blue - first three characters on ever line. No checking done if the value is a valid segment.
Highlight all field delimiters grey - defined as the fourth character in MSH segment
Highlight all other delimiters blue - defined in the field called
Encoding Characters, which is the first field after the MSH segment ID.
The delimiter values used in the MSH segment are the delimiter values used throughout the entire message.
skip highlighting if underlying text is selected - looks prettier in my implementation.
Below is a screen-dump of the results when inserting the example message found at Wikipedia http://en.wikipedia.org/wiki/Health_Level_7 into a TSynMemo component.
Code OnPaintTransient
procedure TFormMain.SynMemoMsgPaintTransient(Sender: TObject; Canvas: TCanvas;
TransientType: TTransientType);
var
i, j: Integer;
DP: TDisplayCoord;
SelStartCoord, SelEndCoord, BC : TBufferCoord;
Pt: TPoint;
FieldDelimiter : char; // MSH|
Delimiters : string; // All message delimiters (including field delimiter)
IsSelected : boolean;
begin
//Avoid drawing twice - Only enter if TransientType = ttAfter.
if TransientType = ttBefore then exit;
//Exit if no text
if SynMemoMsg.Lines.Count = 0 then exit;
//Exit if message does not start with MSH (Message header segment)
if not AnsiStartsText('MSH', SynMemoMsg.Lines[0]) then exit;
//Get the message's delimiters specified as the characters directly after MSH
FieldDelimiter := Copy(SynMemoMsg.Lines[0], 4, 1)[1];
Delimiters := Copy(SynMemoMsg.Lines[0], 4, 5);
//Find out if any text is selected by the user - we will exclude this text from highlighting
SelStartCoord := SynMemoMsg.CharIndexToRowCol(SynMemoMsg.SelStart);
SelEndCoord := SynMemoMsg.CharIndexToRowCol(SynMemoMsg.SelEnd);
//parse evry visible line
for i := SynMemoMsg.TopLine to ((SynMemoMsg.TopLine + SynMemoMsg.LinesInWindow )-1) do
begin
//Highlight Segment ID, i.e. in this implementation the first 3 chars in each line
BC.Char := 1;
BC.Line := i;
//If whole line is selected then continue to next line without highlighting current
if (SelStartCoord.Line < BC.Line) and (SelEndCoord.Line > BC.Line) then continue;
DP := SynMemoMsg.BufferToDisplayPos(BC);
Pt := SynMemoMsg.RowColumnToPixels(DP);
if ((SelStartCoord.Line = BC.Line) and (SelStartCoord.Char > 3))
or ((SelStartCoord.Line <> BC.Line) and (SelEndCoord.Line <> BC.Line))
or (SynMemoMsg.SelLength = 0) then
begin
Canvas.Font.Color := clNavy;
Canvas.Font.Style := [fsBold];
Canvas.TextOut (Pt.X - 1, Pt.Y, Copy(SynMemoMsg.Lines[i - 1], 1, 3)); //Move the Bold text one pixel left to get space i.e. Pt.X - 1)
end;
//Highlight Delimiters - parse each charachter and check if delimiter and not selected
for j := 4 to Length(SynMemoMsg.Lines[i - 1]) do
begin
if IsDelimiter(Delimiters, SynMemoMsg.Lines[i - 1], j) then
begin
BC.Char := j;
BC.Line := i;
//Don't highlight delimiter if selected
if (SynMemoMsg.SelLength > 0) and ((SelStartCoord.Line = BC.Line)or (SelEndCoord.Line = BC.Line)) then
begin
if (SelStartCoord.Line = BC.Line) and (SelEndCoord.Line = BC.Line) then
IsSelected := (SelStartCoord.Char <= BC.Char) and (SelEndCoord.Char > BC.Char)
else if (SelStartCoord.Line = BC.Line) then
IsSelected := SelStartCoord.Char <= BC.Char
else if (SelEndCoord.Line = BC.Line) then
IsSelected := SelEndCoord.Char > BC.Char;
end
else
IsSelected := false;
if not IsSelected then begin
DP := SynMemoMsg.BufferToDisplayPos(BC);
Pt := SynMemoMsg.RowColumnToPixels(DP);
if FieldDelimiter = SynMemoMsg.Lines[i - 1][j] then
Canvas.Font.Color := clGray
else
Canvas.Font.Color := clBlue;
Canvas.TextOut (Pt.X, Pt.Y, Copy(SynMemoMsg.Lines[i - 1], j, 1));
end;
end;
end;
end;
end;
Well I ended up making my own SynEdit syntax-highlighter for HL7 v2.x messaging.
It may not have all the bells and whistles but it’s a good start. My implementation uses Delphi XE3.
Usage:
Copy the SynHighlighterHL7.pas unit found below to your synedit project source folder.
Add SynHighlighterHL7.pas to your project and to the Uses clause.
Add a TSynEdit or TSynMemo component to a form
Add the following code to the form's OnCreate event handler:
Code:
fSynHL7Syn := TSynHL7Syn.Create(Self);
SynMemoMsg.Highlighter := fSynHL7Syn;
SynHighlighterHL7.pas unit:
unit SynHighlighterHL7;
{$I SynEdit.inc}
interface
uses
Classes,
Graphics,
StrUtils,
SynEditTypes,
SynEditHighlighter,
SynUnicode;
const
DEF_FIELD_DELIM = '|'; //Filed seperator
DEF_COMP_DELIM = '^'; //Component seperator
DEF_SUBCOMP_DELIM = '&'; //Sub-component seperator
DEF_ESC_DELIM = '\'; //Escape seperator
DEF_REP_DELIM = '~'; //Repetition seperator
type
TtkTokenKind = (tkSegmentID, tkFieldDelim, tkCompDelim, tkSubCompDelim,
tkEscDelim, tkRepDelim, tkText, tkSpace, tkNull, tkUnknown);
//Keeps track if we're in a message with properly defined delimiters
TRangeState = (rsUnknown, rsMshDelim, rsDefDelim);
type
TSynHL7Syn = class(TSynCustomHighlighter)
private
fRange : TRangeState;
fFieldDelim : char;
fCompDelim : char;
fSubCompDelim : char;
fEscDelim : char;
fRepDelim : char;
FTokenID: TtkTokenKind;
fSegmentIDAttri: TSynHighlighterAttributes;
fFieldDelimAttri: TSynHighlighterAttributes;
fCompDelimAttri: TSynHighlighterAttributes;
fSubCompDelimAttri: TSynHighlighterAttributes;
fEscDelimAttri: TSynHighlighterAttributes;
fRepDelimAttri: TSynHighlighterAttributes;
fUnknownAttri: TSynHighlighterAttributes;
fSpaceAttri : TSynHighlighterAttributes;
fTextAttri: TSynHighlighterAttributes;
procedure SegmentIDProc;
procedure UnknownProc;
procedure CRProc;
procedure TextProc;
procedure LFProc;
procedure NullProc;
procedure SpaceProc;
procedure FieldDelimProc;
procedure CompDelimProc;
procedure EscDelimProc;
procedure RepDelimProc;
procedure SubCompDelimProc;
procedure SetRangeState(const Line: string);
protected
function GetSampleSource: UnicodeString; override;
function IsFilterStored: Boolean; override;
public
function GetRange: Pointer; override;
procedure ResetRange; override;
procedure SetRange(Value: Pointer); override;
class function GetLanguageName: string; override;
class function GetFriendlyLanguageName: UnicodeString; override;
public
constructor Create(AOwner: TComponent); override;
function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
override;
function GetEol: Boolean; override;
function GetTokenID: TtkTokenKind;
function GetTokenAttribute: TSynHighlighterAttributes; override;
function GetTokenKind: integer; override;
procedure Next; override;
published
property SegmentIDAttri: TSynHighlighterAttributes read fSegmentIDAttri
write fSegmentIDAttri;
property TextAttri: TSynHighlighterAttributes read fTextAttri
write fTextAttri;
end;
implementation
uses
SynEditStrConst;
constructor TSynHL7Syn.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fCaseSensitive := true;
fSegmentIDAttri := TSynHighlighterAttributes.Create('Seg ID', 'Segment ID');
fSegmentIDAttri.Style := [fsBold];
fSegmentIDAttri.Foreground := clNavy;
AddAttribute(fSegmentIDAttri);
fFieldDelimAttri := TSynHighlighterAttributes.Create('Field Sep', 'Field Seperator (|)');
fFieldDelimAttri.Foreground := clGray;
AddAttribute(fFieldDelimAttri);
fCompDelimAttri := TSynHighlighterAttributes.Create('Comp Sep', 'Component Seperator (^)');
fCompDelimAttri.Foreground := clBlue;
AddAttribute(fCompDelimAttri);
fSubCompDelimAttri := TSynHighlighterAttributes.Create('Sub-Comp Sep', 'Sub-Component Seperator (&)');
fSubCompDelimAttri.Foreground := clBlue;
AddAttribute(fSubCompDelimAttri);
fRepDelimAttri := TSynHighlighterAttributes.Create('Rep Sep', 'Repeat Seperator (&)');
fRepDelimAttri.Foreground := clBlue;
AddAttribute(fRepDelimAttri);
fEscDelimAttri := TSynHighlighterAttributes.Create('Esc Sep', 'Escape Seperator (\)');
fEscDelimAttri.Style := [fsBold];
fEscDelimAttri.Foreground := clGreen;
AddAttribute(fEscDelimAttri);
fUnknownAttri := TSynHighlighterAttributes.Create('Unknown', 'Non HL7 message i.e arbitary text');
fUnknownAttri.Style := [fsItalic];
fUnknownAttri.Foreground := clRed;
AddAttribute(fUnknownAttri);
fTextAttri := TSynHighlighterAttributes.Create(SYNS_AttrText, SYNS_FriendlyAttrText);
AddAttribute(fTextAttri);
fSpaceAttri := TSynHighlighterAttributes.Create(SYNS_AttrSpace, SYNS_FriendlyAttrSpace);
AddAttribute(fSpaceAttri);
SetAttributesOnChange(DefHighlightChange);
fDefaultFilter := SYNS_FilterINI;
end; { Create }
procedure TSynHL7Syn.FieldDelimProc;
begin
inc(Run);
fTokenID := tkFieldDelim;
end;
procedure TSynHL7Syn.CompDelimProc;
begin
inc(Run);
fTokenID := tkCompDelim;
end;
procedure TSynHL7Syn.SubCompDelimProc;
begin
inc(Run);
fTokenID := tkSubCompDelim;
end;
procedure TSynHL7Syn.EscDelimProc;
begin
fTokenID := tkEscDelim;
//If current position is not the first MSH field then expand token untill
//closing Escape delimiter is found on current line
if not((Run = 6) and StartsStr('MSH', fLine)) then begin
inc(run);
while (FLine[Run] <> fEscDelim) and (FLine[Run] <> #0) do
inc(Run);
end;
if FLine[Run] <> #0 then
inc(Run);
end;
procedure TSynHL7Syn.RepDelimProc;
begin
inc(Run);
fTokenID := tkRepDelim;
end;
procedure TSynHL7Syn.SetRangeState(const Line : string);
function IsValidSegmentIDChar(c : char): Boolean;
begin
case c of
'A'..'Z', '0'..'9':
Result := True;
else
Result := False;
end;
end;
var SegID : string;
OK : boolean;
i : integer;
begin
//Decide if valid segment or arbitary text
if AnsiStartsStr('MSH', Line) and (Length(Line) > 8) then begin
fRange := rsMshDelim;
fFieldDelim := Line[4];
fCompDelim := Line[5];
fRepDelim := Line[6];
//If no escape characters are used in a message, this character may be omitted.
//However, it must be present if subcomponents are used in the message.
if Line[7] <> fFieldDelim then
fEscDelim := Line[7]
else
fEscDelim := DEF_ESC_DELIM;
//If there are no subcomponents in message then this seperator may not be present (use default then)
if Line[8] <> fFieldDelim then
fSubCompDelim := Line[8]
else
fEscDelim := DEF_SUBCOMP_DELIM;
end
else begin
SegID := Copy(FLine, run + 1, 3);
OK := Length(SegID) = 3;
for i := 1 to Length(SegID) do
OK := OK and IsValidSegmentIDChar(SegID[i]);
if OK then begin
case fRange of
rsUnknown : if (Copy(Line, 4, 1) = '|') then fRange := rsDefDelim;
rsMshDelim : if (Copy(Line, 4, 1) <> fFieldDelim) then fRange := rsUnknown;
rsDefDelim : if (Copy(Line, 4, 1) <> '|') then fRange := rsUnknown;
end;
end
else
fRange := rsUnknown;
end;
end;
procedure TSynHL7Syn.ResetRange;
begin
fRange:= rsUnknown;
end;
procedure TSynHL7Syn.SegmentIDProc;
function IsValidSegmentIDChar(c : char): Boolean;
begin
case c of
'A'..'Z', '0'..'9':
Result := True;
else
Result := False;
end;
end;
var OK : boolean;
SegID : String;
i : integer;
begin
// if it is not column 0-2 mark as tkText and get out of here
if Run > 0 then
begin
fTokenID := tkText;
inc(Run);
Exit;
end;
case fRange of
rsMshDelim, rsDefDelim : begin
fTokenID := tkSegmentID;
Run := 3;
end;
rsUnknown : begin
fTokenID := tkUnknown;
Inc(Run);
end;
end;
end;
procedure TSynHL7Syn.CRProc;
begin
fTokenID := tkSpace;
case FLine[Run + 1] of
#10: inc(Run, 2);
else inc(Run);
end;
end;
procedure TSynHL7Syn.TextProc;
function IsTextChar: Boolean;
begin
case fLine[Run] of
'a'..'z', 'A'..'Z', '0'..'9':
Result := True;
else
Result := False;
end;
end;
begin
if Run = 0 then
SegmentIDProc
else
begin
fTokenID := tkText;
inc(Run);
while FLine[Run] <> #0 do
if IsTextChar then
inc(Run)
else
break;
end;
end;
procedure TSynHL7Syn.UnknownProc;
begin
if Run = 0 then
Self.SetRangeState(fLine);
// this is column 0 ok it is a comment
fTokenID := tkUnknown;
inc(Run);
while FLine[Run] <> #0 do
case FLine[Run] of
#10: break;
#13: break;
else inc(Run);
end;
end;
procedure TSynHL7Syn.LFProc;
begin
fTokenID := tkSpace;
inc(Run);
end;
procedure TSynHL7Syn.SetRange(Value: Pointer);
begin
fRange := TRangeState(Value);
end;
procedure TSynHL7Syn.SpaceProc;
begin
inc(Run);
fTokenID := tkSpace;
while (FLine[Run] <= #32) and not IsLineEnd(Run) do inc(Run);
end;
procedure TSynHL7Syn.Next;
begin
//Decide range state by checking first char in line
fTokenPos := Run;
if Run = 0 then SetRangeState(fLine);
case fRange of
rsUnknown : case fLine[Run] of
#0: NullProc;
#10: LFProc;
#13: CRProc;
else
UnknownProc;
end;
rsMshDelim : begin
if fLine[Run] = Self.fFieldDelim then
FieldDelimProc
else if fLine[Run] = Self.fCompDelim then
CompDelimProc
else if fLine[Run] = Self.fSubCompDelim then
SubCompDelimProc
else if fLine[Run] = Self.fEscDelim then
EscDelimProc
else if fLine[Run] = Self.fRepDelim then
RepDelimProc
else begin
case fLine[Run] of
#0: NullProc;
#10: LFProc;
#13: CRProc;
#1..#9, #11, #12, #14..#32: SpaceProc;
else TextProc;
end;
end
end;
rsDefDelim : case fLine[Run] of
#0: NullProc;
#10: LFProc;
#13: CRProc;
#1..#9, #11, #12, #14..#32: SpaceProc;
DEF_FIELD_DELIM : FieldDelimProc;
DEF_COMP_DELIM : CompDelimProc;
DEF_SUBCOMP_DELIM : SubCompDelimProc;
DEF_ESC_DELIM : EscDelimProc;
DEF_REP_DELIM : RepDelimProc;
else TextProc;
end;
end;
inherited;
end;
procedure TSynHL7Syn.NullProc;
begin
fTokenID := tkNull;
inc(Run);
end;
function TSynHL7Syn.GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
begin
case Index of
SYN_ATTR_WHITESPACE: Result := fSpaceAttri;
else
Result := nil;
end;
end;
function TSynHL7Syn.GetEol: Boolean;
begin
Result := Run = fLineLen + 1;
end;
function TSynHL7Syn.GetTokenID: TtkTokenKind;
begin
Result := fTokenId;
end;
function TSynHL7Syn.GetTokenAttribute: TSynHighlighterAttributes;
begin
case fTokenID of
tkSegmentID: Result := fSegmentIDAttri;
tkFieldDelim: Result := fFieldDelimAttri;
tkCompDelim: Result := fCompDelimAttri;
tkSubCompDelim: Result := fSubCompDelimAttri;
tkRepDelim: Result := fRepDelimAttri;
tkEscDelim: Result := fEscDelimAttri;
tkText: Result := fTextAttri;
tkSpace: Result := fSpaceAttri;
tkUnknown: Result := fUnknownAttri;
else Result := nil;
end;
end;
function TSynHL7Syn.GetTokenKind: integer;
begin
Result := Ord(fTokenId);
end;
function TSynHL7Syn.IsFilterStored: Boolean;
begin
Result := fDefaultFilter <> SYNS_FilterINI;
end;
class function TSynHL7Syn.GetLanguageName: string;
begin
Result := SYNS_LangINI;
end;
function TSynHL7Syn.GetRange: Pointer;
begin
Result := Pointer(fRange);
end;
function TSynHL7Syn.GetSampleSource: UnicodeString;
begin
Result := 'MSH|^&\~|123|123'#13#10+
'PID|123|1234'
end;
{$IFNDEF SYN_CPPB_1}
class function TSynHL7Syn.GetFriendlyLanguageName: UnicodeString;
begin
Result := SYNS_FriendlyLangINI;
end;
initialization
RegisterPlaceableHighlighter(TSynHL7Syn);
{$ENDIF}
end.
I have X (more than 1) comboboxes declared on a form. (Designtime)
All these comboboxes have the same properties (except position, handle, and a few others they can't share)
I would to give them all the same behavior during runtime, which means if e.g. I add/delete an item or change the ItemIndex or stuff like that, then all comboboxes should do the same.
How can I "clone" all properties/events/etc. from one component at runtime to X other components without doing an operation over and over again for each component?
You can use ReadComponent and WriteComponent from TStream too.
procedure TForm1.Button1Click(Sender: TObject);
var
oStream: TMemoryStream;
i: integer;
cbCombos: array[0..4] of TComboBox;
begin
oStream := TMemoryStream.Create;
ComboBox1.Tag := '666'; { \m/ }
try
oStream.WriteComponent(ComboBox1);
for i := 0 to 4 do
begin
cbCombos[i] := TComboBox.CreateParented(Self.Handle);
oStream.Position := 0;
oStream.ReadComponent(cbCombos[i]);
cbCombos[i].Name := 'AnotherComboBox' + IntToStr(i+1);
cbCombos[i].Parent := Self;
cbCombos[i].Tag := cbCombos[i].Tag + i + 1;
cbCombos[i].Left := 16;
cbCombos[i].Top := 36 * (i + 2);
cbCombos[i].OnMouseEnter := ComboBox1MouseEnter;
end;
finally
FreeAndNil(oStream);
end;
end;
procedure TForm1.ComboBox1MouseEnter(Sender: TObject);
begin
TWinControl(Sender).Hint := IntToStr(TWinControl(Sender).Tag);
end;
You can do that via Extended RTTI
This is a start - by no means complete:
procedure TForm62.CloneComponent(const aSource, aDestination: TComponent);
var
ctx: TRttiContext;
RttiType, DestType: TRttiType;
RttiProperty: TRttiProperty;
Buffer: TStringlist;
begin
if aSource.ClassType <> aDestination.ClassType then
raise Exception.Create('Source and destiantion must be the same class');
Buffer := TStringlist.Create;
try
Buffer.Sorted := True;
Buffer.Add('Name');
Buffer.Add('Handle');
RttiType := ctx.GetType(aSource.ClassType);
DestType := ctx.GetType(aDestination.ClassType);
for RttiProperty in RttiType.GetProperties do
begin
if not RttiProperty.IsWritable then
continue;
if Buffer.IndexOf(RttiProperty.Name) >= 0 then
continue;
DestType.GetProperty(RttiProperty.Name).SetValue(aDestination, RttiProperty.GetValue(aSource));
end;
finally
Buffer.Free;
end;
end;
I found this code over the net. This puts background color to the selected texts on Trichedit:
uses
RichEdit;
procedure RE_SetSelBgColor(RichEdit: TRichEdit; AColor: TColor);
var
Format: CHARFORMAT2;
begin
FillChar(Format, SizeOf(Format), 0);
with Format do
begin
cbSize := SizeOf(Format);
dwMask := CFM_BACKCOLOR;
crBackColor := AColor;
Richedit.Perform(EM_SETCHARFORMAT, SCF_SELECTION, Longint(#Format));
end;
end;
// Example: Set clYellow background color for the selected text.
procedure TForm1.Button1Click(Sender: TObject);
begin
RE_SetSelBgColor(RichEdit1, clYellow);
end;
However, what I need is to exclude space characters. Can someone help me? Any idea would be helpful?
My idea would be to select all space characters and then format it but then I don't know how to select them.
By the way, I am using delphi 2009.
#junmats, with this code you can select any word in a richedit control.
tested in Delphi 2010 and windows 7
uses
RichEdit;
procedure SetWordBackGroundColor(RichEdit : TRichEdit; aWord : String;AColor: TColor);
var
Format: CHARFORMAT2;
Index : Integer;
Len : Integer;
begin
FillChar(Format, SizeOf(Format), 0);
Format.cbSize := SizeOf(Format);
Format.dwMask := CFM_BACKCOLOR;
Format.crBackColor := AColor;
Index := 0;
Len := Length(RichEdit.Lines.Text) ;
Index := RichEdit.FindText(aWord, Index, Len, []);
while Index <> -1 do
begin
RichEdit.SelStart := Index;
RichEdit.SelLength := Length(aWord) ;
RichEdit.Perform(EM_SETCHARFORMAT, SCF_SELECTION, Longint(#Format));
Index := RichEdit.FindText(aWord,Index + Length(aWord),Len, []) ;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SetWordBackGroundColor(RichEdit1,' ',clYellow);// will mark all spaces
end;
if you wanna select all words except the spaces, you can do something like this
Procedure GetListofWords(Text : String; var ListofWords : TStringList);
var
DummyStr : String;
FoundWord : String;
begin
DummyStr := Text;
FoundWord := '';
if (Length(Text) = 0) then exit;
while (Pos(' ', DummyStr) > 0) do
begin
FoundWord := Copy(DummyStr, 1, Pos(' ', DummyStr) - 1);
ListofWords.Add(FoundWord);
DummyStr := Copy(DummyStr, Pos(' ', DummyStr) + 1, Length(DummyStr) - Length(FoundWord) + 1);
end;
if (Length(DummyStr) > 0) then
ListofWords.Add(DummyStr);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
ListofWords : TStringList;
i : integer;
begin
ListofWords:=TStringList.Create;
try
GetListofWords(RichEdit1.Lines.Text,ListofWords);
if ListofWords.Count>0 then
for i:=0 to ListofWords.Count - 1 do
SetWordBackGroundColor(RichEdit1,ListofWords[i],clYellow);
finally
ListofWords.Clear;
ListofWords.Free;
end;
end;