unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls, Buttons, Math, pngimage,
  NxEdit6,
  NxControls6, NxCollection6,
  NxColumns6, NxGridView6, NxCustomGrid6, NxVirtualGrid6, NxGrid6;

const
  Adjectives: array[0..5] of String = ('famous', 'big', 'red', 'London', '185bhp engined', 'bus');

  NORMAL_HEIGHT   = 375;
  EXTENDED_HEIGHT = 560;


type
  TForm1 = class(TForm)
    StatusBar1: TStatusBar;
    tagWords: TNxTagsEdit6;
    btnTryAgain: TNxButton6;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    SadFaceImage: TImage;
    HappyfaceImage: TImage;
    GreenTickImage: TImage;
    btnSolution: TNxButton6;
    NextGrid61: TNextGrid6;
    NxReportGridView61: TNxReportGridView6;
    NxTextColumn61: TNxTextColumn6;
    NxTextColumn62: TNxTextColumn6;
    NxTextColumn63: TNxTextColumn6;
    NxTextColumn64: TNxTextColumn6;
    NxTextColumn65: TNxTextColumn6;
    NxTextColumn66: TNxTextColumn6;
    NxTextColumn67: TNxTextColumn6;
    NxTextColumn68: TNxTextColumn6;
    NxTextColumn69: TNxTextColumn6;
    Label5: TLabel;
    procedure btnTryAgainClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure tagWordsTagDelete(Sender: TObject; Index: Integer; var Accept: Boolean);
    procedure tagWordsGetTagColor(Sender: TObject; Index: Integer; var FillColor: TColor;
      var BorderColor);
    procedure btnSolutionClick(Sender: TObject);
    procedure tagWordsMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer; Area: TNxTagArea);
    procedure tagWordsMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer;
      Area: TNxTagArea);
    procedure tagWordsMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer; Area: TNxTagArea);
  private
    function RandomizeArray: TStringList;
    function ValidateTagText: Boolean;
    procedure ExpandForm(MinWidth, MinHeight: Integer);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure TForm1.FormCreate(Sender: TObject);
begin
   //initialize solution grid
   NextGrid61.AddCells(['famous', 'big',   '', '','red', 'London', '185bhp engined', '', 'bus'], [0,1,2,3,4,5,6,7,8]);
   NextGrid61.AddCells(['silly',  '',   'young', '', '', 'Italian',  '',    '',    'man'], [0,1,2,3,4,5,6,7,8]);
   NextGrid61.AddCells([  '',    'big', '',   'round', '', '', 'wooden', 'mixing', 'bowl'], [0,1,2,3,4,5,6,7,8]);
   NextGrid61.AddCells(['really', '',  'old', '', 'green', 'Japanese', 'antique', '', 'clock'], [0,1,2,3,4,5,6,7,8]);
   //initialize UI
   btnTryAgain.Click;
end;


procedure TForm1.btnTryAgainClick(Sender: TObject);
var
   Idx: Integer;
   AdjectivesList: TStringList;
begin
   //clear tagedit control and reset selected tag
   tagWords.Tags.Clear;
   tagWords.Tag := -1;
   //refill with adjectives randomly
   AdjectivesList := RandomizeArray;
   try
      for idx := 0 to Pred(AdjectivesList.Count) do
          tagWords.Tags.Add(AdjectivesList[idx]);
   finally
      AdjectivesList.Free;
   end;
   //reset images
   HappyfaceImage.Visible := False;
   SadFaceImage.Visible   := True;
   //reset form height
   Form1.Height := NORMAL_HEIGHT;
   //reset status bar messages
   StatusBar1.Panels[1].Text := '';
   StatusBar1.Panels[2].Text := '';
end;


procedure TForm1.btnSolutionClick(Sender: TObject);
begin
   ExpandForm(Form1.Width, EXTENDED_HEIGHT);
   NextGrid61.Visible := True;
   NextGrid61.SelectFirst;
end;


function TForm1.RandomizeArray: TStringList;
var
   i, Idx: Integer;
   TmpList: TStringList;
begin
   Result := TStringList.Create;
   //fill a temporary stringlist with our adjectives
   TmpList := TStringList.Create;
   for i := 0 to High(Adjectives) do TmpList.Add(Adjectives[i]);
   //use the temp list to populate randomly the function result list
   Randomize;
   for i := TmpList.Count -1 downto 1 do
   begin
      Idx := Random(i+1);        //remember Random generates:  0 <= Number < LimitPlusOne
      Result.Add(TmpList[idx]);
      TmpList.Delete(idx);
   end;
   //only one adjective left in the Temp list so no need to choose
   Result.Add(TmpList[0]);
   //finished
   TmpList.Free
end;


