/* Copyright 2012 Justin LeCheminant This file is part of WindowsFormsCalendar. indowsFormsCalendar is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. indowsFormsCalendar 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 General Public License for more details. You should have received a copy of the GNU General Public License along with indowsFormsCalendar. If not, see . */ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; namespace WindowsFormsCalendar { /// /// Represents an item of the calendar with a date and timespan /// /// /// CalendarItem provides a graphical representation of tasks within a date range. /// public class CalendarItem : CalendarSelectableElement { #region Static /// /// Compares the bounds. /// /// The r1. /// The r2. /// private static int CompareBounds( Rectangle r1, Rectangle r2 ) { return r1.Top.CompareTo( r2.Top ); } #endregion #region Events #endregion #region Fields private Rectangle[] _additionalBounds; private Color _backgroundColor; private Color _backgroundColorLighter; private Color _borderColor; private DateTime _startDate; private DateTime _endDate; private Color _foreColor; private bool _locked; private TimeSpan _duration; private Image _image; private CalendarItemImageAlign _imageAlign; private bool _isDragging; private bool _isEditing; private bool _isResizingStartDate; private bool _isResizingEndDate; private bool _isOnView; private int _minuteStartTop; private int _minuteEndTop; private HatchStyle _pattern; private Color _patternColor; private List _unitsPassing; private List _topsPassing; private object _tag; private string _text; private Font _font; #endregion #region Properties /// /// Gets or sets an array of rectangles containing bounds additional to Bounds property. /// /// /// Items may contain additional bounds because of several graphical occourences, mostly when in /// mode, due to the duration of the item; e.g. when an all day item lasts several weeks, /// one rectangle for week must be drawn to indicate the presence of the item. /// public virtual Rectangle[] AditionalBounds { get { return _additionalBounds; } set { _additionalBounds = value; } } /// /// Gets or sets the a background color for the object. If Color.Empty, renderer default's will be used. /// public Color BackgroundColor { get { return _backgroundColor; } set { _backgroundColor = value; } } /// /// Gets or sets the lighter background color of the item /// public Color BackgroundColorLighter { get { return _backgroundColorLighter; } set { _backgroundColorLighter = value; } } /// /// Gets or sets the bordercolor of the item. If Color.Empty, renderer default's will be used. /// public Color BorderColor { get { return _borderColor; } set { _borderColor = value; } } /// /// Gets the StartDate of the item. Implemented /// public override DateTime Date { get { return StartDate; } } /// /// Gets the day on the where this item ends /// /// /// This day is not necesarily the day corresponding to the day on , /// since this date can be out of the range of the current view. /// If Item is not on view date range this property will return null. /// public CalendarDay DayEnd { get { if( !IsOnViewDateRange ) { return null; } else if( IsOpenEnd ) { return Calendar.Days[Calendar.Days.Length - 1]; } else { return Calendar.FindDay( EndDate ); } } } /// /// Gets the day on the where this item starts /// /// /// This day is not necesarily the day corresponding to the day on , /// since start date can be out of the range of the current view. /// If Item is not on view date range this property will return null. /// public CalendarDay DayStart { get { if( !IsOnViewDateRange ) { return null; } else if( IsOpenStart ) { return Calendar.Days[0]; } else { return Calendar.FindDay( StartDate ); } } } /// /// Gets the duration of the item /// public TimeSpan Duration { get { if( _duration.TotalMinutes == 0 ) { _duration = EndDate.Subtract( StartDate ); } return _duration; } } /// /// Gets or sets the end time of the item /// public DateTime EndDate { get { return _endDate; } set { _endDate = value; _duration = new TimeSpan( 0, 0, 0 ); ClearPassings(); } } /// /// Gets the text of the end date /// public virtual string EndDateText { get { string date = string.Empty; string time = string.Empty; if( IsOpenEnd ) { date = EndDate.ToString( Calendar.ItemsDateFormat ); } if( ShowEndTime && !EndDate.TimeOfDay.Equals( new TimeSpan( 23, 59, 59 ) ) ) { time = EndDate.ToString( Calendar.ItemsTimeFormat ); } return string.Format( "{0} {1}", date, time ).Trim(); } } /// /// Gets or sets the forecolor of the item. If Color.Empty, renderer default's will be used. /// public Color ForeColor { get { return _foreColor; } set { _foreColor = value; } } /// /// Gets or sets an image for the item /// public Image Image { get { return _image; } set { _image = value; } } /// /// Gets or sets the alignment of the image relative to the text /// public CalendarItemImageAlign ImageAlign { get { return _imageAlign; } set { _imageAlign = value; } } /// /// Gets a value indicating if the item is being dragged /// public bool IsDragging { get { return _isDragging; } } /// /// Gets a value indicating if the item is currently being edited by the user /// public bool IsEditing { get { return _isEditing; } } /// /// Gets a value indicating if the item goes on the DayTop area of the /// public bool IsOnDayTop { get { return StartDate.Day != EndDate.AddSeconds( 1 ).Day; } } /// /// Gets a value indicating if the item is currently on view. /// /// /// The item may not be on view because of scrolling /// public bool IsOnView { get { return _isOnView; } } /// /// Gets a value indicating if the item is on the range specified by and /// public bool IsOnViewDateRange { get { //Checks for an intersection of item's dates against calendar dates DateTime fd = Calendar.Days[0].Date; DateTime ld = Calendar.Days[Calendar.Days.Length - 1].Date.Add( new TimeSpan( 23, 59, 59 ) ); DateTime sd = StartDate; DateTime ed = EndDate; return sd < ld && fd < ed; } } /// /// Gets a value indicating if the item's is before the date. /// public bool IsOpenStart { get { return StartDate.CompareTo( Calendar.Days[0].Date ) < 0; } } /// /// Gets a value indicating if the item's is aftter the date. /// public bool IsOpenEnd { get { return EndDate.CompareTo( Calendar.Days[Calendar.Days.Length - 1].Date.Add( new TimeSpan( 23, 59, 59 ) ) ) > 0; } } /// /// Gets a value indicating if item is being resized by the /// public bool IsResizingStartDate { get { return _isResizingStartDate; } } /// /// Gets a value indicating if item is being resized by the /// public bool IsResizingEndDate { get { return _isResizingEndDate; } } /// /// Gets a value indicating if this item is locked. /// /// /// When an item is locked, the user can't drag it or change it's text /// public bool Locked { get { return _locked; } set { _locked = value; } } /// /// Gets the top correspoinding to the ending minute /// public int MinuteEndTop { get { return _minuteEndTop; } } /// /// Gets the top corresponding to the starting minute /// public int MinuteStartTop { get { return _minuteStartTop; } } /// /// Gets or sets the units that this item passes by /// internal List UnitsPassing { get { return _unitsPassing; } set { _unitsPassing = value; } } /// /// Gets or sets the pattern style to use in the background of item. /// public HatchStyle Pattern { get { return _pattern; } set { _pattern = value; } } /// /// Gets or sets the pattern's color /// public Color PatternColor { get { return _patternColor; } set { _patternColor = value; } } /// /// Gets the list of DayTops that this item passes thru /// internal List TopsPassing { get { return _topsPassing; } } /// /// Gets a value indicating if the item should show the time of the /// public bool ShowStartTime { get { return IsOpenStart || ( ( this.IsOnDayTop || Calendar.DaysMode == CalendarDaysMode.Short ) && !StartDate.TimeOfDay.Equals( new TimeSpan( 0, 0, 0 ) ) ); } } /// /// Gets a value indicating if the item should show the time of the /// public virtual bool ShowEndTime { get { return ( IsOpenEnd || ( ( this.IsOnDayTop || Calendar.DaysMode == CalendarDaysMode.Short ) && !EndDate.TimeOfDay.Equals( new TimeSpan( 23, 59, 59 ) ) ) ) && !( Calendar.DaysMode == CalendarDaysMode.Short && StartDate.Date == EndDate.Date ); } } /// /// Gets the text of the start date /// public virtual string StartDateText { get { string date = string.Empty; string time = string.Empty; if( IsOpenStart ) { date = StartDate.ToString( Calendar.ItemsDateFormat ); } if( ShowStartTime && !StartDate.TimeOfDay.Equals( new TimeSpan( 0, 0, 0 ) ) ) { time = StartDate.ToString( Calendar.ItemsTimeFormat ); } return string.Format( "{0} {1}", date, time ).Trim(); } } /// /// Gets or sets the start time of the item /// public virtual DateTime StartDate { get { return _startDate; } set { _startDate = value; _duration = new TimeSpan( 0, 0, 0 ); ClearPassings(); } } /// /// Gets or sets a tag object for the item /// public object Tag { get { return _tag; } set { _tag = value; } } /// /// Gets or sets the text of the item /// public virtual string Text { get { return _text; } set { _text = value; } } /// /// Gets or sets the font. /// /// /// The font. /// public Font Font { get { return _font; } set { _font = value; } } #endregion /// /// Initializes a new instance of the class. /// /// public CalendarItem( Calendar calendar ) : base( calendar ) { _unitsPassing = new List(); _topsPassing = new List(); _backgroundColor = Color.Empty; _borderColor = Color.Empty; _foreColor = Color.Empty; _backgroundColorLighter = Color.Empty; _imageAlign = CalendarItemImageAlign.West; _font = calendar.ItemsFont; _backgroundColor = calendar.ItemsBackgroundColor; _foreColor = calendar.ItemsForeColor; } /// /// Initializes a new instance of the class. /// /// The calendar. /// The start date. /// The end date. /// The text. public CalendarItem( Calendar calendar, DateTime startDate, DateTime endDate, string text ) : this( calendar ) { StartDate = startDate; EndDate = endDate; Text = text; } /// /// Initializes a new instance of the class. /// /// The calendar. /// The start date. /// The duration. /// The text. public CalendarItem( Calendar calendar, DateTime startDate, TimeSpan duration, string text ) : this( calendar, startDate, startDate.Add( duration ), text ) { } #region Public Methods /// /// Applies color to background, border, and forecolor, from the specified color. /// /// The color. public void ApplyColor( Color color ) { BackgroundColor = color; BackgroundColorLighter = Color.FromArgb( color.R + ( 255 - color.R ) / 2 + ( 255 - color.R ) / 3, color.G + ( 255 - color.G ) / 2 + ( 255 - color.G ) / 3, color.B + ( 255 - color.B ) / 2 + ( 255 - color.B ) / 3 ); BorderColor = Color.FromArgb( Convert.ToInt32( Convert.ToSingle( color.R ) * .8f ), Convert.ToInt32( Convert.ToSingle( color.G ) * .8f ), Convert.ToInt32( Convert.ToSingle( color.B ) * .8f ) ); int avg = ( color.R + color.G + color.B ) / 3; if( avg > 255 / 2 ) { ForeColor = Color.Black; } else { ForeColor = Color.White; } } /// /// Gets all the bounds related to the item. /// /// /// /// Items that are broken on two or more weeks may have more than one rectangle bounds. /// public IEnumerable GetAllBounds() { List r = new List( AditionalBounds == null ? new Rectangle[] { } : AditionalBounds ); r.Add( Bounds ); r.Sort( CompareBounds ); return r; } /// /// Removes all specific coloring for the item. /// public void RemoveColors() { BackgroundColor = Color.Empty; ForeColor = Color.Empty; BorderColor = Color.Empty; } /// /// Gets a value indicating if the specified point is in a resize zone of /// /// The point. /// public bool ResizeStartDateZone( Point point ) { int margin = 4; List rects = new List( GetAllBounds() ); Rectangle first = rects[0]; Rectangle last = rects[rects.Count - 1]; if( IsOnDayTop || Calendar.DaysMode == CalendarDaysMode.Short ) { return Rectangle.FromLTRB( first.Left, first.Top, first.Left + margin, first.Bottom ).Contains( point ); } else { return Rectangle.FromLTRB( first.Left, first.Top, first.Right, first.Top + margin ).Contains( point ); } } /// /// Gets a value indicating if the specified point is in a resize zone of /// /// The point. /// public bool ResizeEndDateZone( Point point ) { int margin = 4; List rects = new List( GetAllBounds() ); Rectangle first = rects[0]; Rectangle last = rects[rects.Count - 1]; if( IsOnDayTop || Calendar.DaysMode == CalendarDaysMode.Short ) { return Rectangle.FromLTRB( last.Right - margin, last.Top, last.Right, last.Bottom ).Contains( point ); } else { return Rectangle.FromLTRB( last.Left, last.Bottom - margin, last.Right, last.Bottom ).Contains( point ); } } /// /// Sets the bounds of the item /// /// The rectangle. public new void SetBounds( Rectangle rectangle ) { base.SetBounds( rectangle ); } /// /// Indicates if the time of the item intersects with the provided time /// /// The start time. /// The end time. /// public bool IntersectsWith( TimeSpan startTime, TimeSpan endTime ) { Rectangle r1 = Rectangle.FromLTRB( 0, Convert.ToInt32( StartDate.TimeOfDay.TotalMinutes ), 5, Convert.ToInt32( EndDate.TimeOfDay.TotalMinutes ) ); Rectangle r2 = Rectangle.FromLTRB( 0, Convert.ToInt32( startTime.TotalMinutes ), 5, Convert.ToInt32( endTime.TotalMinutes - 1 ) ); return r1.IntersectsWith( r2 ); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return string.Format( "{0} - {1}", StartDate.ToShortTimeString(), EndDate.ToShortTimeString() ); } #endregion #region Private Methods /// /// Adds bounds for the item /// /// internal void AddBounds( Rectangle r ) { if( r.IsEmpty ) throw new ArgumentException( "r can't be empty" ); if( Bounds.IsEmpty ) { SetBounds( r ); } else { List rs = new List( AditionalBounds == null ? new Rectangle[] { } : AditionalBounds ); rs.Add( r ); AditionalBounds = rs.ToArray(); } } /// /// Adds the specified unit as a passing unit /// /// internal void AddUnitPassing( CalendarTimeScaleUnit calendarTimeScaleUnit ) { if( !UnitsPassing.Contains( calendarTimeScaleUnit ) ) { UnitsPassing.Add( calendarTimeScaleUnit ); } } /// /// Adds the specified as a passing one /// /// internal void AddTopPassing( CalendarDayTop top ) { if( !TopsPassing.Contains( top ) ) { TopsPassing.Add( top ); } } /// /// Clears the item's existance off passing units and tops /// internal void ClearPassings() { foreach( CalendarTimeScaleUnit unit in UnitsPassing ) { unit.ClearItemExistance( this ); } UnitsPassing.Clear(); TopsPassing.Clear(); } /// /// Clears all bounds of the item /// internal void ClearBounds() { SetBounds( Rectangle.Empty ); AditionalBounds = new Rectangle[] { }; SetMinuteStartTop( 0 ); SetMinuteEndTop( 0 ); } /// /// It pushes the left and the right to the center of the item /// to visually indicate start and end time /// internal void FirstAndLastRectangleGapping() { if( !IsOpenStart ) SetBounds( Rectangle.FromLTRB( Bounds.Left + Calendar.Renderer.ItemsPadding, Bounds.Top, Bounds.Right, Bounds.Bottom ) ); if( !IsOpenEnd ) { if( AditionalBounds != null && AditionalBounds.Length > 0 ) { Rectangle r = AditionalBounds[AditionalBounds.Length - 1]; AditionalBounds[AditionalBounds.Length - 1] = Rectangle.FromLTRB( r.Left, r.Top, r.Right - Calendar.Renderer.ItemsPadding, r.Bottom ); } else { Rectangle r = Bounds; SetBounds( Rectangle.FromLTRB( r.Left, r.Top, r.Right - Calendar.Renderer.ItemsPadding, r.Bottom ) ); } } } /// /// Sets the value of the IsDragging property /// /// Value indicating if the item is currently being dragged internal void SetIsDragging( bool dragging ) { _isDragging = dragging; } /// /// Sets the value of the property /// /// Value indicating if user is currently being editing internal void SetIsEditing( bool editing ) { _isEditing = editing; } /// /// Sets the value of the property /// /// Indicates if the item is currently on view internal void SetIsOnView( bool onView ) { _isOnView = onView; } /// /// Sets the value of the property /// /// internal void SetIsResizingStartDate( bool resizing ) { _isResizingStartDate = resizing; } /// /// Sets the value of the property /// /// internal void SetIsResizingEndDate( bool resizing ) { _isResizingEndDate = resizing; } /// /// Sets the value of the property /// /// internal void SetMinuteStartTop( int top ) { _minuteStartTop = top; } /// /// Sets the value of the property /// /// internal void SetMinuteEndTop( int top ) { _minuteEndTop = top; } #endregion } }