{----------------------------------------------------------------------------- The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JvID3v2Editor.PAS, released on 2003-04-16. The Initial Developer of the Original Code is Remko Bonte [remkobonte att myrealbox dott com] Portions created by Remko Bonte are Copyright (C) 2003 Remko Bonte. All Rights Reserved. Contributor(s): You may retrieve the latest version of this file at the Project JEDI's JVCL home page, located at http://jvcl.sourceforge.net Known Issues: -----------------------------------------------------------------------------} // $Id: JvID3v2EditorForm.pas 10610 2006-05-19 13:35:08Z elahn $ unit JvID3v2EditorForm; {$I jvcl.inc} interface { Most code is copied from DSDesign.pas } uses Classes, Windows, Messages, Forms, StdCtrls, ExtCtrls, Menus, Controls, {$IFDEF COMPILER6_UP} DesignEditors, DesignIntf, DesignMenus, DesignWindows, {$ELSE} DsgnIntf, DsgnWnds, {$ENDIF COMPILER6_UP} JvID3v2Base, JvID3v2Types, JvDsgnTypes; type TSelectionProc = function(AFrame: TJvID3Frame): Boolean of object; TFSDesigner = class; TJvID3FramesEditor = class(TDesignWindow) FrameListBox: TListBox; LocalMenu: TPopupMenu; NewItem: TMenuItem; N1: TMenuItem; DeleteItem: TMenuItem; SelectAllItem: TMenuItem; procedure FormCreate(Sender: TObject); procedure ListBoxClick(Sender: TObject); procedure ListBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure ListBoxDragDrop(Sender, Source: TObject; X, Y: Integer); procedure ListBoxDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); procedure FormDestroy(Sender: TObject); procedure NewFrameClick(Sender: TObject); procedure DeleteClick(Sender: TObject); procedure SelectAllClick(Sender: TObject); private FFSDesigner: TFSDesigner; FController: TJvID3Controller; FFocusRectItem: Integer; FMinWidth: Integer; FMinHeight: Integer; procedure MoveFrames(MoveOffset: Integer); procedure RemoveFrames; procedure SelectAll; procedure RestoreSelection(var Selection: TStringList; ItemIndex, TopIndex: Integer; RestoreUpdate: Boolean); procedure SaveSelection(var Selection: TStringList; var ItemIndex, TopIndex: Integer; NoUpdate: Boolean); procedure SetController(Value: TJvID3Controller); procedure UpdateDisplay; procedure UpdateCaption; procedure UpdateFrameList; procedure UpdateSelection; procedure WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo); message WM_GETMINMAXINFO; protected procedure Activated; override; function UniqueName(Component: TComponent): string; override; public function ForEachSelection(Proc: TSelectionProc): Boolean; function DoNewFrame: TJvID3Frame; {$IFDEF COMPILER5} procedure SelectionChanged(ASelection: TDesignerSelectionList); override; procedure EditAction(Action: TEditAction); override; procedure ComponentDeleted(Component: IPersistent); override; procedure FormModified; override; {$ELSE} function EditAction(Action: TEditAction): Boolean; override; procedure ItemDeleted(const ADesigner: IDesigner; AItem: TPersistent); override; procedure SelectionChanged(const ADesigner: IDesigner; const ASelection: IDesignerSelections); override; procedure ItemsModified(const Designer: IDesigner); override; {$ENDIF COMPILER5} property Controller: TJvID3Controller read FController write SetController; end; TJvID3ControllerEditor = class(TComponentEditor) protected procedure Commit; procedure RemoveTag; public procedure ExecuteVerb(Index: Integer); override; function GetVerb(Index: Integer): string; override; function GetVerbCount: Integer; override; end; TFSDesigner = class(TJvID3ControllerDesigner) private FFramesEditor: TJvID3FramesEditor; function GetFrameDescription(const FrameID: TJvID3FrameID): string; public destructor Destroy; override; procedure ID3Event(Event: TJvID3Event; Info: Longint); override; property FramesEditor: TJvID3FramesEditor read FFramesEditor; property FrameDescription[const FrameID: TJvID3FrameID]: string read GetFrameDescription; end; TJvID3FileInfoEditor = class(TClassProperty) public procedure Edit; override; function GetAttributes: TPropertyAttributes; override; end; procedure ShowFramesEditor(Designer: IJvFormDesigner; AController: TJvID3Controller); function CreateFramesEditor(Designer: IJvFormDesigner; AController: TJvID3Controller; var Shared: Boolean): TJvID3FramesEditor; procedure ShowFileInfo(AController: TJvID3Controller); implementation uses Dialogs, Math, SysUtils, JvID3v2DefineForm, JvTypes, JvDsgnConsts; {$R *.dfm} type TJvID3ControllerAccess = class(TJvID3Controller); const cFrameDescriptions: array [TJvID3FrameID] of string = ( RsfiErrorFrame, RsfiPaddingFrame, RsfiNoFrame, RsfiAudioCrypto, RsfiPicture, RsfiAudioSeekPoint, RsfiComment, RsfiCommercial, RsfiCryptoReg, RsfiEqualization2, RsfiEqualization, RsfiEventTiming, RsfiGeneralObject, RsfiGroupingReg, RsfiInvolvedPeople, RsfiLinkedInfo, RsfiCDID, RsfiMPEGLookup, RsfiOwnership, RsfiPrivate, RsfiPlayCounter, RsfiPopularimeter, RsfiPositionsync, RsfiBufferSize, RsfiVolumeAdj2, RsfiVolumeAdj, RsfiReverb, RsfiSeekFrame, RsfiSignature, RsfiSyncedLyrics, RsfiSyncedTempo, RsfiAlbum, RsfiBPM, RsfiComposer, RsfiContentType, RsfiCopyright, RsfiDate, RsfiEncodingTime, RsfiPlaylistDelay, RsfiOrigReleaseTime, RsfiRecordingTime, RsfiReleaseTime, RsfiTaggingTime, RsfiInvolvedPeople2, RsfiEncodedBy, RsfiLyricist, RsfiFileType, RsfiTime, RsfiContentGroup, RsfiTitle, RsfiSubTitle, RsfiInitialKey, RsfiLanguage, RsfiSongLen, RsfiMusicianCreditList, RsfiMediaType, RsfiMood, RsfiOrigAlbum, RsfiOrigFileName, RsfiOrigLyricist, RsfiOrigArtist, RsfiOrigYear, RsfiFileOwner, RsfiLeadArtist, RsfiBand, RsfiConductor, RsfiMixArtist, RsfiPartInSet, RsfiProducedNotice, RsfiPublisher, RsfiTrackNum, RsfiRecordingDates, RsfiNetRadioStation, RsfiNetRadioOwner, RsfiSize, RsfiAlbumSortOrder, RsfiPerformerSortOrder, RsfiTitleSortOrder, RsfiISRC, RsfiEncoderSettings, RsfiSetSubTitle, RsfiUserText, RsfiYear, RsfiUniqueFileID, RsfiTermsOfUse, RsfiUnsyncedLyrics, RsfiWWWCommercialInfo, RsfiWWWCopyright, RsfiWWWAudioFile, RsfiWWWArtist, RsfiWWWAudioSource, RsfiWWWRadioPage, RsfiWWWPayment, RsfiWWWPublisher, RsfiWWWUser, RsfiMetaCrypto, RsfiMetaCompression ); procedure ShowFramesEditor(Designer: IJvFormDesigner; AController: TJvID3Controller); var FramesEditor: TJvID3FramesEditor; VShared: Boolean; begin FramesEditor := CreateFramesEditor(Designer, AController, VShared); if FramesEditor <> nil then FramesEditor.Show; end; function CreateFramesEditor(Designer: IJvFormDesigner; AController: TJvID3Controller; var Shared: Boolean): TJvID3FramesEditor; begin Shared := True; if AController.Designer <> nil then Result := (AController.Designer as TFSDesigner).FFramesEditor else begin Result := TJvID3FramesEditor.Create(Application); Result.Designer := Designer; Result.Controller := AController; Shared := False; end; end; procedure ShowFileInfo(AController: TJvID3Controller); const cVersion: array [TJvMPEGVersion] of string = (RsMPEG25, RsMPEGUnknown, RsMPEG20, RsMPEG10); cLayer: array [TJvMPEGLayer] of string = (RsLayerUnknown, RsLayer3, RsLayer2, RsLayer1); cChannelMode: array [TJvMPEGChannelMode] of string = (RsChannelModeStereo, RsChannelModeJointStereo, RsChannelModeDualChannel, RsChannelModeMono); cEmphasis: array [TJvMPEGEmphasis] of string = (RsEmphasisNone, RsEmphasisMicrosec, RsEmphasisUnknown, RsEmphasisCCITTJ17); cBool: array [Boolean] of string = (RsBoolNo, RsBoolYes); cVbr: array [Boolean] of string = (RsVbrNo, RsVbrVbr); var Msg: string; SavedActive: Boolean; begin SavedActive := AController.Active; try with TJvID3ControllerAccess(AController) do begin if FileName = '' then begin MessageDlg(RsID3Err_NoFileSpecified, mtError, [mbOK], 0); Exit; end; if not FileExists(FileName) then begin MessageDlg(Format(RSID3Err_FileDoesNotExists, [FileName]), mtError, [mbOK], 0); Exit; end; Active := True; with FileInfo do begin if not IsValid then begin MessageDlg(RSID3Err_NoValidMPEGTag, mtError, [mbOK], 0); Exit; end; Msg := Format(RsIDV2FileInfoFmt, [FileSize, HeaderFoundAt, LengthInSec, cVersion[Version], cLayer[Layer], Bitrate, cVbr[IsVbr], FrameCount, SamplingRateFrequency, cChannelMode[ChannelMode], cBool[mbProtection in Bits], cBool[mbCopyrighted in Bits], cBool[mbOriginal in Bits], cEmphasis[Emphasis]]); end; end; { We don't use MessageDlg now, because we want a custom caption } with CreateMessageDialog(Msg, mtCustom, [mbOK]) do try Position := poScreenCenter; Caption := RsIDV2FileInfoCaption; ShowModal; finally Free; end; finally AController.Active := SavedActive; end; end; //=== { TJvID3FrameEditor } ================================================== procedure TJvID3FramesEditor.Activated; begin {$IFDEF COMPILER6_UP} Designer.Activate; {$ENDIF COMPILER6_UP} try UpdateSelection; except FrameListBox.Items.Clear; end; end; {$IFDEF COMPILER5} procedure TJvID3FramesEditor.ComponentDeleted(Component: IPersistent); var P: TPersistent; begin P := ExtractPersistent(Component); if P = Controller then Controller := nil else if (P is TJvID3Frame) and (TJvID3Frame(P).Controller = Controller) then UpdateDisplay; end; {$ENDIF COMPILER5} function TJvID3FramesEditor.DoNewFrame: TJvID3Frame; var DefineFrame: TJvID3DefineDlg; begin Result := nil; DefineFrame := TJvID3DefineDlg.Create(Application); try DefineFrame.FSDesigner := FFSDesigner; DefineFrame.Designer := Designer; DefineFrame.Controller := Controller; if DefineFrame.ShowModal = mrOk then begin Result := DefineFrame.Frame; if Visible then UpdateDisplay; Designer.Modified; end; finally DefineFrame.Release; end; end; {$IFDEF COMPILER6_UP} function TJvID3FramesEditor.EditAction(Action: TEditAction): Boolean; {$ELSE} procedure TJvID3FramesEditor.EditAction(Action: TEditAction); {$ENDIF COMPILER6_UP} begin {$IFDEF COMPILER6_UP} Result := True; {$ENDIF COMPILER6_UP} case Action of {eaCut: Cut; eaCopy: Copy; eaPaste: Paste;} eaDelete: RemoveFrames; eaSelectAll: begin SelectAll; UpdateSelection; end; {$IFDEF COMPILER6_UP} else Result := False; {$ENDIF COMPILER6_UP} end; end; function TJvID3FramesEditor.ForEachSelection(Proc: TSelectionProc): Boolean; var Frame: TJvID3Frame; I: Integer; begin Result := False; with FrameListBox do for I := 0 to Items.Count - 1 do if Selected[I] then begin Frame := TJvID3Frame(Items.Objects[I]); if (Frame <> nil) and not Proc(Frame) then Exit; end; Result := True; end; {$IFDEF COMPILER6_UP} procedure TJvID3FramesEditor.ItemDeleted(const ADesigner: IDesigner; AItem: TPersistent); begin if AItem = Controller then Controller := nil else if (AItem is TJvID3Frame) and (TJvID3Frame(AItem).Controller = Controller) then UpdateDisplay; end; {$ENDIF COMPILER6_UP} {$IFDEF COMPILER6_UP} procedure TJvID3FramesEditor.ItemsModified(const Designer: IDesigner); {$ELSE} procedure TJvID3FramesEditor.FormModified; {$ENDIF COMPILER6_UP} begin UpdateCaption; end; procedure TJvID3FramesEditor.MoveFrames(MoveOffset: Integer); var I, E: Integer; begin try FFSDesigner.BeginDesign; try with FrameListBox do begin I := 0; E := Items.Count; if MoveOffset > 0 then begin I := E - 1; E := -1; end; while I <> E do begin if Selected[I] then with TJvID3Frame(Items.Objects[I]) do Index := Index + MoveOffset; Inc(I, -MoveOffset); end; end; finally FFSDesigner.EndDesign; end; finally UpdateDisplay; Designer.Modified; end; end; procedure TJvID3FramesEditor.RemoveFrames; var I, Focused: Integer; begin try FFSDesigner.BeginDesign; try Focused := FrameListBox.ItemIndex; with FrameListBox do for I := Items.Count - 1 downto 0 do if Selected[I] then TJvID3Frame(Items.Objects[I]).Free; finally FFSDesigner.EndDesign; Designer.Modified; end; finally UpdateDisplay; end; if Focused <> -1 then begin Focused := Min(Focused, FrameListBox.Items.Count - 1); FrameListBox.ItemIndex := Focused; FrameListBox.Selected[Focused] := True; UpdateSelection; end; FrameListBox.SetFocus; end; procedure TJvID3FramesEditor.RestoreSelection(var Selection: TStringList; ItemIndex, TopIndex: Integer; RestoreUpdate: Boolean); var I: Integer; begin try with FrameListBox do for I := 0 to Items.Count - 1 do Selected[I] := Selection.IndexOf(TComponent(Items.Objects[I]).Name) <> -1; if TopIndex <> -1 then FrameListBox.TopIndex := TopIndex; if ItemIndex <> -1 then FrameListBox.ItemIndex := ItemIndex; finally if RestoreUpdate then FrameListBox.Items.EndUpdate; FrameListBox.Invalidate; Selection.Free; Selection := nil; UpdateSelection; end; end; procedure TJvID3FramesEditor.SaveSelection(var Selection: TStringList; var ItemIndex, TopIndex: Integer; NoUpdate: Boolean); var I: Integer; begin { Name of a frame is unique, thus fill Selection with these names of frames that are selected } Selection := TStringList.Create; try ItemIndex := FrameListBox.ItemIndex; TopIndex := FrameListBox.TopIndex; with FrameListBox do for I := 0 to Items.Count - 1 do if Selected[I] then Selection.Add(TComponent(Items.Objects[I]).Name); if NoUpdate then FrameListBox.Items.BeginUpdate; except Selection.Free; Selection := nil; end; end; procedure TJvID3FramesEditor.SelectAll; var I: Integer; begin with FrameListBox do for I := 0 to Items.Count - 1 do Selected[I] := True; end; {$IFDEF COMPILER6_UP} procedure TJvID3FramesEditor.SelectionChanged(const ADesigner: IDesigner; const ASelection: IDesignerSelections); {$ELSE} procedure TJvID3FramesEditor.SelectionChanged(ASelection: TDesignerSelectionList); {$ENDIF COMPILER6_UP} var I: Integer; S: Boolean; function InSelection(Component: TComponent): Boolean; var I: Integer; begin Result := True; if ASelection <> nil then with ASelection do for I := 0 to Count - 1 do if Component = Items[I] then Exit; Result := False; end; begin with FrameListBox do for I := 0 to Items.Count - 1 do begin S := InSelection(TComponent(Items.Objects[I])); if Selected[I] <> S then Selected[I] := S; end; end; procedure TJvID3FramesEditor.SetController(Value: TJvID3Controller); begin if FController <> Value then begin if FController <> nil then FreeAndNil(FFSDesigner); FController := Value; if FController <> nil then begin FFSDesigner := TFSDesigner.Create(Value); FFSDesigner.FFramesEditor := Self; UpdateDisplay; end else begin if not (csDestroying in ComponentState) then Release; end; end; end; function TJvID3FramesEditor.UniqueName(Component: TComponent): string; var FrameName: string; begin if Component is TJvID3Frame then FrameName := TJvID3Frame(Component).FrameName else FrameName := ''; Result := CreateUniqueName(Controller, FrameName, TJvID3FrameClass(Component.ClassType), Component) end; procedure TJvID3FramesEditor.UpdateCaption; const cFrameEditor = '%s%s%s'; var NewCaption: string; begin if (Controller <> nil) and (Controller.Owner <> nil) then NewCaption := Format(cFrameEditor, [Controller.Owner.Name, '.', Controller.Name]); if Caption <> NewCaption then Caption := NewCaption; end; procedure TJvID3FramesEditor.UpdateDisplay; begin UpdateFrameList; UpdateCaption; UpdateSelection; end; procedure TJvID3FramesEditor.UpdateFrameList; var ItemIndex, TopIndex: Integer; Selection: TStringList; EnableList: Boolean; I: Integer; Frame: TJvID3Frame; FrameName: string; begin SaveSelection(Selection, ItemIndex, TopIndex, True); try FrameListBox.Clear; EnableList := False; try if Controller = nil then Exit; for I := 0 to Controller.Frames.Count - 1 do begin Frame := Controller.Frames[I]; if not (csDestroying in Frame.ComponentState) then begin FrameName := Frame.FrameName; if FrameName = '' then FrameName := Format('<%s>', [Controller.Frames[I].Name]); FrameName := FrameName + ' - ' + cFrameDescriptions[Frame.FrameID]; FrameListBox.Items.AddObject(FrameName, Frame); end; end; EnableList := True; finally FrameListBox.Enabled := EnableList; end; finally RestoreSelection(Selection, ItemIndex, TopIndex, True) end; end; procedure TJvID3FramesEditor.UpdateSelection; var I: Integer; Frame: TJvID3Frame; {$IFDEF COMPILER6_UP} ComponentList: IDesignerSelections; {$ELSE} ComponentList: TDesignerSelectionList; {$ENDIF COMPILER6_UP} begin if Active then begin {$IFDEF COMPILER6_UP} ComponentList := TDesignerSelections.Create; {$ELSE} ComponentList := TDesignerSelectionList.Create; {$ENDIF COMPILER6_UP} try with FrameListBox do for I := 0 to Items.Count - 1 do if Selected[I] then begin Frame := TJvID3Frame(Items.Objects[I]); if Frame <> nil then ComponentList.Add(Frame); end; if ComponentList.Count = 0 then ComponentList.Add(Controller); except {$IFDEF COMPILER5} // In D6 up it's an interface, so no need to free up ComponentList.Free; {$ENDIF COMPILER5} raise; end; {$IFDEF COMPILER5} SetSelection(ComponentList); {$ELSE} Designer.SetSelections(ComponentList); {$ENDIF COMPILER5} end; end; procedure TJvID3FramesEditor.WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo); begin inherited; with Msg.MinMaxInfo^.ptMinTrackSize do begin X := FMinWidth; Y := FMinHeight; end; end; //=== { TJvID3ControllerEditor } ============================================= procedure TJvID3ControllerEditor.Commit; begin if MessageDlg(RsCommit, mtConfirmation, mbOKCancel, 0) = mrOk then TJvID3Controller(Component).Commit; end; procedure TJvID3ControllerEditor.ExecuteVerb(Index: Integer); begin case Index of 0: ShowFramesEditor(Designer, TJvID3Controller(Component)); 1: RemoveTag; 2: ShowFileInfo(TJvID3Controller(Component)); 3: Commit; end; end; function TJvID3ControllerEditor.GetVerb(Index: Integer): string; begin case Index of 0: Result := RSID3FrameEditorTag; 1: Result := RSID3RemoveTag; 2: Result := RSID3FileInfoTag; 3: Result := RSID3CommitTag; end; end; function TJvID3ControllerEditor.GetVerbCount: Integer; begin Result := 3; with TJvID3Controller(Component) do if Active and Modified then Inc(Result); end; procedure TJvID3FramesEditor.FormCreate(Sender: TObject); begin FMinWidth := Width; FMinHeight := Height; end; procedure TJvID3ControllerEditor.RemoveTag; begin with TJvID3Controller(Component) do begin if FileName = '' then begin MessageDlg(RsID3Err_NoFileSpecified, mtError, [mbOK], 0); Exit; end; if not FileExists(FileName) then begin MessageDlg(Format(RSID3Err_FileDoesNotExists, [FileName]), mtError, [mbOK], 0); Exit; end; if MessageDlg(RSID3RemoveTagConfirmation, mtConfirmation, mbOKCancel, 0) = mrOk then Erase; end; end; //=== { TFSDesigner } ======================================================== destructor TFSDesigner.Destroy; var F: TJvID3FramesEditor; begin if FFramesEditor <> nil then begin F := FFramesEditor; FFramesEditor := nil; F.FFSDesigner := nil; { (rb) DSDesign.pas uses Release, but that gave problems, with recompiling } F.Free; end; inherited Destroy; end; procedure TJvID3FramesEditor.ListBoxClick(Sender: TObject); begin UpdateSelection; end; function TFSDesigner.GetFrameDescription(const FrameID: TJvID3FrameID): string; begin Result := cFrameDescriptions[FrameID]; end; procedure TFSDesigner.ID3Event(Event: TJvID3Event; Info: Longint); begin if Event in [ideFrameListChange, ideID3Change] then FFramesEditor.UpdateFrameList; end; procedure TJvID3FramesEditor.ListBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin case Key of VK_INSERT: NewFrameClick(Self); VK_DELETE: RemoveFrames; VK_UP: if (ssCtrl in Shift) and (Sender = FrameListBox) then MoveFrames(-1) else Exit; VK_DOWN: if (ssCtrl in Shift) and (Sender = FrameListBox) then MoveFrames(1) else Exit; else Exit; end; Key := 0; end; procedure TJvID3FramesEditor.ListBoxDragDrop(Sender, Source: TObject; X, Y: Integer); var F: TJvID3Frame; I: Integer; begin try FFSDesigner.BeginDesign; try with FrameListBox do begin F := TJvID3Frame(Items.Objects[ItemAtPos(Point(X, Y), True)]); for I := 0 to Items.Count - 1 do if Selected[I] then TJvID3Frame(Items.Objects[I]).Index := F.Index; end; finally FFSDesigner.EndDesign; end; finally UpdateDisplay; Designer.Modified; end; end; procedure TJvID3FramesEditor.ListBoxDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var Item: Integer; procedure DrawRect(Item: Integer); begin if Item <> -1 then with FrameListBox do Canvas.DrawFocusRect(ItemRect(Item)); FFocusRectItem := Item; end; begin Item := FrameListBox.ItemAtPos(Point(X, Y), False); Accept := (Source = FrameListBox) and (Item >= 0) and (Item < FrameListBox.Items.Count) and not FrameListBox.Selected[Item]; if State = dsDragEnter then FFocusRectItem := -1; if (State = dsDragLeave) or not Accept then Item := -1; DrawRect(FFocusRectItem); DrawRect(Item); end; procedure TJvID3FramesEditor.FormDestroy(Sender: TObject); begin if FFSDesigner <> nil then begin { Destroy the designer if the editor is destroyed } FFSDesigner.FFramesEditor := nil; FFSDesigner.Free; FFSDesigner := nil; end; end; procedure TJvID3FramesEditor.NewFrameClick(Sender: TObject); var Selection: TStringList; Frame: TJvID3Frame; begin Frame := DoNewFrame; if Frame <> nil then begin Selection := TStringList.Create; try Selection.Add(Frame.Name); finally RestoreSelection(Selection, -1, -1, False); end; end; FrameListBox.SetFocus; end; procedure TJvID3FramesEditor.DeleteClick(Sender: TObject); begin RemoveFrames; end; procedure TJvID3FramesEditor.SelectAllClick(Sender: TObject); begin SelectAll; UpdateSelection; end; //=== { TJvID3FileInfoEditor } =============================================== procedure TJvID3FileInfoEditor.Edit; var P: TPersistent; begin P := TPersistent(GetComponent(0)); if P is TJvID3Controller then ShowFileInfo(TJvID3Controller(P)); end; function TJvID3FileInfoEditor.GetAttributes: TPropertyAttributes; begin Result := [paDialog]; end; end.