function TForm1.ValidateTagText: Boolean;
var
   GoodText: String;
   TagText: String;
   i: Integer;
begin
   //convert the Adjectives words into text
   GoodText := Adjectives[0];
   for i := 1 to High(Adjectives) do
   begin
      GoodText := GoodText + ' ' + Adjectives[i];
   end;
   //convert to text the tags in the order that the user has placed them
   TagText := Trim(tagWords.Tags[0]);
   for i := 1 to Pred(tagWords.Tags.Count) do
   begin
      TagText := TagText + ' ' + Trim(tagWords.Tags[i]);
   end;
   Result := (AnsiCompareText(GoodText, TagText) = 0);
end;


{----------------------------------The TagEdit control-----------------------------------}

procedure TForm1.tagWordsGetTagColor(Sender: TObject; Index: Integer; var FillColor: TColor;
  var BorderColor);
begin
   if tagWords.Tags[Index] = 'red' then FillColor := clRed
                                   else FillColor := clMoneyGreen;
end;


procedure TForm1.tagWordsTagDelete(Sender: TObject; Index: Integer; var Accept: Boolean);
begin
   //in this demo, cannot allow deletion of tags
   Accept := False;
end;


procedure TForm1.tagWordsMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
  X,Y: Integer; Area: TNxTagArea);
begin
   if Area = csTagArea then
   begin
      //the mouse button has been pressed down on one of the tags
      Screen.Cursor := crSizeWE;
      //remember which tag has been selected
      tagWords.Tag := tagWords.SelectedTag;
   end;
   StatusBar1.Panels[1].Text := 'Tag selected: '+IntToStr(tagWords.SelectedTag);
end;


procedure TForm1.tagWordsMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer; Area: TNxTagArea);
begin
   if (tagWords.Tag <> -1) and                 //we have already a selected tag
      (Screen.Cursor = crSizeWE) and           //we are ready to 'drag'
      (tagWords.SelectedTag <> tagWords.Tag)   //now moved onto a different tag
   then Screen.Cursor := crDrag;
end;


procedure TForm1.tagWordsMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
  X,Y: Integer; Area: TNxTagArea);

  procedure MoveTag(FromPosn, ToPosn: Integer);
  var TagStr: String;
  begin
     if InRange(FromPosn, 0, Pred(tagWords.Tags.Count)) and
        InRange(ToPosn,   0, Pred(tagWords.Tags.Count)) then
     begin
        TagStr := tagWords.Tags[FromPosn];
        tagWords.Tags.Delete(FromPosn);
        tagWords.Tags.Insert(ToPosn, TagStr);
     end;
  end;

var
   NewIdx: Integer;
begin
   case Area of
      csTagArea : StatusBar1.Panels[2].Text := 'Mouse up in TagArea';
      csEditArea: StatusBar1.Panels[2].Text := 'Mouse up in EditArea';
   end;

   if (tagWords.Tag <> -1) and                       //we have already a selected tag
      (Screen.Cursor = crDrag) then                  //we are in 'drag' mode
   begin
      if Area = csEditArea                           //the 'white' edit area beyond the last tag
      then NewIdx := Pred(tagWords.Tags.Count)       //the last tag
      else NewIdx := tagWords.SelectedTag;
      //
      if (NewIdx <> tagWords.Tag)                    //now moved onto a different tag
      then MoveTag(tagWords.Tag, NewIdx);            //move OldIdx --> NewIdx
      StatusBar1.Panels[1].Text := Format('Move tag from %d to %d',[tagWords.Tag, NewIdx]);
   end;
   //
   Screen.Cursor := crDefault;                       //whether the cursor is crSizeWE or crDrag
   tagWords.Tag := -1;
   //is it correct?
   HappyfaceImage.Visible := ValidateTagText;
   GreenTickImage.Visible := HappyfaceImage.Visible;
   SadFaceImage.Visible   := not HappyfaceImage.Visible;
end;



{---------------------------Utility (smooth resize of form)---------------------------------}

procedure TForm1.ExpandForm(MinWidth, MinHeight: Integer);
var
   Interval: Integer;
begin
   Interval := 1;
   while self.Width < MinWidth do
   begin
      Width := Width + 5;
      // make sure it doesn't go off screen as it expands
      if (self.Left + self.Width) > Screen.DesktopWidth
      then self.Left := Screen.DesktopWidth - self.Width - 1;
      Refresh;
      Sleep(Interval);
   end;
   while self.Height < MinHeight do
   begin
      Height := Height + 5;
      // make sure it doesn't go off screen as it expands
      if (self.Top + self.Height) > Screen.DesktopHeight
      then self.Top := Screen.DesktopHeight - self.Height - 1;
      Refresh;
      Sleep(Interval);
   end;
end;



end.
