Files
lazarus-ccr/components/jvcllaz/examples/JvID3v2/JvId3v2EditFormU.pas

721 lines
21 KiB
ObjectPascal
Raw Normal View History

{******************************************************************
JEDI-VCL Demo
Copyright (C) 2002 Project JEDI
Original author:
Contributor(s):
You may retrieve the latest version of this file at the JEDI-JVCL
home page, located at http://jvcl.delphi-jedi.org
The contents of this file are used with permission, 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_1Final.html
Software distributed under the License is distributed on an
"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
******************************************************************}
unit JvId3v2EditFormU;
{$mode objfpc}{$H+}
interface
uses
//Windows,
SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, ActnList, StdCtrls, ExtCtrls,
JvID3v2Base, JvId3v2; //, JvComponent, ImgList, ToolWin; //, JvComponentBase;
type
{ TJvID3v2EditForm }
TJvID3v2EditForm = class(TForm)
Notebook1: TNotebook;
PgWinAmpTags: TPage;
PgLyrics: TPage;
PgPicture: TPage;
PgAllFrames: TPage;
lsbNavigator: TListBox;
ToolBar1: TToolBar;
lblTitle: TLabel;
lblArtist: TLabel;
lblAlbum: TLabel;
lblYear: TLabel;
lblComposer: TLabel;
lblOrigArtist: TLabel;
lblCopyright: TLabel;
lblURL: TLabel;
lblEncodedBy: TLabel;
edtTitle: TEdit;
edtArtist: TEdit;
edtAlbum: TEdit;
edtYear: TEdit;
edtComposer: TEdit;
edtOrigArtist: TEdit;
edtCopyright: TEdit;
edtURL: TEdit;
edtEncodedBy: TEdit;
cmbGenre: TComboBox;
lblGenre: TLabel;
memComment: TMemo;
lblComment: TLabel;
acl16: TActionList;
iml16: TImageList;
actOK: TAction;
actCancel: TAction;
actRemove: TAction;
ToolButton1: TToolButton;
ToolButton2: TToolButton;
ToolButton3: TToolButton;
JvID3v21: TJvID3v2;
lblLanguage: TLabel;
cmbLanguage: TComboBox;
memLyrics: TMemo;
lblDescription: TLabel;
lblWriter: TLabel;
edtDescription: TEdit;
edtWriter: TEdit;
lsvPictures: TListView;
imgPicture: TImage;
actAddPicture: TAction;
actDeletePicture: TAction;
actSavePicture: TAction;
lblPictureName: TLabel;
lblPictureType: TLabel;
edtPictureName: TEdit;
cmbPictureType: TComboBox;
lsvAllFrames: TListView;
btnChange: TButton;
actChangePicture: TAction;
btnAdd: TButton;
btnDelete: TButton;
btnSave: TButton;
ToolButton4: TToolButton;
ToolButton5: TToolButton;
actCopyTov1: TAction;
actCopyFromv1: TAction;
procedure actAddPictureExecute(Sender: TObject);
procedure actCancelExecute(Sender: TObject);
procedure actChangePictureExecute(Sender: TObject);
procedure actCopyFromv1Execute(Sender: TObject);
procedure actCopyTov1Execute(Sender: TObject);
procedure actDeletePictureExecute(Sender: TObject);
procedure actOKExecute(Sender: TObject);
procedure actRemoveExecute(Sender: TObject);
procedure actSavePictureExecute(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ItemSelected(Sender: TObject);
procedure lsbNavigatorClick(Sender: TObject);
procedure lsvAllFramesInfoTip(Sender: TObject; Item: TListItem; var InfoTip: String);
procedure lsvPicturesClick(Sender: TObject);
private
FTagDeleted: Boolean;
protected
procedure Init;
procedure Final;
procedure InitAllFramesTab;
procedure TagToCtrls;
procedure CtrlsToTag;
procedure FillPictureTypes(Strings: TStrings);
class function Instance: TJvID3v2EditForm;
public
class function Execute(const AFileName: string): Boolean;
end;
implementation
uses
ExtDlgs,
JvID3v2Types;
{$R *.lfm}
var
CFrameDescriptions: array[TJvID3FrameID] of string = (
'Error', {fiErrorFrame}
'Padding', {fiPaddingFrame}
'No known frame', {fiNoFrame}
'Audio encryption', {fiAudioCrypto}
'Attached picture', {fiPicture}
'Audio seek point index', {fiAudioSeekPoint}
'Comments', {fiComment}
'Commercial frame', {fiCommercial}
'Encryption method registration', {fiCryptoReg}
'Equalisation (2)', {fiEqualization2}
'Equalization', {fiEqualization}
'Event timing codes', {fiEventTiming}
'General encapsulated object', {fiGeneralObject}
'Group identification registration', {fiGroupingReg}
'Involved people list', {fiInvolvedPeople}
'Linked information', {fiLinkedInfo}
'Music CD identifier', {fiCDID}
'MPEG location lookup table', {fiMPEGLookup}
'Ownership frame', {fiOwnership}
'Private frame', {fiPrivate}
'Play counter', {fiPlayCounter}
'Popularimeter', {fiPopularimeter}
'Position synchronisation frame', {fiPositionsync}
'Recommended buffer size', {fiBufferSize}
'Relative volume adjustment (2)', {fiVolumeAdj2}
'Relative volume adjustment', {fiVolumeAdj}
'Reverb', {fiReverb}
'Seek frame', {fiSeekFrame}
'Signature frame', {fiSignature}
'Synchronized lyric/text', {fiSyncedLyrics}
'Synchronized tempo codes', {fiSyncedTempo}
'Album/Movie/Show title', {fiAlbum}
'BPM (beats per minute)', {fiBPM}
'Composer', {fiComposer}
'Content type', {fiContentType}
'Copyright message', {fiCopyright}
'Date', {fiDate}
'Encoding time', {fiEncodingTime}
'Playlist delay', {fiPlaylistDelay}
'Original release time', {fiOrigReleaseTime}
'Recording time', {fiRecordingTime}
'Release time', {fiReleaseTime}
'Tagging time', {fiTaggingTime}
'Involved people list', {fiInvolvedPeople2}
'Encoded by', {fiEncodedBy}
'Lyricist/Text writer', {fiLyricist}
'File type', {fiFileType}
'Time', {fiTime}
'Content group description', {fiContentGroup}
'Title/songname/content description', {fiTitle}
'Subtitle/Description refinement', {fiSubTitle}
'Initial key', {fiInitialKey}
'Language(s)', {fiLanguage}
'Length', {fiSongLen}
'Musician credits list', {fiMusicianCreditList}
'Media type', {fiMediaType}
'Mood', {fiMood}
'Original album/movie/show title', {fiOrigAlbum}
'Original filename', {fiOrigFileName}
'Original lyricist(s)/text writer(s)', {fiOrigLyricist}
'Original artist(s)/performer(s)', {fiOrigArtist}
'Original release year', {fiOrigYear}
'File owner/licensee', {fiFileOwner}
'Lead performer(s)/Soloist(s)', {fiLeadArtist}
'Band/orchestra/accompaniment', {fiBand}
'Conductor/performer refinement', {fiConductor}
'Interpreted, remixed, or otherwise modified by', {fiMixArtist}
'Part of a set', {fiPartInSet}
'Produced notice', {fiProducedNotice}
'Publisher', {fiPublisher}
'Track number/Position in set', {fiTrackNum}
'Recording dates', {fiRecordingDates}
'Internet radio station name', {fiNetRadioStation}
'Internet radio station owner', {fiNetRadioOwner}
'Size', {fiSize}
'Album sort order', {fiAlbumSortOrder}
'Performer sort order', {fiPerformerSortOrder}
'Title sort order', {fiTitleSortOrder}
'ISRC (international standard recording code)', {fiISRC}
'Software/Hardware and settings used for encoding', {fiEncoderSettings}
'Set subtitle', {fiSetSubTitle}
'User defined text information', {fiUserText}
'Year', {fiYear}
'Unique file identifier', {fiUniqueFileID}
'Terms of use', {fiTermsOfUse}
'Unsynchronized lyric/text transcription', {fiUnsyncedLyrics}
'Commercial information', {fiWWWCommercialInfo}
'Copyright/Legal information', {fiWWWCopyright}
'Official audio file webpage', {fiWWWAudioFile}
'Official artist/performer webpage', {fiWWWArtist}
'Official audio source webpage', {fiWWWAudioSource}
'Official internet radio station homepage', {fiWWWRadioPage}
'Payment', {fiWWWPayment}
'Official publisher webpage', {fiWWWPublisher}
'User defined URL link', {fiWWWUser}
'Encrypted meta frame', {fiMetaCrypto}
'Compressed meta frame' {fiMetaCompression}
);
CPictureTypeStr: array[TJvID3PictureType] of string = (
'Other',
'32x32 pixels ''file icon'' (PNG only)',
'Other file icon',
'Cover (front)',
'Cover (back)',
'Leaflet page',
'Media (e.g. lable side of CD)',
'Lead artist/lead performer/soloist',
'Artist/performer',
'Conductor',
'Band/Orchestra',
'Composer',
'Lyricist/text writer',
'Recording Location',
'During recording',
'During performance',
'Movie/video screen capture',
'A bright coloured fish',
'Illustration',
'Band/artist logotype',
'Publisher/Studio logotype'
);
procedure SetPictureListItemTo(ListItem: TListItem; Frame: TJvID3PictureFrame);
begin
ListItem.Caption := Frame.Description;
while ListItem.SubItems.Count < 3 do
ListItem.SubItems.Add('');
ListItem.SubItems[0] := CPictureTypeStr[Frame.PictureType]; //Type
ListItem.SubItems[1] := Frame.MIMEType; //Format
ListItem.SubItems[2] := IntToStr(Frame.DataSize); //Size
ListItem.Data := Frame;
end;
procedure TJvID3v2EditForm.FormCreate(Sender: TObject);
begin
FillPictureTypes(cmbPictureType.Items);
ISO_639_2Names(cmbLanguage.Items);
ID3_Genres(cmbGenre.Items);
end;
procedure TJvID3v2EditForm.actOKExecute(Sender: TObject);
var
HasTag: Boolean;
Version: TJvID3Version;
lCursor: TCursor;
begin
lCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
try
CtrlsToTag;
JvID3v21.Frames.RemoveEmptyFrames;
HasTag := True;
if JvID3v21.FrameCount = 0 then
GetID3v2Version(JvID3v21.FileName, HasTag, Version);
if HasTag then
JvID3v21.Commit;
ModalResult := mrOk;
finally
Screen.Cursor := lCursor;
end;
end;
procedure TJvID3v2EditForm.actCancelExecute(Sender: TObject);
begin
ModalResult := mrCancel;
end;
procedure TJvID3v2EditForm.actRemoveExecute(Sender: TObject);
var
lCursor: TCursor;
begin
if MessageDlg('Remove tag?', mtConfirmation, mbOKCancel, 0) <> mrOk then
Exit;
lCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
try
JvID3v21.Erase;
FTagDeleted := True;
TagToCtrls;
finally
Screen.Cursor := lCursor;
end;
end;
class function TJvID3v2EditForm.Execute(const AFileName: string): Boolean;
begin
with TJvID3v2EditForm.Instance do
try
JvID3v21.FileName := AFileName;
Init;
try
Result := (ShowModal = mrOk) or FTagDeleted;
finally
Final;
end;
finally
Hide;
end;
end;
procedure TJvID3v2EditForm.Init;
begin
Caption := Format('Edit ''%s''', [ExtractFileName(JvID3v21.FileName)]);
JvID3v21.Open;
TagToCtrls;
imgPicture.Picture.Assign(nil);
lsbNavigator.ItemIndex := 0;
end;
function ChangeYear(const ADateTime: TDateTime; const NewYear: Word): TDateTime;
var
OldYear, Month, Day: Word;
begin
DecodeDate(ADateTime, OldYear, Month, Day);
Result := EncodeDate(NewYear, Month, Day);
end;
procedure TJvID3v2EditForm.CtrlsToTag;
//procedure SetFirstOfList(Strings: {$IFDEF COMPILER12_UP}TStrings{$ELSE}TWideStrings{$ENDIF COMPILER12_UP}; const S: string);
procedure SetFirstOfList(Strings: TStrings; const S: String);
begin
if Strings.Count > 0 then
Strings[0] := S
else
Strings.Add(S);
end;
begin
{ WinAmp tags }
{ WinAmp treats some tags as single line tags; mimic this behaviour by
using function SetFirstOfList }
JvID3v21.Texts.Title := edtTitle.Text;
SetFirstOfList(JvID3v21.Texts.LeadArtist, edtArtist.Text);
JvID3v21.Texts.Album := edtAlbum.Text;
{ The 'year' tag is replaced by the 'recordingtime' tag in v2.4 }
if JvID3v21.WriteVersion = ive2_4 then
JvID3v21.Texts.RecordingTime := ChangeYear(JvID3v21.Texts.RecordingTime, StrToIntDef(edtYear.Text, 0))
else
JvID3v21.Texts.Year := StrToIntDef(edtYear.Text, 0);
SetFirstOfList(JvID3v21.Texts.ContentType, NiceGenreToGenre(cmbGenre.Text));
{ Note that WinAmp doesn't care about other properties than Text of TJvID3ContentFrame }
TJvID3ContentFrame.FindOrCreate(JvID3v21, fiComment).Text := memComment.Lines.Text;
SetFirstOfList(JvID3v21.Texts.Composer, edtComposer.Text);
SetFirstOfList(JvID3v21.Texts.OrigArtist, edtOrigArtist.Text);
JvID3v21.Texts.Copyright := edtCopyright.Text;
{ Note that WinAmp doesn't care about other properties than URL of TJvID3URLUserFrame }
TJvID3URLUserFrame.FindOrCreate(JvID3v21, 0).URL := AnsiString(edtURL.Text);
JvID3v21.Texts.EncodedBy := edtEncodedBy.Text;
{ Lyrics }
with TJvID3ContentFrame.FindOrCreate(JvID3v21, fiUnsyncedLyrics) do
begin
Language := ISO_639_2NameToCode(cmbLanguage.Text);
Text := memLyrics.Lines.Text;
Description := edtDescription.Text;
end;
SetFirstOfList(JvID3v21.Texts.Lyricist, edtWriter.Text);
end;
function YearOf(const ADateTime: TDateTime): Word;
var
D1, D2: Word;
begin
DecodeDate(ADateTime, Result, D1, D2);
end;
procedure TJvID3v2EditForm.TagToCtrls;
//function GetFirstOfList(Strings: {$IFDEF COMPILER12_UP}TStrings{$ELSE}TWideStrings{$ENDIF COMPILER12_UP}): string;
function GetFirstOfList(Strings: TStrings): String;
begin
if Strings.Count > 0 then
Result := Strings[0]
else
Result := '';
end;
var
Frame: TJvID3Frame;
begin
{ Determine which frames are in the tag before calls to JvID3v21.Texts.xxx and
FindOrCreate because those functions might create frames. }
InitAllFramesTab;
{ WinAmp tags }
{ WinAmp treats some tags as single line tags; mimic this behaviour by
using function GetFirstOfList }
edtTitle.Text := JvID3v21.Texts.Title;
edtArtist.Text := GetFirstOfList(JvID3v21.Texts.LeadArtist);
edtAlbum.Text := JvID3v21.Texts.Album;
{ The 'year' tag is replaced by the 'recordingtime' tag in v2.4 }
if JvID3v21.Version = ive2_4 then
edtYear.Text := IntToStr(YearOf(JvID3v21.Texts.RecordingTime))
else
edtYear.Text := IntToStr(JvID3v21.Texts.Year);
cmbGenre.Text := GenreToNiceGenre(GetFirstOfList(JvID3v21.Texts.ContentType));
{ Note that WinAmp doesn't care about other properties than Text of TJvID3ContentFrame }
memComment.Lines.Text := TJvID3ContentFrame.FindOrCreate(JvID3v21, fiComment).Text;
edtComposer.Text := GetFirstOfList(JvID3v21.Texts.Composer);
edtOrigArtist.Text := GetFirstOfList(JvID3v21.Texts.OrigArtist);
edtCopyright.Text := JvID3v21.Texts.Copyright;
{ Note that WinAmp doesn't care about other properties than URL of TJvID3URLUserFrame }
edtURL.Text := string(TJvID3URLUserFrame.FindOrCreate(JvID3v21, 0).URL);
edtEncodedBy.Text := JvID3v21.Texts.EncodedBy;
{ Lyrics }
with TJvID3ContentFrame.FindOrCreate(JvID3v21, fiUnsyncedLyrics) do
begin
cmbLanguage.ItemIndex := cmbLanguage.Items.IndexOf(string(ISO_639_2CodeToName(Language)));
memLyrics.Lines.Text := Text;
edtDescription.Text := Description;
end;
edtWriter.Text := GetFirstOfList(JvID3v21.Texts.Lyricist);
{ Pictures }
lsvPictures.Items.BeginUpdate;
try
lsvPictures.Items.Clear;
if JvID3v21.FindFirstFrame(fiPicture, Frame) then
repeat
if Frame is TJvID3PictureFrame then
SetPictureListItemTo(lsvPictures.Items.Add, TJvID3PictureFrame(Frame));
until not JvID3v21.FindNextFrame(fiPicture, Frame);
finally
lsvPictures.Items.EndUpdate;
end;
end;
procedure TJvID3v2EditForm.actAddPictureExecute(Sender: TObject);
var
Frame: TJvID3PictureFrame;
begin
if cmbPictureType.ItemIndex < 0 then
begin
MessageDlg('Select a picture type', mtError, [mbOK], 0);
FocusControl(cmbPictureType);
Exit;
end;
with TOpenPictureDialog.Create(Application) do
try
if not Execute then
Exit;
Frame := TJvID3PictureFrame(JvID3v21.AddFrame(fiPicture));
with Frame do
begin
with cmbPictureType do
PictureType := TJvID3PictureType(PtrInt(Items.Objects[ItemIndex]));
Description := edtPictureName.Text;
MIMEType := AnsiString(ExtToMIMEType(ExtractFileExt(FileName)));
LoadFromFile(FileName);
lsvPictures.Items.BeginUpdate;
try
SetPictureListItemTo(lsvPictures.Items.Add, Frame);
finally
lsvPictures.Items.EndUpdate;
end;
end;
finally
Free;
end;
end;
procedure TJvID3v2EditForm.actDeletePictureExecute(Sender: TObject);
begin
if not Assigned(lsvPictures.Selected) then
Exit;
JvID3v21.Frames.Remove(TJvID3Frame(lsvPictures.Selected.Data));
lsvPictures.Items.Delete(lsvPictures.Selected.Index);
imgPicture.Picture.Assign(nil);
end;
procedure TJvID3v2EditForm.actSavePictureExecute(Sender: TObject);
var
Frame: TJvID3PictureFrame;
begin
if not Assigned(lsvPictures.Selected) then
Exit;
Frame := TJvID3PictureFrame(lsvPictures.Selected.Data);
if Assigned(Frame) and (Frame.DataSize > 0) and (Frame.MIMEType <> '-->') then
with TSavePictureDialog.Create(Application) do
try
if Execute then
Frame.SaveToFile(FileName);
finally
Free;
end;
end;
procedure TJvID3v2EditForm.FormShow(Sender: TObject);
var
P: TPoint;
begin
P := Point(0, EdtEncodedBy.Top + EdtEncodedby.Height + EdtEncodedBy.BorderSpacing.Bottom);
P := ScreenToClient(EdtEncodedBy.Parent.ClientToScreen(P));
Height := P.Y;
Notebook1.PageIndex := lsbNavigator.ItemIndex;
end;
procedure TJvID3v2EditForm.lsvPicturesClick(Sender: TObject);
var
Frame: TJvID3PictureFrame;
begin
if Assigned(lsvPictures.Selected) then
Frame := TJvID3PictureFrame(lsvPictures.Selected.Data)
else
Frame := nil;
if Assigned(Frame) then
begin
edtPictureName.Text := Frame.Description;
with cmbPictureType do
ItemIndex := Items.IndexOfObject(TObject(PtrInt(Frame.PictureType)));
end;
imgPicture.Picture.Assign(Frame);
end;
procedure TJvID3v2EditForm.lsbNavigatorClick(Sender: TObject);
begin
Notebook1.PageIndex := lsbNavigator.ItemIndex;
end;
procedure TJvID3v2EditForm.FillPictureTypes(Strings: TStrings);
var
PictureType: TJvID3PictureType;
begin
Strings.BeginUpdate;
try
Strings.Clear;
for PictureType := Low(TJvID3PictureType) to High(TJvID3PictureType) do
Strings.AddObject(CPictureTypeStr[PictureType], TObject(PtrInt(PictureType)));
finally
Strings.EndUpdate;
end;
end;
procedure TJvID3v2EditForm.InitAllFramesTab;
var
I: Integer;
ListItem: TListItem;
begin
lsvAllFrames.Items.BeginUpdate;
try
lsvAllFrames.Items.Clear;
for I := 0 to JvID3v21.FrameCount - 1 do
with JvID3v21.Frames[I] do
begin
ListItem := lsvAllFrames.Items.Add;
ListItem.Caption := string(FrameName);
if ClassType <> TJvID3SkipFrame then
ListItem.SubItems.Add('Yes')
else
ListItem.SubItems.Add('No');
ListItem.SubItems.Add(CFrameDescriptions[FrameID]);
ListItem.Data := JvID3v21.Frames[I];
end;
finally
lsvAllFrames.Items.EndUpdate;
end;
end;
procedure TJvID3v2EditForm.actChangePictureExecute(Sender: TObject);
var
Frame: TJvID3PictureFrame;
begin
if not Assigned(lsvPictures.Selected) then
Exit;
if cmbPictureType.ItemIndex < 0 then
begin
MessageDlg('Select a picture type', mtError, [mbOK], 0);
FocusControl(cmbPictureType);
Exit;
end;
Frame := TJvID3PictureFrame(lsvPictures.Selected.Data);
with Frame do
begin
with cmbPictureType do
PictureType := TJvID3PictureType(PtrInt(Items.Objects[ItemIndex]));
Description := edtPictureName.Text;
lsvPictures.Items.BeginUpdate;
try
SetPictureListItemTo(lsvPictures.Selected, Frame);
finally
lsvPictures.Items.EndUpdate;
end;
end;
end;
procedure TJvID3v2EditForm.ItemSelected(Sender: TObject);
begin
if Sender is TAction then
TAction(Sender).Enabled := Assigned(lsvPictures.Selected);
end;
procedure TJvID3v2EditForm.actCopyTov1Execute(Sender: TObject);
begin
if not JvID3v21.CopyToID3v1 then
ShowMessage('Error');
end;
procedure TJvID3v2EditForm.actCopyFromv1Execute(Sender: TObject);
begin
if JvID3v21.CopyFromID3v1 then
TagToCtrls
else
ShowMessage('Error');
end;
var
GInstance: TJvID3v2EditForm = nil;
class function TJvID3v2EditForm.Instance: TJvID3v2EditForm;
begin
if not Assigned(GInstance) then
GInstance := TJvID3v2EditForm.Create(Application);
Result := GInstance;
end;
procedure TJvID3v2EditForm.Final;
begin
JvID3v21.Close;
end;
procedure TJvID3v2EditForm.lsvAllFramesInfoTip(Sender: TObject;
Item: TListItem; var InfoTip: String);
var
Frame: TJvID3Frame;
S: string;
begin
Frame := TJvID3Frame(Item.Data);
if Frame is TJvID3TextFrame then
InfoTip := TJvID3TextFrame(Frame).Text
else if Frame is TJvID3NumberFrame then
InfoTip := IntToStr(TJvID3NumberFrame(Frame).Value)
else if Frame is TJvID3UserFrame then
with Frame as TJvID3UserFrame do
InfoTip := Format('%s: %s', [Description, Value])
else if Frame is TJvID3PictureFrame then
with Frame as TJvID3PictureFrame do
InfoTIp := Format('%s (%s) %d bytes', [Description, MIMEType, DataSize])
else if Frame is TJvID3TimestampFrame then
InfoTip := DateTimeToStr(TJvID3TimestampFrame(Frame).Value)
else if Frame is TJvID3ContentFrame then
InfoTip := TJvID3ContentFrame(Frame).Text
else if Frame is TJvID3SimpleListFrame then
begin
S := TJvID3SimpleListFrame(Frame).List.GetText;
Delete(S, Length(S) - 1, 2);
InfoTip := S;
end;
end;
end.