unit Translate;

{
[Translate] [1.1]
Delphi 2005
December 2005

LICENSE

The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
"http://www.mozilla.org/MPL/"

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.

The Original Code is "[translate.pas]".

The Initial Developer of the Original Code is Martin Holmes (Victoria,
BC, Canada, "http://www.mholmes.com/"). Copyright (C) 2005 Martin Holmes 
and the University of Victoria Computing and Media Centre. The code was 
co-developed for university and personal projects, and rights are shared
by Martin Holmes and the University of Victoria. All Rights Reserved.
}

{
Written by Martin Holmes, Fall 2005.

This form, along with the associated mdhTranslate library, provides introspection
for the application, exposing its strings (in the form of control captions, hints
etc.) for translation into another language. It uses a TVirtualTreeView as its
core control.

Dependencies:

VirtualTreeView (Mike Lischke)
TntUnicodeControls (Troy Wolbrink)
FormState for saving size/position etc.

}

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, TntForms, StdCtrls, TntStdCtrls, ComCtrls, TntComCtrls, TntMenus,
  TypInfo, VirtualTrees, VersionInfo, mdhTranslate,
  ExtCtrls, TntExtCtrls, TntDialogs, AppEvnts, Buttons, TntButtons, FormState,
  StdActns, ActnList, TntActnList, ImgList, Menus, ToolWin,
  TntSysUtils, Clipbrd, IconsIncluder;


//This is the record used to point to the full class

type
  TufrmTranslate = class(TTntForm)
    aeTranslate: TApplicationEvents;
    utbrTranslate: TTntToolBar;
    umnuTranslate: TTntMainMenu;
    upnEditing: TTntPanel;
    umsgItemTextModified: TTntLabel;
    ugbCurrentItem: TTntGroupBox;
    ustItemHint: TTntStaticText;
    ustItemCaption: TTntStaticText;
    ubnCancel: TTntBitBtn;
    umCaption: TTntMemo;
    ubnSaveChanges: TTntBitBtn;
    uspTranslate: TTntSplitter;
    upnVSTTranslate: TTntPanel;
    vstTranslate: TVirtualStringTree;
    umnFile: TTntMenuItem;
    umnEdit: TTntMenuItem;
    umnUndo: TTntMenuItem;
    N1: TTntMenuItem;
    mnuCut: TTntMenuItem;
    mnuCopy: TTntMenuItem;
    mnuPaste: TTntMenuItem;
    mnuDelete: TTntMenuItem;
    N2: TTntMenuItem;
    mnuSelectAll: TTntMenuItem;
    utbSep1: TTntToolButton;
    utbUndo: TTntToolButton;
    utbCut: TTntToolButton;
    utbCopy: TTntToolButton;
    utbPaste: TTntToolButton;
    utbDelete: TTntToolButton;
    utbSelectAll: TTntToolButton;
    umsgSaveInterfaceChanges: TTntLabel;
    udlgSaveTranslation: TTntSaveDialog;
    umTitle: TTntMemo;
    ustItemTitle: TTntStaticText;
    ualTranslate: TTntActionList;
    aNewTranslation: TTntAction;
    aOpenTranslation: TTntAction;
    aSaveTranslation: TTntAction;
    aSaveAsTranslation: TTntAction;
    udlgLoadTranslation: TTntOpenDialog;
    utbNewTranslation: TTntToolButton;
    utnOpenTranslation: TTntToolButton;
    utbSaveTranslation: TTntToolButton;
    utbSaveAsTranslation: TTntToolButton;
    umnNewTranslation: TTntMenuItem;
    umnOpenTranslation: TTntMenuItem;
    umnSaveTranslation: TTntMenuItem;
    umnSaveAsTranslation: TTntMenuItem;
    umsgUnableToReadTranslationFile: TTntStaticText;
    uedSearchString: TTntEdit;
    ubnFind: TTntBitBtn;
    ubnFindNext: TTntBitBtn;
    aFindNext: TTntAction;
    aFind: TTntAction;
    umHint: TTntMemo;
    aEditUndo: TTntAction;
    aEditCut: TTntAction;
    aEditCopy: TTntAction;
    aEditPaste: TTntAction;
    aEditDelete: TTntAction;
    aEditSelectAll: TTntAction;
    aSaveItemChanges: TTntAction;
    aCancelChanges: TTntAction;
    aCloseWindow: TTntAction;
    utbSep2: TTntToolButton;
    utbClose: TTntToolButton;
    N3: TTntMenuItem;
    umnCloseWindow: TTntMenuItem;
    procedure aCloseWindowExecute(Sender: TObject);
    procedure TntFormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure aCancelChangesExecute(Sender: TObject);
    procedure aSaveItemChangesExecute(Sender: TObject);
    procedure aEditSelectAllExecute(Sender: TObject);
    procedure aEditDeleteExecute(Sender: TObject);
    procedure aEditPasteExecute(Sender: TObject);
    procedure aEditCopyExecute(Sender: TObject);
    procedure aEditCutExecute(Sender: TObject);
    procedure aEditUndoExecute(Sender: TObject);
    procedure aFindNextExecute(Sender: TObject);
    procedure uedSearchStringChange(Sender: TObject);
    procedure aFindExecute(Sender: TObject);
    procedure aSaveAsTranslationExecute(Sender: TObject);
    procedure aSaveTranslationExecute(Sender: TObject);
    procedure aOpenTranslationExecute(Sender: TObject);
    procedure aNewTranslationExecute(Sender: TObject);
    procedure upnEditingDblClick(Sender: TObject);
    procedure umHintChange(Sender: TObject);
    procedure aeTranslateIdle(Sender: TObject; var Done: Boolean);
    procedure vstTranslateFocusChanging(Sender: TBaseVirtualTree; OldNode,
      NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex;
      var Allowed: Boolean);
    procedure vstTranslateFocusChanged(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex);
    procedure TntFormShow(Sender: TObject);
    procedure TntFormDestroy(Sender: TObject);
    procedure TntFormCreate(Sender: TObject);
    procedure vstTranslateGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
    procedure vstTranslateGetNodeDataSize(Sender: TBaseVirtualTree;
      var NodeDataSize: Integer);
  private
    ComponentPath: string;
    FVersionInfo: TAppVersionInfo;
    NodeDataModified: Boolean;
    FFormStateSaver: TFormStateSaver;
    procedure DisplayNode(NodeToDisplay: PVirtualNode);
    procedure StashNode(NodeToStash: PVirtualNode);
    function Save: Boolean;
    function SaveChanges: Boolean;
    function SaveAs: Boolean;
    procedure ClearTextBoxes;
    { Private declarations }
  public
    AppRef: TAppReflection;
    CurrFileName: WideString;
    Modified: Boolean;

    { Public declarations }
  end;

