ListHeader.cs :  » Game » RealmForge » CrayzEdsGui » Base » Widgets » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Game » RealmForge 
RealmForge » CrayzEdsGui » Base » Widgets » ListHeader.cs
#region LGPL License

/*************************************************************************
    Crazy Eddie's GUI System (http://crayzedsgui.sourceforge.net)
    Copyright (C)2004 Paul D Turner (crayzed@users.sourceforge.net)
  
  C# Port developed by Chris McGuirk (leedgitar@latenitegames.com)
  Compatible with the Axiom 3D Engine (http://axiomengine.sf.net)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*************************************************************************/

#endregion LGPL License

using System;

namespace CrayzEdsGui.Base.Widgets{
  /// <summary>
  /// Base class for list header widget.
  /// </summary>
  public abstract class ListHeader: Window 
  {
    #region Constants
    /// <summary>
    /// Speed to scroll at when dragging outside header.
    /// </summary>
    const float ScrollSpeed  = 8.0f;

    /// <summary>
    /// Minimum width of a segment in pixels.
    /// </summary>
    public const float MinimumSegmentPixelWidth = 20.0f;
    #endregion

    #region Fields
    /// <summary>
    /// Attached segment windows in header order.
    /// </summary>
    HeaderSegmentList segments;

    /// <summary>
    /// Pointer to the segment that is currently set as the sort-key,
    /// </summary>
    ListHeaderSegment sortSegment;  

    /// <summary>
    /// true if segments can be sized by the user.
    /// </summary>
    bool sizingEnabled;

    /// <summary>
    /// true if the sort criteria modifications by user are enabled (no sorting is actuall done)
    /// </summary>
    bool sortingEnabled;

    /// <summary>
    /// true if drag & drop moving of columns / segments is enabled.
    /// </summary>
    bool movingEnabled;

    /// <summary>
    /// Base offset used to layout the segments (allows scrolling within the window area)
    /// </summary>
    float segmentOffset;    

    /// <summary>
    /// Brief copy of the current sort direction.
    /// </summary>
    SortDirection sortingDirection;

    #endregion

