diff --git a/components/jvcllaz/design/JvMM/images/images.txt b/components/jvcllaz/design/JvMM/images/images.txt index 337373814..b5d93c1d1 100644 --- a/components/jvcllaz/design/JvMM/images/images.txt +++ b/components/jvcllaz/design/JvMM/images/images.txt @@ -1,3 +1,4 @@ +tjvid3v1.bmp tjvgradient.bmp tjvgradientheaderpanel.bmp tjvspecialprogress.bmp diff --git a/components/jvcllaz/design/JvMM/images/tjvid3v1.bmp b/components/jvcllaz/design/JvMM/images/tjvid3v1.bmp new file mode 100644 index 000000000..6c9069c75 Binary files /dev/null and b/components/jvcllaz/design/JvMM/images/tjvid3v1.bmp differ diff --git a/components/jvcllaz/design/JvMM/jvmmreg.pas b/components/jvcllaz/design/JvMM/jvmmreg.pas index c8942f6a4..1f41eea2f 100644 --- a/components/jvcllaz/design/JvMM/jvmmreg.pas +++ b/components/jvcllaz/design/JvMM/jvmmreg.pas @@ -16,11 +16,12 @@ implementation uses Classes, JvDsgnConsts, PropEdits, Controls, - JvGradient, JvGradientHeaderPanel, JvSpecialProgress; + JvId3v1, JvGradient, JvGradientHeaderPanel, JvSpecialProgress; procedure Register; begin RegisterComponents(RsPaletteJvcl, [ + TJvId3v1, TJvGradient, TJvGradientHeaderPanel, TJvSpecialProgress ]); diff --git a/components/jvcllaz/packages/jvmmlazr.lpk b/components/jvcllaz/packages/jvmmlazr.lpk index 20c109843..ad8b10f3f 100644 --- a/components/jvcllaz/packages/jvmmlazr.lpk +++ b/components/jvcllaz/packages/jvmmlazr.lpk @@ -15,7 +15,7 @@ - + @@ -28,6 +28,10 @@ + + + + diff --git a/components/jvcllaz/resource/jvmmreg.res b/components/jvcllaz/resource/jvmmreg.res index 159b7db3b..32f11e20e 100644 Binary files a/components/jvcllaz/resource/jvmmreg.res and b/components/jvcllaz/resource/jvmmreg.res differ diff --git a/components/jvcllaz/run/JvCore/JvJCLUtils.pas b/components/jvcllaz/run/JvCore/JvJCLUtils.pas index c1b631111..17aee2ff4 100644 --- a/components/jvcllaz/run/JvCore/JvJCLUtils.pas +++ b/components/jvcllaz/run/JvCore/JvJCLUtils.pas @@ -65,6 +65,8 @@ const NullHandle = 0; USDecimalSeparator = '.'; + WideNull = WideChar(#0); + (******************** NOT CONVERTED {$IFDEF UNIX} type @@ -1197,10 +1199,18 @@ function SecondsBetween(const Now: TDateTime; const FTime: TDateTime): Integer; ******************** NOT CONVERTED *) +function ReverseBytes(Value: Word): Word; overload; // taken from JclLogic +function ReverseBytes(Value: Integer): Integer; overload; +function ReverseBytes(Value: Cardinal): Cardinal; overload; + +// taken from JclFileUtils +function FindUnusedFileName(FileName: string; const FileExt: string; NumberPrefix: string = ''): string; + + implementation uses - Math, + Math, SysUtils, LazFileUtils, JvConsts; (******************** NOT CONVERTED @@ -9766,6 +9776,73 @@ end; ******************** NOT CONVERTED *) +// from JclLogic +function ReverseBytes(Value: Word): Word; +begin + Result := (Value shr 8) or (Value shl 8); +end; + +// from JclLogic +function ReverseBytes(Value: Integer): Integer; +begin + Result := (Value shr 24) or (Value shl 24) or ((Value and $00FF0000) shr 8) or ((Value and $0000FF00) shl 8); +end; + +// from JclLogic +function ReverseBytes(Value: Cardinal): Cardinal; +begin + Result := (Value shr 24) or (Value shl 24) or ((Value and $00FF0000) shr 8) or ((Value and $0000FF00) shl 8); +end; + +// from JclStrings +function StrEnsurePrefix(const Prefix, Text: string): string; +var + PrefixLen: SizeInt; +begin + PrefixLen := Length(Prefix); + if Copy(Text, 1, PrefixLen) = Prefix then + Result := Text + else + Result := Prefix + Text; +end; + +// from JclFileUtils +function PathAddExtension(const Path, Extension: string): string; +begin + Result := Path; + // (obones) Extension may not contain the leading dot while ExtractFileExt + // always returns it. Hence the need to use StrEnsurePrefix for the SameText + // test to return an accurate value. + if (Path <> '') and (Extension <> '') and + (CompareFileNames(ExtractFileExt(Path), StrEnsurePrefix('.',Extension)) <> 0) then +// not SameText(ExtractFileExt(Path), StrEnsurePrefix('.', Extension)) then + begin + if Path[Length(Path)] = '.' then + Delete(Result, Length(Path), 1); + if Extension[1] = '.' then + Result := Result + Extension + else + Result := Result + '.' + Extension; + end; +end; + +// from JclFileUtils +function FindUnusedFileName(FileName: string; const FileExt: string; NumberPrefix: string = ''): string; +var + I: Integer; +begin + Result := PathAddExtension(FileName, FileExt); + if not FileExists(Result) then + Exit; + if SameText(Result, FileName) then + Delete(FileName, Length(FileName) - Length(FileExt) + 1, Length(FileExt)); + I := 0; + repeat + Inc(I); + Result := PathAddExtension(FileName + NumberPrefix + IntToStr(I), FileExt); + until not FileExists(Result); +end; + end. diff --git a/components/jvcllaz/run/JvMM/JvGradientHeaderPanel.pas b/components/jvcllaz/run/JvMM/JvGradientHeaderPanel.pas index 0ea092831..fc40b0402 100644 --- a/components/jvcllaz/run/JvMM/JvGradientHeaderPanel.pas +++ b/components/jvcllaz/run/JvMM/JvGradientHeaderPanel.pas @@ -23,8 +23,9 @@ Known Issues: -----------------------------------------------------------------------------} // $Id$ -unit JvGradientHeaderPanel; +{$mode objfpc}{$H+} +unit JvGradientHeaderPanel; interface diff --git a/components/jvcllaz/run/JvMM/JvId3v1.pas b/components/jvcllaz/run/JvMM/JvId3v1.pas new file mode 100644 index 000000000..b08a1bdd7 --- /dev/null +++ b/components/jvcllaz/run/JvMM/JvId3v1.pas @@ -0,0 +1,398 @@ +{----------------------------------------------------------------------------- +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: JvID3v1.PAS, released on 2001-02-28. + +The Initial Developer of the Original Code is Sébastien Buysse [sbuysse att buypin dott com] +Portions created by Sébastien Buysse are Copyright (C) 2001 Sébastien Buysse. +All Rights Reserved. + +Contributor(s): Michael Beck [mbeck att bigfoot dott com]. + +You may retrieve the latest version of this file at the Project JEDI's JVCL home page, +located at http://jvcl.delphi-jedi.org + +Known Issues: +-----------------------------------------------------------------------------} +// $Id$ + +unit JvId3v1; + +{$mode objfpc}{$H+} + +interface + +uses + SysUtils, Classes + // JvComponentBase + ; + +type + TID3v1Tag = packed record + Identifier: array [0..2] of AnsiChar; + SongName: array [0..29] of AnsiChar; + Artist: array [0..29] of AnsiChar; + Album: array [0..29] of AnsiChar; + Year: array [0..3] of AnsiChar; + Comment: array [0..29] of AnsiChar; + Genre: Byte; + end; + + TJvID3v1 = class(TComponent) //TJvComponent) + private + FSongName: AnsiString; + FArtist: AnsiString; + FAlbum: AnsiString; + FComment: AnsiString; + FYear: AnsiString; + FGenre: Byte; + FFileName: TFileName; + FActive: Boolean; + FAlbumTrack: Byte; + FStreamedActive: Boolean; + FHasTag: Boolean; + FNeedUpdateHasTag: Boolean; + function GetGenreAsString: string; + function GetHasTag: Boolean; + procedure Reset; + procedure SetActive(const Value: Boolean); + procedure SetFileName(const Value: TFileName); + procedure SetGenreAsString(const Value: string); + protected + procedure CheckActive; + procedure DoOpen; virtual; + procedure DoClose; virtual; + function ReadTag: Boolean; + procedure Loaded; override; + public + procedure Refresh; + procedure Open; + procedure Close; + function Commit: Boolean; + procedure Erase; + property HasTag: Boolean read GetHasTag; + published + property Active: Boolean read FActive write SetActive; + property FileName: TFileName read FFileName write SetFileName; + { Do not store dummies } + property SongName: AnsiString read FSongName write FSongName stored False; + property Artist: AnsiString read FArtist write FArtist stored False; + property Album: AnsiString read FAlbum write FAlbum stored False; + property Year: AnsiString read FYear write FYear stored False; + property Comment: AnsiString read FComment write FComment stored False; + property Genre: Byte read FGenre write FGenre stored False; + property GenreAsString: string read GetGenreAsString write SetGenreAsString stored False; + property AlbumTrack: Byte read FAlbumTrack write FAlbumTrack stored False; + end; + +function HasID3v1Tag(const AFileName: string): Boolean; +function ReadID3v1Tag(const AFileName: string; var ATag: TID3v1Tag): Boolean; +procedure RemoveID3v1Tag(const AFileName: string); +function WriteID3v1Tag(const AFileName: string; const ATag: TID3v1Tag): Boolean; + + +implementation + +uses + Math, + JvId3v2Types, JvTypes, JvResources; + +const + CID3v1Tag: array [0..2] of AnsiChar = AnsiString('TAG'); { do not change case } + + CTagSize = 128; + CTagIDSize = 3; + +//=== Global procedures ====================================================== + +function HasID3v1Tag(const AFileName: string): Boolean; +var + TagID: array [0..CTagIDSize - 1] of AnsiChar; +begin + try + with TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite) do + try + Result := Size >= CTagSize; + if not Result then + Exit; + + Seek(-CTagSize, soFromEnd); + Result := (Read(TagID, CTagIDSize) = CTagIDSize) and (TagID = CID3v1Tag); + finally + Free; + end; + except + Result := False; + end; +end; + +function ReadID3v1Tag(const AFileName: string; var ATag: TID3v1Tag): Boolean; +begin + try + with TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite) do + try + Seek(-CTagSize, soFromEnd); + Result := (Read(ATag, CTagSize) = CTagSize) and (ATag.Identifier = CID3v1Tag); + finally + Free; + end; + except + Result := False; + end; +end; + +procedure RemoveID3v1Tag(const AFileName: string); +var + TagID: array [0..CTagIDSize - 1] of AnsiChar; +begin + with TFileStream.Create(AFileName, fmOpenReadWrite or fmShareDenyWrite) do + try + Seek(-CTagSize, soFromEnd); + + if (Read(TagID, CTagIDSize) = CTagIDSize) and (TagID = CID3v1Tag) then + Size := Size - CTagSize; + finally + Free; + end; +end; + +function WriteID3v1Tag(const AFileName: string; const ATag: TID3v1Tag): Boolean; +var + TagID: array [0..CTagIDSize - 1] of AnsiChar; +begin + try + Result := FileExists(AFileName); + if not Result then + Exit; + + with TFileStream.Create(AFileName, fmOpenReadWrite or fmShareExclusive) do + try + // Remove old Tag ? + if Size >= CTagSize then + begin + Seek(-CTagSize, soFromEnd); + if (Read(TagID, CTagIDSize) = CTagIDSize) and (TagID = CID3v1Tag) then + Seek(-CTagIDSize, soFromCurrent) + else + Seek(0, soFromEnd); + end + else + Seek(0, soFromEnd); + + // Write it + Result := Write(ATag, CTagSize) = CTagSize; + finally + Free; + end; + except + Result := False; + end; +end; + +//=== Local procedures ======================================================= + +procedure AnsiStringToPAnsiChar(const Source: AnsiString; Dest: PAnsiChar; const MaxLength: Integer); +begin + Move(PAnsiChar(Source)^, Dest^, Min(MaxLength, Length(Source))); +end; + +function PAnsiCharToAnsiString(P: PAnsiChar; MaxLength: Integer): AnsiString; +var + Q: PAnsiChar; +begin + Q := P; + while (P - Q < MaxLength) and (P^ <> #0) do + Inc(P); + + { [Q..P) is valid } + SetString(Result, Q, P - Q); +end; + +//=== { TJvID3v1 } =========================================================== + +procedure TJvID3v1.Loaded; +begin + inherited Loaded; + + FNeedUpdateHasTag := True; + if FStreamedActive then + SetActive(True); +end; + +procedure TJvID3v1.CheckActive; +begin + if not FActive then + raise EJVCLException.CreateRes(@RsENotActive); +end; + +procedure TJvID3v1.Close; +begin + SetActive(False); +end; + +function TJvID3v1.Commit: Boolean; +var + lTag: TID3v1Tag; +begin + CheckActive; + + FNeedUpdateHasTag := True; + + FillChar(lTag, CTagSize, #0); + + // Set new Tag + Move(CID3v1Tag[0], lTag.Identifier[0], 3); + AnsiStringToPAnsiChar(SongName, @lTag.SongName, 30); + AnsiStringToPAnsiChar(Artist, @lTag.Artist, 30); + AnsiStringToPAnsiChar(Album, @lTag.Album, 30); + AnsiStringToPAnsiChar(Year, @lTag.Year, 4); + AnsiStringToPAnsiChar(Comment, @lTag.Comment, 30); + lTag.Genre := FGenre; + if lTag.Comment[28] = #0 then + lTag.Comment[29] := AnsiChar(FAlbumTrack); + + Result := WriteID3v1Tag(FileName, lTag); +end; + +procedure TJvID3v1.DoClose; +begin + Reset; +end; + +procedure TJvID3v1.DoOpen; +begin + ReadTag; +end; + +procedure TJvID3v1.Erase; +var + SavedActive: Boolean; +begin + FNeedUpdateHasTag := True; + + SavedActive := Active; + Close; + + try + RemoveID3v1Tag(FileName); + finally + if SavedActive then + Open; + end; +end; + +function TJvID3v1.GetGenreAsString: string; +begin + Result := ID3_IDToGenre(Genre); +end; + +function TJvID3v1.GetHasTag: Boolean; +begin + if FNeedUpdateHasTag then + begin + FNeedUpdateHasTag := False; + FHasTag := HasID3v1Tag(FileName); + end; + + Result := FHasTag; +end; + +procedure TJvID3v1.Open; +begin + SetActive(True); +end; + +function TJvID3v1.ReadTag: Boolean; +var + lTag: TID3v1Tag; +begin + CheckActive; + + Result := ReadID3v1Tag(FileName, lTag); + + FNeedUpdateHasTag := False; + FHasTag := Result; + + if Result then + begin + FSongName := PAnsiCharToAnsiString(@lTag.SongName, 30); + FArtist := PAnsiCharToAnsiString(@lTag.Artist, 30); + FAlbum := PAnsiCharToAnsiString(@lTag.Album, 30); + FYear := PAnsiCharToAnsiString(@lTag.Year, 4); + FComment := PAnsiCharToAnsiString(@lTag.Comment, 30); + // (p3) missing genre added + FGenre := lTag.Genre; + if lTag.Comment[28] = #0 then + FAlbumTrack := Byte(lTag.Comment[29]) + else + FAlbumTrack := 0; + end + else + Reset; +end; + +procedure TJvID3v1.Refresh; +begin + CheckActive; + ReadTag; +end; + +procedure TJvID3v1.Reset; +begin + FSongName := ''; + FArtist := ''; + FAlbum := ''; + FYear := ''; + FComment := ''; + FGenre := 255; +end; + +procedure TJvID3v1.SetActive(const Value: Boolean); +begin + { Based on TCustomConnection.SetConnected } + if (csReading in ComponentState) and Value then + FStreamedActive := True + else + begin + if Value = FActive then + Exit; + FActive := Value; + if FActive then + DoOpen + else + DoClose; + end; +end; + +procedure TJvID3v1.SetFileName(const Value: TFileName); +var + SavedActive: Boolean; +begin + if Value <> FFileName then + begin + SavedActive := Active; + + Close; + + FNeedUpdateHasTag := True; + FFileName := Value; + + if SavedActive then + Open; + end; +end; + +procedure TJvID3v1.SetGenreAsString(const Value: string); +begin + Genre := ID3_GenreToID(Value); +end; + + +end.