var
  ufrmTranslate: TufrmTranslate;

implementation

{$R *.dfm}

procedure TufrmTranslate.vstTranslateGetNodeDataSize(Sender: TBaseVirtualTree;
  var NodeDataSize: Integer);
begin
  NodeDataSize := SizeOf(rComponentReflection);
end;

procedure TufrmTranslate.vstTranslateGetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: WideString);
var
NodeRec: ^rComponentReflection;

begin
  NodeRec := Sender.GetNodeData(Node);
  if NodeRec.CompRef = nil then
    CellText := 'nil'
  else
    CellText := NodeRec.CompRef.Name;
end;

procedure TufrmTranslate.TntFormCreate(Sender: TObject);
var
TranslationDir: WideString;

begin
  Icon := Application.Icon;
  FVersionInfo := TAppVersionInfo.Create;
  CurrFileName := '';
  Modified := False;
  TranslationDir := WideExtractFilePath(Application.ExeName) + 'translations\';
  udlgLoadTranslation.InitialDir := TranslationDir;
  udlgSaveTranslation.InitialDir := TranslationDir;
end;

procedure TufrmTranslate.TntFormDestroy(Sender: TObject);
begin
  AppRef.Free;
  FreeAndNil(FVersionInfo);
  FreeAndNil(FFormStateSaver);
end;

procedure TufrmTranslate.TntFormShow(Sender: TObject);
begin
  FFormStateSaver := TFormStateSaver.Create(Self, True, True, True, False, False, False, False, False, True);
  AppRef := TAppReflection.Create;
  AppRef.VST := vstTranslate;
  AppRef.WriteToVST;
  ClearTextBoxes;
end;

procedure TufrmTranslate.vstTranslateFocusChanged(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex);
begin
if Node <> nil then
    DisplayNode(Node);
end;

procedure TufrmTranslate.DisplayNode(NodeToDisplay: PVirtualNode);
var
NodeRec: ^rComponentReflection;

begin
  if NodeToDisplay <> nil then
    begin
      NodeRec := vstTranslate.GetNodeData(NodeToDisplay);
      with TComponentReflection(NodeRec.CompRef) do
        begin
          umHint.Text := Hint;
          umCaption.Text := Caption;
          umTitle.Text := Title;
          umHint.Enabled := HasHint;
          umCaption.Enabled := HasCaption;
          umTitle.Enabled := HasTitle;
          ustItemHint.Enabled := HasHint;
          ustItemCaption.Enabled := HasCaption;
          ustItemTitle.Enabled := HasTitle;
        end;
    end;
  NodeDataModified := False;
end;

procedure TufrmTranslate.StashNode(NodeToStash: PVirtualNode);
var
NodeRec: ^rComponentReflection;

begin
  if NodeToStash <> nil then
    begin
      NodeRec := vstTranslate.GetNodeData(NodeToStash);
      with TComponentReflection(NodeRec.CompRef) do
        begin
          Hint := umHint.Text;
          Caption := umCaption.Text;
          Title := umTitle.Text;
          Modified := True;
        end;
    end;
  NodeDataModified := False;
end;

procedure TufrmTranslate.vstTranslateFocusChanging(Sender: TBaseVirtualTree;
  OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex;
  var Allowed: Boolean);
begin
  if NodeDataModified = True then
    begin
      WideShowMessage(umsgItemTextModified.Caption);
      Allowed := False;
    end;
end;

procedure TufrmTranslate.aeTranslateIdle(Sender: TObject;
  var Done: Boolean);
var
IsEdit: Boolean;
HasSelection: Boolean;
HasText: Boolean;
boolCanUndo: Boolean;

begin
  ubnSaveChanges.Enabled := NodeDataModified;
  ubnCancel.Enabled := NodeDataModified;
  aFind.Enabled := (Length(uedSearchString.Text) > 0) and not NodeDataModified;
  if NodeDataModified then
    aFindNext.Enabled := False;

  IsEdit := ((ActiveControl is TTntEdit) or
            (ActiveControl is TTntMemo));
  if IsEdit then
    begin
      if (ActiveControl is TTntEdit) then
        with (ActiveControl as TTntEdit) do
          begin
            HasSelection := (SelLength > 0);
            boolCanUndo := CanUndo;
            HasText := (Length(Text) > 0);
          end;
      if (ActiveControl is TTntMemo) then
        with (ActiveControl as TTntMemo) do
          begin
            HasSelection := (SelLength > 0);
            boolCanUndo := CanUndo;
            HasText := (Length(Text) > 0);
          end;
      aEditUndo.Enabled := boolCanUndo;
      aEditCut.Enabled := HasSelection;
      aEditCopy.Enabled := HasSelection;
      aEditPaste.Enabled := Clipboard.HasFormat(CF_TEXT);
      aEditDelete.Enabled := HasSelection;
      aEditSelectAll.Enabled := HasText;
    end
  else
    begin
      aEditUndo.Enabled := False;
      aEditCut.Enabled := False;
      aEditCopy.Enabled := False;
      aEditPaste.Enabled := False;
      aEditDelete.Enabled := False;
      aEditSelectAll.Enabled := False;
    end;
end;

procedure TufrmTranslate.umHintChange(Sender: TObject);
begin
  NodeDataModified := True;
end;

procedure TufrmTranslate.upnEditingDblClick(Sender: TObject);
begin
//  AppRef.WriteToGUI;
//  SetWideStrProp(TObject(umnFile), 'caption', 'Test');
end;

function TufrmTranslate.SaveChanges: Boolean;
var
Response: integer;

begin
  Result := False;//default
  Response := WideMessageDlg(umsgSaveInterfaceChanges.Caption, mtConfirmation, mbYesNoCancel, 0);
  case Response of
    mrYes: Result := Save;
    mrNo: Result := True;
    mrCancel: Result := False;
  end;
end;

function TufrmTranslate.SaveAs: Boolean;
begin
  Result := False; //default
  if udlgSaveTranslation.Execute then
    begin
      CurrFileName := udlgSaveTranslation.FileName;
      try
        AppRef.WriteToXMLFile(CurrFileName);
        Result := True;
        Modified := False;
      except
      end;
    end;
end;

function TufrmTranslate.Save: Boolean;
begin
  Result := False;//default
  if FileExists(CurrFileName) then
    begin
      try
        AppRef.WriteToXMLFile(CurrFileName);
        Modified := False;
        Result := True;
      except
      end;
    end
  else
    Result := SaveAs;
end;

procedure TufrmTranslate.aNewTranslationExecute(Sender: TObject);
begin
  if not Modified or SaveChanges then
    begin
      CurrFileName := '';
//Just destroy and recreate the AppRef object, and it will be refreshed
//from the GUI
      AppRef.Free;
      AppRef := TAppReflection.Create;
      AppRef.VST := vstTranslate;
      AppRef.WriteToVST;
      ClearTextBoxes;
      Modified := False;
    end;
end;

procedure TufrmTranslate.aOpenTranslationExecute(Sender: TObject);
begin
  if not Modified or SaveChanges then
    begin
      if udlgLoadTranslation.Execute then
        begin
          try
            AppRef.ReadFromXMLFile(udlgLoadTranslation.FileName);
          except
            WideMessageDlg(umsgUnableToReadTranslationFile.Caption,
                            mtWarning, [mbOK], 0);
            Exit;
          end;
          CurrFileName := udlgLoadTranslation.FileName;
          AppRef.WriteToVST;
          ClearTextBoxes;
          Modified := False;
        end;
    end;
end;

procedure TufrmTranslate.aSaveTranslationExecute(Sender: TObject);
begin
  Save;
end;

procedure TufrmTranslate.aSaveAsTranslationExecute(Sender: TObject);
begin
  if FileExists(CurrFileName) then
    udlgSaveTranslation.FileName := CurrFileName;
  if udlgSaveTranslation.Execute then
    begin
      try
        AppRef.WriteToXMLFile(udlgSaveTranslation.FileName);
      except
        Exit;
      end;
      CurrFileName := udlgSaveTranslation.FileName;
      Modified := False;
    end;
end;

procedure TufrmTranslate.ClearTextBoxes;
begin
  umHint.Text := '';
  umCaption.Text := '';
  umTitle.Text := '';
  umHint.Enabled := False;
  ustItemHint.Enabled := False;
  umCaption.Enabled := False;
  ustItemCaption.Enabled := False;
  umTitle.Enabled := False;
  ustItemTitle.Enabled := False;
  NodeDataModified := False;
end;

procedure TufrmTranslate.aFindExecute(Sender: TObject);
var
CompRef: TComponentReflection;

begin
  CompRef := AppRef.Find(uedSearchString.Text);
  if CompRef <> nil then
    begin
      vstTranslate.FocusedNode := CompRef.PVSTNode;
      vstTranslate.Selected[CompRef.PVSTNode] := True;
      aFindNext.Enabled := True;
    end;
end;

procedure TufrmTranslate.uedSearchStringChange(Sender: TObject);
begin
  aFindNext.Enabled := False;
end;

procedure TufrmTranslate.aFindNextExecute(Sender: TObject);
var
CompRef: TComponentReflection;

begin
  CompRef := AppRef.FindNext(uedSearchString.Text);
  if CompRef <> nil then
    begin
      vstTranslate.FocusedNode := CompRef.PVSTNode;
      vstTranslate.Selected[CompRef.PVSTNode] := True;
    end;
end;

procedure TufrmTranslate.aEditUndoExecute(Sender: TObject);
begin
  if ActiveControl is TTntEdit then
    (ActiveControl as TTntEdit).Perform(EM_UNDO, 0, 0);
  if ActiveControl is TTntMemo then
    (ActiveControl as TTntMemo).Perform(EM_UNDO, 0, 0);
end;

procedure TufrmTranslate.aEditCutExecute(Sender: TObject);
begin
  if ActiveControl is TTntEdit then
    (ActiveControl as TTntEdit).CutToClipboard;
  if ActiveControl is TTntMemo then
    (ActiveControl as TTntMemo).CutToClipboard;
end;

procedure TufrmTranslate.aEditCopyExecute(Sender: TObject);
begin
  if ActiveControl is TTntEdit then
    (ActiveControl as TTntEdit).CopyToClipboard;
  if ActiveControl is TTntMemo then
    (ActiveControl as TTntMemo).CopyToClipboard;
end;

procedure TufrmTranslate.aEditPasteExecute(Sender: TObject);
begin
  if ActiveControl is TTntEdit then
    (ActiveControl as TTntEdit).PasteFromClipboard;
  if ActiveControl is TTntMemo then
    (ActiveControl as TTntMemo).PasteFromClipboard;
end;

procedure TufrmTranslate.aEditDeleteExecute(Sender: TObject);
begin
  if ActiveControl is TTntEdit then
    (ActiveControl as TTntEdit).SelText := '';
  if ActiveControl is TTntMemo then
    (ActiveControl as TTntMemo).SelText := '';
end;

procedure TufrmTranslate.aEditSelectAllExecute(Sender: TObject);
begin
  if ActiveControl is TTntEdit then
    (ActiveControl as TTntEdit).SelectAll;
  if ActiveControl is TTntMemo then
    (ActiveControl as TTntMemo).SelectAll;
end;

procedure TufrmTranslate.aSaveItemChangesExecute(Sender: TObject);
begin
  StashNode(vstTranslate.FocusedNode);
end;

procedure TufrmTranslate.aCancelChangesExecute(Sender: TObject);
begin
  DisplayNode(vstTranslate.FocusedNode);
end;

procedure TufrmTranslate.TntFormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  CanClose := (not Modified) or (SaveChanges);
end;

procedure TufrmTranslate.aCloseWindowExecute(Sender: TObject);
begin
  Close;
end;

end.
