(************************************************************************
 *                                                                      *
 *   Ant Movie Catalog 4.x                                              *
 *   (C) 2000-2011 Antoine Potten, Mickaël Vanneufville                 *
 *   http://www.antp.be/software                                        *
 *                                                                      *
 ************************************************************************
 *                                                                      *
 *   This program 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 program 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.                       *
 *                                                                      *
 ************************************************************************)

unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, ActnList, ImgList, Menus, StdCtrls, Buttons, Clipbrd, ComCtrls,
  StdActns, Contnrs,

  ElTree, ElList, ElHeader, TB2Common, TB2Item, TB2MRU, TB2Toolbar, TB2Dock,
  TB2ToolWindow, TBX, TBXExtItems, TBXSwitcher, TBXStatusBars,

  AntJvDragDrop, AntStringList, AntCorelButton,
  AntJvSpin, AntJvEdit, AntJvExControls, AntJvToolEdit,

  framemovie, MovieClass, FileManager, ConstValues,
  FramePictureSelectionOptions,

  HTMLSubs, HtmlView, MetaFilePrinter, PrintStatusForm, PreviewForm,

  JPEG, PNGImage, AntJvGIF, rkIntegerList, rkSmartView, framemoviecustom;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

const
  strDefaultCaption = 'Ant Movie Catalog Unofficial %s [BETA] - %s';
  strDefaultTask = '%s - Ant Movie Catalog Unofficial';
  CM_UpdateView = WM_USER + 2102; // Custom Message...

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

