#if FRAMEWORK20 using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using DevComponents.DotNetBar.ScrollBar; using DevComponents.Schedule.Model; namespace DevComponents.DotNetBar.Schedule { public class TimeLineView : BaseView { #region Static variables static private AppointmentColor _appointmentColor = new AppointmentColor(); #endregion #region Private variables private CalendarWeekDayColor _ViewColor = // View display color table new CalendarWeekDayColor(eCalendarColor.Automatic); private List _CalendarItems = new List(); private Point _LastMovePoint; // Last mouse move Point private Point _LastPointOffset; // Last Point offset private Rectangle _LastBounds; // MouseDown item bounds private int _SelectedColStart; // Column start selection private int _SelectedColEnd; // Column end selection private Timer _ScrollViewTimer; // Timer used to implement auto view scrolling private int _ScrollDwell; // Scroll dwell (pause, throttle) counter private Color _WorkColor; // Cached Brush colors private Color _OffWorkColor; private Color _SelectedColor; private Brush _WorkBrush; // Cached Brushes private Brush _OffWorkBrush; private Brush _SelectedBrush; private int _HScrollPos; // Horizontal scroll position private List _CondensedColList; private List _CollateLines = new List(); private bool _ModelReloaded = true; #endregion public TimeLineView(CalendarView calendarView, eCalendarView eCalendarView) : base(calendarView, eCalendarView) { // Set our non-client drawing info and CalendarColor NClientData = new NonClientData( eTabOrientation.Vertical, (int)eCalendarWeekDayPart.OwnerTabBorder, (int)eCalendarWeekDayPart.OwnerTabForeground, (int)eCalendarWeekDayPart.OwnerTabBackground, (int)eCalendarWeekDayPart.OwnerTabContentBackground, (int)eCalendarWeekDayPart.OwnerTabSelectedForeground, (int)eCalendarWeekDayPart.OwnerTabSelectedBackground); CalendarColorTable = _ViewColor; // Hook onto our events HookEvents(true); if (calendarView.TimeLineHScrollPanel.ScrollBar != null) { _HScrollPos = -calendarView.TimeLineHScrollPanel.ScrollBar.Value * calendarView.TimeLineColumnWidth; } } #region Public properties #region CalendarItems /// /// Gets array of CalendarItems /// public List CalendarItems { get { return (_CalendarItems); } } #endregion #region StartDate /// /// Start date - readonly /// public new DateTime StartDate { get { return (base.StartDate); } internal set { base.StartDate = value; } } #endregion #region EndDate /// /// End date - readonly /// public new DateTime EndDate { get { return (base.EndDate); } internal set { base.EndDate = value; } } #endregion #region ColumnWidth /// /// Gets the ColumnWidth /// public int ColumnWidth { get { return (CalendarView.TimeLineColumnWidth); } } #endregion #region BaseInterval /// /// Gets the BaseInterval (interval in total minutes) /// public double BaseInterval { get { return (CalendarView.BaseInterval); } } #endregion #region TimeLineColumnCount /// /// Gets the number of Columns /// public int TimeLineColumnCount { get { return (CalendarView.TimeLineColumnCount); } } #endregion #region MinAppointmentWidth /// /// Gets the MinAppointmentWidth /// public int MinAppointmentWidth { get { return (CalendarView.TimeLineMinAppointmentWidth); } } #endregion #endregion #region Private properties #region ShowCondensed /// /// Gets the CondensedView visibility state /// private bool ShowCondensed { get { if (CalendarView.TimeLineCondensedViewVisibility == eCondensedViewVisibility.Hidden) return (false); if (CalendarView.TimeLineCondensedViewHeight < 10) return (false); if (CalendarView.TimeLineCondensedViewVisibility == eCondensedViewVisibility.AllResources) return (true); return (CalendarView.SelectedOwnerIndex == DisplayedOwnerKeyIndex); } } #endregion #region Cached Brushes #region WorkBrush /// /// Gets and sets the Work time brush /// private Brush WorkBrush { get { Color color = _ViewColor.GetColor( (int)eCalendarWeekDayPart.DayWorkHoursBackground); if (_WorkColor != color) { _WorkColor = color; WorkBrush = new SolidBrush(color); } return (_WorkBrush); } set { if (_WorkBrush != value) { if (_WorkBrush != null) _WorkBrush.Dispose(); _WorkBrush = value; } } } #endregion #region OffWorkBrush /// /// Gets and sets the Off-hours work time brush /// private Brush OffWorkBrush { get { Color color = _ViewColor.GetColor( (int)eCalendarWeekDayPart.DayOffWorkHoursBackground); if (_OffWorkColor != color) { _OffWorkColor = color; OffWorkBrush = new SolidBrush(color); } return (_OffWorkBrush); } set { if (_OffWorkBrush != value) { if (_OffWorkBrush != null) _OffWorkBrush.Dispose(); _OffWorkBrush = value; } } } #endregion #region SelectedBrush /// /// Gets and sets the selected brush /// private Brush SelectedBrush { get { Color color = _ViewColor.GetColor( (int)eCalendarWeekDayPart.SelectionBackground); if (_SelectedColor != color) { _SelectedColor = color; SelectedBrush = new SolidBrush(color); } return (_SelectedBrush); } set { if (_SelectedBrush != value) { if (_SelectedBrush != null) _SelectedBrush.Dispose(); _SelectedBrush = value; } } } #endregion #endregion #endregion #region Internal Properties /// /// Gets the first visible timeline column /// internal int FirstVisibleColumn { get { return (-_HScrollPos / ColumnWidth); } } /// /// Gets the condensed time line height /// internal int CondensedLineHeight { get { return (CalendarView.TimeLineCondensedViewHeight); } } /// /// Gets and sets the model reload state /// internal bool ModelReloaded { get { return (_ModelReloaded); } set { _ModelReloaded = value; } } #endregion #region Hook / Unhook Events /// /// Routine hooks all necessary events for this control /// /// True to hook, false to unhook private void HookEvents(bool hook) { if (hook == true) { CalendarView.SelectedViewChanged += SelectedViewChanged; CalendarView.SelectedOwnerChanged += SelectedOwnerChanged; CalendarView.TimeLineViewStartDateChanged += TimeLineViewStartDateChanged; CalendarView.TimeLineViewEndDateChanged += TimeLineViewEndDateChanged; CalendarView.TimeLineIntervalChanged += TimeLineIntervalChanged; CalendarView.TimeLineIntervalPeriodChanged += TimeLineIntervalPeriodChanged; CalendarView.TimeLineCondensedViewVisibilityChanged += TimeLineCondensedViewVisibilityChanged; CalendarView.TimeLineHScrollPanel.ScrollPanelChanged += ScrollPanelChanged; } else { CalendarView.SelectedViewChanged -= SelectedViewChanged; CalendarView.SelectedOwnerChanged -= SelectedOwnerChanged; CalendarView.TimeLineViewStartDateChanged -= TimeLineViewStartDateChanged; CalendarView.TimeLineViewEndDateChanged -= TimeLineViewEndDateChanged; CalendarView.TimeLineIntervalChanged -= TimeLineIntervalChanged; CalendarView.TimeLineIntervalPeriodChanged -= TimeLineIntervalPeriodChanged; CalendarView.TimeLineCondensedViewVisibilityChanged -= TimeLineCondensedViewVisibilityChanged; CalendarView.TimeLineHScrollPanel.ScrollPanelChanged -= ScrollPanelChanged; } } #endregion #region Event handling routines #region SelectedViewChanged /// /// Processes view changes /// /// object /// SelectedViewEventArgs void SelectedViewChanged(object sender, SelectedViewEventArgs e) { // Update our IsViewSelected state IsViewSelected = (e.NewValue == ECalendarView); if (IsViewSelected == true) { AutoSyncViewDate(e.OldValue); UpdateDateSelection(); } else { ResetView(); } } #endregion #region SelectedOwnerChanged void SelectedOwnerChanged(object sender, SelectedOwnerChangedEventArgs e) { if (CalendarView.TimeLineStretchRowHeight == true) { if (CalendarView.TimeLineCondensedViewVisibility == eCondensedViewVisibility.SelectedResource) NeedRecalcLayout = true; } } #endregion #region TimeLineViewStartDateChanged /// /// Processes StartDate changes /// /// /// void TimeLineViewStartDateChanged(object sender, DateChangeEventArgs e) { StartDate = e.NewValue; } #endregion #region TimeLineViewEndDateChanged /// /// Processes EndDate changes /// /// /// void TimeLineViewEndDateChanged(object sender, DateChangeEventArgs e) { EndDate = e.NewValue; } #endregion #region TimeLineIntervalPeriodChanged /// /// Handles IntervalPeriodChange notification /// /// /// void TimeLineIntervalPeriodChanged(object sender, TimeLineIntervalPeriodChangedEventArgs e) { NeedRecalcSize = true; NeedRecalcLayout = true; } #endregion #region TimeLineIntervalChanged /// /// Handles IntervalChange notification /// /// /// void TimeLineIntervalChanged(object sender, TimeLineIntervalChangedEventArgs e) { NeedRecalcSize = true; NeedRecalcLayout = true; } #endregion #region TimeLineCondensedViewVisibilityChanged void TimeLineCondensedViewVisibilityChanged( object sender, TimeLineCondensedViewVisibilityChangedEventArgs e) { if (CalendarView.TimeLineStretchRowHeight == true) NeedRecalcLayout = true; } #endregion #region ScrollPanelChanged /// /// Handles ScrollPanel change notification /// /// /// void ScrollPanelChanged(object sender, EventArgs e) { TimeLineHScrollPanel panel = sender as TimeLineHScrollPanel; if (panel != null) { HScrollBarAdv hScrollBar = panel.ScrollBar; _HScrollPos = -hScrollBar.Value * CalendarView.TimeLineColumnWidth; // Redraw our view if (Displayed == true) { NeedRecalcLayout = true; InvalidateRect(true); Refresh(); } } } #endregion #endregion #region GetViewAreaFromPoint /// /// Gets the view area under the given mouse /// point (tab, header, content, etc) /// /// Point /// eViewArea public override eViewArea GetViewAreaFromPoint(Point pt) { if (Bounds.Contains(pt) == true) { if (pt.X >= ClientRect.X) { // CondensedView if (ShowCondensed == true) { if (GetCondensedRect().Contains(pt) == true) return (eViewArea.InCondensedView); } return (eViewArea.InContent); } return (base.GetViewAreaFromPoint(pt)); } return (eViewArea.NotInView); } #endregion #region GetDateSelectionFromPoint /// /// Gets the date selection from the given point. The startDate /// and endDate will vary based upon the view type /// /// Point in question /// out start date /// out end date /// True if a valid selection exists /// at the given point public override bool GetDateSelectionFromPoint( Point pt, out DateTime startDate, out DateTime endDate) { base.GetDateSelectionFromPoint(pt, out startDate, out endDate); int col; if (GetPointItem(pt, out col, true) == true) { startDate = CalendarView.TimeLineAddInterval(StartDate, col); endDate = CalendarView.TimeLineAddInterval(startDate, 1); return (true); } return (false); } #endregion #region SetSelectedItem /// /// Handles selected item changes /// /// CalendarItem /// EventArgs public void ItemIsSelectedChanged(object sender, EventArgs e) { CalendarItem ci = sender as CalendarItem; if (ci != null) { if (ci.IsSelected == true) { if (SelectedItem != null && ci != SelectedItem) SelectedItem.IsSelected = false; SelectedItem = ci; } else { if (ci == SelectedItem) SelectedItem = null; } // Make sure the selection is reflected // in the condensed view (if present) if (ShowCondensed == true) InvalidateRect(GetCondensedRect()); } } /// /// Sets the current selected item /// /// Previous CalendarItem /// New CalendarItem /// New selected CalendarItem protected override CalendarItem SetSelectedItem(CalendarItem pci, CalendarItem nci) { if (nci != null) nci.IsSelected = true; else if (pci != null) pci.IsSelected = false; return (nci); } #endregion #region UpdateDateSelection /// /// Updates our slice selection range to reflect /// the given date selection start and end values /// protected override void UpdateDateSelection() { if (IsViewSelected == true) { // Get the new absolute slice selection range int colStart = GetDateCol(DateSelectionStart); int colEnd = GetDateCol(DateSelectionEnd); // Limit our range to only those columns // that are visible on the screen int c1 = -_HScrollPos / ColumnWidth; int c2 = c1 + ClientRect.Width / ColumnWidth + 1; if (c2 > c1) { ProcessSelRange(colStart, colEnd, c1, c2); // Save our new selection range _SelectedColStart = colStart; _SelectedColEnd = colEnd; } if (ShowCondensed == true) { Rectangle r = GetCondensedRect(); this.InvalidateRect(r); } } } /// /// Processes the selection time column range /// /// Column range start /// Column range end /// Column start limit /// Column end limit private void ProcessSelRange(int colStart, int colEnd, int c1, int c2) { bool[] oldSelected = SelectedColumns(c1, c2, _SelectedColStart, _SelectedColEnd); bool[] newSelected = SelectedColumns(c1, c2, colStart, colEnd); // Invalidate those slices whose // selection status changed Rectangle r = GetColRect(c1); if (r.Width > 0 && r.Height > 0) { for (int i = 0; i < c2 - c1; i++) { if (oldSelected[i] != newSelected[i]) InvalidateRect(r); r.X += ColumnWidth; } } } /// /// Gets an array of column selection values /// over the given range of columns /// /// Column start limit /// Column end limit /// Slice range start /// Slice range end /// Array of selection values private bool[] SelectedColumns(int c1, int c2, int colStart, int colEnd) { // Calculate our number of entries and // allocate our IsSelected array accordingly int n = c2 - c1; bool[] sel = new bool[n + 1]; // Loop through the range of entries determining if // the specific col is within the selection range for (int i = 0; i < n; i++) sel[i] = (c1 + i >= colStart && c1 + i < colEnd); // Return the array to the caller return (sel); } #endregion #region RecalcSize routines /// /// Performs NeedRecalcSize requests /// public override void RecalcSize() { base.RecalcSize(); if (IsViewSelected == true) { // Normalize our start and end dates DateTime startDate; DateTime endDate; NormalizeDates(out startDate, out endDate); // Update our Model connection view, // CalendarItems, and DateSelection UpdateView(); UpdateDateSelection(); } NeedRecalcLayout = false; } #region NormalizeDates /// /// Normalizes the user specified start and end dates /// /// [out] Normalized start date /// [out] Normalized end date protected virtual void NormalizeDates(out DateTime startDate, out DateTime endDate) { startDate = this.StartDate; endDate = this.EndDate; } #endregion #region UpdateView /// /// Updates our connection model view /// private void UpdateView() { // Make sure we have a model connection if (Connector == null) { if (CalendarModel != null) Connector = new ModelTimeLineViewConnector(CalendarModel, this); } else { // The timeline range could have changed, so let // the connection refresh the data if needed ((ModelTimeLineViewConnector)Connector).RefreshData(false); } // We have a connection, so update our CalendarItems if (Connector != null) { UpdateCalendarItems(); if (ShowCondensed == true) UpdateCondensedColumnList(); CalendarView.DoViewLoadComplete(this); } } #endregion #region ResetView /// /// Disconnects and resets the Model connection /// internal override void ResetView() { _ModelReloaded = true; base.ResetView(); } #endregion #region UpdateCalendarItems /// /// Updates our CalendarItems list /// private void UpdateCalendarItems() { if (NeedRecalcLayout == true) { _CollateLines.Clear(); if (_CalendarItems.Count > 0) { List items = SortCalendarItems(_CalendarItems); if (CalendarView.HasTimeLineGetRowCollateIdCallout) { SortedDictionary> ditems = CollateCalendarItems(items); int dy = 0; foreach (KeyValuePair> kvp in ditems) { ColumnList colList = new ColumnList(); for (int i = 0; i < kvp.Value.Count; i++) colList.AddColumnSlot(kvp.Value[i], 0); colList.CountColumns(); dy = CalcAppointmentBounds(colList, dy, true); _CollateLines.Add(dy); } } else { ColumnList colList = new ColumnList(); for (int i = 0; i < items.Count; i++) colList.AddColumnSlot(items[i], 0); colList.CountColumns(); CalcAppointmentBounds(colList, 0, false); } } NeedRecalcLayout = false; InvalidateRect(); } } #region CollateCalendarItems private SortedDictionary> CollateCalendarItems(List items) { SortedDictionary> citems = new SortedDictionary>(); for (int i = 0; i < items.Count; i++) { if (items[i].CollateId < 0) { TimeLineGetRowCollateIdEventArgs e = new TimeLineGetRowCollateIdEventArgs(items[i]); CalendarView.DoTimeLineGetRowCollateId(e); items[i].CollateId = e.CollateId; } int collateId = items[i].CollateId; if (citems.ContainsKey(collateId) == false) citems.Add(collateId, new List()); citems[collateId].Add(items[i]); } return (citems); } #endregion #region SortCalendarItems /// /// Sorts the provided CalendarItems /// /// Sorted CalendarItems private List SortCalendarItems(IEnumerable clist) { List items = new List(); items.AddRange(clist); if (items.Count > 0) { items.Sort( delegate(CalendarItem c1, CalendarItem c2) { if (c1.StartTime > c2.StartTime) return (1); if (c1.StartTime < c2.StartTime) return (-1); if (c1.EndTime < c2.EndTime) return (1); if (c1.EndTime > c2.EndTime) return (-1); int result = 0; CalendarView.DoDetailSortEvent(this, c1, c2, ref result); return (result); } ); } return (items); } #endregion #region CalcAppointmentBounds /// /// Calculates normal appointment bounds /// /// Accumulated ColumnList /// /// private int CalcAppointmentBounds(ColumnList colList, int dy, bool collate) { if (colList.SList.Count > 0) { // If we are stretching the rows height, then calculate // the default row height and the row spread, to evenly // distribute the fill remainder bool stretchRow = (collate == false && CalendarView.TimeLineStretchRowHeight == true); int height = GetColRect(0).Height; int rowSpread; int rowHeight = GetColumnRowHeight(colList.SList.Count, stretchRow, height, out rowSpread) - 2; for (int i = 0; i < colList.SList.Count; i++) { int maxRowHeight = 0; for (int j = 0; j < colList.SList[i].Count; j++) { SlotItem sitem = colList.SList[i][j]; CalendarItem item = sitem.CItem; Rectangle r = new Rectangle(); r.Y = ClientRect.Y + dy; r.Height = rowHeight; if (stretchRow == false) r.Height = GetRowHeight(item, rowHeight); TimeSpan ts1 = item.StartTime - StartDate; TimeSpan ts2 = item.EndTime - item.StartTime; int pos = (int) ((ts1.TotalMinutes * ColumnWidth) / BaseInterval); int width = (int) ((ts2.TotalMinutes * ColumnWidth) / BaseInterval); if (CalendarView.TimeLinePeriod == eTimeLinePeriod.Years) { int years = item.StartTime.Year - StartDate.Year; pos = (years * ColumnWidth) / CalendarView.TimeLineInterval; } if (width < MinAppointmentWidth) width = MinAppointmentWidth; if (width < 4) width = 4; r.X = ClientRect.X + pos + _HScrollPos; r.Width = width; Rectangle p = ClientRect; int hpad = CalendarView.TimeLineHorizontalPadding; if (r.Left >= p.Left) { r.X += hpad; r.Width -= hpad; } if (r.Right <= p.Right) r.Width -= hpad; if (i < rowSpread) r.Height++; if (item.StartTime != item.EndTime) { if (r.Height + 2 > maxRowHeight) maxRowHeight = r.Height + 2; } if (stretchRow == true) { if (CalendarView.TimeLineStretchRowHeightMode == TimeLineStretchRowHeightMode.Full) { if (sitem.SList == null) { r.Height = height - dy - 2; } else { int n = GetColumnCount(sitem); r.Height = (rowHeight + 1) * n - 1; for (int k = 0; k < n; k++) { if (i + k >= rowSpread) break; r.Height++; } } } } r.Height++; item.Bounds = r; // Set it's display state item.Displayed = r.IntersectsWith(ClientRect); } dy += maxRowHeight; } } return (dy); } #region GetColumnCount private int GetColumnCount(SlotItem sitem) { int column = int.MaxValue; for (int i = 0; i < sitem.SList.Count; i++) { if (sitem.SList[i].Column < column) column = sitem.SList[i].Column; } return (column - sitem.Column); } #endregion #region GetColumnRowHeight private int GetColumnRowHeight(int count, bool stretchRow, int height, out int rowSpread) { rowSpread = 0; int rowHeight = AppointmentHeight; if (stretchRow == true) { rowHeight = height / count; if (rowHeight < AppointmentHeight) rowHeight = AppointmentHeight; else rowSpread = height - (count * rowHeight); } return (rowHeight); } #endregion #region GetRowHeight /// /// Get the RowHeight for the given CalendarItem /// /// CalendarItem /// Calculated height /// private int GetRowHeight(CalendarItem item, int height) { TimeLineGetRowHeightEventArgs e = new TimeLineGetRowHeightEventArgs(item, height); CalendarView.DoTimeLineGetRowHeight(e); return (e.Height); } #endregion #endregion #endregion #region UpdateCondensedColumnList /// /// Updates the condensed view column list /// internal void UpdateCondensedColumnList() { if (_ModelReloaded == true && Connector != null) { _CondensedColList = new List(); List items = new List(); // Get our appointment collection GetCondensedAppts(items); GetCondensedCustomItems(items); // If we have any items, sort them and // create a corresponding ColumnList if (items.Count > 0) { items = SortCalendarItems(items); if (CalendarView.HasTimeLineGetRowCollateIdCallout) { SortedDictionary> ditems = CollateCalendarItems(items); foreach (KeyValuePair> kvp in ditems) { ColumnList colList = new ColumnList(); for (int i = 0; i < kvp.Value.Count; i++) colList.AddColumnSlot(kvp.Value[i], 0); colList.CountColumns(); _CondensedColList.Add(colList); } } else { _CondensedColList.Add(new ColumnList()); for (int i = 0; i < items.Count; i++) _CondensedColList[0].AddColumnSlot(items[i], 0); _CondensedColList[0].CountColumns(); } } _ModelReloaded = false; } } #region GetCondensedAppts private void GetCondensedAppts(List items) { List appts = ((ModelTimeLineViewConnector)Connector).ListAppts; if (appts != null && appts.Count > 0) { int n = Math.Min(appts.Count, 500); n = Math.Max(appts.Count / n, 1); for (int i = 0; i < appts.Count; i += n) { if (IsAppointmentVisible(appts[i]) == true) { CalendarItem ci = new CalendarItem(); ci.StartTime = appts[i].StartTime; ci.EndTime = appts[i].EndTime; ci.ModelItem = appts[i]; items.Add(ci); } } } } /// /// Determines if an appointment is visible /// for the given DisplayOwner /// /// /// private bool IsAppointmentVisible(Appointment app) { if (string.IsNullOrEmpty(DisplayedOwnerKey)) return (true); return (app.OwnerKey == DisplayedOwnerKey); } #endregion #region GetCondensedCustomItems private void GetCondensedCustomItems(List items) { for (int i = 0; i < CalendarView.CustomItems.Count; i++) { CustomCalendarItem item = CalendarView.CustomItems[i]; if (IsCustomItemVisible(item) == true && (item.StartTime < EndDate && item.EndTime > StartDate)) { CalendarItem ci = new CalendarItem(); ci.StartTime = item.StartTime; ci.EndTime = item.EndTime; ci.ModelItem = item; items.Add(ci); } } } private bool IsCustomItemVisible(CustomCalendarItem item) { if (string.IsNullOrEmpty(Connector.DisplayOwnerKey)) return (true); return (item.OwnerKey.Equals(Connector.DisplayOwnerKey)); } #endregion #endregion #endregion #region Paint processing #region Root paint code /// /// Paint processing /// /// ItemPaintArgs public override void Paint(ItemPaintArgs e) { Graphics g = e.Graphics; // Set our current color table _ViewColor.SetColorTable(); // calculate our Column count and range int colStart, colEnd; if (GetColRange(e, out colStart, out colEnd) > 0) { Region rgnSave = g.Clip; g.SetClip(ClientRect, CombineMode.Intersect); // Draw the TimeLine DrawTimeLine(g, colStart, colEnd); DrawTimeIndicators(g, colStart, colEnd, eTimeIndicatorLevel.Bottom); if (Connector != null && ShowCondensed == true) DrawCondensedLine(g); // If we have a model connection then // draw our appointments if (Connector != null && Connector.IsConnected) { Rectangle r = ClientRect; r.X += 1; if (ShowCondensed == true) { Rectangle r2 = GetCondensedRect(); if (r.Bottom > r2.Top) r.Height -= (r.Bottom - r2.Top); } g.SetClip(r, CombineMode.Intersect); DrawAppointments(e); } DrawTimeIndicators(g, colStart, colEnd, eTimeIndicatorLevel.Top); g.Clip = rgnSave; // Update our pos window UpdatePosWin(ClientRect); } // Let the base painting take place base.Paint(e); } #endregion #region DrawTimeLine /// /// Initiates the drawing of the TimeLine /// /// Graphics /// Starting column /// Ending column private void DrawTimeLine(Graphics g, int colStart, int colEnd) { int n = colEnd - colStart; eSlotDisplayState[] states = new eSlotDisplayState[n + 1]; for (int i = 0; i <= n; i++) states[i] = GetSlotState(colStart + i); DrawContent(g, colStart, colEnd, states); DrawBorder(g, colStart, colEnd, states); } #region DrawContent /// /// Draws the content area of the TimeLine /// /// /// Starting column /// Ending column /// private void DrawContent(Graphics g, int colStart, int colEnd, eSlotDisplayState[] states) { Rectangle r = GetColRect(colStart); if (CalendarView.HasTimeLineSlotBackgroundCallout == true) { for (int i = colStart; i <= colEnd; i++) { int n = i - colStart; DateTime startTime = StartDate.AddMinutes(i * BaseInterval); DateTime endTime = startTime.AddMinutes(BaseInterval); if (CalendarView.DoTimeLineViewPreRenderSlotBackground( g, this, startTime, endTime, r, ref states[n]) == false) { g.FillRectangle(GetContentBrush(states[n]), r); DrawCollateLines(g, r); CalendarView.DoTimeLineViewPostRenderSlotBackground( g, this, startTime, endTime, r, states[n]); } r.X += ColumnWidth; } } else { Rectangle t = r; for (int i = 0; i <= colEnd - colStart; i++) { g.FillRectangle(GetContentBrush(states[i]), t); t.X += ColumnWidth; } r.Width += (t.Right - r.Right); DrawCollateLines(g, r); } } #region DrawCollateLines private void DrawCollateLines(Graphics g, Rectangle r) { if (CalendarView.TimeLineShowCollateLines == true && _CollateLines.Count > 1) { using (Pen pen = new Pen( _ViewColor.GetColor((int) eCalendarWeekDayPart.DayHalfHourBorder))) { for (int i = 0; i < _CollateLines.Count - 1; i++) { int y = _CollateLines[i]; g.DrawLine(pen, r.X, r.Y + y, r.Right, r.Y + y); } } } } #endregion #region GetSlotState /// /// GetSlotState /// /// /// private eSlotDisplayState GetSlotState(int col) { eSlotDisplayState state = eSlotDisplayState.None; if (DisplayedOwnerKeyIndex == CalendarView.SelectedOwnerIndex) { if (col >= _SelectedColStart && col < _SelectedColEnd) state |= eSlotDisplayState.Selected; } DateTime date = StartDate.AddMinutes(col * CalendarView.BaseInterval); WorkTime workTime = new WorkTime(date.Hour, date.Minute); if (IsWorkTime((int)date.DayOfWeek, workTime) == true) state |= eSlotDisplayState.Work; return (state); } #endregion #region GetContentBrush /// /// Gets the background content brush /// for the given time slice /// /// Background brush private Brush GetContentBrush(eSlotDisplayState state) { if ((state & eSlotDisplayState.Selected) == eSlotDisplayState.Selected) return (SelectedBrush); if ((state & eSlotDisplayState.Work) == eSlotDisplayState.Work) return (WorkBrush); return (OffWorkBrush); } #endregion #region IsWorkTime /// /// Determines if the given time is tagged as a "Work time" /// /// Day of week /// WorkTime to test /// true if specified "time" is a Work time private bool IsWorkTime(int day, WorkTime time) { ModelTimeLineViewConnector tlc = (ModelTimeLineViewConnector)Connector; WorkTime workStartTime = tlc.DayInfo[day].WorkStartTime; WorkTime workEndTime = tlc.DayInfo[day].WorkEndTime; return (time >= workStartTime && time < workEndTime); } #endregion #endregion #region DrawBorder /// /// Draws the TimeLine border /// /// /// Starting column /// Ending column /// private void DrawBorder(Graphics g, int colStart, int colEnd, eSlotDisplayState[] states) { Rectangle r = GetColRect(colStart); // Draw the vertical borders if (CalendarView.TimeLinePeriod == eTimeLinePeriod.Minutes) DrawHalfHourBorders(g, colStart, colEnd, r, states); else DrawHourBorders(g, colStart, colEnd, r, states); // Now draw the horizontal border r.Width = (colEnd - colStart + 1) * ColumnWidth; r.Height = ClientRect.Height; Color color = _ViewColor.GetColor((int)eCalendarWeekDayPart.DayViewBorder); if (CalendarView.DoTimeLineViewRenderViewBorder(g, this, colStart, colEnd, r, color) == false) { using (Pen pen1 = new Pen(color)) { Point pt1 = new Point(r.X, r.Bottom - 1); Point pt2 = new Point(r.Right - 1, pt1.Y); g.DrawLine(pen1, pt1, pt2); if (CalendarView.TimeLineShowPeriodHeader == false && CalendarView.TimeLineShowIntervalHeader == false) { pt1 = new Point(r.X, r.Top); pt2 = new Point(r.Right - 1, pt1.Y); g.DrawLine(pen1, pt1, pt2); } } } } #region DrawHalfHourBorders /// /// DrawHalfHourBorders /// /// /// /// /// /// private void DrawHalfHourBorders(Graphics g, int colStart, int colEnd, Rectangle bounds, eSlotDisplayState[] states) { // Draw the vertical hour and half hour borders using (Pen pen1 = new Pen( _ViewColor.GetColor((int)eCalendarWeekDayPart.DayHourBorder))) { using (Pen pen2 = new Pen( _ViewColor.GetColor((int)eCalendarWeekDayPart.DayHalfHourBorder))) { Point pt1 = new Point(bounds.X, ClientRect.Y); Point pt2 = new Point(bounds.X, ClientRect.Bottom - 1); int n = 60 / CalendarView.TimeLineInterval; int start = -_HScrollPos / ColumnWidth; for (int i = colStart; i <= colEnd; i++) { bool isHour = (i == start || (i % n) == 0); Pen pen = (isHour ? pen1 : pen2); if (CalendarView.DoTimeLineViewRenderSlotBorder( g, this, i, isHour, states[i - colStart], pt1, pt2, pen) == false) { g.DrawLine((isHour ? pen1 : pen2), pt1, pt2); } pt1.X = pt2.X = (pt1.X + ColumnWidth); } } } } #endregion #region DrawHourBorders /// /// DrawHourBorders /// /// /// /// /// /// private void DrawHourBorders(Graphics g, int colStart, int colEnd, Rectangle bounds, eSlotDisplayState[] states) { // Draw the vertical hour borders using (Pen pen = new Pen( _ViewColor.GetColor((int)eCalendarWeekDayPart.DayHourBorder))) { Point pt1 = new Point(bounds.X, ClientRect.Y); Point pt2 = new Point(bounds.X, ClientRect.Bottom - 1); for (int i = colStart; i <= colEnd; i++) { if (CalendarView.DoTimeLineViewRenderSlotBorder( g, this, i, true, states[i - colStart], pt1, pt2, pen) == false) { g.DrawLine(pen, pt1, pt2); } pt1.X = pt2.X = (pt1.X + ColumnWidth); } } } #endregion #endregion #region DrawTimeIndicators #region DrawTimeIndicators /// /// Draws view TimeIndicators /// /// /// /// private void DrawTimeIndicators(Graphics g, int colStart, int colEnd, eTimeIndicatorLevel level) { DateTime start = CalendarView.TimeLineAddInterval(StartDate, colStart); DateTime end = CalendarView.TimeLineAddInterval(StartDate, colEnd); Rectangle r = Rectangle.Union(GetColRect(colStart), GetColRect(colEnd)); for (int i = 0; i < CalendarView.TimeIndicators.Count; i++) { TimeIndicator ti = CalendarView.TimeIndicators[i]; if (ti.IndicatorLevel == level) { if (ti.IndicatorArea == eTimeIndicatorArea.All || ti.IndicatorArea == eTimeIndicatorArea.Content) { if (ti.IsVisible(CalendarView, this)) { DateTime time = ti.IndicatorDisplayTime; if (time >= start && time < end) DrawTimeIndicator(g, start, r, ti); } } } } } #endregion #region DrawTimeIndicator #region DrawTimeIndicator /// /// Draws individual view TimeIndicator /// /// /// /// /// private void DrawTimeIndicator(Graphics g, DateTime startDate, Rectangle sRect, TimeIndicator ti) { Rectangle r = GetIndicatorRect(ti, startDate, sRect); if (r.IntersectsWith(sRect) == true) { if (r.Width > 0) { ColorDef cdef = GetIndicatorColor(ti); if (cdef != null) { float angle = cdef.Angle - 90; using (Brush br = _ViewColor.BrushPart(cdef, r, angle)) { if (br is LinearGradientBrush) ((LinearGradientBrush) br).WrapMode = WrapMode.TileFlipX; g.FillRectangle(br, r); } } } Color color = GetIndicatorBorder(ti); if (color.IsEmpty == false) { using (Pen pen = new Pen(color)) g.DrawLine(pen, r.Right, r.Top, r.Right, r.Bottom - 1); } } } #endregion #region GetIndicatorColor /// /// Gets the Indicator Back color /// /// /// private ColorDef GetIndicatorColor(TimeIndicator ti) { ColorDef cdef = ti.IndicatorColor; if (cdef == null || cdef.IsEmpty == true) cdef = _ViewColor.GetColorDef((int)eCalendarWeekDayPart.TimeIndicator); return (cdef); } #endregion #region GetIndicatorBorder /// /// Gets the Indicator Border color /// /// /// private Color GetIndicatorBorder(TimeIndicator ti) { return (ti.BorderColor.IsEmpty == false ? ti.BorderColor : _ViewColor.GetColor((int)eCalendarWeekDayPart.TimeIndicatorBorder)); } #endregion #endregion #region GetIndicatorRect /// /// Gets the TimeIndicator Rectangle /// /// /// /// /// private Rectangle GetIndicatorRect( TimeIndicator ti, DateTime startDate, Rectangle sRect) { double x = ColumnWidth / CalendarView.BaseInterval; int offset = (int)((ti.IndicatorDisplayTime - startDate).TotalMinutes * x); sRect.X += (offset - ti.Thickness - 1); sRect.Width = ti.Thickness; return (sRect); } /// /// Gets the TimeIndicator Rectangle /// /// /// internal override Rectangle GetIndicatorRect(TimeIndicator ti) { return (GetIndicatorRect(ti, ti.IndicatorDisplayTime)); } /// /// Gets the TimeIndicator Rectangle for the given date /// /// /// /// internal override Rectangle GetIndicatorRect(TimeIndicator ti, DateTime time) { int colStart = -_HScrollPos / ColumnWidth; DateTime startDate = CalendarView.TimeLineAddInterval(StartDate, colStart); double x = ColumnWidth / CalendarView.BaseInterval; int offset = (int)((time - startDate).TotalMinutes * x); Rectangle r = GetColRect(colStart); r.X += (offset - ti.Thickness - 1); r.Width = ti.Thickness; return (r); } #endregion #endregion #endregion #region DrawCondensedLine /// /// Draws the condensed TimeLine /// /// private void DrawCondensedLine(Graphics g) { TimeSpan ts0 = EndDate - StartDate; float scale = (float)(ClientRect.Width / (ts0.TotalMinutes + BaseInterval)); Rectangle r = GetCondensedRect(); using (GraphicsPath path = GetCondensedViewPath(r, scale)) { using (Brush br = _ViewColor.BrushPart((int)eCalendarWeekDayPart.CondensedViewBackground, r)) { g.FillRectangle(br, r); } g.FillPath(Brushes.White, path); DrawCondensedContent(g, r, scale); DrawCondensedAppointments(g, scale); g.DrawPath(Pens.Gray, path); } } #region DrawCondensedContent /// /// Draws the Condensed Content area /// /// /// /// private void DrawCondensedContent(Graphics g, Rectangle vRect, float scale) { using (Pen pen = new Pen( _ViewColor.GetColor((int)eCalendarWeekDayPart.DayHourBorder))) { int selCols = _SelectedColEnd - _SelectedColStart; if (selCols > 0) { Rectangle r = vRect; r.X += (int)(_SelectedColStart * BaseInterval * scale); r.Width = (int)(selCols * BaseInterval * scale); using (Brush br = _ViewColor.BrushPart((int)eCalendarWeekDayPart.DayOffWorkHoursBackground, r)) { g.FillRectangle(br, r); } g.DrawRectangle(pen, r); } g.DrawLine(pen, vRect.Left, vRect.Y, vRect.Right, vRect.Y); } } #endregion #region GetCondensedViewPath /// /// Gets the condensed view display path /// /// Scale factor /// Condensed view rect /// Path private GraphicsPath GetCondensedViewPath(Rectangle vRect, float scale) { int pos = (int)(FirstVisibleColumn * BaseInterval * scale); int width = (int)((BaseInterval * ClientRect.Width) / ColumnWidth * scale); Rectangle r = new Rectangle(ClientRect.X + pos, vRect.Y, width, vRect.Height); r.Inflate(0, -1); return (DisplayHelp.GetRoundedRectanglePath(r, 2, 2, 2, 2)); } #endregion #region DrawCondensedAppointments /// /// Draws condensed appointments /// /// Graphics /// Scale factor private void DrawCondensedAppointments(Graphics g, float scale) { // Display each item SmoothingMode saveMode = g.SmoothingMode; g.SmoothingMode = SmoothingMode.None; int dy = 3; foreach (ColumnList clist in _CondensedColList) { for (int i = 0; i < clist.SList.Count; i++) { for (int j = 0; j < clist.SList[i].Count; j++) { CalendarItem item = clist.SList[i][j].CItem; TimeSpan ts1 = item.StartTime - StartDate; TimeSpan ts2 = item.EndTime - item.StartTime; int pos = (int)(ts1.TotalMinutes * scale); int width = (int)(ts2.TotalMinutes * scale); if (width < 4) width = 4; Point pt1 = new Point(ClientRect.X + pos, ClientRect.Bottom - CondensedLineHeight + (i * 3) + dy); Point pt2 = new Point(pt1.X + width, pt1.Y); using (Pen pen = GetCondensedPen(item.ModelItem)) g.DrawLine(pen, pt1, pt2); } } dy += (clist.SList.Count * 3); } g.SmoothingMode = saveMode; } #region GetCondensedPen /// /// Gets the appointments condensed pen /// /// Appointment object /// private Pen GetCondensedPen(object o) { string category = null; if (o is Appointment) category = (o as Appointment).CategoryColor; else if (o is CustomCalendarItem) category = (o as CustomCalendarItem).CategoryColor; if (category != null) { // Check to see if we have any user defined // AppointmentCategoryColors if (CalendarView.HasCategoryColors == true) { AppointmentCategoryColor acc = CalendarView.CategoryColors[category]; if (acc != null) { ColorDef cdef = acc.BackColor; return (new Pen(cdef.Colors[cdef.Colors.Length - 1], 2)); } } if (category.Equals(Appointment.CategoryBlue)) return (GetCategoryPen(eAppointmentPart.BlueBackground)); if (category.Equals(Appointment.CategoryGreen)) return (GetCategoryPen(eAppointmentPart.GreenBackground)); if (category.Equals(Appointment.CategoryOrange)) return (GetCategoryPen(eAppointmentPart.OrangeBackground)); if (category.Equals(Appointment.CategoryPurple)) return (GetCategoryPen(eAppointmentPart.PurpleBackground)); if (category.Equals(Appointment.CategoryRed)) return (GetCategoryPen(eAppointmentPart.RedBackground)); if (category.Equals(Appointment.CategoryYellow)) return (GetCategoryPen(eAppointmentPart.YellowBackground)); } return (GetCategoryPen(eAppointmentPart.DefaultBackground)); } #endregion #region GetCategoryPen /// /// GetCategoryPen /// /// /// private Pen GetCategoryPen(eAppointmentPart part) { ColorDef cdef = _appointmentColor.GetColorDef((int)part); return (new Pen(cdef.Colors[cdef.Colors.Length - 1], 2)); } #endregion #endregion #endregion #region DrawAppointments /// /// Draws TimeLine appointments /// /// private void DrawAppointments(ItemPaintArgs e) { List items = CalendarItems; if (items.Count > 0) { // Loop through each CalendarItem in the week int selItem = -1; for (int i = 0; i < items.Count; i++) { // If we can display the item, then initiate the paint if (items[i].Displayed == true) { if (e.ClipRectangle.IsEmpty || e.ClipRectangle.IntersectsWith(items[i].DisplayRectangle)) { if (items[i].IsSelected == true) selItem = i; else items[i].Paint(e); } } } if (selItem >= 0) { for (int i = 0; i < items.Count; i++) { // If we can display the item, then initiate the paint if (items[i].Displayed == true) { if (e.ClipRectangle.IsEmpty || e.ClipRectangle.IntersectsWith(items[i].DisplayRectangle)) { if (items[i].IsSelected == true) items[i].Paint(e); } } } } } } #endregion #region GetColRange /// /// Calculates the range of columns needed to be drawn /// to satisfy the specified paint request /// /// ItemPaintArgs /// [out] Column start index /// [out] Column end index /// Column range count (end - start) private int GetColRange(ItemPaintArgs e, out int colStart, out int colEnd) { // Calc our starting index int start = -_HScrollPos / ColumnWidth; int x = ClientRect.X + start * ColumnWidth + _HScrollPos; while (start < TimeLineColumnCount) { if (x + ColumnWidth > e.ClipRectangle.X) break; x += ColumnWidth; start++; } // Calc our ending index int end = start; while (end < TimeLineColumnCount) { if (x >= e.ClipRectangle.Right) break; x += ColumnWidth; end++; } end++; // Set the user supplied 'out' values, and // return the range count to the caller if (end - start == 0) { colStart = 0; colEnd = 0; return (0); } colStart = start; colEnd = end; return (end - start); } #endregion #endregion #region GetColRect /// /// Gets the display rectangle for the given column /// /// Column /// Display rectangle private Rectangle GetColRect(int col) { Rectangle r = ClientRect; r.X += ((col * ColumnWidth) + _HScrollPos); r.Width = ColumnWidth; r.Height -= 1; if (ShowCondensed == true) r.Height -= (CondensedLineHeight - 1); return (r); } #endregion #region GetCondensedRect /// /// Gets the CondensedView rectangle /// /// CondensedView rectangle private Rectangle GetCondensedRect() { Rectangle r = ClientRect; r.X += 1; r.Width -= 1; r.Y = r.Bottom - CondensedLineHeight; r.Height = CondensedLineHeight - 1; return (r); } #endregion #region GetDateCol /// /// Gets the absolute column value for the given date /// /// Selection date /// Absolute column private int GetDateCol(DateTime? selDate) { if (selDate.HasValue) { TimeSpan ts = selDate.Value - StartDate; return (int)(ts.TotalMinutes / CalendarView.BaseInterval); } return (0); } #endregion #region Mouse routines #region MouseDown processing /// /// MouseDown event processing /// /// public override void InternalMouseDown(MouseEventArgs objArg) { // Forward on the event base.InternalMouseDown(objArg); if (objArg.Button == MouseButtons.Left) { if (IsTabMoving == false) { // Locate where the event took place if (ClientRect.Contains(objArg.Location) == true) { if (PointInCondensedView(objArg.Location) == true) { // User is mousing in the CondensedView area MyCursor = Cursors.Hand; ProcessCvlButtonDown(objArg); IsCondMoving = true; } else { int col; if (GetPointItem(objArg.Location, out col, true) == true) { // User is mouseing in the Content area MyCursor = GetContentCursor(); if (ProcessCilButtonDown(objArg) == false) ProcessTvlButtonDown(col); IsMouseDown = true; } IsCopyDrag = false; } } } } } #region CondensedView MouseDown processing /// /// Handles CondensedView Left Button Down events /// /// private void ProcessCvlButtonDown(MouseEventArgs objArg) { ProcessCvlPoint(objArg); } #region ProcessCvlPoint /// /// Processes CondensedView point selection /// /// private void ProcessCvlPoint(MouseEventArgs objArg) { TimeSpan ts0 = EndDate - StartDate; float scale = (float)((ClientRect.Width + 2) / ts0.TotalMinutes); int width = (int)((BaseInterval * ClientRect.Width) / ColumnWidth * scale); double x = (objArg.Location.X - ClientRect.X - (width / 2)) / scale; int col = (int)(x / BaseInterval); // Make sure we stay within our bounds, and // then set our new scroll value accordingly HScrollBarAdv hScrollBar = CalendarView.TimeLineHScrollPanel.ScrollBar; if (col < hScrollBar.Minimum) col = hScrollBar.Minimum; if (col > hScrollBar.Maximum - hScrollBar.LargeChange) col = hScrollBar.Maximum - hScrollBar.LargeChange; hScrollBar.Value = col; } #endregion #endregion #region CalendarItem MouseDown processing /// /// CalendarItem left mouseDown processing /// /// MouseEventArgs private bool ProcessCilButtonDown(MouseEventArgs objArg) { CalendarItem item = m_HotSubItem as CalendarItem; if (item != null) { if (item.HitArea != CalendarItem.eHitArea.None) { // Give the user a chance to cancel the // operation before it starts if (CalendarView.DoBeforeAppointmentViewChange(this, item, ((item.HitArea == CalendarItem.eHitArea.Move) ? eViewOperation.AppointmentMove : eViewOperation.AppointmentResize)) == false) { _LastBounds = item.Bounds; _LastPointOffset = objArg.Location; _LastMovePoint = objArg.Location; if (IsResizing == false && IsMoving == false) { OldStartTime = item.StartTime; OldEndTime = item.EndTime; OldOwnerKey = OwnerKey; } // Flag appropriate action if (item.HitArea == CalendarItem.eHitArea.LeftResize) IsStartResizing = true; else if (item.HitArea == CalendarItem.eHitArea.RightResize) IsEndResizing = true; else if (item.HitArea == CalendarItem.eHitArea.Move) IsMoving = true; // Update our initial PosWin display UpdatePosWin(ClientRect); } } return (true); } return (false); } #endregion #region TimeLineView MouseDown processing /// /// Handles TimeLineView left MouseDown events /// /// Column index private void ProcessTvlButtonDown(int col) { DateTime startDate = CalendarView.TimeLineAddInterval(StartDate, col); DateTime endDate = CalendarView.TimeLineAddInterval(startDate, 1); ExtendSelection(ref startDate, ref endDate); CalendarView.DateSelectionStart = startDate; CalendarView.DateSelectionEnd = endDate; SelectedItem = null; } #endregion #endregion #region MouseUp processing /// /// MouseUp event processing /// /// MouseEventArgs public override void InternalMouseUp(MouseEventArgs objArg) { base.InternalMouseUp(objArg); // Cancel our scroll timer CancelScrollTimer(); } #endregion #region MouseMove processing /// /// MouseMove event processing /// /// MouseEventArgs public override void InternalMouseMove(MouseEventArgs objArg) { // Forward on the event, but only if we are not in // the middle of moving or resizing a CalendarItem if (Control.MouseButtons == MouseButtons.None) ClearMouseStates(); if (!IsMoving && !IsStartResizing && !IsEndResizing) base.InternalMouseMove(objArg); if (IsTabMoving == false) { if (IsCondMoving == true || PointInCondensedView(objArg.Location) == true) { // The user in the CondensedView area MyCursor = Cursors.Hand; if (IsCondMoving == true) ProcessCvlMouseMove(objArg); } else { // The user in the Content area MyCursor = GetContentCursor(); if (objArg.Button == MouseButtons.Left) ProcessContentMove(objArg); } } } #region ProcessCvlMouseMove /// /// Handles CondensedView mouse moves /// /// private void ProcessCvlMouseMove(MouseEventArgs objArg) { ProcessCvlPoint(objArg); } #endregion #region ProcessContentMove /// /// Processes content mouse moves /// /// MouseEventArgs private void ProcessContentMove(MouseEventArgs objArg) { // Locate where the event took place // and process it accordingly int col; if (GetPointItem(objArg.Location, out col, false) == true) { // The col is visible, so no need to // enable scrolling - just process the event EnableViewScrolling(false); ProcessMouseMove(col, objArg); } else if (IsMouseDown == true) { // Check to see if the user is performing // a "DragDrop" operation if (DragDropAppointment(objArg) == false) { // The selected slice is not visible, // so we need to enable scrolling EnableViewScrolling(true); // Only process the event if the user is selecting // time cells (auto moving apps is intrusive looking) if (DateSelectionAnchor != null) ProcessMouseMove(col, objArg); } } } #region DragDropAppointment /// /// Initiates a user "DragDrop" operation - if enabled /// /// /// True if operation started private bool DragDropAppointment(MouseEventArgs objArgs) { if (IsMoving == true && CanDrag == true) { Point pt = objArgs.Location; BaseView bv = CalendarView.GetViewFromPoint(pt); TimeLineView tlv = bv as TimeLineView; if (tlv != null && tlv != this) { eViewArea area = bv.GetViewAreaFromPoint(pt); if (area == eViewArea.InContent) { if (CalendarView.DoAppointmentViewChanging(SelectedItem, tlv.OwnerKey, SelectedItem.StartTime, SelectedItem.EndTime, eViewOperation.AppointmentMove, IsNewCopyDrag) == false) { DragCopy(); ClearMouseStates(); CancelScrollTimer(); if (PosWin != null) PosWin.Hide(); AppointmentView av = SelectedItem as AppointmentView; if (av != null) return (tlv.DragAppointment(this, av)); CustomCalendarItem ci = SelectedItem as CustomCalendarItem; if (ci != null) return (tlv.DragCustomItem(this, ci)); } } } } return (false); } #region DragCustomItem private bool DragCustomItem(TimeLineView pv, CustomCalendarItem ci) { // Set the new owner and selected view, and // recalc the new layout ci.OwnerKey = OwnerKey; NeedRecalcLayout = true; RecalcSize(); CalendarView.SelectedOwnerIndex = this.DisplayedOwnerKeyIndex; // Get the new view CustomCalendarItem view = GetCustomCalendarItem(ci); if (view != null) SetNewDragItem(pv, view); return (true); } #endregion #region DragAppointment /// /// Drags the given appointment from one view to another /// /// Previous view /// Item to drag private bool DragAppointment(TimeLineView pv, AppointmentView av) { // Set the new owner and selected view, and // recalc the new layout av.Appointment.OwnerKey = OwnerKey; NeedRecalcLayout = true; RecalcSize(); CalendarView.SelectedOwnerIndex = this.DisplayedOwnerKeyIndex; // Get the new view AppointmentView view = GetAppointmentView(av.Appointment); if (view != null) SetNewDragItem(pv, view); return (true); } #endregion #region SetNewDragItem private void SetNewDragItem(TimeLineView pv, CalendarItem view) { _LastBounds = pv._LastBounds; _LastMovePoint = pv._LastMovePoint; _LastPointOffset = pv._LastPointOffset; int dx = _LastPointOffset.X - _LastBounds.X; CalendarView.CalendarPanel.InternalMouseMove(new MouseEventArgs(MouseButtons.None, 0, view.Bounds.X + dx, view.Bounds.Y, 0)); MouseEventArgs args = new MouseEventArgs(MouseButtons.Left, 1, view.Bounds.X + dx, view.Bounds.Y, 0); IsMoving = true; InternalMouseMove(args); CalendarView.CalendarPanel.InternalMouseDown(args); SelectedItem = view; IsCopyDrag = true; } #endregion #endregion #endregion #region GetContentCursor /// /// Gets the cursor /// /// Cursor private Cursor GetContentCursor() { CalendarItem item = m_HotSubItem as CalendarItem; if (item != null) { switch (item.HitArea) { case CalendarItem.eHitArea.LeftResize: case CalendarItem.eHitArea.RightResize: return (Cursors.SizeWE); case CalendarItem.eHitArea.Move: return (IsMoving ? Cursors.SizeAll : DefaultCursor); } } return (DefaultCursor); } #endregion #region ProcessMouseMove /// /// Processes user MouseMove /// /// DayColumn /// private void ProcessMouseMove(int col, MouseEventArgs objArg) { if (DateSelectionAnchor != null) { ProcessTvMouseMove(col); } else if (SelectedItem != null) { if (objArg.Location.Equals(_LastMovePoint) == false) { if (IsMoving == true) ProcessItemMove(col, objArg); else if (IsStartResizing == true) ProcessItemLeftResize(col, objArg); else if (IsEndResizing == true) ProcessItemRightResize(col, objArg); _LastMovePoint = objArg.Location; } } } #endregion #region ProcessTvMouseMove /// /// Processes TimeLineView mouseMove events /// /// Column private void ProcessTvMouseMove(int col) { if (DateSelectionAnchor != null) { DateTime date = CalendarView.TimeLineAddInterval(StartDate, col); // Let the user select forwards or backwards if (date >= DateSelectionAnchor) { CalendarView.DateSelectionStart = DateSelectionAnchor.Value; CalendarView.DateSelectionEnd = CalendarView.TimeLineAddInterval(date, 1); } else { CalendarView.DateSelectionStart = date; CalendarView.DateSelectionEnd = CalendarView.TimeLineAddInterval(DateSelectionAnchor.Value, 1); } } } #endregion #region CalendarItem MouseMove processing /// /// Processes CalendarItem mouseMove events /// /// Column /// MouseEventArgs private void ProcessItemMove(int col, MouseEventArgs objArg) { // Calculate our new item date DateTime newDate; if (CalendarView.TimeLinePeriod == eTimeLinePeriod.Years) { newDate = StartDate.AddYears( GetDeltaYears(col, true, objArg)); } else { newDate = StartDate.AddMinutes( GetDeltaMinutes(col, true, objArg)); } if (newDate != SelectedItem.StartTime) { if (DragCopy() == false) { TimeSpan ts = SelectedItem.EndTime - SelectedItem.StartTime; try { if (SelectedItem is CustomCalendarItem) CalendarView.CustomItems.BeginUpdate(); else CalendarModel.BeginUpdate(); // Make the move if (CalendarView.DoAppointmentViewChanging(SelectedItem, null, newDate, newDate + ts, eViewOperation.AppointmentMove, IsNewCopyDrag) == false) { SelectedItem.StartTime = newDate; SelectedItem.EndTime = newDate + ts; NeedRecalcLayout = true; } } finally { if (SelectedItem is CustomCalendarItem) CalendarView.CustomItems.EndUpdate(); else CalendarModel.EndUpdate(); } } } } #endregion #region CalendarItem MouseResize processing /// /// Processes CalendarItem left resizing /// /// Column /// private void ProcessItemLeftResize(int col, MouseEventArgs objArg) { DateTime date = (CalendarView.TimeLinePeriod == eTimeLinePeriod.Years) ? StartDate.AddYears(GetDeltaYears(col, true, objArg)) : StartDate.AddMinutes(GetDeltaMinutes(col, true, objArg)); if (date < SelectedItem.EndTime && SelectedItem.StartTime != date) ResizeItem(date, SelectedItem.EndTime); } /// /// Processes CalendarItem right resizing /// /// Column /// private void ProcessItemRightResize(int col, MouseEventArgs objArg) { DateTime date = (CalendarView.TimeLinePeriod == eTimeLinePeriod.Years) ? StartDate.AddYears(GetDeltaYears(col, false, objArg)) : StartDate.AddMinutes(GetDeltaMinutes(col, false, objArg)); if (date > SelectedItem.StartTime && SelectedItem.EndTime != date) ResizeItem(SelectedItem.StartTime, date); } /// /// Initiates the resize of the selected item /// /// /// private void ResizeItem(DateTime startTime, DateTime endTime) { // Let the user cancel the operation if desired if (CalendarView.DoAppointmentViewChanging(SelectedItem, null, startTime, endTime, eViewOperation.AppointmentResize, IsNewCopyDrag) == false) { SelectedItem.StartTime = startTime; SelectedItem.EndTime = endTime; NeedRecalcLayout = true; } } #endregion #region GetDeltaMinutes /// /// Gets the change from the last offset (in minutes) /// /// Column /// Are we going left or right /// MouseEventArgs /// Change in mnutes private double GetDeltaMinutes(int col, bool left, MouseEventArgs objArg) { // Calculate our delta minutes Rectangle r = GetColRect(col); double dm = (objArg.Location.X - _LastPointOffset.X); dm += left ? _LastBounds.Left - r.Left : _LastBounds.Right - r.Right; dm = BaseInterval * dm / ColumnWidth; // If the Alt key is not pressed, then round // our value off to the nearest column if ((Control.ModifierKeys & Keys.Alt) != Keys.Alt) { col += (int)(dm / BaseInterval); dm = 0; } dm += (col * BaseInterval); // Make sure we don't cross our day boundaries if (left == true) { if (dm < 0) dm = 0; } else { dm += BaseInterval; } return (dm); } #endregion #region GetDeltaYears /// /// Gets the change from the last offset (in years) /// /// Column /// Are we going left or right /// MouseEventArgs /// Change in years private int GetDeltaYears(int col, bool left, MouseEventArgs objArg) { // Calculate our delta minutes Rectangle r = GetColRect(col); int dm = (objArg.Location.X - _LastPointOffset.X); dm += left ? _LastBounds.Left - r.Left : _LastBounds.Right - r.Right; dm = CalendarView.TimeLineInterval * dm / ColumnWidth; // If the Alt key is not pressed, then round // our value off to the nearest column if ((Control.ModifierKeys & Keys.Alt) != Keys.Alt) { col += dm / CalendarView.TimeLineInterval; dm = 0; } dm += (col * CalendarView.TimeLineInterval); // Make sure we don't cross our day boundaries if (left == true) { if (dm < 0) dm = 0; } else { dm += CalendarView.TimeLineInterval; } return (dm); } #endregion #region View Scrolling support /// /// Routine to enable or disable view scrolling /// /// true to enable private void EnableViewScrolling(bool enable) { if (enable == true) { if (_ScrollViewTimer == null) { _ScrollViewTimer = new Timer(); _ScrollViewTimer.Interval = 10; _ScrollViewTimer.Tick += ScrollViewTimerTick; _ScrollViewTimer.Start(); } _ScrollDwell = 0; } else { CancelScrollTimer(); } } /// /// Cancels the view scroll timer /// private void CancelScrollTimer() { // Dispose of our scroll timer if (_ScrollViewTimer != null) { _ScrollViewTimer.Stop(); _ScrollViewTimer.Tick -= ScrollViewTimerTick; _ScrollViewTimer = null; } } /// /// Determines the amount to scroll (which is /// based loosely upon the delta magnitude) /// /// Point delta /// Scroll amount private int ScrollAmount(int delta) { _ScrollDwell++; int dx = Math.Abs(delta); int n = (dx < 16 ? 8 : dx < 32 ? 4 : 1); if (_ScrollDwell % n == 0) return (delta < 0 ? -1 : 1); return (0); } /// /// Handles view scroll timer ticks /// /// object /// EventArgs void ScrollViewTimerTick(object sender, EventArgs e) { Control c = (Control)this.GetContainerControl(true); if (c != null) { // Calculate our delta Point pt = c.PointToClient(Cursor.Position); Rectangle r = ClientRect; int n = r.X + (ClientRect.Width / ColumnWidth) * ColumnWidth; int dx = (pt.X < r.Left) ? ScrollAmount(pt.X - r.Left) : (pt.X >= n) ? ScrollAmount(pt.X - n) : 0; // Make sure we stay within our upper bounds if (dx < 0) { if (CalendarView.TimeLineHScrollPanel.ScrollBar.Value + dx < 0) dx = -CalendarView.TimeLineHScrollPanel.ScrollBar.Value; } else { int maxValue = CalendarView.TimeLineHScrollPanel.ScrollBar.Maximum - CalendarView.TimeLineHScrollPanel.ScrollBar.LargeChange; if (CalendarView.TimeLineHScrollPanel.ScrollBar.Value + dx > maxValue) dx = maxValue - CalendarView.TimeLineHScrollPanel.ScrollBar.Value; } // Scroll if necessary if (dx != 0) { CalendarView.TimeLineHScrollPanel.ScrollBar.Value += dx; if (PosWin != null) PosWin.Hide(); InternalMouseMove(new MouseEventArgs(MouseButtons.Left, 0, pt.X, pt.Y, 0)); } } } #endregion #endregion #region GetPointItem /// /// Gets the item column at the given point /// /// Point in question /// [out] Column /// True if partial hits are ok /// True if valid item private bool GetPointItem(Point pt, out int col, bool partial) { int start = -_HScrollPos / ColumnWidth; int end = start + ClientRect.Width / ColumnWidth; Rectangle r = GetColRect(start); for (int i = start; i <= end; i++) { if (r.Contains(pt) == true) { col = i; return (IsColVisible(r, partial)); } r.X += ColumnWidth; } col = (pt.X < ClientRect.X) ? 0 : TimeLineColumnCount; return (false); } /// /// Determines if a given column is visible /// /// Display rectangle /// True if partial visibility is ok /// True if visible private bool IsColVisible(Rectangle r, bool partial) { if (partial == true) { if (r.Right < ClientRect.Left) return (false); if (r.Left > ClientRect.Right) return (false); } else { if (r.Left < ClientRect.Left) return (false); if (r.Right > ClientRect.Right) return (false); } return (true); } #endregion #region PointInCondensedView /// /// Determines if the given point in in /// the CondensedView area /// /// /// private bool PointInCondensedView(Point pt) { if (ShowCondensed == true) return (GetCondensedRect().Contains(pt)); return (false); } #endregion #endregion #region InternalKeyDown #region InternalKeyDown /// /// Processes KeyDown events /// /// public override void InternalKeyDown(KeyEventArgs objArg) { switch (objArg.KeyData) { case Keys.Up: case Keys.Up | Keys.Shift: case Keys.Up | Keys.Control: case Keys.Up | Keys.Control | Keys.Shift: objArg.Handled = true; break; case Keys.Down: case Keys.Down | Keys.Shift: case Keys.Down | Keys.Control: case Keys.Down | Keys.Control | Keys.Shift: objArg.Handled = true; break; case Keys.Left: case Keys.Left | Keys.Shift: case Keys.Left | Keys.Control: case Keys.Left | Keys.Control | Keys.Shift: ProcessLeftRightKey(objArg, -1); break; case Keys.Right: case Keys.Right | Keys.Shift: case Keys.Right | Keys.Control: case Keys.Right | Keys.Control | Keys.Shift: ProcessLeftRightKey(objArg, 1); break; case Keys.Home: case Keys.Home | Keys.Shift: case Keys.Home | Keys.Control: case Keys.Home | Keys.Control | Keys.Shift: ProcessHomeKey(objArg); break; case Keys.End: case Keys.End | Keys.Shift: case Keys.End | Keys.Control: case Keys.End | Keys.Control | Keys.Shift: ProcessEndKey(objArg); break; } } #endregion #region ProcessLeftRightKey /// /// Processes Left and Right Key events /// /// /// protected virtual void ProcessLeftRightKey(KeyEventArgs objArg, int dx) { if (ValidDateSelection()) { // ReSharper disable PossibleInvalidOperationException DateTime startDate = CalendarView.DateSelectionStart.Value; DateTime endDate = CalendarView.DateSelectionEnd.Value; if (startDate.Equals(DateSelectionAnchor.Value) == true) startDate = CalendarView.TimeLineAddInterval(endDate, -1); // ReSharper restore PossibleInvalidOperationException startDate = CalendarView.TimeLineAddInterval(startDate, dx); endDate = CalendarView.TimeLineAddInterval(startDate, 1); DateTime viewStart = CalendarView.TimeLineViewScrollStartDate; DateTime viewEnd = CalendarView.TimeLineViewScrollEndDate; if (startDate < viewStart) { CalendarView.TimeLineViewScrollStartDate = startDate; } else if (endDate > viewEnd) { CalendarView.TimeLineViewScrollStartDate = CalendarView.TimeLineAddInterval(CalendarView.TimeLineViewScrollStartDate, 1); } ExtendSelection(ref startDate, ref endDate); CalendarView.DateSelectionStart = startDate; CalendarView.DateSelectionEnd = endDate; } objArg.Handled = true; } #endregion #region ProcessHomeKey /// /// Handles Home key events /// /// protected virtual void ProcessHomeKey(KeyEventArgs objArg) { DateTime startDate = CalendarView.TimeLineViewScrollStartDate; DateTime endDate = CalendarView.TimeLineAddInterval(startDate, 1); ExtendSelection(ref startDate, ref endDate); CalendarView.DateSelectionStart = startDate; CalendarView.DateSelectionEnd = endDate; SelectedItem = null; objArg.Handled = true; } #endregion #region ProcessEndKey /// /// Processes End key events /// /// protected virtual void ProcessEndKey(KeyEventArgs objArg) { DateTime endDate = CalendarView.TimeLineViewScrollEndDate; DateTime startDate = CalendarView.TimeLineAddInterval(endDate, -1); ExtendSelection(ref startDate, ref endDate); CalendarView.DateSelectionStart = startDate; CalendarView.DateSelectionEnd = endDate; SelectedItem = null; objArg.Handled = true; } #endregion #endregion #region IDisposable Members protected override void Dispose(bool disposing) { if (disposing == true && IsDisposed == false) { ResetView(); WorkBrush = null; OffWorkBrush = null; SelectedBrush = null; HookEvents(false); } base.Dispose(disposing); } #endregion } } #endif