    #region Constructor
    /// <summary>
    ///    Constructor.
    /// </summary>
    /// <param name="type"></param>
    /// <param name="name"></param>
    public ListHeader(string name) : base(name)
    {
      segments = new HeaderSegmentList();
      sortSegment = null;
      sizingEnabled = true;
      sortingEnabled = true;
      movingEnabled = true;
      segmentOffset = 0.0f;
      sortingDirection = SortDirection.None;
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets the number of columns (segments) in the header.
    /// </summary>
    public int ColumnCount {
      get {
        return segments.Count;
      }
    }

    /// <summary>
    /// Gets/Sets the current sort segment
    /// </summary>
    public ListHeaderSegment SortSegment {
      get {
        if (sortSegment == null) {
          throw new InvalidRequestException("Sort segment is null.  Maybe there are no segments attached to the header?");
        }
        else {
          return sortSegment;
        }
      }

      set {
        SortColumnIndex = GetColumnIndexFromSegment(value);
      }
    }

    /// <summary>
    /// Gets/Sets the current sort column index
    /// </summary>
    public int SortColumnIndex {
      get {
        return GetColumnIndexFromSegment(SortSegment);
      }

      set {
        if (value >= ColumnCount) {
          throw new InvalidRequestException("The specified index ({0}) is out of range for this ListHeader.", value);
        }
        else {
          // if column is different to current segment
          if (sortSegment != segments[value]) {
            // set sort direction on current segment to none
            if (sortSegment != null) {
              sortSegment.SortDirection = SortDirection.None;
            }

            // set up new sort segment
            sortSegment = segments[value];
            sortSegment.SortDirection = sortingDirection;

            // fire event
            OnSortColumnChanged(new WindowEventArgs(this));
          }
        }
      }
    }

    /// <summary>
    /// Get/Set current sort column via it's ID.
    /// </summary>
    [WidgetProperty("SortColumnID")]
    public int SortColumnID {
      get {
        return SortSegment.ID;
      }

      set {
        SetSortColumnFromID(value);
      }
    }

    /// <summary>
    /// Get the pixel extent of all segments
    /// </summary>
    public float TotalPixelExtent  {
      get {
        float extent = 0.0f;

        for (int i = 0; i < ColumnCount; ++i) 
        {
          extent += segments[i].AbsoluteWidth;
        }

        return extent;
      }
    }

    /// <summary>
    /// Gets/Sets the current sorting direction.
    /// </summary>
    [SortDirectionProperty("SortDirection")]
    public SortDirection SortDirection 
    {
      get {
        return sortingDirection;
      }

      set {
        if (sortingDirection != value) {
          sortingDirection = value;

          // set direction of current sort segment
          if (sortSegment != null) {
            sortSegment.SortDirection = sortingDirection;
          }

          // fire event
          OnSortDirectionChanged(new WindowEventArgs(this));
        }
      }
        }

    /// <summary>
    /// Gets/Sets whether use control of the sort column and direction is enabled.
    /// </summary>
    [WidgetProperty("SortSettingEnabled")]
    public bool UserSortControlEnabled 
    {
      get {
        return sortingEnabled;
      }

      set {
        if (sortingEnabled != value) {
          sortingEnabled = value;

          // make the setting change for all segments
          for (int i = 0; i < ColumnCount; ++i) {
            segments[i].Clickable = sortingEnabled;
          }

          // fire event
          OnSortSettingChanged(new WindowEventArgs(this));
        }
      }
    }

    /// <summary>
    /// Gets/Sets whether the user may re-size the header segments.
    /// </summary>
    [WidgetProperty("ColumnsSizable")]
    public bool SegmentSizingEnabled {
      get {
        return sizingEnabled;
      }

      set {
        if (sizingEnabled != value) {
          sizingEnabled = value;

          // modify setting on all segments
          for(int i = 0; i < ColumnCount; ++i) {
            segments[i].Sizable = sizingEnabled;
          }

          // fire event
          OnDragSizeSettingChanged(new WindowEventArgs(this));
        }
      }
    }

    /// <summary>
    /// Gets/Sets whether the user may drag & drop the header segments to change their sequence.
    /// </summary>
    [WidgetProperty("ColumnsMovable")]
    public bool SegmentDraggingEnabled
    {
      get {
        return movingEnabled;
      }

      set {
        if (movingEnabled != value) {
          movingEnabled = value;

          // modify setting on all segments
          for(int i = 0; i < ColumnCount; ++i) {
            segments[i].Draggable = movingEnabled;
          }

          // fire event
          OnDragMoveSettingChanged(new WindowEventArgs(this));
        }
      }
    }

    /// <summary>
    /// Gets/Sets the current segment scroll offset value.
    /// </summary>
    public float ScrollOffset {
      get {
        return segmentOffset;
      }

      set {
        if (segmentOffset != value) {
          segmentOffset = value;
          LayoutSegments();
          RequestRedraw();

          OnScrollOffsetChanged(new WindowEventArgs(this));
        }
      }
    }

    #endregion

    #region Indexers
    /// <summary>
    /// Indexer to return the ListHeaderSegment for a given column index.
    /// </summary>
    public ListHeaderSegment this[int index] 
    {
      get {
        return segments[index];
      }
    }
    #endregion

    #region Methods

    /// <summary>
    ///    Return the ListHeaderSegment object with the specified ID.
    /// </summary>
    /// <param name="id">id code of the ListHeaderSegment to be returned.</param>
    /// <returns>
    ///    ListHeaderSegment object with the requested ID.  If more than one segment has
    ///    the same ID, only the first one will ever be returned.
    /// </returns>
    /// <exception cref="InvalidRequestException">thrown if no segment with the requested ID is attached.</exception>
    public ListHeaderSegment GetSegmentFromID(int id)
    {
      for (int i = 0; i < ColumnCount; ++i) {
        if (segments[i].ID == id) {
          return segments[i];
        }
      }

      // no such segment...
      throw new InvalidRequestException("There is no segment attached with the ID code {0}", id);
    }

    /// <summary>
    ///    Return the zero based column index of the specified segment.
    /// </summary>
    /// <param name="segment">ListHeaderSegment whos zero based index is to be returned.</param>
    /// <returns>Zero based column index of the given ListHeaderSegment</returns>
    /// <exception cref="InvalidRequestException">thrown if the given segment is not attached to this ListHeader.</exception>
    public int GetColumnIndexFromSegment(ListHeaderSegment segment)
    {
      for (int i = 0; i < ColumnCount; ++i) {
        if (segments[i] == segment) {
          return i;
        }
      }

      // no such segment...
      throw new InvalidRequestException("The specified ListHeaderSegment is not attached to the ListHeader.");
    }

    /// <summary>
    ///    Return the zero based column index of the segment with the specified ID.
    /// </summary>
    /// <param name="id">ID code of the segment whos column index is to be returned.</param>
    /// <returns>Zero based column index of the first ListHeaderSegment whos ID matches 'id'.</returns>
    /// <exception cref="InvalidRequestException">thrown if no attached segment has the requested ID.</exception>
    public int GetColumnIndexFromSegmentID(int id)
    {
      for (int i = 0; i < ColumnCount; ++i) {
        if (segments[i].ID == id) {
          return i;
        }
      }

      // no such segment
      throw new InvalidRequestException("There is no segment attached with the ID code {0}", id);
    }

    /// <summary>
    ///    Return the zero based column index of the segment with the specified text.
    /// </summary>
    /// <param name="text">string containing the text to be searched for.</param>
    /// <returns>Zero based column index of the segment with the specified text.</returns>
    /// <exception cref="InvalidRequestException">thrown if no attached segments have the requested text.</exception>
    public int GetColumnIndexFromSegmentText(string text)
    {
      for (int i = 0; i < ColumnCount; ++i) {
        if (segments[i].Text == text) {
          return i;
        }
      }

      // no such segment...
      throw new InvalidRequestException("No segment with the text '{0}' is attached to this ListHeader.", text);
    }

    /// <summary>
    ///    Return the pixel offset to the given ListHeaderSegment.
    /// </summary>
    /// <param name="segment">ListHeaderSegment object that the offset to is to be returned.</param>
    /// <returns>The number of pixels up-to the begining of the ListHeaderSegment described by 'segment'.</returns>
    /// <exception cref="InvalidRequestException">thrown if 'segment' is not attached to the ListHeader.</exception>
    public float GetPixelOffsetToSegment(ListHeaderSegment segment)
    {
      float offset = 0.0f;

      for (int i = 0; i < ColumnCount; ++i) {
        if (segments[i] == segment) {
          return offset;
        }

        offset += segments[i].AbsoluteWidth;
      }

      // no such segment...
      throw new InvalidRequestException("The specified ListHeaderSegment is not attached to this ListHeader.");
    }

    /// <summary>
    ///    Return the pixel offset to the ListHeaderSegment at the given zero based column index.
    /// </summary>
    /// <param name="column">Zero based column index of the ListHeaderSegment whos pixel offset it to be returned.</param>
    /// <returns>
    ///    The number of pixels up-to the begining of the ListHeaderSegment located at zero based column index 'column'.
    /// </returns>
    /// <exception cref="InvalidRequestException">thrown if 'column' is out of range.</exception>
    public float GetPixelOffsetToColumn(int column)
    {
      if (column >= ColumnCount) {
        throw new InvalidRequestException("The specified index ({0}) is out of range for this ListHeader.", column);
      }
      else {
        float offset = 0.0f;

        for (int i = 0; i < column; ++i) {
          offset += segments[i].AbsoluteWidth;
        }

        return offset;
      }
    }

    /// <summary>
    ///    Return the pixel width of the specified column.
    /// </summary>
    /// <param name="column">Zero based column index of the segment whos pixel width is to be returned.</param>
    /// <returns>Pixel width of the ListHeaderSegment at the zero based column index specified by 'column'.</returns>
    /// <exception cref="InvalidRequestException">thrown if 'column' is out of range.</exception>
    public float GetPixelWidthOfColumn(int column)
    {
      if (column >= ColumnCount) {
        throw new InvalidRequestException("The specified index ({0}) is out of range for this ListHeader.", column);
      }
      else {
        return segments[column].AbsoluteWidth;
      }
    }

    /// <summary>
    ///    Set the column to to be used for sorting via its ID code.
    /// </summary>
    /// <param name="id">ID code of the column segment that is to be used as the sort column.</param>
    /// <exception cref="InvalidRequestException">thrown if no segment with ID 'id' is attached to the ListHeader.</exception>
    public void SetSortColumnFromID(int id)
    {
      SortColumnIndex = GetColumnIndexFromSegmentID(id);
    }

    /// <summary>
    ///    Add a new column segment to the end of the header.
    /// </summary>
    /// <param name="text">String object holding the initial text for the new segment</param>
    /// <param name="id">Client specified ID code to be assigned to the new segment.</param>
    /// <param name="width">Initial width of the new segment using the active metrics system</param>
    public void AddColumn(string text, int id, float width)
    {
      // add just inserts at the end
      InsertColumn(text, id, width, ColumnCount);
    }

    /// <summary>
    ///    Insert a new column segment at the specified position.
    /// </summary>
    /// <param name="text">String object holding the initial text for the new segment</param>
    /// <param name="id">Client specified ID code to be assigned to the new segment.</param>
    /// <param name="width">Initial width of the new segment using the active metrics system</param>
    /// <param name="position">
    ///    Zero based column index indicating the desired position for the new column.
    ///    If this is greater than the current number of columns, the new segment is added to the end if the header.
    /// </param>
    public void InsertColumn(string text, int id, float width, int position)
    {
      // if position is too big, insert at end
      if (position > ColumnCount) {
        position = ColumnCount;
      }

      ListHeaderSegment newSegment = CreateInitialisedSegment(text, id, width);
      segments.Insert(position, newSegment);

      // add new segment as a child window
      AddChild(newSegment);

      LayoutSegments();

      // fire event
      OnSegmentAdded(new WindowEventArgs(this));

      // if sort segment is invalid, set-up sorting now we have a segment
      if (sortSegment == null) {
        SortColumnIndex = position;
      }
    }

    /// <summary>
    ///    Removes a column segment from the ListHeader.
    /// </summary>
    /// <param name="columnIndex">Zero based column index indicating the segment to be removed.</param>
    /// <exception cref="InvalidRequestException">thrown if 'columnIndex' is out of range.</exception>
    public void RemoveColumn(int columnIndex)
    {
      if (columnIndex >= ColumnCount) {
        throw new InvalidRequestException("The specified index ({0}) is out of range for this ListHeader.", columnIndex);
      }
      else {
        ListHeaderSegment seg = segments[columnIndex];

        // remove the segment from the list
        segments.Remove(seg);

        // patch up sort segment if we have removed it
        if (sortSegment == seg) {
          // more columns?
          if (ColumnCount > 0) {
            // make first column the new sort column
            sortingDirection = SortDirection.None;
            SortColumnIndex = 0;
          }
          // no columns
          else {
            sortSegment = null;
          }
        }

        // remove the segment from our child list
        RemoveChild(seg);

        DestroyListSegment(seg);
        LayoutSegments();

        // fire event
        OnSegmentRemoved(new WindowEventArgs(this));
      }
    }

    /// <summary>
    ///    Moves a column segment into a new position.
    /// </summary>
    /// <param name="columnIndex">Zero based column index indicating the column segment to be moved.</param>
    /// <param name="positionIndex">
    ///    Zero based column index indicating the new position for the segment.  If this is greater than the
    ///    current number of segments, the segment is moved to the end of the header.
    /// </param>
    /// <exception cref="InvalidRequestException">thrown if 'columnIndex' is out of range for this ListHeader.</exception>
    public void MoveColumn(int columnIndex, int positionIndex)
    {
      if (columnIndex >= ColumnCount) {
        throw new InvalidRequestException("The specified index ({0}) is out of range for this ListHeader.", columnIndex);
      }
      else {
        // if the target position is out of range, move to the end
        if (positionIndex > ColumnCount) {
          positionIndex = ColumnCount - 1;
        }

        ListHeaderSegment seg = segments[columnIndex];

        // remove original copy of segment
        segments.Remove(seg);

        // re-insert segment at its new position
        segments.Insert(positionIndex, seg);

        // fire event
        OnSegmentSequenceChanged(new HeaderSequenceEventArgs(columnIndex, positionIndex));

        LayoutSegments();
      }
    }

    /// <summary>
    ///    Insert a new column segment at the specified position.
    /// </summary>
    /// <param name="text">String object holding the initial text for the new segment</param>
    /// <param name="id">Client specified ID code to be assigned to the new segment.</param>
    /// <param name="width">Initial width of the new segment using the relative metrics system</param>
    /// <param name="position">
    ///    ListHeaderSegment object indicating the insert position for the new segment.
    ///    The new segment will be inserted before the segment indicated by 'position'.
    /// </param>
    /// <exception cref="InvalidRequestException">thrown if ListHeaderSegment 'position' is not attached to the ListHeader.</exception>
    public void InsertColumn(string text, int id, float width, ListHeaderSegment position)
    {
      InsertColumn(text, id, width, GetColumnIndexFromSegment(position));
    }

    /// <summary>
    ///    Remove the specified segment from the ListHeader.
    /// </summary>
    /// <param name="segment">ListHeaderSegment object that is to be removed from the ListHeader.</param>
    /// <exception cref="InvalidRequestException">thrown if 'segment' is not attached to this ListHeader.</exception>
    public void RemoveSegment(ListHeaderSegment segment)
    {
      RemoveColumn(GetColumnIndexFromSegment(segment));
    }

    /// <summary>
    ///    Move a column segment to a new position.
    /// </summary>
    /// <param name="columnIndex">Zero based column index indicating the column segment to be moved.</param>
    /// <param name="position">
    ///    ListHeaderSegment object indicating the new position for the segment.  The segment at 'columnIndex'
    ///    will be moved behind segment 'position' (that is, segment 'columnIndex' will appear to the right of
    ///    segment 'position').
    /// </param>
    /// <exception cref="InvalidRequestException">
    ///    thrown if 'columnIndex' is out of range for this ListHeader,
    ///    or if 'position' is not attached to this ListHeader.
    ///    </exception>
    public void MoveColumn(int columnIndex, ListHeaderSegment position)
    {
      MoveColumn(columnIndex, GetColumnIndexFromSegment(position));
    }

    /// <summary>
    ///    Moves a segment into a new position.
    /// </summary>
    /// <param name="segment">ListHeaderSegment object that is to be moved.</param>
    /// <param name="positionIndex">
    ///    Zero based column index indicating the new position for the segment.
    ///    If this is greater than the current number of segments, the segment is moved to the end of the header.
    /// </param>
    /// <exception cref="InvalidRequestException">thrown if 'segment' is not attached to this ListHeader.</exception>
    public void MoveSegment(ListHeaderSegment segment, int positionIndex)
    {
      MoveColumn(GetColumnIndexFromSegment(segment), positionIndex);
    }

    /// <summary>
    ///    Move a segment to a new position.
    /// </summary>
    /// <param name="segment">ListHeaderSegment object that is to be moved.</param>
    /// <param name="position">
    ///    ListHeaderSegment object indicating the new position for the segment.  The segment 'segment'
    ///    will be moved behind segment 'position' (that is, segment 'segment' will appear to the right
    ///    of segment 'position').
    /// </param>
    /// <exception cref="InvalidRequestException">thrown if either 'segment' or 'position' are not attached to this ListHeader.</exception>
    public void MoveSegment(ListHeaderSegment segment, ListHeaderSegment position)
    {
      MoveColumn(GetColumnIndexFromSegment(segment), GetColumnIndexFromSegment(position));
    }

    /// <summary>
    ///    Set the pixel width of the specified column.
    /// </summary>
    /// <param name="columnIndex">Zero based column index of the segment whos pixel width is to be set.</param>
    /// <param name="width">
    ///    float value specifying the new pixel width to set for the ListHeaderSegment at the zero based
    ///    column index specified by 'columnIndex'.
    /// </param>
    /// <exception cref="InvalidRequestException">thrown if 'columnIndex' is out of range.</exception>
    public void SetColumnPixelWidth(int columnIndex, float width)
    {
      if (columnIndex >= ColumnCount) {
        throw new InvalidRequestException("The specified index ({0}) is out of range for this ListHeader.", columnIndex);
      }
      else {
        // TODO: Update window to support setting of dimensions in each metrics mode, then 
        // this method can be much simpler.

        ListHeaderSegment seg = segments[columnIndex];

        if (seg.MetricsMode == MetricsMode.Relative) {
          seg.Width = AbsoluteToRelativeX(width);
        }
        else {
          seg.Width = width;
        }

        LayoutSegments();

        OnSegmentSized(new WindowEventArgs(segments[columnIndex]));
      }
    }

    #endregion

    #region Implementation Members

    #region Properties
    #endregion

    #region Methods

    /// <summary>
    ///    Create initialise and return a ListHeaderSegment object, with all events subscribed and ready to use.
    /// </summary>
    /// <param name="text"></param>
    /// <param name="id"></param>
    /// <param name="width"></param>
    /// <returns></returns>
    protected ListHeaderSegment CreateInitialisedSegment(string text, int id, float width)
    {
      string uniqueName = string.Format("{0}_segment_{1}", Name, Guid.NewGuid());

      // create segment
      ListHeaderSegment newSegment = CreateNewSegment(uniqueName);

      // set up segment
      newSegment.MetricsMode = MetricsMode.Relative;
      newSegment.Size = new Size(width, 1.0f);
      newSegment.MinimumSize = AbsoluteToRelativeImpl(null, new Size(MinimumSegmentPixelWidth, 0));
      newSegment.Text = text;
      newSegment.ID = id;

      // subscribe to events we listed to
      newSegment.SegmentSized += new WindowEventHandler(SegmentSized_handler);
      newSegment.SegmentDragStop += new WindowEventHandler(SegmentDragStop_handler);
      newSegment.SegmentClicked += new WindowEventHandler(SegmentClicked_handler);
      newSegment.SplitterDoubleClicked += new WindowEventHandler(SplitterDoubleClicked_handler);
      newSegment.SegmentDragPositionChanged += new WindowEventHandler(SegmentDragPositionChanged_handler);
      
      return newSegment;
    }

    /// <summary>
    ///    Layout the attached segments
    /// </summary>
    protected void LayoutSegments()
    {
      Point position = new Point(-segmentOffset, 0.0f);

      for (int i = 0; i < ColumnCount; ++i) {
        segments[i].Position = position;
        position.x += segments[i].Width;
      }
    }

    #endregion

    #region Abstract Methods

    /// <summary>
    ///    Create a ListHeaderSegment of an appropriate sub-class type.
    /// </summary>
    /// <param name="name">Unique name for the new segment widget</param>
    /// <returns></returns>
    protected abstract ListHeaderSegment CreateNewSegment(string name);

    /// <summary>
    ///    Destroy the given ListHeaderSegment.
    /// </summary>
    /// <param name="segment">ListHeaderSegment to be destroyed.</param>
    /// <returns></returns>
    protected abstract void DestroyListSegment(ListHeaderSegment segment);

    #endregion

    #endregion

    #region Events

    #region Event Declarations
    /// <summary>
    /// The current sort column changed.
    /// </summary>
    public event WindowEventHandler SortColumnChanged;

    /// <summary>
    /// The sort direction changed.
    /// </summary>
    public event WindowEventHandler SortDirectionChanged;

    /// <summary>
    /// A segment has been sized by the user (e.Window is the segment).
    /// </summary>
    public event WindowEventHandler SegmentSized;

    /// <summary>
    /// A segment has been clicked by the user (e.Window is the segment).
    /// </summary>
    public event WindowEventHandler SegmentClicked;

    /// <summary>
    /// A segment splitter has been double-clicked.  (e.Window is the segment).
    /// </summary>
    public event WindowEventHandler SplitterDoubleClicked;

    /// <summary>
    /// The order of the segments has changed.  ('e' is a HeaderSequenceEventArgs)
    /// </summary>
    public event HeaderSequenceEventHandler SegmentSequenceChanged;

    /// <summary>
    /// A segment has been added to the header.
    /// </summary>
    public event WindowEventHandler SegmentAdded;

    /// <summary>
    /// A segment has been removed from the header.
    /// </summary>
    public event WindowEventHandler SegmentRemoved;

    /// <summary>
    /// The setting that controls user modification to sort configuration changed.
    /// </summary>
    public event WindowEventHandler SortSettingChanged;

    /// <summary>
    /// The setting that controls user drag & drop of segments changed.
    /// </summary>
    public event WindowEventHandler DragMoveSettingChanged;

    /// <summary>
    /// The setting that controls user sizing of segments changed.
    /// </summary>
    public event WindowEventHandler DragSizeSettingChanged;

    /// <summary>
    /// The rendering offset for the segments changed (header has been scrolled).
    /// </summary>
    public event WindowEventHandler ScrollOffsetChanged;

    #endregion
    
    #region Trigger Methods
    /// <summary>
    ///    Handler invoked internally when the sort column is changed.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSortColumnChanged(WindowEventArgs e) 
    {
      if(SortColumnChanged != null) {
        SortColumnChanged(this, e);
      }
    }

    /// <summary>
    ///    Handler invoked internally when the dort direction is changed.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSortDirectionChanged(WindowEventArgs e) 
    {
      if(SortDirectionChanged != null) {
        SortDirectionChanged(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when a segment size is changed.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSegmentSized(WindowEventArgs e) 
    {
      if(SegmentSized != null) {
        SegmentSized(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when a segment is clicked.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSegmentClicked(WindowEventArgs e) 
    {
      if(SegmentClicked != null) {
        SegmentClicked(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when a segment sizer is double-clicked.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSplitterDoubleClicked(WindowEventArgs e) 
    {
      if(SplitterDoubleClicked != null) {
        SplitterDoubleClicked(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when the segment order is changed.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSegmentSequenceChanged(HeaderSequenceEventArgs e) 
    {
      if(SegmentSequenceChanged != null) {
        SegmentSequenceChanged(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when a segment is added.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSegmentAdded(WindowEventArgs e) 
    {
      if(SegmentAdded != null) {
        SegmentAdded(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when a segment is removed.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSegmentRemoved(WindowEventArgs e) 
    {
      if(SegmentRemoved != null) {
        SegmentRemoved(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when user control of sorting is enabled or disabled.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnSortSettingChanged(WindowEventArgs e) 
    {
      if(SortSettingChanged != null) {
        SortSettingChanged(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when drag & drop for segments is enabled or disabled.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnDragMoveSettingChanged(WindowEventArgs e) 
    {
      if(DragMoveSettingChanged != null) {
        DragMoveSettingChanged(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when user sizing of segments is enabled or disabled.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnDragSizeSettingChanged(WindowEventArgs e) 
    {
      if(DragSizeSettingChanged != null) {
        DragSizeSettingChanged(this, e);
      }
    }
    /// <summary>
    ///    Handler invoked internally when the segment render offset (scroll position) is changed.
    /// </summary>
    /// <param name="e">Events args.</param>
    protected internal void OnScrollOffsetChanged(WindowEventArgs e) 
    {
      if(ScrollOffsetChanged != null) {
        ScrollOffsetChanged(this, e);
      }
    }
    #endregion
    
    #endregion

    #region Window Members

    #region Overridden Trigger Methods
    #endregion

    #endregion

    #region Event Handler Methods

    /// <summary>
    /// Handler method called when an attached segment is sized.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void SegmentSized_handler(object sender, WindowEventArgs e)
    {
      LayoutSegments();

      // fire event
      OnSegmentSized(e);
    }

    /// <summary>
    /// Handler called when an attached segment is dropped after being dragged
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void SegmentDragStop_handler(object sender, WindowEventArgs e)
    {
      Point mousePosition = MouseCursor.Instance.Position;

      // segment must be dropped within the ListHeader area
      if (IsHit(mousePosition)) {
        // convert mouse position to local coordinates
        mousePosition = ScreenToWindow(mousePosition);

        // consider header scroll value
        float currWidth = -ScrollOffset;

        // convert figures to pixels if needed
        if (MetricsMode == MetricsMode.Relative) {
          mousePosition = RelativeToAbsolute(mousePosition);
          currWidth = RelativeToAbsoluteX(currWidth);
        }

        // calculate column where dragged segment was dropped
        int columnIndex = 0;
        for ( ; columnIndex < ColumnCount; ++columnIndex) {
          currWidth += segments[columnIndex].AbsoluteWidth;

          if (mousePosition.x < currWidth) {
            // this is the column; break from loop
            break;
          }
        }

        // perform move operation
        MoveSegment((ListHeaderSegment)e.Window, columnIndex);
      }
    }

    /// <summary>
    /// Handler called when an attached segment is clicked.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void SegmentClicked_handler(object sender, WindowEventArgs e)
    {
      // double-check we are set to allow this
      if (sortingEnabled) {
        ListHeaderSegment seg = (ListHeaderSegment)e.Window;

        // are we changing column?
        if (sortSegment != seg) {
          sortingDirection = SortDirection.Descending;
          SortSegment = seg;
        }
        // not a different column; toggle direction instead
        else {
          SortDirection currDirection = sortSegment.SortDirection;

          // set new direction based on current value
          switch (currDirection) {
            case SortDirection.None:
              SortDirection = SortDirection.Descending;
              break;

            case SortDirection.Ascending:
              SortDirection = SortDirection.Descending;
              break;

            case SortDirection.Descending:
              SortDirection = SortDirection.Ascending;
              break;
          }
        }

        // fire event
        OnSegmentClicked(e);
      }

    }

    /// <summary>
    /// Handler called when the sizer/splitter on an attached segment is double-clicked.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void SplitterDoubleClicked_handler(object sender, WindowEventArgs e)
    {
      OnSplitterDoubleClicked(e);
    }

    /// <summary>
    /// Handler called when the drag position of a segment changes.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void SegmentDragPositionChanged_handler(object sender, WindowEventArgs e)
    {
      // get mouse position as local coordinates
      Point localMousePos = ScreenToWindow(MouseCursor.Instance.Position);

      MetricsMode metrics = MetricsMode;

      // convert mouse position back to pixels if needed
      if (metrics == MetricsMode.Relative) {
        localMousePos = RelativeToAbsolute(localMousePos);
      }

      // scroll left?
      if (localMousePos.x < 0.0f) {
        if (segmentOffset > 0.0f) {
          float adjust = ScrollSpeed;

          // convert adjustment to relative offset if needed
          if (metrics == MetricsMode.Relative) {
            adjust = AbsoluteToRelativeX(adjust);
          }

          ScrollOffset = Math.Max(0.0f, segmentOffset - adjust);
        }
      }
      // scroll right?
      else if (localMousePos.x >= AbsoluteWidth) {
        float adjust = ScrollSpeed;
        float currOffset = segmentOffset;
        float maxOffset = Math.Max(0.0f, TotalPixelExtent - AbsoluteWidth);
        
        // if needed, convert values to something we can use
        if (metrics == MetricsMode.Relative) {
          maxOffset  = AbsoluteToRelativeX(maxOffset);
          adjust    = AbsoluteToRelativeX(adjust);
          currOffset  = RelativeToAbsoluteX(currOffset);
        }

        // if we have not scrolled to the limit
        if (segmentOffset < maxOffset) {
          // scroll, but never beyond max
          ScrollOffset = Math.Min(maxOffset, segmentOffset + adjust);
        }
      }
    }

    #endregion
  }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.