type
  TJpegImageFix = class(TJpegImage)
  protected
    procedure Draw(ACanvas: TCanvas; const Rect: TRect); Override;
  end;

  {ThumbsViewer}
  PCacheItem = ^TCacheItem;
  TCacheItem = record
    Idx: Integer;
    Size: Integer;
    Age: TDateTime;
    Scale: Integer;
    Bmp: TBitmap;
  end;

  ThumbThread = class(TThread)
  private
    { Private declarations }
    ThumbsViewer: TrkSmartView;
    ListView1: TElTree;
    MovieList: TMovieList;

  protected
    procedure Execute; override;
  public
    constructor Create(Thumbs: TrkSmartView; Items: TElTree; Movies: TMovieList);
  end;
  {End ThumbsViewer}
  
  TMainWindow = class(TForm)
    ActionDisplayMainToolbar: TAction;
    ActionDisplayPictureToolbar: TAction;
    ActionDisplayStatusBar: TAction;
    ActionExit: TAction;
    ActionFileExport: TAction;
    ActionFileImport: TAction;
    ActionFileNew: TAction;
    ActionFileOpen: TAction;
    ActionFileOpenNoRecent: TAction;
    ActionFilePrint: TAction;
    ActionFileProperties: TAction;
    ActionFileSave: TAction;
    ActionFileSaveAs: TAction;
    ActionFindDisplay: TAction;
    ActionFindFindnext: TAction;
    ActionFindWholefield: TAction;
    ActionGroupNone: TAction;
    ActionHelpAbout: TAction;
    ActionHelpIndex: TAction;
    ActionLanguage: TAction;
    ActionList1: TActionList;
    ActionLoan: TAction;
    ActionMenuFile: TAction;
    ActionMenuGet: TAction;
    ActionMenuGroup: TAction;
    ActionMenuSort: TAction;
    ActionMenuHelp: TAction;
    ActionMenuMovie: TAction;
    ActionMenuPicture: TAction;
    ActionMenuTools: TAction;
    ActionMovieAdd: TAction;
    ActionMovieCheck: TAction;
    ActionMovieCopy: TAction;
    ActionMovieDelete: TAction;
    ActionMovieFind: TAction;
    ActionMovieImportCD: TAction;
    ActionMovieImportFiles: TAction;
    ActionMovieImportScript: TAction;
    ActionMovieNext: TAction;
    ActionMovieNumber: TAction;
    ActionMoviePaste: TAction;
    ActionMoviePictureShow: TAction;
    ActionMoviePrevious: TAction;
    ActionMovieRenumber: TAction;
    ActionMovieSearch: TAction;
    ActionMovieSelCheck: TAction;
    ActionMovieSelGroup: TAction;
    ActionMovieSelUncheck: TAction;
    ActionMovieStats: TAction;
    ActionMovieUncheck: TAction;
    ActionMovieUndo: TAction;
    ActionOptions: TAction;
    ActionPicCopy: TAction;
    ActionPicDelete: TAction;
    ActionPicSaveAs: TAction;
    ActionPicSelect: TAction;
    ActionPicUndock: TAction;
    ActionRefresh: TAction;
    ActionToolsGrid: TAction;
    ActionToolsScripting: TAction;
    ActionURLBrowse: TAction;
    ActionURLOpen: TAction;
    ActionURLCopy: TAction;
    BtnFindNext: TCorelButton;
    CBDisplayResults: TCheckBox;
    CBWholefield: TCheckBox;
    cbxField: TComboBox;
    DockBottomList: TTBXDock;
    DockImageLeft: TTBXDock;
    DockImageTop: TTBXDock;
    DockMainBottom: TTBXDock;
    DockMainLeft: TTBXDock;
    DockMainTop: TTBXDock;
    DockRightInfo: TTBXDock;
    EValue: TEdit;
    Fields: TAntStringList;
    FrmMovie: TMovieFrame;
    FrmMovieCustom: TMovieFrameCustom;
    ImageListHot: TImageList;
    ImageListNormal: TTBImageList;
    LField: TLabel;
    ListView1: TElTree;
    LValue: TLabel;
    Messages: TAntStringList;
    MnuFil__1: TTBXSeparatorItem;
    MnuFil__2: TTBXSeparatorItem;
    MnuFil__3: TTBXSeparatorItem;
    MnuFilExp: TTBXItem;
    MnuFilImp: TTBXItem;
    MnuFilNew: TTBXItem;
    MnuFilOpn: TTBXItem;
    MnuFilPrn: TTBXItem;
    MnuFilPrp: TTBXItem;
    MnuFilSaa: TTBXItem;
    MnuFilSav: TTBXItem;
    MnuFilXit: TTBXItem;
    MnuGetFil: TTBXItem;
    MnuGetScr: TTBXItem;
    MnuGrp__1: TTBXSeparatorItem;
    MnuGrpNon: TTBXItem;
    MnuHlpAbt: TTBXItem;
    MnuHlpIdx: TTBXItem;
    MnuMlp__1: TTBXSeparatorItem;
    MnuMlp__2: TTBXSeparatorItem;
    MnuMlp__3: TTBXSeparatorItem;
    MnuMlpAdd: TTBXItem;
    MnuMlpChk: TTBXItem;
    MnuMlpCpy: TTBXItem;
    MnuMlpDel: TTBXItem;
    MnuMlpGet: TTBXSubmenuItem;
    MnuMlpGrd: TTBXItem;
    MnuMlpGrp: TTBXSubmenuItem;
    MnuMlpNum: TTBXItem;
    MnuMlpPst: TTBXItem;
    MnuMlpSlc: TTBXItem;
    MnuMlpSlg: TTBXItem;
    MnuMlpSlu: TTBXItem;
    MnuMlpUch: TTBXItem;
    MnuMnuFil: TTBXSubmenuItem;
    MnuMnuHlp: TTBXSubmenuItem;
    MnuMnuMov: TTBXSubmenuItem;
    MnuMnuTls: TTBXSubmenuItem;
    MnuMov__1: TTBXSeparatorItem;
    MnuMov__2: TTBXSeparatorItem;
    MnuMovAdd: TTBXItem;
    MnuMovCpy: TTBXItem;
    MnuMovDel: TTBXItem;
    MnuMovFnd: TTBXItem;
    MnuMovGet: TTBXSubmenuItem;
    MnuMovNum: TTBXItem;
    MnuMovPic: TTBXSubmenuItem;
    MnuMovPst: TTBXItem;
    MnuMovSch: TTBXSubmenuItem;
    MnuMovUnd: TTBXItem;
    MnuMpi__1: TTBXSeparatorItem;
    MnuMpiCpy: TTBXItem;
    MnuMpiDck: TTBXItem;
    MnuMpiDel: TTBXItem;
    MnuMpiDis: TTBXItem;
    MnuMpiLoa: TTBXItem;
    MnuMpiSav: TTBXItem;
    MnuPicCpy: TTBXItem;
    MnuPicDck: TTBXItem;
    MnuPicDel: TTBXItem;
    MnuPicLoa: TTBXItem;
    MnuPicSav: TTBXItem;
    MnuTbp__1: TTBXSeparatorItem;
    MnuTbpMtb: TTBXItem;
    MnuTbpOpt: TTBXItem;
    MnuTbpPtb: TTBXItem;
    MnuTbpStb: TTBXItem;
    MnuTls__1: TTBXSeparatorItem;
    MnuTls__2: TTBXSeparatorItem;
    MnuTlsGrd: TTBXItem;
    MnuTlsGrp: TTBXSubmenuItem;
    MnuTlsSort: TTBXSubmenuItem;
    MnuTlsLng: TTBXItem;
    MnuTlsLoa: TTBXItem;
    MnuTlsOpt: TTBXItem;
    MnuTlsRen: TTBXItem;
    MnuTlsScr: TTBXItem;
    MnuTlsSta: TTBXItem;
    MnuUrlBrw: TTBXItem;
    MnuUrpOpen: TTBXItem;
    MoviePicture: TImage;
    PanelLeft: TPanel;
    PanelMovieInfos: TPanel;
    PanelPicture: TPanel;
    PopupEURL: TTBXPopupMenu;
    PopupMovieList: TTBXPopupMenu;
    PopupToolbar: TTBXPopupMenu;
    ScrollBox1: TScrollBox;
    SplitterBottomList: TSplitter;
    SplitterMovieInfos: TSplitter;
    SplitterRightInfo: TSplitter;
    StatusBar1: TTBXStatusBar;
    TBItemAbout: TTBXItem;
    TBItemExit: TTBXItem;
    TBItemFileExport: TTBXItem;
    TBItemFileImport: TTBXItem;
    TBItemFileNew: TTBXItem;
    TBItemFileOpen: TTBXItem;
    TBItemFileOpenMRU: TTBXSubmenuItem;
    TBItemFileOpenNone: TTBXItem;
    TBItemFilePrint: TTBXItem;
    TBItemFileProperties: TTBXItem;
    TBItemFileSave: TTBXItem;
    TBItemFileSaveAs: TTBXItem;
    TBItemGetInfo: TTBXSubmenuItem;
    TBItemGetInfoFiles: TTBXItem;
    TBItemGetInfoScript: TTBXItem;
    TBItemGrid: TTBXItem;
    TBItemGroup: TTBXSubmenuItem;
    TBItemHelp: TTBXItem;
    TBItemLoans: TTBXItem;
    TBItemMovieAdd: TTBXItem;
    TBItemMovieCopy: TTBXItem;
    TBItemMovieDelete: TTBXItem;
    TBItemMovieFind: TTBXItem;
    TBItemMovieNumber: TTBXItem;
    TBItemMoviePaste: TTBXItem;
    TBItemMoviePicture: TTBXItem;
    TBItemMoviePictureMenu: TTBXSubmenuItem;
    TBItemMovieUndo: TTBXItem;
    TBItemPreferences: TTBXItem;
    TBItemRenumber: TTBXItem;
    TBItemScripting: TTBXItem;
    TBItemSearch: TTBXSubmenuItem;
    TBItemStats: TTBXItem;
    TBMRUList1: TTBXMRUList;
    TBMRUListItem1: TTBXMRUListItem;
    TBMRUListItem2: TTBXMRUListItem;
    TBSeparatorFile: TTBXSeparatorItem;
    TBSeparatorGet: TTBXSeparatorItem;
    TBSeparatorHelp: TTBXSeparatorItem;
    TBSeparatorMisc: TTBXSeparatorItem;
    TBSeparatorMovie: TTBXSeparatorItem;
    TBSeparatorMovieEdit: TTBXSeparatorItem;
    TBXSwitcher1: TTBXSwitcher;
    ToolbarFind: TTBXToolWindow;
    ToolbarMain: TTBXToolbar;
    ToolbarMenu: TTBXToolbar;
    ToolbarPicture: TTBXToolbar;
    ToolbarPictureWindow: TTBXToolWindow;
    ActionHelpVersion: TAction;
    MnuHlpVer: TTBXItem;
    TBItemLanguage: TTBXItem;
    TBItemRandom: TTBXItem;
    ActionMovieRandom: TAction;
    TBItemStretchList: TTBXItem;
    ActionStretchList: TAction;
    MnuTlsStr: TTBXItem;
    MnuMovRan: TTBXItem;
    HTMLViewer: THTMLViewer;
    ActionDisplayHTML: TAction;
    TBItemDisplayHTML: TTBXItem;
    MnuTlsHTM: TTBXItem;
    ThumbsPanel: TPanel;
    ThumbsPanelBottom: TPanel;
    ThumbsSizer: TTrackBar;
    ProgressBar1: TProgressBar;
    ThumbsViewer: TrkSmartView;
    ActionDisplayThumbnails: TAction;
    MnuTlsThu: TTBXItem;
    TBItemDisplayThumbnails: TTBXItem;
    TBItemSort: TTBXSubmenuItem;
    MnuMlpSort: TTBXSubmenuItem;
    MnuTlsSortAscend: TTBXItem;
    MnuTlsSortDescend: TTBXItem;
    MnuTlsSortSep: TTBXSeparatorItem;
    ActionSortAscend: TAction;
    ActionSortDescend: TAction;
    PopupImage: TTBXPopupMenu;
    TBXMnuPicSelect: TTBXItem;
    TBXMnuPicDelete: TTBXItem;
    TBXMnuPicSaveAs: TTBXItem;
    TBXMnuPicCopy: TTBXItem;
    MnuUrlCopy: TTBXItem;
    PopupHTMLViewer: TTBXPopupMenu;
    MnuHtmlCopy: TTBXItem;
    TabMovieInfos: TTabControl;
    ActionManageFields: TAction;
    MnuTlsMan: TTBXItem;
    TBItemFieldsManager: TTBXItem;
    MnuTlsSortFields: TTBXSubmenuItem;
    MnuTlsSortCustomFields: TTBXSubmenuItem;
    MnuTlsGroupCustomFields: TTBXSubmenuItem;
    MnuTlsGroupFields: TTBXSubmenuItem;
    MnuTlsGrdFields: TTBXSubmenuItem;
    ActionMenuGridFields: TAction;
    MnuTlsGridCustomFields: TTBXSubmenuItem;
    MnuTlsGridFields: TTBXSubmenuItem;
    TBItemGridFields: TTBXSubmenuItem;
    MnuMlpGrdFields: TTBXSubmenuItem;
    ActionURLExplore: TAction;
    MnuUrlExp: TTBXItem;
    MnuMlpSch: TTBXSubmenuItem;
    procedure ActionDisplayMainToolbarExecute(Sender: TObject);
    procedure ActionDisplayPictureToolbarExecute(Sender: TObject);
    procedure ActionDisplayStatusBarExecute(Sender: TObject);
    procedure ActionExitExecute(Sender: TObject);
    procedure ActionFileExportExecute(Sender: TObject);
    procedure ActionFileImportExecute(Sender: TObject);
    procedure ActionFileNewExecute(Sender: TObject);
    procedure ActionFileOpenExecute(Sender: TObject);
    procedure ActionFilePrintExecute(Sender: TObject);
    procedure ActionFilePropertiesExecute(Sender: TObject);
    procedure ActionFileSaveAsExecute(Sender: TObject);
    procedure ActionFileSaveExecute(Sender: TObject);
    procedure ActionFindDisplayExecute(Sender: TObject);
    procedure ActionFindFindnextExecute(Sender: TObject);
    procedure ActionFindWholefieldExecute(Sender: TObject);
    procedure ActionGroupExecute(Sender: TObject);
    procedure ActionSortExecute(Sender: TObject);
    procedure ActionGridFieldsExecute(Sender: TObject);
    procedure ActionHelpAboutExecute(Sender: TObject);
    procedure ActionHelpIndexExecute(Sender: TObject);
    procedure ActionLanguageExecute(Sender: TObject);
    procedure ActionLoanExecute(Sender: TObject);
    procedure ActionMenuFileExecute(Sender: TObject);
    procedure ActionMenuGetExecute(Sender: TObject);
    procedure ActionMenuGroupExecute(Sender: TObject);
    procedure ActionMenuSortExecute(Sender: TObject);
    procedure ActionMenuHelpExecute(Sender: TObject);
    procedure ActionMenuMovieExecute(Sender: TObject);
    procedure ActionMenuPictureExecute(Sender: TObject);
    procedure ActionMenuToolsExecute(Sender: TObject);
    procedure ActionMovieAddExecute(Sender: TObject);
    procedure ActionMovieCopyExecute(Sender: TObject);
    procedure ActionMovieDeleteExecute(Sender: TObject);
    procedure ActionMovieFindExecute(Sender: TObject);
    procedure ActionMovieImportCDExecute(Sender: TObject);
    procedure ActionMovieImportFilesExecute(Sender: TObject);
    procedure ActionMovieImportScriptExecute(Sender: TObject);
    procedure ActionMovieNextExecute(Sender: TObject);
    procedure ActionMovieNumberExecute(Sender: TObject);
    procedure ActionMoviePasteExecute(Sender: TObject);
    procedure ActionMoviePictureShowExecute(Sender: TObject);
    procedure ActionMoviePreviousExecute(Sender: TObject);
    procedure ActionMovieRenumberExecute(Sender: TObject);
    procedure ActionMovieSearchExecute(Sender: TObject);
    procedure ActionMovieSelGroupExecute(Sender: TObject);
    procedure ActionMovieSelCheckExecute(Sender: TObject);
    procedure ActionMovieSelUncheckExecute(Sender: TObject);
    procedure ActionMovieCheckExecute(Sender: TObject);
    procedure ActionMovieUncheckExecute(Sender: TObject);
    procedure ActionMovieStatsExecute(Sender: TObject);
    procedure ActionMovieUndoExecute(Sender: TObject);
	  procedure ActionMovieRandomExecute(Sender: TObject);
    procedure ActionOptionsExecute(Sender: TObject);
    procedure ActionPicCopyExecute(Sender: TObject);
    procedure ActionPicDeleteExecute(Sender: TObject);
    procedure ActionPicSaveAsExecute(Sender: TObject);
    procedure ActionPicSelectExecute(Sender: TObject);
    procedure ActionPicUndockExecute(Sender: TObject);
    procedure ActionRefreshExecute(Sender: TObject);
    procedure ActionToolsGridExecute(Sender: TObject);
    procedure ActionToolsScriptingExecute(Sender: TObject);
    procedure ActionURLBrowseExecute(Sender: TObject);
    procedure ActionURLOpenExecute(Sender: TObject);
    procedure ActionURLCopyExecute(Sender: TObject);
    procedure ActionURLExploreExecute(Sender: TObject);
    procedure ActionHelpVersionExecute(Sender: TObject);
    procedure ActionStretchListExecute(Sender: TObject);
    procedure ActionDisplayHTMLExecute(Sender: TObject);
    procedure ActionDisplayThumbnailsExecute(Sender: TObject);
    procedure cbxFieldChange(Sender: TObject);
    procedure DockBottomListRequestDock(Sender: TObject; Bar: TTBCustomDockableWindow; var Accept: Boolean);
    procedure DockImageLeftRequestDock(Sender: TObject; Bar: TTBCustomDockableWindow; var Accept: Boolean);
    procedure DockMainBottomRequestDock(Sender: TObject; Bar: TTBCustomDockableWindow; var Accept: Boolean);
    procedure DockMainLeftRequestDock(Sender: TObject; Bar: TTBCustomDockableWindow; var Accept: Boolean);
    procedure DockMainTopRequestDock(Sender: TObject; Bar: TTBCustomDockableWindow; var Accept: Boolean);
    procedure DragDropFilesDrop(Sender: TObject; Pos: TPoint; Value: TStringList);
    procedure EValueChange(Sender: TObject);
    procedure EValueKeyPress(Sender: TObject; var Key: Char);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure ListView1AfterSelectionChange(Sender: TObject);
    procedure ListView1CompareItems(Sender: TObject; Item1, Item2: TElTreeItem; var res: Integer);
    procedure ListView1ItemChecked(Sender: TObject; Item: TElTreeItem);
    procedure ListView1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure ListView1Resize(Sender: TObject);
    procedure MoviePictureMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure PopupMovieListPopup(Sender: TObject);
    procedure SplitterBottomListCanResize(Sender: TObject; var NewSize: Integer; var Accept: Boolean);
    procedure TBMRUList1Click(Sender: TObject; const Filename: String);
    procedure ToolbarFindVisibleChanged(Sender: TObject);
    procedure ToolbarMainClose(Sender: TObject);
    procedure ToolbarPictureClose(Sender: TObject);
    procedure ToolbarPictureWindowClose(Sender: TObject);
    procedure ToolbarPictureWindowDockChanged(Sender: TObject);
    procedure ListView1HeaderColumnClick(Sender: TObject; SectionIndex: Integer);
    procedure ListView1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure FrmMovieMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    {FrameMovie}
    procedure OnURLButtonClick(Sender: TObject);
    procedure OnURLEnter(Sender: TObject);
    procedure OnMovieFieldChange(Sender: TObject);
    procedure OnMovieFieldPropertiesChange(Sender: TObject);
    procedure OnCustomFieldAdd(Sender: TObject);
    procedure OnCustomFieldModify(Sender: TObject);
    procedure OnCustomFieldDelete(Sender: TObject);
    procedure OnUserAskListUpdate(Sender: TObject);
    {End FrameMovie}
    {HTMLViewer}
    procedure HTMLViewerMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure HTMLViewerImageRequest(Sender: TObject; const SRC: String; var Stream: TMemoryStream);
    procedure HTMLViewerImageClick(Sender, Obj: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure HTMLViewerKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure HTMLViewerHotSpotClick(Sender: TObject; const SRC: String; var Handled: Boolean);
    procedure HTMLViewerRightClick(Sender: TObject; Parameters: TRightClickParameters);
    procedure MnuHtmlCopyClick(Sender: TObject);
    {End HTMLViewer}
    {ThumbsViewer}
    procedure CMUpdateView(var message: TMessage); message CM_UpdateView;
    procedure StartThumbThread;
    procedure StopThumbThread;
    procedure ThumbsViewerStop;
    procedure ThumbsViewerStart;
    procedure GenCellColors;
    procedure ItemPaintBasic(Canvas: TCanvas; R: TRect; State: TsvItemState);
    procedure ThumbsViewerCellPaint(Sender: TObject; Canvas: TCanvas; Cell: TRect;
      IdxA, Idx: Integer; State: TsvItemState);
    procedure ThumbsViewerSelecting(Sender: TObject; Count: Integer);
    procedure ThumbsSizerChange(Sender: TObject);
    procedure ThumbsViewerMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure ThumbsViewerDividerPaint(Sender: TObject; Canvas: TCanvas;
      Cell: TRect; Group: PSmartGroup; State: TsvItemState);
    procedure ThumbsViewerResize(Sender: TObject);
    procedure ThumbsViewerHeaderPaint(Sender: TObject; Canvas: TCanvas;
      Header: TRect; Offset, Active: Integer; State: TsvItemState; Columns: array of Integer);
    procedure ThumbsViewerHeaderClick(Sender: TObject; Column: Integer);
    procedure ListView1ItemCollapse(Sender: TObject; Item: TElTreeItem);
    procedure ListView1ItemExpand(Sender: TObject; Item: TElTreeItem);
    procedure ThumbsViewerMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure ThumbsViewerDblClick(Sender: TObject);
    procedure ThumbsViewerKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure TabMovieInfosChange(Sender: TObject);
    procedure ActionMenuGridFieldsExecute(Sender: TObject);
    procedure ListView1ItemColorPick(Sender: TObject; Item: TElTreeItem);
    {End ThumbsViewer}

  private
    ColumnTitleDisplay: Integer;
    UsePrefixes:        Boolean;
    MovieList:          TMovieList;
    MovieSave:          TMovie;
    MovieCopy:          TMovie;
    PanelLeftOldWidth:  Integer;
    FPreviousItem:      TElTreeItem;
    FCatalogFile:       TFileManager;
    FCurrentVersion:    Integer;
    FGroupField:        Integer;
    FSortField:         Integer;
    DragDropFiles:      TJvDragDrop;
    MoviesVisible:      Integer;
    GroupItemEmpty:     TElTreeItem;
    FSelectedURL:       TAntJvComboEditXP;
    FURL:               string;
    {HTMLViewer}
    MStream:            TMemoryStream;
    MStreamAppr:        array[0..4] of TMemoryStream;
    MStreamAppr10:      array[0..10] of TMemoryStream;
    HTMLTemplatePreGenerated: TStringList;
    HTMLTemplatePath:   string;
    {End HTMLViewer}
    {ThumbsViewer}
    ThumbSizeW, ThumbSizeH: Integer;
    ThumbJPEG: TJpegImageFix;
    ThumbGIF: TJvGIFImage;
    ThumbPNG: TPNGObject;
    WI, HI, HSX: Integer;
    CellJpeg : TJpegImageFix;
    CellScale: Integer;
    CellStyle: Integer;
    ThumbsPool: TList;
    PoolSize, MaxPool: Integer;
    FullExpandOrCollapse: Boolean;
    {End ThumbsViewer}
    procedure ListView1UpdateItem;
    procedure ListView1EnsureVisiblePlus;
    procedure SearchInternetClick(Sender: TObject);
    function GetTitle: string;
    procedure SetTitle;
    procedure SetStatus;
    procedure UpdateMRU(const strFilePath: string);
    procedure SetGroupField(Field: Integer);
    procedure SetSortField(Field: Integer; Descend: Boolean; SortList: Boolean = True);
    procedure OnFileModified(Sender: TObject; State: Boolean);
    procedure OnFileChange(Sender: TObject; AFileName: TFileName; var Result: Boolean);
    procedure OnNewFile(Sender: TObject; AFileName: TFileName; var Result: Boolean);
    procedure OnOpenFile(Sender: TObject; AFileName: TFileName; var Result: Boolean);
    procedure OnSaveFile(Sender: TObject; AFileName: TFileName; var Result: Boolean);
    procedure OnDialogTypeChange(Sender: TObject);
    procedure SetSaveFilter;
    procedure MakeMovieSave(Movie: TMovie; LockMovie: Boolean = True);
    procedure NewItem;
    function OpenItem(const Item: TElTreeItem): integer; overload;
    function OpenItem(const AMovie: TMovie): integer; overload;
    function SaveCurrentItem: integer; overload;
    function SaveCurrentItem(const AMovie: TMovie; FreeMovieSave: Boolean = True): integer; overload;
    function AddItem(Movie: TMovie; GroupItem: TElTreeItem): TElTreeItem;
    {procedure ModifyItem(Item: TElTreeItem; MovieTmp: TMovie);}
    function DeleteItem(Movie: TMovie; const CanShowWindow, ShowAllButtons: Boolean): Integer;
    procedure UpdateItem(Item : TElTreeItem);
    function AddGroupItem(GroupName: string) : TElTreeItem;
    function GetGroupNameWithoutCount(GroupName: string) : string;
    procedure UpdateGroupName(GroupItem: TElTreeItem; GroupNameWithoutCount: string);
    procedure ClearItem;
    procedure RemoveMoviePicture;
    procedure LoadMoviePicture(const ForceLoad: Boolean = False);
    procedure MovieSelected;
    procedure RefreshMovieList(const KeepSelection: Boolean = True; const SaveColumnSettingsCF: Boolean = True);
    procedure SaveColumnSettings(const SaveCustomFields: Boolean = True);
    procedure RestoreColumnSettings;
    procedure StoreSelectedState;
    procedure LoadSelectedState;
    procedure LoadOptions;
    procedure ApplyOptions;
    procedure ApplyLanguage;
    procedure RebuildSearchMenu;
    procedure FillFieldsSortBy;
    procedure FillFieldsGroupBy;
    procedure FillFieldsGrid;
    procedure FillFieldsCBSearch(SelectedItem: Integer = 0);
    procedure SaveOptions;
    procedure LoadLists;
    procedure SaveLists;
    procedure UpdateLists;
    procedure ImportMoviePicture(Movie: TMovie; const AFileName: TFileName;
      const ImportMethod: TPictureSelectOption; ShowConfirm: Boolean = True);
    function OnAppHelp(Command: Word; Data: Longint; var CallHelp: Boolean): Boolean;
    {HTMLViewer}
    procedure HTMLViewerUpdate;
    procedure PreGenerateHTMLTemplate;
    function GenerateHTMLSelectedMovie : string;
    {End HTMLViewer}
    {ThumbsViewer}
    procedure ThumbsViewerUpdateSelection(EnsureVisible: Boolean = True);
    procedure ThumbsGetThumbnail(Sender: TObject; Movie: TMovie);
    function ThumbBmp(idx: Integer): TBitmap;
    procedure SetThumbSize(Value: Integer; UpdateTrackbar: Boolean);
    procedure ClearThumbs;
    procedure ClearThumbsPool;
    {End ThumbsViewer}

  protected
    {ThumbsViewer}
    ThumbThr: ThumbThread;
    ThreadDone: Boolean;
    WParam: Integer;
    LParam: Integer;
    {End ThumbsViewer}

  public
    {ThumbsViewer}
    // Colors
    cGSelectedStart,
    cGSelectedEnd,
    cGHotStart,
    cGHotEnd,
    cGDisabledStart,
    cGDisabledEnd,
    cGHeaderStart,
    cGHeaderEnd,
    cGHeaderHotStart,
    cGHeaderHotEnd,
    cGHeaderSelStart,
    cGHeaderSelEnd,
    cHot,
    cSelected,
    cDisabled,
    cBackground,
    cLineHighLight: TColor;
    cShadeSelect: TColor;
    cShadeDisabled: TColor;
    CellShade0: TColor;
    CellShade1: TColor;
    CellShade2: TColor;
    CellShade3: TColor;
    CellShade4: TColor;
    CellBkgColor: TColor;
    CellBrdColor: array[Boolean, Boolean] of TColor;
    {End ThumbsViewer}
    function ExecuteAction(Action: TBasicAction): Boolean; override;
  end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

var
  MainWindow: TMainWindow;

implementation

uses
  CommDlg, ExtDlgs, Math, DateUtils, StrUtils,

  IdURI,

  BrowseForFolderU, rmkGradient, rmkFunctions,

  {$IFNDEF DISABLETHEMES}
  TBXOfficeXPTheme,
  {$ENDIF}

  functions_str, functions_sys, functions_video, functions_files, functions_gui,
  functions_xml,

  Global, options, about, export, number, loan, splash, stats, customfieldsmanager,
  renumber, fields, properties, loanhistory, getscript, Languageselect,
  ProgramSettings, printform, PictureSelection, PictureDragDrop, pictureform,
  progress, getfile, import2, Variants, SyncObjs, Types;

{$R *.DFM}

const
  // Messages
  msgDeleteMovie      = 0;
  msgUndo             = 1;
  msgStatusCount      = 2;
  msgStatusVisible    = 3;
  msgUnsavedFile      = 4;
  msgDoNotConfirm     = 5;
  msgUnsavedStatus    = 6;
  msgPicture          = 7;
  msgPicSaveAs        = 8;
  msgImporting        = 9;
  msgSaving           = 10;
  msgLoading          = 11;
  msgPicOpen          = 12;
  msgPicDelete        = 13;
  msgPicLoadFailed    = 14;
  msgByteString       = 15;
  msgSelectFilesInfo  = 16;
  msgSearchInfo       = 17;
  msgNoRecentFile     = 18;
  msgXmlConvertImages = 19;
  msgGroupBy          = 20;
  msgGroupEmpty       = 21;
  msgPicDeleteLink    = 22;
  msgPicReplaceLink   = 23;
  msgPicCopyNoCatPath = 24;
  msgPicHintStored    = 25;
  msgPicHintLinked    = 26;
  msgPicHintInfo      = 27;
  msgPrintLoading     = 28;
  msgPrintSearching   = 29;
  msgSelectFileURL    = 30;
  msgGroupUnique      = 31;
  msgFindFieldAll     = 32;
  msgSortBy           = 33;
  msgMovieFields      = 34;
  msgCustomFields     = 35;
  msgImportFields     = 36;
  msgExportFields     = 37;
  msgFileNotExists    = 38;
  msgSaveAMC35Warning = 39;

  panelHint     = 0;
  panelModified = 1;
  panelCount    = 2;

{-------------------------------------------------------------------------------
   Update components when selected item informations changes
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1UpdateItem;
begin
  if (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil) and
    (ListView1.Selected.Data <> nil) and (FrmMovie.Modified or FrmMovieCustom.Modified) then
  begin
    UpdateItem(ListView1.Selected);
  end;
end;

{-------------------------------------------------------------------------------
  Ensure Visible (Plus for groups) on selected item
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1EnsureVisiblePlus;
begin
  with ListView1 do
  begin
    if (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil) then
    begin
      EnsureVisible(ListView1.Selected);
      if ListView1.Selected.Parent <> nil then
      begin
        EnsureVisible(ListView1.Selected.Parent);
        EnsureVisibleBottom(ListView1.Selected);
      end;
    end;
  end;
end;

{-------------------------------------------------------------------------------
   Item management
-------------------------------------------------------------------------------}

procedure TMainWindow.ClearItem;
begin
  FrmMovie.LoadFromObject(Settings.rOptions.rMovieInformation.rDefaultMovie.Values);
  FrmMovieCustom.SetDefaultValues;
  HTMLViewerUpdate;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.NewItem;
var
  Item: TElTreeItem;
  NewMovie, Existing: TMovie;
begin
  SaveCurrentItem;

  ListView1.DeselectAll;
  NewMovie := MovieList.Add;
  NewMovie.InitFields;
  NewMovie.Assign(Settings.rOptions.rMovieInformation.rDefaultMovie.Values, False, False, False);
  if Settings.rOptions.rMovieInformation.SetCurrentDate then
   NewMovie.iDate := Trunc(Date);
  NewMovie.CustomFields.SetDefaultValues;
  NewMovie.iNumber := NumberWin.ENumber.AsInteger;
  //NewMovie.bChecked := True;

  case NumberWin.grpNotUnique.ItemIndex of
    1:
      begin
        Existing := MovieList.Find(NumberWin.ENumber.AsInteger);
        if Existing <> nil then
        begin
          if Settings.rOptions.rMovieInformation.FirstAvailable then
            Existing.iNumber := MovieList.FirstFreeNumber
          else
            Existing.iNumber := MovieList.MaxNumber;
        end;
        RefreshMovieList(False);
      end;
    2:
      begin
        MovieList.ShiftNumbers(NumberWin.ENumber.AsInteger, NewMovie);
        RefreshMovieList(False);
      end;
  end;

  ListView1.Items.BeginUpdate;
  if (FGroupField <> -1) then
  begin
    if GroupItemEmpty = nil then
    begin
      GroupItemEmpty := AddGroupItem(Messages.Strings[msgGroupEmpty]);
      Item := AddItem(NewMovie, GroupItemEmpty);
      UpdateGroupName(GroupItemEmpty, GroupItemEmpty.Text);
    end else
    begin
      Item := AddItem(NewMovie, GroupItemEmpty);
      UpdateGroupName(GroupItemEmpty, GetGroupNameWithoutCount(GroupItemEmpty.Text));
    end;
  end else
    Item := AddItem(NewMovie, nil);

  ListView1.ItemFocused := Item;
  ListView1.Selected := ListView1.GetNextSelected(nil);

  ListView1.Items.EndUpdate;
  ListView1AfterSelectionChange(ListView1);
  ListView1EnsureVisiblePlus;
  SetStatus;
  FCatalogFile.Modified := True;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ImportMoviePicture(Movie: TMovie; const AFileName: TFileName;
  const ImportMethod: TPictureSelectOption; ShowConfirm: Boolean);
var
  NewName: TFileName;
  ExistingName: TFileName;
  ExistingInSamePath: Boolean;
  SameFile: Boolean;
  Ext, CatalogPrefix: string;
  CatalogPath: TFileName;
  Stream: TFileStream;
begin
  if not FileExists(ExpandFileName(AFileName)) then
    Exit;
  with Movie do
  begin
    try
      Stream := TFileStream.Create(AFileName, fmOpenRead);
      try
        Ext := GetRealPictureExt(Stream, extImage[extPNG], extImage[extJPG], extImage[extGIF], ExtractFileExt(AFileName))
      finally
        Stream.Free;
      end;
      if Ext = '' then
        raise Exception.Create('Unrecognized picture format');

      CatalogPath := ExtractFilePath(FCatalogFile.CurrentFile);
      SetCurrentDir(CatalogPath);
      ExistingInSamePath := False;
      SameFile := False;
      if (strPicture <> '') and (Picture = nil) then
      begin
        ExistingName := ExpandFileName(strPicture);
        if not FileExists(ExistingName) then
          ExistingName := ''
        else
          ExistingInSamePath := Pos('\', strPicture) = 0;
      end
      else
        ExistingName := '';

      if (ExistingName <> '') then
      begin // there was a linked picture
        if LowerCase(ExistingName) = LowerCase(ExpandFileName(AFileName)) then
          SameFile := True
        else if ExistingInSamePath then
          DeleteFile(ExistingName)
        else if ShowConfirm then // it was not in same path than catalog, so not sure that it can be deleted
          case MessageWin.Execute(Format(messages.Strings[msgPicReplaceLink], [ExistingName]), mtConfirmation, [mbYes, mbNo, mbCancel]) of
            1:    DeleteFile(ExistingName);
            0, 3: Exit;
          end;
      end;

      if ImportMethod = psoStore then
      begin
        try
          strPicture := Ext;
          FreeAndNil(Picture);
          if IndexText(strPicture, extImage) <> -1 then
          begin
            Picture := TMemoryStream.Create;
            Picture.LoadFromFile(AFileName);
          end;
        except
          on e: Exception do
            begin
              Picture.Free;
              Picture := nil;
              strPicture := '';
              raise e;
            end;
        end;
        if SameFile then
          DeleteFile(ExistingName);
      end
      else if ImportMethod = psoCopy then
      begin
        if SameFile then
          Exit;
        if CatalogPath = '' then
          raise Exception.Create(messages.Strings[msgPicCopyNoCatPath]);
        case Settings.rOptions.rMovieInformation.rPicImport.Naming of
          1:  NewName := IntToStr(iNumber) + Ext;
          2:  NewName := ValidateFileName(GetFormattedTitle) + Ext;
        else
          NewName := ChangeFileExt(ExtractFileName(AFileName), Ext);
        end;
        if Settings.rOptions.rMovieInformation.rPicImport.CatalogPrefix then
        begin
          CatalogPrefix := ChangeFileExt(ExtractFileName(FCatalogFile.CurrentFile), '');
          if not StartsStr(CatalogPrefix + '_', NewName) then
            NewName := Format('%s_%s', [CatalogPrefix, NewName]);
        end;
        NewName := ExtractFileName(MakeUniqueFileName(CatalogPath + NewName, '_'));
        if not CopyFile(PChar(AFileName), PChar(CatalogPath + NewName), False) then
          RaiseLastOSError;
        strPicture := NewName;
        Picture.Free;
        Picture := nil;
      end
      else
      begin
        if SameFile then
          Exit;
        if ImportMethod = psoLinkRel then
          NewName := ExtractRelativePath(CatalogPath, AFileName)
        else
          NewName := AFileName;
        strPicture := NewName;
        Picture.Free;
        Picture := nil;
      end;
      FCatalogFile.Modified := True;
    except
      on e: Exception do
        MessageWin.Execute(Format(Messages.Strings[msgPicLoadFailed], [e.Message]), mtError, [mbOk]);
    end;
    if(_thumb <> nil) then
      FreeAndNil(_thumb);
    _thumbError := 0;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.RemoveMoviePicture;
begin
  with MoviePicture do
  begin
    Hint := '|' + GetLongHint(Hint);
    Picture.Assign(nil);
    Width := 0;
    Height := 0;
  end;
end;

procedure TMainWindow.LoadMoviePicture(const ForceLoad: Boolean = False);
var
  loadedPic: TGraphic;
  PicSizeDouble: Double;
begin
  RemoveMoviePicture;
  with ListView1 do
    if (Selected <> nil) and Selected.Selected and (Selected.Data <> nil) and
      (ToolbarPictureWindow.Visible or ForceLoad) then
    begin
      with TMovie(Selected.Data) do
      begin
        Lock;
        try
          if Picture = nil then
          begin
            if strPicture = '' then
              MoviePicture.Picture.LoadFromFile(strDirApp + strDirTemplates + strFileNoPoster)
            else
            begin
              if FCatalogFile.CurrentFile <> '' then
                SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
              if FileExists(ExpandFileName(strPicture)) then
                MoviePicture.Picture.LoadFromFile(strPicture)
              else
                MoviePicture.Picture.LoadFromFile(strDirApp + strDirTemplates + strFileNoFileFound);
            end;
            PicSizeDouble := GetFileSize(strPicture);
            MoviePicture.Hint := Format('%s%s%s%s', [
              Format(Messages.Strings[msgPicHintLinked], [strPicture]),
              sLineBreak,
              Format(Messages.Strings[msgPicHintInfo], [PicSizeDouble]),
              MoviePicture.Hint
            ]);
          end else
          begin
            case IndexText(strPicture, extImage) of
              extPNG:
                loadedPic := TPNGObject.Create;
              extJPG, extJPE, extJPEG:
                loadedPic := TJPEGImageFix.Create;
              extGIF:
                loadedPic := TJvGIFImage.Create;
            else
              loadedPic := nil;
              Abort;
            end;
            PicSizeDouble := Picture.Size;
            MoviePicture.Hint := Format('%s%s%s%s', [
              Format(Messages.Strings[msgPicHintStored], [
                typeImage[IndexText(strPicture, extImage)]
              ]),
              sLineBreak,
              Format(Messages.Strings[msgPicHintInfo], [
                PicSizeDouble
              ]),
              MoviePicture.Hint
            ]);
            Picture.Seek(0, soFromBeginning);
            loadedPic.LoadFromStream(Picture);
            MoviePicture.Picture.Assign(loadedPic);
            loadedPic.Free;
          end;
        except
          with MoviePicture do
          begin
            Width:=0;
            Height:=0;
          end;
        end;
        UnLock;
      end;
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.OpenItem(const AMovie: TMovie): integer;
begin
  //SetCurrentDir(strDirApp);
  FrmMovie.LoadFromObject(AMovie);
  FrmMovieCustom.LoadFromObject(AMovie);
  HTMLViewerUpdate;
  LoadMoviePicture;
  result := 0;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.OpenItem(const Item: TElTreeItem): integer;
begin
  Result := -1;
  if (Item <> nil) and (Item.Data <> nil) then
  begin
    OpenItem(TMovie(Item.Data));
    FPreviousItem := Item;
    Result := 0;
  end else
  begin
    FPreviousItem := nil;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.SaveCurrentItem(const AMovie: TMovie; FreeMovieSave: Boolean): Integer;
begin
  if (AMovie = nil) then
  begin
    Result := -1;
    Exit;
  end;
  with FrmMovie, AMovie, Settings.rOptions.rFiles do
    if History and (strBorrower <> EBorrower.Text) then
    begin
      SetCurrentDir(strDirApp);
      with TLoanHistory.Create(ExpandFileName(HistoryFile), ExtractFileName(FCatalogFile.CurrentFile)) do
      try
        if strBorrower <> '' then
          Add(strBorrower, iNumber, strMedia, GetFormattedTitle, lhIn);
        if EBorrower.Text <> '' then
          Add(EBorrower.Text, iNumber, strMedia, GetFormattedTitle, lhOut);
      finally
        Free;
      end;
    end;

  // Update current item in the list if needed
  if FrmMovie.Modified or FrmMovieCustom.Modified then
  begin
    if (FPreviousItem <> nil) and (FPreviousItem.Data = AMovie) then
      UpdateItem(FPreviousItem) //FrmMovie.SaveToObject(AMovie) done in UpdateItem
    else
    begin
      FrmMovie.SaveToObject(AMovie);
      FrmMovieCustom.SaveToObject(AMovie);
    end;
  end;

  if FreeMovieSave then
    FreeAndNil(MovieSave);

  UpdateLists;
  Result := 0;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.SaveCurrentItem: Integer;
begin
  Result := -1;
  if (FPreviousItem = nil) or (FPreviousItem.Data = nil) then
    Exit;
  //if ListView1.Enabled and self.Enabled then
  //  ListView1.SetFocus;
  Result := SaveCurrentItem(TMovie(FPreviousItem.Data));
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.AddItem(Movie: TMovie; GroupItem: TElTreeItem) : TElTreeItem;
var
  j: Integer;
  Prop: TCustomFieldProperties;
begin
  Inc(MoviesVisible);
  Result := ListView1.Items.AddChild(GroupItem, Movie.GetFormattedTitle(ColumnTitleDisplay, UsePrefixes));
  Movie._listItems.Add(Result);
  // *** Adding each item to the list ***
  with Result do
  begin
    Result.SubItems.Add(IntToStr(Movie.iNumber));
    if ActionToolsGrid.Checked then
    begin
      SubItems.Add('');
      SubItems.Add(IntToStr(Movie.iNumber));
      for j := 0 to fieldCount-1 do
        if not (j in [fieldNumber, fieldFormattedTitle]) then
        begin
          if (Settings.rOptions.rMovieList.GridTextSize > 0) and
            not (j in [fieldFormattedTitle, fieldOriginalTitle, fieldTranslatedTitle]) then
          begin
            SubItems.Add(Copy(Movie.GetFieldValue(j, True), 1, Settings.rOptions.rMovieList.GridTextSize));
          end else
          begin
            SubItems.Add(Movie.GetFieldValue(j, True));
          end;
        end;
      with MovieList.CustomFieldsProperties do
        for j := 0 to Count-1 do
        begin
          Prop := Objects[j];
          if (Settings.rOptions.rMovieList.GridTextSize > 0) then
          begin
            SubItems.Add(Copy(Movie.CustomFields.GetFieldValue(Prop.FieldTag, False, True), 1, Settings.rOptions.rMovieList.GridTextSize));
          end else
          begin
            SubItems.Add(Movie.CustomFields.GetFieldValue(Prop.FieldTag, False, True));
          end;
        end;
    end else
      SubItems.Add('');
    Checked := Movie.bChecked;
    Data := Movie;
    ShowCheckBox := True;
    Tag := 0;

    {if True then // CheckBoxColor
    begin
      UseCheckBoxColor := True;
      CheckBoxColor := DefaultColors[Movie.iColor mod (Lenght(DefaultColors)+1)];
    end;

    if (True) then // LineColor
    begin
      UseBkColor := True;
      ParentColors := False;
      RowBkColor := DefaultColors[Movie.iColor mod (Lenght(DefaultColors)+1)];
      BkColor := RowBkColor;
    end;

    //ShowHint := False;
    //Hint := 'Color '+ Movie.iColor;}

  end; // with Item
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.DeleteItem(Movie: TMovie; const CanShowWindow, ShowAllButtons: Boolean): Integer;
var
  DoNotAsk: Boolean;
  Buttons: array of Variant;
  Res, i: Integer;
  Item, ItemParent: TElTreeItem;
begin
  if ShowAllButtons then
    Buttons := VarArrayOf([mbYes,mbYesToAll,mbNo,mbNoToAll])
  else
    Buttons := VarArrayOf([mbYes,mbNo]);
  Result := -1;
  if Movie <> nil then
  begin
    if Settings.rOptions.rMovieList.ConfirmDelete and CanShowWindow then
    begin
      DoNotAsk := false;
      Res := MessageWin.Execute(Format(Messages.Strings[msgDeleteMovie],
        [Movie.GetFieldValue(fieldNumber), Movie.GetFormattedTitle]),
        mtConfirmation, DoNotAsk, Messages.Strings[msgDoNotConfirm], Buttons);
      if (not ShowAllButtons) and (Res = 2) then
        Res := 3;
      case Res of
        1:
          begin
            Settings.rOptions.rMovieList.ConfirmDelete := not DoNotAsk;
            Result := 1;
          end;
        2:
          begin
            Settings.rOptions.rMovieList.ConfirmDelete := not DoNotAsk;
            if DoNotAsk then
              Result := 1
            else
              Result := 2;
          end;
        3:
          begin
            Result := 3;
            Exit;
          end;
        4,0:
          begin
            Result := 4;
            Exit;
          end;
      end;
    end;
    ThumbsViewerStop;
    // Delete movie in ListView
    ListView1.Items.BeginUpdate;
    for i := 0 to Movie._listItems.Count-1 do
    begin
      Item := TElTreeItem(Movie._listItems.Items[i]);
      ItemParent := Item.Parent;
      if Item = ListView1.ItemFocused then
        ListView1.ItemFocused := nil;
      Listview1.Items.Delete(Item);
      Dec(MoviesVisible);
      if (ItemParent <> nil) then
      begin
        if ItemParent.Count = 0  then
        begin
          if ItemParent = GroupItemEmpty then
            GroupItemEmpty := nil;
          if ItemParent = ListView1.ItemFocused then
            ListView1.ItemFocused := nil;
          Listview1.Items.Delete(ItemParent)
        end else
          UpdateGroupName(ItemParent, GetGroupNameWithoutCount(ItemParent.Text));
      end;
    end;
    ListView1.Items.EndUpdate;
    // Delete movie picture file if in catalog
    if FCatalogFile.CurrentFile <> '' then
      SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
    if ExtractFilePath(Movie.strPicture) = '' then
      DeleteFile(ExpandFileName(Movie.strPicture));
    // Delete movie in MovieList
    MovieList.Remove(Movie);
    if Result = -1 then
      Result := 0;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

// Update tree item with informations in FrmMovie !
procedure TMainWindow.UpdateItem(Item: TElTreeItem);
var
  Modifs: Boolean;
  i, j: Integer;
  List: TObjectList;
  SameItem: TElTreeItem;
  FieldValue: string;
  Movie: TMovie;
  SortField: Integer;
  OldSortFieldValue, SortFieldValue: string;

  procedure UpdateI(Field: Integer; Text: string);
  var
    ListField: Integer;
  begin
    ListField := Field;
    if ListField >= customFieldLow then
      ListField := ListField - customFieldLow + fieldCount;
    if ListField >= fieldFormattedTitle then
      Dec(ListField);
    ListField := ListField + 2;
    if (Settings.rOptions.rMovieList.GridTextSize > 0) and (Field <> fieldFormattedTitle) and
       (Field <> fieldTranslatedTitle) and (Field <> fieldOriginalTitle) then
      FieldValue := Copy(Text, 1, Settings.rOptions.rMovieList.GridTextSize)
    else
      FieldValue := Text;
    if (AnsiCompareStr(FieldValue, Item.SubItems.Strings[ListField]) <> 0) then
    begin
      if not Modifs then
      begin
        Modifs := True;
        ListView1.Items.BeginUpdate;
      end;
      Item.SubItems.Strings[ListField] := FieldValue;
    end;
  end;

begin
  if (Item <> nil) and (Item.Data <> nil) then
  begin
    Modifs := False;
    Movie := TMovie(Item.Data);

    if ActionToolsGrid.Checked then
    begin
      with FrmMovie do
      begin
        SaveToObject(Movie);
        UpdateI(fieldMedia, EMedia.Text);
        UpdateI(fieldMediaType, EMediaType.Text);
        UpdateI(fieldSource, ESource.Text);
        if EDate.Checked then
          UpdateI(fieldDate, DateToStr(Trunc(EDate.DateTime)))
        else
          UpdateI(fieldDate, '');
        UpdateI(fieldBorrower, EBorrower.Text);
        UpdateI(fieldRating, ERating.Text);
        UpdateI(fieldOriginalTitle, EOriginalTitle.Text);
        UpdateI(fieldTranslatedTitle, ETranslatedTitle.Text);
        UpdateI(fieldDirector, EDirector.Text);
        UpdateI(fieldProducer, EProducer.Text);
        UpdateI(fieldCountry, ECountry.Text);
        UpdateI(fieldCategory, ECategory.Text);
        UpdateI(fieldYear, EYear.Text);
        UpdateI(fieldLength, ELength.Text);
        UpdateI(fieldActors, EActors.Text);
        UpdateI(fieldURL, EURL.Text);
        UpdateI(fieldDescription, EDescription.Text);
        UpdateI(fieldComments, EComments.Text);
        UpdateI(fieldVideoFormat, EVideoFormat.Text);
        UpdateI(fieldVideoBitrate, EVideoBitrate.Text);
        UpdateI(fieldAudioFormat, EAudioFormat.Text);
        UpdateI(fieldAudioBitrate, EAudioBitrate.Text);
        UpdateI(fieldResolution, EResolution.Text);
        UpdateI(fieldFrameRate, EFramerate.Text);
        UpdateI(fieldLanguages, ELanguages.Text);
        UpdateI(fieldSubtitles, ESubtitles.Text);
        UpdateI(fieldSize, ESize.Text);
        UpdateI(fieldDisks, EDisks.Text);
        UpdateI(fieldNumber, TMovie(Item.Data).GetFieldValue(fieldNumber));
        if Modifs then
          Item.Text := GetTitle;
        //Item.SubItems.Strings[2] := TMovie(Item.Data).GetFieldValue(fieldNumber);
        //Item.Checked := TMovie(Item.Data).bChecked;
      end;
      FrmMovieCustom.SaveToObject(Movie);
      with MovieList.CustomFieldsProperties do
        for i := 0 to Count-1 do
        begin
          with TPanelCustomField(Objects[i].FieldObject) do
            FieldValue := ConvertFieldValue(CFValue, CFType, True, False, False);
          UpdateI(customFieldLow+i, FieldValue)
        end;
    end else
    begin
      OldSortFieldValue := '';
      SortField := Abs(FSortField) - 1;
      if SortField < fieldCount then
        OldSortFieldValue := Movie.GetFieldValue(SortField)
      else if (SortField >= customFieldLow) and (SortField - customFieldLow < MovieList.CustomFieldsProperties.Count) then
        OldSortFieldValue := Movie.CustomFields.GetFieldValue(MovieList.CustomFieldsProperties.Strings[SortField - customFieldLow]);
      FrmMovie.SaveToObject(Movie);
      FrmMovieCustom.SaveToObject(Movie);
      SortFieldValue := '';
      if SortField < fieldCount then
        SortFieldValue := Movie.GetFieldValue(SortField)
      else if (SortField >= customFieldLow) and (SortField - customFieldLow < MovieList.CustomFieldsProperties.Count) then
        SortFieldValue := Movie.CustomFields.GetFieldValue(MovieList.CustomFieldsProperties.Strings[SortField - customFieldLow]);
      FieldValue := GetTitle;
      if (AnsiCompareStr(OldSortFieldValue, SortFieldValue) <> 0) or
        (AnsiCompareStr(FieldValue, Item.Text) <> 0) then
      begin
        Modifs := True;
        ListView1.Items.BeginUpdate;
        Item.Text := FieldValue;
        Item.SubItems.Strings[1] := ''; // Hidden column for other field sort; Needed to force automatic sort on item
        //Item.SubItems.Strings[0] := TMovie(Item.Data).GetFieldValue(fieldNumber);
        //Item.Checked := TMovie(Item.Data).bChecked;
      end;
    end;
    if Modifs then
    begin
      List := TObjectList(TMovie(Item.Data)._listItems);
      for i := 0 to List.Count-1 do
      begin
        SameItem := TElTreeItem(List.Items[i]);
        if SameItem <> Item then
        begin
          for j := 0 to SameItem.SubItems.Count-1 do
          begin
            SameItem.SubItems.Strings[j] := Item.SubItems.Strings[j];
          end;
          SameItem.Text := Item.Text;
        end;
      end;
      i := Item.AbsoluteIndex;
      ListView1.Items.EndUpdate;
      j := Item.AbsoluteIndex;
      if(i <> j) then
      begin
        ListView1EnsureVisiblePlus;
        ThumbsViewerStart;
      end
      else //if(ThumbsPanel.Visible) then
        ThumbsViewer.Invalidate;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.AddGroupItem(GroupName: string) : TElTreeItem;
begin
  Result := ListView1.Items.AddChild(nil, GroupName);
  with Result do
  begin
    Data := nil;
    Tag := 0;
    Expanded := Settings.rOptions.rMovieList.GroupExpand;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.GetGroupNameWithoutCount(GroupName: string) : string;
var
  lastPosGroupName: Integer;
begin
  if Settings.rOptions.rMovieList.GroupCount then
  begin
     lastPosGroupName := LastPos(' (', GroupName) - 1;
     Result := Copy(GroupName, 1, lastPosGroupName)
  end else
    Result := GroupName;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.UpdateGroupName(GroupItem: TElTreeItem; GroupNameWithoutCount: string);
begin
  if Settings.rOptions.rMovieList.GroupCount and (GroupItem <> nil) and (GroupItem.Data = nil) then
    GroupItem.Text := Format('%s (%d)', [GroupNameWithoutCount, GroupItem.Count])
  else
    GroupItem.Text := GroupNameWithoutCount;
end;

{-------------------------------------------------------------------------------
  File Management
-------------------------------------------------------------------------------}

procedure TMainWindow.OnFileChange(Sender: TObject; AFileName: TFileName; var Result: Boolean);
begin
  Result := True;
  SetTitle;
  SetStatus;
  MovieSelected;
  SaveLists;
  LoadLists;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.OnFileModified(Sender: TObject; State: Boolean);
begin
  with StatusBar1.Panels.Items[panelModified] do
    if State then
      Caption := Messages.Strings[msgUnsavedStatus]
    else
      Caption := '';
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.OnNewFile(Sender: TObject; AFileName: TFileName; var Result: Boolean);
begin
  ThumbsViewerStop;
  Result := True;
  ListView1.DeselectAll;
  ClearItem;
  MovieSelected;

  FPreviousItem := nil;
  FrmMovieCustom.Properties := nil;
  FrmMovieCustom.ClearFields;
  FreeAndNil(MovieCopy);
  MovieList.Free;
  MovieList := TMovieList.Create;
  with Settings.rOptions.rFiles do
    if AutoLoadCF and (AutoLoadCFFile <> '') then
    begin
      if FileExists(ExpandFileName(AutoLoadCFFile)) then
        try
          SetCurrentDir(strDirApp + strDirCatalogs);
          MovieList.CustomFieldsProperties.ImportFromXML(AutoLoadCFFile);
        finally
        end
      else
         MessageWin.Execute(Format(Messages.Strings[msgFileNotExists], [AutoLoadCFFile]), mtError, [mbOk]);
    end;
  FrmMovieCustom.Properties := MovieList.CustomFieldsProperties;
  FrmMovieCustom.GenerateFields;
  FillFieldsCBSearch(IfThen(cbxField.ItemIndex - 1 > fieldCount, 0, cbxField.ItemIndex));
  FillFieldsSortBy;
  SetSortField(IfThen(Abs(FSortField)-1 >= customFieldLow, fieldNumber, Abs(FSortField)-1), FSortField < 0, False);
  FillFieldsGroupBy;
  SetGroupField(IfThen(FGroupField >= customFieldLow, -1, FGroupField));
  FillFieldsGrid;
  PreGenerateHTMLTemplate;
  FCatalogFile.Modified := False;
  FCurrentVersion := 99;
  UpdateMRU(AFileName);
  RefreshMovieList(False, False); //ThumbsViewerStart; -> Done in RefreshMovieList
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.OnOpenFile(Sender: TObject; AFileName: TFileName; var Result: Boolean);
begin
  ThumbsViewerStop;
  Result := True;
  ListView1.DeselectAll;
  ClearItem;
  MovieSelected;

  try
    Application.ProcessMessages;
    with ProgressWin do
    begin
      Maximum := 2;
      Status := Format(Messages.Strings[msgLoading],[ExtractFileName(AFileName)]);
      IntProgress := 0;
      Execute(Self);
      try
        FPreviousItem := nil;
        FrmMovieCustom.Properties := nil;
        FrmMovieCustom.ClearFields;
        FreeAndNil(MovieCopy);
        MovieList.Free;

        MovieList := TMovieList.Create;
        if LowerCase(ExtractFileExt(AFileName)) = extCatalog[extXML] then
          MovieList.LoadFromXML(AFileName)
        else
          MovieList.LoadFromFile(AFileName);
        IntProgress := Maximum-1;
        FrmMovieCustom.Properties := MovieList.CustomFieldsProperties;
        FrmMovieCustom.GenerateFields;
        FillFieldsCBSearch(IfThen(cbxField.ItemIndex - 1 > fieldCount, 0, cbxField.ItemIndex));
        FillFieldsSortBy;
        SetSortField(IfThen(Abs(FSortField)-1 >= customFieldLow, fieldNumber, Abs(FSortField)-1), FSortField < 0, False);
        FillFieldsGroupBy;
        SetGroupField(IfThen(FGroupField >= customFieldLow, -1, FGroupField));
        FillFieldsGrid;
        PreGenerateHTMLTemplate;
        //ThumbsViewerStart; -> Done in RefreshMovieList
        FCatalogFile.Modified := False;
        FCurrentVersion := 99;
        UpdateMRU(AFileName);
        IntProgress := Maximum;
      finally
        Close;
      end;
    end;
  except
    on E: Exception do
    begin
      ProgressWin.Close;
      Application.ProcessMessages;
      FCatalogFile.New;
      MessageWin.Execute(E.Message,mtError,[mbOk]);
    end;
  end;
  RefreshMovieList(False, False);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.OnSaveFile(Sender: TObject; AFileName: TFileName; var Result: Boolean);
var
  ResXML, SaveFormat: Integer;
  Cancel: Boolean;
begin
  ThumbsViewerStop;
  Result := True;
  SaveCurrentItem; // To be sure that the current item is also saved
  with ProgressWin do
  begin
    Application.ProcessMessages;
    Maximum := 2;
    Status := Format(Messages.Strings[msgSaving],[ExtractFileName(AFileName)]);
    IntProgress := 0;
    Execute(Self);
    try
      try
        SaveFormat := -1;
        Cancel := False;
        ResXML := 2;
        with FCatalogFile.SaveDialog do
          case FilterIndex of
            DialogCatalogFilterAMC:
              SaveFormat := extAMC;
            DialogCatalogFilterAMC35:
            begin
              SaveFormat := extAMC;
              if MovieList.CustomFieldsProperties.Count > 0 then
              begin
                case MessageWin.Execute(Messages.Strings[msgSaveAMC35Warning], mtConfirmation, [mbYes, mbNo, mbCancel]) of
                  1: FCurrentVersion := 35;
                  2: FCurrentVersion := 99;
                  else Cancel := True;
                end;
              end else
                FCurrentVersion := 35;
            end;
            DialogCatalogFilterXML:
            begin
              SaveFormat := extXML;
              if MovieList.HasImages(True, False, False) then
                ResXML := MessageWin.Execute(InsertLineBreaks(Messages.Strings[msgXmlConvertImages]), mtConfirmation, [mbOk, mbIgnore, mbCancel]);
              if (ResXML in [0, 3]) then
                Cancel := True;
            end;
            else Cancel := True;
          end;

        if not Cancel then
        begin
          MovieList.Sort(fieldNumber);
          FrmMovieCustom.SaveFieldsProperties;
          DeleteFile(ChangeFileExt(AFileName,'.bak'));
          RenameFile(AFileName, ChangeFileExt(AFileName,'.bak'));
          case SaveFormat of
            extAMC: MovieList.SaveToFile(AFileName, FCurrentVersion);
            extXML: MovieList.SaveToXML(AFileName, ResXML = 1{, Settings.rOptions.rFiles.XMLHeader})
          end;
          UpdateMRU(AFileName);
          if not Settings.rOptions.rFiles.Backup then
          begin
            DeleteFile(ChangeFileExt(AFileName,'.bak'));
          end;
        end;

        IntProgress := Maximum;
      finally
        Close;
      end;
      Application.ProcessMessages;
    except
      on E: Exception do
      begin
        MessageWin.Execute(E.Message,mtError,[mbOk]);
        Result := False;
      end;
    end;
  end;
  ThumbsViewerStart;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.OnDialogTypeChange(Sender: TObject);
var
  NewName, NewExt: string;
begin
  if Sender is TSaveDialog then
    with Sender as TSaveDialog do
    begin
      if not DirectoryExists(FileName) then
      begin
        case FilterIndex of
          DialogCatalogFilterAMC, DialogCatalogFilterAMC35:
            begin
              NewName := ChangeFileExt(ExtractFileName(FileName), extCatalog[extAMC]);
              NewExt := extCatalog[extAMC];
            end;
          DialogCatalogFilterXML:
            begin
              NewName := ChangeFileExt(ExtractFileName(FileName), extCatalog[extXML]);
              NewExt := extCatalog[extXML];
            end;
        end;
        Delete(NewExt, 1, 1);
        SendMessage(Windows.GetParent(Handle), CDM_SETCONTROLTEXT, 1152, Integer(PChar(NewName)));
        SendMessage(Windows.GetParent(Handle), CDM_SETDEFEXT, 1152, Integer(PChar(NewExt)));
      end;
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SetTitle;
var
  FileName: string;
begin
  if FCatalogFile.CurrentFile = '' then
    FileName := '<untitled>'
  else
    FileName := FCatalogFile.CurrentFile;
  Caption := Format(strDefaultCaption,[strVersion, FileName]);
  {$IFDEF DISABLETHEMES}
  Caption := Format('%s [themes support disabled]', [Caption]);
  {$ENDIF}
  {$IFDEF ANTDEBUG}
  Caption := Format('%s [BETA/DEBUG]', [Caption]);
  {$ENDIF}
  Application.Title := Format(strDefaultTask, [ExtractFileName(FileName)]);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SetStatus;
var
  MoviesCount: Integer;
begin
  if MovieList <> nil then
    MoviesCount := MovieList.Count
  else
    MoviesCount := 0;
  with StatusBar1.Panels.Items[panelCount] do
  begin
    Caption := Format(Messages.Strings[msgStatusCount], [MoviesCount]) + ' ' +
      Format(Messages.Strings[msgStatusVisible], [MoviesVisible]);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.UpdateMRU(const strFilePath: string);
begin
  if strFilePath <> '' then
    TBMRUList1.Add(strFilePath);
  ActionFileOpenNoRecent.Visible := TBMRUList1.Items.Count = 0;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.TBMRUList1Click(Sender: TObject;
  const Filename: String);
begin
  FCatalogFile.Open(FileName)
end;

{-------------------------------------------------------------------------------
   Misc.
-------------------------------------------------------------------------------}

procedure TMainWindow.RefreshMovieList(const KeepSelection,SaveColumnSettingsCF: Boolean);
var
  i, k: Integer;
  GroupItem, GroupItemUnique, Item: TElTreeItem;
  MovieFilter: TMovieFilter;
  GridMode, ItemInList: Boolean;
  GroupUnique, GroupCount, GroupExpand: Boolean;

  Groups : TStringList;
  GroupMovies: TObjectList;
  GroupName: string;
  iGroup, iMovie: Integer;
  Movie: TMovie;
  Prop: TCustomFieldProperties;

  // Args: IN [GroupName] OUT [GroupItem]
  procedure AddGroupI;
  begin
    GroupItem := ListView1.Items.AddChild(nil, GroupName);
    with GroupItem do
    begin
      Data := nil;
      Tag := 0;
      Expanded := GroupExpand;
    end;
  end;

  // Args: IN [GroupItem]
  procedure AddGroupCount;
  begin
    if GroupCount and (GroupItem <> nil) and (GroupItem <> GroupItemUnique) then
      GroupItem.Text := Format('%s (%d)', [GroupItem.Text, GroupItem.Count]);
  end;

begin
  if MovieList = nil then
    Exit;
  ThumbsViewerStop;
  if KeepSelection then
    StoreSelectedState;
  ListView1.DeselectAll;
  HTMLViewerUpdate;
  MovieSelected;
  try
    with Settings.rOptions do
    begin
      GroupUnique := rMovieList.GroupUnique;
      GroupCount := rMovieList.GroupCount;
      GroupExpand := rMovieList.GroupExpand;
    end;
    GroupItem := nil;
    GroupItemUnique := nil;
    SaveCurrentItem;
    FPreviousItem := nil;
    MovieFilter := nil;
    MoviesVisible := 0;
    GroupItemEmpty := nil;
    if ActionFindDisplay.Checked then
    begin
      MovieFilter := TMovieFilter.Create;
      MovieFilter.value := EValue.Text;
      MovieFilter.field := cbxField.ItemIndex - 1;
      if MovieFilter.field >= fieldCount then // It is a custom field
        MovieFilter.field := MovieFilter.field - fieldCount + customFieldLow;
      MovieFilter.wholeField := ActionFindWholefield.Checked;
      MoviesVisible := 0;
    end;
    GridMode := ActionToolsGrid.Checked;

    with ListView1 do
    begin
      Items.BeginUpdate;
      try
        Items.Clear;

        // *** Building headers ***
        SaveColumnSettings(SaveColumnSettingsCF);
        with HeaderSections do
        begin
          i := Count-1;
          while i > 1 do
          begin
            DeleteSection(Item[i]);
            Dec(i);
          end;

          if GridMode then
          begin
            ListView1.OnCompareItems := ListView1CompareItems;
            Item[0].Visible := False;
            Item[1].Visible := False;
            with AddSection do
            begin
              Text := strFields.Strings[fieldNumber];
              FieldName := IntToStr(fieldNumber);
              FieldType := sftCustom;//sftNumber;
              Alignment := hsaRight;
            end;
            with AddSection do
            begin
              Text := strFields.Strings[fieldFormattedTitle];
              FieldName := IntToStr(fieldFormattedTitle);
              FieldType := sftCustom;//sftText;
            end;
            for i := 0 to fieldCount-1 do
            begin
              if not (i in [fieldNumber, fieldFormattedTitle]) then
              begin
                with AddSection do
                begin
                  Text := strFields.Strings[i];
                  FieldName := IntToStr(i);
                  case GetFieldType(i) of
                    ftInteger:
                      begin
                        FieldType := sftCustom;//sftNumber;
                        Alignment := hsaRight;
                      end;
                    ftReal:
                      begin
                        FieldType := sftCustom;//sftFloating;
                        Alignment := hsaRight;
                      end;
                    ftDate:
                      begin
                        FieldType := sftCustom;//sftDate;
                      end;
                  else
                    FieldType := sftCustom;//sftText;
                  end;
                end;
              end;
            end;
            with MovieList.CustomFieldsProperties do
              for i := 0 to Count-1 do
              begin
                Prop := Objects[i];
                with AddSection do
                begin
                  Text := Prop.FieldName;
                  FieldName := IntToStr(customFieldLow + i);
                  case Prop.FieldType of
                    ftInteger:
                      begin
                        FieldType := sftCustom;//sftNumber;
                        Alignment := hsaRight;
                      end;
                    ftReal:
                      begin
                        FieldType := sftCustom;//sftFloating;
                        Alignment := hsaRight;
                      end;
                    ftDate:
                      begin
                        FieldType := sftCustom;//sftDate;
                      end;
                  else
                    FieldType := sftCustom;//sftText;
                  end;
                end;
              end;
            MainTreeColumn := 3;
            HideHorzScrollBar := False;
            MoveColumnOnDrag := True;
            DraggableSections := True;
            // Set column order, visible and grid fields menu (checked or not)
            RestoreColumnSettings;
          end else
          begin
            ListView1.OnCompareItems := ListView1CompareItems;
            with HeaderSections do
            with AddSection do
            begin
              Text := 'Invisible';
              FieldType := sftCustom;
              Visible := False;
            end;
            Item[0].Visible := Settings.rOptions.rMovieList.MovieNumColumn;
            Item[0].FieldName  := IntToStr(fieldNumber);
            Item[1].Visible := True;
            Item[1].FieldName  := IntToStr(fieldFormattedTitle);
            MainTreeColumn := 1;
            HideHorzScrollBar := True;
            MoveColumnOnDrag := False;
            DraggableSections := False;
            // Just set grid fields menu (checked or not)
            RestoreColumnSettings;
          end; // if gridmode
          SetSortField(Abs(FSortField) - 1, FSortField < 0, False);
        end; // with headers

        // *** Filling the list ***
        if (MovieList <> nil) then
        begin
          // *** Remove all associated items for a movie ***
          for k := 0 to MovieList.Count-1 do
            MovieList.Items[k]._listItems.Clear;

          // *** Group movies by selected field ***
          Groups := MovieList.GetMoviesByGroups(FGroupField, mioAll, MovieFilter);

          // *** For all groups ***
          for iGroup := 0 to Groups.Count-1 do
          begin
            AddGroupCount;
            GroupName := Groups.Strings[iGroup];
            GroupMovies := TObjectList(Groups.Objects[iGroup]);

            // ** If group wanted ***
            if FGroupField <> -1 then
              if GroupUnique and (GroupMovies.Count = 1) and
                (AnsiCompareStr(GroupName, Messages.Strings[msgGroupEmpty]) <> 0) and
                (AnsiCompareStr(GroupName, strErrorParenthesis) <> 0) then
                if GroupItemUnique = nil then
                begin
                  GroupName := Messages.Strings[msgGroupUnique];
                  AddGroupI;
                  GroupItemUnique := GroupItem;
                end else
                  GroupItem := GroupItemUnique
              else if GroupName = '$$$EMPTY$$$' then
              begin
                GroupName := Messages.Strings[msgGroupEmpty];
                AddGroupI;
                GroupItemEmpty := GroupItem; // We keep group empty for next add !
              end
              else
                AddGroupI;

            // *** For all movies in group ***
            for iMovie := 0 to GroupMovies.Count-1 do
            begin
              Movie := TMovie(GroupMovies.Items[iMovie]);
              with Movie do
              begin
                // *** Check if movie is already in group ***
                ItemInList := False;
                for k := 0 to Movie._listItems.Count-1 do
                begin
                  Item := TElTreeItem(Movie._listItems.Items[k]);
                  if Item.Parent = GroupItem then
                  begin
                    ItemInList := True;
                    break;
                  end;
                end;
                // *** If movie is not already in group ***
                if not ItemInList then
                begin
                  AddItem(Movie, GroupItem);
                end; // Check if movie is already in group !
              end; // with Movie
            end; // if movie is not already in group
          end; // for all groups
          AddGroupCount; // add last group count
          GroupItem := GroupItemUnique;
          GroupItemUnique := nil;
          AddGroupCount; // add group count for unique group
          FreeObjects(Groups);
          Groups.Free;
        end; // if movielist <> nil
      finally
      end;
    end;
    SetStatus;
  finally
    ListView1Resize(Self);
  end;
  ListView1.Items.EndUpdate;
  if KeepSelection then
    LoadSelectedState;
  ListView1EnsureVisiblePlus;
  ThumbsViewerStart;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SaveColumnSettings(const SaveCustomFields: Boolean);
var
  i, idx, Field, posError : Integer;
  s, s2, tmp, newValue : string;
  Section : TElHeaderSection;
  Prop : TCustomFieldProperties;
begin
  if MovieList = nil then
    Exit;
  if (ListView1.HeaderSections.Item[1].Visible) then // not grid
  begin
    if SaveCustomFields then
    begin
      if Settings.rMain.ColumnSettings = '' then
        Settings.rMain.ColumnSettings :=
          'i0:w40:vf,i1:w120:vf,i2:w120:vt,i3:w120:vt,i4:w120:vt,i5:w120:vt,i6:w120:vt,i7:w120:vt,i8:w120:vt,i9:w120:vt,i10:w120:vt,' +
          'i11:w120:vt,i12:w120:vt,i13:w120:vt,i14:w120:vt,i15:w120:vt,i16:w120:vt,i17:w120:vt,i18:w120:vt,i19:w120:vt,i20:w120:vt,' +
          'i21:w120:vt,i22:w120:vt,i23:w120:vt,i24:w120:vt,i25:w120:vt,i26:w120:vt,i27:w120:vt,i28:w120:vt,i29:w120:vt,i30:w120:vt,i31:w120:vt,i32:w120:vt,';

      newValue := Settings.rMain.ColumnSettings;
      s2 := '';

      with ListView1 do
      with HeaderSections do
        while newValue <> '' do
        begin
          s := copy(newValue, 2, pos(':', newValue) - 2);
          delete(newValue, 1, pos(':', newValue));
          idx := StrToInt(s);
          s2 := s2 + 'i' + s + ':';
          s := copy(newValue, 2, pos(':', newValue) - 2);
          delete(newValue, 1, pos(':', newValue));
          if idx < 2 then
            s2 := s2 + 'w' + IntToStr(Item[idx].Width) + ':'
          else
            s2 := s2 + 'w' + s + ':';
          delete(newValue, 1, 3);
          if idx >= 2 then
          begin
            idx := idx - 2;
            if idx = 1 then
              idx := fieldFormattedTitle
            else if (idx <> 0) and (idx <= fieldFormattedTitle) then
              Dec(idx);
            if (idx < MnuTlsGridFields.Count) and (not MnuTlsGridFields.Items[idx].Checked) then
              s2 := s2 + 'vf,'
            else
              s2 := s2 + 'vt,';
          end else
            s2 := s2 + 'vf,';
        end;

      Settings.rMain.ColumnSettings := s2;

      posError := 0;
      s2 := '';
      with MovieList.CustomFieldsProperties do
        if ColumnSettings = '' then
          for i := 0 to Count-1 do
            ColumnSettings := ColumnSettings + Strings[i] + ':'+'p'+IntToStr(fieldCount+2) + ':w120:vt,';
      newValue := MovieList.CustomFieldsProperties.ColumnSettings;
      while newValue <> '' do
      begin
        s := copy(newValue, 1, pos(':', newValue) - 1);
        delete(newValue, 1, pos(':', newValue));
        idx := MovieList.CustomFieldsProperties.IndexOf(s);
        if idx = -1 then
        begin
          inc(posError);
          delete(newValue, 1, pos(',', newValue));
        end
        else
        begin
          s2 := s2 + s + ':';
          s := copy(newValue, 2, pos(':', newValue) - 2);
          delete(newValue, 1, pos(':', newValue));
          i := StrToInt(s) - posError;
          s2 := s2 + 'p' + IntToStr(i) + ':';
          s := copy(newValue, 2, pos(':', newValue) - 2);
          delete(newValue, 1, pos(':', newValue));
          s2 := s2 + 'w' + s + ':';
          delete(newValue, 1, 3);
          if (idx < MnuTlsGridCustomFields.Count) and (not MnuTlsGridCustomFields.Items[idx].Checked) then
            s2 := s2 + 'vf,'
          else
            s2 := s2 + 'vt,';
        end;
      end;
      if MovieList.CustomFieldsProperties.ColumnSettings <> s2 then
      begin
        MovieList.CustomFieldsProperties.ColumnSettings := s2;
        FCatalogFile.Modified := True;
      end;
    end;
  end else
  begin
    s := '';
    s2 := '';
    with ListView1 do
    with HeaderSections do
      for i := 0 to Count-1 do
      begin
        Section := ItemByPos[i];
        Field := StrToIntDef(Section.FieldName, 0);
        if Field < fieldCount then
        begin
          tmp := 'i' + IntToStr(Section.Index) + ':w' + IntToStr(Section.Width) + ':v';
          if Section.Visible then
            tmp := tmp + 't,'
          else
            tmp := tmp + 'f,';
          s := s + tmp;
        end else if SaveCustomFields and (Field >= customFieldLow) and
          (Field - customFieldLow < MovieList.CustomFieldsProperties.Count) then
        begin
          Prop := MovieList.CustomFieldsProperties.Objects[Field - customFieldLow];
          tmp := Prop.FieldTag + ':p' + IntToStr(i) + ':w' + IntToStr(Section.Width) + ':v';
          if Section.Visible then
            tmp := tmp + 't,'
          else
            tmp := tmp + 'f,';
          s2 := s2 + tmp;
        end;
       end;
      Settings.rMain.ColumnSettings := s;
      if SaveCustomFields then
      begin
        if MovieList.CustomFieldsProperties.ColumnSettings <> s2 then
        begin
          MovieList.CustomFieldsProperties.ColumnSettings := s2;
          FCatalogFile.Modified := True;
        end;
      end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.RestoreColumnSettings;
var
  i, idx, posError : integer;
  Section : TElHeaderSection;
  newValue, s : string;
  GridMode : Boolean;
begin
  if MovieList = nil then
    Exit;
  GridMode := False;
  if (not ListView1.HeaderSections.Item[1].Visible) then
    GridMode := True;
  // Set grid fields visible by default (menu)
  with MnuTlsGridFields do
    for i := 0 to Count-1 do
      MnuTlsGridFields.Items[i].Checked := True;
    MnuTlsGridFields.Items[fieldFormattedTitle].Enabled := False;
  with MnuTlsGridCustomFields do
    for i := 0 to Count-1 do
      MnuTlsGridCustomFields.Items[i].Checked := True;

  i := 0;
  newValue := Settings.rMain.ColumnSettings;
  with ListView1 do
  with HeaderSections do
    while newValue <> '' do
    begin
      Section := nil;
      s := copy(newValue, 2, pos(':', newValue) - 2);
      delete(newValue, 1, pos(':', newValue));
      idx := StrToInt(s);
      if GridMode then Section := Item[idx];
      if GridMode then MoveSection(Section, i);
      s := copy(newValue, 2, pos(':', newValue) - 2);
      delete(newValue, 1, pos(':', newValue));
      if GridMode then Section.Width := StrToInt(s);
      if (pos('vf,', newValue) = 1) and (idx <> 3) {FormattedTitle / MainColumn} then
      begin
        if GridMode then Section.Visible := False;
        if idx >= 2 then
        begin
          idx := idx - 2;
          if idx = 1 then
            idx := fieldFormattedTitle
          else if (idx <> 0) and (idx <= fieldFormattedTitle) then
            Dec(idx);
          if idx < MnuTlsGridFields.Count then
            MnuTlsGridFields.Items[idx].Checked := False;
        end;
      end
      else
        if GridMode then Section.Visible := True;
      delete(newValue, 1, 3);
      inc(i);
    end;

  posError := 0;
  newValue := MovieList.CustomFieldsProperties.ColumnSettings;
  with ListView1 do
  with HeaderSections do
    while newValue <> '' do
    begin
      Section := nil;
      s := copy(newValue, 1, pos(':', newValue) - 1);
      delete(newValue, 1, pos(':', newValue));
      idx := MovieList.CustomFieldsProperties.IndexOf(s);
      if idx = -1 then
      begin
        inc(posError);
        delete(newValue, 1, pos(',', newValue));
      end
      else
      begin
        if GridMode then Section := Item[fieldCount + 2 + idx];
        //if StrToInt(Section.FieldName) <> (idx + customFieldLow) then
        //  showmessage('Error: FieldName='+Section.FieldName+'|Value='+ IntToStr(idx + customFieldLow));
        s := copy(newValue, 2, pos(':', newValue) - 2);
        delete(newValue, 1, pos(':', newValue));
        i := StrToInt(s) - posError;
        if GridMode then MoveSection(Section, i);
        s := copy(newValue, 2, pos(':', newValue) - 2);
        delete(newValue, 1, pos(':', newValue));
        if GridMode then Section.Width := StrToInt(s);
        if (pos('vf,', newValue) = 1) then
        begin
          if GridMode then Section.Visible := False;
          if idx < MnuTlsGridCustomFields.Count then
            MnuTlsGridCustomFields.Items[idx].Checked := False;
        end
        else
          if GridMode then Section.Visible := True;
        delete(newValue, 1, 3);
      end;
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.GetTitle: string;
begin
  with FrmMovie do
    Result := GetFormattedTitle(EOriginalTitle.Text, ETranslatedTitle.Text, EMedia.Text, ColumnTitleDisplay, UsePrefixes)
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.MovieSelected;
var
  i: Integer;
  bPic, isGroup: Boolean;
  SelCount: Integer;
begin
  SelCount := ListView1.SelectedCount;
  isGroup := False;
  with ListView1 do
  begin
    //WARNING: Some bugs in TElTree with CTRL+Selection !
    //Need to check SelCount and Selected because when SelCount = 1, Selected can be nil !!!
    if (SelCount = 1) then
      if (Selected <> nil) then
      begin
        if (Selected.Data = nil) then
        begin
          SelCount := 0;
          isGroup := True;
        end;
      end else
        SelCount := 0;
  end;
  with FrmMovie do
    for i := 0 to ControlCount-1 do
      Controls[i].Enabled := SelCount = 1;
  FrmMovieCustom.AllowEdit := SelCount = 1;
  if SelCount <> 1 then
    RemoveMoviePicture;
  ActionMovieDelete.Enabled := ((SelCount > 0) or isGroup) and
    ((ActionDisplayThumbnails.Checked = False) or (ThumbsViewer.Selection.Count > 0));
  ActionMovieSelGroup.Enabled := isGroup and (ActionDisplayThumbnails.Checked = False);
  ActionMovieSelCheck.Enabled := (ActionDisplayThumbnails.Checked = False);
  ActionMovieSelUncheck.Enabled := (ActionDisplayThumbnails.Checked = False);
  ActionMovieCheck.Enabled := (SelCount > 0) and
    ((ActionDisplayThumbnails.Checked = False) or (ThumbsViewer.Selection.Count > 0));
  ActionMovieUncheck.Enabled := (SelCount > 0) and
    ((ActionDisplayThumbnails.Checked = False) or (ThumbsViewer.Selection.Count > 0));
  ActionMovieSearch.Enabled := SelCount = 1;
  ActionMovieNumber.Enabled := SelCount = 1;
  ActionMovieUndo.Enabled := SelCount = 1;
  ActionMovieCopy.Enabled := SelCount = 1;
  ActionMoviePaste.Enabled := (SelCount = 1) and (MovieCopy <> nil);
  ActionPicSelect.Enabled := SelCount = 1;
  ActionMovieImportFiles.Enabled := (SelCount < 2);
  if SelCount = 1 then
    with ListView1.Selected do
    begin
      bPic := TMovie(Data).strPicture <> '';
      ActionPicDelete.Enabled := bPic;
      if (FCatalogFile <> nil) then
        SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
      bPic := bPic and ((TMovie(Data).Picture <> nil) or FileExists(ExpandFileName(TMovie(Data).strPicture)));
      ActionPicCopy.Enabled := bPic;
      ActionPicSaveAs.Enabled := bPic;
    end
  else
  begin
    ActionPicSaveAs.Enabled := False;
    ActionPicDelete.Enabled := False;
    ActionPicCopy.Enabled := False;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.LoadLists;
  procedure LoadList(const ACombo: TComboBox; ddlIndex: Integer);
  begin
    with Settings.rOptions.rMovieInformation.rCombo[ddlIndex] do
    begin
      if UseCatalogValues then
      begin
        if MovieList <> nil then
          MovieList.GetValues(ACombo.Items, strFieldsDdl[ddlIndex])
        else
          ACombo.Items.Clear;
      end else
        ACombo.Items.Assign(Contents);
      ACombo.Sorted := Sort;
      ACombo.AutoComplete := AutoComplete;
    end;
  end;
begin
  with FrmMovie do
  begin
    LoadList(ECountry, ddlCountry);
    LoadList(ECategory, ddlCategory);
    LoadList(EVideoFormat, ddlVideo);
    LoadList(EAudioFormat, ddlAudio);
    LoadList(ELanguages, ddlLanguages);
    LoadList(ESubtitles, ddlSubtitles);
    LoadList(EBorrower, ddlBorrowers);
    LoadList(EFramerate, ddlFramerate);
    LoadList(EMediaType, ddlMediaType);
    LoadList(ESource, ddlSources);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.LoadOptions;
var
  ButtonName: string;
  TheButton: TTBCustomItem;
  i: Integer;
begin
  ToolbarFind.Floating := True;
  TBCustomLoadPositions(Self, XmlReadInt, XmlReadString, Settings.rMain.Toolbars);
  try
    if Settings.rMain.Toolbars.Items.ItemNamed[ToolbarMain.Name] <> nil then
      with Settings.rMain.Toolbars.Items.ItemNamed[ToolbarMain.Name].Items do
        for i := 0 to Count-1 do
        begin
          ButtonName := Item[i].Properties.Value('Name');
          if ButtonName <> '' then
          begin
            TheButton := TTBCustomItem(Self.FindComponent(ButtonName));
            if TheButton <> nil then
              TheButton.Visible := Item[i].Properties.BoolValue('Visible', TheButton.Visible);
          end;
        end;
  except
  end;
  ActionDisplayMainToolbar.Checked := ToolbarMain.Visible;
  ActionDisplayPictureToolbar.Checked := ToolbarPicture.Visible;
  with Settings do
  begin
    case rMain.WindowState of
      0:
        begin
          WindowState := wsMinimized;
        end;
      1:
        begin
          WindowState := wsNormal;
          Width := rMain.WindowWidth;
          Height := rMain.WindowHeight;
          Left := rMain.WindowLeft;
          Top := rMain.WindowTop;
        end;
      2:
        begin
          WindowState := wsMaximized;
        end;
      else
        WindowState := wsNormal;
    end;
    with rMain do
    begin
      ActionDisplayStatusBar.Checked := Statusbar;
      ActionDisplayStatusBarExecute(Self);
      PanelLeft.Width := ListWidth;
      ToolbarPictureWindow.Height := 32; // Force Resize (Display Bug)
      ToolbarPictureWindow.Height := PictureDockedHeight;
      TBMRUList1.Items.Assign(MRU);
      UpdateMRU('');
      FGroupField := Group;
      if FGroupField > fieldCount then
        FGroupField := -1;
      FSortField := SortField;
      if Abs(FSortField)-1 > fieldCount then
        FSortField := (fieldNumber + 1) * IfThen(FSortField < 0, -1, 1);
      ActionFindWholefield.Checked := FindWholefield;
      if GridMode then
        ActionToolsGrid.Execute;
      if ListAlClient then
        ActionStretchList.Execute;
      if DisplayHTML then
        ActionDisplayHTML.Execute;
      if DisplayThumbnails then
        ActionDisplayThumbnails.Execute;
    end;
  end; // with Settings do
  ActionMovieFind.Checked := ToolbarFind.Visible;
  ActionMoviePictureShow.Checked := ToolbarPictureWindow.Visible;
  SplitterBottomList.Visible := ToolbarPictureWindow.Visible and not ToolbarPictureWindow.Floating;
  ApplyOptions;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SaveLists;
  procedure SaveList(const ACombo: TComboBox; ddlIndex: Integer);
  begin
    with Settings.rOptions.rMovieInformation do
      if (not rCombo[ddlIndex].UseCatalogValues) and (rCombo[ddlIndex].AutoAdd) and (ACombo.Items.Count > 0) then
        rCombo[ddlIndex].Contents.Assign(ACombo.Items);
  end;
begin
  with FrmMovie do
  begin
    SaveList(ECountry, ddlCountry);
    SaveList(ECategory, ddlCategory);
    SaveList(EVideoFormat, ddlVideo);
    SaveList(EAudioFormat, ddlAudio);
    SaveList(ELanguages, ddlLanguages);
    SaveList(ESubtitles, ddlSubtitles);
    SaveList(EBorrower, ddlBorrowers);
    SaveList(EFramerate, ddlFramerate);
    SaveList(EMediaType, ddlMediaType);
    SaveList(ESource, ddlSources);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.UpdateLists;
  procedure UpdateList(const Combo: TComboBox; DdlIndex: Integer);
  begin
    with Settings.rOptions.rMovieInformation.rCombo[DdlIndex] do
      if (AutoAdd or UseCatalogValues) and (Combo.Text <> '') and (Combo.Items.IndexOf(Combo.Text) = -1) then
        Combo.Items.Add(Combo.Text);
  end;
begin
  with FrmMovie do
  begin
    UpdateList(ECountry, ddlCountry);
    UpdateList(ECategory, ddlCategory);
    UpdateList(EVideoFormat, ddlVideo);
    UpdateList(EAudioFormat, ddlAudio);
    UpdateList(ELanguages, ddlLanguages);
    UpdateList(ESubtitles, ddlSubtitles);
    UpdateList(EBorrower, ddlBorrowers);
    UpdateList(EFramerate, ddlFramerate);
    UpdateList(EMediaType, ddlMediaType);
    UpdateList(ESource, ddlSources);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SaveOptions;
var
  i: Integer;
begin
  with Settings do
  begin
    case WindowState of
      wsMinimized:
        rMain.WindowState := 0;
      wsNormal:
        begin
          rMain.WindowState := 1;
          rMain.WindowWidth := Width;
          rMain.WindowHeight := Height;
          rMain.WindowLeft := Left;
          rMain.WindowTop := Top;
        end;
      wsMaximized:
        rMain.WindowState := 2;
    end;
    with rMain do
    begin
      Statusbar := ActionDisplayStatusBar.Checked;
      if ActionStretchList.Checked then
        ListWidth := PanelLeftOldWidth
      else
        ListWidth := PanelLeft.Width;
      PictureDockedHeight := ToolbarPictureWindow.Height;
      MRU.Assign(TBMRUList1.Items);
      if FGroupField < fieldCount then
        Group := FGroupField
      else
        Group := -1;
      if Abs(FSortField)-1 < fieldCount then
        SortField := FSortField
      else
        SortField := (fieldNumber + 1) * IfThen(FSortField < 0, -1, 1);
      GridMode := ActionToolsGrid.Checked;
      ListAlClient := ActionStretchList.Checked;
      DisplayHTML := ActionDisplayHTML.Checked;
      DisplayThumbnails := ActionDisplayThumbnails.Checked;
      FindWholefield := ActionFindWholefield.Checked;
      if cbxField.ItemIndex - 1 < fieldCount then
        FindField := cbxField.ItemIndex - 1
      else
        FindField := -1;
      SaveColumnSettings;
    end;
    version := strVersion;

    // Save Toolbars
    Settings.rMain.Toolbars.Clear;
    TBCustomSavePositions(Self, XmlWriteInt, XmlWriteString, Settings.rMain.Toolbars);
    with Settings.rMain.Toolbars.Items.ItemNamed[ToolbarMain.Name].Items do
      for i := 0 to ToolbarMain.Items.Count-1 do
        with Add('Button') do
        begin
          Properties.Add('Name', ToolbarMain.Items.Items[i].Name);
          Properties.Add('Visible', ToolbarMain.Items.Items[i].Visible);
        end;

    // Save drop-down lists
    SaveLists;

    // Write to file
    Save;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ApplyOptions;
var
  bState:  Boolean;
  tb_color, tb_grey: TBitmap;
  ToolbarColorType: Integer;
  s: string;
  ClassicIconTheme: Boolean;
  procedure SetToolbarImages(const AImageList: TCustomImageList);
  begin
    if Settings.rOptions.rDisplay.ImagesInMenu then
    begin
      ToolbarMenu.Images := AImageList;
      PopupMovieList.Images := AImageList;
      PopupToolbar.Images := AImageList;
      PopupImage.Images := AImageList;
      PopupEURL.Images := AImageList;
      PopupHTMLViewer.Images := AImageList;
    end;
    ToolbarMain.Images := AImageList;
    ToolbarPicture.Images := AImageList;
    ToolbarImages := AImageList;
  end;
begin
  with Settings.rOptions do
  begin

    { --- Toolbar icons, skin & colors --- }

    tb_color := TBitmap.Create;
    tb_grey := TBitmap.Create;
    try
      ClassicIconTheme := True;
      s := strDirApp + strDirToolbars + rDisplay.IconSet + '.bmp';
      if FileExists(s) then
        try
          tb_color.LoadFromFile(s);
          ClassicIconTheme := False;
        except
        end;
      if ClassicIconTheme then
        tb_color.LoadFromResourceName(HInstance, 'TOOLBARCLASSIC');
      tb_color.PixelFormat := pf24bit;
      ToolbarColorType := rDisplay.ColorType;
      if ToolbarColorType = 0 then
        SetToolbarImages(ImageListHot)
      else
      begin
        SetToolbarImages(ImageListNormal);
        tb_grey.Assign(tb_color);
        case ToolbarColorType of
          1: GrayScale(tb_grey);
          2: Blend(tb_grey, clWhite);
          3: Blend(tb_grey, clDkGray);
        end;
        ImageListNormal.Clear;
        ImageListNormal.Height := tb_grey.Height;
        ImageListNormal.Width := ImageListNormal.Height;
        ImageListNormal.AddMasked(tb_grey, tb_grey.Canvas.Pixels[0, 0]);
      end;
      ImageListHot.Clear;
      ImageListHot.Height := tb_color.Height;
      ImageListHot.Width := ImageListHot.Height;
      ImageListHot.AddMasked(tb_color, tb_color.Canvas.Pixels[0, 0]);
    finally
      tb_color.Free;
      tb_grey.Free
    end;
    with ImageListHot do
    begin
      GetIcon(Ord(ICON_MOVIENUMBER), NumberWin.Icon);
    end;


    FrmMovieCustom.SetImageList(ImageListHot);


    {$IFNDEF DISABLETHEMES}
    if rDisplay.OfficeXP then
      TBXSwitcher1.Theme := 'OfficeXP'
    else
      TBXSwitcher1.Theme := 'Default';
    {$ENDIF}


    { --- Comboboxes --- }

    SaveLists;
    LoadLists;

    { -- Other options --- }

    TBMRUList1.MaxItems := rFiles.RecentFiles;

    ColumnTitleDisplay := rMovieList.TitleColumn;
    UsePrefixes := rMovieList.UsePrefixes;

    bState := rMovieList.Checkboxes;
    listview1.ShowCheckboxes := bState;
    //ActionMovieCheck.Enabled := bState;
    //ActionMovieUncheck.Enabled := bState;
    //ActionMovieSelCheck.Enabled := bState;
    //ActionMovieSelUncheck.Enabled := bState;

    with ListView1 do
    begin
      Tracking := rMovieList.HotTrack;
      UseCustomScrollBars := rMovieList.EnhancedScrollbars;
    end;

    ActionMoviePrevious.ShortCut := rMovieList.ShortcutPrev;
    ActionMovieNext.ShortCut := rMovieList.ShortcutNext;

    s := FrmMovie.ERating.Hint;
    if rMovieInformation.RatingTrunc then
      FrmMovie.ERating.Decimal := 0
    else
      FrmMovie.ERating.Decimal := 1;

    if rMovieInformation.PictureBackground = clDefault then
      ScrollBox1.Color := clBtnFace
    else
      ScrollBox1.Color := rMovieInformation.PictureBackground;

    if rMovieInformation.PictureFitWindow then
    begin
      MoviePicture.Align := alClient;
      MoviePicture.Stretch := true;
    end else
    begin
      MoviePicture.Align := alNone;
      MoviePicture.Stretch := false;
    end;

    if rDisplay.SoftBorders then
    begin
      with ListView1 do
      begin
        Flat := True;
        HeaderFlat := True;
        VertScrollBarStyles.ActiveFlat := True;
        VertScrollBarStyles.Flat := True;
      end;
      ScrollBox1.BorderStyle := bsNone;
      ScrollBox1.BevelKind := bkTile;
      PanelMovieInfos.BorderStyle := bsNone;
      PanelMovieInfos.BevelOuter := bvLowered;
    end else
    begin
      with ListView1 do
      begin
        Flat := False;
        HeaderFlat := IsThemedXP;
        VertScrollBarStyles.ActiveFlat := False;
        VertScrollBarStyles.Flat := False;
      end;
      ScrollBox1.BorderStyle := bsSingle;
      ScrollBox1.BevelKind := bkNone;
      PanelMovieInfos.BorderStyle := bsSingle;
      PanelMovieInfos.BevelOuter := bvNone;
    end;

    RebuildSearchMenu;

  end; // with Settings.rOptions

  FrmMovie.LSizeUnit.Caption := Settings.GetFilesSizeUnit(Messages.Strings[msgByteString]);

end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ApplyLanguage;
var
  LangFile: TFileName;
  i: Integer;
begin
  with Settings.rOptions do
  begin
    if rLanguage.Language = '?' then
      ActionLanguage.Execute;
    LangFile := Settings.GetLanguageFile;
    if (LangFile <> '') and (rLanguage.Language <> Settings.Language) and FileExists(LangFile) then
    begin
      Translator.SetLanguageFile(LangFile);
      Self.Font.Name := Graphics.DefFontData.Name;
      Self.Font.Charset := Graphics.DefFontData.Charset;
      Translator.Translate(MainWindow);
      Translator.Translate(FrmMovie);
      FrmMovieCustom.Translate;
      NumberWin.Translate;
      InputWin.Translate;
      MessageWin.Translate;
    end;
    Settings.Language := rLanguage.Language;
    strHelpFile := ChangeFileExt(LangFile, '.chm');
    if not FileExists(strHelpFile) then
      strHelpFile := strDirApp + strDirLanguages + strFileHelp;
    FCatalogFile.MessageSaveQuery := Messages.Strings[msgUnsavedFile];
    FrmMovie.LSizeUnit.Caption := Settings.GetFilesSizeUnit(Messages.Strings[msgByteString]);
  end;
  strFieldPicture := Messages.Strings[msgPicture];
  TabMovieInfos.Tabs.Strings[0] := Messages.Strings[msgMovieFields];
  TabMovieInfos.Tabs.Strings[1] := Messages.Strings[msgCustomFields];
  RebuildSearchMenu;
  { --- Sort by --- }
  MnuTlsSortFields.Caption := Messages.Strings[msgMovieFields];
  MnuTlsSortCustomFields.Caption := Messages.Strings[msgCustomFields];
  FillFieldsSortBy;
  //SetSortField(Abs(FSortField) - 1, FSortField < 0); // -> Updated in RefreshMovieList
  { --- Group by --- }
  MnuTlsGroupFields.Caption := Messages.Strings[msgMovieFields];
  MnuTlsGroupCustomFields.Caption := Messages.Strings[msgCustomFields];
  FillFieldsGroupBy;
  SetGroupField(FGroupField);
  { --- Grid Fields --- }
  SaveColumnSettings;
  MnuTlsGridFields.Caption := Messages.Strings[msgMovieFields];
  MnuTlsGridCustomFields.Caption := Messages.Strings[msgCustomFields];
  FillFieldsGrid;
  { --- Find --- }
  i := cbxField.ItemIndex;
  if i = -1 then
    if Settings.rMain.FindField < fieldCount then
      FillFieldsCBSearch(Settings.rMain.FindField + 1)
    else
      FillFieldsCBSearch(0)
  else
    FillFieldsCBSearch(i);
  { --- HTMLViewer --- }
  PreGenerateHTMLTemplate;
  { --- ListView1 --- }
  RefreshMovieList(True, False);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.RebuildSearchMenu;
var
  newMenuItem: TTBXItem;
  i: Integer;
begin
  MnuMovSch.Clear;
  with Settings.rOptions.rMovieInformation.SearchSites do
    for i := 0 to Count-1 do
    begin
      if Pos('-', Strings[i]) = 1 then
        MnuMovSch.Add(TTBXSeparatorItem.Create(MnuMovSch))
      else
      begin
        newMenuItem := TTBXItem.Create(MnuMovSch);
        MnuMovSch.Add(newMenuItem);
        with newMenuItem do
        begin
          Caption := Names[i];
          Hint := Format(Messages.Strings[msgSearchInfo], [Names[i]]);
          Tag := i;
          OnClick := SearchInternetClick;
        end;
      end; // if-else
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FillFieldsSortBy;
var
  i: Integer;
  newMenuItem: TTBXItem;
begin
  with MnuTlsSortFields do
    while Count > 0 do
      Delete(0);
  with MnuTlsSortCustomFields do
    while Count > 0 do
      Delete(0);
  with strFields do
    for i := 0 to fieldCount-1 do
      if (i in SortByFields) then
      begin
        newMenuItem := TTBXItem.Create(MnuTlsSortFields);
        MnuTlsSortFields.Add(newMenuItem);
        with newMenuItem do
        begin
          Caption := Strings[i];
          Hint := Format(Messages.Strings[msgSortBy], [Strings[i]]);
          Tag := i;
          OnClick := ActionSortExecute;
          GroupIndex := 2;
        end;
      end;
  if (MovieList <> nil) then
    with MovieList.CustomFieldsProperties do
      for i := 0 to Count-1 do
      begin
        newMenuItem := TTBXItem.Create(MnuTlsSortCustomFields);
        MnuTlsSortCustomFields.Add(newMenuItem);
        with newMenuItem do
        begin
          Caption := Objects[i].FieldName;
          Hint := Format(Messages.Strings[msgSortBy], [Caption]);
          Tag := customFieldLow + i;
          OnClick := ActionSortExecute;
          GroupIndex := 2;
        end;
      end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FillFieldsGroupBy;
var
  i: Integer;
  newMenuItem: TTBXItem;
begin
  with MnuTlsGroupFields do
    while Count > 0 do
      Delete(0);
  with MnuTlsGroupCustomFields do
    while Count > 0 do
      Delete(0);
  with strFields do
    for i := 0 to fieldCount-1 do
      if (i in GroupByFields) then
      begin
        newMenuItem := TTBXItem.Create(MnuTlsGroupFields);
        MnuTlsGroupFields.Add(newMenuItem);
        with newMenuItem do
        begin
          Caption := Strings[i];
          Hint := Format(Messages.Strings[msgGroupBy], [Strings[i]]);
          Tag := i;
          OnClick := ActionGroupExecute;
          GroupIndex := 3;
        end;
      end;
  if (MovieList <> nil) then
    with MovieList.CustomFieldsProperties do
      for i := 0 to Count-1 do
      begin
        newMenuItem := TTBXItem.Create(MnuTlsGroupCustomFields);
        MnuTlsGroupCustomFields.Add(newMenuItem);
        with newMenuItem do
        begin
          Caption := Objects[i].FieldName;
          Hint := Format(Messages.Strings[msgGroupBy], [Caption]);
          Tag := customFieldLow + i;
          OnClick := ActionGroupExecute;
          GroupIndex := 3;
        end;
      end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FillFieldsGrid;
var
  i: Integer;
  newMenuItem: TTBXItem;
begin
  with MnuTlsGridFields do
    while Count > 0 do
      Delete(0);
  with MnuTlsGridCustomFields do
    while Count > 0 do
      Delete(0);
  with strFields do
    for i := 0 to fieldCount-1 do
    begin
      newMenuItem := TTBXItem.Create(MnuTlsGridFields);
      MnuTlsGridFields.Add(newMenuItem);
      with newMenuItem do
      begin
        Caption := Strings[i];
        Hint := '';
        Tag := i;
        OnClick := ActionGridFieldsExecute;
        GroupIndex := 0;
      end;
    end;
  if (MovieList <> nil) then
    with MovieList.CustomFieldsProperties do
      for i := 0 to Count-1 do
      begin
        newMenuItem := TTBXItem.Create(MnuTlsGridCustomFields);
        MnuTlsGridCustomFields.Add(newMenuItem);
        with newMenuItem do
        begin
          Caption := Objects[i].FieldName;
          Hint := '';
          Tag := customFieldLow + i;
          OnClick := ActionGridFieldsExecute;
          GroupIndex := 0;
        end;
      end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FillFieldsCBSearch(SelectedItem: Integer);
var
  i: Integer;
begin
  cbxField.Items.Clear;
  cbxField.Items.Assign(Fields.Strings);
  cbxField.Items.Insert(0, Messages.Strings[msgFindFieldAll]);
  if MovieList <> nil then
    with MovieList.CustomFieldsProperties do
      for i := 0 to Count-1 do
        cbxField.Items.Add(Objects[i].FieldName);
  cbxField.ItemIndex := SelectedItem;
end;

{-------------------------------------------------------------------------------
   Actions
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFileNewExecute(Sender: TObject);
begin
  SaveColumnSettings;
  SetSaveFilter;
  FCatalogFile.New;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFileOpenExecute(Sender: TObject);
begin
  SaveColumnSettings;
  SetSaveFilter;
  with FCatalogFile do
  begin
    OpenDialog.InitialDir := Settings.rOptions.rFolders[fuCatalogs].Value;
    if Open then
    begin
      Settings.rOptions.rFolders[fuCatalogs].Value := ExtractFilePath(CurrentFile);
      UpdateMRU(CurrentFile);
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SetSaveFilter;
var
  ext: string;
begin
  if FCatalogFile.CurrentFile <> '' then
    ext := ExtractFileExt(FCatalogFile.CurrentFile)
  else
    ext := extCatalog[extAMC];
  if ext = extCatalog[extAMC] then
    if FCurrentVersion = 35 then
      FCatalogFile.SaveDialog.FilterIndex := DialogCatalogFilterAMC35
    else
      FCatalogFile.SaveDialog.FilterIndex := DialogCatalogFilterAMC
  else
  if ext = extCatalog[extXML] then
    FCatalogFile.SaveDialog.FilterIndex := DialogCatalogFilterXML;
  Delete(ext, 1, 1);
  with FCatalogFile.SaveDialog do
  begin
    DefaultExt := ext;
    InitialDir := Settings.rOptions.rFolders[fuCatalogs].Value;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFileSaveExecute(Sender: TObject);
begin
  SaveColumnSettings;
  SetSaveFilter;
  FCatalogFile.Save;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFileSaveAsExecute(Sender: TObject);
begin
  SaveColumnSettings;
  SetSaveFilter;
  with FCatalogFile do
  begin
    if SaveAs then
    begin
      Settings.rOptions.rFolders[fuCatalogs].Value := ExtractFilePath(CurrentFile);
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFileExportExecute(Sender: TObject);
begin
  SaveColumnSettings;
  SaveCurrentItem;
  ThumbsViewerStop; // Needed in the case of movielist sort !
  Screen.Cursor := crHourGlass;
  try
    Application.CreateForm(TExportWin, ExportWin);
    try
      ImageListHot.GetIcon(Ord(ICON_FILEEXPORT), ExportWin.Icon);
      StoreSelectedState;
      ExportWin.Execute(MovieList, FCatalogFile.CurrentFile);
    finally
      ExportWin.Release;
      ExportWin := nil;
    end;
  finally
    Screen.Cursor := crDefault;
  end;
  ThumbsViewerStart;
  // Reload HTMLTemplate (if file is modified in export window)
  PreGenerateHTMLTemplate;
  // Refresh HTML display
  HTMLViewerUpdate;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFilePrintExecute(Sender: TObject);
begin
  SaveCurrentItem;
  ThumbsViewerStop; // Needed in the case of movielist sort !
  with ProgressWin do
  begin
    Maximum := 1;
    Status := Messages.Strings[msgPrintLoading];
    Progress := Messages.Strings[msgPrintSearching];
    Execute(Self);
    try
      Application.ProcessMessages;
      printform.Init_FR;
      IntProgress := 1;
    finally
      Close;
    end;
  end;
  try
    Application.CreateForm(TPrintWin, PrintWin);
    try
      ImageListHot.GetIcon(Ord(ICON_FILEPRINT), PrintWin.Icon);
      StoreSelectedState;
      PrintWin.Execute(FCatalogFile.CurrentFile, MovieList);
    finally
      PrintWin.Release;
      PrintWin := nil;
    end;
  finally
    printform.Final_FR;
  end;
  ThumbsViewerStart;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieAddExecute(Sender: TObject);
begin
  ThumbsViewerStop;
  with Settings.rOptions.rMovieInformation do
    if(NumberWin.Execute(MovieList, true, not AskNumber, AskNumber) = mrOk) then
    begin
      if NumberWin.CBDoNotAsk.checked then
      begin
        AskNumber := false;
      end;
      NewItem;
      with FrmMovie.EOriginalTitle do
        if CanFocus then
          SetFocus;
      if Sender <> nil then
      begin
        if AddFiles then
          ActionMovieImportFiles.Execute;
        if AddScript then
          ActionMovieImportScript.Execute;
      end;
    end;
  ThumbsViewerStart;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieNumberExecute(Sender: TObject);
var
  CurMovie: TMovie;
  Existing: TMovie;
begin
  CurMovie := nil;
  if (ListView1.Selected <> nil) and (ListView1.Selected.Selected) then
    CurMovie := TMovie(ListView1.Selected.Data);
  if CurMovie <> nil then
  begin
    if NumberWin.Execute(MovieList, False, False, True, CurMovie.iNumber) = mrOk then
    begin
      case NumberWin.grpNotUnique.ItemIndex of
        1:
          begin
            Existing := MovieList.Find(NumberWin.ENumber.AsInteger);
            if Existing <> nil then
            begin
              if Settings.rOptions.rMovieInformation.FirstAvailable then
                Existing.iNumber := MovieList.FirstFreeNumber
              else
                Existing.iNumber := MovieList.MaxNumber + 1; // + 1 !
            end;
          end;
        2:
          begin
            MovieList.ShiftNumbers(NumberWin.ENumber.AsInteger, CurMovie);
          end;
      end;
      CurMovie.iNumber := NumberWin.ENumber.AsInteger;
      FCatalogFile.Modified := True;
      RefreshMovieList;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.MakeMovieSave(Movie: TMovie; LockMovie: Boolean = True);
begin
  if(MovieSave = nil) then
  begin
    MovieSave := TMovie.Create(MovieList.CustomFieldsProperties);
    if LockMovie then
      Movie.Lock;
    try
      MovieSave.Assign(Movie);
      if FCatalogFile <> nil then
        SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
      if (MovieSave.Picture = nil) and (MovieSave.strPicture <> '') and
        (Pos('\', Movie.strPicture) = 0) then // linked picture in same path (Catalog)
      begin // Store linked picture in memory
        if FileExists(ExpandFileName(MovieSave.strPicture)) then
        begin
          MovieSave.Picture := TMemoryStream.Create;
          MovieSave.Picture.LoadFromFile(MovieSave.strPicture);
          MovieSave.strPicture := '*' + MovieSave.strPicture; // '*' to distinct linked picture easier during restore
        end else
          MovieSave.strPicture := '';
      end;
    finally
      if LockMovie then
        Movie.UnLock;
    end;
  end
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieUndoExecute(Sender: TObject);
var
  DoNotAsk: boolean;
  Movie: TMovie;
begin
  if (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil) and
    (ListView1.Selected.Data <> nil) and (MovieSave <> nil) then
  begin
    if Settings.rOptions.rMovieList.ConfirmUndo then
    begin
      DoNotAsk := false;
      if MessageWin.Execute(Messages.Strings[msgUndo], mtConfirmation, DoNotAsk,
                            Messages.Strings[msgDoNotConfirm], [mbYes,mbNo]) = 1 then
        Settings.rOptions.rMovieList.ConfirmUndo := not DoNotAsk
      else
        exit;
    end;
    ThumbsViewerStop;

    Movie := TMovie(Listview1.Selected.Data);
    if FCatalogFile <> nil then
      SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));

    with Movie do
      if (Picture = nil) and (strPicture <> '') and (Pos('\', strPicture) = 0) then
        DeleteFile(ExpandFileName(strPicture)); // delete old linked picture in same path (Catalog)
    Movie.Assign(MovieSave, False, True);
    with Movie do
      if (Picture <> nil) and (StartsStr('*', strPicture)) then // linked picture temporaly stored in memory to restore
      begin
        System.Delete(strPicture, 1, 1);
        Picture.SaveToFile(ExpandFileName(strPicture));
        FreeAndNil(Picture);
        FreeAndNil(_thumb);
        _thumbError := 0;
      end;

    OpenItem(Listview1.Selected);
    UpdateItem(ListView1.Selected);
    ListView1UpdateItem;
    FreeAndNil(MovieSave);
    MovieSelected;
    ThumbsViewerStart;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieDeleteExecute(Sender: TObject);
var
  i,j: Integer;
  AllClicked, OneDeleted: Boolean;
  ListDelete: TStringList;
  KeyMovie: string;
begin
  AllClicked := False;
  OneDeleted := False;
  FPreviousItem := nil;
  with ListView1 do
  begin
    if SelectedCount > 0 then
    begin
      with Items do
      begin
        try
          ListDelete := TStringList.Create;
          ListDelete.Sorted := True;
          for i := 0 to Count-1 do
            with Item[i] do
            begin
              if (Selected) then
                if (Data <> nil) then
                begin
                  KeyMovie := Format('%p', [Data]);
                  if ListDelete.IndexOf(KeyMovie) = -1 then
                    ListDelete.AddObject(KeyMovie, Data);
                end else
                begin
                  for j := 0 to Items[i].ChildrenCount-1 do
                  begin
                    KeyMovie := Format('%p', [Items[i].Children[j].Data]);
                    if ListDelete.IndexOf(KeyMovie) = -1 then
                      ListDelete.AddObject(KeyMovie, Items[i].Children[j].Data);
                  end;
                end;
            end;
          //ThumbsViewerStop;
          BeginUpdate;
          for i := 0 to ListDelete.Count-1 do
          begin
            case Self.DeleteItem(TMovie(ListDelete.Objects[i]), not AllClicked, True) of
              1:  OneDeleted := True;
              2:  begin AllClicked := True; OneDeleted := True; end;
              4:  break;
            end;

            if not AllClicked then
            begin
              EndUpdate;
              //if(OneDeleted) then
              //  ThumbsViewerStart;
              SetStatus;
              BeginUpdate;
            end;
          end;
          ListDelete.Free;
          // Fix bug in selected item after delete
          if SelectedCount = 1 then
            for i := 0 to Count-1 do
              if Item[i].Selected then
                Selected := Item[i];
          if SelectedCount = 0 then
            ClearItem;
        finally
          EndUpdate;
          if(OneDeleted) then
            ThumbsViewerStart;
        end;
      end; // end with Items
    end; // if SelectedCount > 0
  end; // with ListView1
  if(OneDeleted) then
  begin
    ListView1AfterSelectionChange(ListView1);
    ListView1EnsureVisiblePlus;
    SetStatus;
    FCatalogFile.Modified := True;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionHelpAboutExecute(Sender: TObject);
begin
  Screen.Cursor := crHourGlass;
  try
    Application.CreateForm(TAboutWin, AboutWin);
    try
      ImageListHot.GetIcon(Ord(ICON_ABOUT), AboutWin.Icon);
      AboutWin.ShowModal;
    finally
      AboutWin.Release;
      AboutWin := nil;
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionHelpVersionExecute(Sender: TObject);
begin
  Screen.Cursor := crHourGlass;
  try
    Application.CreateForm(TAboutWin, AboutWin);
    try
      AboutWin.ShowVersions;
    finally
      AboutWin.Release;
      AboutWin := nil;
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionExitExecute(Sender: TObject);
begin
  Close;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieImportFilesExecute(Sender: TObject);
var
  s: string;
  i: Integer;
begin
  //ThumbsViewerStop;
  with TOpenDialog.Create(Self) do
    try
      InitialDir := Settings.rOptions.rFolders[fuGetFromFiles].Value;
      FileName := '';
      Title := Messages.Strings[msgSelectFilesInfo];
      Options := DialogOpenOptions + [ofAllowMultiSelect];
      s := '';
      for i := Low(extVideo) to High(extVideo) do
      begin
        if s <> '' then
          s := s + ';';
        s := Format('%s*%s', [s, extVideo[i]]); 
      end;
      Filter := Format(DialogGetInfoFilter, [s]);
      if Execute then
      begin
        Settings.rOptions.rFolders[fuGetFromFiles].Value := ExtractFilePath(FileName);
        DragDropFilesDrop(sender, point(0,0), TStringList(Files));
      end;
  finally
    Free;
  end;
  //ThumbsViewerStart;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieImportCDExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieImportScriptExecute(Sender: TObject);
begin
  if (ListView1.SelectedCount = 0) then
    ActionMovieAddExecute(nil);
  if (ListView1.SelectedCount > 0) then
    ActionToolsScriptingExecute(ActionMovieImportScript);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionToolsScriptingExecute(Sender: TObject);
var
  WorkMode: TScriptWorkMode;
begin
  if Sender = ActionMovieImportScript then
    WorkMode := swmGetInfo
  else
    WorkMode := swmScripting;
  StoreSelectedState;
  SaveCurrentItem;
  FPreviousItem := nil;
  Screen.Cursor := crHourGlass;
  ThumbsViewerStop;
  try
    GetScriptWin := TGetScriptWin.Create(Self, WorkMode);
    with GetScriptWin do
      try
        ImageListHot.GetIcon(Ord(ICON_MOVIEIMPORTSCRIPT), Icon);
        if GetScriptWin.Execute(FCatalogFile, Self.MovieList) = mrOk then
        begin
          FCatalogFile.Modified := True;
          RefreshMovieList(False);
          LoadSelectedState;
          ListView1EnsureVisiblePlus;
        end;
      finally
        Release;
        GetScriptWin := nil;
      end;
  finally
    Screen.Cursor := crDefault;
    ThumbsViewerStart;
  end;
  FPreviousItem := ListView1.Selected;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieFindExecute(Sender: TObject);
begin
  ToolbarFind.Visible := ActionMovieFind.Checked;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

{procedure TMainWindow.ActionMovieCopyExecute(Sender: TObject);
var
  TempMovie: TMovie;
  DataHandle: THandle;
begin
  with ListView1 do
    if Selected <> nil then
      with Selected do
        if (Selected) and (Data <> nil) then
        begin
          TMovie(Data).Lock;
          TempMovie := TMovie.Create(MovieList.CustomFieldsProperties);
          try
            FrmMovie.SaveToObject(TempMovie, True);
            FrmMovieCustom.SaveToObject(TempMovie, True);
            with TMovie(Data) do
            begin
              if strPicture <> '' then
              begin
                TempMovie.strPicture := strPicture;
                if Picture <> nil then
                begin
                  TempMovie.Picture := TMemoryStream.Create;
                  Picture.Seek(0, soFromBeginning);
                  TempMovie.Picture.CopyFrom(Picture, Picture.Size);
                end;
              end;
            end;
            try
              DataHandle := TempMovie.SaveToMemory;
              try
                ClipBoard.SetAsHandle(ClipboardFormat, DataHandle);
              finally
                TempMovie.FreeMemory(DataHandle, True);
              end;
            except
              on e:Exception do
              begin
                MessageWin.Execute('Copy to clipboard - Error: '+e.Message, mtError, [mbOk]);
              end;
            end;
          finally
            TempMovie.Free;
            TMovie(Data).Unlock;
          end;
        end;
end;}

procedure TMainWindow.ActionMovieCopyExecute(Sender: TObject);
begin
  with ListView1 do
    if Selected <> nil then
      with Selected do
        if (Selected) and (Data <> nil) then
        begin
          try
            TMovie(Data).Lock;
            //MakeMovieSave(Data, False);
            FrmMovie.SaveToObject(TMovie(Data), True);
            FrmMovieCustom.SaveToObject(TMovie(Data), True);
            if (MovieCopy = nil) then
            begin
              MovieCopy := TMovie.Create(MovieList.CustomFieldsProperties);
              MovieSelected;
            end;
            MovieCopy.Assign(Data);
          finally
            TMovie(Data).Unlock;
          end;
        end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

{procedure TMainWindow.ActionMoviePasteExecute(Sender: TObject);
var
  TempMovie: TMovie;
  DataHandle: THandle;
begin
  with ListView1 do
    if Selected <> nil then
      with Selected do
        if (Selected) and (Data <> nil) then
        begin
          ThumbsViewerStop;
          MakeMovieSave(Data, False);
          TempMovie := TMovie.Create(MovieList.CustomFieldsProperties);
          try
            DataHandle := ClipBoard.GetAsHandle(ClipboardFormat);
            if DataHandle <> 0 then
            begin
              TempMovie.LoadFromMemory(DataHandle);
              if TempMovie.strPicture <> '' then
              begin
                with TMovie(Data) do
                begin
                  Picture.Free;
                  Picture := nil;
                  strPicture := TempMovie.strPicture;
                  if TempMovie.Picture <> nil then
                  begin
                    Picture := TMemoryStream.Create;
                    TempMovie.Picture.Seek(0, soFromBeginning);
                    Picture.CopyFrom(TempMovie.Picture, TempMovie.Picture.Size);
                  end;
                  if (_thumb <> nil) then
                    FreeAndNil(_thumb);
                  _thumbError := 0;
                end;
              end;
              OpenItem(TempMovie);
              FrmMovie.Modified := True;
              FrmMovieCustom.Modified := True;
              FCatalogFile.Modified := True;
              ListView1UpdateItem;
            end;
          finally
            TempMovie.Free;
            ThumbsViewerStart;
          end;
        end;
end;}

procedure TMainWindow.ActionMoviePasteExecute(Sender: TObject);
var
  Movie: TMovie;
begin
  with ListView1 do
    if Selected <> nil then
      with Selected do
        if (Selected) and (Data <> nil) and (MovieCopy <> nil) then
        begin
          try
            ThumbsViewerStop;

            MakeMovieSave(Data, False);

            Movie := TMovie(Listview1.Selected.Data);
            FrmMovie.SaveToObject(Movie, True);
            FrmMovieCustom.SaveToObject(Movie, True);

            {if ((MovieCopy.Picture = nil) and (MovieCopy.strPicture <> '')) or
              ((Movie.Picture = nil) and (Movie.strPicture <> '')) then // one linked picture (no copied)
              Movie.Assign(MovieCopy, False, False)
            else
              Movie.Assign(MovieCopy, False, True);}

            if FCatalogFile <> nil then
              SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
            if (MovieCopy.Picture = nil) and (MovieCopy.strPicture <> '') then // linked picture
            begin
              Movie.Assign(MovieCopy, False, False);
              if (Pos('\', MovieCopy.strPicture) = 0) then // picture in same path (Catalog)
                if FileExists(ExpandFileName(MovieCopy.strPicture)) then
                  ImportMoviePicture(Movie, MovieCopy.strPicture, psoCopy, False)
                else
                  with Movie do
                  begin
                    FreeAndNil(Picture);
                    strPicture := '';
                  end
              else
              begin
                with Movie do
                begin
                  if (Picture = nil) and (strPicture <> '') and (Pos('\', strPicture) = 0) then
                    DeleteFile(ExpandFileName(strPicture)); // delete old linked picture in same path (Catalog)
                  FreeAndNil(Picture);
                  strPicture := MovieCopy.strPicture;
                end;
              end;
            end else // stored picture
            begin
              with Movie do
                if (Picture = nil) and (strPicture <> '') and (Pos('\', strPicture) = 0) then
                  DeleteFile(ExpandFileName(strPicture)); // delete old linked picture in same path (Catalog)
              Movie.Assign(MovieCopy, False, True);
            end;

            if(Movie._thumb <> nil) then
              FreeAndNil(Movie._thumb);
            Movie._thumbError := 0;

            OpenItem(ListView1.Selected);
            UpdateItem(ListView1.Selected);
            FrmMovie.Modified := True;
            FrmMovieCustom.Modified := True;
            FCatalogFile.Modified := True;
            ListView1UpdateItem;
            MovieSelected;
          finally
            ThumbsViewerStart;
          end;
        end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieRenumberExecute(Sender: TObject);
var
  Refresh: Boolean;
begin
  ThumbsViewerStop;
  Refresh := False;
  Screen.Cursor := crHourGlass;
  try
    Application.CreateForm(TRenumberWin, RenumberWin);
    try
      ImageListHot.GetIcon(Ord(ICON_RENUMBER), RenumberWin.Icon);
      if RenumberWin.Execute(MovieList) = mrOk then
      begin
        FCatalogFile.Modified := True;
        RefreshMovieList;
        Refresh := True;
      end;
    finally
      RenumberWin.Release;
      RenumberWin := nil;
    end;
  finally
    Screen.Cursor := crDefault;
  end;
  if not Refresh then
    ThumbsViewerStart;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFileImportExecute(Sender: TObject);
begin
  SaveCurrentItem;
  ThumbsViewerStop;
  with TImportWin2.Create(Self) do
    try
      ImageListHot.GetIcon(Ord(ICON_FILEIMPORT), Icon);
      if Execute(FCatalogFile.CurrentFile, MovieList) then
      begin
        RefreshMovieList;
        FCatalogFile.Modified := True;
      end;
    finally
      Release;
    end;
  ThumbsViewerStart;
{
  Screen.Cursor := crHourGlass;
  try
    Application.CreateForm(TImportWin, ImportWin);
    try
      ImageListHot.GetIcon(ICON_FILEIMPORT, ImportWin.Icon);
      if ImportWin.Execute(FCatalogFile.CurrentFile) = mrOk then
      begin
        SaveCurrentItem;
        with ProgressWin do
        begin
          IntProgress := 0;
          Status := Format(Messages.Strings[msgImporting],[ExtractFileName(ImportWin.edtSourceFile.Text)]);
          Execute(Self);
        end;
        try
          Application.ProcessMessages;
          ProgressWin.Maximum := 1;
          MovieList.Add(ImportWin.GetMovies, Settings.rImport.AllowDupNumbers);
          FCatalogFile.Modified := True;
          ProgressWin.IntProgress := 1;
        finally
          ProgressWin.Close;
        end;
      end;
    finally
      ImportWin.Release;
      ImportWin := nil;
    end;
    RefreshMovieList;
  finally
    Screen.Cursor := crDefault;
  end;
}
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionHelpIndexExecute(Sender: TObject);
begin
  LaunchHelp(self);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionLoanExecute(Sender: TObject);
var
  FileModified: Boolean;
begin
  ActionLoan.Enabled := False;
  SaveCurrentItem;
  Screen.Cursor := crHourGlass;
  try
    Application.CreateForm(TLoanWin, LoanWin);
    try
      ImageListHot.GetIcon(Ord(ICON_LOANS), LoanWin.Icon);
      LoanWin.Execute(MovieList, ExtractFileName(FCatalogFile.CurrentFile), FrmMovie.EBorrower.Items, FileModified);
      with Settings.rOptions.rMovieInformation.rCombo[ddlBorrowers] do
        if not UseCatalogValues then
          Contents.Assign(FrmMovie.EBorrower.Items);
    finally
      LoanWin.Release;
      LoanWin := nil;
    end;
    FCatalogFile.Modified := FileModified or FCatalogFile.Modified;
  finally
    Screen.Cursor := crDefault;
  end;
  if FileModified then
    RefreshMovieList;
  ActionLoan.Enabled := True;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionGroupExecute(Sender: TObject);
begin
  if Sender = ActionGroupNone then
    SetGroupField(-1)
  else if Sender is TTBXItem then
    SetGroupField(TTBXItem(Sender).Tag);
  RefreshMovieList;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SetGroupField(Field: Integer);
var
  i: Integer;
  Found : Boolean;
begin
  if not ( (Field in GroupByFields) or
           ((MovieList <> nil) and (Field >= customFieldLow) and
            (Field - customFieldLow < MovieList.CustomFieldsProperties.Count)) ) then
    Field := -1;
  FGroupField := Field;

  // Unckeck all
  MnuGrpNon.Checked := False;
  with MnuTlsGroupFields do
    for i := 0 to Count-1 do
      if Items[i].Checked then
        Items[i].Checked := False;
  with MnuTlsGroupCustomFields do
    for i := 0 to Count-1 do
      if Items[i].Checked then
        Items[i].Checked := False;


  if FGroupField = -1 then
    MnuGrpNon.Checked := True
  else
  begin
    Found := False;
    with MnuTlsGroupFields do
      for i := 0 to Count-1 do
        with Items[i] do
          if (Tag = Field) then
          begin
            Found := True;
            Checked := True;
            Break;
          end;
     if (not Found) then
      with MnuTlsGroupCustomFields do
        for i := 0 to Count-1 do
          with Items[i] do
            if (Tag = Field) then
            begin
              Checked := True;
              Break;
            end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionSortExecute(Sender: TObject);
begin
  if (Sender = ActionSortAscend) or (Sender = ActionSortDescend) then
    SetSortField(Abs(FSortField) - 1, Sender = ActionSortDescend)
  else if Sender is TTBXItem then
    SetSortField(TTBXItem(Sender).Tag, MnuTlsSortDescend.Checked);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SetSortField(Field: Integer; Descend: Boolean; SortList: Boolean);
var
  i: Integer;
  section: TElHeaderSection;
  Found : Boolean;
begin
  if not ( (Field in SortByFields) or
           ((MovieList <> nil) and (Field >= customFieldLow) and
            (Field - customFieldLow < MovieList.CustomFieldsProperties.Count)) ) then
    Field := fieldNumber;
   FSortField := Field + 1;
   if Descend then
     FSortField := FSortField * -1;

  if Descend then
    MnuTlsSortDescend.Checked := True
  else
    MnuTlsSortAscend.Checked := True;

  // Unckeck all
  with MnuTlsSortFields do
    for i := 0 to Count-1 do
      if Items[i].Checked then
        Items[i].Checked := False;
  with MnuTlsSortCustomFields do
    for i := 0 to Count-1 do
      if Items[i].Checked then
        Items[i].Checked := False;

  Found := False;
  with MnuTlsSortFields do
    for i := 0 to Count-1 do
      with Items[i] do
        if (Tag = Field) then
        begin
          Found := True;
          Checked := True;
          Break;
        end;
  if (not Found) then
    with MnuTlsSortCustomFields do
    for i := 0 to Count-1 do
      with Items[i] do
        if (Tag = Field) then
        begin
          Checked := True;
          Break;
        end;

  section := nil;
  with ListView1 do
  with HeaderSections do
  begin
    if ActionToolsGrid.Checked then
    begin
      for i:=2 to Count-1 do
        if StrToInt(Item[i].FieldName) = Field then
        begin
          section := Item[i];
          break;
        end;
    end else
    begin
      if Field = fieldNumber then
        section := Item[0]
      else if Field = fieldFormattedTitle then
        section := Item[1]
      else
        section := Item[2];
    end;

    if section <> nil then
    begin
      ListView1.SortSection := section.Index;
      if Descend then
        Item[SortSection].SortMode := hsmDescend
      else
        Item[SortSection].SortMode := hsmAscend;
      if SortList then
        ListView1.Sort(True);
    end;
  end;
  ListView1EnsureVisiblePlus;
  ThumbsViewerStart;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionGridFieldsExecute(Sender: TObject);
var
  ListField: Integer;
begin
  if Sender is TTBXItem then
    with TTBXItem(Sender) do
    begin
      ListField := Tag;
      if ListField >= customFieldLow then
      begin
        ListField := ListField - customFieldLow + fieldCount;
        MnuTlsGridCustomFields.Items[Tag - customFieldLow].Checked :=
          not MnuTlsGridCustomFields.Items[Tag - customFieldLow].Checked;
      end else
        MnuTlsGridFields.Items[Tag].Checked := not MnuTlsGridFields.Items[Tag].Checked;
      if ListField = fieldFormattedTitle then ListField := 3 //(1 + 2)
      else if ListField = fieldNumber then ListField := 2 //(0 + 2)
      else if (ListField > fieldFormattedTitle) then ListField := ListField + 2 // (x + 2)
      else ListField := ListField + 3; // (x + 2 + 1)
      if (not ListView1.HeaderSections.Item[1].Visible) then // Grid mode
        ListView1.HeaderSections.Item[ListField].Visible :=
          not ListView1.HeaderSections.Item[ListField].Visible
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionToolsGridExecute(Sender: TObject);
begin
  SaveCurrentItem;
  FPreviousItem := nil;
  ActionToolsGrid.Checked := not ActionToolsGrid.Checked;
  RefreshMovieList;
end;
{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionRefreshExecute(Sender: TObject);
begin
  RefreshMovieList;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionOptionsExecute(Sender: TObject);
begin
  Screen.Cursor := crHourGlass;
  try
    Application.CreateForm(TOptionsWin, OptionsWin);
    try
      ImageListHot.GetIcon(Ord(ICON_OPTIONS), OptionsWin.Icon);
      StoreSelectedState; // Need to be done here !
      if OptionsWin.Execute(ToolbarMain, ImageListHot) = mrOk then
      begin
        ApplyOptions;
        // Reload HTMLTemplate
        PreGenerateHTMLTemplate;
        // Reload ListView1
        RefreshMovieList(False);
        LoadSelectedState;
        ListView1EnsureVisiblePlus;
      end;
    finally
      OptionsWin.Release;
      OptionsWin := nil;
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionLanguageExecute(Sender: TObject);
begin
  Screen.Cursor := crHourGlass;
  with TLanguageWin.Create(Self) do
    try
      ImageListHot.GetIcon(Ord(ICON_LANGUAGES), Icon);
      if Execute then
        ApplyLanguage;
    finally
      Screen.Cursor := crDefault;
      Release;
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionDisplayPictureToolbarExecute(Sender: TObject);
begin
  ToolbarPicture.Visible := ActionDisplayPictureToolbar.Checked;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionDisplayMainToolbarExecute(Sender: TObject);
begin
  ToolbarMain.Visible := ActionDisplayMainToolbar.Checked;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionDisplayStatusBarExecute(Sender: TObject);
begin
  StatusBar1.Visible := ActionDisplayStatusBar.Checked;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieStatsExecute(Sender: TObject);
begin
  Screen.Cursor := crHourGlass;
  try
    SaveCurrentItem;
    StoreSelectedState;
    Application.CreateForm(TStatsWin, StatsWin);
    try
      ImageListHot.GetIcon(Ord(ICON_STATISTICS), StatsWin.Icon);
      StatsWin.Execute(MovieList, FCatalogFile.CurrentFile);
    finally
      FreeAndNil(StatsWin);
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuFileExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuMovieExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuToolsExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuHelpExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuPictureExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuGetExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuGroupExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuSortExecute(Sender: TObject);
begin
  //
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMenuGridFieldsExecute(Sender: TObject);
begin
  //
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieSearchExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFilePropertiesExecute(Sender: TObject);
begin
  Screen.Cursor := crHourGlass;
  try
    Application.CreateForm(TPropertiesWin, PropertiesWin);
    with PropertiesWin do
      try
        ImageListHot.GetIcon(Ord(ICON_FILEPROPERTIES), Icon);
        if Execute(FCatalogFile.CurrentFile, MovieList) then
          FCatalogFile.Modified := True;
      finally
        Release;
        PropertiesWin := nil;
      end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieSelGroupExecute(Sender: TObject);
var
  i: Integer;
  GroupItem: TElTreeItem;
begin
  with ListView1 do
    if (Selected <> nil) and Selected.Selected and (Selected.Data = nil) then
    begin
      GroupItem := Selected;
      Items.BeginUpdate;
      DeselectAll;
      ItemFocused := nil;
      if not GroupItem.Expanded then
        GroupItem.Expanded := True;
      with GroupItem do
        for i := 0 to Count-1 do
          if SelectedCount = 0 then
            ListView1.ItemFocused := Item[i]
          else
            Item[i].Selected := True;
      Selected := GetNextSelected(nil);
      Items.EndUpdate;
      ListView1AfterSelectionChange(ListView1);
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieSelCheckExecute(Sender: TObject);
var
  i: Integer;
begin
  with ListView1 do
  begin
    FullExpandOrCollapse := True;
    Items.BeginUpdate;
    DeselectAll;
    ItemFocused := nil;
    with Items do
      for i := 0 to Count-1 do
        if (Item[i].Data <> nil) and (Item[i].Checked) then
        begin
          if (Items[i].Parent <> nil) and (not Items[i].Parent.Expanded) then
            Items[i].Parent.Expanded := True;
          if SelectedCount = 0 then
            ItemFocused := Items[i]
          else
            Items[i].Selected := True;
        end;
    Selected := ListView1.GetNextSelected(nil);
    Items.EndUpdate;
    FullExpandOrCollapse := False;
    ThumbsViewer.CalcView(True);
    ThumbsViewer.Invalidate;
    ListView1AfterSelectionChange(ListView1);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieSelUncheckExecute(Sender: TObject);
var
  i: Integer;
begin
  with ListView1 do
  begin
    FullExpandOrCollapse := True;
    Items.BeginUpdate;
    DeselectAll;
    ItemFocused := nil;
    with Items do
      for i := 0 to Count-1 do
        if (Item[i].Data <> nil) and (not Item[i].Checked) then
        begin
          if (Items[i].Parent <> nil) and (not Items[i].Parent.Expanded) then
            Items[i].Parent.Expanded := True;
          if SelectedCount = 0 then
            ItemFocused := Items[i]
          else
            Items[i].Selected := True;
        end;
    Selected := ListView1.GetNextSelected(nil);
    Items.EndUpdate;
    FullExpandOrCollapse := False;
    ThumbsViewer.CalcView(True);
    ThumbsViewer.Invalidate;
    ListView1AfterSelectionChange(ListView1);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieCheckExecute(Sender: TObject);
var
  i:integer;
begin
  ListView1.Items.BeginUpdate;
  with ListView1 do
    with Items do
      for i := 0 to Count-1 do
      begin
        if Item[i].Selected then
        begin
          Item[i].Checked := True;
          OnItemChecked(Sender, Item[i]);
        end;
      end;
  ListView1.Items.EndUpdate;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieUncheckExecute(Sender: TObject);
var
  i:integer;
begin
  ListView1.Items.BeginUpdate;
  with ListView1 do
    with Items do
      for i := 0 to Count-1 do
      begin
        if Item[i].Selected then
        begin
          Item[i].Checked := False;
          OnItemChecked(Sender, Item[i]);
        end;
      end;
  ListView1.Items.EndUpdate;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieNextExecute(Sender: TObject);
var
  prevFocused: TWinControl;
  ListItem: TElTreeItem;
begin
  with ListView1 do
  begin
    if Items.Count > 0 then
    begin
      prevFocused := ActiveControl;
      if (prevFocused <> nil) {and not (prevFocused is TElTreeView)} then
      begin
        ListItem := Selected;
        DeselectAll;
        ItemFocused := nil;
        MultiSelect := False;
        if ListItem = nil then
          Selected := Items.Item[0]
        else
        begin
          //ListView1UpdateItem; <-- Check in SaveCurrentItem
          Selected := ListItem.GetNext;
        end;
        if (Selected <> nil) and (Selected.Data = nil) then
          Selected := Selected.GetNext;
        ListView1EnsureVisiblePlus;
        MultiSelect := True;
        ListView1AfterSelectionChange(ListView1);
        if prevFocused.Enabled then
          FocusControl(prevFocused);
      end;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMoviePreviousExecute(Sender: TObject);
var
  prevFocused: TWinControl;
  ListItem: TElTreeItem;
begin
  with ListView1 do
  begin
    if Items.Count > 0 then
    begin
      prevFocused := ActiveControl;
      if (prevFocused <> nil) {and not (prevFocused is TElTreeView)} then
      begin
        ListItem := Selected;
        DeselectAll;
        ItemFocused := nil;
        MultiSelect := False;
        if ListItem = nil then
          Selected := Items.Item[Items.Count-1]
        else
        begin
          //ListView1UpdateItem; <-- Check in SaveCurrentItem
          Selected := ListItem.GetPrev;
        end;
        if (Selected <> nil) and (Selected.Data = nil) then
          Selected := Selected.GetPrev;
        ListView1EnsureVisiblePlus;
        MultiSelect := True;
        ListView1AfterSelectionChange(ListView1);
        if prevFocused.Enabled then
          FocusControl(prevFocused);
      end;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionURLOpenExecute(Sender: TObject);
var
  url: string;
begin
  url := '';
  if FSelectedURL <> nil then
    url := FSelectedURL.Text
  else
    url := FURL;
  if url <> '' then
  begin
    SetCurrentDir(strDirApp);
    if FileExists(ExpandFileName(url)) then
      LaunchProg(ExpandFileName(url))
    else
      LaunchProg(url);
  end

end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionURLBrowseExecute(Sender: TObject);
begin
  if FSelectedURL <> nil then
    with TOpenDialog.Create(Self) do
      try
        InitialDir := Settings.rOptions.rFolders[fuGetFromFiles].Value;
        FileName := '';
        Title := Messages.Strings[msgSelectFileURL];
        Options := DialogOpenOptions;
        Filter := DialogAnyFileFilter;
        if Execute then
        begin
          Settings.rOptions.rFolders[fuGetFromFiles].Value := ExtractFilePath(FileName);
          FSelectedURL.Text := FileName;
        end;
    finally
      Free;
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionURLCopyExecute(Sender: TObject);
begin
  if FSelectedURL <> nil then
    Clipboard.AsText := FSelectedURL.Text
  else if FURL <> '' then
    Clipboard.AsText := FURL;

end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionURLExploreExecute(Sender: TObject);
var
  url: string;
begin
  if FSelectedURL <> nil then
    url := FSelectedURL.Text
  else
    url := FURL;
  if url <> '' then
  begin
    SetCurrentDir(strDirApp);
    LaunchExplorer(ExpandFileName(url));
  end
end;

{-------------------------------------------------------------------------------
   Events
-------------------------------------------------------------------------------}

procedure TMainWindow.OnMovieFieldChange(Sender: TObject);
begin
  FCatalogFile.Modified := True;
  MakeMovieSave(ListView1.Selected.Data, True);
{ We don't update tree item here because is too slow when there are many items in tree.
  The item is updated later in this cases (call ListView1UpdateItem procedure) :
  - The user move the mouse on the tree
  - The user move the mouse on the left side of FrmMovie (X < 90).
  - The user press ENTER or ESCAPE in a field to force update.
    - ENTER is not take account for fields Description and Comments
  - The user press a header column of tree to sort items.
  - The user press stretch list button
  - The user paste a movie on the selected movie
  - The user cancel modifications
  - Others : Check in SaveCurrentItem function }
end;

procedure TMainWindow.OnMovieFieldPropertiesChange(Sender: TObject);
begin
  FCatalogFile.Modified := True;
end;

procedure TMainWindow.OnCustomFieldAdd(Sender: TObject);
begin
  OnCustomFieldModify(nil);
end;

procedure TMainWindow.OnCustomFieldModify(Sender: TObject);
var
  CustomFieldProperties: TCustomFieldProperties;
  Field, FieldSearch : Integer;
  CustomFieldSearch : string;
  CustomFieldSort : string;
  CustomFieldGroup : string;
begin
  CustomFieldProperties := nil;
  if (Sender <> nil) and (Sender is TPanelCustomField) then
    CustomFieldProperties := TPanelCustomField(Sender).Properties;
  Screen.Cursor := crHourGlass;
  try
    FrmMovieCustom.SaveFieldsProperties; // Save fields position, size, etc...
    if (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil) and
      (ListView1.Selected.Data <> nil) then
    begin
      ListView1UpdateItem;
      SaveCurrentItem(TMovie(ListView1.Selected.Data), False);
    end;

    // Save selected search field
    CustomFieldSearch := '';
    FieldSearch := cbxField.ItemIndex - 1;
    if (FieldSearch >= fieldCount) then
      CustomFieldSearch := MovieList.CustomFieldsProperties.Strings[FieldSearch - fieldCount];

    // Save custom field sort
    CustomFieldSort := '';
    Field := Abs(FSortField) - 1;
    if (Field in AllCustomFields) and
      (Field - customFieldLow < MovieList.CustomFieldsProperties.Count) then
      CustomFieldSort := MovieList.CustomFieldsProperties.Strings[Field - customFieldLow];

    // Save custom field group
    CustomFieldGroup := '';
    Field := FGroupField;
    if (Field in AllCustomFields) and
      (Field - customFieldLow < MovieList.CustomFieldsProperties.Count) then
      CustomFieldGroup := MovieList.CustomFieldsProperties.Strings[Field - customFieldLow];

    // Save column settings
    SaveColumnSettings;

    Application.CreateForm(TCustomFieldsManagerWin, CustomFieldsManagerWin);
    try
      ImageListHot.GetIcon(Ord(ICON_FIELDEDIT), CustomFieldsManagerWin.Icon);
      if CustomFieldsManagerWin.Execute(MovieList.CustomFieldsProperties,
        Messages.Strings.Strings[msgImportFields],
        Messages.Strings.Strings[msgExportFields],
        CustomFieldProperties) then
      begin
        PreGenerateHTMLTemplate;
        FrmMovieCustom.GenerateFields; // Regenerate fields after changes
        if (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil) and
          (ListView1.Selected.Data <> nil) then
        begin
          FrmMovieCustom.LoadFromObject(TMovie(ListView1.Selected.Data));
          HTMLViewerUpdate;
        end;

        // Restore selected search field
        if CustomFieldSearch <> '' then
        begin
          FieldSearch := MovieList.CustomFieldsProperties.IndexOf(CustomFieldSearch);
          if FieldSearch <> -1 then
            FieldSearch := FieldSearch + fieldCount;
        end;
        FillFieldsCBSearch(FieldSearch + 1);

        // Restore field sort
        FillFieldsSortBy;
        if CustomFieldSort = '' then
          SetSortField(Abs(FSortField)-1, FSortField < 0)
        else
        begin
          Field := MovieList.CustomFieldsProperties.IndexOf(CustomFieldSort);
          if Field <> -1 then
            SetSortField(customFieldLow + Field, FSortField < 0, False)
          else
            SetSortField(fieldNumber, FSortField < 0, False);
        end;

        // Restore field group
        FillFieldsGroupBy;
        if CustomFieldGroup = '' then
          SetGroupField(FGroupField)
        else
        begin
          Field := MovieList.CustomFieldsProperties.IndexOf(CustomFieldGroup);
          if Field <> -1 then
            SetGroupField(customFieldLow + Field)
          else
            SetGroupField(-1);
        end;

        // Regenerate Grid Fields
        FillFieldsGrid;

        FCatalogFile.Modified := True;
        RefreshMovieList(True, False);
      end;
    finally
      CustomFieldsManagerWin.Release;
      CustomFieldsManagerWin := nil;
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

procedure TMainWindow.OnCustomFieldDelete(Sender: TObject);
begin
  OnCustomFieldModify(Sender);
end;

procedure TMainWindow.OnUserAskListUpdate(Sender: TObject);
begin
  ListView1UpdateItem;
end;

procedure TMainWindow.ListView1MouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
begin
  if(ListView1.CanFocus) then
    ListView1.SetFocus;
  ListView1UpdateItem;
end;

procedure TMainWindow.FrmMovieMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
begin
  if (X < 90) then
    ListView1UpdateItem;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FormShow(Sender: TObject);
var
  s, FileToOpen: string;
begin
  SplashWin.ProgressBar1.Position := 60;
  LoadOptions;
  SplashWin.ProgressBar1.Position := 60;
  ApplyLanguage;
  SplashWin.ProgressBar1.Position := 70;
  Application.ProcessMessages;
  SplashWin.Release;
  SplashWin := nil;
  if not ActionDisplayHTML.Checked then
    TabMovieInfos.Visible := True;
  FCatalogFile.Modified := False;
  with Settings.rOptions.rFiles do
    if (AutoLoad) and (AutoLoadFile <> '') then
      FileToOpen := AutoLoadFile
    else
      FileToOpen := '';
  if ParamCount > 0 then
  begin
    s := ParamStr(1);
    if (s <> '') and (s[1] <> '/') then
      FileToOpen := s;
  end;
  if (FileToOpen <> '') then
    if FileExists(ExpandFileName(FileToOpen)) then
      FCatalogFile.Open(ExpandFileName(FileToOpen))
    else
      MessageWin.Execute(Format(Messages.Strings[msgFileNotExists], [FileToOpen]), mtError, [mbOk])
  else
    FCatalogFile.New;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FormCreate(Sender: TObject);
var
  i : integer;
begin
  Font.Name := Graphics.DefFontData.Name;
  Font.Charset := Graphics.DefFontData.Charset;
  {$IFDEF DISABLETHEMES}
  TBXSwitcher1.EnableXPStyles := False;
  TBXSwitcher1.FlatMenuStyle := fmsDisable;
  ToolbarFind.Color := clBtnFace;
  ToolbarMain.Color := clBtnFace;
  ToolbarMenu.Color := clBtnFace;
  ToolbarPicture.Color := clBtnFace;
  ToolbarPictureWindow.Color := clBtnFace;
  DockBottomList.Color := clBtnFace;
  DockImageLeft.Color := clBtnFace;
  DockImageTop.Color := clBtnFace;
  DockMainBottom.Color := clBtnFace;
  DockMainLeft.Color := clBtnFace;
  DockMainTop.Color := clBtnFace;
  DockRightInfo.Color := clBtnFace;
  {$ENDIF}

  DragDropFiles := TJvDragDrop.Create(Self);
  DragDropFiles.OnDrop := DragDropFilesDrop;
  DragDropFiles.AcceptDrag := True;

  FPreviousItem := nil;
  MovieList := nil;
  MovieSave := nil;
  MovieCopy := nil;
  FGroupField := -1;
  GroupItemEmpty := nil;
  MoviesVisible := 0;
  FSelectedURL := nil;
  FURL := '';
  
  FCatalogFile := TFileManager.Create(Self);
  FCatalogFile.OnFileChange := OnFileChange;
  FCatalogFile.OnFileModified := OnFileModified;
  FCatalogFile.OnNewFile := OnNewFile;
  FCatalogFile.OnOpenFile := OnOpenFile;
  FCatalogFile.OnSaveFile := OnSaveFile;
  with FCatalogFile do
  begin
    with OpenDialog do
    begin
      Filter := DialogCatalogFilter;
      Options := DialogOpenOptions;
    end;
    with SaveDialog do
    begin
      Filter := DialogCatalogSaveFilter;
      Options := DialogSaveOptions;
      OnTypeChange := Self.OnDialogTypeChange;
    end;
  end;
  FCurrentVersion := 99;

  ActionFileNew.ImageIndex := Ord(ICON_FILENEW);
  ActionFileOpen.ImageIndex := Ord(ICON_FILEOPEN);
  ActionFileSave.ImageIndex := Ord(ICON_FILESAVE);
  ActionFileSaveAs.ImageIndex := Ord(ICON_FILESAVEAS);
  ActionFileImport.ImageIndex := Ord(ICON_FILEIMPORT);
  ActionFileExport.ImageIndex := Ord(ICON_FILEEXPORT);
  ActionFilePrint.ImageIndex := Ord(ICON_FILEPRINT);
  ActionFileProperties.ImageIndex := Ord(ICON_FILEPROPERTIES);
  ActionExit.ImageIndex := Ord(ICON_EXIT);
  ActionMovieAdd.ImageIndex := Ord(ICON_MOVIEADD);
  ActionMovieNumber.ImageIndex := Ord(ICON_MOVIENUMBER);
  ActionMovieCopy.ImageIndex := Ord(ICON_MOVIECOPY);
  ActionMoviePaste.ImageIndex := Ord(ICON_MOVIEPASTE);
  ActionMovieDelete.ImageIndex := Ord(ICON_MOVIEDELETE);
  ActionMovieFind.ImageIndex := Ord(ICON_MOVIEFIND);
  ActionMovieUndo.ImageIndex := Ord(ICON_MOVIEUNDO);
  ActionMoviePictureShow.ImageIndex := Ord(ICON_MOVIEPICTURE);
  ActionMenuPicture.ImageIndex := Ord(ICON_MOVIEPICTURE);
  ActionMenuGet.ImageIndex := Ord(ICON_MOVIEIMPORT);
  ActionMenuGroup.ImageIndex := Ord(ICON_GROUP);
  ActionMenuSort.ImageIndex := Ord(ICON_VIEWDETAIL);
  ActionMenuGridFields.ImageIndex := Ord(ICON_GRIDMODE);
  ActionMovieImportFiles.ImageIndex := Ord(ICON_MOVIEIMPORTFILES);
  ActionMovieImportCD.ImageIndex := Ord(ICON_MOVIEIMPORTCD);
  ActionMovieImportScript.ImageIndex := Ord(ICON_MOVIEIMPORTSCRIPT);
  ActionMovieSearch.ImageIndex := Ord(ICON_MOVIESEARCH);
  ActionMovieStats.ImageIndex := Ord(ICON_STATISTICS);
  ActionHelpAbout.ImageIndex := Ord(ICON_ABOUT);
  ActionHelpIndex.ImageIndex := Ord(ICON_HELP);
  ActionLoan.ImageIndex := Ord(ICON_LOANS);
  ActionToolsScripting.ImageIndex := Ord(ICON_SCRIPTING);
  ActionToolsGrid.ImageIndex := Ord(ICON_GRIDMODE);
  ActionMovieRenumber.ImageIndex := Ord(ICON_RENUMBER);
  ActionOptions.ImageIndex := Ord(ICON_OPTIONS);
  ActionLanguage.ImageIndex := Ord(ICON_LANGUAGES);
  ActionRefresh.ImageIndex := Ord(ICON_REFRESH);
  ActionPicSelect.ImageIndex := Ord(ICON_PICTUREOPEN);
  ActionPicDelete.ImageIndex := Ord(ICON_PICTUREDELETE);
  ActionPicUndock.ImageIndex := Ord(ICON_PICTUREUNDOCK);
  ActionPicSaveAs.ImageIndex := Ord(ICON_PICTURESAVE);
  ActionPicCopy.ImageIndex := Ord(ICON_PICTURECOPY);
  ActionMovieRandom.ImageIndex := Ord(ICON_RANDOM);
  ActionStretchList.ImageIndex := Ord(ICON_MOVERIGHTALL);
  ActionDisplayHTML.ImageIndex := Ord(ICON_HTML);
  ActionDisplayThumbnails.ImageIndex := Ord(ICON_VIEWLIST);
  ActionManageFields.ImageIndex := Ord(ICON_FIELDEDIT);

  ActionURLOpen.ImageIndex := Ord(ICON_MOVIESEARCH);
  ActionURLCopy.ImageIndex := Ord(ICON_MOVIECOPY);
  ActionURLBrowse.ImageIndex := Ord(ICON_FILEOPEN);
  ActionURLExplore.ImageIndex := Ord(ICON_MOVIESEARCH);

  MnuHtmlCopy.ImageIndex := Ord(ICON_MOVIECOPY);

  Settings.version := StrVersion;
  Application.OnHelp := OnAppHelp;
  FrmMovie.OnFieldChange := Self.OnMovieFieldChange;
  FrmMovie.OnUserAskListUpdate := Self.OnUserAskListUpdate;
  FrmMovie.OnURLButtonClick := Self.OnURLButtonClick;
  FrmMovie.OnURLEnter := Self.OnURLEnter;
  FrmMovieCustom.OnFieldChange := Self.OnMovieFieldChange;
  FrmMovieCustom.OnFieldPropertiesChange := Self.OnMovieFieldPropertiesChange;
  FrmMovieCustom.OnFieldAdd := Self.OnCustomFieldAdd;
  FrmMovieCustom.OnFieldModify := Self.OnCustomFieldModify;
  FrmMovieCustom.OnFieldDelete := Self.OnCustomFieldDelete;
  FrmMovieCustom.OnUserAskListUpdate := Self.OnUserAskListUpdate;
  FrmMovieCustom.OnURLButtonClick := Self.OnURLButtonClick;
  FrmMovieCustom.OnURLEnter := Self.OnURLEnter;
  MStream := TMemoryStream.Create;
  HTMLTemplatePreGenerated := TStringList.Create;
  HTMLTemplatePath := '';

  for i := 0 to High(MStreamAppr) do
    if FileExists(strDirApp + strDirTemplates + 'appr' + IntToStr(i) + '.gif') then
    begin
      MStreamAppr[i] := TMemoryStream.Create;
      MStreamAppr[i].LoadFromFile(strDirApp + strDirTemplates + 'appr' + IntToStr(i) + '.gif');
    end else
      MStreamAppr[i] := nil;

  for i := 0 to High(MStreamAppr10) do
    if FileExists(strDirApp+strDirTemplates + 'appr10_' + IntToStr(i) + '.gif') then
    begin
      MStreamAppr10[i] := TMemoryStream.Create;
      MStreamAppr10[i].LoadFromFile(strDirApp + strDirTemplates + 'appr10_' + IntToStr(i) + '.gif');
    end else
      MStreamAppr10[i] := nil;

  {ThumbsViewer}
  ThumbJPEG := TJpegImageFix.Create;
  ThumbJPEG.CompressionQuality := 80;
  ThumbJPEG.Performance := jpBestSpeed;
  ThumbGIF := TJvGIFImage.Create;
  ThumbPNG := TPNGObject.Create;
  // Max thumbnail size...
  ThumbSizeW := Trunc(255*0.75);
  ThumbSizeH := 255;
  ThumbsViewer.CellWidth := ThumbSizeW + 10;
  ThumbsViewer.CellHeight := ThumbSizeH + 30;
  CellJpeg := TJpegImageFix.Create;
  CellJPEG.Performance := jpBestSpeed;
  GenCellColors;
  CellStyle := -1;
  PoolSize := 0;
  MaxPool := Round((Screen.Width * Screen.Height) * 1.5);
  ThumbsPool := TList.Create;
  FullExpandOrCollapse := False;
  {End ThumbsViewer}
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FormDestroy(Sender: TObject);
var
  i: integer;
begin
  {ThumbsViewer}
  ThumbsViewerStop;
  ThumbJPEG.Free;
  ThumbGIF.Free;
  ThumbPNG.Free;
  CellJpeg.Free;
  ThumbsPool.Free;
  {End ThumbsViewer}

  MovieList.Free;
  MovieSave.Free;
  MovieCopy.Free;
  FCatalogFile.Free;
  DragDropFiles.Free;
  MStream.Free;
  for i := 0 to High(MStreamAppr) do
    MStreamAppr[i].Free;
  for i := 0 to High(MStreamAppr10) do
    MStreamAppr10[i].Free;
  HTMLTemplatePreGenerated.Free;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  SaveOptions;
  ListView1.DeselectAll;
  HTMLViewerUpdate;
  MovieSelected;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  SaveColumnSettings;
  SetSaveFilter;
  with Settings.rOptions.rFiles do
    if AutoLoadLast then
      AutoLoadFile := FCatalogFile.CurrentFile;
  CanClose := FCatalogFile.Close;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1CompareItems(Sender: TObject; Item1, Item2: TElTreeItem; var res: Integer);
var
  Field : Integer;
  Movie1, Movie2: TMovie;
  ItemTmp: TElTreeItem;
  Prop: TCustomFieldProperties;
begin
  if (ListView1.SortSection > -1) and (ListView1.SortSection < ListView1.HeaderSections.Count)
    and (ListView1.HeaderSections.Item[ListView1.SortSection].SortMode = hsmDescend) then
  begin
    ItemTmp := Item1;
    Item1 := Item2;
    Item2 := ItemTmp;
  end;
  if Item1 = Item2 then
    res := 0
  else
  if ((Item1.Data = nil) or (Item2.Data = nil)) then
  begin
    if (Item1.Data = nil) and (Item2.Data <> nil) then
      if Settings.rOptions.rMovieList.GroupsAbove then res := -1 else res := 1
    else
    if (Item1.Data <> nil) and (Item2.Data = nil) then
      if Settings.rOptions.rMovieList.GroupsAbove then res := 1 else res := -1
    else
    if StartsStr(strErrorParenthesis, Item1.Text) then
      if Settings.rOptions.rMovieList.GroupsAbove then res := 1 else res := -1
    else
    if StartsStr(strErrorParenthesis, Item2.Text) then
      if Settings.rOptions.rMovieList.GroupsAbove then res := -1 else res := 1
    else
    if StartsStr(Messages.Strings[msgGroupEmpty], Item1.Text) then
      if Settings.rOptions.rMovieList.GroupsAbove then res := 1 else res := -1
    else
    if StartsStr(Messages.Strings[msgGroupEmpty], Item2.Text) then
      if Settings.rOptions.rMovieList.GroupsAbove then res := -1 else res := 1
    else
    if StartsStr(Messages.Strings[msgGroupUnique], Item1.Text) then
      if Settings.rOptions.rMovieList.GroupsAbove then res := 1 else res := -1
    else
    if StartsStr(Messages.Strings[msgGroupUnique], Item2.Text) then
      if Settings.rOptions.rMovieList.GroupsAbove then res := -1 else res := 1
    else
    begin
      res := 0;
      if Settings.rOptions.rMovieList.SortGroupsByCount then
      begin
        res := Item2.Count - Item1.Count;
      end;
      if res = 0 then
      begin
        if FGroupField < fieldCount then
        begin
          case GetFieldType(FGroupField) of
            ftInteger:  res := StrToIntTrunc(Item1.Text, 0) - StrToIntTrunc(Item2.Text, 0);
            ftReal:     res := CompareValue(StrToFloatTrunc(Item1.Text, 0), StrToFloatTrunc(Item2.Text, 0));
            ftDate:     res := Trunc(StrToDateDef(TextBefore(Item1.Text, ' ('), 0)) - Trunc(StrToDateDef(TextBefore(Item2.Text, ' ('), 0));
          else
            res := AnsiCompareText(Item1.Text, Item2.Text);
          end;
        end else if (FGroupField >= customFieldLow) and
          (FGroupField - customFieldLow < MovieList.CustomFieldsProperties.Count) then
        begin
          Prop := MovieList.CustomFieldsProperties.Objects[FGroupField - customFieldLow];
          case Prop.FieldType of
            ftInteger:  res := StrToIntTrunc(Item1.Text, 0) - StrToIntTrunc(Item2.Text, 0);
            ftReal:     res := CompareValue(StrToFloatTrunc(Item1.Text, 0), StrToFloatTrunc(Item2.Text, 0));
            ftDate:     res := Trunc(StrToDateDef(TextBefore(Item1.Text, ' ('), 0)) - Trunc(StrToDateDef(TextBefore(Item2.Text, ' ('), 0));
          else
            res := AnsiCompareText(Item1.Text, Item2.Text);
          end;
        end;
      end;
    end;
  end else
  begin
    if(FSortField < 0) then
    begin
      ItemTmp := Item1;
      Item1 := Item2;
      Item2 := ItemTmp;
    end;
    with ListView1 do
    begin
      Field := Abs(FSortField) - 1;
      Movie1 := Item1.Data;
      Movie2 := Item2.Data;
      if Field < FieldCount then
      begin
        case GetFieldType(Field) of
          ftInteger, ftReal, ftDate:
            res := Movie1.GetIntFieldValue(Field) - Movie2.GetIntFieldValue(Field);
          else
            res := AnsiCompareText(Movie1.GetFieldValue(Field), Movie2.GetFieldValue(Field));
          end;
      end else if (Field >= customFieldLow) and
        (Field - customFieldLow < MovieList.CustomFieldsProperties.Count) then
      begin
        Prop := MovieList.CustomFieldsProperties.Objects[Field - customFieldLow];
        case Prop.FieldType of
          ftInteger, ftReal, ftDate:
            res := Movie1.CustomFields.GetIntFieldValue(Prop.FieldTag) -
              Movie2.CustomFields.GetIntFieldValue(Prop.FieldTag);
          else
            res := AnsiCompareText(Movie1.CustomFields.GetFieldValue(Prop.FieldTag, False),
              Movie2.CustomFields.GetFieldValue(Prop.FieldTag, False));
          end;
      end;
      if res = 0 then
        res := AnsiCompareText(Movie1.GetFormattedTitle, Movie2.GetFormattedTitle);
      if res = 0 then
        res := Movie1.iNumber - Movie2.iNumber;
      if res = 0 then
        res := AnsiCompareStr(Format('%p', [Item1.Data]), Format('%p', [Item2.Data]));
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1Resize(Sender: TObject);
begin
  with ListView1 do
    if HeaderSections.Item[1].Visible then
      HeaderSections.Item[1].Width := ClientWidth - IfThen(HeaderSections.Item[0].Visible, HeaderSections.Item[0].Width) - IfThen(VertScrollBarVisible and Settings.rOptions.rMovieList.EnhancedScrollbars, VertScrollBarStyles.Width);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1ItemChecked(Sender: TObject;
  Item: TElTreeItem);
var
  i: Integer;
  _listItems: TObjectList;
  oldSortMode: TSortModes;
begin
  if (Item <> nil) and (Item.Data <> nil) then
  begin
    TMovie(Item.Data).bChecked := Item.Checked;
    FCatalogFile.Modified := True;
    oldSortMode := ListView1.SortMode; // Remove automatic sort because is uncomfortable for user
    ListView1.SortMode := smNone;

    ListView1.Items.BeginUpdate;
    _listItems := TMovie(Item.Data)._listItems;
    for i := 0 to _listItems.Count-1 do
    begin
      if ActionToolsGrid.Checked then
        TElTreeItem(_listItems.Items[i]).SubItems.Strings[fieldChecked+2] := BoolToStr(Item.Checked, True);
      //else
        //TElTreeItem(_listItems.Items[i]).SubItems.Strings[1] := ''; // Hidden column for other field sort; Needed to force automatic sort on item
      TElTreeItem(_listItems.Items[i]).Checked := Item.Checked;
    end;
    ListView1.Items.EndUpdate;

    ListView1.SortMode := oldSortMode; // Restore old sort mode
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1AfterSelectionChange(Sender: TObject);
begin
  SaveCurrentItem;
  if (Sender <> ThumbsViewer) then
    ThumbsViewerUpdateSelection(True);
  with ListView1 do
  begin
    if (Selected <> nil) and (Selected.Selected) then
      OpenItem(Selected)
    else
      HTMLViewerUpdate;
    MovieSelected;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if (Key = Ord('A')) and (Shift = [ssCtrl]) then
  begin
    ListView1.SelectAll;
    ListView1AfterSelectionChange(ListView1);
  end
  else
  if (Key = VK_DELETE) and (Shift = []) then
    ActionMovieDelete.Execute
  else
  if ((Key = Ord('T')) and (Shift = [ssCtrl])) or
    ((Key = VK_MULTIPLY) and (Shift = [])) or ((Key = VK_ADD) and (Shift = [ssCtrl])) then
  begin
    FullExpandOrCollapse := True;
    ListView1.FullExpand;
    FullExpandOrCollapse := False;
    ListView1EnsureVisiblePlus;
    ThumbsViewer.CalcView(True);
    ThumbsViewerUpdateSelection;
  end else
  if ((Key = Ord('T')) and (Shift = [ssCtrl, ssShift])) or
    ((Key = VK_SUBTRACT) and (Shift = [ssCtrl])) then
  begin
    FullExpandOrCollapse := True;
    ListView1.FullCollapse;
    FullExpandOrCollapse := False;
    ListView1EnsureVisiblePlus;
    ThumbsViewer.CalcView(True);
    ThumbsViewerUpdateSelection;
  end else
  if (Key = VK_SPACE) and (Shift = []) then
    with ListView1 do
      if ShowCheckboxes and (Selected <> nil) then
        with Selected do
          if Selected and (Data <> nil) then
          begin
            if Checked then
              ActionMovieUncheck.Execute
            else
              ActionMovieCheck.Execute;
          end;

end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.DragDropFilesDrop(Sender: TObject; Pos: TPoint;
  Value: TStringList);
var
  FileName, ext: string;
  i: Integer;
  DoNotAsk, MultipleMoviecovers: Boolean;
  PicImportMethod: TPictureSelectOption;
begin
  MultipleMoviecovers := False;
  for i := 0 to Value.Count-1 do
  begin
    FileName := Value.Strings[i];
    ext := LowerCase(ExtractFileExt(FileName));

    // **** Catalog ****
    if IndexText(ext, extCatalog) <> -1 then
    begin
      FCatalogFile.Open(FileName);
    end else

    // **** Import something ****
    begin
      if (ListView1.Selected = nil) or (ListView1.Selected.Selected = False) then
        ActionMovieAddExecute(nil);
      if (ListView1.Selected <> nil) and (ListView1.Selected.Selected) and
        (ListView1.Selected.Data <> nil) then
      begin

        // **** Picture ***
        if IndexText(ext, extImage) <> -1 then
        begin
          if ActionPicSelect.Enabled then
            with Settings.rOptions.rMovieInformation do
            begin
              PicImportMethod := TPictureSelectOption(Abs(rPicImport.GetInfoMethod));
              if rPicImport.GetInfoMethod <= 0 then
              begin
                with TPictureDragDropWin.Create(Application) do
                  try
                    ImageListHot.GetIcon(Ord(ICON_PICTUREOPEN), Icon);
                    if not Execute(PicImportMethod, DoNotAsk) then
                      Break; // next file
                    rPicImport.GetInfoMethod := Integer(PicImportMethod);
                    if not DoNotAsk then
                      rPicImport.GetInfoMethod := - rPicImport.GetInfoMethod;
                  finally
                    Release;
                  end;
              end;
              ThumbsViewerStop;
              MakeMovieSave(ListView1.Selected.Data, False);
              ImportMoviePicture(ListView1.Selected.Data, FileName, PicImportMethod);
              LoadMoviePicture;
              HTMLViewerUpdate;
              MovieSelected;
              ThumbsViewerStart;
            end;
        end else

        // **** Video ****
        if IndexText(ext, extVideo) <> -1 then
        begin
          try
            if GetInfoFromMediaFile(FileName, FrmMovie) then
            begin
              MakeMovieSave(ListView1.Selected.Data, True);
              FrmMovie.Modified := True;
              FCatalogFile.Modified := True;
              ListView1UpdateItem;
              HTMLViewerUpdate;
            end;
          except
            on e: Exception do
              MessageWin.Execute(GetShortHint(ActionMovieImportFiles.Hint) + sLineBreak + sLineBreak + e.Message, mtError, [mbOk]);
          end;
        end  // if extVideo
        else

        // **** MovieCovers.com description files ****
        if SameText(ext, '.film') then
        begin
          FCatalogFile.Modified := True;
          if MultipleMoviecovers then
          begin
            ActionMovieAddExecute(nil);
            if (ListView1.Selected = nil) or (ListView1.Selected.Data = nil) then
              Exit;
          end else
            MultipleMoviecovers := True;
          with TStringList.Create do
            try
              MakeMovieSave(ListView1.Selected.Data, True);
              FrmMovie.BeginUpdate;
              try
                LoadFromFile(FileName);
                try
                  FrmMovie.ETranslatedTitle.Text := Strings[0];
                  FrmMovie.EDirector.Text := Strings[1];
                  FrmMovie.EYear.Text := Strings[2];
                  FrmMovie.ECountry.Text := Strings[3];
                  FrmMovie.ECategory.Text := Strings[4];
                  FrmMovie.ELength.Value := StrToIntDef(TextBefore(UpperCase(Strings[5]), 'H'), 0) * 60 + StrToIntDef(TextAfter(UpperCase(Strings[5]), 'H'), 0);
                  FrmMovie.EActors.Text := Strings[6];
                  FrmMovie.EDescription.Text := Strings[7];
                  FrmMovie.EProducer.Text := Strings[8];
                  FrmMovie.EOriginalTitle.Text := Strings[9];
                except
                end;
                if FrmMovie.EOriginalTitle.Text = '' then
                begin
                  FrmMovie.EOriginalTitle.Text := FrmMovie.ETranslatedTitle.Text;
                  FrmMovie.ETranslatedTitle.Text := '';
                end;
              finally
                FrmMovie.EndUpdate;
              end;
              FrmMovie.Modified := True;
              FCatalogFile.Modified := True;
              ListView1UpdateItem;
              HTMLViewerUpdate;
            finally
              Free;
            end;
        end;
      end;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SearchInternetClick(Sender: TObject);
var
  idx: Integer;
begin
  if Sender is TTBXItem then
  begin
    idx := (Sender as TTBXItem).Tag;
    with Settings.rOptions.rMovieInformation.SearchSites do
      if idx in [0..Count-1] then
        LaunchProg(Format(Values[Names[idx]], [TIdURI.ParamsEncode(FrmMovie.EOriginalTitle.Text)]));
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.PopupMovieListPopup(Sender: TObject);
var
  i: Integer;
begin
  with PopupMovieList.Items do
    for i := 0 to Count-1 do
      with Items[i] do
        Visible := Enabled;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.OnURLButtonClick(Sender: TObject);
var
  FieldPos: TPoint;
begin
  if Sender is TAntJvComboEditXP then
  begin
    FSelectedURL := TAntJvComboEditXP(Sender);
    FURL := '';
    with FSelectedURL do
      FieldPos := ClientToScreen(Point(Width-1, Height-1));
    PopupEURL.Popup(FieldPos.X, FieldPos.Y);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.OnURLEnter(Sender: TObject);
begin
  if Sender is TAntJvComboEditXP then
  begin
    FSelectedURL := TAntJvComboEditXP(Sender);
    FURL := '';
    ActionURLOpen.Execute;
  end;
end;

{-------------------------------------------------------------------------------
   Picture panel
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMoviePictureShowExecute(Sender: TObject);
begin
  if ToolbarPictureWindow.Visible then
  begin
    ActionMoviePictureShow.Checked := False;
    ToolbarPictureWindow.Visible := False;
    SplitterBottomList.Visible := False;
  end else
  begin
    ActionMoviePictureShow.Checked := True;
    ToolbarPictureWindow.Visible := True;
    SplitterBottomList.Visible := not ToolbarPictureWindow.Floating;
    if(MoviePicture.Picture.Graphic = nil) then
      LoadMoviePicture;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.MoviePictureMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  CurMovie: TMovie;
  Pt: TPoint;
begin
  if (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil) and
    (ListView1.Selected.Data <> nil) then
  begin
    if (Button = mbLeft) then
    begin
      PictureWin := TPictureWin.Create(Self);
      CurMovie := TMovie(ListView1.Selected.Data);
      try
        CurMovie.Lock;
        if (CurMovie.strPicture <> '') then
        begin
          if (CurMovie.Picture <> nil) then
            PictureWin.Execute(CurMovie.GetFormattedTitle, CurMovie.Picture, CurMovie.strPicture)
          else
          begin
            if FCatalogFile.CurrentFile <> '' then
              SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
            if FileExists(ExpandFileName(CurMovie.strPicture)) then
              PictureWin.Execute(CurMovie.GetFormattedTitle, ExpandFileName(CurMovie.strPicture))
            else
              MessageWin.Execute(Format(Messages.Strings[msgFileNotExists], [CurMovie.strPicture]), mtError, [mbOk]);
          end;
        end;
      finally
        FreeAndNil(PictureWin);
        CurMovie.Unlock;
      end;
    end
    else if (Button = mbRight) then
    begin
      GetCursorPos(Pt);
      PopupImage.Popup(Pt.X, Pt.Y);
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ToolbarPictureWindowClose(Sender: TObject);
begin
  ActionMoviePictureShow.Checked := False;
  SplitterBottomList.Visible := False;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ToolbarPictureWindowDockChanged(Sender: TObject);
begin
  SplitterBottomList.Visible := ToolbarPictureWindow.Visible and not ToolbarPictureWindow.Floating;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionPicSelectExecute(Sender: TObject);
var
  ImportMethod: TPictureSelectOption;
begin
  if (ListView1.Selected <> nil) and (ListView1.Selected.Selected) and
  (ListView1.Selected.Data <> nil) then
    with TPictureSelectionWin.Create(Self) do
      try
        InitialDir := Settings.rOptions.rFolders[fuPicture].Value;
        FileName := '';
        HelpContext := 1062;
        Options := DialogOpenOptions + [ofShowHelp];
        Title := Messages.Strings[msgPicOpen];
        Filter := DialogImageFilter;
        ImportMethod := TPictureSelectOption(Abs(Settings.rOptions.rMovieInformation.rPicImport.GetInfoMethod));
        if Execute(ImportMethod) then
        begin
          Settings.rOptions.rFolders[fuPicture].Value := ExtractFilePath(FileName);
          with Settings.rOptions.rMovieInformation do
            if rPicImport.GetInfoMethod > 0 then
              rPicImport.GetInfoMethod := Integer(ImportMethod)
            else
              rPicImport.GetInfoMethod := - Integer(ImportMethod);
          ThumbsViewerStop;
          MakeMovieSave(ListView1.Selected.Data, False);
          ImportMoviePicture(ListView1.Selected.Data, FileName, ImportMethod);
          LoadMoviePicture;
          HTMLViewerUpdate;
          MovieSelected;
          ThumbsViewerStart;
        end;
      finally
        Free;
      end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionPicSaveAsExecute(Sender: TObject);
var
  Ext: string;
begin
  with ListView1 do
    if Selected <> nil then
      with Selected do
        if (Selected) and (Data <> nil) then
          with TMovie(Data) do
            if strPicture <> '' then
              with TSaveDialog.Create(Self) do
                try
                  InitialDir := Settings.rOptions.rFolders[fuPicture].Value;
                  Options := DialogSaveOptions;
                  Title := Messages.Strings[msgPicSaveAs];
                  Ext := ExtractFileExt(strPicture);
                  case IndexText(ext, extImage) of
                    extPNG:
                      Filter := 'PNG (*.png)|*.png';
                    extJPG, extJPE, extJPEG:
                      Filter := 'JPEG (*.jpg, *.jpe, *.jpeg)|*.jpg;*.jpe;*.jpeg';
                    extGIF:
                      Filter := 'GIF (*.gif)|*.gif';
                  else
                    Filter := 'All files (*.*)|*.*';
                  end;
                  FileName := ValidateFileName(GetFormattedTitle);
                  if FileName = '' then
                    FileName := IntToStr(iNumber);
                  System.Delete(Ext, 1, 1);
                  DefaultExt := Ext;
                  if Execute then
                  begin
                    Settings.rOptions.rFolders[fuPicture].Value := ExtractFilePath(FileName);
                    if Picture <> nil then
                      Picture.SaveToFile(FileName)
                    else
                    begin
                      SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
                      CopyFile(PChar(strPicture), PChar(FileName), False);
                    end;
                  end;
                finally
                  Free;
                end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionPicCopyExecute(Sender: TObject);
begin
  if(MoviePicture.Picture.Graphic = nil) then
  begin
    LoadMoviePicture(True);
  end;
  Clipboard.Assign(MoviePicture.Picture);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionPicDeleteExecute(Sender: TObject);
var
  res: TModalResult;
  DeletePic: Boolean;
begin
  DeletePic := False;

  with ListView1 do
    if Selected <> nil then
      with Selected do
        if (Selected) and (Data <> nil) then
        begin

          with TMovie(Data) do
          begin
            if Picture <> nil then
            begin
              if MessageWin.Execute(Messages.Strings[msgPicDelete], mtConfirmation, [mbOk, mbCancel]) = 1 then
              begin
                ThumbsViewerStop;
                MakeMovieSave(Data, False);
                DeletePic := True;
              end;
            end
            else
            if strPicture <> '' then
            begin
              if FCatalogFile.CurrentFile <> '' then
                SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
              if not FileExists(ExpandFileName(strPicture)) then
                DeletePic := True
              else
              begin
                if ExtractFilePath(strPicture) = '' then
                begin
                  res := MessageWin.Execute(Messages.Strings[msgPicDelete], mtConfirmation, [mbOk, mbCancel]);
                  if res = 2 then
                    res := 3;
                end
                else
                begin
                  res := MessageWin.Execute(Format(Messages.Strings[msgPicDeleteLink], [ExpandFileName(strPicture)]), mtConfirmation, [mbYes, mbNo, mbCancel]);
                end;
                if res in [1, 2] then
                begin
                  ThumbsViewerStop;
                  MakeMovieSave(Data, False);
                  if res = 1 then
                    DeleteFile(ExpandFileName(strPicture));
                  DeletePic := True;
                end;
              end;
            end;
            if DeletePic then
            begin
              if(Picture <> nil) then
              begin
                Picture.Free;
                Picture := nil;
              end;
              strPicture := '';
              if (_thumb <> nil) then
                FreeAndNil(_thumb);
              _thumbError := 0;
              FCatalogFile.Modified := True;
              LoadMoviePicture;
              HTMLViewerUpdate;
              MovieSelected;
              ThumbsViewerStart;
            end;
          end;
        end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionPicUndockExecute(Sender: TObject);
begin
  with ToolbarPictureWindow do
  begin
    Floating := not Floating;
    if not Floating then
    begin
      CurrentDock := DockBottomList;
      SplitterBottomList.Visible := True;
    end else
    begin
      SplitterBottomList.Visible := False;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SplitterBottomListCanResize(Sender: TObject; var NewSize: Integer; var Accept: Boolean);
var
  d: TTBXDock;
  i: Integer;
begin
  d := ToolbarPictureWindow.CurrentDock as TTBXDock;
  if d = DockBottomList then
  begin
    i := d.ClientHeight - ToolbarPictureWindow.Height;
    (Sender as TSplitter).MinSize := i + 75;
    ToolbarPictureWindow.Height := NewSize - i;
  end
  else
  if d = DockRightInfo then
  begin
    i := d.ClientWidth - ToolbarPictureWindow.Width;
    (Sender as TSplitter).MinSize := i + 75;
    ToolbarPictureWindow.Width := NewSize - i;
  end;
end;

{-------------------------------------------------------------------------------
  Find Window
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFindFindnextExecute(Sender: TObject);
var
  Idx, StartIdx, SelectedField: Integer;
  WholeField, Finished: Boolean;
  Value: string;
begin
  with ListView1 do
    if Items.Count > 1 then
    begin
      WholeField := ActionFindWholefield.Checked;
      SelectedField := cbxField.ItemIndex - 1;
      if SelectedField >= fieldCount then // It is a custom field
        SelectedField := SelectedField - fieldCount + customFieldLow;
      Value := EValue.Text;
      if Selected = nil then
      begin
        StartIdx := 0;
        Idx := 0;
      end else
      begin
        StartIdx := Selected.AbsoluteIndex;
        Idx := StartIdx + 1;
      end;
      Finished := False;
      with Items do
        repeat
          if (Item[Idx] <> nil) and (Item[Idx].Data <> nil) then
            if TMovie(Item[Idx].Data).ContainsText(Value, SelectedField, WholeField) then
            begin
              Finished := True;
              DeselectAll;
              Selected := Item[Idx];
              Item[Idx].Selected := True;
              ListView1EnsureVisiblePlus;
              ListView1AfterSelectionChange(ListView1);
            end;
          inc(Idx);
          if Idx >= Count then
            Idx := 0;
        until (StartIdx = Idx) or (Finished);
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFindDisplayExecute(Sender: TObject);
begin
  ActionFindFindnext.Enabled := not ActionFindDisplay.Checked;
  RefreshMovieList;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionFindWholefieldExecute(Sender: TObject);
begin
  if ActionFindDisplay.Checked then
    ActionFindDisplayExecute(ActionFindWholefield);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.EValueKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = Char(VK_RETURN) then
    ActionFindFindnext.Execute;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.cbxFieldChange(Sender: TObject);
begin
  if ActionFindDisplay.Checked then
    RefreshMovieList;
  HTMLViewerUpdate;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.EValueChange(Sender: TObject);
begin
  if ActionFindDisplay.Checked then
    RefreshMovieList;
  HTMLViewerUpdate;
end;

{-------------------------------------------------------------------------------
  Toolbars
-------------------------------------------------------------------------------}

procedure TMainWindow.ToolbarMainClose(Sender: TObject);
begin
  ActionDisplayMainToolbar.Checked := false;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ToolbarPictureClose(Sender: TObject);
begin
  ActionDisplayPictureToolbar.Checked := false;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ToolbarFindVisibleChanged(Sender: TObject);
begin
  if ToolbarFind.Visible then
  begin
    ActionMovieFind.Checked := True;
    ActionFindDisplay.Checked := False;
    EValue.SetFocus;
  end else
  begin
    ActionMovieFind.Checked := False;
    with ActionFindDisplay do
      if Checked then
        Execute;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.DockMainTopRequestDock(Sender: TObject;
  Bar: TTBCustomDockableWindow; var Accept: Boolean);
begin
  Accept := (Bar = ToolbarMain) or (Bar = ToolbarMenu) or (Bar = ToolbarPicture) or (Bar = ToolbarFind);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.DockMainLeftRequestDock(Sender: TObject;
  Bar: TTBCustomDockableWindow; var Accept: Boolean);
begin
  Accept := (Bar = ToolbarMain) or (Bar = ToolbarMenu) or (Bar = ToolbarPicture);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.DockMainBottomRequestDock(Sender: TObject;
  Bar: TTBCustomDockableWindow; var Accept: Boolean);
begin
  Accept := (Bar = ToolbarFind);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.DockImageLeftRequestDock(Sender: TObject;
  Bar: TTBCustomDockableWindow; var Accept: Boolean);
begin
  Accept := (Bar = ToolbarPicture);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.DockBottomListRequestDock(Sender: TObject;
  Bar: TTBCustomDockableWindow; var Accept: Boolean);
begin
  Accept := (Bar = ToolbarPictureWindow);
end;

{-------------------------------------------------------------------------------
  Store & Restore selected items
-------------------------------------------------------------------------------}

procedure TMainWindow.StoreSelectedState;
var
//  DefCheck: Boolean;
  i: Integer;
//  PrevItem: TElTreeItem;
begin
  if MovieList <> nil then
    with MovieList do
      for i := 0 to Count-1 do
        with TMovie(Items[i]) do
        begin
          _bSelected := False;
          _bVisible := False;
          _selectedGroup := '';
          _selectedItem := nil;
        end;
  with ListView1 do
    for i := 0 to Items.Count-1 do
      if (Items[i].Data <> nil) then
        with TMovie(Items[i].Data) do
        begin
          _bVisible := True;
          if Items[i].Selected and (not _bSelected) then
          begin
            _bSelected := True;
            if Items[i].Parent <> nil then
              _selectedGroup := GetGroupNameWithoutCount(Items[i].Parent.Text);
          end;
        end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.LoadSelectedState;
var
  i: Integer;
begin
  with ListView1 do
  begin
    Items.BeginUpdate;
    DeselectAll;
    ItemFocused := nil;
    for i := 0 to Items.Count-1 do
      if Items[i].Data <> nil then
        with TMovie(Items[i].Data) do
        begin
          if _bSelected and ((_selectedItem = nil) or
            ((Items[i].Parent <> nil) and
            (AnsiCompareText(GetGroupNameWithoutCount(Items[i].Parent.Text), _selectedGroup) = 0))) then
          begin
            if(_selectedItem <> nil) then
            begin
              TElTreeItem(_selectedItem).Selected := False;
              if (TElTreeItem(_selectedItem).Parent <> nil) and
                (not Settings.rOptions.rMovieList.GroupExpand) then
                TElTreeItem(_selectedItem).Parent.Expanded := False;
            end;
            _selectedItem := Items[i];
            if SelectedCount = 0 then
              ItemFocused := Items[i]
            else
              Items[i].Selected := True;
            if (TElTreeItem(_selectedItem).Parent <> nil) then
              Items[i].Parent.Expanded := True;
          end;
        end;
    Selected := GetNextSelected(nil);
    Items.EndUpdate;
  end;
  ListView1AfterSelectionChange(ListView1);
end;

{-------------------------------------------------------------------------------
  Others
-------------------------------------------------------------------------------}

function TMainWindow.ExecuteAction(Action: TBasicAction): Boolean;
begin
  if (Action is THintAction) then
  begin
    StatusBar1.Panels[panelHint].Caption := THintAction(Action).Hint;
    Result := True;
  end
  else
    Result := inherited ExecuteAction(Action);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.OnAppHelp(Command: Word; Data: Integer; var CallHelp: Boolean): Boolean;
begin
  if Data <> Self.HelpContext then
    LaunchHelp(Data);
  CallHelp := False;
  Result := False;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionMovieRandomExecute(Sender: TObject);
var
  i, nb : Integer;
begin
  with ListView1 do
  begin
    if Items.Count > 0 then
    begin
      nb := 100;
      repeat
        i := Random(ListView1.Items.Count);
        nb := nb - 1;
      until (ListView1.Items.Item[i].Data <> nil) or (nb < 0);
      if (nb >= 0) then
      begin
        //ListView1UpdateItem; <-- Check in SaveCurrentItem
        {if not Settings.rOptions.rMovieList.GroupExpand then
        begin
          FullExpandOrCollapse := True;
          FullCollapse;
          FullExpandOrCollapse := False;
          ThumbsViewer.CalcView(True);
          ThumbsViewer.Invalidate;
        end;}
        DeselectAll;
        ItemFocused := nil;
        ItemFocused := Items.Item[i];
        Selected := GetNextSelected(nil);
        ListView1EnsureVisiblePlus;
        ListView1AfterSelectionChange(ListView1);
      end;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1HeaderColumnClick(Sender: TObject;
  SectionIndex: Integer);
var
  Field: Integer;
  Descend: Boolean;
begin
  ListView1UpdateItem;
  Field := StrToInt(ListView1.HeaderSections.Item[SectionIndex].FieldName);
  Descend := FSortField < 0;
  if (Abs(FSortField) - 1) = Field then
    Descend := not Descend;
  SetSortField(Field, Descend);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionStretchListExecute(Sender: TObject);
begin
  ActionStretchList.Checked := not ActionStretchList.Checked;
  if ActionStretchList.Checked then
  begin
    PanelLeftOldWidth := PanelLeft.Width;
    PanelMovieInfos.Visible := False;
    SplitterMovieInfos.Visible := False;
    PanelLeft.Align := alClient;
    ListView1UpdateItem;
  end else
  begin
    PanelLeft.Align := alLeft;
    PanelLeft.Width := PanelLeftOldWidth;
    SplitterMovieInfos.Visible := True;
    PanelMovieInfos.Visible := True;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionDisplayHTMLExecute(Sender: TObject);
begin
  ActionDisplayHTML.Checked := not ActionDisplayHTML.Checked;
  HTMLViewer.Visible := ActionDisplayHTML.Checked;
  TabMovieInfos.Visible := not ActionDisplayHTML.Checked;
  HTMLViewerUpdate;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ActionDisplayThumbnailsExecute(Sender: TObject);
begin
  ActionDisplayThumbnails.Checked := not ActionDisplayThumbnails.Checked;
  ThumbsPanel.Visible := ActionDisplayThumbnails.Checked;
  ListView1.Visible := not ActionDisplayThumbnails.Checked;
  if (ThumbsPanel.Visible) and (ThumbsViewer.Items.Count = 0) and
    (ThumbsViewer.SmartGroups.Count = 0) and (ListView1.Items.Count > 0) then
    ThumbsViewerStart // MovieSelected done in ThumbsViewerStart
  else
    MovieSelected;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.TabMovieInfosChange(Sender: TObject);
begin
  FrmMovie.Visible := False;
  FrmMovieCustom.Visible := False;
  FrmMovie.Visible := TabMovieInfos.TabIndex = 0;
  FrmMovieCustom.Visible := TabMovieInfos.TabIndex = 1;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.HTMLViewerMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
begin
  if(HTMLViewer.CanFocus) then
    HTMLViewer.SetFocus;
end;

{-------------------------------------------------------------------------------
  HTML Viewer
-------------------------------------------------------------------------------}

procedure TMainWindow.HTMLViewerUpdate;
begin
  if ActionDisplayHTML.Checked then
  begin
    HTMLViewer.VScrollBarPosition := 0;
    HTMLViewer.LoadFromString(GenerateHTMLSelectedMovie, HTMLTemplatePath);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.PreGenerateHTMLTemplate;
var
  i, j, t, lastPos: integer;
  ATag : string;
  Found : Boolean;
  HTMLTemplate : string;
begin
  HTMLTemplatePreGenerated.Clear;
  HTMLTemplatePath := '';

  if FCatalogFile <> nil then
    SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));

  with Settings.rOptions.rFiles do
  begin
    if FileExists(HTMLTemplateFile) then
      HTMLTemplatePath := HTMLTemplateFile
    else if FileExists(strDirApp + strDirTemplates + HTMLTemplateFile) then
      HTMLTemplatePath := strDirApp + strDirTemplates + HTMLTemplateFile
    else if FileExists(strDirApp + HTMLTemplateFile) then
      HTMLTemplatePath := strDirApp + HTMLTemplateFile;

    if HTMLTemplatePath = '' then
    begin
      if HTMLTemplateFile = '' then
        HTMLTemplatePreGenerated.Add('No file template !')
      else
        HTMLTemplatePreGenerated.Add('"' + HTMLTemplateFile + '"' + ' not found !');
      Exit;
    end;

    HTMLTemplatePreGenerated.LoadFromFile(HTMLTemplatePath);
    HTMLTemplate := HTMLTemplatePreGenerated.Text;
    HTMLTemplatePreGenerated.Clear;
  end;

  i := 1;
  lastPos := 1;
  while(HTMLTemplate[i] <> #0) do
  begin
    if (HTMLTemplate[i] = '$') and (HTMLTemplate[i+1] = '$') then
    begin

      // Fields
      Found := False;
      t := 0;
      while (t < strListTags.Count) and (not Found) do
      begin
        ATag := strListTags.Strings[t];
        j := 1;
        while (HTMLTemplate[i+j-1] <> #0) and (ATag[j] <> #0) do
        begin
          if (HTMLTemplate[i+j-1] <> ATag[j]) then
            break;
          if (ATag[j+1] = #0) then
          begin
            if (i <> lastPos) then
              HTMLTemplatePreGenerated.Add(Copy(HTMLTemplate, lastPos, i-lastPos));
            // Replace Labels
            if ATag = TAG_LABELNUMBER then HTMLTemplatePreGenerated.Add(strFields.Strings[0])
            else if ATag = TAG_LABELCHECKED then HTMLTemplatePreGenerated.Add(strFields.Strings[1])
            else if ATag = TAG_LABELMEDIA then HTMLTemplatePreGenerated.Add(strFields.Strings[2])
            else if ATag = TAG_LABELTYPE then HTMLTemplatePreGenerated.Add(strFields.Strings[3])
            else if ATag = TAG_LABELSOURCE then HTMLTemplatePreGenerated.Add(strFields.Strings[4])
            else if ATag = TAG_LABELDATEADD then HTMLTemplatePreGenerated.Add(strFields.Strings[5])
            else if ATag = TAG_LABELBORROWER then HTMLTemplatePreGenerated.Add(strFields.Strings[6])
            else if ATag = TAG_LABELRATING then HTMLTemplatePreGenerated.Add(strFields.Strings[7])
            else if ATag = TAG_LABELORIGINALTITLE then HTMLTemplatePreGenerated.Add(strFields.Strings[8])
            else if ATag = TAG_LABELTRANSLATEDTITLE then HTMLTemplatePreGenerated.Add(strFields.Strings[9])
            else if ATag = TAG_LABELFORMATTEDTITLE then HTMLTemplatePreGenerated.Add(strFields.Strings[10])
            else if ATag = TAG_LABELDIRECTOR then HTMLTemplatePreGenerated.Add(strFields.Strings[11])
            else if ATag = TAG_LABELPRODUCER then HTMLTemplatePreGenerated.Add(strFields.Strings[12])
            else if ATag = TAG_LABELCOUNTRY then HTMLTemplatePreGenerated.Add(strFields.Strings[13])
            else if ATag = TAG_LABELCATEGORY then HTMLTemplatePreGenerated.Add(strFields.Strings[14])
            else if ATag = TAG_LABELYEAR then HTMLTemplatePreGenerated.Add(strFields.Strings[15])
            else if ATag = TAG_LABELLENGTH then HTMLTemplatePreGenerated.Add(strFields.Strings[16])
            else if ATag = TAG_LABELACTORS then HTMLTemplatePreGenerated.Add(strFields.Strings[17])
            else if ATag = TAG_LABELURL then HTMLTemplatePreGenerated.Add(strFields.Strings[18])
            else if ATag = TAG_LABELDESCRIPTION then HTMLTemplatePreGenerated.Add(strFields.Strings[19])
            else if ATag = TAG_LABELCOMMENTS then HTMLTemplatePreGenerated.Add(strFields.Strings[20])
            else if ATag = TAG_LABELVIDEOFORMAT then HTMLTemplatePreGenerated.Add(strFields.Strings[21])
            else if ATag = TAG_LABELVIDEOBITRATE then HTMLTemplatePreGenerated.Add(strFields.Strings[22])
            else if ATag = TAG_LABELAUDIOFORMAT then HTMLTemplatePreGenerated.Add(strFields.Strings[23])
            else if ATag = TAG_LABELAUDIOBITRATE then HTMLTemplatePreGenerated.Add(strFields.Strings[24])
            else if ATag = TAG_LABELRESOLUTION then HTMLTemplatePreGenerated.Add(strFields.Strings[25])
            else if ATag = TAG_LABELFRAMERATE then HTMLTemplatePreGenerated.Add(strFields.Strings[26])
            else if ATag = TAG_LABELLANGUAGES then HTMLTemplatePreGenerated.Add(strFields.Strings[27])
            else if ATag = TAG_LABELSUBTITLES then HTMLTemplatePreGenerated.Add(strFields.Strings[28])
            else if ATag = TAG_LABELSIZE then HTMLTemplatePreGenerated.Add(strFields.Strings[29])
            else if ATag = TAG_LABELDISKS then HTMLTemplatePreGenerated.Add(strFields.Strings[30])
            else if ATag = TAG_LABELPICTURE then HTMLTemplatePreGenerated.Add(Messages.Strings[7])
            else if ATag = TAG_LABELAUDIOKBPS then HTMLTemplatePreGenerated.Add(FrmMovie.LAudioKbps.Caption)
            else if ATag = TAG_LABELVIDEOKBPS then HTMLTemplatePreGenerated.Add(FrmMovie.LVideoKbps.Caption)
            else if ATag = TAG_LABELFPS then HTMLTemplatePreGenerated.Add(FrmMovie.LFramerateFPS.Caption)
            else if ATag = TAG_LABELUNIT then HTMLTemplatePreGenerated.Add(FrmMovie.LSizeUnit.Caption)
            else HTMLTemplatePreGenerated.Add(ATag);
            lastPos := i+j;
            i := lastPos;
            Found := True;
          end;
          Inc(j)
        end;
        Inc(t);
      end; // End while

      // Custom Fields
      if (Found = False) and (MovieList <> nil) then
      begin
        // Replace long tag before short tag (alphabetic order)
        // Ex: $$LABEL_CF_TEST1 replaced before $$LABEL_CF_TEST
        with MovieList.CustomFieldsProperties do
        begin
          t := (Count*2) - 1;
          while (t >= 0) and (not Found) do
          begin
            if (t < Count) then
              ATag := TAG_LABEL_CF + UpperCase(Strings[t])
            else
              ATag := TAG_ITEM_CF + UpperCase(Strings[t-Count]);
            j := 1;
            while (HTMLTemplate[i+j-1] <> #0) and (ATag[j] <> #0) do
            begin
              if (HTMLTemplate[i+j-1] <> ATag[j]) then
                break;
              if (ATag[j+1] = #0) then
              begin
                if (i <> lastPos) then
                  HTMLTemplatePreGenerated.Add(Copy(HTMLTemplate, lastPos, i-lastPos));
                if (t < Count) then
                  HTMLTemplatePreGenerated.Add(Objects[t].FieldName)
                else
                  HTMLTemplatePreGenerated.Add(ATag);
                lastPos := i+j;
                i := lastPos;
                Found := True;
              end;
              Inc(j)
            end;
            Dec(t);
          end; // End while
        end; // End with
      end; // End if Found = False
    end;
    Inc(i);
  end;
  if (i <> lastPos) then
  begin
    HTMLTemplatePreGenerated.Add(Copy(HTMLTemplate, lastPos, i-lastPos));
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.GenerateHTMLSelectedMovie : string;
var
  LineBreak: string;
  i, n: integer;
  ImageFileName, ImageApprName: string;
  FHTMLIMGAttr, ATag: string;
  Found: Boolean;

  function GetV(Field: Integer; Text: string) : string;
  begin
    if (EValue.Text <> '') and ((cbxField.ItemIndex = 0) or (Field = (cbxField.ItemIndex - 1))) then
      Result := StringReplace(Text, EValue.Text, '<b><font color="#FF0000">'+EValue.Text+'</font></b>', [rfReplaceAll, rfIgnoreCase])
    else
      Result := Text;
  end;

  function GetCFV(FieldTag: string; Idx: Integer) : string;
  var
    Text: string;
  begin
    Text := ConvertFieldValue(FrmMovieCustom.GetCurrentValue(FieldTag),
      MovieList.CustomFieldsProperties.Objects[Idx].FieldType, True, False, False);
    if (MovieList.CustomFieldsProperties.Objects[Idx].FieldType = ftText) and (LineBreak <> '') then
      Text := StringReplace(Text, #13#10, LineBreak, [rfReplaceAll]);
    if (EValue.Text <> '') and ((cbxField.ItemIndex = 0) or
      ((cbxField.ItemIndex - 1 >= fieldCount) and (MovieList <> nil) and (idx = cbxField.ItemIndex - fieldCount - 1))) then
      Result := StringReplace(Text, EValue.Text, '<b><font color="#FF0000">'+EValue.Text+'</font></b>', [rfReplaceAll, rfIgnoreCase])
    else
      Result := Text;
  end;

begin
  Result := '';
  if (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil) and
    (ListView1.Selected.Data <> nil) then
  begin
    LineBreak := Settings.rOptions.rExport.Linebreak;
    ImageFileName := '*MoviePoster:';
    ImageApprName := '*StreamAppr:';
    FHTMLIMGAttr := '';
    with Settings.rOptions.rExport do
    begin
      if ForcePicSizeW >= 0 then
        FHTMLIMGAttr := Format(' width="%d"', [ForcePicSizeW]);
      if ForcePicSizeH >= 0 then
        FHTMLIMGAttr := Format('%s height="%d"', [FHTMLIMGAttr, ForcePicSizeH]);
    end;

    for n := 0 to HTMLTemplatePreGenerated.Count-1 do
    begin
      if (HTMLTemplatePreGenerated.Strings[n][1] <> '$') or (HTMLTemplatePreGenerated.Strings[n][2] <> '$') then
      begin
        Result := Result + HTMLTemplatePreGenerated.Strings[n];
        continue;
      end;
      with TMovie(ListView1.Selected.Data) do
      with FrmMovie do
      begin
        ATag := HTMLTemplatePreGenerated.Strings[n];
        // Replace Items
        if ATag = TAG_ITEMNUMBER then Result := Result + GetV(fieldNumber, GetFieldValue(fieldNumber))
        else if ATag = TAG_ITEMCHECKED then Result := Result + IfThen(bChecked, 'x', ' ')
        else if ATag = TAG_ITEMTYPE then Result := Result + GetV(fieldMediaType, EMediaType.Text)
        else if ATag = TAG_ITEMMEDIA then Result := Result + GetV(fieldMedia, EMedia.Text)
        else if ATag = TAG_ITEMSOURCE then Result := Result + GetV(fieldSource, ESource.Text)
        else if ATag = TAG_ITEMBORROWER then Result := Result + GetV(fieldBorrower, EBorrower.Text)
        else if ATag = TAG_ITEMDATEADD then
        begin
          if EDate.Checked then
            Result := Result + GetV(fieldDate, DateToStr(Trunc(EDate.DateTime)))
        end else if ATag = TAG_ITEMORIGINALTITLE then Result := Result + GetV(fieldOriginalTitle, EOriginalTitle.Text)
        else if ATag = TAG_ITEMTRANSLATEDTITLE then Result := Result + GetV(fieldTranslatedTitle, ETranslatedTitle.Text)
        else if ATag = TAG_ITEMFORMATTEDTITLE then Result := Result + GetV(fieldFormattedTitle, GetTitle)
        else if ATag = TAG_ITEMDIRECTOR then Result := Result + GetV(fieldDirector, EDirector.Text)
        else if ATag = TAG_ITEMPRODUCER then Result := Result + GetV(fieldProducer, EProducer.Text)
        else if ATag = TAG_ITEMCOUNTRY then Result := Result + GetV(fieldCountry, ECountry.Text)
        else if ATag = TAG_ITEMYEAR then Result := Result + GetV(fieldYear, EYear.Text)
        else if ATag = TAG_ITEMCATEGORY then Result := Result + GetV(fieldCategory, ECategory.Text)
        else if ATag = TAG_ITEMLENGTH then Result := Result + GetV(fieldLength, ELength.Text)
        else if ATag = TAG_ITEMACTORS then Result := Result + GetV(fieldActors, IfThen(LineBreak <> '', StringReplace(EActors.Text, #13#10, LineBreak, [rfReplaceAll]), EActors.Text))
        else if ATag = TAG_ITEMURL then Result := Result + GetV(fieldURL, EURL.Text)
        else if ATag = TAG_ITEMCOMMENTS then Result := Result + GetV(fieldComments, IfThen(LineBreak <> '', StringReplace(EComments.Text, #13#10, LineBreak, [rfReplaceAll]), EComments.Text))
        else if ATag = TAG_ITEMDESCRIPTION then Result := Result + GetV(fieldDescription, IfThen(LineBreak <> '', StringReplace(EDescription.Text, #13#10, LineBreak, [rfReplaceAll]), EDescription.Text))
        else if ATag = TAG_ITEMFORMAT then Result := Result + GetV(fieldVideoFormat, EVideoFormat.Text)
        else if ATag = TAG_ITEMVIDEOFORMAT then Result := Result + GetV(fieldVideoFormat, EVideoFormat.Text)
        else if ATag = TAG_ITEMAUDIOFORMAT then Result := Result + GetV(fieldAudioFormat, EAudioFormat.Text)
        else if ATag = TAG_ITEMVIDEOBITRATE then Result := Result + GetV(fieldVideoBitrate, EVideoBitrate.Text)
        else if ATag = TAG_ITEMAUDIOBITRATE then Result := Result + GetV(fieldAudioBitrate, EAudioBitrate.Text)
        else if ATag = TAG_ITEMRESOLUTION then Result := Result + GetV(fieldResolution, EResolution.Text)
        else if ATag = TAG_ITEMFRAMERATE then Result := Result + GetV(fieldFrameRate, EFramerate.Text)
        else if ATag = TAG_ITEMSIZE then Result := Result + GetV(fieldSize, ESize.Text)
        else if ATag = TAG_ITEMLANGUAGES then Result := Result + GetV(fieldLanguages, ELanguages.Text)
        else if ATag = TAG_ITEMSUBTITLES then Result := Result + GetV(fieldSubtitles, ESubtitles.Text)
        else if ATag = TAG_ITEMDISKS then Result := Result + GetV(fieldDisks, EDisks.Text)
				else if ATag = TAG_ITEMRATING then Result := Result + GetV(fieldRating, ERating.Text)
				else if ATag = TAG_ITEMPICTUREFILENAME then // Warning put before TAG_ITEMPICTURE !
					Result := Result + ImageFileName + IntToStr(Integer(@(ListView1.Selected.Data)))
				else if ATag = TAG_ITEMPICTURE then
					Result := Result + Format('<img src="%s" alt="poster%d"%s />', [ImageFileName + IntToStr(Integer(@(ListView1.Selected.Data))), iNumber, FHTMLIMGAttr])
        else if ATag = TAG_ITEMAPPR10 then
        begin
          if ERating.Text <> '' then
          begin
            i := Round(StrToFloat(ERating.Text));
					  Result := Result + Format('<img src="%s" alt="%d/10" />', [ImageApprName + IntToStr(Integer(@MStreamAppr10[i])), i]);
          end;
				end else if ATag = TAG_ITEMAPPRECIATION then
				begin
          if ERating.Text <> '' then
          begin
            i := 0;
            case Trunc(StrToFloatTrunc(ERating.Text)*10) of
               0..29:  begin i := 0; end;
              30..49:  begin i := 1; end;
              50..69:  begin i := 2; end;
              70..89:  begin i := 3; end;
              90..100: begin i := 4; end;
            end;
            Result := Result + Format('<img src="%s" alt="%d/4" />', [ImageApprName + IntToStr(Integer(@MStreamAppr[i])), i]);
          end;
        // Custom fields value
				end else
        begin
          Found := False;
          if MovieList <> nil then
          begin
            with MovieList.CustomFieldsProperties do
              // Replace long tag before short tag (alphabetic order)
              // Ex: $$LABEL_CF_TEST1 replaced before $$LABEL_CF_TEST
              for i := Count-1 downto 0 do
                if ATag = TAG_ITEM_CF + UpperCase(Strings[i]) then
                begin
                  Result := Result + GetCFV(Strings[i], i);
                  Found := True;
                  break;
                end;
          end;
          if not Found then
            Result := Result + ATag;
        end;
      end; // with
    end; // for
  end; // if selected item <> nil
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.HTMLViewerImageRequest(Sender: TObject;
  const SRC: String; var Stream: TMemoryStream);
var
  PMovie: ^TMovie;
  PStream: ^TMemoryStream;
  ImageFileName: string;
begin
  if StartsStr('*MoviePoster:', SRC) then
  begin
    PMovie := Pointer(StrToInt(StringReplace(SRC, '*MoviePoster:', '', [])));
    with PMovie^ do
    begin
      if Picture <> nil then
      begin
        Lock;
        try
          Picture.Seek(0, soBeginning);
          MStream.LoadFromStream(Picture);
          Stream := MStream;
        finally
          UnLock;
        end;
      end
      else
      begin
        if strPicture = '' then
          ImageFileName := strDirApp + strDirTemplates + strFileNoPoster
        else
        begin
          if FCatalogFile.CurrentFile <> '' then
            SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
          ImageFileName := ExpandFileName(strPicture);
          if not FileExists(ImageFileName) then
            ImageFileName := strDirApp + strDirTemplates + strFileNoFileFound
        end;
        Lock;
        try
          if FileExists(ImageFileName) then
          begin
            MStream.LoadFromFile(ImageFileName);
            Stream := MStream;
          end;
        finally
          Unlock;
        end;
      end;
    end;
  end else if StartsStr('*StreamAppr:', SRC) then
  begin
    PStream := Pointer(StrToInt(StringReplace(SRC, '*StreamAppr:', '', [])));
    Stream := PStream^;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.HTMLViewerImageClick(Sender, Obj: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  PMovie: ^TMovie;
  Pt: TPoint;
begin
  if (Obj is TImageObj) and StartsStr('*MoviePoster:', TImageObj(Obj).Source) then
  begin
    if (Button = mbLeft) then
    begin
      PMovie := Pointer(StrToInt(StringReplace(TImageObj(Obj).Source, '*MoviePoster:', '', [])));
      PictureWin := TPictureWin.Create(Self);
      try
        PMovie^.Lock;
        if (PMovie^.strPicture <> '') then
          if (PMovie^.Picture <> nil) then
            PictureWin.Execute(PMovie^.GetFormattedTitle, PMovie^.Picture, PMovie^.strPicture)
          else
          begin
            if FCatalogFile.CurrentFile <> '' then
              SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
            if FileExists(ExpandFileName(PMovie^.strPicture)) then
              PictureWin.Execute(PMovie^.GetFormattedTitle, ExpandFileName(PMovie^.strPicture))
            else
              MessageWin.Execute(Format(Messages.Strings[msgFileNotExists], [PMovie^.strPicture]), mtError, [mbOk]);
          end;
      finally
        FreeAndNil(PictureWin);
        PMovie^.Unlock;
      end;
    end
    else if (Button = mbRight) then
    begin
      GetCursorPos(Pt);
      PopupImage.Popup(Pt.X, Pt.Y);
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.HTMLViewerKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if(Shift = [ssCtrl]) and (Key = Ord('C')) then
  begin
    HTMLViewer.CopyToClipboard;
  end
  else if(Shift = [ssCtrl]) and (Key = Ord('A')) then
  begin
    HTMLViewer.SelectAll;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.HTMLViewerHotSpotClick(Sender: TObject;
  const SRC: String; var Handled: Boolean);
begin
  Handled := True;
  LaunchProg(SRC);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.HTMLViewerRightClick(Sender: TObject;
  Parameters: TRightClickParameters);
var
  Pt: TPoint;
begin
  FSelectedURL := nil;
  FURL := '';
  if (Parameters.URL <> '') then
  begin
    FURL := Parameters.URL;
    GetCursorPos(Pt);
    ActionURLBrowse.Enabled := False;
    ActionURLBrowse.Visible := ActionURLBrowse.Enabled;
    PopupEURL.Alignment := paLeft;
    PopupEURL.Popup(Pt.X, Pt.Y);
    PopupEURL.Alignment := paRight;
    ActionURLBrowse.Enabled := True;
    ActionURLBrowse.Visible := ActionURLBrowse.Enabled;
  end
  else if (Parameters.Image = nil) then
  begin
    GetCursorPos(Pt);
    if HTMLViewer.SelText <> '' then
      MnuHtmlCopy.Enabled := True
    else
      MnuHtmlCopy.Enabled := False;
    PopupHTMLViewer.Popup(Pt.X, Pt.Y);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.MnuHtmlCopyClick(Sender: TObject);
begin
  HTMLViewer.CopyToClipboard;
end;

{-------------------------------------------------------------------------------
  TJPEGImageFix
-------------------------------------------------------------------------------}

procedure TJPEGImageFix.Draw(ACanvas: TCanvas; const Rect: TRect);
begin
  Bitmap.Canvas.Lock; // New
  try
    ACanvas.StretchDraw(Rect, Bitmap);
  finally
    Bitmap.Canvas.Unlock;
  end;
end;

{-------------------------------------------------------------------------------
  ThumbThread
-------------------------------------------------------------------------------}

constructor ThumbThread.Create(Thumbs: TrkSmartView; Items: TElTree; Movies: TMovieList);
begin
  ThumbsViewer := Thumbs;
  ListView1 := Items;
  MovieList := Movies;
  FreeOnTerminate := False;
  inherited Create(False);
  Priority := tpLower;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure ThumbThread.Execute;
var
  id, nb, nb10, i: Integer;
  Movie: TMovie;
  InView: Boolean;
  PtTmp: TPoint;
  List: TIntList;
begin
  inherited;

  if (ThumbsViewer.Items.Count <> 0) or (ThumbsViewer.SmartGroups.Count <> 0) then
  begin
    id := 0;
    nb := 0;
    nb10 := 0;
    i := 0;
    while (i < MovieList.Count) and (Terminated = False) do
    begin
      if(MovieList.Items[i]._thumb <> nil) or
        (MovieList.Items[i]._thumbError > 0) then
        Inc(nb);
      Inc(i);
    end;
    if (nb > 0) and (Terminated = False) then
    begin
      nb10 := Trunc(10*nb/MovieList.Count);
      PostMessage(MainWindow.Handle, CM_UpdateView, 0, nb10);
    end;

    while (nb < MovieList.Count) and (Terminated = False) do
    begin
      i := 0;
      InView := True;
      Movie := nil;
      ThumbsViewer.LockInView();
      while (i < ThumbsViewer.InView.Count) and
        ((Movie = nil) or (Movie._thumb <> nil) or (Movie._thumbError > 0)) do
      begin
        PtTmp := ThumbsViewer.InView.Points[i];
        if(PtTmp.X <> -1) then
          List := PSmartGroup(ThumbsViewer.SmartGroups.Items[PtTmp.X]).Items
        else
          List := ThumbsViewer.Items;
        Movie := MovieList.Items[List.Integers[PtTmp.Y]];
        Inc(i);
      end;
      ThumbsViewer.UnlockInView();

      if (Movie = nil) or (Movie._thumb <> nil) or (Movie._thumbError > 0) then
      begin
        InView := False;
        while (MovieList.Items[id]._thumb <> nil) or (MovieList.Items[id]._thumbError > 0) do
        begin
          Inc(id);
          if (id > MovieList.Count) then
            id := 0;
        end;
        Movie := MovieList.Items[id];
      end;

      Movie.Lock;
      MainWindow.ThumbsGetThumbnail(ThumbsViewer, Movie);
      Movie.Unlock;
      Inc(nb);

      if InView then
      begin
        nb10 := Trunc(10*nb/MovieList.Count);
        PostMessage(MainWindow.Handle, CM_UpdateView, 1, nb10)
      end
      else if(Trunc(10*nb/MovieList.Count) > nb10) then
      begin
        nb10 := Trunc(10*nb/MovieList.Count);
        PostMessage(MainWindow.Handle, CM_UpdateView, 0, nb10);
      end;
    end;
  end;
  if (not Terminated) then
    PostMessage(MainWindow.Handle, CM_UpdateView, 1, 10);
  MainWindow.ThreadDone := True;
end;

{-------------------------------------------------------------------------------
  ThumbsViewer
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerUpdateSelection(EnsureVisible: Boolean);
begin
  if (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil) and
      (ListView1.Selected.Data <> nil) then
  begin
    if (ListView1.Selected.Parent <> nil) then
    begin
      if (ListView1.Selected.Parent.Index < ThumbsViewer.SmartGroups.Count) and
        (ListView1.Selected.Index < PSmartGroup(ThumbsViewer.SmartGroups[
        ListView1.Selected.Parent.Index]).Items.Count) then
      begin
        ThumbsViewer.Selection.Clear;
        ThumbsViewer.Selection.Add(MovieList.IndexOf(ListView1.Selected.Data));
        ThumbsViewer.IdxItem := ListView1.Selected.Index;
        ThumbsViewer.IdxGrp := ListView1.Selected.Parent.Index;
        if EnsureVisible then
        begin
          ThumbsViewer.SetAtTop(ThumbsViewer.IdxGrp, -1);
          ThumbsViewer.SetInView(ThumbsViewer.IdxGrp, ThumbsViewer.IdxItem);
        end;
        ThumbsViewer.Invalidate;
      end;
    end
    else
    begin
      if (ListView1.Selected.Index < ThumbsViewer.Items.Count) then
      begin
        ThumbsViewer.Selection.Clear;
        ThumbsViewer.Selection.Add(MovieList.IndexOf(ListView1.Selected.Data));
        ThumbsViewer.IdxItem := ListView1.Selected.Index;
        ThumbsViewer.IdxGrp := -1;
        if EnsureVisible then
          ThumbsViewer.SetAtTop(ThumbsViewer.IdxGrp, ThumbsViewer.IdxItem);
        ThumbsViewer.Invalidate;
      end;
    end;
  end
  else
  begin
    ThumbsViewer.Selection.Clear;
    ThumbsViewer.Invalidate;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.CMUpdateView(var message: TMessage);
begin
  if message.WParam = 1 then
    ThumbsViewer.Invalidate;
  if (message.LParam >= 0) and (message.LParam <> ProgressBar1.Position) then
  begin
    ProgressBar1.Position := message.LParam;
    if ProgressBar1.Position = ProgressBar1.Max then
      ProgressBar1.Visible := False;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsGetThumbnail(Sender: TObject; Movie: TMovie);
var
  ThumbBmp, bmp: TBitmap;
  MS: TMemoryStream;
  ThumbSize: TPoint;
  newW, newH: Integer;
  sf: Integer;
  Ext: String;
  Path: String;
  ThumbGraphic: TGraphic;
  isLock: Boolean;

begin
  ThumbBmp := nil;
  bmp := nil;
  ThumbGraphic := nil;
  Path := '';
  isLock := False;

  if Movie.Picture = nil then
  begin
    if Movie.strPicture = '' then
      Path := strDirApp + strDirTemplates + strFileNoPoster
    else
    begin
      if FCatalogFile.CurrentFile <> '' then
        SetCurrentDir(ExtractFilePath(FCatalogFile.CurrentFile));
      Path := ExpandFileName(Movie.strPicture);
      if not FileExists(Path) then
        Path := strDirApp + strDirTemplates + strFileNoFileFound
    end;
    Ext := LowerCase(ExtractFileExt(ExtractFileName(Path)))
  end else
  begin
    Ext := Movie.strPicture;
  end;

  if (Ext = '.jpg') or (Ext = '.jpeg') then
    ThumbGraphic := ThumbJpeg
  else if (Ext = '.gif') then
    ThumbGraphic := ThumbGIF
  else if (Ext = '.png') then
    ThumbGraphic := ThumbPNG
  else
    Movie._thumbError := 1;

  if Movie._thumbError = 0 then
  begin
    if ThumbGraphic = ThumbJpeg then
    begin
      if (Movie.Picture = nil) then
        GetJPGSizeFromFile(Path, WI, HI)
      else
        GetJPGSize(Movie.Picture, WI, HI);
      if WI < 1 then
        WI := 1;
      if HI < 1 then
        HI := 1;
      sf := Trunc(Min(HI / 255, WI / 255));
      if sf < 0 then
        sf := 0;
      case sf of
        0..1: ThumbJpeg.Scale := jsFullSize;
        2..3: ThumbJpeg.Scale := jsHalf;
        4..7: ThumbJpeg.Scale := jsQuarter;
      else
        ThumbJpeg.Scale := jsEighth;
      end;
    end;

    try
      if (Movie.Picture = nil) then
        ThumbGraphic.LoadFromFile(Path)
      else
      begin
        Movie.Picture.Seek(0, soBeginning);
        ThumbGraphic.LoadFromStream(Movie.Picture);
      end;
      bmp := TBitmap.Create;
      bmp.PixelFormat := pf24Bit;
      bmp.Width := ThumbGraphic.Width;
      bmp.Height := ThumbGraphic.Height;
      bmp.Canvas.Lock;
      isLock := True;
      bmp.Canvas.Draw(0, 0, ThumbGraphic);

      try
        ThumbBmp := TBitmap.Create;
        ThumbBmp.Canvas.Lock;
        ThumbSize := CalcThumbSize(bmp.Width, bmp.Height,
          ThumbSizeW, ThumbSizeH);
        newW := ThumbSize.X;
        newH := ThumbSize.Y;
        if newW <= 0 then
          newW := 1;
        if newH <= 0 then
          newH := 1;
        ThumbBmp.PixelFormat := pf24Bit;
        ThumbBmp.Width := newW;
        ThumbBmp.Height := newH;
        MakeThumbNail(bmp, ThumbBmp);
      except
        ThumbBmp.Canvas.UnLock;
        FreeAndNil(ThumbBmp);
        Movie._thumbError := 3;
      end;

      bmp.Canvas.UnLock;
      bmp.Free;
    except
      Movie._thumbError := 2;
      if (bmp <> nil) then
      begin
        if (isLock) then
          bmp.Canvas.Unlock;
        bmp.Free;
      end;
    end;

    if ThumbBmp <> nil then
    begin
      MS := TMemoryStream.Create;
      MS.Seek(0, soBeginning);

      try
        ThumbJpeg.Assign(ThumbBmp);
        ThumbJpeg.Compress;
        ThumbJpeg.SaveToStream(MS);
        Movie._thumb := MS;
        Movie._thumbWidth := ThumbBmp.Width;
        Movie._thumbHeight := ThumbBmp.Height;
      except
        MS.Free;
        Movie._thumbError := 4;
      end;

      ThumbBmp.Canvas.UnLock;
      ThumbBmp.Free;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.StartThumbThread;
begin
  if ThumbThr <> nil then
    Exit;
  ThreadDone := False;
  ThumbThr := ThumbThread.Create(ThumbsViewer, ListView1, MovieList);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.StopThumbThread;
begin
  if ThumbThr <> nil then
  begin
    ThumbThr.Terminate;
    ThumbThr.WaitFor;
    ThumbThr.Free;
    ThumbThr := nil;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ClearThumbs;
begin
  ThumbsViewer.ClearSmartGroups;
  ThumbsViewer.Items.Clear;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ClearThumbsPool;
var
  i: Integer;
  Thumb: PCacheItem;
begin
  for i := ThumbsPool.Count - 1 downto 0 do
  begin
    Thumb := ThumbsPool[i];
    if Thumb.Bmp <> nil then
      Thumb.Bmp.Free;
    Dispose(Thumb);
  end;
  ThumbsPool.Clear;
  PoolSize := 0;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerStop;
begin
  StopThumbThread;
  ClearThumbs;
  ClearThumbsPool;
  ThumbsViewer.Clear;
  ProgressBar1.Visible := False;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerStart;
var
  n, Grp, Idx: Integer;
  Item: TElTreeItem;
  Group: PSmartGroup;
  bSelect: Boolean;
begin
  ThumbsViewerStop;
  if ThumbsPanel.Visible then
  begin
    Group := nil;
    bSelect := (ListView1.SelectedCount = 1) and (ListView1.Selected <> nil);
    if (FGroupField <> -1) then
    begin
      Grp := -1;
      ThumbsViewer.Grouped := True;
      Item := ListView1.Items.GetFirstNode;
      while (Item <> nil) do
      begin
        if(Item.Data = nil) then
        begin
          Grp := ThumbsViewer.AddSmartGroup(Item.Text);
          Group := PSmartGroup(ThumbsViewer.SmartGroups.Items[Grp]);
          Group.Expanded := Item.Expanded;
        end else
        begin
          n := MovieList.IndexOf(Item.Data);
          if (n <> -1) then
          begin
            Idx := Group.Items.Add(n);
            if (bSelect and Item.Selected) then
            begin
              ThumbsViewer.Selection.Add(n);
              ThumbsViewer.IdxGrp := Grp;
              ThumbsViewer.IdxItem := Idx;
            end;
          end;
        end;
        Item := Item.GetNext;
      end;
    end
    else
    begin
      ThumbsViewer.Grouped := False;
      Item := ListView1.Items.GetFirstNode;
      while (Item <> nil) do
      begin
        if Item.Data <> nil then
        begin
          n := MovieList.IndexOf(Item.Data);
          if (n <> -1) then
          begin
            Idx := ThumbsViewer.Items.Add(n);
            if (bSelect and Item.Selected) then
            begin
              ThumbsViewer.Selection.Add(n);
              ThumbsViewer.IdxGrp := -1;
              ThumbsViewer.IdxItem := Idx;
            end;
          end;
        end;
        Item := Item.GetNext;
      end;
    end;
    
    if(ListView1.Items.Count > 0) then
    begin
      ThumbsViewer.CalcView(True);
      SetThumbSize((ThumbsSizer.Position shl 4) - 1, False);
      if (FGroupField <> -1) then
      begin
        ThumbsViewer.SetAtTop(ThumbsViewer.IdxGrp, -1);
        ThumbsViewer.SetInView(ThumbsViewer.IdxGrp, ThumbsViewer.IdxItem);
      end else
      begin
        ThumbsViewer.SetAtTop(-1, ThumbsViewer.IdxItem);
      end;
      ProgressBar1.Position := 0;
      ProgressBar1.Visible := True;
      if ThumbThr = nil then
      begin
        ThreadDone := False;
        ThumbThr := ThumbThread.Create(ThumbsViewer, ListView1, MovieList);
      end;
    end
    else
    begin
      ProgressBar1.Visible := False;
    end;
  end;
  MovieSelected;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.GenCellColors;
begin
  cHot := $00FDDE99;
  cgHotStart := $00FDF5E6;
  cGHotEnd := $00FDFBF6;
  cSelected := $00FDCE99;
  cGSelectedStart := $00FCEFC4;
  cGSelectedEnd := $00FDF8EF;
  cShadeSelect := $00F8F3EA;
  cDisabled := $00D9D9D9;
  cGDisabledStart := $00EAE9E9;
  cGDisabledEnd := $00FCFBFB;
  cShadeDisabled := $00F6F5F5;
  cGHeaderStart := $00F9F9F9;
  cGHeaderEnd := $00FEFEFE;
  cGHeaderHotStart := $00FFEDBD;
  cGHeaderHotEnd := $00FFF7E3;
  cGHeaderSelStart := $00FCEABA;
  cGHeaderSelEnd := $00FCF4E0;
  cBackground := clWindow;
  cLineHighLight := $00FEFBF6;
  CellBkgColor := clWindow;
  CellBrdColor[False, False] := cDisabled;
  CellBrdColor[False, True] := cDisabled;
  CellBrdColor[True, False] := $00B5B5B5;
  CellBrdColor[True, True] := cSelected;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ItemPaintBasic(Canvas: TCanvas; R: TRect; State: TsvItemState);
var
  C: TColor;
begin
  Canvas.Brush.Style := bsClear;
  if (State = svSelected) or (State = svHot) or (State = svHotSelected) then
  begin
    if (ThumbsViewer.Focused) and ((State = svSelected) or (State = svHotSelected)) then
    begin
      Canvas.Pen.Color:= cSelected;
      GradientFill(Canvas, R, cGSelectedStart, cGSelectedEnd, tgTopBottom);
    end
    else if (State = svHot) then
    begin
      Canvas.Pen.Color:= cHot;
      GradientFill(Canvas, R, cGHotStart, cGHotEnd, tgTopBottom);
    end else
    begin
      Canvas.Pen.Color:= cDisabled;
      GradientFill(Canvas, R, cGDisabledStart, cGDisabledEnd, tgTopBottom);
    end;
    Canvas.Rectangle(R);
    if (ThumbsViewer.Focused) then
      C:= cShadeSelect
    else
      C:= cShadeDisabled;
    Canvas.Pen.Color:= C;
    Canvas.MoveTo(R.Left + 1, R.Top + 2);
    Canvas.LineTo(R.Left + 1, R.Bottom - 2);
    Canvas.LineTo(R.Right - 2, R.Bottom - 2);
    Canvas.LineTo(R.Right - 2, R.Top + 1);
    Canvas.Pen.Style:= psSolid;
    Canvas.Pixels[R.Left, R.Top]:= C;
    Canvas.Pixels[R.Left, R.Bottom - 1]:= C;
    Canvas.Pixels[R.Right - 1, R.Top]:= C;
    Canvas.Pixels[R.Right - 1, R.Bottom -1]:= C;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.SetThumbSize(Value: Integer; UpdateTrackbar: Boolean);
var
  w, h: Integer;
begin
  case Value of
    31..63: CellJpeg.Scale := jsQuarter;
    64..127: CellJpeg.Scale := jsHalf;
    128..255: CellJpeg.Scale := jsFullSize;
  else
    CellJpeg.Scale := jsEighth;
  end;
  w := Trunc(Value*0.75);
  h := Value;
  w := w + 10;
  if CellStyle = 0 then
    h := h + 10
  else
    h := h + 30;
  HSX := (w - 70) shr 1; // Help for calculating rating...
  ThumbsViewer.CellWidth := w;
  ThumbsViewer.CellHeight := h;
  CellScale := Value;
  if UpdateTrackbar then
  begin
    ThumbsSizer.OnChange := nil;
    ThumbsSizer.Position := Trunc((CellScale + 1) shr 4);
    ThumbsSizer.OnChange := ThumbsSizerChange;
  end;
  ThumbsViewer.CalcView(False);
  ThumbsViewer.Invalidate;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsSizerChange(Sender: TObject);
begin
  SetThumbSize((ThumbsSizer.Position shl 4) - 1, False);
  if (ThumbsViewer.Grouped) then
  begin
    ThumbsViewer.SetAtTop(ThumbsViewer.IdxGrp, -1);
    ThumbsViewer.SetInView(ThumbsViewer.IdxGrp, ThumbsViewer.IdxItem);
  end
  else
    ThumbsViewer.SetAtTop(ThumbsViewer.IdxGrp, ThumbsViewer.IdxItem);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TMainWindow.ThumbBmp(Idx: Integer): TBitmap;
var
  i, n, sf: Integer;
  p: PCacheItem;
  Movie: TMovie;
  Bmp, tmp: TBitmap;
  pt: TPoint;
  Oldest: TDateTime;
begin
  Result := nil;
  // if we have thumbs, see if we can find it...
  if ThumbsPool.Count > 0 then
  begin
    i := ThumbsPool.Count - 1;
    while (i >= 0) and (PCacheItem(ThumbsPool[i]).Idx <> Idx) do
      i := i - 1;
    if i <> -1 then
    begin
      p := ThumbsPool[i];
      if (p.Idx = Idx) then
      begin
        if (p.Scale = CellScale) then
        begin
          p.Age := Now;
          Result := p.Bmp
        end
        else
        begin
          PoolSize := PoolSize - p.Size;
          p.Bmp.Free;
          Dispose(p);
          ThumbsPool.Delete(i);
        end;
      end;
    end;
  end;
  // if we dont have a thumb, make one...
  if Result = nil then
  begin
    Movie := MovieList.Items[Idx];
    if Movie._thumb <> nil then
    begin
      sf := Trunc(Min(Movie._thumbWidth / Trunc(CellScale*0.75), Movie._thumbHeight / CellScale));
      if sf < 0 then
        sf := 0;
      case sf of
        0..1: CellJPEG.Scale := jsFullSize;
        2..3: CellJPEG.Scale := jsHalf;
        4..7: CellJPEG.Scale := jsQuarter;
      else
        CellJPEG.Scale := jsEighth;
      end;
      Movie._thumb.Seek(0, soBeginning);
      CellJPEG.LoadFromStream(Movie._thumb);

      Bmp := TBitmap.Create;
      Bmp.PixelFormat := pf24bit;
      pt := CalcThumbSize(CellJPEG.Width, CellJPEG.Height, Trunc(CellScale*0.75),
        CellScale);
      if pt.x <> CellJPEG.Width then
      begin
        tmp := TBitmap.Create;
        tmp.PixelFormat := pf24bit;
        tmp.Width := CellJPEG.Width;
        tmp.Height := CellJPEG.Height;
        tmp.Canvas.Draw(0, 0, CellJPEG);
        Bmp.Width := pt.x;
        Bmp.Height := pt.y;
        if (Bmp.Width > 4) and (Bmp.Height > 4) then
          BiReSample(tmp, Bmp, False)
        else
          bmp.Canvas.StretchDraw(Rect(0, 0, pt.X, pt.Y), tmp);
        tmp.Free;
      end
      else
      begin
        Bmp.Width := CellJPEG.Width;
        Bmp.Height := CellJPEG.Height;
        Bmp.Canvas.Draw(0, 0, CellJPEG);
      end;

      // Purge thumbs if needed
      while (PoolSize > MaxPool) and (ThumbsPool.Count > 0) do
      begin
        Oldest := Now;
        n := 0;
        for i := 0 to ThumbsPool.Count - 1 do
        begin
          p := ThumbsPool[i];
          if p.Age <= Oldest then
          begin
            Oldest := p.Age;
            n := i;
          end;
        end;
        Assert(n >= 0);
        p := ThumbsPool[n];
        PoolSize := PoolSize - p.Size;
        p.Bmp.Free;
        Dispose(p);
        ThumbsPool.Delete(n);
      end;

      New(p);
      p.Idx := Idx;
      p.Size := Bmp.Width * Bmp.Height;
      p.Age := Now;
      p.Scale := CellScale;
      p.Bmp := Bmp;
      ThumbsPool.Add(p);
      PoolSize := PoolSize + p.Size;
      Result := p.Bmp;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerCellPaint(Sender: TObject; Canvas: TCanvas;
  Cell: TRect; IdxA, Idx: Integer; State: TsvItemState);
var
  X, Y: Integer;
  F, S: Boolean;
  R: TRect;
  TW, TH: Integer;
  Txt: string;
  Movie: TMovie;
  pt: TPoint;
  LockOk, ThumbOk: Boolean;
begin
  Movie := MovieList.Items[Idx];
  LockOk := Movie.LockWait(0);
  if (LockOk) then
    ThumbOk := (Movie._thumb <> nil) and (Movie._thumbError = 0)
  else
    ThumbOk := False;

  ItemPaintBasic(Canvas, Cell, State);
  F := ThumbsViewer.Focused;
  S := (State = svSelected) or (State = svHotSelected);

  if ThumbOk then
    pt := CalcThumbSize(Movie._thumbWidth, Movie._thumbHeight, Trunc(CellScale*0.75),
      CellScale)
  else
    pt := CalcThumbSize(Trunc(CellScale*0.75), CellScale, Trunc(CellScale*0.75),
      CellScale);

  TW := pt.X;
  TH := pt.Y;
  X := Cell.Left + ((Cell.Right - (Cell.Left + TW)) shr 1);
  Y := Cell.Top + ((Cell.Bottom - (Cell.Top + TH)) shr 1);
  Y := Y - 10;
  R.Left := X;
  R.Top := Y;
  R.Right := X + TW;
  R.Bottom := Y + TH;

  if ThumbOk then
    Canvas.Draw(X, Y, ThumbBmp(idx))
  else if (Movie = nil) then
  begin
    txt := 'Group';
    DrawText(Canvas.Handle, PChar(txt), Length(txt), R, DT_END_ELLIPSIS or
    DT_SINGLELINE or DT_NOPREFIX or DT_CENTER or DT_VCENTER);
  end
  else if (Movie._thumbError = 0) then
  begin
    txt := 'Loading...';
    DrawText(Canvas.Handle, PChar(txt), Length(txt), R, DT_END_ELLIPSIS or
    DT_SINGLELINE or DT_NOPREFIX or DT_CENTER or DT_VCENTER);
  end
  else
  begin
    R.Top := R.Top - 10;
    R.Bottom := R.Bottom - 10;

    txt := 'No Poster';
    DrawText(Canvas.Handle, PChar(txt), Length(txt), R, DT_END_ELLIPSIS or
    DT_SINGLELINE or DT_NOPREFIX or DT_CENTER or DT_VCENTER);

    R.Top := R.Top + 20;
    R.Bottom := R.Bottom + 20;

    txt := 'Error '+ IntToStr(Movie._thumbError);
    DrawText(Canvas.Handle, PChar(txt), Length(txt), R, DT_END_ELLIPSIS or
    DT_SINGLELINE or DT_NOPREFIX or DT_CENTER or DT_VCENTER);

    R.Top := R.Top - 10;
    R.Bottom := R.Bottom - 10;
  end;

  Canvas.Pen.Color := CellBrdColor[F, S];
  InflateRect(R, 2, 2);
  Canvas.Rectangle(R);
  Canvas.Pen.Color := clWhite;
  InflateRect(R, -1, -1);
  Canvas.Rectangle(R);
  R := Cell;
  R.Top := R.Bottom - 20;
  R.Left := R.Left + 2;
  R.Right := R.Right - 2;
  txt := Movie.GetFormattedTitle;
  DrawText(Canvas.Handle, PChar(txt), Length(txt), R, DT_END_ELLIPSIS or
    DT_SINGLELINE or DT_NOPREFIX or DT_CENTER or DT_VCENTER);
  if (LockOk) then
    Movie.Unlock
  else
    PostMessage(MainWindow.Handle, CM_UpdateView, 1, -1) // Need to be refresh again later !
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerSelecting(Sender: TObject; Count: Integer);
var
  TreeItem : TElTreeItem;
  IdxGrp, IdxItem : Integer;
begin
  IdxGrp := ThumbsViewer.IdxGrp;
  IdxItem := ThumbsViewer.IdxItem;
  if (IdxGrp <> -1) and (IdxItem = -1) then
  begin
    ListView1.Items[ThumbsViewer.GetAbsoluteIdx(IdxGrp, 0) + IdxGrp].Expanded :=
      PSmartGroup(ThumbsViewer.SmartGroups.Items[IdxGrp]).Expanded;
  end
  else if (ThumbsViewer.Selection.Count = 1) then
  begin
    if IdxItem <> -1 then
    begin
      if (ListView1.Items[ThumbsViewer.GetAbsoluteIdx(IdxGrp,
        IdxItem) + IdxGrp + 1] <> nil) then
      begin
        TreeItem := ListView1.Items[ThumbsViewer.GetAbsoluteIdx(IdxGrp,
          IdxItem) + IdxGrp + 1];
        if (ListView1.Selected <> TreeItem) or (ListView1.SelectedCount <> 1) then
        begin
          ListView1.DeselectAll;
          ListView1.ItemFocused := nil;
          ListView1.ItemFocused := TreeItem;
          ListView1.Selected := ListView1.GetNextSelected(nil);
          ListView1EnsureVisiblePlus;
          ListView1AfterSelectionChange(ThumbsViewer);
        end;
      end;
    end;
  end
  else
  begin
    ListView1.DeselectAll;
    ListView1AfterSelectionChange(ThumbsViewer);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
begin
  if (ThumbsViewer.CanFocus) then
    ThumbsViewer.SetFocus;
  ListView1UpdateItem;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerDividerPaint(Sender: TObject;
  Canvas: TCanvas; Cell: TRect; Group: PSmartGroup; State: TsvItemState);
var
  R: TRect;
  txt: string;
  x: Integer;
  cFontColorSave: TColor;
begin
  cFontColorSave := Canvas.Font.Color;
  R := Cell;
  ItemPaintBasic(Canvas, Cell, State);
  if (Group.Expanded) then
  begin
    ListView1.MinusPicture.Transparent := True;
    Canvas.Draw(R.Left + 1, R.Top, ListView1.MinusPicture);
  end else
  begin
    ListView1.PlusPicture.Transparent := True;
    Canvas.Draw(R.Left + 1, R.Top, ListView1.PlusPicture);
  end;
  Canvas.Pen.Width := 1;
  Canvas.Brush.Style := bsClear;
  //Canvas.Font.Color := $00905422;
  R.Left := R.Left + 22;
  txt := Group.Caption;
  DrawText(Canvas.Handle, PChar(txt), Length(txt), R,
    DT_END_ELLIPSIS or DT_SINGLELINE or DT_NOPREFIX or DT_VCENTER);
  R.Top := R.Top + 1;
  R.Bottom := R.Bottom + 1;
  x := Canvas.TextWidth(txt);
  R.Left := R.Left + x;
  Canvas.Pen.Color := $00F0F0F0;
  Canvas.MoveTo(x + 36, R.Top + 7);
  Canvas.LineTo(R.Right - 8, R.Top + 7);
  Canvas.Font.Color := cFontColorSave;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerResize(Sender: TObject);
begin
  if (ThumbsViewer.Grouped) then
  begin
    ThumbsViewer.SetAtTop(ThumbsViewer.IdxGrp, -1);
    ThumbsViewer.SetInView(ThumbsViewer.IdxGrp, ThumbsViewer.IdxItem);
  end
  else
    ThumbsViewer.SetAtTop(ThumbsViewer.IdxGrp, ThumbsViewer.IdxItem);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerHeaderPaint(Sender: TObject;
  Canvas: TCanvas; Header: TRect; Offset, Active: Integer;
  State: TsvItemState; Columns: array of Integer);
begin
  //
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerHeaderClick(Sender: TObject;
  Column: Integer);
begin
  //
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1ItemCollapse(Sender: TObject;
  Item: TElTreeItem);
begin
  if (Item.Index < ThumbsViewer.SmartGroups.Count) and
    (PSmartGroup(ThumbsViewer.SmartGroups.Items[Item.Index]).Expanded) then
  begin
    PSmartGroup(ThumbsViewer.SmartGroups.Items[Item.Index]).Expanded := False;
    if (not FullExpandOrCollapse) then
    begin
      ThumbsViewer.CalcView(True);
      ThumbsViewer.Invalidate;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1ItemExpand(Sender: TObject;
  Item: TElTreeItem);
begin
  if (Item.Index < ThumbsViewer.SmartGroups.Count) and
    (not PSmartGroup(ThumbsViewer.SmartGroups.Items[Item.Index]).Expanded) then
  begin
    PSmartGroup(ThumbsViewer.SmartGroups.Items[Item.Index]).Expanded := True;
    if (not FullExpandOrCollapse) then
    begin
      ThumbsViewer.CalcView(True);
      ThumbsViewer.Invalidate;
    end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if(Button = mbRight) then
  begin
    PopupMovieListPopup(ThumbsViewer);
    PopupMovieList.Popup(ThumbsViewer.ClientOrigin.X + X, ThumbsViewer.ClientOrigin.Y + Y);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerDblClick(Sender: TObject);
begin
  if (ActionStretchList.Checked = True) then
    ActionStretchListExecute(ThumbsViewer);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ThumbsViewerKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key <> Ord('A')) then
    ListView1KeyUp(ThumbsViewer, Key, Shift);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TMainWindow.ListView1ItemColorPick(Sender: TObject;
  Item: TElTreeItem);
{var
  i: Integer;
  Found: Boolean;}
begin
  {if (Item = nil) or (Item.Data = nil) then
    Exit;
  if (True) then // if CheckBoxColor or LineColor
  begin
    Found := False;
    for i := 0 to High(DefaultColors) do
      if DefaultColors[i] = Item.CheckBoxColor then
      begin
        Found := True;
        Break;
      end;
    if not Found then
      i := High(DefaultColors);
    i := (i + 1) mod (High(DefaultColors)+1);

    if (True) then // CheckBoxColor
      Item.CheckBoxColor := DefaultColors[i];
    
    if (True) then // LineColor
    begin
      Item.RowBkColor := DefaultColors[i];
      Item.BkColor := Item.RowBkColor;
    end;

    //Item.Hint := 'Color ' + IntToStr(i);
    //SetCapture(Self.Handle); ReleaseCapture; // Update Hint value
  end;}
end;

end.
