lazarus/examples/imgviewer/frmmain.pas

540 lines
13 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
* *
***************************************************************************
}
unit frmmain;
{$MODE Delphi}
interface
uses
SysUtils, Classes, Controls, Forms, LazFileUtils, LazUTF8,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, ActnList, Menus, LCLType,
fpreadtiff {adds TIFF format read support to TImage};
type
{ TMainForm }
TMainForm = class(TForm)
MainMenu1: TMainMenu;
ToolBar1: TToolBar;
ActionList1: TActionList;
AOpen: TAction;
AOpenDir: TAction;
AExit: TAction;
LBFiles: TListBox;
SPImage: TSplitter;
File1: TMenuItem;
MIOpen: TMenuItem;
MIOPenDir: TMenuItem;
N1: TMenuItem;
MIQuit: TMenuItem;
TBOPen: TToolButton;
TBOpenDir: TToolButton;
ILMain: TImageList;
ODImage: TOpenDialog;
AClear: TAction;
MIOpenDirRec: TMenuItem;
MIClear: TMenuItem;
OpenDirRecursively: TAction;
TBOpenDirRec: TToolButton;
ADoubleSize: TAction;
MImage: TMenuItem;
D1: TMenuItem;
AHalfSize: TAction;
MIHalfSize: TMenuItem;
PImage: TPanel;
ScrollBox1: TScrollBox;
IMain: TImage;
ANextImage: TAction;
APreviousImage: TAction;
ANextImageDir: TAction;
APrevImageDir: TAction;
MINextImage: TMenuItem;
PreviousImage1: TMenuItem;
Nextimagedirectory1: TMenuItem;
Previousimagedirectory1: TMenuItem;
ToolButton1: TToolButton;
ToolButton4: TToolButton;
TBPRev: TToolButton;
TBNext: TToolButton;
TBPRevDir: TToolButton;
TBNextDir: TToolButton;
TBDoubleSize: TToolButton;
TBHalfSize: TToolButton;
ToolButton3: TToolButton;
N2: TMenuItem;
OpenDialog1: TOpenDialog;
procedure AOpenExecute(Sender: TObject);
procedure LBFilesClick(Sender: TObject);
procedure AOpenDirExecute(Sender: TObject);
procedure AExitExecute(Sender: TObject);
procedure OpenDirRecursivelyExecute(Sender: TObject);
procedure AClearExecute(Sender: TObject);
procedure ADoubleSizeExecute(Sender: TObject);
procedure AHalfSizeExecute(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
procedure FormShow(Sender: TObject);
procedure ANextImageExecute(Sender: TObject);
procedure APreviousImageExecute(Sender: TObject);
procedure ANextImageDirExecute(Sender: TObject);
procedure APrevImageDirExecute(Sender: TObject);
private
FImageScale: double;
procedure AddFile(FileName: string; ShowFile: boolean);
procedure ShowFile(Index: integer);
procedure AddDir(Directory: string; Recurse: boolean);
procedure RescaleImage(NewScale: double);
procedure NextImage;
procedure PreviousImage;
procedure NextImageDir;
procedure PreviousImageDir;
function NextDirIndex(Direction: integer): integer;
procedure ShiftImageIndex(MoveBy: integer);
procedure ProcessCommandLine;
procedure DoError(Msg: string; Args: array of const);
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
const
ImageTypes = '|.jpg|.jpeg|.bmp|.xpm|.png';
resourcestring
SSelectImageDir = 'Select directory to add images from';
SSelectImageDirRec = 'Select directory to recursively add images from';
SImageViewer = 'Image viewer';
SErrNeedArgument = 'Option at position%d (%s) needs an argument';
{ [] }
procedure TMainForm.AOpenExecute(Sender: TObject);
var
I: integer;
begin
with ODImage do
begin
if Execute then
for I := 0 to Files.Count - 1 do
AddFile(Files[I], (I = 0));
end;
end;
procedure TMainForm.AddFile(FileName: string; ShowFile: boolean);
// Adds a file to the listbox and displays it if ShowFile is true
var
Index: integer;
begin
ShowFile := ShowFile or (LBFiles.Items.Count = 0);
Index := LBFiles.Items.Add(FileName);
if ShowFile then
self.ShowFile(Index);
end;
procedure TMainForm.ShowFile(Index: integer);
// Loads file and displays it into the IMain TImage
var
LoadOK: boolean;
begin
if Index = -1 then
begin
IMain.Picture := nil;
Caption := SImageViewer;
end
else
repeat
try
LoadOK := false;
IMain.Align := AlClient;
Imain.Stretch := false;
FImageScale := 1.0;
IMain.Picture.LoadFromFile(LBFiles.Items[Index]);
Caption := SImageViewer + ' (' + LBFiles.Items[Index] + ')';
LoadOK := true;
except
// If we can't load the image, try next file unless we're at the end
if Index < LBFiles.Items.Count - 1 then
Inc(Index)
else
Index := -1;
end
until LoadOK or (Index = -1);
// Now synchronize our listbox to the file we loaded:
with LBFiles do
begin
if Index <> ItemIndex then
LBFiles.ItemIndex := Index;
{ If Not ItemVisible(ItemIndex) then
MakeCurrentVisible;}
end;
end;
procedure TMainForm.LBFilesClick(Sender: TObject);
begin
ShowFile(LBFiles.ItemIndex);
end;
procedure TMainForm.AOpenDirExecute(Sender: TObject);
// Open a single directory (non recursively)
var
Dir: string;
WasSorted: boolean;
begin
if SelectDirectory(SSelectImageDir, '/', Dir, true) then
begin
Screen.BeginWaitCursor; //Show user he may have to wait for big directories
try
LBFiles.Items.BeginUpdate; //Indicate to the listbox that we're doing a lengthy operation
WasSorted:=LBFiles.Sorted;
LBFiles.Sorted:=true;
AddDir(Dir, false);
LBFiles.Sorted:=WasSorted;
finally
LBFiles.Items.EndUpdate;
Screen.EndWaitCursor;
end;
end;
end;
procedure TMainForm.AddDir(Directory: string; Recurse: boolean);
var
Info: TSearchRec;
Ext: string;
begin
Directory := IncludeTrailingPathDelimiter(Directory);
if FindFirstUTF8(Directory + '*.*', 0, Info) = 0 then
try
repeat
Ext := ExtractFileExt(Info.Name);
// Support opening tiff files as well as the built-in image types.
// Note: requires fpreadtiff in the uses clause to work.
if Pos(lowercase('|'+Ext+'|'), ImageTypes+'|.tif|.tiff|') <> 0 then
AddFile(Directory + Info.Name, false);
until (FindNextUTF8(Info) <> 0)
finally
FindCloseUTF8(Info);
end;
if Recurse then
if FindFirstUTF8(Directory + '*', faDirectory, Info) = 0 then
try
repeat
if (Info.Name <> '.') and (Info.Name <> '') and (info.Name <> '..') and ((Info.Attr and faDirectory) <> 0) then
AddDir(Directory + Info.Name, true);
until (FindNextUTF8(Info) <> 0)
finally
FindCloseUTF8(Info);
end;
end;
procedure TMainForm.AExitExecute(Sender: TObject);
begin
Close;
end;
procedure TMainForm.OpenDirRecursivelyExecute(Sender: TObject);
// Open a directory recursively
var
Dir: string;
WasSorted: boolean;
begin
if SelectDirectory(SSelectImageDirRec, '/', Dir, true) then
begin
Screen.BeginWaitCursor; //Show user he may have to wait for big directories
try
LBFiles.Items.BeginUpdate; //Indicate to the listbox that we're doing a lengthy operation
WasSorted:=LBFiles.Sorted;
LBFiles.Sorted:=true;
AddDir(Dir, true);
LBFiles.Sorted:=WasSorted;
finally
LBFiles.Items.EndUpdate;
Screen.EndWaitCursor;
end;
end;
end;
procedure TMainForm.AClearExecute(Sender: TObject);
begin
LBFiles.ItemIndex := -1;
ShowFile(-1);
LBFiles.Items.Clear;
end;
procedure TMainForm.ADoubleSizeExecute(Sender: TObject);
begin
RescaleImage(2.0);
end;
procedure TMainForm.RescaleImage(NewScale: double);
var
OrgWidth, OrgHeight: integer;
Rect: TRect;
begin
OrgWidth := IMain.Picture.Bitmap.Width;
OrgHeight := IMain.Picture.Bitmap.Height;
FImageScale := FImageScale * NewScale;
Rect := IMain.BoundsRect;
Rect.Right := Rect.Left + Round(OrgWidth * FImageScale);
Rect.Bottom := Rect.Top + Round(OrgHeight * FImageScale);
Imain.Align := AlNone;
IMain.BoundsRect := Rect;
Imain.Stretch := true;
end;
procedure TMainForm.AHalfSizeExecute(Sender: TObject);
begin
RescaleImage(0.5);
end;
procedure TMainForm.NextImage;
begin
ShiftImageIndex(1);
end;
procedure TMainForm.PreviousImage;
begin
ShiftImageIndex(-1);
end;
procedure TMainForm.ShiftImageIndex(MoveBy: integer);
var
ImageIndex: integer;
begin
ImageIndex := LBFiles.ItemIndex;
ImageIndex := ImageIndex + MoveBy;
if ImageIndex < 0 then
ImageIndex := LBFiles.Items.Count - 1;
if ImageIndex >= LBFiles.Items.Count then
begin
ImageIndex := 0;
if LBFiles.Items.Count = 0 then
ImageIndex := -1;
end;
ShowFile(ImageIndex);
end;
procedure TMainForm.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
begin
// todo: write help about with at least key combinations!
if (shift = [ssShift]) or (shift = [ssAlt]) then
begin
if (key = VK_Prior) then
begin
// Page Up
RescaleImage(2.0);
Key := 0;
end
else if (key = VK_Next) then
begin
// Page Down
RescaleImage(0.5);
Key := 0;
end
else if (key = VK_Left) then
begin
// Left
PreviousImage;
Key := 0;
end
else if (key = VK_right) then
begin
// Right
NextImage;
Key := 0;
end;
end
else if (shift = []) then
begin
if Key = VK_UP then
begin
// Up
Previousimage;
Key := 0;
end
else if Key = VK_DOWN then
begin
// Down
NextImage;
Key := 0;
end;
end;
end;
procedure TMainForm.DoError(Msg: string; Args: array of const);
begin
ShowMessage(Format(Msg, Args));
end;
procedure TMainForm.ProcessCommandLine;
function CheckOption(Index: integer; Short, Long: string): boolean;
var
O: string;
begin
O := ParamStrUTF8(Index);
Result := (O = '-' + short) or (copy(O, 1, Length(Long) + 3) = ('--' + long + '='));
end;
function OptionArg(var Index: integer): string;
var
P: integer;
begin
if (Length(ParamStrUTF8(Index)) > 1) and (ParamStrUTF8(Index)[2] <> '-') then
begin
if Index < ParamCount then
begin
Inc(Index);
Result := ParamStrUTF8(Index);
end
else
DoError(SErrNeedArgument, [Index, ParamStrUTF8(Index)]);
end
else if length(ParamStrUTF8(Index)) > 2 then
begin
P := Pos('=', ParamStrUTF8(Index));
if (P = 0) then
DoError(SErrNeedArgument, [Index, ParamStrUTF8(Index)])
else
begin
Result := ParamStrUTF8(Index);
Delete(Result, 1, P);
end;
end;
end;
var
I: integer;
S: string;
FRecursive: boolean;
begin
FRecursive := false;
I := 0;
while (I < ParamCount) do
begin
Inc(I);
if CheckOption(I, 'r', 'recursive') then
FRecursive := true
else
begin
S := ParamStrUTF8(I);
Screen.BeginWaitCursor; //Show user he may have to wait
try
if DirectoryExistsUTF8(S) then
AddDir(ExpandFileNameUTF8(S), FRecursive)
else if FileExistsUTF8(S) then
AddFile(ExpandFileNameUTF8(S), LBFiles.Items.Count = 0);
finally
Screen.EndWaitCursor;
end;
end;
end;
end;
procedure TMainForm.FormShow(Sender: TObject);
begin
ProcessCommandLine;
end;
procedure TMainForm.NextImageDir;
var
Index: integer;
begin
Index := NextDirIndex(1);
ShowFile(Index);
end;
function TMainForm.NextDirIndex(Direction: integer): integer;
var
Dir: string;
begin
Result := -1;
if LBFiles.ItemIndex = -1 then
Exit;
Result := LBFiles.ItemIndex;
Dir := ExtractFilePath(LBFiles.Items[Result]);
repeat
Result := Result + Direction;
until ((Result = -1) or (Result >= LBFiles.Items.Count)) or (Dir <> ExtractFilePath(LBFiles.Items[Result]));
if Result >= LBFiles.Items.Count then
Result := -1;
end;
procedure TMainForm.PreviousImageDir;
var
Index: integer;
begin
Index := NextDirIndex(-1);
ShowFile(Index);
end;
procedure TMainForm.ANextImageExecute(Sender: TObject);
begin
NextImage;
end;
procedure TMainForm.APreviousImageExecute(Sender: TObject);
begin
PreviousImage;
end;
procedure TMainForm.ANextImageDirExecute(Sender: TObject);
begin
NextImageDir;
end;
procedure TMainForm.APrevImageDirExecute(Sender: TObject);
begin
PreviousImageDir;
end;
end.