classdef TSTextArea < toolpack.component.component
  % Toolstrip text area.
  %
  % Example:
  %   text = 'Some text here';
  %   columns = 10;
  %
  %   txt = toolpack.component.TSTextArea;
  %   txt = toolpack.component.TSTextArea(columns);
  %   txt = toolpack.component.TSTextArea(text);
  %   txt = toolpack.component.TSTextArea(text, columns);
  %
  % See also toolpack.component.TSTextField
  
  % Author(s): Bora Eryilmaz
  % Revised:
  % Copyright 2012-2013 The MathWorks, Inc.
  
  % ----------------------------------------------------------------------------
  properties (Dependent, Access = public, SetObservable, AbortSet)
    % Component's text (string, default = '')
    Text
  end
  
  properties (Dependent, Access = public)
    % Component's number of columns (integer, default = 0)
    Columns
    % Component's editable state (logical, default = true)
    Editable
  end
  
  properties (Access = private)
    % String
    Text_ = ''; % AbortSet does not let setting []-->'' in the constructor.
    
    % JTextArea (different than Peer, which is a scroll pane)
    JTextArea_
  end
  
  % ----------------------------------------------------------------------------
  events
    % Event sent when the component loses keyboard focus.
    FocusLost
    % Event sent when the component gains keyboard focus.
    FocusGained
    % Event sent upon modifying the text or losing focus after a real text
    % change.
    TextEdited
  end
  
  % ----------------------------------------------------------------------------
  methods
    function this = TSTextArea(text, columns)
      % Creates a toolstrip text area.
      this = this@toolpack.component.component;
      cls = 'com.mathworks.toolstrip.components.TSTextArea';
      
      if nargin == 0
        % TSTextArea();
        this.JTextArea_ = javaObjectEDT(cls);
      elseif nargin == 1
        text = controllib.internal.util.hString2Char(text);
        if ischar(text)
          % TSTextArea(text)
          this.JTextArea_ = javaObjectEDT(cls, text);
        else
          % TSTextArea(columns)
          rows = 0;
          columns = text;
          this.JTextArea_ = javaObjectEDT(cls, rows, columns);
        end
      else
        % TSTextArea(text, columns)
        rows = 0;
        this.JTextArea_ = javaObjectEDT(cls, text, rows, columns);
      end
      % TODO: Remove next two lines after switching to TSTextArea.java
      this.JTextArea_.setLineWrap(true)
      this.JTextArea_.setWrapStyleWord(true)
      this.Peer = javaObjectEDT('com.mathworks.toolstrip.components.TSScrollPane', ...
        this.JTextArea_);
      
      this.Text = char(this.JTextArea_.getText);
      addDocumentListener( this, {@LocalDocumentChanged, this} )
      addFocusGainedListener( this, {@LocalFocusGained, this} )
      addFocusLostListener( this, {@LocalFocusLost, this} )
    end
    
    function value = get.Text(this)
      % GET function for Text property.
      value = this.Text_;
    end
    
    function set.Text(this, value)
      % SET function for Text property.
      value = controllib.internal.util.hString2Char(value);
      if isempty(value)
        value = '';
      elseif ~ischar(value)
        error(message('Controllib:toolpack:StringArgumentNeeded'))
      end
      this.Text_ = value;
      % Break Java --> MCOS -->x Java loop
      if ~strcmp(value, char(this.JTextArea_.getText))
        this.JTextArea_.setText(value);
      end
    end
    
    function value = get.Columns(this)
      % GET function for Columns property.
      value = this.JTextArea_.getColumns;
    end
    
    function set.Columns(this, value)
      % SET function for Columns property.
      if ~isnumeric(value)
        error(message('Controllib:toolpack:InvalidPropertyValue', 'Columns'))
      end
      this.JTextArea_.setColumns(value);
    end
    
    function value = get.Editable(this)
      % GET function for Editable property.
      value = this.JTextArea_.isEditable;
    end
    
    function set.Editable(this, value)
      % SET function for Editable property.
      if ~islogical(value)
        error(message('Controllib:toolpack:InvalidPropertyValue', 'Editable'))
      end
      this.JTextArea_.setEditable(value);
    end
    
    function value = getVerticalScrollBarPolicy(this)
      % Get vertical scroll bar policy.
      p = this.Peer.getVerticalScrollBarPolicy;
      value = toolpack.component.ScrollPaneConstants.getScrollBarPolicy(p);
    end
    
    function setVerticalScrollBarPolicy(this, value)
      % Set vertical scroll bar policy.
      if ~isa(value, 'toolpack.component.ScrollPaneConstants')
        error(message('Controllib:toolpack:InvalidPropertyValue','VerticalScrollBarPolicy'))
      end
      this.Peer.setVerticalScrollBarPolicy( value.JavaEnum );
    end
    
    function value = getHorizontalScrollBarPolicy(this)
      % Get horizontal scroll bar policy.
      p = this.Peer.getHorizontalScrollBarPolicy;
      value = toolpack.component.ScrollPaneConstants.getScrollBarPolicy(p);
    end
    
    function setHorizontalScrollBarPolicy(this, value)
      % Set horizontal scroll bar policy.
      if ~isa(value, 'toolpack.component.ScrollPaneConstants')
        error(message('Controllib:toolpack:InvalidPropertyValue','HorizontalScrollBarPolicy'))
      end
      this.Peer.setHorizontalScrollBarPolicy( value.JavaEnum );
    end
    
    function setLineWrap(this, value)
      % Set line wrapping policy
      if ~islogical(value)
        error(message('Controllib:toolpack:InvalidPropertyValue', 'LineWrap'))
      end
      this.JTextArea_.setLineWrap(value)
    end
    
    function value = getLineWrap(this)
      % Get the line wrapping policy.
      value = logical(this.JTextArea_.getLineWrap);
    end
  end
  
  methods (Access = protected)
    function value = getName_(this)
      % Overloaded to work with the JTextArea_ component instead of the Peer.
      value = char(this.JTextArea_.getName);
    end
    
    function setName_(this, value)
      % Overloaded to work with the JTextArea_ component instead of the Peer.
      this.JTextArea_.setName(value);
    end
    
    function value = getEnabled_(this)
      % Overloaded to work with the JTextArea_ component instead of the Peer.
      value = this.JTextArea_.isEnabled;
    end
    
    function setEnabled_(this, value)
      % Overloaded to work with the JTextArea_ component instead of the Peer.
      this.JTextArea_.setEnabled(value);
    end
  end
  
  methods (Access = private)
    function addDocumentListener(this, fcn)
      % Do not modify. Use as is to prevent Java memory leaks.
      cb = javaMethodEDT( ...
        'getDocumentCallback', ...
        'com.mathworks.toolbox.shared.controllib.desktop.TSUtils', ...
        this.JTextArea_);
      L = addlistener(cb, 'delayed', fcn);
      this.Listeners = [this.Listeners; L];
    end
    
    function addFocusLostListener(this, fcn)
      % Do not modify. Use as is to prevent Java memory leaks.
      L = addlistener(this.JTextArea_, 'FocusLost', fcn);
      this.Listeners = [this.Listeners; L];
    end
    
    function addFocusGainedListener(this, fcn)
      % Do not modify. Use as is to prevent Java memory leaks.
      L = addlistener(this.JTextArea_, 'FocusGained', fcn);
      this.Listeners = [this.Listeners; L];
    end
  end
end

% ------------------------------------------------------------------------------
function LocalDocumentChanged(~,~,obj)
oldText = obj.Text;
newText = char(obj.JTextArea_.getText);
obj.Text = newText;

data = struct('oldValue', oldText, 'newValue', newText);
obj.notify('TextEdited', toolpack.ComponentEventData(data))
end

function LocalFocusLost(~,~,obj)
obj.notify('FocusLost')
end

function LocalFocusGained(~,~,obj)
obj.notify('FocusGained')
end
