context: // 
  // TableViewBackend.cs
  //  
  // Author:
  //       Lluis Sanchez <lluis@xamarin.com>
  // 
  // Copyright (c) 2011 Xamarin Inc
  // 
  // 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.
  
  using System;
  using System.Collections.Generic;
  using AppKit;
  using CoreGraphics;
  using Foundation;
  using Xwt.Backends;
  using Xwt.Drawing;
  
  namespace Xwt.Mac
  {
  	public abstract class TableViewBackend<T,S>: ViewBackend<NSScrollView,S>, ITableViewBackend, ICellSource
  		where T:NSTableView where S:ITableViewEventSink
  	{
  		List<NSTableColumn> cols = new List<NSTableColumn> ();
  		protected NSTableView Table;
  		ScrollView scroll;
  		NSObject selChangeObserver;
  		NormalClipView clipView;
  
  		NSTableView ICellSource.TableView { get { return Table; } }
  
  		List<NSTableColumn> ICellSource.Columns {
  			get { return cols; }
  		}
  
  		protected List<NSTableColumn> Columns {
  			get { return cols; }
  		}
  
  		public override void Initialize ()
  		{
  			Table = CreateView ();
  			Table.ColumnAutoresizingStyle = NSTableViewColumnAutoresizingStyle.Sequential;
  			scroll = new ScrollView ();
  			clipView = new NormalClipView ();
  			clipView.Scrolled += OnScrolled;
  			scroll.ContentView = clipView;
  			scroll.DocumentView = Table;
  			scroll.BorderType = NSBorderType.BezelBorder;
  			ViewObject = scroll;
  			Widget.AutoresizingMask = NSViewResizingMask.HeightSizable | NSViewResizingMask.WidthSizable;
  			Widget.AutoresizesSubviews = true;
  		}
  		
  		public ScrollPolicy VerticalScrollPolicy {
  			get {
  				if (scroll.AutohidesScrollers && scroll.HasVerticalScroller)
  					return ScrollPolicy.Automatic;
  				else if (scroll.HasVerticalScroller)
  					return ScrollPolicy.Always;
  				else
  					return ScrollPolicy.Never;
  			}
  			set {
  				switch (value) {
  				case ScrollPolicy.Automatic:
  					scroll.AutohidesScrollers = true;
  					scroll.HasVerticalScroller = true;
  					break;
  				case ScrollPolicy.Always:
  					scroll.AutohidesScrollers = false;
  					scroll.HasVerticalScroller = true;
  					break;
  				case ScrollPolicy.Never:
  					scroll.HasVerticalScroller = false;
  					break;
  				}
  			}
  		}
  
  		public ScrollPolicy HorizontalScrollPolicy {
  			get {
  				if (scroll.AutohidesScrollers && scroll.HasHorizontalScroller)
  					return ScrollPolicy.Automatic;
  				else if (scroll.HasHorizontalScroller)
  					return ScrollPolicy.Always;
  				else
  					return ScrollPolicy.Never;
  			}
  			set {
  				switch (value) {
  				case ScrollPolicy.Automatic:
  					scroll.AutohidesScrollers = true;
  					scroll.HasHorizontalScroller = true;
  					break;
  				case ScrollPolicy.Always:
  					scroll.AutohidesScrollers = false;
  					scroll.HasHorizontalScroller = true;
  					break;
  				case ScrollPolicy.Never:
  					scroll.HasHorizontalScroller = false;
  					break;
  				}
  			}
  		}
  
  		ScrollControlBackend vertScroll;
  		public IScrollControlBackend CreateVerticalScrollControl ()
  		{
  			if (vertScroll == null)
  				vertScroll = new ScrollControlBackend (ApplicationContext, scroll, true);
  			return vertScroll;
  		}
  
  		ScrollControlBackend horScroll;
  		public IScrollControlBackend CreateHorizontalScrollControl ()
  		{
  			if (horScroll == null)
  				horScroll = new ScrollControlBackend (ApplicationContext, scroll, false);
  			return horScroll;
  		}
  
  		void OnScrolled (object o, EventArgs e)
  		{
  			if (vertScroll != null)
  				vertScroll.NotifyValueChanged ();
  			if (horScroll != null)
  				horScroll.NotifyValueChanged ();
  		}
  
  		protected override Size GetNaturalSize ()
  		{
  			return EventSink.GetDefaultNaturalSize ();
  		}
  		
  		protected abstract NSTableView CreateView ();
  		protected abstract string SelectionChangeEventName { get; }
  		
  		public override void EnableEvent (object eventId)
  		{
  			base.EnableEvent (eventId);
  			if (eventId is TableViewEvent) {
  				switch ((TableViewEvent)eventId) {
  				case TableViewEvent.SelectionChanged:
  					selChangeObserver = NSNotificationCenter.DefaultCenter.AddObserver (new NSString (SelectionChangeEventName), HandleTreeSelectionDidChange, Table);
  					break;
  				}
  			}
  		}
  		
  		public override void DisableEvent (object eventId)
  		{
  			base.DisableEvent (eventId);
  			if (eventId is TableViewEvent) {
  				switch ((TableViewEvent)eventId) {
  				case TableViewEvent.SelectionChanged:
  					if (selChangeObserver != null)
  						NSNotificationCenter.DefaultCenter.RemoveObserver (selChangeObserver);
  					break;
  				}
  			}
  		}
  
  		void HandleTreeSelectionDidChange (NSNotification notif)
  		{
  			ApplicationContext.InvokeUserCode (EventSink.OnSelectionChanged);
  		}
  
  		public SelectionMode SelectionMode { get; private set; }
  		
  		public void SetSelectionMode (SelectionMode mode)
  		{
  			SelectionMode = mode;
  			Table.AllowsMultipleSelection = mode == SelectionMode.Multiple;
  			if (mode == SelectionMode.None && Table.SelectedRowCount > 0)
  				UnselectAll ();
  		}
  
  		public virtual NSTableColumn AddColumn (ListViewColumn col)
  		{
  			var tcol = new TableColumn (ApplicationContext, this, Table);
  			cols.Add (tcol);
  			tcol.UpdateColumn (col);
  			Table.AddColumn (tcol);
  			return tcol;
  		}
  		object IColumnContainerBackend.AddColumn (ListViewColumn col)
  		{
  			return AddColumn (col);
  		}
  		
  		public void RemoveColumn (ListViewColumn col, object handle)
  		{
  			var tcol = (NSTableColumn)handle;
  			cols.Remove (tcol);
  			Table.RemoveColumn (tcol);
  		}
  
  		public void UpdateColumn (ListViewColumn col, object handle, ListViewColumnChange change)
  		{
  			var tcol = handle as TableColumn;
  			if (tcol != null)
  				tcol.UpdateColumn (col, change);
  		}
  
  		public Rectangle GetCellBounds (int row, CellView cell, bool includeMargin)
  		{
  			var rect = Rectangle.Zero;
  			var cellBackend = cell.GetBackend () as CellViewBackend;
  			var container = Table.GetView (cellBackend.Column, row, false) as CompositeCell;
  			if (container != null) {
  				var cellView = container.GetCellViewForBackend (cellBackend);
  				rect = cellView.ConvertRectToView (new CGRect (CGPoint.Empty, cellView.Frame.Size), Table).ToXwtRect ();
  				rect.Y -= scroll.DocumentVisibleRect.Y;
  				rect.X -= scroll.DocumentVisibleRect.X;
  			}
  			return rect;
  		}
  
  		public Rectangle GetRowBounds (int row, bool includeMargin)
  		{
  			var rect = Rectangle.Zero;
  			var rowView = Table.GetRowView (row, false);
  			if (rowView != null) {
  				rect = rowView.Frame.ToXwtRect ();
  
gt: 				rect.Y -= scroll.DocumentVisibleRect.Y;