?? calendardisplay.as
字號:
/*Copyright (c) 2006 Adobe Systems Incorporated
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
package qs.controls
{
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.DropShadowFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import flash.utils.getTimer;
import mx.collections.ArrayCollection;
import mx.collections.IList;
import mx.collections.XMLListCollection;
import mx.containers.Canvas;
import mx.controls.Button;
import mx.controls.Label;
import mx.controls.VScrollBar;
import mx.core.ClassFactory;
import mx.core.Container;
import mx.core.DragSource;
import mx.core.EdgeMetrics;
import mx.core.IDataRenderer;
import mx.core.IFlexDisplayObject;
import mx.core.UIComponent;
import mx.events.CollectionEvent;
import mx.events.DragEvent;
import mx.events.ScrollEvent;
import mx.managers.DragManager;
import mx.skins.RectangularBorder;
import mx.skins.halo.HaloBorder;
import qs.calendar.CalendarEvent;
import qs.controls.calendarDisplayClasses.CalendarAllDayRegion;
import qs.controls.calendarDisplayClasses.CalendarDay;
import qs.controls.calendarDisplayClasses.CalendarDisplayEvent;
import qs.controls.calendarDisplayClasses.CalendarEventRenderer;
import qs.controls.calendarDisplayClasses.CalendarHeader;
import qs.controls.calendarDisplayClasses.CalendarHours;
import qs.controls.calendarDisplayClasses.ICalendarEventRenderer;
import qs.utils.DateRange;
import qs.utils.DateUtils;
import qs.utils.InstanceCache;
import qs.utils.ReservationAgent;
import qs.utils.SortedArray;
import qs.utils.TimeZone;
import mx.core.Application;
import mx.events.FlexEvent;
// dispatched when the range displayed by the calendar changes.
[Event("change")]
[Event(name="displayModeChange", type="qs.controls.calendarDisplayClasses.CalendarDisplayEvent")]
// dispatched when a user clicks on a header in the calendar
[Event(name="headerClick",type="qs.controls.calendarDisplayClasses.CalendarDisplayEvent")]
// dispatched when a user clicks on a day in the calendar
[Event(name="dayClick",type="qs.controls.calendarDisplayClasses.CalendarDisplayEvent")]
// dispatched when a user clicks on an event in the calendar.
[Event(name="itemClick",type="qs.controls.calendarDisplayClasses.CalendarDisplayEvent")]
// dispatched when a user selects days or times in the calendar to add event.
[Event(name="itemAdd",type="qs.controls.calendarDisplayClasses.CalendarDisplayEvent")]
// dispatched when a user double click on the event renderer to edit event.
[Event(name="itemEdit",type="qs.controls.calendarDisplayClasses.CalendarDisplayEvent")]
// the color of the border dividing the hour labels from the days
[Style(name="hourDividerColor", type="uint", format="Color", inherit="no")]
// the thickness of the border dividing the hour labels from the days
[Style(name="hourDividerThickness", type="Number", format="Length", inherit="no")]
// the color of the border dividing the all day area from the intra-day area
[Style(name="allDayDividerColor", type="uint", format="Color", inherit="no")]
// the thickness of the border dividing the all day area from the intra-day area
[Style(name="allDayDividerThickness", type="Number", format="Length", inherit="no")]
// the color of the background of the all day area from the intra-day area
[Style(name="allDayColor", type="uint", format="Color", inherit="no")]
// the color of the gridlines marking the hours
[Style(name="hourColor", type="uint", format="Color", inherit="no")]
// the thickness of the gridlines marking the hours
[Style(name="hourThickness", type="Number", format="Length", inherit="no")]
// the background color of the hour labels
[Style(name="hourBackgroundColor", type="uint", format="Color", inherit="no")]
// the stylename assigned to the hour labels
[Style(name="hourStyleName", type="String", inherit="no")]
// the stylename assigned to the individual day squares
[Style(name="dayStyleName", type="String", inherit="no")]
// the stylename assigned to the individual headers
[Style(name="dayHeaderStyleName", type="String", inherit="no")]
// the stylename assigned to the individual events.
[Style(name="eventStyleName", type="String", inherit="no")]
public class CalendarDisplay extends UIComponent
{
// max number of columns displayed
private const MAXIMUM_ROW_LENGTH:int = 7;
// how much, in pixels, an event gets inset from the area assigned to it (like padding)
private const EVENT_INSET:int = 3;
// the minimum height we'll let an hour shrink to in days layout. If it gets smaller than this,
// we'll scroll instead of shrinking.
private const MIN_HOUR_HEIGHT:Number = 40;
// the animator responsible for our animation
private var _animator:LayoutAnimator;
// the cache of day renderers used to represent individual days
private var _dayCache:InstanceCache;
// the cache of header renderers used to render the day headers.
private var _headerCache:InstanceCache;
// layer used to group all the headers in depth
private var _headerLayer:UIComponent;
// layer used to group all the days in depth
private var _dayLayer:UIComponent;
// layer used to group all the intra-day events for masking and scrolling.
private var _eventLayer:UIComponent;
// layer used to group all-day events
private var _allDayEventLayer:UIComponent;
// skin used to render the all day event area in days layout.
private var _allDayEventBorder:IFlexDisplayObject;
// shape used to mask out the intra-day events in days layout so they get clipped when scrolling.
private var _eventMask:Shape;
// skin used to render the hour labels and gridlines.
private var _hourGrid:CalendarHours;
// our current range, as assigned by the client.
private var _currentRange:DateRange;
// a pending range assigned by the client, that will be assigned to current range in the next commitProperties().
private var _pendingRange:DateRange;
// our current calculated display mode. Can be day, days, week, weeks, or month.
private var _displayMode:String = "month";
// our current display mode assigned by the client. Can be day, week, month, or 'auto.'
private var _userDisplayMode:String = "month";
// a pending display mode that will be used at the next commitProperties() call.
private var _pendingDisplayMode:String;
// flag to indicate if we should throw out all of our event renderers and render all events from scratch.
private var _removeAllEventData:Boolean;
// flag to indicate whether we should animate state changes or not.
private var _animated:Boolean = false;
// a lookup table that allows us to map from a CalendarEvent to an EventData structure used to store rendering data about the event.
private var _eventData:Dictionary;
// the set of all events being rendererd (potentially) by this calendar.
private var _dataProvider:IList;
// the set of all events in the data provider, sorted by start date.
private var _allEventsSortedByStart:SortedArray;
// a flag to indicate if we need to rebuild our _allEventsSortedByStart list from the dataProvider.
private var _allEventsDirty:Boolean = true;
// the height of the all day area when in days layout.
private var _allDayAreaHeight:Number;
// the date range currently visible in the calendar. This can be larger than the currentRange, when in weeks or month mode,
// since we round out the display to the nearest week boundary.
private var _visibleRange:DateRange;
// the current range combines with the display mode to get the computed range. i.e., in months display mode, we expand the
// assigned current range to the nearest month boundaries.
private var _computedRange:DateRange;
// the number of cells in a columnin our current display.
private var _columnLength:int;
// the number of cells in a row in our current display.
private var _rowLength:int;
// the width of a single day cell.
private var _cellWidth:Number;
// the height of a single day cell.
private var _cellHeight:Number;
// the set of all events visible given the current visible range.
private var _visibleEvents:Array;
// the height of a single hour, when in days layout.
private var _hourHeight:Number;
// the height of a single day, when in days layout. This is essentially cellHeight minus headerHeight and allDayAreaHeight.
private var _dayAreaHeight:Number;
// whether or not we should animate the days that are dissapearing off of the calendar during a transition. This gets set to true
// and false under various different conditions, determined to be 'right' emperically.
private var _animateRemovingDays:Boolean = false;
// state for drag operations
// whether this is a resize or move
private var _dragType:String;
// the event data for the current event being manipulated.
private var _dragEventData:EventData;
// where the user clicked to start the drag.
private var _dragDownPt:Point;
// the renderer for the event being dragged
// TODO: this could probably be removed, and replaced with usage of the drageventdata property instead.
private var _dragRenderer:UIComponent;
// the offset, in milliseconds, between the start of the event being dragged and the time that was originally clicked on to
// start the drag.
private var _dragOffset:Number;
// the border between the calendar edges and the day area edges (i.e., accounting for scrollbars, hours, etc).
private var _border:EdgeMetrics = new EdgeMetrics();
// the scrollbar used during days layout.
private var _scroller:VScrollBar;
// the hour at the top of the visible region for the current scroll.
private var _scrollHour:Number;
// a dropshadow filter applied to event renderers during a drag operation.
private var _dragFilter:DropShadowFilter = new DropShadowFilter();
// the timezone being used to render the calendar.
private var _tz:TimeZone;
public function CalendarDisplay():void
{
// initialize our current range and timezone
var dt:Date = new Date();
_tz = TimeZone.localTimeZone;
range = new DateRange(_tz.startOfMonth(dt),_tz.endOfMonth(dt));
_visibleRange = new DateRange();
// the animator class does all of our layout for us, to make sure we get nice smooth animation
_animator = new LayoutAnimator();
_animator.layoutFunction = generateLayout;
_animator.animationSpeed = .5;
// the day cache manages the components we'll use to render each individual day
_dayCache = new InstanceCache();
_dayCache.destroyUnusedInstances = false;
_dayCache.createCallback = dayChildCreated;
_dayCache.assignCallback = InstanceCache.showInstance;
_dayCache.releaseCallback = hideInstance;
_dayCache.destroyCallback = InstanceCache.removeInstance;
_dayCache.factory = new ClassFactory(CalendarDay);
// the header cache manages the components we'll use to render the header for each day.
_headerCache = new InstanceCache();
_headerCache.destroyUnusedInstances = false;
_headerCache.createCallback = headerChildCreated;
_headerCache.assignCallback = InstanceCache.showInstance;
_headerCache.releaseCallback = hideInstance;
_headerCache.destroyCallback = InstanceCache.removeInstance;
_headerCache.factory = new ClassFactory(CalendarHeader);
// the parent for all of our day instances
_dayLayer = new UIComponent();
addChild(_dayLayer);
// the parent for all the headers
_headerLayer = new UIComponent();
addChild(_headerLayer);
// the parent for all standard events
_eventLayer = new UIComponent();
addChild(_eventLayer);
// the parent for all-day events.
_allDayEventLayer = new UIComponent();
addChild(_allDayEventLayer);
// the component responsible for rendering the region at the top of the calendar where all-day events go.
_allDayEventBorder = new CalendarAllDayRegion(this);
_allDayEventLayer.addChild(DisplayObject(_allDayEventBorder));
// a mask to make sure our events don't stick out past our rendering region.
_eventMask = new Shape();
_eventMask.visible = false;
addChild(_eventMask);
// the component responsible for drawing the gridlines for the hours in days view.
_hourGrid = new CalendarHours(this);
_eventLayer.addChild(_hourGrid);
_hourGrid.alpha = 0;
//Time selection event
_hourGrid.selection.addEventListener(MouseEvent.MOUSE_UP, daySelectUpHandler);
_hourGrid.selection.addEventListener(MouseEvent.MOUSE_MOVE, daySelectMoveHandler);
// the scrollbar when in days view.
_scroller = new VScrollBar();
_scroller.addEventListener(ScrollEvent.SCROLL,scrollHandler);
addChild(_scroller);
_scroller.alpha = 0;
_eventData = new Dictionary();
dataProvider = null;
// set ourselves up for drag/drop events
addEventListener(DragEvent.DRAG_ENTER,dragEventEnteredCalendarHandler);
addEventListener(DragEvent.DRAG_OVER,dragEventMovedOverCalendarHandler);
addEventListener(DragEvent.DRAG_EXIT,dragEventLeftCalendarHandler);
addEventListener(DragEvent.DRAG_DROP,dragEventDroppedOnCalendarHandler);
}
// an explicit request from the client as to how to display our current range.
// could be one of: day, week, month, or auto. If it's auto, we
// will choose the best mode.
public function set displayMode(value:String):void
{
_userDisplayMode = value;
_pendingDisplayMode = value;
range = _currentRange;
}
public function get displayMode():String
{
return (_pendingDisplayMode == null)? _displayMode:_pendingDisplayMode;
}
public function get layoutMode():String
{
return (_displayMode == "weeks" || _displayMode == "week" || _displayMode == "month")? LayoutMode.WEEKS:LayoutMode.DAYS;
}
// whether or not to animate our layout changes.
public function set animated(value:Boolean):void
{
_animated = value;
}
public function get animated():Boolean { return _animated; }
// the current range being displayed by the calendar.
// if displayMode is auto, we'll look at this value and automatically determine the best
// display mode based on its size.
public function set range(value:DateRange):void
{
var dayCount:int = _tz.rangeDaySpan(value);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -