
{*****************************************************************************}
{                                                                             }
{    Tnt Delphi Unicode Controls                                              }
{      http://www.tntware.com/delphicontrols/unicode/                         }
{        Version: 2.3.0                                                       }
{                                                                             }
{    Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com)       }
{                                                                             }
{*****************************************************************************}

unit TntWideStringProperty_Design;

{$INCLUDE ..\Source\TntCompilers.inc}

interface

{*****************************************************}
{  TWideCharProperty-editor implemented by Maël Hörz  }
{*****************************************************}

{$IFDEF COMPILER_9_UP}
  {$MESSAGE FATAL 'The Object Inspector in Delphi 9 is already Unicode enabled.'}
{$ENDIF}

uses
  Classes, Messages, Windows, Graphics, TypInfo, TntDesignEditors_Design,
  DesignIntf, DesignEditors, VCLEditors;

type
  TWideStringProperty = class(TPropertyEditor, ICustomPropertyDrawing)
  private
    FActivateWithoutGetValue: Boolean;
    FPropList: PInstPropList;
  protected
    procedure SetPropEntry(Index: Integer; AInstance: TPersistent; APropInfo: PPropInfo); override;
    function GetWideStrValueAt(Index: Integer): WideString; dynamic;
    function GetWideStrValue: WideString;
    procedure SetWideStrValue(const Value: WideString); dynamic;
    function GetWideVisualValue: WideString;
  public
    constructor Create(const ADesigner: ITntDesigner; APropCount: Integer); override;
    destructor Destroy; override;
    procedure Activate; override;
    procedure PropDrawName(ACanvas: TCanvas; const ARect: TRect; ASelected: Boolean);
    procedure PropDrawValue(ACanvas: TCanvas; const ARect: TRect; ASelected: Boolean);
    function AllEqual: Boolean; override;
    function GetEditLimit: Integer; override;
    function GetValue: AnsiString; override;
    procedure SetValue(const Value: AnsiString); override;
    {$IFDEF MULTI_LINE_STRING_EDITOR}
    function GetAttributes: TPropertyAttributes; override;
    procedure Edit; override;
    {$ENDIF}
  end;

  TWideCaptionProperty = class(TWideStringProperty)
  public
    function GetAttributes: TPropertyAttributes; override;
  end;

  TWideCharProperty = class(TWideStringProperty)
  protected
    {$IFDEF COMPILER_7_UP}
    function GetIsDefault: Boolean; override;
    {$ENDIF}
    function GetWideStrValueAt(Index: Integer): WideString; override;
    procedure SetWideStrValue(const Value: WideString); override;
  public
    function GetAttributes: TPropertyAttributes; override;
    function GetEditLimit: Integer; override;
  end;

procedure Register;

implementation

uses
  Controls, Forms, SysUtils, StdCtrls, TntGraphics, TntControls,
  TntSysUtils, TntSystem, Consts,
  RTLConsts;

procedure Register;
begin
  RegisterPropertyEditor(TypeInfo(WideString), nil, '', TWideStringProperty);
  RegisterPropertyEditor(TypeInfo(TWideCaption), nil, '', TWideCaptionProperty);
  RegisterPropertyEditor(TypeInfo(WideChar), nil, '', TWideCharProperty);
end;

function GetOIInspListBox: TWinControl;
var
  ObjectInspectorForm: TCustomForm;
  Comp: TComponent;
begin
  Result := nil;
  ObjectInspectorForm := GetObjectInspectorForm;
  if ObjectInspectorForm <> nil then begin
    Comp := ObjectInspectorForm.FindComponent('PropList');
    if Comp is TWinControl then
      Result := TWinControl(Comp);
  end;
end;

function GetOIPropInspEdit: TCustomEdit{TNT-ALLOW TCustomEdit};
var
  OIInspListBox: TWinControl;
  Comp: TComponent;
begin
  Result := nil;
  OIInspListBox := GetOIInspListBox;
  if OIInspListBox <> nil then begin
    Comp := OIInspListBox.FindComponent('EditControl');
    if Comp is TCustomEdit{TNT-ALLOW TCustomEdit} then
      Result := TCustomEdit{TNT-ALLOW TCustomEdit}(Comp);
  end;
end;
//------------------------------

type TAccessWinControl = class(TWinControl);

{ TWideStringProperty }

var
  WideStringPropertyCount: Integer = 0;

constructor TWideStringProperty.Create(const ADesigner: ITntDesigner; APropCount: Integer);
begin
  inherited;
  Inc(WideStringPropertyCount);
  GetMem(FPropList, APropCount * SizeOf(TInstProp));
end;

procedure ConvertObjectInspectorBackToANSI;
var
  Edit: TCustomEdit{TNT-ALLOW TCustomEdit};
begin
  if (Win32PlatformIsUnicode) then begin
    Edit := GetOIPropInspEdit;
    if Assigned(Edit)
    and IsWindowUnicode(Edit.Handle) then
      TAccessWinControl(Edit).RecreateWnd;
  end;
end;

destructor TWideStringProperty.Destroy;
begin
  Dec(WideStringPropertyCount);
  if (WideStringPropertyCount = 0) then
    ConvertObjectInspectorBackToANSI;
  if FPropList <> nil then
    FreeMem(FPropList, PropCount * SizeOf(TInstProp));
  inherited;
end;

{$IFDEF DELPHI_7} // verified against VCL source in Delphi 7
type
  THackPropertyEditor = class
    FDesigner: IDesigner;
    FPropList: PInstPropList;
  end;
{$ENDIF}

procedure TWideStringProperty.Activate;
var
  Edit: TCustomEdit{TNT-ALLOW TCustomEdit};
begin
  FActivateWithoutGetValue := True;
  if (Win32PlatformIsUnicode) then begin
    Edit := GetOIPropInspEdit;
    if Assigned(Edit)
    and (not IsWindowUnicode(Edit.Handle)) then
      ReCreateUnicodeWnd(Edit, 'EDIT', True);
  end;
end;

procedure TWideStringProperty.SetPropEntry(Index: Integer;
  AInstance: TPersistent; APropInfo: PPropInfo);
begin
  inherited;
  with FPropList^[Index] do
  begin
    Instance := AInstance;
    PropInfo := APropInfo;
  end;
end;

function TWideStringProperty.GetWideStrValueAt(Index: Integer): WideString;
begin
  with FPropList^[Index] do Result := GetWideStrProp(Instance, PropInfo);
end;

function TWideStringProperty.GetWideStrValue: WideString;
begin
  Result := GetWideStrValueAt(0);
end;

procedure TWideStringProperty.SetWideStrValue(const Value: WideString);
var
  I: Integer;
begin
  for I := 0 to PropCount - 1 do
    with FPropList^[I] do SetWideStrProp(Instance, PropInfo, Value);
  Modified;
end;

function TWideStringProperty.GetWideVisualValue: WideString;
begin
  if AllEqual then
    Result := GetWideStrValue
  else
    Result := '';
end;

procedure TWideStringProperty.PropDrawName(ACanvas: TCanvas; const ARect: TRect; ASelected: Boolean);
begin
  DefaultPropertyDrawName(Self, ACanvas, ARect);
end;

procedure TWideStringProperty.PropDrawValue(ACanvas: TCanvas; const ARect: TRect; ASelected: Boolean);
begin
  WideCanvasTextRect(ACanvas, ARect, ARect.Left + 1, ARect.Top + 1, GetWideVisualValue);
end;

function TWideStringProperty.AllEqual: Boolean;
var
  I: Integer;
  V: WideString;
begin
  Result := False;
  if PropCount > 1 then
  begin
    V := GetWideStrValue;
    for I := 1 to PropCount - 1 do
      if GetWideStrValueAt(I) <> V then Exit;
  end;
  Result := True;
end;

function TWideStringProperty.GetEditLimit: Integer;
var
  Edit: TCustomEdit{TNT-ALLOW TCustomEdit};
begin
  Result := MaxInt;
  // GetEditLimit is called right before the inplace editor text has been set
  if Win32PlatformIsUnicode then begin
    Edit := GetOIPropInspEdit;
    if Assigned(Edit) then begin
      TntControl_SetText(Edit, GetWideStrValue);
      TntControl_SetHint(Edit, GetWideStrValue);
    end;
  end;
end;

function TWideStringProperty.GetValue: AnsiString;
begin
  FActivateWithoutGetValue := False;
  Result := WideStringToStringEx(GetWideStrValue, CP_ACP{TNT-ALLOW CP_ACP}); // use the same code page as the inplace editor
end;

procedure TWideStringProperty.SetValue(const Value: AnsiString);
var
  Edit: TCustomEdit{TNT-ALLOW TCustomEdit};
begin
  if (not FActivateWithoutGetValue) then begin
    Edit := GetOIPropInspEdit;
    if Assigned(Edit) and Win32PlatformIsUnicode then
      SetWideStrValue(TntControl_GetText(Edit))
    else
      SetWideStrValue(StringToWideStringEx(Value, CP_ACP{TNT-ALLOW CP_ACP})); // use the same code page as the inplace editor
  end;
end;

{$IFDEF MULTI_LINE_STRING_EDITOR}
function TWideStringProperty.GetAttributes: TPropertyAttributes;
begin
  Result := inherited GetAttributes + [paDialog];
end;

procedure TWideStringProperty.Edit;
var
  Temp: WideString;
begin
  with TTntStrEditDlg.Create(Application) do
  try
    PrepareForWideStringEdit;
    Memo.Text := GetWideStrValue;
    UpdateStatus(nil);
    if ShowModal = mrOk then begin
      Temp := Memo.Text;
      while (Length(Temp) > 0) and (Temp[Length(Temp)] < ' ') do
        System.Delete(Temp, Length(Temp), 1); { trim control characters from end }
      SetWideStrValue(Temp);
    end;
  finally
    Free;
  end;
end;
{$ENDIF}

{ TWideCaptionProperty }

function TWideCaptionProperty.GetAttributes: TPropertyAttributes;
begin
  Result := inherited GetAttributes + [paAutoUpdate];
end;

{ TWideCharProperty }

function TWideCharProperty.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paRevertable];
end;            

function TWideCharProperty.GetEditLimit: Integer;
begin
  inherited GetEditLimit;
  Result := 63;
end;

{$IFDEF COMPILER_7_UP}
function TWideCharProperty.GetIsDefault: Boolean;
var
  i: Integer;
  OldPropList: PInstPropList;
begin
  Result := True;
  if PropCount > 0 then
  begin
    OldPropList := THackPropertyEditor(Self).FPropList;
    // The memory FPropList points to is write-protected.
    // In the constructor we dynamically allocated our own PropList,
    // which can be written, so point there instead.
    THackPropertyEditor(Self).FPropList := FPropList;

    // Delphi can't handle WideChar-type, but does well with Word-type,
    // which has exactly the same size as WideChar (i.e. 2 Bytes)
    for i := 0 to PropCount - 1 do
      FPropList^[i].PropInfo^.PropType^ := TypeInfo(Word);

    Result := inherited GetIsDefault;

    for i := 0 to PropCount - 1 do
      FPropList^[i].PropInfo^.PropType^ := TypeInfo(WideChar);

    THackPropertyEditor(Self).FPropList := OldPropList;
  end;
end;
{$ENDIF}

function IsCharGraphic(C: WideChar): Boolean;
begin
  if Win32PlatformIsUnicode then
    Result := not IsWideCharCntrl(C) and not IsWideCharSpace(C)
  else // representation as charcode avoids corruption on ANSI-systems
    Result := (C >= #33) and (C <= #127);
end;

function TWideCharProperty.GetWideStrValueAt(Index: Integer): WideString;
var
  C: WideChar;
begin
  with FPropList^[Index] do
    C := WideChar(GetOrdProp(Instance, PropInfo));

  if IsCharGraphic(C) then
    Result := C
  else
    Result := WideFormat('#%d', [Ord(C)]);
end;

procedure TWideCharProperty.SetWideStrValue(const Value: WideString);
var
  C: Longint;
  I: Integer;
begin
  if Length(Value) = 0 then
    C := 0
  else if Length(Value) = 1 then
    C := Ord(Value[1])
  else if Value[1] = '#' then
    C := StrToInt(Copy(Value, 2, Maxint))
  else
    raise EPropertyError.Create(SInvalidPropertyValue);

  with GetTypeData(GetPropType)^ do
    if (C < MinValue) or (C > MaxValue) then
      raise EPropertyError.CreateFmt(SOutOfRange, [MinValue, MaxValue]);

  for I := 0 to PropCount - 1 do
    with FPropList^[I] do SetOrdProp(Instance, PropInfo, C);
    
  Modified;
end;

initialization

finalization
  ConvertObjectInspectorBackToANSI;

end.
