{*******************************************************************} { } { ExpressWeb Framework by Developer Express } { Designer Module } { } { Copyright (c) 2000-2007 Developer Express Inc. } { ALL RIGHTS RESERVED } { } { The entire contents of this file is protected by U.S. and } { International Copyright Laws. Unauthorized reproduction, } { reverse-engineering, and distribution of all or any portion of } { the code contained in this file is strictly prohibited and may } { result in severe civil and criminal penalties and will be } { prosecuted to the maximum extent possible under the law. } { } { RESTRICTIONS } { } { THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES } { (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE } { SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS } { LICENSED TO DISTRIBUTE THE EXPRESSWEB FRAMEWORK AND ALL } { ACCOMPANYING VCL CLASSES AS PART OF AN EXECUTABLE WEB } { APPLICATION ONLY. } { } { THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED } { FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE } { COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE } { AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT } { AND PERMISSION FROM DEVELOPER EXPRESS INC. } { } { CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON } { ADDITIONAL RESTRICTIONS. } { } {*******************************************************************} unit cxWebHTMLDesigner; interface uses Windows, SysUtils, Messages, Classes, Controls, ToolsAPI, Forms, Graphics, ImgList, ExtCtrls, Menus, DesignIntf, DesignEditors, VCLEditors, VCLMenus, DesignMenus, cxWebCustomDesigner, cxWebIntf, cxWebClasses, cxWebDsgnEnvOpt, cxWebDsgnIntf, cxWebDsgnTypes, cxWebDsgnHTMLSource, cxWebDsgnDelphiManager, MSHTML_TLB, cxWebDsgnHTMLConverter, cxWebRender, cxWebDsgnHTMLCtrls, cxWebHTMLEditor, cxWebHTMLEdBar, cxWebHTMLEdIntf; type TcxWebHTMLDesigner = class; TcxWebHTMLControl = class(TcxHTMLEditor) private FDesigner: TcxWebHTMLDesigner; function GetMainParentElement: IHTMLElement; procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN; procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE; protected function ShowBorders: Boolean; override; function ShowDetails: Boolean; override; function ShowGrid: Boolean; override; function SnapToGrid: Boolean; override; function GridXSize: Integer; override; function GridYSize: Integer; override; function IsToolSelected: Boolean; override; procedure GetViewLinkContent(const AElement: IHTMLElement; var AContent, ACSSContent, AInlineStyle: string); override; function IsElementContainer(const AElement: IHTMLElement): Boolean; override; function CanHaveBehavior(const AElement: IHTMLElement): Boolean; override; function ContainerLayout(const AElement: IHTMLElement): TcxHTMLContainerLayout; override; function CanElementSelect(const AElement: IHTMLElement): Boolean; override; procedure DoOnKeyDown(const AEventObj: IHTMLEventObj); override; function DoOnDblClick(const AEventObj: IHTMLEventObj): WordBool; override; function DoOnDrop(const AEventObj: IHTMLEventObj): WordBool; override; procedure GetHTMLElementsList(const AList: IInterfaceList); override; public procedure DeactivateInnerHMTLElement; procedure RegisterEWFTags; property MainParentElement: IHTMLElement read GetMainParentElement; end; TcxWebHTMLSourceEditorNotifier = class(TNotifierObject, IOTANotifier, IOTAEditorNotifier) private FDesigner: TcxWebHTMLDesigner; FID: Integer; FIsModified: Boolean; protected procedure AfterSave; procedure BeforeSave; procedure Destroyed; procedure Modified; procedure ViewNotification(const View: IOTAEditView; Operation: TOperation); procedure ViewActivated(const View: IOTAEditView); public constructor Create(ADesigner: TcxWebHTMLDesigner); destructor Destroy; override; property ID: Integer read FID write FID; property IsModified: Boolean read FIsModified write FIsModified; end; TcxWebFormNotifier = class(TNotifierObject, IOTANotifier, IOTAFormNotifier) private FID: Integer; FDesigner: TcxWebHTMLDesigner; FEditor: IOTAEditor; protected procedure AfterSave; procedure BeforeSave; procedure Destroyed; procedure Modified; procedure FormActivated; procedure FormSaving; procedure ComponentRenamed(ComponentHandle: TOTAHandle; const OldName, NewName: string); public constructor Create(ADesigner: TcxWebHTMLDesigner); destructor Destroy; override; end; TcxWebIDENotifier = class(TNotifierObject, IOTANotifier, IOTAIDENotifier) private FID: Integer; FDesigner: TcxWebHTMLDesigner; protected procedure AfterSave; procedure BeforeSave; procedure Destroyed; procedure Modified; procedure FileNotification(NotifyCode: TOTAFileNotification; const FileName: string; var Cancel: Boolean); procedure BeforeCompile(const Project: IOTAProject; var Cancel: Boolean); procedure AfterCompile(Succeeded: Boolean); public constructor Create(ADesigner: TcxWebHTMLDesigner); destructor Destroy; override; end; TcxWebDesignerControlNotify = class(TInterfacedObject, IcxWebDesignerControlNotify) private FComponent: TComponent; FDesigner: TcxWebHTMLDesigner; FOldWidth, FOldHeight: Integer; function GetWebControl: IcxWebControl; public constructor Create(ADesigner: TcxWebHTMLDesigner; AComponent: TComponent); procedure BoundsChanged; procedure ParentChanged; procedure UpdateContext; property WebControl: IcxWebControl read GetWebControl; end; TcxWebHTMLUndoItem = class private FStream: TMemoryStream; FComponentClass: TComponentClass; FName: string; public constructor Create(AComponentClass: TComponentClass; AName: string; AStream: TStream); destructor Destroy; override; property Name: string read FName; property ComponentClass: TComponentClass read FComponentClass; end; TcxWebHTMLUndoItems = class private FDesigner: TcxWebHTMLDesigner; FItems: TList; public constructor Create(ADesigner: TcxWebHTMLDesigner); destructor Destroy; override; procedure AddItem(AComponentClass: TComponentClass; AName: string; AStream: TStream); function LastItem: TcxWebHTMLUndoItem; procedure RestoreItem; end; TcxWebHTMLUndo = class private FDesigner: TcxWebHTMLDesigner; FList: TList; protected function DesignerControl: TcxWebHTMLControl; function LastItem: TcxWebHTMLUndoItems; public constructor Create(ADesigner: TcxWebHTMLDesigner); destructor Destroy; override; procedure Clear; procedure Undo; function CanUndo: Boolean; function LastGroup: TcxWebHTMLUndoItems; function CreateNewGroup: TcxWebHTMLUndoItems; procedure AddDeletedComponent(AItem: TcxWebHTMLUndoItems; AComponent: TComponent); end; TcxWebHTMLDesigner = class(TcxWebCustomDesigner, IcxBaseHTMLElementCreatorNotify) private FDesignerControl: TcxWebHTMLControl; FDesignerBar: TcxHTMLEditorBar; FUndo: TcxWebHTMLUndo; FHTMLSource: TcxWebDsgnHTMLSource; FHTMLSourceEditorNotifier: TcxWebHTMLSourceEditorNotifier; FEditorManagerListener: TcxWebDelphiEditorManagerListener; FFormNotifier: IOTAFormNotifier; FIDeNotifier: IOTAIDENotifier; FHTMLSelectedControls: TList; FHasHTMLLoaded: Boolean; FHTMLSaving: Boolean; FIsComponentInserting: Boolean; FInsertingComponent: TComponent; FInsertingComponentName: string; FClipboardSelection: TList; FStoredTags: TcxEWFStoredTags; FChangedNames: TStringList; FOldPositioningType: TcxWebPositioningType; FHTMLBefore, FHTMLAfter: string; // HTML Editor events procedure DHTMLControlInsertControl(Sender: TObject; R: TRect; var HTML, InnerHTML: string; var ParentElement: IHTMLElement); procedure DHTMLControlInsertedControl(Sender: TObject; Element: IHTMLElement); procedure DHTMLControlGetContext(AElement: IHTMLElement; var AContent, ACSSContent, AInlineStyle: string); procedure DHTMLControlParentChanged(ASender: TObject; ANewParent: IHTMLElement); procedure DHTMLControlSelectionChanged(Sender: TObject); procedure DHTMLControlMoved(Sender: TObject); procedure DHTMLControlResized(Sender: TObject); procedure DHTMLControlContextMenu(ASender: TObject; APos: TPoint; AElement: IHTMLElement); procedure DoEditorFormActivated(Sender: TObject); // HTML & designer synchronization procedure UpdateHTMLSource; procedure UpdateDesignerHTML(ADeleteComponents: Boolean); procedure UpdateHTMLControlContext(AComponent: TComponent); procedure InsertWebDesignControl(AWebControl: IcxWebControl); procedure DHTMLControlBoundsRectChanged(AIsMoved: Boolean); procedure DoMapperElementChanged(Sender: TObject); procedure HTMLChangeNotify(Sender: TObject); procedure FreeComponents(AList: TList; ADelete: Boolean); procedure CheckForDeletedControls; procedure UpdateSelectedEWFBehaviors; procedure InternalInsertHTMLControl(AComponent: TComponent; ANewName: string; ANeedOffSet: Boolean); //Selection procedure ClearHTMLSelectedControls; procedure ClearSelection; procedure CreateHTMLSelectedControls; function SelectionCount: Integer; function IsComponentSelected: Boolean; procedure InternalDHTMLControlSelectionChanged; procedure ReadComponent(AComponent: TComponent); procedure DoPaste; procedure DoCopy; protected procedure IDEDesignerOptionsChagned; override; function GetHTMLTemplateModified(var AFileName, AText: string): Boolean; override; { IcxBaseHTMLElementCreatorNotify } procedure HTMLElementCreated(ABaseHTMLElement: TcxBaseHTMLElement); function GetHTMLElementFileName: string; procedure ReaderSetName(AReader: TReader; AComponent: TComponent; var AName: string); override; public constructor Create(AForm: TCustomForm; ADelphiDesigner: IDesigner); override; destructor Destroy; override; procedure Modified; override; procedure ComponentCreated(AnItem: TComponent); override; procedure ComponentDestroyed(AnItem: TComponent); override; procedure ComponentChangedName(AComponent: TComponent; const AOldName, ANewName: string); override; procedure CreateDesignerControl(AParentControl: TWinControl); override; procedure DestroyDesignerControl; override; function GetDesignerControl: TWinControl; override; procedure RefreshDesignControls; override; procedure UpdateDesignerControl; override; procedure DesignerIdChanging; override; procedure DesignerPositioningTypeChanged; override; procedure DesignerActivated; override; procedure DesignerDeactivated; override; procedure PreviewShowing; override; function GetControlPositioningType: TcxWebPositioningType; override; class function GetID: Integer; override; class function GetName: String; override; function CanAlign: Boolean; override; function CanAlignToGrid: Boolean; override; function CanCopy: Boolean; override; function CanCut: Boolean; override; function CanDelete: Boolean; override; function CanPaste: Boolean; override; function CanSelectAll: Boolean; override; function CanTabOrder: Boolean; override; function CanUndo: Boolean; override; procedure ScaleSelection(AScaleFactor: Integer); override; procedure AlignSelection(AHorzAlign: TcxHorzAlign; AVertAlign: TcxVertAlign); override; procedure SizeSelection(AHorzAffect: TcxSizeAffect; AVertAffect: TcxSizeAffect; AHorzAbsolute: Integer; AVertAbsolute: Integer); override; function ShowTabOrderSelectionDlg: Boolean; override; { IcxWebDesigner } procedure AlignToGrid; override; function CanSelect(const AControl: TComponent): Boolean; override; procedure CopySelection; override; procedure CutSelection; override; procedure DeleteSelection; override; procedure Edit(AComponent: TComponent); override; procedure GetSelections(const AList: TList); override; procedure PasteSelection; override; procedure SelectAll; override; procedure SelectionChanged(const AList: TList); override; procedure Undo; override; end; implementation uses ComponentDesigner, cxWebDsgnFactory, cxWebStrs, StrUtils, cxWebUtils, cxWebHTMLElementMapper, HTTPProd, cxWebHTMLProd, cxWebDsgnUtils, Clipbrd, Variants, cxWebFileCacheManager, cxWebPathConst, cxfmTabOrder, cxWebDsgnStrs; { TcxWebHTMLControl } procedure TcxWebHTMLControl.GetHTMLElementsList(const AList: IInterfaceList); begin GetHTMLElements(Document, AList); end; procedure TcxWebHTMLControl.RegisterEWFTags; var I: Integer; AStrings: TStringList; begin AStrings := TStringList.Create; GetRegisterEWFTags(AStrings); try for I := 0 to AStrings.Count - 1 do RegisterTags(scxEWF, [AStrings[I]]); finally AStrings.Free; end; end; function TcxWebHTMLControl.GetMainParentElement: IHTMLElement; begin Result := GetDesignerHTMLMainParentElement(Document, FDesigner.Root); end; procedure TcxWebHTMLControl.WMKeyDown(var Message: TWMKeyDown); begin if Message.CharCode = VK_TAB then Message.Result := 1 else inherited; end; procedure TcxWebHTMLControl.WMGetDlgCode(var Message: TWMGetDlgCode); begin Message.Result := DLGC_WANTARROWS or DLGC_WANTTAB; end; function TcxWebHTMLControl.ShowBorders: Boolean; begin Result := inherited ShowBorders; end; function TcxWebHTMLControl.ShowDetails: Boolean; begin Result := inherited ShowDetails; end; function TcxWebHTMLControl.ShowGrid: Boolean; begin Result := FDesigner.DisplayGrid; end; function TcxWebHTMLControl.SnapToGrid: Boolean; begin Result := FDesigner.SnapToGrid; end; function TcxWebHTMLControl.GridXSize: Integer; begin Result := FDesigner.GridSizeX; end; function TcxWebHTMLControl.GridYSize: Integer; begin Result := FDesigner.GridSizeY; end; function TcxWebHTMLControl.IsToolSelected: Boolean; begin Result := FDesigner.GetCurrComponentClass <> nil; end; type TWinControlAccess = class(TWinControl); procedure TcxWebHTMLControl.DoOnKeyDown(const AEventObj: IHTMLEventObj); begin if AEventObj <> nil then case AEventObj.keyCode of Integer('Z'): //TODO if AEventObj.ctrlKey then begin AEventObj.returnValue := False; FDesigner.Undo; end; Integer('X'): if AEventObj.ctrlKey then begin AEventObj.returnValue := False; FDesigner.CutSelection; end; Integer('V'): if AEventObj.ctrlKey then begin AEventObj.returnValue := False; FDesigner.PasteSelection; end; Integer('C'): if AEventObj.ctrlKey then begin AEventObj.returnValue := False; FDesigner.CopySelection; end; VK_INSERT: begin if AEventObj.ctrlKey then begin AEventObj.returnValue := False; FDesigner.CopySelection; end else if AEventObj.shiftKey then begin AEventObj.returnValue := False; FDesigner.PasteSelection; end; end; VK_DELETE: begin if FDesigner.IsComponentSelected then begin AEventObj.returnValue := False; FDesigner.DeleteSelection; end; end; VK_F9, VK_F12: begin AEventObj.returnValue := False; AEventObj.cancelBubble := True; if (GetParentForm(self) <> nil) then begin try Enabled := False; finally Enabled := True; end; PostMessage(GetParentForm(self).Handle, WM_KEYDOWN, AEventObj.keyCode, 0); PostMessage(GetParentForm(self).Handle, WM_KEYUP, AEventObj.keyCode, 0); end; exit; end; end; inherited DoOnKeyDown(AEventObj); end; function TcxWebHTMLControl.DoOnDblClick(const AEventObj: IHTMLEventObj): WordBool; var AComponent: TComponent; begin Result := inherited DoOnDblClick(AEventObj); if (AEventObj.srcElement <> nil) then begin AComponent := GetComponentByHTMLElement(FDesigner.Root, AEventObj.srcElement); if (AComponent <> nil) and not Supports(AComponent, IcxWebContainerControl) then FDesigner.DelphiDesigner.Edit(AComponent); end; end; procedure TcxWebHTMLControl.GetViewLinkContent(const AElement: IHTMLElement; var AContent, ACSSContent, AInlineStyle: string); begin inherited; FDesigner.DHTMLControlGetContext(AElement, AContent, ACSSContent, AInlineStyle); end; function TcxWebHTMLControl.IsElementContainer(const AElement: IHTMLElement): Boolean; var AComponent: TComponent; begin AComponent := GetComponentByHTMLElement(FDesigner.Root, AElement); Result := (AComponent <> nil) and Supports(AComponent, IcxWebContainerControl); end; function TcxWebHTMLControl.CanHaveBehavior(const AElement: IHTMLElement): Boolean; begin Result := not Supports(AElement, IHTMLFormElement) and not SameText(AElement.tagName, 'form') ; end; function TcxWebHTMLControl.ContainerLayout(const AElement: IHTMLElement): TcxHTMLContainerLayout; begin Result := inherited ContainerLayout(AElement); if Supports(AElement, IHTMLBodyElement) then if FDesigner.PositioningType = cxptFlow then Result := hclFlow else Result := hclGrid; end; function TcxWebHTMLControl.CanElementSelect(const AElement: IHTMLElement): Boolean; begin if AElement <> nil then Result := not Supports(AElement, IHTMLBodyElement) and not Supports(AElement, IHTMLFormElement) and not SameText(AElement.tagName, 'form') else Result := False; end; procedure TcxWebHTMLControl.DeactivateInnerHMTLElement; var AElement: IHTMLElement; begin if not FDesigner.FHasHTMLLoaded then exit; if (Selection.Count = 1) then begin AElement := Selection[0]; if (AElement <> nil) and not (AElement as IHTMLElement3).isDisabled and (GetComponentByHTMLElement(FDesigner.Root, AElement) = nil) then begin try FDesigner.BeginUpdate; (AElement as IHTMLElement3).disabled := true; (AElement as IHTMLElement3).disabled := false; if AElement.ParentElement <> nil then Selection.Add(AElement); finally FDesigner.CancelUpdate; end; end; end; end; function TcxWebHTMLControl.DoOnDrop(const AEventObj: IHTMLEventObj): WordBool; begin if AEventObj.ctrlKey and IsHTMLCustomElement(self.DraggingElement) then begin AEventObj.returnValue := False; Result := False; end else Result := inherited DoOnDrop(AEventObj); end; procedure UpdateHTMLElementStylePositions(AHTMLElement: IHTMLElement; AWebControl: IcxWebControl); begin with AWebControl.BoundsRect do begin AHTMLElement.style.pixelLeft := Left; AHTMLElement.style.pixelTop := Top; AHTMLElement.style.pixelWidth := Right - Left; AHTMLElement.style.pixelHeight := Bottom - Top; end; end; { TcxWebDesignerControlNotify } constructor TcxWebDesignerControlNotify.Create(ADesigner: TcxWebHTMLDesigner; AComponent: TComponent); begin FDesigner := ADesigner; FComponent := AComponent; end; function TcxWebDesignerControlNotify.GetWebControl: IcxWebControl; begin Supports(FComponent, IcxWebControl, Result); end; procedure TcxWebDesignerControlNotify.BoundsChanged; var AHTMLElement: IHTMLElement; AComponentName: string; begin if not FDesigner.FHasHTMLLoaded or FDesigner.FHTMLSaving then exit; if FComponent.Name <> '' then AComponentName := FComponent.Name else AComponentName := FDesigner.FInsertingComponentName; if AComponentName = '' then exit; AHTMLElement := GetHTMLElementByComponentName(FDesigner.FDesignerControl.Document3, AComponentName); if AHTMLElement <> nil then UpdateHTMLElementStylePositions(AHTMLElement, WebControl); with WebControl.BoundsRect do begin if (FOldWidth <> Right - Left) or (FOldHeight <> Bottom - Top) then UpdateContext; FOldWidth := Right - Left; FOldHeight := Bottom - Top; if (FDesigner.UpdateCount = 0) then begin if not (FDesigner.FDesignerControl.State = esMoving) then WebControl.UpdateControlPosition; FDesigner.BeginUpdate; try FDesigner.DelphiDesigner.Modified; finally FDesigner.CancelUpdate; end; end; end; end; procedure TcxWebDesignerControlNotify.ParentChanged; var AWebParent: IcxWebContainerControl; AParentElement, AElement: IHTMLElement; AParentComponent: TComponent; AParentNode, AElementNode: IHTMLDOMNode; begin if not FDesigner.FHasHTMLLoaded or (csDestroying in FComponent.ComponentState) then exit; AWebParent := WebControl.Parent; AElement := GetHTMLElementByComponentName(FDesigner.FDesignerControl.Document3, FComponent.Name); AParentComponent := GetComponentByInterface(AWebParent); if AParentComponent = nil then AParentComponent := FDesigner.DelphiDesigner.Root; AParentElement := GetHTMLElementByComponentName(FDesigner.FDesignerControl.Document3, AParentComponent.Name); if AParentElement = nil then AParentElement := FDesigner.FDesignerControl.Document.body; if Supports(AElement, IHTMLDOMNode, AElementNode) and Supports(AParentElement, IHTMLDOMNode, AParentNode) then begin // if not AParentElement.contains(AElement) then if not IsEqualElement(AElement.parentElement, AParentElement) then AParentNode.appendChild(AElementNode); if FDesigner.PositioningType <> cxptFlow then begin if Supports(AParentComponent, IcxWebPage) then AElement.style.setAttribute('position', 'absolute', 0) else AElement.style.removeAttribute('position', 0); end; end; WebControl.UpdateControlPosition; end; procedure TcxWebDesignerControlNotify.UpdateContext; begin if not (csDestroying in FDesigner.Root.ComponentState) and FDesigner.FHasHTMLLoaded then FDesigner.UpdateHTMLControlContext(FComponent); end; { TcxWebHTMLUndoItem } constructor TcxWebHTMLUndoItem.Create(AComponentClass: TComponentClass; AName: string; AStream: TStream); begin FStream := TMemoryStream.Create; FStream.LoadFromStream(AStream); FComponentClass := AComponentClass; FName := AName; end; destructor TcxWebHTMLUndoItem.Destroy; begin FStream.Free; inherited Destroy; end; { TcxWebHTMLUndoItems } constructor TcxWebHTMLUndoItems.Create(ADesigner: TcxWebHTMLDesigner); begin FDesigner := ADesigner; FItems := TList.Create; end; destructor TcxWebHTMLUndoItems.Destroy; var I: Integer; begin for I := 0 to FItems.Count - 1 do TcxWebHTMLUndoItem(FItems[I]).Free; FItems.Free; inherited Destroy; end; procedure TcxWebHTMLUndoItems.AddItem(AComponentClass: TComponentClass; AName: string; AStream: TStream); begin FItems.Add(TcxWebHTMLUndoItem.Create(AComponentClass, AName, AStream)); end; function TcxWebHTMLUndoItems.LastItem: TcxWebHTMLUndoItem; begin if FItems.Count > 0 then Result := TcxWebHTMLUndoItem(FItems.Last) else Result := nil; end; procedure TcxWebHTMLUndoItems.RestoreItem; var AComponent: TComponent; AName: string; AWebControl: IcxWebControl; begin AComponent := LastItem.ComponentClass.Create(FDesigner.Root); if FDesigner.Root.FindComponent(LastItem.Name) = nil then AName := LastItem.Name else AName := FDesigner.DelphiDesigner.UniqueName(DropT(LastItem.ComponentClass.ClassName)); AComponent.Name := AName; LastItem.FStream.ReadComponent(AComponent); if Supports(AComponent, IcxWebControl, AWebControl) then begin AWebControl.UpdateControlPosition; if (AWebControl.DesignerControl <> nil) then AWebControl.DesignerControl.UpdateContext; end; FItems.Delete(FItems.Count - 1); end; { TcxWebHTMLUndo } constructor TcxWebHTMLUndo.Create(ADesigner: TcxWebHTMLDesigner); begin FDesigner := ADesigner; FList := TList.Create; end; destructor TcxWebHTMLUndo.Destroy; begin Clear; FList.Free; inherited Destroy; end; procedure TcxWebHTMLUndo.Clear; var I: Integer; begin for I := 0 to FList.Count - 1 do TcxWebHTMLUndoItem(FList[I]).Free; FList.Clear; end; function TcxWebHTMLUndo.DesignerControl: TcxWebHTMLControl; begin Result := FDesigner.FDesignerControl; end; function TcxWebHTMLUndo.LastItem: TcxWebHTMLUndoItems; begin if FList.Count > 0 then Result := TcxWebHTMLUndoItems(FList.Last) else Result := nil; end; procedure TcxWebHTMLUndo.Undo; begin if not CanUndo then exit; begin while (LastGroup.LastItem <> nil) do LastGroup.RestoreItem; LastGroup.Free; FList.Delete(FList.Count - 1); end; end; function TcxWebHTMLUndo.CanUndo: Boolean; begin Result := LastGroup <> nil; end; function TcxWebHTMLUndo.LastGroup: TcxWebHTMLUndoItems; begin if FList.Count > 0 then Result := TcxWebHTMLUndoItems(FList.Last) else Result := nil; end; function TcxWebHTMLUndo.CreateNewGroup: TcxWebHTMLUndoItems; begin Result := TcxWebHTMLUndoItems.Create(FDesigner); FList.Add(Result); end; procedure TcxWebHTMLUndo.AddDeletedComponent(AItem: TcxWebHTMLUndoItems; AComponent: TComponent); var AStream: TMemoryStream; begin if not Supports(AComponent, IcxWebControl) or (csDestroying in FDesigner.Root.ComponentState) then exit; AStream := TMemoryStream.Create; try AStream.WriteComponent(AComponent); AItem.AddItem(TComponentClass(AComponent.ClassType), AComponent.Name, AStream); finally AStream.Free; end; end; {TcxWebHTMLSourceEditorNotifier} constructor TcxWebHTMLSourceEditorNotifier.Create(ADesigner: TcxWebHTMLDesigner); begin FDesigner := ADesigner; end; destructor TcxWebHTMLSourceEditorNotifier.Destroy; begin inherited Destroy; end; procedure TcxWebHTMLSourceEditorNotifier.AfterSave; begin end; procedure TcxWebHTMLSourceEditorNotifier.BeforeSave; begin end; procedure TcxWebHTMLSourceEditorNotifier.Destroyed; begin end; procedure TcxWebHTMLSourceEditorNotifier.Modified; begin FIsModified := True; end; procedure TcxWebHTMLSourceEditorNotifier.ViewNotification(const View: IOTAEditView; Operation: TOperation); begin end; procedure TcxWebHTMLSourceEditorNotifier.ViewActivated(const View: IOTAEditView); begin end; { TcxWebFormNotifier } constructor TcxWebFormNotifier.Create(ADesigner: TcxWebHTMLDesigner); begin FDesigner := ADesigner; FEditor := (BorlandIDEServices as IOTAModuleServices).CurrentModule.CurrentEditor; if FEditor <> nil then FID := FEditor.AddNotifier(self); end; destructor TcxWebFormNotifier.Destroy; begin if FEditor <> nil then FEditor.RemoveNotifier(FID); FEditor := nil; inherited Destroy; end; procedure TcxWebFormNotifier.AfterSave; begin end; procedure TcxWebFormNotifier.BeforeSave; begin FDesigner.UpdateHTMLSource; end; procedure TcxWebFormNotifier.Destroyed; begin end; procedure TcxWebFormNotifier.Modified; begin end; procedure TcxWebFormNotifier.FormActivated; begin end; procedure TcxWebFormNotifier.FormSaving; begin FDesigner.UpdateHTMLSource; end; procedure TcxWebFormNotifier.ComponentRenamed(ComponentHandle: TOTAHandle; const OldName, NewName: string); begin end; { TcxWebIDENotifier } constructor TcxWebIDENotifier.Create(ADesigner: TcxWebHTMLDesigner); begin FDesigner := ADesigner; FID := (BorlandIDEServices as IOTAServices50).AddNotifier(self); end; destructor TcxWebIDENotifier.Destroy; begin (BorlandIDEServices as IOTAServices50).RemoveNotifier(FID); inherited Destroy; end; procedure TcxWebIDENotifier.AfterSave; begin end; procedure TcxWebIDENotifier.BeforeSave; begin end; procedure TcxWebIDENotifier.Destroyed; begin end; procedure TcxWebIDENotifier.Modified; begin end; procedure TcxWebIDENotifier.FileNotification(NotifyCode: TOTAFileNotification; const FileName: string; var Cancel: Boolean); var AImplFileName, AIntfFileName, AFormFileName: string; begin if (NotifyCode = ofnFileOpened) and not (FDesigner.FHasHTMLLoaded) and (FDesigner.FHTMLSource.SourceEditor = nil) then begin FDesigner.DelphiDesigner.ModuleFileNames(AImplFileName, AIntfFileName, AFormFileName); if FileName = AImplFileName then with FDesigner do begin FHTMLSource.ReRequestSourceEditor(FileName, nil); if FHTMLSource.SourceEditor <> nil then begin FHTMLSourceEditorNotifier.ID := FHTMLSource.SourceEditor.AddNotifier(FHTMLSourceEditorNotifier); UpdateDesignerHTML(False); end; end; end; if (NotifyCode = ofnFileOpening) then begin FDesigner.DelphiDesigner.ModuleFileNames(AImplFileName, AIntfFileName, AFormFileName); if FileName = AFormFileName then FDesigner.UpdateHTMLSource; end; end; procedure TcxWebIDENotifier.BeforeCompile(const Project: IOTAProject; var Cancel: Boolean); begin FDesigner.UpdateHTMLSource; end; procedure TcxWebIDENotifier.AfterCompile(Succeeded: Boolean); begin end; type TcxWebDesignerFileManager = class(TInterfacedObject, IDesignerFileManager) private function GetGlobalPath: string; function GetJSScriptPath: string; function GetImagePath: string; protected function QualifyFileName(const AFileName: string): string; function GetStream(const AFileName: string; var AOwned: Boolean): TStream; end; function TcxWebDesignerFileManager.GetGlobalPath: string; begin Result := ExcludeTrailingPathDelimiter(ExtractFilePath(cxGetDesignerApplicationFileName)); if ExtractFileName(Result) = 'Lib' then Result := ExtractFilePath(Result) // old installation else Result := ExtractFilePath(ExcludeTrailingPathDelimiter(ExtractFilePath(Result))); // new installation end; function TcxWebDesignerFileManager.GetJSScriptPath: string; begin Result := GetGlobalPath + cxWebJScriptDTPath + PathDelim; end; function TcxWebDesignerFileManager.GetImagePath: string; begin Result := GetGlobalPath + cxWebImageDTPath + PathDelim; end; function TcxWebDesignerFileManager.QualifyFileName(const AFileName: string): string; var AExt: string; begin AExt := UpperCase(ExtractFileExt(AFileName)); if (AExt <> '') and (AExt[1] = '.') then AExt := Copy(AExt, 2, Length(AExt)); if AExt = 'JS' then Result := GetJSScriptPath + AFileName else if (AExt = 'GIF') or (AExt = 'JPEG') or (AExt = 'BMP') or (AExt = 'JPG') then Result := GetImagePath + AFileName else Result := AFileName; Result := StringReplace(Result, '\', '/', [rfReplaceAll, rfIgnoreCase]); end; function TcxWebDesignerFileManager.GetStream(const AFileName: string; var AOwned: Boolean): TStream; begin Result := nil; end; var FWebDesignerFileManager: TcxWebDesignerFileManager = nil; FWebDesignerFileManagerCount: Integer = 0; procedure AddDesignerFileManager; begin if FWebDesignerFileManager = nil then begin FWebDesignerFileManager := TcxWebDesignerFileManager.Create; DesignerFileManager := FWebDesignerFileManager; end; Inc(FWebDesignerFileManagerCount); end; procedure ReleaseDesignerFileManager; begin Dec(FWebDesignerFileManagerCount); if FWebDesignerFileManagerCount = 0 then begin FWebDesignerFileManager := nil; DesignerFileManager := nil; end; end; { TcxWebHTMLDesigner } constructor TcxWebHTMLDesigner.Create(AForm: TCustomForm; ADelphiDesigner: IDesigner); begin inherited Create(AForm, ADelphiDesigner); FHTMLSelectedControls := TList.Create; TcxWebFileCacheManager.Instance.IncDesignerCount; AddDesignerFileManager; FStoredTags := TcxEWFStoredTags.Create(Root); end; destructor TcxWebHTMLDesigner.Destroy; begin FStoredTags.Free; ReleaseDesignerFileManager; TcxWebFileCacheManager.Instance.DecDesignerCount; FHTMLSelectedControls.Free; inherited Destroy; end; procedure TcxWebHTMLDesigner.Modified; begin inherited; if FDesignerControl <> nil then FDesignerControl.Modified := True; end; procedure TcxWebHTMLDesigner.ComponentCreated(AnItem: TComponent); var AWebControl: IcxWebControl; begin if FHasHTMLLoaded and not (csLoading in AnItem.ComponentState) and Supports(AnItem, IcxWebControl, AWebControl) then begin InsertWebDesignControl(AWebControl); end; end; procedure TcxWebHTMLDesigner.ComponentDestroyed(AnItem: TComponent); var AHTMLElement: IHTMLElement; begin FStoredTags.Remove(AnItem); AHTMLElement := GetHTMLElementByComponentName(FDesignerControl.Document3, AnItem.Name); if AHTMLElement <> nil then AHTMLElement.outerHTML := ''; end; procedure TcxWebHTMLDesigner.ComponentChangedName(AComponent: TComponent; const AOldName, ANewName: string); var AHTMLElement: IHTMLElement; AWebControl: IcxWebControl; begin if not FHasHTMLLoaded then exit; AHTMLElement := GetHTMLElementByComponentName(FDesignerControl.Document3, AOldName); if AHTMLElement <> nil then AHTMLElement.id := ANewName else begin if FHasHTMLLoaded and Supports(AComponent, IcxWebControl, AWebControl) and (AOldName = '') and (ANewName <> '') and not FIsComponentInserting then begin BeginUpdate; try InternalInsertHTMLControl(AComponent, ANewName, True); finally CancelUpdate; end; end; end; end; procedure TcxWebHTMLDesigner.CreateDesignerControl(AParentControl: TWinControl); procedure CreateDesignerBar; begin try SendMessage(AParentControl.Handle, WM_SETREDRAW, Integer(False), 0); FDesignerBar := TcxHTMLEditorBar.Create(nil); FDesignerBar.Parent := AParentControl; FDesignerBar.Align := alTop; FDesignerBar.CreateActionControls; FDesignerControl.Bar := FDesignerBar; finally SendMessage(AParentControl.Handle, WM_SETREDRAW, Integer(True), 0); RedrawWindow(AParentControl.Handle, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN); AParentControl.Update; end; end; begin FDesignerControl := TcxWebHTMLControl.Create(nil); FDesignerControl.FDesigner := self; FDesignerControl.Parent := AParentControl; FDesignerControl.RegisterEWFTags; FDesignerControl.Align := alClient; FDesignerControl.SetDesigning(False); FDesignerControl.OnControlInsert := DHTMLControlInsertControl; FDesignerControl.OnControlInserted := DHTMLControlInsertedControl; FDesignerControl.OnSelectionChanged := DHTMLControlSelectionChanged; FDesignerControl.OnMoved := DHTMLControlMoved; FDesignerControl.OnResized := DHTMLControlResized; FDesignerControl.OnShowContextMenu := DHTMLControlContextMenu; FDesignerControl.OnParentChanged := DHTMLControlParentChanged; CreateDesignerBar; if GetParentForm(AParentControl) <> nil then GetParentForm(AParentControl).ActiveControl := FDesignerControl; FUndo := TcxWebHTMLUndo.Create(self); FHTMLSource := TcxWebDsgnHTMLSource.Create('', DelphiDesigner); FHTMLSourceEditorNotifier := TcxWebHTMLSourceEditorNotifier.Create(self); if FHTMLSource.SourceEditor <> nil then FHTMLSourceEditorNotifier.ID := FHTMLSource.SourceEditor.AddNotifier(FHTMLSourceEditorNotifier); FEditorManagerListener := TcxWebDelphiEditorManagerListener.Create; FEditorManagerListener.OnEditorFormActivated := DoEditorFormActivated; FFormNotifier := TcxWebFormNotifier.Create(self); FIDeNotifier := TcxWebIDENotifier.Create(self); AddBaseHTMLElementCreatorNotify(self); end; procedure TcxWebHTMLDesigner.DestroyDesignerControl; begin FDesignerControl.OnDocumentChange := nil; RemoveBaseHTMLElementCreatorNotify(self); FHasHTMLLoaded := False; FEditorManagerListener.Free; if FHTMLSource.SourceEditor <> nil then FHTMLSource.SourceEditor.RemoveNotifier(FHTMLSourceEditorNotifier.ID); FHTMLSourceEditorNotifier := nil; FFormNotifier._Release; FFormNotifier := nil; FIDENotifier._Release; FIDENotifier := nil; FHTMLSource.Free; ClearHTMLSelectedControls; FUndo.Free; FDesignerControl.Bar := nil; FDesignerBar.Free; FDesignerBar := nil; FDesignerControl.Free; FDesignerControl := nil; end; function TcxWebHTMLDesigner.GetDesignerControl: TWinControl; begin Result := FDesignerControl; end; procedure TcxWebHTMLDesigner.RefreshDesignControls; begin FOldPositioningType := PositioningType; if FHTMLSource.SourceEditor <> nil then UpdateDesignerHTML(False); end; procedure TcxWebHTMLDesigner.UpdateDesignerControl; var AList: IInterfaceList; I: Integer; begin AList := CreateControlList; for I := 0 to AList.Count - 1 do begin InsertWebDesignControl(IcxWebControl(AList[I])); if (IcxWebControl(AList[I]).DesignerControl <> nil) and FHasHTMLLoaded then with IcxWebControl(AList[I]).DesignerControl do begin ParentChanged; BoundsChanged; UpdateContext; end; end; end; procedure TcxWebHTMLDesigner.DesignerIdChanging; begin if (Root <> nil) and not (csLoading in Root.ComponentState) then UpdateHTMLSource; end; procedure TcxWebHTMLDesigner.DesignerPositioningTypeChanged; procedure UpdatePositions(AAbsolute: Boolean); var AList: IInterfaceList; I: Integer; AStyle2: IHTMLStyle2; begin AList := TInterfaceList.Create; GetHTMLElements(FDesignerControl.Document, AList); for I := 0 to AList.Count - 1 do if Supports(IHTMLElement(AList[I]).style, IHTMLStyle2, AStyle2) then begin if AAbsolute then AStyle2.position := 'absolute' //TODO check the parent. else AStyle2.position := ''; end; if AAbsolute then UpdateWebDesignerControlsPosition(Root, FDesignerControl.Document); end; begin if not FHasHTMLLoaded then exit; FDesignerControl.Wait; if (FOldPositioningType = cxptFlow) then UpdatePositions(True) else if (PositioningType = cxptFlow) then UpdatePositions(False); FDesignerControl.SetupControl; FOldPositioningType := PositioningType; if PositioningType = cxptGrid then UpdateControlsPosition; FDesignerControl.UndoManager.Clear; FDesignerControl.Modified := True; end; class function TcxWebHTMLDesigner.GetID: Integer; begin Result := 2; end; class function TcxWebHTMLDesigner.GetName: String; begin Result := 'HTML' end; function TcxWebHTMLDesigner.CanAlign: Boolean; begin Result := PositioningType <> cxptFlow; end; function TcxWebHTMLDesigner.CanAlignToGrid: Boolean; begin Result := (PositioningType <> cxptFlow) and FDesignerControl.SnapToGrid; end; function TcxWebHTMLDesigner.CanCopy: Boolean; begin Result := (SelectionCount > 0) or FDesignerControl.Selection.CanCopy; end; function TcxWebHTMLDesigner.CanCut: Boolean; begin Result := CanCopy and CanDelete; end; function TcxWebHTMLDesigner.CanDelete: Boolean; begin Result := (inherited CanDelete); if not Result and (SelectionCount = 0) then Result := FDesignerControl.Selection.CanDelete; end; function TcxWebHTMLDesigner.CanPaste: Boolean; begin Result := (PositioningType <> cxptFlow) or FDesignerControl.Selection.CanPaste; end; function TcxWebHTMLDesigner.CanSelectAll: Boolean; begin Result := (Root.ComponentCount > 0); end; function TcxWebHTMLDesigner.CanTabOrder: Boolean; begin Result := True; end; function TcxWebHTMLDesigner.CanUndo: Boolean; begin Result := FUndo.CanUndo; end; procedure TcxWebHTMLDesigner.ScaleSelection(AScaleFactor: Integer); procedure ScaleControl(AControl: IHTMLElement); begin if not SameText((AControl as IHTMLElement2).currentStyle.position, 'static') then // Do not localize begin AControl.style.setAttribute('width', MulDiv(AControl.offsetWidth, AScaleFactor, 100), 0); AControl.style.setAttribute('height', MulDiv(AControl.offsetHeight, AScaleFactor, 100), 0); end; end; var I: Integer; begin with FDesignerControl do begin UndoManager.BeginUndoBlock(scxCmdResize); try for I := 0 to Selection.Count - 1 do ScaleControl(Selection[I]); DHTMLControlBoundsRectChanged(False); finally UndoManager.EndUndoBlock; end; end; end; function HorzCompareFunc(AItem1, AItem2: Pointer): Integer; begin Result := IHTMLElement(AItem1).offsetLeft - IHTMLElement(AItem2).offsetLeft; end; function VertCompareFunc(AItem1, AItem2: Pointer): Integer; begin Result := IHTMLElement(AItem1).offsetTop - IHTMLElement(AItem2).offsetTop; end; procedure TcxWebHTMLDesigner.AlignSelection(AHorzAlign: TcxHorzAlign; AVertAlign: TcxVertAlign); type PcxAlignData = ^TcxAlignData; TcxAlignData = record CenterWidth: Integer; CenterHeight: Integer; MostLeft: Integer; MostTop: Integer; ParentWidth: Integer; ParentHeight: Integer; HorzDistance: Integer; VertDistance: Integer; HorzSortedList: TList; VertSortedList: TList; end; var AlignData: TcxAlignData; Selections: IInterfaceList; procedure CalcAlignData; var I: Integer; Control: IHTMLElement; LeftMost, RightMost, TopMost, BottomMost: IHTMLElement; begin LeftMost := nil; RightMost := nil; TopMost := nil; BottomMost := nil; for I := 0 to FDesignerControl.Selection.Count - 1 do begin Control := FDesignerControl.Selection[I]; if not SameText((Control as IHTMLElement2).currentStyle.position, 'static') and // Do not localize ((Selections.Count = 0) or IsEqualElement(IHTMLElement(Selections[0]).offsetParent, Control.offsetParent)) then begin Selections.Add(Control); if (LeftMost = nil) or (LeftMost.offsetLeft > Control.offsetLeft) then LeftMost := Control; if (RightMost = nil) or (RightMost.offsetLeft + RightMost.offsetWidth < Control.offsetLeft + Control.offsetWidth) then RightMost := Control; if (TopMost = nil) or (TopMost.offsetTop > Control.offsetTop) then TopMost := Control; if (BottomMost = nil) or (BottomMost.offsetTop + BottomMost.offsetHeight < Control.offsetTop + Control.offsetHeight) then BottomMost := Control; end; end; with AlignData do begin Control := IHTMLElement(Selections[0]); if Control <> nil then begin CenterWidth := Control.offsetWidth; CenterHeight := Control.offsetHeight; Control := Control.offsetParent; end; if Control = nil then Control := FDesignerControl.Document.body; ParentWidth := (Control as IHTMLElement2).clientWidth; ParentHeight := (Control as IHTMLElement2).clientHeight; if Selections.Count > 1 then begin HorzDistance := (RightMost.offsetLeft - LeftMost.offsetLeft) div (Selections.Count - 1); VertDistance := (BottomMost.offsetTop - TopMost.offsetTop) div (Selections.Count - 1); end; HorzSortedList := nil; if AHorzAlign = haSpace then begin HorzSortedList := CloneList(Selections); HorzSortedList.Sort(HorzCompareFunc); end; VertSortedList := nil; if AVertAlign = vaSpace then begin VertSortedList := CloneList(Selections); VertSortedList.Sort(VertCompareFunc); end; MostLeft := LeftMost.offsetLeft; MostTop := TopMost.offsetTop; end; end; function IndexOfElement(AList: TList; AElement: IHTMLElement): Integer; var I: Integer; begin Result := -1; if (AList = nil) or (AElement = nil) then Exit; for I := 0 to AList.Count - 1 do if IsEqualElement(IHTMLElement(AList.Items[I]), AElement) then begin Result := I; break; end; end; procedure PlaceControls; var I, Index: Integer; Control, FirstControl: IHTMLElement; ALeft, ATop, AWidth, AHeight: Integer; begin FirstControl := IHTMLElement(Selections[0]); with AlignData do for I := 0 to Selections.Count - 1 do begin Control := IHTMLElement(Selections[I]); ALeft := Control.offsetLeft; ATop := Control.offsetTop; AWidth := Control.offsetWidth; AHeight := Control.offsetHeight; case AHorzAlign of cxWebDsgnTypes.haLeft: ALeft := FirstControl.offsetLeft; cxWebDsgnTypes.haCenter: ALeft := FirstControl.offsetLeft - (AWidth - CenterWidth) div 2; cxWebDsgnTypes.haRight: ALeft := (FirstControl.offsetLeft + FirstControl.offsetWidth) - AWidth; cxWebDsgnTypes.haSpace: begin Index := IndexOfElement(HorzSortedList, Control); if (Index > 0) and (Index <> HorzSortedList.Count - 1) then ALeft := MostLeft + HorzDistance * Index; end; cxWebDsgnTypes.haWinCenter: ALeft := (ParentWidth - AWidth) div 2; end; case AVertAlign of cxWebDsgnTypes.vaTop: ATop := FirstControl.offsetTop; cxWebDsgnTypes.vaCenter: ATop := FirstControl.offsetTop - (AHeight - CenterHeight) div 2; cxWebDsgnTypes.vaBottom: ATop := (FirstControl.offsetTop + FirstControl.offsetHeight) - AHeight; cxWebDsgnTypes.vaSpace: begin Index := IndexOfElement(VertSortedList, Control); if (Index > 0) and (Index <> VertSortedList.Count - 1) then ATop := MostTop + VertDistance * Index; end; cxWebDsgnTypes.vaWinCenter: ATop := (ParentHeight - AHeight) div 2; end; Control.style.setAttribute('left', ALeft, 0); Control.style.setAttribute('top', ATop, 0); end; end; procedure FreeList(AList: TList); var I: Integer; begin if AList = nil then Exit; for I := 0 to AList.Count - 1 do IInterface(AList[I])._Release; AList.Free; end; begin if PositioningType = cxptFlow then Exit; if (AHorzAlign = haNothing) and (AVertAlign = vaNothing) or (SelectionCount = 0) then Exit; with FDesignerControl do begin UndoManager.BeginUndoBlock(scxCmdAlign); Selections := TInterfaceList.Create; FillChar(AlignData, SizeOf(TcxAlignData), 0); try CalcAlignData; PlaceControls; DHTMLControlBoundsRectChanged(True); finally FreeList(AlignData.HorzSortedList); FreeList(AlignData.VertSortedList); UndoManager.EndUndoBlock; end; end; end; procedure TcxWebHTMLDesigner.SizeSelection(AHorzAffect: TcxSizeAffect; AVertAffect: TcxSizeAffect; AHorzAbsolute: Integer; AVertAbsolute: Integer); var I, AWidth, AHeight: Integer; ACurrentWidth, ACurrentHeight: Integer; begin if (AHorzAffect = saNothing) and (AVertAffect = saNothing) then Exit; with FDesignerControl do begin UndoManager.BeginUndoBlock(scxCmdResize); try AWidth := -1; AHeight := -1; if (AHorzAffect in [saShrink, saGrow]) or (AVertAffect in [saShrink, saGrow]) then for I := 0 to Selection.Count - 1 do begin if AHorzAffect in [saGrow, saShrink] then begin ACurrentWidth := Selection[I].offsetWidth; case AHorzAffect of saShrink: if (ACurrentWidth < AWidth) or (AWidth = -1) then AWidth := ACurrentWidth; saGrow: if ACurrentWidth > AWidth then AWidth := ACurrentWidth; end; end; if AVertAffect in [saGrow, saShrink] then begin ACurrentHeight := Selection[I].offsetHeight; case AVertAffect of saShrink: if (ACurrentHeight < AHeight) or (AHeight = -1) then AHeight := ACurrentHeight; saGrow: if ACurrentHeight > AHeight then AHeight := ACurrentHeight; end; end; end; if AHorzAffect = saAbsolute then AWidth := AHorzAbsolute; if AVertAffect = saAbsolute then AHeight := AVertAbsolute; for I := 0 to Selection.Count - 1 do begin Selection[I].style.setAttribute('width', AWidth, 0); Selection[I].style.setAttribute('height', AHeight, 0); end; DHTMLControlBoundsRectChanged(False); finally UndoManager.EndUndoBlock; end; end; end; procedure TcxWebHTMLDesigner.AlignToGrid; procedure AlignControlToGrid(AControl: IHTMLElement); var C: Integer; begin if not SameText((AControl as IHTMLElement2).currentStyle.position, 'static') then // Do not localize begin C := AControl.offsetTop + GridSizeY div 2; AControl.style.setAttribute('top', GridSizeY * (C div GridSizeY), 0); C := AControl.offsetLeft + GridSizeX div 2; AControl.style.setAttribute('left', GridSizeX * (C div GridSizeX), 0); end; end; var I: Integer; begin with FDesignerControl do begin UndoManager.BeginUndoBlock(scxCmdAlign); try for I := 0 to Selection.Count - 1 do AlignControlToGrid(Selection[I]); DHTMLControlBoundsRectChanged(True); finally UndoManager.EndUndoBlock; end; end; end; function TcxWebHTMLDesigner.ShowTabOrderSelectionDlg: Boolean; var Container: IcxWebContainerControl; Data: TcxTabOrderDlgData; begin if FDesignerControl.Selection.Count = 1 then Supports(GetComponentByHTMLElement(Root, FDesignerControl.Selection[0]), IcxWebContainerControl, Container); if Container = nil then Supports(Root, IcxWebContainerControl, Container); if Container <> nil then begin FillChar(Data, SizeOf(TcxTabOrderDlgData), 0); Data.Controls := TList.Create; try Container.GetTabOrderList(Data.Controls); Result := cxShowTabOrderDialog(@Data); if Result then begin Container.SetTabOrderList(Data.Controls); DelphiDesigner.Modified; end; finally Data.Controls.Free; end; end else Result := False; end; function TcxWebHTMLDesigner.CanSelect(const AControl: TComponent): Boolean; begin Result := True; end; procedure TcxWebHTMLDesigner.CopySelection; begin if CanCopy then DoCopy; end; procedure TcxWebHTMLDesigner.CutSelection; begin if CanCut then begin CopySelection; DeleteSelection; end; end; procedure TcxWebHTMLDesigner.DeleteSelection; var I: Integer; AList: IDesignerSelections; AComponentList: TList; Element: IHTMLElement; begin if SelectionCount = 0 then begin FDesignerControl.Selection.Delete; exit; end; AList := TDesignerSelections.Create; DelphiDesigner.GetSelections(AList); if AList.Count > 0 then begin BeginUpdate; AComponentList := TList.Create; try for I := 0 to AList.Count - 1 do if AList[I] is TcxWebHTMLElementMapper then begin Element := TcxWebHTMLElementMapper(AList[I]).Element; if Element <> nil then (Element as IHTMLDOMNode).removeNode(True); end else if AList[I] is TComponent then AComponentList.Add(AList[I]); FreeComponents(AComponentList, False); DelphiDesigner.DeleteSelection(True); ClearSelection; finally AComponentList.Free; EndUpdate; end; end; end; procedure TcxWebHTMLDesigner.Edit(AComponent: TComponent); begin end; procedure TcxWebHTMLDesigner.GetSelections(const AList: TList); var ASelList: IDesignerSelections; I: Integer; begin ASelList := TDesignerSelections.Create; DelphiDesigner.GetSelections(ASelList); for I := 0 to ASelList.Count - 1 do AList.Add(ASelList.Items[I]); end; procedure TcxWebHTMLDesigner.PasteSelection; begin BeginUpdate; try DoPaste; finally EndUpdate; end; end; procedure TcxWebHTMLDesigner.SelectAll; begin if CanSelectAll then DoSelectAll; end; procedure TcxWebHTMLDesigner.SelectionChanged(const AList: TList); procedure RemoveHTMLSelectedControls; var I: Integer; begin I := 0; while I < FHTMLSelectedControls.Count do begin if AList.IndexOf(FHTMLSelectedControls[I]) > -1 then Inc(I) else begin TcxWebHTMLElementMapper(FHTMLSelectedControls[I]).Free; FHTMLSelectedControls.Delete(I); end; end; end; procedure RemoveElementsFromSelection; function HasElement(AElement: IHTMLElement): Boolean; var I: Integer; begin Result := False; for I := 0 to AList.Count - 1 do if (TPersistent(AList[I]) is TComponent) and Supports(TPersistent(AList[I]), IcxWebControl) then begin if AElement = GetHTMLElementByComponentName(FDesignerControl.Document3, TComponent(AList[I]).Name) then begin Result := True; break; end; end else if (TPersistent(AList[I]) is TcxWebHTMLElementMapper) and (AElement = TcxWebHTMLElementMapper(AList[I]).Element) then begin Result := True; break; end; end; var I: Integer; AElementList: IInterfaceList; AElement: IHTMLElement; begin AElementList := TInterfaceList.Create; for I := 0 to FDesignerControl.Selection.Count - 1 do begin AElement := FDesignerControl.Selection[I]; if (AElement <> nil) and not HasElement(AElement) then AElementList.Add(AElement); end; for I := 0 to AElementList.Count - 1 do FDesignerControl.Selection.Remove(AElementList[I] as IHTMLElement); end; procedure SelectWebControls; var I: Integer; AHTMLElement: IHTMLElement; begin for I := 0 to AList.Count - 1 do if (TPersistent(AList[I]) is TComponent) and Supports(TPersistent(AList[I]), IcxWebControl) then begin AHTMLElement := GetHTMLElementByComponentName(FDesignerControl.Document3, TComponent(AList[I]).Name); if AHTMLElement <> nil then FDesignerControl.Selection.Add(AHTMLElement); end; end; procedure SelectHTMLElements; var I: Integer; begin for I := 0 to FHTMLSelectedControls.Count - 1 do begin if Supports(TcxWebHTMLElementMapper(FHTMLSelectedControls[I]).Element, IHTMLControlElement) then FDesignerControl.Selection.Add(TcxWebHTMLElementMapper(FHTMLSelectedControls[I]).Element); end; end; begin if not FHasHTMLLoaded then exit; if UpdateCount <> 0 then exit; BeginUpdate; try FDesignerControl.Wait; RemoveHTMLSelectedControls; RemoveElementsFromSelection; SelectWebControls; SelectHTMLElements; finally CancelUpdate; end; end; procedure TcxWebHTMLDesigner.Undo; begin FUndo.Undo; end; procedure TcxWebHTMLDesigner.IDEDesignerOptionsChagned; begin FDesignerControl.SetupControl; end; function TcxWebHTMLDesigner.GetHTMLTemplateModified(var AFileName, AText: string): Boolean; begin if (FHTMLSource <> nil) and (FHTMLSource.FSourceEditor <> nil) and (FHTMLSource.FSourceEditor.Modified or not FileExists(FHTMLSource.FSourceEditor.FileName)) then begin Result := True; AFileName := FHTMLSource.FSourceEditor.FileName; AText := FHTMLSource.HTML; end else Result := inherited GetHTMLTemplateModified(AFileName, AText); end; procedure TcxWebHTMLDesigner.HTMLElementCreated(ABaseHTMLElement: TcxBaseHTMLElement); var R: TRect; begin FInsertingComponent := ABaseHTMLElement; try R.Left := FDesignerControl.ClientWidth div 2; R.Right := R.Left; R.Top := FDesignerControl.ClientHeight div 2; R.Bottom := R.Top; FDesignerControl.DoInsert(nil, R); finally FInsertingComponent := nil; end; end; function TcxWebHTMLDesigner.GetHTMLElementFileName: string; var AIntfFileName, AFormFileName: string; begin DelphiDesigner.ModuleFileNames(Result, AIntfFileName, AFormFileName); end; procedure TcxWebHTMLDesigner.ReaderSetName(AReader: TReader; AComponent: TComponent; var AName: string); var OldName: string; begin OldName := AName; inherited; if (FChangedNames <> nil) and not SameText(OldName, AName) then FChangedNames.AddObject(OldName, AComponent); end; procedure TcxWebHTMLDesigner.DesignerActivated; begin inherited DesignerActivated; if (FHTMLSourceEditorNotifier <> nil) and (FHTMLSourceEditorNotifier.IsModified) then begin try BeginUpdate; ShowDesignerPage; finally CancelUpdate; end; UpdateDesignerHTML(True); end; end; procedure TcxWebHTMLDesigner.DesignerDeactivated; begin FDesignerControl.DeactivateInnerHMTLElement; end; procedure TcxWebHTMLDesigner.PreviewShowing; begin FDesignerControl.DeactivateInnerHMTLElement; ClearSelection; UpdateHTMLSource; end; function TcxWebHTMLDesigner.GetControlPositioningType: TcxWebPositioningType; begin if GetShowingType = stDesigner then Result := cxptFlow else Result := PositioningType; end; {DHTML Control events} procedure TcxWebHTMLDesigner.DHTMLControlInsertControl(Sender: TObject; R: TRect; var HTML, InnerHTML: string; var ParentElement: IHTMLElement); var AComponentClass: TComponentClass; function GetHTMLControlContext: string; var IsAbsolutePos: Boolean; begin IsAbsolutePos := PositioningType <> cxptFlow; if IsAbsolutePos and Supports(GetComponentByHTMLElement(Root, ParentElement), IcxWebControl) then IsAbsolutePos := False; Result := TcxBaseHTMLElementClass(AComponentClass).GetHTML(IsAbsolutePos, R); InnerHTML := TcxBaseHTMLElementClass(AComponentClass).GetInnerHTML; end; function GetEWFControlContext: string; var AComponent: TComponent; AWebControl: IcxWebControl; AContainerControl: IcxWebContainerControl; AParentComponent: TComponent; AComponentName: string; begin if FInsertingComponent = nil then begin AComponent := CreateComponent(R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top); if Supports(AComponent, IcxWebControl, AWebControl) and ((R.Right = R.Left) or (R.Bottom = R.Top)) then with AWebControl.BoundsRect do begin R.Right := R.Left + Right - Left; R.Bottom := R.Top + Bottom - Top; end; end else AComponent := FInsertingComponent; if AComponent <> nil then begin if Supports(AComponent, IcxWebControl, AWebControl) then begin AWebControl.BoundsRect := R; if (ParentElement <> nil) then AParentComponent := GetComponentByHTMLElement(Root, ParentElement) else AParentComponent := nil; if AParentComponent = nil then AParentComponent := Root; if not Supports(AParentComponent, IcxWebContainerControl, AContainerControl) then begin if Supports(AParentComponent, IcxWebControl, AWebControl) then AContainerControl := AWebControl.Parent; end; if AContainerControl = nil then Supports(Root, IcxWebContainerControl, AContainerControl); AWebControl.Parent := AContainerControl; end; if AComponent.Name <> '' then AComponentName := AComponent.Name else AComponentName := FInsertingComponentName; Result := Format('<%0:s:%1:s id="%2:s"> ', [scxEWF, GetDesignerHTMLClassName(AComponent.ClassName), AComponentName]); InnerHTML := ''; end; end; begin FDesignerControl.Wait; if FInsertingComponent = nil then AComponentClass := GetCurrComponentClass else AComponentClass := TComponentClass(FInsertingComponent.ClassType); if not AComponentClass.InheritsFrom(TcxBaseHTMLElement) and not Supports(AComponentClass, IcxWebControl) then begin CreateComponent(R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top); end else begin if (ParentElement = nil) or not IsHTMLCustomElement(ParentElement) then ParentElement := FDesignerControl.MainParentElement; try FIsComponentInserting := True; if AComponentClass.InheritsFrom(TcxBaseHTMLElement) then HTML := GetHTMLControlContext else begin HTML := GetEWFControlContext; end; finally FIsComponentInserting := False; end; end; end; procedure TcxWebHTMLDesigner.DHTMLControlInsertedControl(Sender: TObject; Element: IHTMLElement); var AComponentClass: TComponentClass; AComponent: TComponent; AStyle2: IHTMLStyle2; AWebControl: IcxWebControl; begin if Element = nil then exit; if FInsertingComponent <> nil then AComponent := FInsertingComponent else AComponent := GetComponentByHTMLElement(Root, Element); if (AComponent <> nil) and Supports(AComponent, IcxWebControl, AWebControl) then begin if PositioningType <> cxptFlow then begin if (GetComponentByInterface(AWebControl.Parent) = Root) and Supports(Element.style, IHTMLStyle2, AStyle2) then AStyle2.position := 'absolute'; end; if (AWebControl.DesignerControl <> nil) then AWebControl.DesignerControl.BoundsChanged; end; if FInsertingComponent = nil then AComponentClass := GetCurrComponentClass else AComponentClass := TComponentClass(FInsertingComponent.ClassType); if (AComponentClass <> nil) and AComponentClass.InheritsFrom(TcxBaseHTMLElement) then TcxBaseHTMLElementClass(AComponentClass).AfterElementCreated(Element); if (AComponentClass <> nil) and (ActiveDesigner.Environment <> nil) then ActiveDesigner.Environment.ResetCompClass; FDesignerControl.Selection.Clear; FDesignerControl.Selection.Add(Element); InternalDHTMLControlSelectionChanged; end; procedure TcxWebHTMLDesigner.DHTMLControlGetContext(AElement: IHTMLElement; var AContent, ACSSContent, AInlineStyle: string); var AComponent: TComponent; AWebControl: IcxWebControl; begin AComponent := GetComponentByHTMLElement(Root, AElement); if (AComponent <> nil) and Supports(AComponent, IcxWebControl, AWebControl) then begin InsertWebDesignControl(AWebControl); AContent := GetDesignInnerHTMLByWebControl(AComponent); ACSSContent := GetDesignStyleByWebControl(AComponent); AInlineStyle := GetDesignInlineStyleByWebControl(AComponent); end else begin AContent := ''; ACSSContent := ''; AInlineStyle := ''; end; end; procedure TcxWebHTMLDesigner.DHTMLControlParentChanged(ASender: TObject; ANewParent: IHTMLElement); var AParentControl: IcxWebContainerControl; AParentComponent, AComponent: TComponent; AWebControl: IcxWebControl; I: Integer; AElements: IInterfaceList; begin AParentComponent := nil; while (AParentComponent = nil) and (ANewParent <> nil) do begin AParentComponent := GetComponentByHTMLElement(Root, ANewParent); ANewParent := ANewParent.parentElement; end; AParentControl := nil; if (AParentComponent <> nil) and Supports(AParentComponent, IcxWebControl, AWebControl) then if not Supports(AParentComponent, IcxWebContainerControl, AParentControl) then AParentControl := AWebControl.Parent; if AParentControl = nil then AParentControl := Root as IcxWebContainerControl; AElements := TInterfaceList.Create; for I := 0 to FDesignerControl.Selection.Count - 1 do if FDesignerControl.Selection[I] <> nil then AElements.Add(FDesignerControl.Selection[I]); for I := 0 to AElements.Count - 1 do begin AComponent := GetComponentByHTMLElement(Root, AElements[I] as IHTMLElement); if (PositioningType = cxptFlow) or Supports(AParentControl, IcxWebControl) then (AElements[I] as IHTMLElement).style.removeAttribute('position', 0) else (AElements[I] as IHTMLElement).style.setAttribute('position', 'absolute', 0); if Supports(AComponent, IcxWebControl) then (AComponent as IcxWebControl).Parent := AParentControl; end; end; procedure TcxWebHTMLDesigner.ClearHTMLSelectedControls; var I: Integer; begin if FHTMLSelectedControls.Count = 0 then exit; for I := 0 to FHTMLSelectedControls.Count - 1 do TcxWebHTMLElementMapper(FHTMLSelectedControls[I]).Free; FHTMLSelectedControls.Clear; end; procedure TcxWebHTMLDesigner.ClearSelection; var AList: IDesignerSelections; begin BeginUpdate; try AList := TDesignerSelections.Create; AList.Add(Root); DelphiDesigner.SetSelections(AList); ClearHTMLSelectedControls; FDesignerControl.Selection.Clear; finally EndUpdate; end; end; procedure TcxWebHTMLDesigner.CreateHTMLSelectedControls; var I: Integer; AList: IDesignerSelections; AHTMLElement: IHTMLElement; AComponent: TComponent; begin FDesignerControl.Wait; AList := TDesignerSelections.Create; for I := 0 to FDesignerControl.Selection.Count - 1 do begin AHTMLElement := FDesignerControl.Selection[I]; AComponent := GetComponentByHTMLElement(Root, AHTMLElement); if (AComponent = nil) and (AHTMLElement <> nil) then begin FHTMLSelectedControls.Add(WebHTMLElementMapperManager.CreateElementMapper(AHTMLElement)); TcxWebHTMLElementMapper(FHTMLSelectedControls.Last).onElementChanged := DoMapperElementChanged; AList.Add(TPersistent(FHTMLSelectedControls.Last)); end else AList.Add(AComponent); end; if AList.Count > 0 then DelphiDesigner.NoSelection; DelphiDesigner.SetSelections(AList); end; function TcxWebHTMLDesigner.SelectionCount: Integer; var AList: IDesignerSelections; begin AList := TDesignerSelections.Create; DelphiDesigner.GetSelections(AList); if (AList.Count = 1) and (AList.Items[0] = Root) then Result := 0 else Result := AList.Count; end; function TcxWebHTMLDesigner.IsComponentSelected: Boolean; var AList: IDesignerSelections; I: Integer; begin Result := False; AList := TDesignerSelections.Create; DelphiDesigner.GetSelections(AList); for I := 0 to AList.Count - 1 do if (AList.Items[I] is TComponent) and not Supports(AList.Items[I], IcxWebControl) then begin Result := True; break; end end; procedure TcxWebHTMLDesigner.InternalDHTMLControlSelectionChanged; function IsSelectionTheSame: Boolean; var I: Integer; AList: IDesignerSelections; AHTMLElement: IHTMLElement; begin AList := TDesignerSelections.Create; DelphiDesigner.GetSelections(AList); Result := FDesignerControl.Selection.Count = AList.Count; if (FDesignerControl.Selection.Count = 0) and (AList.Count = 1) and (AList.Items[0] = Root) then begin Result := True; exit; end; if not Result then exit; if (FDesignerControl.Selection.Count = 1) and (FDesignerControl.Selection[0] = nil) and (AList.Items[0] = Root) then exit; for I := 0 to AList.Count - 1 do begin if (AList.Items[I] is TcxWebHTMLElementMapper) then AHTMLElement := TcxWebHTMLElementMapper(AList.Items[I]).Element else if (AList.Items[I] is TComponent) then AHTMLElement := GetHTMLElementByComponentName(FDesignerControl.Document3, TComponent(AList.Items[I]).Name) else AHTMLElement := nil; Result := FDesignerControl.Selection.IndexOf(AHTMLElement) <> -1; if not Result then break; end; end; begin if (UpdateCount <> 0) or IsSelectionTheSame then exit; BeginUpdate; try ClearHTMLSelectedControls; CreateHTMLSelectedControls; finally CancelUpdate; end; end; procedure TcxWebHTMLDesigner.DHTMLControlSelectionChanged(Sender: TObject); begin InternalDHTMLControlSelectionChanged; end; procedure TcxWebHTMLDesigner.DHTMLControlMoved(Sender: TObject); begin DHTMLControlBoundsRectChanged(True); end; procedure TcxWebHTMLDesigner.DHTMLControlResized(Sender: TObject); begin DHTMLControlBoundsRectChanged(False); end; procedure TcxWebHTMLDesigner.DoEditorFormActivated(Sender: TObject); begin if not FHasHTMLLoaded then exit; UpdateHTMLSource; end; procedure TcxWebHTMLDesigner.DHTMLControlContextMenu(ASender: TObject; APos: TPoint; AElement: IHTMLElement); const Filters: array[Boolean] of TcxLocalMenuFilters = ([], [cxlmComponent]); var AList: IDesignerSelections; begin AList := TDesignerSelections.Create; DelphiDesigner.GetSelections(AList); InvokeLocalMenuAtCursor(nil, Filters[AList.Count > 1]); end; procedure TcxWebHTMLDesigner.UpdateHTMLSource; begin try FHTMLSaving := True; FDesignerControl.Wait; if FDesignerControl.Modified then begin FHTMLSource.HTML := FHTMLBefore + GetTempateHTMLFromDesigner(Root, FDesignerControl.DocumentHTML, FStoredTags, PositioningType) + FHTMLAfter; FHTMLSourceEditorNotifier.IsModified := False; FDesignerControl.Modified := False; end; finally FHTMLSaving := False; end; end; var FUpdateDesignerFailed: Boolean = False; procedure TcxWebHTMLDesigner.UpdateDesignerHTML(ADeleteComponents: Boolean); function PrepareHTMLSource(const AHTML: string): string; var P: PChar; begin Result := AHTML; P := SearchBuf(PChar(Result), Length(Result), 0, 0, ' nil then begin SetString(FHTMLBefore, PChar(Result), P - PChar(Result)); SetString(Result, P, PChar(Result) + Length(Result) - P); end else FHTMLBefore := ''; P := SearchBuf(PChar(Result), Length(Result), Length(Result), 0, '', []); if P <> nil then begin P := P + 7; SetString(FHTMLAfter, P, PChar(Result) + Length(Result) - P); SetString(Result, PChar(Result), P - PChar(Result)); end else FHTMLAfter := ''; end; begin if FUpdateDesignerFailed then begin FUpdateDesignerFailed := False; FHTMLSource.SourceEditor.Show; exit; end; FUpdateDesignerFailed := True; FDesignerControl.OnDocumentChange := nil; if FHasHTMLLoaded then ClearSelection; FUndo.Clear; try FHasHTMLLoaded := False; FDesignerControl.DocumentHTML := GetDesignHTMLContextFromTemplate(Root, PrepareHTMLSource(FHTMLSource.HTML), FStoredTags, ADeleteComponents, PositioningType); FUpdateDesignerFailed := False; except on E: EcxWebHTMLParserException do FHTMLSource.ShowParserError(E.ParserError, E.ErrorStr, E.Message); else raise; end; if FHTMLSource.FSourceEditor <> nil then FDesignerControl.BaseURL := ExtractFilePath(FHTMLSource.FSourceEditor.FileName); IDEDesignerOptionsChagned; FDesignerControl.OnDocumentChange := HTMLChangeNotify; FHasHTMLLoaded := True; FHTMLSourceEditorNotifier.IsModified := False; end; procedure TcxWebHTMLDesigner.UpdateHTMLControlContext(AComponent: TComponent); var AHTMLElement: IHTMLElement; begin if not FHasHTMLLoaded or Supports(AComponent, IcxWebContainerControl) then exit; AHTMLElement := GetHTMLElementByComponentName(FDesignerControl.Document3, AComponent.Name); if AHTMLElement <> nil then FDesignerControl.UpdateViewLinkContent(AHTMLElement); end; procedure TcxWebHTMLDesigner.InsertWebDesignControl(AWebControl: IcxWebControl); begin if AWebControl.DesignerControl = nil then AWebControl.DesignerControl := TcxWebDesignerControlNotify.Create(self, GetComponentByInterface(AWebControl)); end; procedure TcxWebHTMLDesigner.DHTMLControlBoundsRectChanged(AIsMoved: Boolean); var I: Integer; AComponent: TComponent; AWebControl: IcxWebControl; AHTMLElement: IHTMLElement; R: TRect; ASelectedList: IInterfaceList; begin ASelectedList := TInterfaceList.Create; for I := 0 to FDesignerControl.Selection.Count - 1 do if FDesignerControl.Selection[I] <> nil then ASelectedList.Add(FDesignerControl.Selection[I]); BeginUpdate; try for I := 0 to ASelectedList.Count - 1 do begin AHTMLElement := ASelectedList[I] as IHTMLElement; AComponent := GetComponentByHTMLElement(Root, AHTMLElement); if (AComponent <> nil) and Supports(AComponent, IcxWebControl, AWebControl) then begin with AHTMLElement.style do if not AIsMoved then SetRect(R, pixelLeft, pixelTop, pixelLeft + pixelWidth, pixelTop + pixelHeight) else begin R := AWebControl.BoundsRect; OffsetRect(R, pixelLeft - R.Left, pixelTop - R.Top); end; AWebControl.BoundsRect := R; end; end; finally CancelUpdate; end; for I := 0 to ASelectedList.Count - 1 do begin AHTMLElement := ASelectedList[I] as IHTMLElement; AComponent := GetComponentByHTMLElement(Root, AHTMLElement); if (AComponent <> nil) and Supports(AComponent, IcxWebControl, AWebControl) then begin AWebControl.UpdateControlPosition; UpdateHTMLElementStylePositions(AHTMLElement, AWebControl); end; end; DelphiDesigner.Modified; BeginUpdate; try FDesignerControl.Selection.Empty; for I := 0 to ASelectedList.Count - 1 do if FDesignerControl.Selection.IndexOf(ASelectedList[I] as IHTMLElement) < 0 then FDesignerControl.Selection.Add(ASelectedList[I] as IHTMLElement); finally CancelUpdate; end; end; procedure TcxWebHTMLDesigner.DoMapperElementChanged(Sender: TObject); begin if (BorlandIDEServices as IOTAModuleServices).CurrentModule <> nil then (BorlandIDEServices as IOTAModuleServices).CurrentModule.MarkModified; BeginUpdate; DelphiDesigner.Modified; EndUpdate; end; procedure TcxWebHTMLDesigner.HTMLChangeNotify(Sender: TObject); begin if (BorlandIDEServices as IOTAModuleServices).CurrentModule <> nil then (BorlandIDEServices as IOTAModuleServices).CurrentModule.MarkModified; BeginUpdate; DelphiDesigner.Modified; EndUpdate; CheckForDeletedControls; UpdateSelectedEWFBehaviors; end; procedure TcxWebHTMLDesigner.FreeComponents(AList: TList; ADelete: Boolean); function HasParentInList(AComponent: TComponent): Boolean; var I: Integer; AParent: TComponent; begin Result := False; AParent := AComponent.GetParentComponent; while AParent <> nil do begin for I := 0 to AList.Count - 1 do begin if AParent = TComponent(AList[I]) then begin Result := True; break; end; end; AParent := AParent.GetParentComponent; end; end; procedure RemoveChildren; var I: Integer; AChildren: TList; begin AChildren := TList.Create; try for I := 0 to AList.Count - 1 do if HasParentInList(TComponent(AList[I])) then AChildren.Add(AList[I]); for I := 0 to AChildren.Count - 1 do AList.Remove(AChildren[I]); finally AChildren.Free; end; end; var I: Integer; AItems: TcxWebHTMLUndoItems; AComponent: TComponent; begin RemoveChildren; if AList.Count > 0 then begin AItems := FUndo.CreateNewGroup; for I := 0 to AList.Count - 1 do begin AComponent := TComponent(AList[I]); FUndo.AddDeletedComponent(AItems, AComponent); if ADelete or Supports(AComponent, IcxWebControl) then FreeAndNil(AComponent); end; end; end; procedure TcxWebHTMLDesigner.CheckForDeletedControls; var AList: TList; I: Integer; begin AList := TList.Create; for I := 0 to Root.ComponentCount - 1 do if Supports(Root.Components[I], IcxWebControl) and (GetHTMLElementByComponentName(FDesignerControl.Document3, Root.Components[I].Name) = nil) then AList.Add(Root.Components[I]); FreeComponents(AList, True); if AList.Count > 0 then try BeginUpdate; FDesignerControl.Selection.Clear; finally CancelUpdate; end; AList.Free; end; procedure TcxWebHTMLDesigner.UpdateSelectedEWFBehaviors; var AList: IDesignerSelections; AElement: IHTMLElement; I: Integer; begin AList := TDesignerSelections.Create; DelphiDesigner.GetSelections(AList); for I := 0 to AList.Count - 1 do if Supports(AList.Items[I], IcxWebControl) and (AList.Items[I] is TComponent) then AElement := GetHTMLElementByComponentName(FDesignerCOntrol.Document3, TComponent(AList.Items[I]).Name); end; procedure TcxWebHTMLDesigner.InternalInsertHTMLControl(AComponent: TComponent; ANewName: string; ANeedOffSet: Boolean); var AWebControl: IcxWebControl; AParentElement: IHTMLElement; R: TRect; AParent: TComponent; begin if not Supports(AComponent, IcxWebControl, AWebControl) then exit; try if ANewName = '' then ANewName := AComponent.Name; FInsertingComponentName := ANewName; FInsertingComponent := AComponent; AParentElement := nil; AParent := AComponent.GetParentComponent; if AParent <> nil then AParentElement := GetHTMLElementByComponentName(FDesignerCOntrol.Document3, AParent.Name); if AParentElement = nil then AParentElement := FDesignerControl.MainParentElement; R := AWebControl.BoundsRect; if ANeedOffSet then OffsetRect(R, (FDesignerControl.ClientWidth + R.Left - R.Right) div 2, (FDesignerControl.ClientHeight + R.Top - R.Bottom) div 2); FDesignerControl.DoInsert(AParentElement, R); finally FInsertingComponent := nil; FInsertingComponentName := ''; end; end; { Clipboard routines } procedure EWFCopyStreamToClipboard(AComponentStream: TMemoryStream; const AHTML: string); procedure CopyToClipboard(Format: Word; AStream: TMemoryStream); var Handle: THandle; Mem: Pointer; begin Handle := GlobalAlloc(GMEM_MOVEABLE, AStream.Size); Mem := GlobalLock(Handle); Move(AStream.Memory^, Mem^, AStream.Size); GlobalUnlock(Handle); Clipboard.SetAsHandle(Format, Handle); end; var AStream: TMemoryStream; I: TValueType; V: Integer; begin Clipboard.Open; try AStream := TMemoryStream.Create; try if AComponentStream <> nil then begin AComponentStream.Position := 0; repeat AComponentStream.Read(I, SizeOf(I)); AComponentStream.Seek(-SizeOf(I), 1); if I = vaNull then Break; ObjectBinaryToText(AComponentStream, AStream); until False; end; if AHTML <> '' then AStream.WriteBuffer(PChar(AHTML)^, Length(AHTML)); V := 0; AStream.Write(V, 1); CopyToClipboard(CF_TEXT, AStream); finally AStream.Free; end; finally Clipboard.Close; end; end; function EWFGetClipboardStream(var AHTML: string): TMemoryStream; var S, T: TMemoryStream; Handle: THandle; Mem: Pointer; Format: Word; V: TValueType; ALength: Integer; function AnotherObject(S: TStream): Boolean; var Buffer: array[0..255] of Char; Position: Integer; begin Position := S.Position; Buffer[S.Read(Buffer, SizeOf(Buffer))-1] := #0; S.Position := Position; Result := PossibleStream(Buffer); end; begin AHTML := ''; Result := TMemoryStream.Create; try if Clipboard.HasFormat(CF_COMPONENTS) then Format := CF_COMPONENTS else Format := CF_TEXT; Clipboard.Open; try Handle := Clipboard.GetAsHandle(Format); Mem := GlobalLock(Handle); try Result.Write(Mem^, GlobalSize(Handle)); finally GlobalUnlock(Handle); end; finally Clipboard.Close; end; Result.Position := 0; if Format = CF_TEXT then begin S := TMemoryStream.Create; try while AnotherObject(Result) do ObjectTextToBinary(Result, S); if Result.Position < Result.Size - 1 then begin ALength := Result.Size - Result.Position; SetLength(AHTML, ALength); Result.ReadBuffer(AHTML[1], ALength); end; V := vaNull; S.Write(V, SizeOf(V)); T := Result; Result := nil; T.Free; except S.Free; raise; end; Result := S; Result.Position := 0; end; except Result.Free; raise; end; end; const EWFHTMLSign = 'EWF_HTML'; procedure TcxWebHTMLDesigner.ReadComponent(AComponent: TComponent); begin FClipboardSelection.Add(AComponent); end; procedure TcxWebHTMLDesigner.DoPaste; var SaveElements: IInterfaceList; procedure UpdateSelection; var I: Integer; AList: IDesignerSelections; AWebControl: IcxWebControl; begin if FClipboardSelection.Count = 0 then exit; AList := TDesignerSelections.Create; for I := 0 to FClipboardSelection.Count - 1 do begin AList.Add(TComponent(FClipboardSelection[I])); if Supports(TComponent(FClipboardSelection[I]), IcxWebControl, AWebControl) and (AWebControl.DesignerControl <> nil) then AWebControl.DesignerControl.UpdateContext; FDesignerControl.Selection.Add(GetHTMLElementByComponentName(FDesignerControl.Document3, TComponent(FClipboardSelection[I]).Name)); end; DelphiDesigner.SetSelections(AList); end; function FindNewElementByName(const AName: string): IHTMLElement; function IsElementInList(AList: IInterfaceList; AElement: IHTMLElement): Boolean; var I: Integer; begin Result := False; for I := 0 to AList.Count - 1 do if IsEqualElement(AList[I] as IHTMLElement, AElement) then begin Result := True; break; end; end; var Elements: IHTMLElementCollection; I: Integer; begin Result := nil; Elements := FDesignerControl.Document3.getElementsByName(AName); for I := 0 to Elements.length - 1 do if not IsElementInList(SaveElements, Elements.item(I, 0) as IHTMLElement) then begin Result := Elements.item(I, 0) as IHTMLElement; break; end; end; procedure CreateDesignerControls; var I, Idx: Integer; AList: IInterfaceList; AFoundElement: IHTMLElement; ASelectedList: IDesignerSelections; AComponent: TComponent; AParentControl: IcxWebContainerControl; begin AParentControl := nil; if (SelectionCount > 0) then begin ASelectedList := TDesignerSelections.Create; DelphiDesigner.GetSelections(ASelectedList); if (ASelectedList.Count = 1) and (ASelectedList.Items[0] <> Root) then Supports(ASelectedList.Items[0], IcxWebContainerControl, AParentControl); end; AList := CreateControlList; for I := 0 to AList.Count - 1 do begin AComponent := GetComponentByInterface(AList[I]); if (AComponent <> nil) and (GetHTMLElementByComponentName(FDesignerControl.Document3, AComponent.Name) = nil) then begin Idx := FChangedNames.IndexOfObject(AComponent); if Idx >= 0 then begin AFoundElement := FindNewElementByName(FChangedNames.Strings[Idx]); if AFoundElement <> nil then AFoundElement.id := AComponent.Name else InternalInsertHTMLControl(AComponent, '', False); FChangedNames.Delete(Idx); end else InternalInsertHTMLControl(AComponent, '', False); if AParentControl <> nil then IcxWebControl(AList[I]).Parent := AParentControl; end; end; end; var AStream: TStream; AReader: TReader; AHTML: string; ParentElement, Element: IHTMLElement; I: Integer; begin AStream := EWFGetClipboardStream(AHTML); SaveElements := TInterfaceList.Create; for I := 0 to Root.ComponentCount - 1 do begin Element := GetHTMLElementByComponentName(FDesignerControl.Document3, Root.Components[I].Name); if Element <> nil then SaveElements.Add(Element); end; if AHTML <> '' then begin if Pos(EWFHTMLSign, AHTML) = 1 then begin AHTML := Copy(AHTML, Length(EWFHTMLSign) + 1, Length(AHTML)); ParentElement := nil; if (FDesignerControl.Selection.Count = 1) and Supports(FDesignerControl.Selection[0], IHTMLTextContainer) then ParentElement := FDesignerControl.Selection[0]; if ParentElement = nil then ParentElement := FDesignerControl.MainParentElement; ParentElement.insertAdjacentHTML('beforeEnd', AHTML); end else FDesignerControl.Selection.Paste; FDesignerControl.Wait; end; if AStream.Size > 1 then try AReader := TReader.Create(AStream, 1024); FClipboardSelection := TList.Create; try FChangedNames := TStringList.Create; AReader.OnSetName := ReaderSetName; AReader.OnFindMethod := ReaderFindMethod; AReader.Parent := Root; AReader.ReadComponents(Root, Root, ReadComponent); CreateDesignerControls; if PositioningType = cxptGrid then UpdateControlsPosition; UpdateSelection; finally AReader.Free; FClipboardSelection.Free; FreeAndNil(FChangedNames); end; finally AStream.Free; end; end; procedure TcxWebHTMLDesigner.DoCopy; var AStream: TMemoryStream; AHTML: String; procedure WriteSelectedComponents; var AList: IDesignerSelections; function IsNotChild(AComponent: TComponent): Boolean; var I: Integer; begin Result := True; for I := 0 to AList.Count - 1 do if (AList.Items[I] is TComponent) and (AList.Items[I] <> AComponent) and (TComponent(AList.Items[I]) = AComponent.GetParentComponent) then begin Result := False; break; end; end; function NotInList(AComponent: TComponent): Boolean; var I: Integer; begin Result := True; for I := 0 to AList.Count - 1 do if (AList[I] is TComponent) and (TComponent(AList[I]) = AComponent) then begin Result := False; break; end; end; var V: Integer; AWriter: TWriter; I, J: Integer; Elements: IHTMLElementCollection; WebComp: TComponent; begin AList := TDesignerSelections.Create; DelphiDesigner.GetSelections(AList); if AList.Count > 0 then begin for I := 0 to FDesignerControl.Selection.Count - 1 do begin Elements := FDesignerControl.Selection[I].all as IHTMLElementCollection; for J := 0 to Elements.length - 1 do begin WebComp := GetComponentByHTMLElement(Root, Elements.item(J, 0) as IHTMLElement); if (WebComp <> nil) and NotInList(WebComp) then AList.Add(WebComp); end; end; AWriter := TWriter.Create(AStream, 4096); try AWriter.Root := DelphiDesigner.Root; for I := 0 to AList.Count - 1 do if (AList.Items[I] is TComponent) and (AList.Items[I] <> Root) and IsNotChild(TComponent(AList.Items[I])) then begin AWriter.WriteSignature; AWriter.WriteComponent(TComponent(AList.Items[I])); end; finally AWriter.WriteListEnd; AWriter.Free; end; if AStream.Size > 0 then begin V := 0; AStream.Write(V, 1); end; end; end; procedure GetHTMLText; var AList: IDesignerSelections; I: Integer; begin AHTML := ''; AList := TDesignerSelections.Create; DelphiDesigner.GetSelections(AList); for I := 0 to AList.Count - 1 do if (AList.Items[I] is TcxWebHTMLElementMapper) then AHTML := AHTML + TcxWebHTMLElementMapper(AList.Items[I]).Element.outerHTML; if AHTML <> '' then AHTML := EWFHTMLSign + AHTML; end; begin if SelectionCount = 0 then FDesignerControl.Selection.Copy else begin AStream := TMemoryStream.Create; try WriteSelectedComponents; GetHTMLText; if AStream.Size > 0 then EWFCopyStreamToClipboard(AStream, AHTML) else EWFCopyStreamToClipboard(nil, AHTML); finally AStream.Free; end; end; end; type PTransBuffer = ^TTransBuffer; TTransBuffer = array[0..1] of SmallInt; function GetIEVerInfo: string; const AFileName = 'mshtml'; AProductName = 'ProductVersion'; var i: integer; AInfoSize, AVerSize, dummy: DWORD; ptrans: PTransBuffer; typeStr: string; value: PChar; AVerBuf: pointer; begin Result := ''; AInfoSize := GetFileVersioninfoSize(AFileName, dummy); if AInfoSize <> 0 then begin GetMem(AVerBuf, AInfoSize); try if GetFileVersionInfo(AFileName, 0, AInfoSize, AVerBuf) then begin VerQueryvalue(AVerBuf, '\VarFileInfo\Translation', Pointer(ptrans), AInfoSize); for I := 0 to AInfoSize div SizeOf(TTransBuffer) do begin typeStr := 'StringFileInfo\' + IntToHex(ptrans^[0], 4) + IntToHex(ptrans^[1], 4) + '\' + AProductName; if VerQueryvalue(AVerBuf, PChar(typeStr), Pointer(value), AVerSize) then begin Result := Value; break; end; ptrans := PTransBuffer(Integer(ptrans) + SizeOf(TTransBuffer)); end; end; finally FreeMem(AVerBuf); end; end; end; function IsIE5_5: Boolean; var AVerInfo: string; function GetNextDigit: Integer; var ASt: string; I: Integer; begin Result := 0; if Pos('.', AVerInfo) > 0 then begin ASt := Copy(AVerInfo, 1, Pos('.', AVerInfo) - 1); AVerInfo := Copy(AVerInfo, Pos('.', AVerInfo) + 1, Length(AVerInfo)); end else begin ASt := AVerInfo; AVerInfo := ''; end; if (ASt <> '') then begin for I := 1 to Length(ASt) do if not (ASt[I] in ['0'..'9']) then exit; Result := StrToInt(ASt); end; end; var AVerNumber: Integer; begin Result := False; AVerInfo := GetIEVerInfo; if AVerInfo <> '' then begin AVerNumber := GetNextDigit; cxWebHTMLDesignerMajorVersion := AVerNumber; Result := AVerNumber > 4; if Result and (AVerNumber = 5) then Result :=GetNextDigit > 4; end; end; initialization if IsIE5_5 then WebDesignerFactory.RegisterDesigner(TcxWebHTMLDesigner, True); finalization WebDesignerFactory.UnregisterDesigner(TcxWebHTMLDesigner); end.