2017-12-02 18:45:28 +00:00
|
|
|
unit mdvMain;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2022-03-07 12:10:52 +00:00
|
|
|
Classes, SysUtils, FileUtil, SynEdit, SynHighlighterXML, Forms, Controls,
|
|
|
|
Graphics, Dialogs, ShellCtrls, ExtCtrls, ComCtrls, StdCtrls, fpeGlobal,
|
|
|
|
fpeMetadata;
|
2017-12-02 18:45:28 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
{ TMainForm }
|
|
|
|
|
|
|
|
TMainForm = class(TForm)
|
2018-12-29 13:15:37 +00:00
|
|
|
BtnChangeDate: TButton;
|
2022-03-07 21:03:56 +00:00
|
|
|
btnApplyChangesXMP: TButton;
|
|
|
|
btnSaveXMP: TButton;
|
2017-12-07 11:35:50 +00:00
|
|
|
CbDecodeMakerNotes: TCheckBox;
|
2019-01-24 14:34:35 +00:00
|
|
|
CbShowTagIDs: TCheckBox;
|
|
|
|
CbShowParentTagID: TCheckBox;
|
2022-03-07 21:03:56 +00:00
|
|
|
cbProcessXMP: TCheckBox;
|
2018-12-29 13:15:37 +00:00
|
|
|
EdChangeDate: TEdit;
|
2017-12-02 18:45:28 +00:00
|
|
|
FilenameInfo: TLabel;
|
2017-12-06 22:12:10 +00:00
|
|
|
Image: TImage;
|
2019-01-24 14:34:35 +00:00
|
|
|
Label1: TLabel;
|
2018-12-29 13:15:37 +00:00
|
|
|
LblChangeDate: TLabel;
|
2022-03-07 21:03:56 +00:00
|
|
|
Panel5: TPanel;
|
2022-03-07 12:10:52 +00:00
|
|
|
XMPListView: TListView;
|
2017-12-06 22:12:10 +00:00
|
|
|
Messages: TMemo;
|
2023-10-28 23:14:27 +00:00
|
|
|
PageControl: TPageControl;
|
2017-12-06 22:12:10 +00:00
|
|
|
Panel1: TPanel;
|
2017-12-02 18:45:28 +00:00
|
|
|
Panel2: TPanel;
|
|
|
|
Panel3: TPanel;
|
2018-12-29 13:15:37 +00:00
|
|
|
DateTimePanel: TPanel;
|
2019-01-24 14:34:35 +00:00
|
|
|
Panel4: TPanel;
|
2017-12-02 18:45:28 +00:00
|
|
|
PreviewImage: TImage;
|
|
|
|
ImageList: TImageList;
|
2017-12-06 22:12:10 +00:00
|
|
|
Splitter3: TSplitter;
|
2022-03-07 12:10:52 +00:00
|
|
|
Splitter4: TSplitter;
|
2017-12-02 18:45:28 +00:00
|
|
|
StatusBar1: TStatusBar;
|
2017-12-06 22:12:10 +00:00
|
|
|
PgMetadata: TTabSheet;
|
|
|
|
PgImage: TTabSheet;
|
2022-03-07 12:10:52 +00:00
|
|
|
PgXMP: TTabSheet;
|
2022-03-07 21:03:56 +00:00
|
|
|
XMPSynEdit: TSynEdit;
|
2022-03-07 12:10:52 +00:00
|
|
|
SynXMLSyn: TSynXMLSyn;
|
2017-12-02 18:45:28 +00:00
|
|
|
TagListView: TListView;
|
|
|
|
ShellPanel: TPanel;
|
|
|
|
ShellListView: TShellListView;
|
|
|
|
ShellTreeView: TShellTreeView;
|
|
|
|
Splitter1: TSplitter;
|
|
|
|
Splitter2: TSplitter;
|
2022-03-07 21:03:56 +00:00
|
|
|
procedure btnApplyChangesXMPClick(Sender: TObject);
|
2018-12-29 13:15:37 +00:00
|
|
|
procedure BtnChangeDateClick(Sender: TObject);
|
2022-03-07 21:03:56 +00:00
|
|
|
procedure btnSaveXMPClick(Sender: TObject);
|
2019-01-24 14:34:35 +00:00
|
|
|
procedure CbShowTagIDsChange(Sender: TObject);
|
2017-12-02 18:45:28 +00:00
|
|
|
procedure FormCreate(Sender: TObject);
|
|
|
|
procedure FormDestroy(Sender: TObject);
|
2023-10-28 23:14:27 +00:00
|
|
|
procedure PageControlChange(Sender: TObject);
|
2017-12-02 18:45:28 +00:00
|
|
|
procedure ShellListViewSelectItem(Sender: TObject; Item: TListItem;
|
|
|
|
Selected: Boolean);
|
|
|
|
procedure ShellTreeViewGetImageIndex(Sender: TObject; Node: TTreeNode);
|
|
|
|
procedure ShellTreeViewSelectionChanged(Sender: TObject);
|
|
|
|
procedure TagListViewCompare(Sender: TObject; Item1, Item2: TListItem;
|
2021-10-27 14:54:14 +00:00
|
|
|
{%H-}Data: Integer; var Compare: Integer);
|
2017-12-02 18:45:28 +00:00
|
|
|
procedure TagListViewSelectItem(Sender: TObject; Item: TListItem;
|
|
|
|
Selected: Boolean);
|
|
|
|
private
|
|
|
|
FImgInfo: TImgInfo;
|
2017-12-06 22:12:10 +00:00
|
|
|
FImageLoaded: Boolean;
|
2019-08-05 22:39:15 +00:00
|
|
|
FImageOrientation: TExifOrientation;
|
2020-02-17 18:52:45 +00:00
|
|
|
FFileName: String;
|
2017-12-02 18:45:28 +00:00
|
|
|
procedure LoadFile(const AFileName: String);
|
|
|
|
procedure LoadFromIni;
|
2022-03-07 21:03:56 +00:00
|
|
|
procedure LoadXMPTags;
|
2017-12-02 18:45:28 +00:00
|
|
|
procedure SaveToIni;
|
|
|
|
procedure UpdateCaption;
|
|
|
|
|
|
|
|
public
|
|
|
|
procedure BeforeRun;
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
MainForm: TMainForm;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
{$R *.lfm}
|
|
|
|
|
|
|
|
uses
|
2021-10-27 14:54:14 +00:00
|
|
|
LCLType, LCLVersion, IniFiles, Math, DateUtils, IntfGraphics,
|
2019-08-05 22:39:15 +00:00
|
|
|
fpeTags, fpeExifData, fpeIptcData;
|
2017-12-02 18:45:28 +00:00
|
|
|
|
2019-01-24 14:34:35 +00:00
|
|
|
const
|
|
|
|
TAG_ID_CAPTION = 'Tag ID';
|
2017-12-02 18:45:28 +00:00
|
|
|
|
|
|
|
function CalcIniName: String;
|
|
|
|
begin
|
|
|
|
Result := ChangeFileExt(Application.ExeName, '.ini');
|
|
|
|
end;
|
|
|
|
|
2019-08-05 22:39:15 +00:00
|
|
|
procedure RotateBitmap(const ABitmap: TBitmap; AOrientation: TExifOrientation);
|
|
|
|
Var
|
|
|
|
bmp: TBitmap;
|
|
|
|
srcImg, dstImg: TLazIntfImage;
|
|
|
|
imgHandle, imgMaskHandle: HBitmap;
|
|
|
|
i, j: integer;
|
|
|
|
w1, h1: Integer; // Input bitmap width and height diminished by 1
|
|
|
|
Begin
|
|
|
|
Assert(ABitmap <> nil, 'RotateBitmap: Input bitmap is expected not to be nil.');
|
|
|
|
|
|
|
|
if (AOrientation = eoUnknown) or (AOrientation = eoNormal) then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
w1 := ABitmap.Width - 1;
|
|
|
|
h1 := ABitmap.Height - 1;
|
|
|
|
srcImg := TLazIntfImage.Create(0, 0);
|
|
|
|
try
|
|
|
|
srcImg.LoadFromBitmap(ABitmap.Handle, ABitmap.MaskHandle);
|
|
|
|
bmp := TBitmap.Create;
|
|
|
|
try
|
|
|
|
dstImg := TLazIntfImage.Create(0, 0);
|
|
|
|
try
|
|
|
|
if AOrientation in [eoRotate90, eoRotate270, eoMirrorHorRot90, eoMirrorHorRot270] then
|
|
|
|
begin
|
|
|
|
bmp.SetSize(ABitmap.Height, ABitmap.Width);
|
|
|
|
dstImg.LoadFromBitmap(bmp.Handle, bmp.MaskHandle);
|
|
|
|
case AOrientation of
|
|
|
|
eoRotate90:
|
|
|
|
for i:=0 to w1 do
|
|
|
|
for j:=0 to h1 do
|
|
|
|
dstImg.Colors[h1-j, i] := srcImg.Colors[i, j];
|
|
|
|
eoRotate270:
|
|
|
|
for i:=0 to w1 do
|
|
|
|
for j:=0 to h1 do
|
|
|
|
dstImg.Colors[j, w1-i] := srcImg.Colors[i, j];
|
|
|
|
eoMirrorHorRot90:
|
|
|
|
for i:=0 to w1 do
|
|
|
|
for j:=0 to h1 do
|
|
|
|
dstImg.Colors[h1-j, w1-i] := srcImg.Colors[i, j];
|
|
|
|
eoMirrorHorRot270:
|
|
|
|
for i:=0 to w1 do
|
|
|
|
for j:=0 to h1 do
|
|
|
|
dstImg.Colors[j, i] := srcImg.Colors[i, j];
|
|
|
|
end;
|
|
|
|
end else
|
|
|
|
if AOrientation in [eoRotate180, eoMirrorHor, eoMirrorVert] then
|
|
|
|
begin
|
|
|
|
bmp.SetSize(ABitmap.Width, ABitmap.Height);
|
|
|
|
dstImg.LoadFromBitmap(bmp.Handle, bmp.MaskHandle);
|
|
|
|
case AOrientation of
|
|
|
|
eoRotate180:
|
|
|
|
for i:=0 to w1 do
|
|
|
|
for j:=0 to h1 do
|
|
|
|
dstImg.Colors[w1-i, h1-j] := srcImg.Colors[i, j];
|
|
|
|
eoMirrorHor:
|
|
|
|
for j:=0 to h1 do
|
|
|
|
for i:=0 to w1 do
|
|
|
|
dstImg.Colors[w1-i, j] := srcImg.Colors[i, j];
|
|
|
|
eoMirrorVert:
|
|
|
|
for i:=0 to w1 do
|
|
|
|
for j:=0 to h1 do
|
|
|
|
dstImg.Colors[i, h1-j] := srcImg.Colors[i, j];
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
dstImg.CreateBitmaps(imgHandle, imgMaskHandle, false);
|
|
|
|
bmp.Handle := ImgHandle;
|
|
|
|
bmp.MaskHandle := ImgMaskHandle;
|
|
|
|
finally
|
|
|
|
dstImg.Free;
|
|
|
|
end;
|
|
|
|
ABitmap.Assign(bmp);
|
|
|
|
finally
|
|
|
|
bmp.Free;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
srcImg.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2017-12-02 18:45:28 +00:00
|
|
|
|
|
|
|
{ TMainForm }
|
|
|
|
|
|
|
|
procedure TMainForm.BeforeRun;
|
|
|
|
begin
|
|
|
|
LoadFromIni;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.FormCreate(Sender: TObject);
|
|
|
|
begin
|
2021-04-27 14:48:09 +00:00
|
|
|
{$IF LCL_FullVersion < 2010000}
|
|
|
|
ShellListView.SmallImages := ImageList;
|
|
|
|
ShellTreeView.Images := ImageList;
|
|
|
|
{$ENDIF}
|
2022-03-07 12:10:52 +00:00
|
|
|
|
|
|
|
// XMPListView.ControlStyle:= XMPListView.Controlstyle + [csOpaque];
|
2017-12-02 18:45:28 +00:00
|
|
|
end;
|
|
|
|
|
2018-12-29 13:15:37 +00:00
|
|
|
procedure TMainForm.BtnChangeDateClick(Sender: TObject);
|
|
|
|
var
|
|
|
|
lTag: TTag;
|
|
|
|
dt: TDateTime;
|
|
|
|
fn: String;
|
|
|
|
begin
|
|
|
|
if (FImgInfo = nil) or (FImgInfo.ExifData = nil) then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
if not TryStrToDateTime(EdChangeDate.Text, dt) then begin
|
|
|
|
MessageDlg('No valid date/time. Use your locale settings.', mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
lTag := FImgInfo.ExifData.TagByName['DateTimeOriginal'];
|
|
|
|
if lTag <> nil then
|
|
|
|
TDateTimeTag(lTag).AsDateTime := dt;
|
|
|
|
|
|
|
|
lTag := FImgInfo.ExifData.TagByName['DateTimeDigitized'];
|
|
|
|
if lTag <> nil then
|
|
|
|
TDateTimeTag(lTag).AsDateTime := dt;
|
|
|
|
|
|
|
|
lTag := FImgInfo.ExifData.TagByName['DateTime'];
|
|
|
|
if lTag <> nil then
|
|
|
|
TDateTimeTag(lTag).AsDateTime := dt;
|
|
|
|
|
|
|
|
fn := FImgInfo.FileName;
|
|
|
|
fn := ChangeFileExt(fn, '') + '_modified' + ExtractFileExt(fn);
|
|
|
|
FImgInfo.SaveToFile(fn);
|
|
|
|
end;
|
|
|
|
|
2022-03-07 21:03:56 +00:00
|
|
|
procedure TMainForm.btnApplyChangesXMPClick(Sender: TObject);
|
|
|
|
var
|
|
|
|
ms: TMemoryStream;
|
|
|
|
begin
|
|
|
|
if Assigned(FImgInfo) and FImgInfo.HasXMP then
|
|
|
|
begin
|
|
|
|
ms := TMemoryStream.Create;
|
|
|
|
try
|
|
|
|
XMPSynEdit.Lines.SaveToStream(ms);
|
|
|
|
ms.Position := 0;
|
|
|
|
FImgInfo.XMPData.LoadFromStream(ms);
|
|
|
|
LoadXMPTags;
|
|
|
|
finally
|
|
|
|
ms.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.btnSaveXMPClick(Sender: TObject);
|
|
|
|
var
|
|
|
|
fn: String;
|
|
|
|
begin
|
|
|
|
if Assigned(FImgInfo) then
|
|
|
|
begin
|
|
|
|
fn := FImgInfo.FileName;
|
|
|
|
fn := ChangeFileExt(fn, '') + '_modified' + ExtractFileExt(fn);
|
|
|
|
FImgInfo.SaveToFile(fn);
|
|
|
|
ShowMessage('Modified image saved as ' + fn);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2019-01-24 14:34:35 +00:00
|
|
|
procedure TMainForm.CbShowTagIDsChange(Sender: TObject);
|
|
|
|
var
|
|
|
|
c: TListColumn;
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
TagListView.BeginUpdate;
|
|
|
|
try
|
|
|
|
c := nil;
|
|
|
|
for i:=0 to TagListView.Columns.Count-1 do
|
|
|
|
if TagListView.Columns[i].Caption = TAG_ID_CAPTION then begin
|
|
|
|
c := TagListView.Columns[i];
|
|
|
|
break;
|
|
|
|
end;
|
|
|
|
if c <> nil then
|
|
|
|
c.Visible := CbShowTagIDs.Checked;;
|
|
|
|
finally
|
|
|
|
TagListView.EndUpdate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2017-12-02 18:45:28 +00:00
|
|
|
procedure TMainForm.FormDestroy(Sender: TObject);
|
|
|
|
begin
|
|
|
|
try
|
|
|
|
SaveToIni;
|
|
|
|
except
|
|
|
|
end;
|
|
|
|
FImgInfo.Free;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.LoadFile(const AFileName: String);
|
|
|
|
var
|
|
|
|
lTag: TTag;
|
|
|
|
item: TListItem;
|
|
|
|
i: Integer;
|
|
|
|
ms: TMemoryStream;
|
2019-01-26 00:15:13 +00:00
|
|
|
suffix: String;
|
2019-08-05 22:39:15 +00:00
|
|
|
crs: TCursor;
|
2017-12-02 18:45:28 +00:00
|
|
|
begin
|
2017-12-06 22:12:10 +00:00
|
|
|
FImageLoaded := false;
|
2020-02-17 18:52:45 +00:00
|
|
|
FFileName := AFileName;
|
2017-12-06 22:12:10 +00:00
|
|
|
Image.Picture.Clear;
|
|
|
|
|
2017-12-02 18:45:28 +00:00
|
|
|
TagListView.Items.BeginUpdate;
|
|
|
|
try
|
|
|
|
TagListView.Clear;
|
|
|
|
FImgInfo.Free;
|
|
|
|
FImgInfo := TImgInfo.Create;
|
|
|
|
try
|
|
|
|
try
|
2017-12-07 11:35:50 +00:00
|
|
|
if CbDecodeMakerNotes.Checked then
|
|
|
|
FImgInfo.MetadataKinds := FImgInfo.MetadataKinds + [mdkExif] - [mdkExifNoMakerNotes]
|
|
|
|
else
|
|
|
|
FImgInfo.MetadataKinds := FImgInfo.MetadataKinds - [mdkExif] + [mdkExifNoMakerNotes];
|
2022-03-07 21:03:56 +00:00
|
|
|
if CbProcessXMP.Checked then
|
|
|
|
FImgInfo.MetadataKinds := FImgInfo.MetaDataKinds + [mdkXMP]
|
|
|
|
else
|
|
|
|
FImgInfo.MetadataKinds := FImgInfo.MetaDataKinds - [mdkXMP];
|
2017-12-02 18:45:28 +00:00
|
|
|
FImgInfo.LoadFromFile(AFileName);
|
2017-12-06 22:12:10 +00:00
|
|
|
Messages.Hide;
|
2017-12-02 18:45:28 +00:00
|
|
|
except
|
2017-12-06 22:12:10 +00:00
|
|
|
on E:EFpExif do begin
|
|
|
|
Messages.Lines.Text := E.Message;
|
|
|
|
Messages.Show;
|
|
|
|
end;
|
2017-12-02 18:45:28 +00:00
|
|
|
end;
|
|
|
|
if FImgInfo.HasExif then begin
|
2019-08-05 22:39:15 +00:00
|
|
|
FImageOrientation := FImgInfo.ExifData.ImgOrientation;
|
2017-12-02 18:45:28 +00:00
|
|
|
FImgInfo.ExifData.ExportOptions := FImgInfo.ExifData.ExportOptions + [eoTruncateBinary];
|
|
|
|
for i := 0 to FImgInfo.ExifData.TagCount-1 do begin
|
|
|
|
lTag := FImgInfo.ExifData.TagByIndex[i];
|
2019-01-26 00:15:13 +00:00
|
|
|
|
|
|
|
if lTag is TMakerNoteStringTag then
|
|
|
|
suffix := ':' + IntToStr(TMakerNoteStringTag(lTag).Index)
|
|
|
|
else if lTag is TMakerNoteIntegerTag then
|
|
|
|
suffix := ':' + IntToStr(TMakerNoteIntegerTag(lTag).Index)
|
|
|
|
else if lTag is TMakerNoteFloatTag then
|
|
|
|
suffix := ':' + IntToStr(TMakerNoteFloatTag(lTag).Index)
|
|
|
|
else
|
|
|
|
suffix := '';
|
|
|
|
|
|
|
|
if lTag is TVersionTag then
|
|
|
|
TVersionTag(lTag).Separator := '.';
|
2017-12-02 18:45:28 +00:00
|
|
|
item := TagListView.Items.Add;
|
|
|
|
item.Data := lTag;
|
|
|
|
item.Caption := 'EXIF.' + NiceGroupNames[lTag.Group];
|
2019-01-24 14:34:35 +00:00
|
|
|
if CbShowParentTagID.Checked then
|
2019-01-26 00:15:13 +00:00
|
|
|
item.SubItems.Add(Format('$%.04x:$%.04x%s', [
|
|
|
|
lTag.TagIDRec.Parent, lTag.TagIDRec.Tag, suffix]))
|
2019-01-24 14:34:35 +00:00
|
|
|
else
|
|
|
|
Item.SubItems.Add(Format('$%.04x', [lTag.TagIDRec.Tag]));
|
2017-12-02 18:45:28 +00:00
|
|
|
item.SubItems.Add(lTag.Description);
|
|
|
|
item.SubItems.Add(lTag.AsString);
|
|
|
|
end;
|
2018-12-29 13:15:37 +00:00
|
|
|
|
|
|
|
lTag := FImgInfo.ExifData.TagByName['DateTimeOriginal'];
|
|
|
|
if lTag <> nil then
|
|
|
|
EdChangeDate.Text := DateTimeToStr(TDateTimeTag(lTag).AsDateTime)
|
|
|
|
else
|
|
|
|
EdChangeDate.Text := '';
|
|
|
|
DateTimePanel.Show;
|
|
|
|
end else
|
|
|
|
DateTimePanel.Hide;
|
2022-03-07 12:10:52 +00:00
|
|
|
|
|
|
|
if FImgInfo.HasXMP then begin
|
|
|
|
ms := TMemoryStream.Create;
|
2022-03-07 21:03:56 +00:00
|
|
|
try
|
|
|
|
FImgInfo.XMPData.SaveToStream(ms);
|
|
|
|
ms.Position := 0;
|
|
|
|
XMPSynEdit.Lines.LoadFromStream(ms);
|
|
|
|
LoadXMPTags;
|
|
|
|
finally
|
|
|
|
ms.Free;
|
2022-03-07 12:10:52 +00:00
|
|
|
end;
|
2022-03-07 21:03:56 +00:00
|
|
|
end else
|
|
|
|
begin
|
|
|
|
XMPListView.Clear;
|
|
|
|
XMPSynEdit.Clear;
|
2022-03-07 12:10:52 +00:00
|
|
|
end;
|
2018-12-29 13:15:37 +00:00
|
|
|
|
2017-12-02 18:45:28 +00:00
|
|
|
if FImgInfo.HasIptc then begin
|
|
|
|
for i := 0 to FImgInfo.IptcData.TagCount-1 do begin
|
|
|
|
lTag := FImgInfo.IptcData.TagByIndex[i];
|
|
|
|
item := TagListView.Items.Add;
|
|
|
|
item.Data := lTag;
|
|
|
|
item.Caption := 'IPTC';
|
|
|
|
item.SubItems.Add(lTag.Description);
|
|
|
|
item.SubItems.Add(lTag.AsString);
|
|
|
|
end;
|
|
|
|
end;
|
2019-08-05 22:39:15 +00:00
|
|
|
|
2019-11-21 15:11:12 +00:00
|
|
|
if FImgInfo.HasThumbnail then begin
|
2017-12-02 18:45:28 +00:00
|
|
|
ms := TMemoryStream.Create;
|
|
|
|
try
|
2019-11-21 15:11:12 +00:00
|
|
|
FImgInfo.SaveThumbnailToStream(ms);
|
2017-12-02 18:45:28 +00:00
|
|
|
ms.Position := 0;
|
|
|
|
PreviewImage.Picture.LoadFromStream(ms);
|
2019-08-05 22:39:15 +00:00
|
|
|
RotateBitmap(PreviewImage.Picture.Bitmap, FImageOrientation);
|
2017-12-02 18:45:28 +00:00
|
|
|
finally
|
|
|
|
ms.Free;
|
|
|
|
end;
|
2017-12-06 22:12:10 +00:00
|
|
|
end else
|
|
|
|
PreviewImage.Picture.Clear;
|
|
|
|
|
|
|
|
if FImgInfo.HasWarnings then begin
|
|
|
|
Messages.Lines.Text := FImgInfo.Warnings;
|
|
|
|
Messages.Show;
|
|
|
|
end;
|
|
|
|
|
2023-10-28 23:14:27 +00:00
|
|
|
if PageControl.ActivePage = PgImage then begin
|
2019-08-05 22:39:15 +00:00
|
|
|
crs := Screen.Cursor;
|
|
|
|
try
|
|
|
|
Screen.Cursor := crHourglass;
|
|
|
|
Image.Picture.LoadFromFile(AFileName);
|
|
|
|
if Assigned(FImgInfo.ExifData) then
|
|
|
|
RotateBitmap(Image.Picture.Bitmap, FImageOrientation);
|
|
|
|
FImageLoaded := true;
|
|
|
|
finally
|
|
|
|
Screen.Cursor := crs;
|
|
|
|
end;
|
2017-12-02 18:45:28 +00:00
|
|
|
end;
|
|
|
|
except
|
2017-12-06 22:12:10 +00:00
|
|
|
on E:Exception do begin
|
|
|
|
FreeAndNil(FImgInfo);
|
|
|
|
Messages.Lines.Text := E.Message;
|
|
|
|
Messages.Show;
|
|
|
|
end;
|
2017-12-02 18:45:28 +00:00
|
|
|
end;
|
|
|
|
UpdateCaption;
|
|
|
|
finally
|
|
|
|
TagListView.Items.EndUpdate;
|
|
|
|
TagListView.Sort;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.LoadFromIni;
|
|
|
|
var
|
|
|
|
ini: TCustomIniFile;
|
|
|
|
i, W, H, L, T: Integer;
|
|
|
|
rct: TRect;
|
|
|
|
s: String;
|
2019-01-24 14:34:35 +00:00
|
|
|
b: Boolean;
|
2017-12-02 18:45:28 +00:00
|
|
|
begin
|
|
|
|
ini := TIniFile.Create(CalcIniName);
|
|
|
|
try
|
|
|
|
L := ini.ReadInteger('MainForm', 'Left', Left);
|
|
|
|
T := ini.ReadInteger('MainForm', 'Top', Top);
|
|
|
|
W := ini.ReadInteger('MainForm', 'Width', Width);
|
|
|
|
H := ini.ReadInteger('MainForm', 'Height', Height);
|
|
|
|
rct := Screen.DesktopRect;
|
|
|
|
if W > rct.Right - rct.Left then W := rct.Right - rct.Left;
|
|
|
|
if H > rct.Bottom - rct.Top then H := rct.Bottom - rct.Top;
|
|
|
|
if L < rct.Left then L := rct.Left;
|
|
|
|
if T < rct.Top then T := rct.Top;
|
|
|
|
if L+W > rct.Right then L := rct.Right - W;
|
|
|
|
if T+H > rct.Bottom then T := rct.Bottom - H;
|
|
|
|
SetBounds(L, T, W, H);
|
|
|
|
|
|
|
|
s := ini.ReadString('MainForm', 'Path', '');
|
|
|
|
if s <> '' then ShellTreeView.Path := s;
|
|
|
|
|
|
|
|
w := ini.ReadInteger('MainForm', 'LeftPanelWidth', 0);
|
|
|
|
if w <> 0 then ShellPanel.Width := w;
|
|
|
|
|
|
|
|
h := ini.ReadInteger('MainForm', 'TreeHeight', 0);
|
|
|
|
if h <> 0 then ShellTreeView.Height := h;
|
|
|
|
|
2023-10-28 23:14:27 +00:00
|
|
|
PageControl.ActivePageIndex := ini.ReadInteger('MainForm', 'PageControl', 0);
|
|
|
|
|
2017-12-02 18:45:28 +00:00
|
|
|
for i:=0 to TagListView.Columns.Count-1 do begin
|
|
|
|
w := ini.ReadInteger('TagList', 'ColWidth'+IntToStr(i), 0);
|
|
|
|
if w <> 0 then
|
|
|
|
TagListView.Columns[i].Width := w;
|
|
|
|
end;
|
2019-01-24 14:34:35 +00:00
|
|
|
|
|
|
|
b := ini.ReadBool('TagList', 'ShowTagIDs', true);
|
|
|
|
for i:=0 to TagListView.Columns.Count-1 do
|
|
|
|
if TagListView.Columns[i].Caption = TAG_ID_CAPTION then
|
|
|
|
TagListView.Columns[2].Visible := b;
|
|
|
|
CbShowTagIDs.Checked := b;
|
|
|
|
|
|
|
|
b := ini.ReadBool('TagList', 'ShowParentTagID', false);
|
|
|
|
CbShowParentTagID.Checked := b;
|
|
|
|
|
2017-12-02 18:45:28 +00:00
|
|
|
finally
|
|
|
|
ini.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-03-07 21:03:56 +00:00
|
|
|
procedure TMainForm.LoadXMPTags;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
item: TListItem;
|
|
|
|
begin
|
|
|
|
XMPListView.Items.BeginUpdate;
|
|
|
|
try
|
|
|
|
XMPListView.Items.Clear;
|
|
|
|
for i := 0 to FImgInfo.XMPData.TagCount-1 do begin
|
|
|
|
item := XMPListView.Items.Add;
|
|
|
|
item.Caption := FImgInfo.XMPData.TagName[i];
|
|
|
|
item.SubItems.Add(FImgInfo.XMPData.TagByIndex[i]);
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
XMPListView.Items.EndUpdate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2023-10-28 23:14:27 +00:00
|
|
|
procedure TMainForm.PageControlChange(Sender: TObject);
|
2019-08-05 22:39:15 +00:00
|
|
|
var
|
|
|
|
crs: TCursor;
|
2017-12-06 22:12:10 +00:00
|
|
|
begin
|
|
|
|
if FImgInfo = nil then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
if not FImageLoaded then begin
|
2019-08-05 22:39:15 +00:00
|
|
|
crs := Screen.Cursor;
|
|
|
|
try
|
|
|
|
Screen.Cursor := crHourglass;
|
2020-02-17 18:52:45 +00:00
|
|
|
Image.Picture.LoadFromFile(FFileName);
|
|
|
|
if Assigned(FImgInfo) and Assigned(FImgInfo.ExifData) then
|
2019-08-05 22:39:15 +00:00
|
|
|
RotateBitmap(Image.Picture.Bitmap, FImgInfo.ExifData.ImgOrientation);
|
|
|
|
FImageLoaded := true;
|
|
|
|
finally
|
|
|
|
Screen.Cursor := crs;
|
|
|
|
end;
|
2017-12-06 22:12:10 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2017-12-02 18:45:28 +00:00
|
|
|
procedure TMainForm.SaveToIni;
|
|
|
|
var
|
|
|
|
ini: TCustomIniFile;
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
ini := TIniFile.Create(CalcIniName);
|
|
|
|
try
|
|
|
|
if WindowState = wsNormal then begin
|
|
|
|
ini.WriteInteger('MainForm', 'Left', Left);
|
|
|
|
ini.WriteInteger('MainForm', 'Top', Top);
|
|
|
|
ini.WriteInteger('MainForm', 'Width', Width);
|
|
|
|
ini.WriteInteger('MainForm', 'Height', Height);
|
|
|
|
end;
|
|
|
|
ini.WriteString('MainForm', 'Path', ShellTreeView.Path);
|
|
|
|
ini.WriteInteger('MainForm', 'LeftPanelWidth', ShellPanel.Width);
|
|
|
|
ini.WriteInteger('MainForm', 'TreeHeight', ShellTreeView.Height);
|
2023-10-28 23:14:27 +00:00
|
|
|
ini.WriteInteger('MainForm', 'PageControl', PageControl.ActivePageIndex);
|
2019-01-24 14:34:35 +00:00
|
|
|
|
|
|
|
ini.WriteBool('TagList', 'ShowTagIDs', CbShowTagIDs.Checked);
|
|
|
|
ini.WriteBool('TagList', 'ShowParentTagID', CbShowParentTagID.Checked);
|
2017-12-02 18:45:28 +00:00
|
|
|
for i:=0 to TagListView.Columns.Count-1 do
|
|
|
|
ini.WriteInteger('TagList', 'ColWidth'+IntToStr(i), TagListView.Columns[i].Width);
|
|
|
|
finally
|
|
|
|
ini.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.ShellListViewSelectItem(Sender: TObject; Item: TListItem;
|
|
|
|
Selected: Boolean);
|
|
|
|
var
|
|
|
|
dir, fn: String;
|
|
|
|
begin
|
|
|
|
if Selected then
|
|
|
|
begin
|
|
|
|
dir := ShellTreeView.GetPathFromNode(ShellTreeView.Selected);
|
|
|
|
fn := Item.Caption;
|
|
|
|
LoadFile(dir + fn);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.ShellTreeViewGetImageIndex(Sender: TObject; Node: TTreeNode);
|
|
|
|
begin
|
|
|
|
if Node = nil then
|
|
|
|
exit;
|
|
|
|
if Node.Level = 0 then
|
|
|
|
Node.ImageIndex := 0
|
|
|
|
else
|
|
|
|
if Node.Expanded then
|
|
|
|
Node.ImageIndex := 2
|
|
|
|
else
|
|
|
|
Node.ImageIndex := 1;
|
|
|
|
Node.SelectedIndex := Node.ImageIndex;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.ShellTreeViewSelectionChanged(Sender: TObject);
|
|
|
|
begin
|
|
|
|
TagListView.Items.Clear;
|
|
|
|
PreviewImage.Picture.Assign(nil);
|
|
|
|
ShellTreeViewGetImageIndex(nil, ShellTreeView.Selected);
|
|
|
|
FreeAndNil(FImgInfo);
|
|
|
|
UpdateCaption;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.TagListViewCompare(Sender: TObject; Item1, Item2: TListItem;
|
|
|
|
Data: Integer; var Compare: Integer);
|
|
|
|
var
|
|
|
|
tag1, tag2: TTag;
|
|
|
|
begin
|
|
|
|
tag1 := TTag(Item1.Data);
|
|
|
|
tag2 := TTag(Item2.Data);
|
|
|
|
Compare := CompareValue(ord(tag1.Group), ord(tag2.Group));
|
|
|
|
if Compare = 0 then
|
|
|
|
Compare := CompareText(Item1.SubItems[0], Item2.SubItems[0]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.TagListViewSelectItem(Sender: TObject; Item: TListItem;
|
|
|
|
Selected: Boolean);
|
|
|
|
const
|
|
|
|
{ TTagType:
|
|
|
|
ttUInt8 = 1, ttString, ttUInt16, ttUInt32, ttURational,
|
|
|
|
ttSInt8, ttBinary, ttSInt16, ttSInt32, ttSRational,
|
|
|
|
ttSingle, ttDouble,
|
|
|
|
ttIFD // rarely used, in Olympus maker notes
|
|
|
|
}
|
|
|
|
TAGTYPE_NAMES: array[TTagType] of string = (
|
|
|
|
'BYTE', 'ASCII', 'UINT16', 'UINT32', 'URATIONAL',
|
|
|
|
'SBYTE', 'BINARY', 'SINT16', 'SINT32', 'SRATIONAL',
|
|
|
|
'SINGLE', 'DOUBLE',
|
|
|
|
'IFD'
|
|
|
|
);
|
|
|
|
var
|
|
|
|
lTag: TTag;
|
|
|
|
tagID: TTagIDRec;
|
|
|
|
begin
|
|
|
|
if Selected then begin
|
|
|
|
lTag := TTag(Item.Data);
|
|
|
|
if lTag <> nil then begin
|
|
|
|
tagID := TTagIDRec(lTag.TagID);
|
|
|
|
Statusbar1.Panels[0].Text := Format('ID %d [$%.4x]', [tagID.Tag, tagID.Tag]);
|
|
|
|
Statusbar1.Panels[1].Text := Format('Parent %d [$%.4x]', [tagID.Parent, tagID.Parent]);
|
|
|
|
Statusbar1.Panels[2].Text := 'Name: ' + lTag.Name;
|
|
|
|
Statusbar1.Panels[3].Text := 'Type: ' + TAGTYPE_NAMES[lTag.TagType];
|
|
|
|
Statusbar1.Panels[4].Text := 'Elements: ' + IntToStr(lTag.Count);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
Statusbar1.Panels[0].Text := '';
|
|
|
|
Statusbar1.Panels[1].Text := '';
|
|
|
|
Statusbar1.Panels[2].Text := '';
|
|
|
|
Statusbar1.Panels[3].Text := '';
|
|
|
|
Statusbar1.Panels[4].Text := '';
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMainForm.UpdateCaption;
|
|
|
|
begin
|
|
|
|
if FImgInfo <> nil then
|
|
|
|
FileNameInfo.Caption := Format(
|
|
|
|
'File: %s' + LineEnding +
|
|
|
|
'Size: %d kB' + LineEnding +
|
|
|
|
'Date: %s', [
|
|
|
|
FImgInfo.Filename, FImgInfo.FileSize div 1024, DateTimeToStr(FImgInfo.FileDate)])
|
|
|
|
else
|
2020-02-17 18:52:45 +00:00
|
|
|
if FFileName <> '' then
|
|
|
|
FilenameInfo.Caption := Format('File: %s', [FFileName])
|
|
|
|
else
|
|
|
|
FilenameInfo.Caption := '< no file >';
|
2017-12-02 18:45:28 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|
|
|