/*
 * Created on Jan 19, 2004
 *
 */
package LumiSoft.UI.Controls.Grid;

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;

import javax.swing.JDialog;

import LumiSoft.UI.Controls.*;
import LumiSoft.UI.Controls.Paint;
import LumiSoft.UI.Controls.Grid.Editors.*;
import LumiSoft.Data.*;

/**
 * Provides tabular data view for WGridControl 
 */
public class WGridTableView 
{	
	private ActionListener     m_pDataViewListener       = null;
	private WGridControl       m_pGrid                   = null;
	private WGridTableView     m_pDetailView             = null;
	private WGridTableView     m_pParentView             = null;
	private RowInfo            m_pParentRow              = null;
	private int                m_FirstVisibleRow         = 0;
	private int                m_LastVisibleRow          = -1;
	private WGridColumn        m_pActiveColumn           = null;
	private RowInfo            m_pActiveRow              = null;
	private Object[]           m_ActiveRowValues         = null;
	private WScrollBar         m_pVtScrollBar            = null;
	private WScrollBar         m_pHzScrollBar            = null;
	private int                m_PressedKeyCode          = 0;
	private WDragWindow        m_pDragWindow             = null;
	private WGridColumn        m_pDragColumn             = null;
	private boolean            m_AllowEdit               = false;
	private boolean            m_AllowSort               = true;
	private boolean            m_AllowAppendNewRow       = false;
	private boolean            m_AllowMultiSelect        = false;
	private boolean            m_AllowReorderColumns     = true;
	private boolean            m_EnterKeyDoubleClicks    = false;
	private boolean            m_ShowColumns             = true;
	private boolean            m_ShowRowIndicators       = true;
	private boolean            m_pShowFooter             = true;
	private boolean            m_ShowMaximizeForMainView = false;
	private boolean            m_ShowCurrentCell         = true;
	private boolean            m_ShowVerticalGridlines   = true;
	private boolean            m_ShowHorizontalGridlines = true;
	private WGridColumns       m_pColumns                = null;
	private Rectangle          m_pBounds                 = null;
	private WBaseEditor        m_pActiveEditor           = null;
	private Object             m_pTag                    = null;	
	private ArrayList<RowInfo> m_pRowInfos               = null;
	private ArrayList<RowInfo> m_pSelectedRows           = null;		
	private DataView           m_pDataSource             = null;
	private DataRelation       m_pRelation               = null;
	private int                m_ColumnsStartX           = 0;
	private boolean            m_ViewMaximized           = false;
	private Point              m_MouseDownPoint          = new Point(-1,-1);;
	
	/**
	 * Default constructor.
	 * @param grid
	 */
	public WGridTableView(WGridControl grid)
	{
		m_pGrid         = grid;
		m_pColumns      = new WGridColumns(this);
		m_pRowInfos     = new ArrayList<RowInfo>();
		m_pSelectedRows = new ArrayList<RowInfo>();
		m_pBounds       = new Rectangle(-1,-1,0,0);
		
		m_pVtScrollBar = new WScrollBar();
		m_pVtScrollBar.setVisible(false);
		m_pVtScrollBar.addScrollBarListener(new WScrollBarListener(){
			public void PositionChanged(){
				OnViewScrolled();
			}
			
			public boolean MayChangePosition(){
				return !IsActiveRowModified();
			}
		});
		
		m_pHzScrollBar = new WScrollBar();
		m_pHzScrollBar.setVisible(false);
		m_pHzScrollBar.setVertical(false);
		m_pHzScrollBar.addScrollBarListener(new WScrollBarListener(){
			public void PositionChanged(){
				OnViewScrolled();
			}
			
			public boolean MayChangePosition(){
				return !IsActiveRowModified();
			}
		});
		
		m_pGrid.add(m_pVtScrollBar);
		m_pGrid.add(m_pHzScrollBar);
	}	
	
	/**
	 * Clean up any resources being used.
	 *
	 */
	public void Dispose()
	{
		m_pGrid.remove(m_pVtScrollBar);
		m_pGrid.remove(m_pHzScrollBar);
	}
	
	
	/**
	 * Clones grid view.
	 * @return
	 */
	public WGridTableView Clone() throws Exception
	{
		WGridTableView view = new WGridTableView(m_pGrid);
			
		for(WGridColumn columnSource : this.getColumns()){
			WGridColumn columnNew = view.getColumns().AddColumn(columnSource.getText());
			columnNew.setTextID(columnSource.getTextID());
			columnNew.setTextAlign(columnSource.getTextAlign());
			columnNew.setCellsTextAlign(columnSource.getCellsTextAlign());
			columnNew.setCellTextFormat(columnSource.getCellTextFormat());
			columnNew.setAllowEdit(columnSource.getAllowEdit());
			columnNew.setFont(columnSource.getFont());
			columnNew.setFooterText(columnSource.getFooterText());
			columnNew.setFooterTextAlign(columnSource.getFooterTextAlign());
			columnNew.setMappingName(columnSource.getMappingName());
			columnNew.setAllowResize(columnSource.getAllowResize());
			columnNew.setAllowSort(columnSource.getAllowSort());
			columnNew.setTag(columnSource.getTag());
			columnNew.setVisible(columnSource.getVisible());
			columnNew.setWidth(columnSource.getWidth());			
		}
		
		if(m_pDataSource != null){
			view.setDataSource(new DataView(m_pDataSource.getTable()));
		}
		
		return view;
	}
	
	protected void OnViewScrolled()
	{
		if(IsActiveRowModified()){
			return;
		}
		HideEditor();
		
		m_FirstVisibleRow = m_pVtScrollBar.getPosition();
		Calculate(true);
	}
	
	/**
	 * Is called when DataView sorting,filtering,... changed.
	 *
	 */
	protected void OnDataViewChanged()
	{
		HideEditor();
		m_pRowInfos.clear();
		m_pSelectedRows.clear();
		m_FirstVisibleRow = 0;
		m_LastVisibleRow  = -1;
		m_pVtScrollBar.setPosition(0);
		m_pVtScrollBar.setMaximum(0);
		m_pHzScrollBar.setPosition(0);
		m_pHzScrollBar.setMaximum(0);
		m_pActiveRow    = null;
		m_pActiveColumn = null;
		
		// Construct row info holder
		for(int i=0;i<m_pDataSource.getCount();i++){
			m_pRowInfos.add(new RowInfo(this,m_pDataSource.get(i)));
		}
		
		Calculate(true);
		MoveNext();
	}
		
		
	/**
	 * Paints table view.
	 * @param g
	 */
	protected void Paint(Graphics2D g)
	{		
		g.setClip(this.getBounds());
		
		g.setColor(Color.WHITE);
		g.fill(this.getBounds());
			
		if(getShowColumns()){
			PaintHeader(g);
		}
		if(getShowFooter()){
			PaintFoother(g);
		}
		
		g.setClip(getRowsRect());
		// Paint visible rows
		if(m_pRowInfos.size() > 0){
			for(int i=m_FirstVisibleRow;i<=m_LastVisibleRow;i++){			
				RowInfo rowInfo = m_pRowInfos.get(i);
		
				PaintRow(g,rowInfo);
			}
		}
		g.setClip(this.getBounds());
		
		// Paint view border
		g.setColor(ViewStyle.GetColorFromRgb(128,128,128));
		g.drawRect(getBounds().x,getBounds().y,getBounds().width - 1,getBounds().height - 1);		
	}
			
	/**
	 * Paints view header.
	 * @param g
	 */
	private void PaintHeader(Graphics2D g)
	{
		//--- Paint unused area of columns       ------------------------------------------------//
		g.setColor(ViewStyle.GetColorFromRgb(212,208,200));
		g.fillRect(getBounds().x,getBounds().y,getColumnsRect().width,getColumnsRect().height);
		
		g.setColor(ViewStyle.GetColorFromRgb(128,128,128));
		g.drawLine(getBounds().x,getBounds().y,getColumnsRect().width,getBounds().y);
		if(!m_pVtScrollBar.isVisible()){
			g.drawLine((int)getBounds().getMaxX() - 1,getBounds().y,(int)getBounds().getMaxX() - 1,(int)getColumnsRect().getMaxY());
		}
		
		// If child grid draw maximize/minimize button
		if(m_pParentView != null || m_ShowMaximizeForMainView){
			if(m_ViewMaximized){
				// Draw minimize X
				g.setColor(Color.RED);
				g.drawLine(getBounds().x + 6,getBounds().y + 6,getBounds().x + 14,getBounds().y + 14);
				g.drawLine(getBounds().x + 6,getBounds().y + 14,getBounds().x + 14,getBounds().y + 6);
			}
			else{
				// Draw maximize magnify glass
				g.setColor(Color.white);
				g.fillOval(getBounds().x + 4,getBounds().y + 4,9,9);
				g.setColor(Color.BLACK);
				g.drawOval(getBounds().x + 4,getBounds().y + 4,9,9);
				g.drawLine(getBounds().x + 12,getBounds().y + 12,getBounds().x + 12 + 4,getBounds().y + 12 + 4);
			}
		}
		//--------------------------------------------------------------------------------------//
		
		String sort = "";
		if(m_pDataSource != null){
			sort = m_pDataSource.getSort().toLowerCase();
		}
		String[] sortList = sort.split(",");
		
		g.setClip(g.getClipBounds().intersection(this.getColumnsRect()));
		for(WGridColumn col : this.getColumns().getVisibleColumns()){
			// Paint only visible columns
			if(col.getBounds().getMaxX() > -1 &&  col.getBounds().x < getRowsRect().getMaxX()){
				// Fill backround
				g.setColor(ViewStyle.GetColorFromRgb(212,208,200));
				g.fillRect(col.getBounds().x,col.getBounds().y,col.getBounds().width,col.getBounds().height);
				
				//--- Paint sorting arrow ----------------------------------------------------//
				g.setColor(Color.GRAY);
				for(int c=0;c<sortList.length;c++){
					if(sortList[c].split(" ")[0].equals(col.getMappingName().toLowerCase())){
						if(sortList[c].indexOf(" desc") > -1){
							Paint.DrawTriangle(g,new Rectangle((int)col.getBounds().getMaxX() - 13,col.getBounds().y + 7,9,7),Direction.Down);
						}
						else{
							Paint.DrawTriangle(g,new Rectangle((int)col.getBounds().getMaxX() - 13,col.getBounds().y + 7,9,7),Direction.Up);
						}
						break;
					}					
				}
				//---------------------------------------------------------------------------//
				
				// Paint text
				g.setColor(Color.BLACK);
				Paint.DrawText(g,col.getFont(),col.getText(),col.getBounds(),HorizontalAlignment.Center);
	
				// Paint border around column
				PaintRaisedBorder(g,new Rectangle(col.getBounds().x,col.getBounds().y,col.getBounds().width,col.getBounds().height));
			}
		}		
		g.setClip(this.getBounds());
	}
	
	/**
	 * Paints view foother.
	 * @param g
	 */
	private void PaintFoother(Graphics2D g)
	{
		// Fill backround
		g.setColor(ViewStyle.GetColorFromRgb(212,208,200));
		g.fill(this.getFooterRect());
			
		for(WGridColumn col : this.getColumns().getVisibleColumns()){			
			// Paint only visible columns
			if(col.getBounds().getMaxX() > -1 && col.getBounds().x < getRowsRect().getMaxX()){
				String footerText = "";
				switch(col.getFooterType())
				{
					case WGridViewFooterType.Text:
						footerText = col.getFooterText();
						break;
					case WGridViewFooterType.Count:
						if(m_pDataSource != null){
							footerText = col.getFooterText() + String.valueOf(m_pDataSource.getCount());
						}
						else{
							footerText = col.getFooterText() + "0";
						}
						break;
					case WGridViewFooterType.Sum:
						double sum = 0;
						if(m_pDataSource != null){
							for(int v=0;v<m_pDataSource.getCount();v++){
								DataRowView dr = m_pDataSource.get(v);
								
								try{
									sum += Double.valueOf(dr.get(col.getMappingName()).toString()).doubleValue();
								}
								catch(Exception x){
								}
							}
						}
						
						// Decimal places formating
						if(col.getCellTextFormat().startsWith("f")){
							int decimalPlaces = 0;
							try{
								decimalPlaces = Integer.parseInt(col.getCellTextFormat().substring(1));
								
								java.text.DecimalFormat format = new java.text.DecimalFormat();
								format.setMinimumFractionDigits(decimalPlaces);
								format.setMaximumFractionDigits(decimalPlaces);
								format.setGroupingUsed(false);
								
								footerText = col.getFooterText() + format.format(sum);
							}
							catch(Exception x){					
							}
						}
						else{			
							footerText = col.getFooterText() + sum;
						}
						break;
				}
				
				// Paint column footer only it's available
				if(footerText.length() > 0){
					// Draw text
					g.setColor(Color.BLACK);
					Paint.DrawText(g,col.getFont(),footerText,new Rectangle(col.getBounds().x + 4,getFooterRect().y + 3,col.getBounds().width - 8,getFooterRect().height - 6),col.getFooterTextAlign());
				
					// Draw border
					g.setColor(ViewStyle.GetColorFromRgb(128,128,128));
					g.drawRect(col.getBounds().x + 2,getFooterRect().y + 2,col.getBounds().width - 4,getFooterRect().height - 6);
				}
			}
		}
		
		// Draw border around foother area
		g.drawRect(getFooterRect().x,getFooterRect().y,getFooterRect().width,getFooterRect().height);
	}
	
	/**
	 * Paints grid row.
	 * @param g
	 * @param rowInfo
	 */
	private void PaintRow(Graphics2D g,RowInfo rowInfo)
	{			
		boolean activeRow = m_pActiveRow != null && rowInfo.getRowSource().hashCode() == m_pActiveRow.getRowSource().hashCode();
		boolean selected  = m_pSelectedRows.contains(rowInfo);
		boolean evenRow   = ((double)(m_pRowInfos.indexOf(rowInfo) +  1) / 2 == Math.floor((double)(m_pRowInfos.indexOf(rowInfo) +  1) / 2));
		for(WGridColumn col : this.getColumns().getVisibleColumns()){			
			// Paint only visible cells
			if(col.getBounds().getMaxX() > -1 && col.getBounds().x < getRowsRect().getMaxX()){
				boolean cellSelected = false;
				// Active cell or not selected row
				if(!selected || (activeRow && m_pActiveColumn != null && col.equals(m_pActiveColumn) && getShowCurrentCell())){
					// Rows with index 1,3,5, ....					
					if(!evenRow){
						g.setColor(m_pGrid.getViewStyle().getGridRowColor());
					}
					// Rows with index 2,4,6, ....
					else{
						g.setColor(m_pGrid.getViewStyle().getGridEvenRowColor());					
					}
					
					g.fillRect(col.getBounds().x,rowInfo.getY(),col.getBounds().width,rowInfo.getRowHeight());					
				}
				// Selected cell
				else{
					g.setColor(m_pGrid.getViewStyle().getGridRowSelectedColor());
					g.fillRect(col.getBounds().x,rowInfo.getY(),col.getBounds().width,rowInfo.getRowHeight());
					
					cellSelected = true;
				}
			
				// Ask column editor to paint itself
				col.getEditor().PaintDefault(g,rowInfo.GetValue(col),new Rectangle(col.getBounds().x + 4,rowInfo.getY(),col.getBounds().width - 8,rowInfo.getRowHeight()),evenRow,cellSelected,col);
				
				//---- Draw cell gridlines
				g.setColor(ViewStyle.GetColorFromRgb(128,128,128));				
				if(getShowVerticalGridlines()){
					// Cell leftside vertical border
					g.drawLine(col.getBounds().x,rowInfo.getY(),col.getBounds().x,rowInfo.getY() + rowInfo.getRowHeight());
					// Cell rightside vertical border
					g.drawLine(col.getBounds().x + col.getBounds().width,rowInfo.getY(),col.getBounds().x + col.getBounds().width,rowInfo.getY() + rowInfo.getRowHeight());
				}
				if(getShowHorizontalGridlines()){
					// Cell top horizontal border
					g.drawLine(col.getBounds().x,rowInfo.getY(),col.getBounds().x + col.getBounds().width,rowInfo.getY() );
					// Cell bottom horizontal border
					g.drawLine(col.getBounds().x,rowInfo.getY() +  + rowInfo.getRowHeight(),col.getBounds().x + col.getBounds().width,rowInfo.getY() + rowInfo.getRowHeight());
				}
				//--------------------------------
			}
		}
		
		// Repaint active cell border
		if(getShowCurrentCell() && activeRow && m_pActiveColumn != null){
			g.setColor(Color.BLACK);
			g.drawRect(m_pActiveColumn.getBounds().x + 1,rowInfo.getY() + 1,m_pActiveColumn.getBounds().width - 2,rowInfo.getRowHeight() - 2);
		}
		
		
		//--- Draw row idicator column ----------------------------------------------------------------------//
		if(getShowRowIndicators()){
			// Fill backround
			g.setColor(ViewStyle.GetColorFromRgb(212,208,200));
			g.fillRect(getBounds().x,rowInfo.getBounds().y,20,rowInfo.getBounds().height);
			
			// Draw border
			PaintRaisedBorder(g,new Rectangle(getBounds().x,rowInfo.getBounds().y,20,rowInfo.getBounds().height));
			
			// Paint active row arrow
			if(activeRow){
				g.setColor(Color.BLACK);
				Paint.DrawTriangle(g,new Rectangle(getBounds().x + 8,rowInfo.getBounds().y + 5,5,9),Direction.Right);
			}
		}
		//----------------------------------------------------------------------------------------------------//
			
		//--- Draw master detail column ---------------------------------------------------------//
		if(getMasterDetail()){
			// Fill backround
			g.setColor(Color.WHITE);
			g.fillRect(getMasterDetailRect().x,rowInfo.getBounds().y,20,rowInfo.getBounds().height);
					
			if(rowInfo.getDetailView() == null){
				// Paint +
				g.setColor(ViewStyle.GetColorFromRgb(128,128,128));
				g.drawRect(getMasterDetailRect().x + 5,rowInfo.getBounds().y + 5,10,10);
				g.drawLine(getMasterDetailRect().x + 8,rowInfo.getBounds().y + 10,getMasterDetailRect().x + 12,rowInfo.getBounds().y + 10);
				g.drawLine(getMasterDetailRect().x + 10,rowInfo.getBounds().y + 8,getMasterDetailRect().x + 10,rowInfo.getBounds().y + 12);
			}
			else{
				// Paint -
				g.setColor(ViewStyle.GetColorFromRgb(128,128,128));
				g.drawRect(getMasterDetailRect().x + 5,rowInfo.getBounds().y + 5,10,10);
				g.drawLine(getMasterDetailRect().x + 8,rowInfo.getBounds().y + 10,getMasterDetailRect().x + 12,rowInfo.getBounds().y + 10);
			}
				
			// Paint border
			PaintRaisedBorder(g,new Rectangle(getMasterDetailRect().x,rowInfo.getBounds().y,20,rowInfo.getBounds().height));
		}
		//----------------------------------------------------------------------------------------//
				
		// Paint sub grid
		if(rowInfo.getDetailView() != null){
			Rectangle clip = g.getClipBounds();
			
			rowInfo.getDetailView().Paint(g);
			
			// Restore original clip
			g.setClip(clip);
		}
	}
			
	/**
	 * Repaints specified row.
	 * @param rowInfo
	 */
	private void RepaintRow(RowInfo rowInfo)
	{
		Graphics2D g = m_pGrid.GetPaintGraphics();
		if(g != null){
			// Allow to paint only visible rows
			if(m_pRowInfos.indexOf(rowInfo) >= m_FirstVisibleRow && m_pRowInfos.indexOf(rowInfo) <= m_LastVisibleRow){
				g.setClip(this.getRowsRect());
			
				PaintRow(g,rowInfo);			
				m_pGrid.repaint(this.getBounds().x,rowInfo.getY(),this.getBounds().width,rowInfo.getRowHeight());
			}
		}		
	}
	
	/**
	 * Repaints table view.
	 *
	 */
	public void Repaint()
	{				
		Graphics2D g = m_pGrid.GetPaintGraphics();
		if(g != null){
			Paint(g);
			m_pGrid.repaint(this.getBounds());
		}		
	}
	
	/**
	 * Repaints active row.
	 *
	 */
	public void RepaintActiveRow()
	{
		if(m_pActiveRow != null){
			RepaintRow(m_pActiveRow);
		}
	}
		
	
	/**
	 * Paints border with raised effect.
	 * @param g
	 * @param rect
	 */
	private void PaintRaisedBorder(Graphics2D g,Rectangle rect)
	{
		g.setColor(ViewStyle.GetColorFromRgb(128,128,128));
		g.draw(rect);
		g.setColor(Color.WHITE);
		g.drawLine(rect.x + 1,rect.y + 1,rect.x + rect.width - 2,rect.y + 1);
		g.drawLine(rect.x + 1,rect.y + 1,rect.x + 1,rect.y + rect.height - 2);
	}
	
				
	//--- Process key events ----------------------//
	protected void Process_keyTyped(KeyEvent e)
	{
		
	}
	
	public void Process_keyPressed(KeyEvent e)
	{	
		m_PressedKeyCode = e.getKeyCode();
		
		//--- Process keyboard events ------------------------------------------------------//
		if(e.getKeyCode() == KeyEvent.VK_ENTER){			
			if(m_EnterKeyDoubleClicks){
				if(m_pActiveRow != null){
					m_pGrid.RaiseOnRowDoubleClicked(m_pActiveRow.getRowSource());
				}
			}
			else{
				MoveNext();
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_DOWN){
			// If row contains sub grid, move active row to sub grid first visible row
			if(m_pActiveRow != null && m_pActiveRow.getDetailView() != null){
				m_pActiveRow.getDetailView().SelectFirstVisibleRow();
			}
			else{
				// If sub grid and active row is last row, move active row to parent gird next row.
				if(m_pActiveRow != null && m_pParentView != null && m_pRowInfos.indexOf(m_pActiveRow) == m_pRowInfos.size() - 1){
					// If view maximized, don't allow move to parent grid view 
					if(!m_ViewMaximized){
						m_pParentView.SelectNextRow(m_pParentRow);
					}					
				}
				else{
					SelectNextRow();
				}
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_UP){
			// If sub grid and active row is first row, move active row to parent grid parent row.
			if(m_pRowInfos.indexOf(m_pActiveRow) == 0 && m_pParentView != null && m_pParentRow != null){
				// If view maximized, don't allow move to parent grid view 
				if(!m_ViewMaximized){
					m_pParentView.SelectRow(m_pParentRow,null,false);
				}				
			}
			else{
				if(m_pRowInfos.indexOf(m_pActiveRow) > 0){
					RowInfo row = (RowInfo)m_pRowInfos.get(m_pRowInfos.indexOf(m_pActiveRow) - 1);
					// If row contains sub grid, move active row to sub grid last visible row
					if(row.getDetailView() != null){
						if((m_pRowInfos.indexOf(m_pActiveRow) - 1 < m_FirstVisibleRow)){
							SelectPreviousRow();
						}
						row.getDetailView().SelectLastVisibleRow();		
					}
					else{
						SelectPreviousRow();
					}
				}
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
			if(m_pColumns.indexOf(m_pActiveColumn) < m_pColumns.getCount() - 1){
				SelectRow(m_pActiveRow,(WGridColumn)m_pColumns.get(m_pColumns.indexOf(m_pActiveColumn) + 1),false);
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_LEFT){
			if(m_pColumns.indexOf(m_pActiveColumn) > 0){
				SelectRow(m_pActiveRow,(WGridColumn)m_pColumns.get(m_pColumns.indexOf(m_pActiveColumn) - 1),false);
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_PAGE_UP){
			if((m_FirstVisibleRow - (m_LastVisibleRow - m_FirstVisibleRow)) > 0){
				SelectRow((RowInfo)m_pRowInfos.get((m_FirstVisibleRow - (m_LastVisibleRow - m_FirstVisibleRow))),m_pActiveColumn,true);
			}
			else{
				SelectRow((RowInfo)m_pRowInfos.get(0),m_pActiveColumn,true);
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_PAGE_DOWN){
			if(m_LastVisibleRow + (m_LastVisibleRow - m_FirstVisibleRow - 1) < m_pRowInfos.size() - 1){
				SelectRow((RowInfo)m_pRowInfos.get(m_LastVisibleRow + (m_LastVisibleRow - m_FirstVisibleRow - 1)),m_pActiveColumn,true);
			}
			else{
				SelectRow((RowInfo)m_pRowInfos.get(m_pRowInfos.size() - 1),m_pActiveColumn,true);
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_C && e.isControlDown()){
			if(getActiveRow() != null && getActiveColumn() != null){
				String val = getActiveRow().get(getActiveColumn().getMappingName()).toString();
				Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new java.awt.datatransfer.StringSelection(val),new java.awt.datatransfer.ClipboardOwner(){
					public void lostOwnership(java.awt.datatransfer.Clipboard aClipboard,java.awt.datatransfer.Transferable aContents){					     
					}
				});
			}			
		}
	}
	
	protected void Process_keyReleased(KeyEvent e)
	{							
		m_PressedKeyCode = 0;
	}
	//------------------------------------------------------------------------------------//
	
	//--- Process mouse events ------------------------------------------------//
	protected void Process_mousePressed(MouseEvent e)
	{	
		m_MouseDownPoint = e.getPoint();
		
		if(!m_pGrid.hasFocus()){
			m_pGrid.grabFocus();			
		}
		
		RowInfo hittedRow = GetRowFromPoint(e.getPoint());
		if(hittedRow == null){
			return;
		}
		
		//--- See if master detail expand/collapse clicked ----------------------------------------------------//
		if(getMasterDetail() && (e.getPoint().x > getMasterDetailRect().x && e.getPoint().x < m_ColumnsStartX)){
			ToggleRow(hittedRow);
		}
		//------------------------------------------------------------------------------------------------------//
		
		
		WGridColumn hittedCellColumn = null;
		for(WGridColumn col : this.getColumns().getVisibleColumns()){
			if(e.getPoint().x > col.getBounds().x && e.getPoint().x < col.getBounds().getMaxX()){
				hittedCellColumn = col;
				break;
			}
		}
				
		//--- Handle row selection ------------------------//
		if(m_AllowMultiSelect){
			if(e.isShiftDown()){
				if(m_pActiveRow != null){
					SelectRows(m_pActiveRow,hittedRow);
				}
				else{
					SelectRows(hittedRow,hittedRow);
				}
				
				this.Repaint();
				return;
			}
			else if(e.isControlDown()){
				ToggleRowSelection(hittedRow);
				RepaintRow(hittedRow);
				return;
			}
		}
		//-------------------------------------------------//
		
		SelectRow(hittedRow,hittedCellColumn,true);
		
	//	m_pGrid.m_pFocusedView = this;
	}

	protected void Process_mouseReleased(MouseEvent e)
	{	
		m_MouseDownPoint = new Point(-1,-1);
		
		//--- There is active drag operation, end it. ---------------------//
		if(m_pDragWindow != null){
			Object[] tag         = (Object[])m_pDragWindow.getTag();
			WGridColumn   col         = (WGridColumn)tag[0];
			boolean  resize_drag = ((Boolean)(tag[1])).booleanValue();
			
			if(resize_drag){
				int x    = m_pGrid.getLocationOnScreen().x + e.getPoint().x;
				int colX = m_pGrid.getLocationOnScreen().x + col.getBounds().x;
				int width = x - colX;
				if(width > 0){
					col.setWidth(width);
					Calculate(true);
					
					UpdateActiveEditor();
				}
			}
			else{
				// Column dragged to right
				if(col.getBounds().getMaxX() < e.getPoint().x){
					// Find first column which contains mouse position
					for(int i=col.getIndex();i<this.getColumns().getCount();i++){
						WGridColumn nextColumn = (WGridColumn)this.getColumns().get(i);						
						if(nextColumn.getVisible() && nextColumn.getBounds().contains(e.getPoint())){
							col.setIndex(nextColumn.getIndex());
							break;
						}
					}
				}
				// Column dragged to left
				else if(col.getBounds().getX() > e.getPoint().x){
					// Find first column which contains mouse position
					for(int i=col.getIndex();i>-1;i--){
						WGridColumn nextColumn = (WGridColumn)this.getColumns().get(i);						
						if(nextColumn.getVisible() && nextColumn.getBounds().contains(e.getPoint())){
							col.setIndex(nextColumn.getIndex());
							break;
						}
					}
				}
			}
			
			m_pDragWindow.dispose();
			m_pDragWindow = null;
			m_pDragColumn = null;
		//	m_pGrid.m_pDragView = null;
			
			this.Repaint();
			
			return;
		}
		else{
			// See if maximaze/minimaze child grid clicked
			if(e.getPoint().x < m_ColumnsStartX && e.getPoint().y < this.getBounds().y + 20){				
				if(m_pParentView != null){
					if(m_ViewMaximized){
						m_ViewMaximized = false;
						m_pGrid.OnViewMinimized(this);
						m_pParentView.Calculate(true);
					}
					else{							
						SelectRow((RowInfo)m_pRowInfos.get(0),m_pColumns.get(0),true);
						m_ViewMaximized = true;
						m_pGrid.OnViewMaximized(this);						
						this.setBounds(m_pParentView.getBounds());
						Calculate(true);
					}
				}
				else if(m_ShowMaximizeForMainView){
					m_ViewMaximized = !m_ViewMaximized;
					
					m_pGrid.RaiseOnMaxMinButtonPressed(m_ViewMaximized);
					Calculate(true);
				}
			}
		}
		//-----------------------------------------------------------------//
	}
	
	protected void Process_mouseClicked(MouseEvent e) 
	{				
		if(e.getClickCount() == 2){
			RowInfo hittedRow = GetRowFromPoint(e.getPoint());
			if(hittedRow != null){
				m_pGrid.RaiseOnRowDoubleClicked(hittedRow.getRowSource());
			}
		}
		// See if column sorting
		else if(e.getButton() == MouseEvent.BUTTON1 && getAllowSort() && m_pDataSource != null && m_pDataSource.getCount() > 0){
			// Don't allow sorting if active row modified
			if(m_pGrid.getFocusedView().IsActiveRowModified()){
				return;
			}
			m_pGrid.getFocusedView().HideEditor();
			
			WGridColumn mouseCol = GetColumnFromPoint(e.getPoint());
			if(mouseCol != null && m_pDataSource != null && mouseCol.getAllowSort()){
				String[] sortList   = m_pDataSource.getSort().split(",");
				String   sortString = "";
				
				ArrayList<String> columns = new ArrayList<String>();
				for(String sort : sortList){
					columns.add(sort.split(" ")[0].toLowerCase());
				}
				
				// Add or remove sort
				if(e.isControlDown()){
					// Remove
					if(columns.contains(mouseCol.getMappingName().toLowerCase())){
						for(String sort : sortList){
							if(!sort.toLowerCase().split(" ")[0].equals(mouseCol.getMappingName().toLowerCase())){
								sortString += sort + ",";
							}
						}
					}
					// Add
					else{
						sortString += m_pDataSource.getSort() + "," + mouseCol.getMappingName();						
					}
				}
				// Toggle/replace sort
				else{
					// Toggle
					if(columns.contains(mouseCol.getMappingName().toLowerCase())){					
						for(String sort : sortList){
							if(sort.toLowerCase().split(" ")[0].equals(mouseCol.getMappingName().toLowerCase())){								
								if(sort.toLowerCase().indexOf(" asc") > -1){
									sortString += sort.toLowerCase().replaceAll(" asc"," desc") + ",";
								}
								else if(sort.toLowerCase().indexOf(" desc") > -1){
									sortString += sort.toLowerCase().replaceAll(" desc"," asc") + ",";
								}
								else{
									sortString += sort + " desc,";
								}
							}
							else{
								sortString += sort + ",";
							}
						}
					}
					// Replace
					else{
						sortString = mouseCol.getMappingName();
					}
				}
				
				// Normalize sort string
				if(sortString.startsWith(",")){
					sortString = sortString.substring(1);
				}
				if(sortString.endsWith(",")){
					sortString = sortString.substring(0,sortString.length() - 1);
				}
			
				m_pDataSource.setSort(sortString);
				
				// Construct row info holder
				m_pRowInfos.clear();
				for(int i=0;i<m_pDataSource.getCount();i++){
					m_pRowInfos.add(new RowInfo(this,m_pDataSource.get(i)));
				}
			
				// Clear selection,active row and select first visible row.
				ClearSelection();
				m_pActiveRow = null;
				Calculate(true);
				SelectRow(m_pRowInfos.get(m_FirstVisibleRow),m_pActiveColumn,true);
			}
		}
	}


	protected void Process_mouseEntered(MouseEvent e)
	{		
	}

	protected void Process_mouseExited(MouseEvent e)
	{
	}
	
	protected void Process_mouseWheelMoved(MouseWheelEvent e) 
	{	
		if(IsActiveRowModified()){
			return;
		}
		
		if(m_PressedKeyCode == KeyEvent.VK_SHIFT){
			if(e.getUnitsToScroll() < 0){
				if(m_pHzScrollBar.getPosition() > m_pHzScrollBar.getMinimum()){
					m_pHzScrollBar.setPosition(m_pHzScrollBar.getPosition() - 5);
					OnViewScrolled(); 
				}
			}
			else{
				if(m_pHzScrollBar.getPosition() < m_pHzScrollBar.getMaximum()){
					m_pHzScrollBar.setPosition(m_pHzScrollBar.getPosition() + 5);
					OnViewScrolled();
				}
			}
		}
		else if(m_pVtScrollBar.isVisible()){
			if(e.getUnitsToScroll() < 0){
				if(m_pVtScrollBar.getPosition() > m_pVtScrollBar.getMinimum()){
					m_pVtScrollBar.setPosition(m_pVtScrollBar.getPosition() -1);
					m_FirstVisibleRow = m_pVtScrollBar.getPosition();
					OnViewScrolled();
				}
			}
			else{
				if(m_pVtScrollBar.getPosition() < m_pVtScrollBar.getMaximum()){
					m_pVtScrollBar.setPosition(m_pVtScrollBar.getPosition() + 1);
					m_FirstVisibleRow = m_pVtScrollBar.getPosition();
					OnViewScrolled();
				}
			}
		}
	}
	
	protected void Process_mouseMoved(MouseEvent e)
	{
		// There is active drag operation, we don't need that event.
		if(m_pDragWindow != null){			
			return;
		}
		
		Component cursorControl = m_pGrid;
		// There is some trouble with Cursors in java.
		// If control is on JDialog, cursor setting won't work.
		// For workaroud just set JDialog cursor instead.
		if(javax.swing.SwingUtilities.getRoot(m_pGrid) instanceof JDialog){
			cursorControl = javax.swing.SwingUtilities.getRoot(m_pGrid);
		}
		
		//--- Resize cursor refreshing ---------------------------------------//		
		cursorControl.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		WGridColumn mouseCol = GetColumnFromPoint(e.getPoint());
		if(mouseCol != null){
			if(e.getPoint().x > mouseCol.getBounds().getMaxX() - 5 && e.getPoint().x < mouseCol.getBounds().getMaxX() - 2){
				cursorControl.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
			}		
		}
		//--------------------------------------------------------------------//
	}	
				
	protected void Process_mouseDragged(MouseEvent e)
	{			
		if(m_pDragWindow == null){
			WGridColumn mouseCol = GetColumnFromPoint(m_MouseDownPoint);
			
			//--- Column resize --------------------------------------------//
			int x = m_pGrid.getLocationOnScreen().x + e.getPoint().x;
			int y = m_pGrid.getLocationOnScreen().y + getColumnsRect().y;		
			if(mouseCol != null && m_MouseDownPoint.getX() > mouseCol.getBounds().getMaxX() - 7 && m_MouseDownPoint.getX() <= mouseCol.getBounds().getMaxX()){
				m_pDragWindow = new WDragWindow(m_pGrid);
				m_pDragWindow.setTag(new Object[]{mouseCol,new Boolean(true)});
				m_pDragWindow.Show(x,y,1,this.getBounds().height - m_pHzScrollBar.getBounds().height,Color.BLACK);
				
				m_pGrid.m_pFocusedView = this;
			//	m_pGrid.m_pDragView    = this;				
			}			
			//--- If column drag (reorder)
			else if(getAllowReorderColumns() && mouseCol != null && m_MouseDownPoint.getX() >= mouseCol.getBounds().getX() + 10){
				m_pDragColumn = mouseCol;
				
				m_pDragWindow = new WDragWindow(m_pGrid);
				m_pDragWindow.setTag(new Object[]{mouseCol,new Boolean(false)});
				m_pDragWindow.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
							
				//----------- Draw column to image
				Image image = m_pGrid.createImage(mouseCol.getBounds().width,mouseCol.getBounds().height);
				Graphics2D g = (Graphics2D)image.getGraphics();
				
				// Fill backround
				g.setColor(ViewStyle.GetColorFromRgb(212,208,200));
				g.fillRect(0,0,mouseCol.getBounds().width,mouseCol.getBounds().height);
							
				// Paint text
				g.setColor(Color.BLACK);
				Paint.DrawText(g,mouseCol.getFont(),mouseCol.getText(),new Rectangle(0,0,mouseCol.getBounds().width,mouseCol.getBounds().height),HorizontalAlignment.Center);

				// Paint border around column
				PaintRaisedBorder(g,new Rectangle(0,0,mouseCol.getBounds().width,mouseCol.getBounds().height));
				
				g.dispose();
				//----------------------------------
				
				m_pDragWindow.Show(x,y - mouseCol.getBounds().height,mouseCol.getBounds().width,mouseCol.getBounds().height,image);
				
				m_pGrid.m_pFocusedView = this;
			//	m_pGrid.m_pDragView    = this;
			}
		}
		
		// There is current drag operation, update drag window location.
		if(m_pDragWindow != null){
			m_pDragWindow.UpdatePosition(m_pGrid.getLocationOnScreen().x + e.getPoint().x);
			
			WGridColumn mouseCol = GetColumnFromPoint(e.getPoint());
									
			// Draw column reorder destination column hot rect
			Object[] tag = (Object[])m_pDragWindow.getTag();
			
			// Restore default grid header
			Graphics2D g = m_pGrid.GetPaintGraphics();
			PaintHeader(g);
			m_pGrid.repaint(this.getColumnsRect());
			
			if(mouseCol != null && !mouseCol.equals(m_pDragColumn) && !((Boolean)tag[1]).booleanValue()){
				// Paint new destination column
				g.setColor(Color.GREEN);
				g.drawRect(mouseCol.getBounds().x,mouseCol.getBounds().y,mouseCol.getBounds().width - 1,mouseCol.getBounds().height - 1);
				m_pGrid.repaint(this.getColumnsRect());								
			}
		}	
	}
	//-------------------------------------------------------------------------//
	
	
	/**
	 * Is called when column porperty changed.
	 */
	protected void OnColumnChanged(WGridColumn column)
	{				
		HideEditor();
			
		Calculate(true);		
	}
			
	/**
	 * Calculates table view draw info.
	 */
	protected void Calculate(boolean repaint)
	{					
		int rowIndicatorsWidth = 20;
		if(!getShowRowIndicators()){
			rowIndicatorsWidth = 0;
		}
		int masterDetailWidth = 20;
		if(!getMasterDetail()){
			masterDetailWidth = 0;
		}
		m_ColumnsStartX   = getBounds().x + rowIndicatorsWidth + masterDetailWidth;
		int colsHeight    = CalculateColumnsHeight();
		int footherHeight = 21;
		if(!getShowFooter()){
			footherHeight = 0;
		}
		
		// Hide all this View sub grids
		for(WGridTableView view : m_pGrid.m_pActiveViews){
			if(view.m_pParentView == this){
				view.setBounds(new Rectangle(-1,-1,0,0));
				view.HideScrollBars();
			}
		}
							
		//--- Calculate columns info --------------------------------------//
		int colsWidth  = 0;		
		int colX       = m_ColumnsStartX;
		//--- Calculate columns total width -------//
		for(WGridColumn col : this.getColumns()){
			if(col.getVisible()){				
				colsWidth += col.getWidth();			
			}			
		}
		//-----------------------------------------//
		
		boolean hzScrollbarVisible = m_ColumnsStartX + colsWidth + 20 > this.getBounds().getMaxX();
		
		//--- Calculate columns bounds -------------//
		for(WGridColumn col : this.getColumns()){		
			if(col.getVisible()){
				if(hzScrollbarVisible){
					col.setBounds(new Rectangle(colX - m_pHzScrollBar.getPosition(),getBounds().y,col.getWidth(),colsHeight));
				}
				else{
					col.setBounds(new Rectangle(colX,getBounds().y,col.getWidth(),colsHeight));
				}
				colX += col.getWidth();			
			}			
		}
		//------------------------------------------------------------------//
				
		//--- Calculate rows info ---------------------------------------//
		boolean vtScrollbarVisible = m_FirstVisibleRow != 0;
		int rowStartY = this.getBounds().y + colsHeight;	
		int rowMaxAllowedY = (int)getBounds().getMaxY() - footherHeight;
		if(hzScrollbarVisible){
			rowMaxAllowedY -= m_pHzScrollBar.getBounds().height;
		}
		for(int i=m_FirstVisibleRow;i<m_pRowInfos.size();i++){
			RowInfo rowInfo = (RowInfo)m_pRowInfos.get(i);
					
			int rowHeight = CalculateRowHeight(rowInfo);
			// Row is over visble area
			if(rowStartY > rowMaxAllowedY){
				vtScrollbarVisible = true;
				break;
			}
			else{				
				rowInfo.setRowHeight(rowHeight);
				rowInfo.setBounds(new Rectangle(m_ColumnsStartX,rowStartY,colX - m_ColumnsStartX,rowHeight));
				
				// Update sub grid size,location
				if(rowInfo.getDetailView() != null){
					int maxHeight = (int)getRowsRect().getMaxY() - rowStartY - rowInfo.getRowHeight();
					rowInfo.getDetailView().setBounds(new Rectangle(rowInfo.getBounds().x,rowInfo.getBounds().y + rowInfo.getRowHeight(),getBounds().width - rowInfo.getBounds().x,maxHeight));
					rowInfo.getDetailView().AutoHeight();
					rowInfo.setBounds(new Rectangle(rowInfo.getBounds().x,rowInfo.getBounds().y,rowInfo.getBounds().width,rowInfo.getRowHeight() + rowInfo.getDetailView().getBounds().height));
				}
			}
			m_LastVisibleRow = i;
			rowStartY += rowInfo.getBounds().height;			
		}
		// See if last visible row partially visible
		if(m_pRowInfos.size() > 0 && !vtScrollbarVisible){
			if(rowStartY > rowMaxAllowedY){
				vtScrollbarVisible = true;
			}
		}
		

		// If vertical scrollbar visible, we need to update row and open View width
		if(vtScrollbarVisible){
			for(int i=m_FirstVisibleRow;i<=m_LastVisibleRow;i++){
				RowInfo rowInfo = m_pRowInfos.get(i);
				
				Rectangle r = rowInfo.getBounds();
				rowInfo.setBounds(new Rectangle(r.x,r.y,r.width - 18,r.height));				
				if(rowInfo.getDetailView() != null){
					r = rowInfo.getDetailView().getBounds();
					rowInfo.getDetailView().setBounds(new Rectangle(r.x,r.y,r.width - 18,r.height));
				}
			}
		}
		//--------------------------------------------------------------------------//
			
		//--- Set scrollbars -------------------------------------------------------//
		if(!hzScrollbarVisible){
			m_pHzScrollBar.setVisible(false);
			m_pHzScrollBar.setBounds(new Rectangle(-1,-1,0,0));
			m_pHzScrollBar.setPosition(0);
			m_pHzScrollBar.setMaximum(0);
		}
		else{			
			if(vtScrollbarVisible){
				m_pHzScrollBar.setBounds(new Rectangle(this.getBounds().x,(int)this.getBounds().getMaxY() - 17,(int)this.getBounds().width - 18,17));
			}
			else{
				m_pHzScrollBar.setBounds(new Rectangle(this.getBounds().x,(int)this.getBounds().getMaxY() - 17,(int)this.getBounds().width,17));
			}
			m_pHzScrollBar.setMaximum(m_ColumnsStartX + colsWidth - this.getBounds().width + 20);
			m_pHzScrollBar.setVisible(true);
		}		
		if(!vtScrollbarVisible){
			m_pVtScrollBar.setVisible(false);
			m_pVtScrollBar.setBounds(new Rectangle(-1,-1,0,0));
			m_pVtScrollBar.setPosition(0);
			m_pVtScrollBar.setMaximum(0);
		}
		else{	
			if(hzScrollbarVisible){
				m_pVtScrollBar.setBounds(new Rectangle((int)this.getBounds().getMaxX() - 18,this.getBounds().y,18,this.getBounds().height - 17));				
			}
			else{
				m_pVtScrollBar.setBounds(new Rectangle((int)this.getBounds().getMaxX() - 18,this.getBounds().y,18,this.getBounds().height));				
			}
			//
		/*	int maxRowsInView     = 0;
			int viewCurrentHeight = 0;
			int viewMaxRowsHeight = getRowsRect().height;
			for(int i=m_pRowInfos.size()-1;i>-1;i--){
				int rowHeight = CalculateRowHeight(m_pRowInfos.get(i));
				if(viewCurrentHeight < viewMaxRowsHeight){
					viewCurrentHeight += rowHeight;
					maxRowsInView++;
				}
				else{ 
					break;
				}
			}*/
		//	m_pVtScrollBar.setMaximum(m_pDataSource.getCount() - maxRowsInView + 1);
			m_pVtScrollBar.setMaximum(m_pDataSource.getCount() - 1);
			m_pVtScrollBar.setVisible(true);
		}
		//--------------------------------------------------------------------------//
								
		if(repaint){
			Repaint();
		}					
	}
	
	protected void HideScrollBars()
	{
		m_pHzScrollBar.setVisible(false);
		m_pVtScrollBar.setVisible(false);
	}
	
	protected void AutoHeight()
	{
		if(m_pRowInfos.size() == 0){
			return;
		}
		
		RowInfo rowInfo = m_pRowInfos.get(m_pRowInfos.size() - 1);
		if(m_FirstVisibleRow == 0 && m_pRowInfos.size() - 1 == m_LastVisibleRow){
			int height = (int)getRowsRect().getMaxY() - (int)rowInfo.getBounds().getY() - rowInfo.getRowHeight();
			if(height > 0){
				this.setBounds(new Rectangle(m_pBounds.x,m_pBounds.y,m_pBounds.width,m_pBounds.height - height + 2));
			}
		}
	}
	
	/**
	 * Calculats columns height.
	 * @return
	 */
	private int CalculateColumnsHeight()
	{
		if(getShowColumns()){
			return 20;
		}
		else{
			return 0;
		}
	}
		
	/**
	 * Calculates row height.
	 * @param rowInfo
	 * @return
	 */
	private int CalculateRowHeight(RowInfo rowInfo)
	{
		Graphics2D g = (Graphics2D)m_pGrid.getGraphics();
		if(g == null){
			return 19;
		}
		
		int rowHeight = 19;
		for(WGridColumn col : this.getColumns()){			
			int h = col.getEditor().CalculateHeight(g,rowInfo.GetValue(col),col);
			if(h > rowHeight){
				// TODO: don't allow bigger than grid
				rowHeight = h;
			}
		}
		
		return rowHeight;
	}
	
	/**
	 * Clears view selected rows.
	 *
	 */
	public void ClearSelection()
	{
		if(m_pActiveRow != null){
			RowInfo activeRow = m_pActiveRow;

			m_pActiveRow = null;
			RepaintRow(activeRow);
		}
		
		ArrayList selectedRows = (ArrayList)m_pSelectedRows.clone();
		m_pSelectedRows.clear();
		
		for(int i=0;i<selectedRows.size();i++){
			RowInfo row = (RowInfo)selectedRows.get(i);
			
			RepaintRow(row);			
		}
	}

		
	/**
	 * Activates specified row. If row isn't visible, it will be scrolled to visible area.
	 * @param rowInfo
	 * @param col
	 * @param clearSelection
	 */
	private void SelectRow(RowInfo rowInfo,WGridColumn col,boolean clearSelection)
	{	
		// Row or column didn't changed
		if(m_pActiveRow == rowInfo && m_pActiveColumn == col){
			ShowEditor(col);
			return;
		}
		// New row different from active row, but active row modified and we can't allow to chage row. 
		else if(m_pActiveRow != rowInfo && IsActiveRowModified()){
			return;
		}
		
		HideEditor();
		
		// Focused view changed
		if(m_pGrid.m_pFocusedView != null && !m_pGrid.m_pFocusedView.equals(this)){
			m_pGrid.m_pFocusedView.HideEditor();
			m_pGrid.m_pFocusedView.ClearSelection();
		}
		
		// Clear all selected rows
		if(clearSelection){
			ArrayList selectedRows = (ArrayList)m_pSelectedRows.clone();
			m_pSelectedRows.clear();
			
			for(int i=0;i<selectedRows.size();i++){
				RowInfo row = (RowInfo)selectedRows.get(i);
				
				RepaintRow(row);			
			}			
		}
		
		RowInfo oldRow = m_pActiveRow;
					
		if(col != null){
			m_pActiveColumn = col;
		}
					
		m_pGrid.m_pFocusedView = this;
		m_pActiveRow = rowInfo;
		if(oldRow != m_pActiveRow){
			m_ActiveRowValues = (Object[])rowInfo.getRowSource().getRow().getItemArray().clone();
		}
		// add active row selected rows list
		if(!m_pSelectedRows.contains(m_pActiveRow)){
			m_pSelectedRows.add(m_pActiveRow);
		}
		
		boolean repaintNeeded = false;
		//--- Scroll if needed ----------------------------------------------//
		if(m_FirstVisibleRow > m_pRowInfos.indexOf(m_pActiveRow)){
			m_pVtScrollBar.setPosition(m_pRowInfos.indexOf(m_pActiveRow));
			m_FirstVisibleRow = m_pVtScrollBar.getPosition();
			Calculate(false);
			
			repaintNeeded = true;
		}
		else if(m_LastVisibleRow > -1 && m_LastVisibleRow < m_pRowInfos.indexOf(m_pActiveRow)){	
			int counter = 0;
			while(m_LastVisibleRow < m_pRowInfos.indexOf(m_pActiveRow) && counter < m_pRowInfos.size()){
				m_pVtScrollBar.setPosition(m_pVtScrollBar.getPosition() + 1);
				m_FirstVisibleRow = m_pVtScrollBar.getPosition();
				Calculate(false);
				counter++;
			}
			repaintNeeded = true;
		}
		//--------------------------------------------------------------------//
			
		// Row isn't visible, make it fully visible
		if(m_LastVisibleRow > -1 && getRowsRect().getMaxY() < m_pActiveRow.getBounds().getMaxY()){
			int counter = 0;
			while(getRowsRect().getMaxY() < m_pActiveRow.getBounds().getMaxY() && counter < m_pRowInfos.size()){				
				m_pVtScrollBar.setPosition(m_pVtScrollBar.getPosition() + 1);
				m_FirstVisibleRow = m_pVtScrollBar.getPosition();
				Calculate(false);
				counter++;
			}
			repaintNeeded = true;
		}
		
		// Column isn't fully visible, make it
		if(col != null){
			if(col.getBounds().getMaxX() > this.getBounds().getMaxX() - m_pVtScrollBar.getWidth()){
				m_pHzScrollBar.setPosition(m_pHzScrollBar.getPosition() + (int)col.getBounds().getMaxX() - (int)this.getBounds().getMaxX() + m_pVtScrollBar.getWidth() + 10);
				Calculate(false);
				repaintNeeded = true;
			}
			if(col.getBounds().x < m_ColumnsStartX){
				m_pHzScrollBar.setPosition(m_pHzScrollBar.getPosition() - (m_ColumnsStartX - col.getBounds().x));
				Calculate(false);
				repaintNeeded = true;
			}
		}
		
		if(oldRow != m_pActiveRow){
			m_pGrid.RaiseOnActiveRowChanged(m_pActiveRow.getRowSource());
		}
		
		// We need to repaint all
		if(repaintNeeded){
			Repaint();
		}
		// Repaint old and new active row 
		else{
			if(oldRow != null){
				RepaintRow(oldRow);
			}
			RepaintRow(m_pActiveRow);
		}
		
		if(col != null){
			ShowEditor(col);
		}
	}
	
	/**
	 * Selects first visible row.
	 *
	 */
	private void SelectFirstVisibleRow()
	{
		SelectRow((RowInfo)m_pRowInfos.get(m_FirstVisibleRow),m_pActiveColumn,true);
	}
	
	/**
	 * Selects last visible row.
	 *
	 */
	private void SelectLastVisibleRow()
	{
		RowInfo lastVisbleRow = null;
		for(int i=m_FirstVisibleRow;i<=m_LastVisibleRow;i++){
			RowInfo rowInfo = (RowInfo)m_pRowInfos.get(i);
			if(rowInfo.getBounds().getMaxY() <= getRowsRect().getMaxY()){
				lastVisbleRow = rowInfo;
			}
			else{
				break;
			}
		}
		
		SelectRow(lastVisbleRow,m_pActiveColumn,true);
	}
	
	/**
	 * Selects next row. If next row isn't visible, then scrolls and makes that row visible.
	 *
	 */
	private void SelectNextRow()
	{				
		SelectNextRow(m_pActiveRow);
	}
	
	/**
	 * Selects next row from specified row. If next row isn't visible, then scrolls and makes that row visible.
	 * @param row
	 */
	private void SelectNextRow(RowInfo row)
	{
		if(m_pRowInfos.indexOf(row) < m_pRowInfos.size() - 1){
			SelectRow((RowInfo)m_pRowInfos.get(m_pRowInfos.indexOf(row) + 1),m_pActiveColumn,true);
		}
	}
	
	/**
	 * Selects previous row. If next previous isn't visible, then scrolls and makes that row visible.
	 *
	 */
	private void SelectPreviousRow()
	{
		if(m_pRowInfos.indexOf(m_pActiveRow) > 0){			
			SelectRow((RowInfo)m_pRowInfos.get(m_pRowInfos.indexOf(m_pActiveRow) - 1),m_pActiveColumn,true);
		}
	}
	
	/**
	 * Selects specified range of rows.
	 * @param startRow Selection start.
	 * @param endRow Selection end.
	 */
	private void SelectRows(RowInfo startRow,RowInfo endRow)
	{
		m_pSelectedRows.clear();
		
		if(m_pRowInfos.indexOf(startRow) == m_pRowInfos.indexOf(endRow)){
			m_pSelectedRows.add(startRow);
		}
		if(m_pRowInfos.indexOf(startRow) < m_pRowInfos.indexOf(endRow)){
			for(int i=m_pRowInfos.indexOf(startRow);i<=m_pRowInfos.indexOf(endRow);i++){
				m_pSelectedRows.add((RowInfo)m_pRowInfos.get(i));
			}
		}
		else{
			for(int i=m_pRowInfos.indexOf(endRow);i<=m_pRowInfos.indexOf(startRow);i++){
				m_pSelectedRows.add((RowInfo)m_pRowInfos.get(i));
			}
		}
		
		SelectRow(endRow,m_pActiveColumn,false);
	}
			
	/**
	 * Toggles row selection. If row is selected then unselects it, if row is unselected then selects it.
	 * @param rowInfo
	 * @return
	 */
	private boolean ToggleRowSelection(RowInfo rowInfo)
	{
		if(m_pSelectedRows.contains(rowInfo)){
			m_pSelectedRows.remove(rowInfo);
			
			if(rowInfo.equals(m_pActiveRow)){
				m_pActiveRow = null;
			}
			
			return false;
		}
		else{							
			SelectRow(rowInfo,m_pActiveColumn,false);
			
			return true;
		}
	}
	
	/**
	 * Selects first editable column in active row.
	 *
	 */
	public void SelectFirstEditableColumn()
	{
		HideEditor();
		m_pActiveColumn = null;
		MoveNext();
	}
	
	
	/**
	 * Gets column from specified point or null if there isn't any column at that point.
	 * @param pt
	 * @return
	 */
	private WGridColumn GetColumnFromPoint(Point pt)
	{
		for(WGridColumn col : this.getColumns()){					
			if(col.getVisible() && col.getBounds().contains(pt)){
				return col;
			}
		}
	
		return null;
	}
	
	/**
	 * Gets row from specified point or null if there isn't any row at that point.
	 * @param pt
	 * @return
	 */
	private RowInfo GetRowFromPoint(Point pt)
	{
		if(m_pRowInfos.size() == 0){
			return null;
		}
		
		// Find hitted row		
		for(int i=m_FirstVisibleRow;i<=m_LastVisibleRow;i++){
			RowInfo rowInfo = m_pRowInfos.get(i);
			
			if(this.getRowsRect().contains(pt) && pt.y >= rowInfo.getBounds().y && pt.y <= rowInfo.getBounds().getMaxY()){
				return rowInfo;
			}
		}
		
		return null;
	}
		
	/**
	 * Acivates specified column editor.
	 * @param col
	 */	
	private void ShowEditor(WGridColumn col)
	{
		HideEditor();
		
		// If grid readonly or column isn't visible, don't activate editor.
		if(m_AllowEdit && col != null && col.getVisible() && col.getAllowEdit() && m_pActiveRow != null){
			m_pActiveEditor = col.getEditor();
			UpdateActiveEditor();
			m_pGrid.add(m_pActiveEditor);			
			m_pActiveEditor.StartEdit(this,m_pActiveRow.GetValue(col));
		
			RepaintRow(m_pActiveRow);
		}
	}
	
	/**
	 * Hides active editor, if exists.
	 *
	 */
	private void HideEditor()
	{
		// Hide active editor, if any
		if(m_pActiveEditor != null){
	//		if(m_pActiveEditor.getIsModified()){ // TODO: call validate cell
		//		System.out.println("mod");
	//		}
			
			// Store editor value to DataRowView.
			if(m_pActiveEditor.getIsModified()){
				m_pActiveRow.getRowSource().set(m_pActiveColumn.getMappingName(),m_pActiveEditor.getEditValue());
			}
					
			m_pGrid.requestFocus();
			m_pGrid.remove(m_pActiveEditor);			
			m_pActiveEditor = null;			
		}
	}
	
	/**
	 * Updates active editor size and location.
	 *
	 */
	private void UpdateActiveEditor()
	{
		WGridColumn col = m_pActiveColumn;
		if(m_pActiveEditor != null){
			// Part of Column is bigger or out of view
			if(col.getBounds().getMaxX() > this.getBounds().getMaxX() - m_pVtScrollBar.getWidth()){
				m_pActiveEditor.setBounds(col.getBounds().x + 2,m_pActiveRow.getY() + 2,(int)this.getBounds().getMaxX() - m_pVtScrollBar.getWidth() - col.getBounds().x - 2,m_pActiveRow.getRowHeight() - 3);
			}
			else{
				m_pActiveEditor.setBounds(col.getBounds().x + 2,m_pActiveRow.getY() + 2,col.getBounds().width - 3,m_pActiveRow.getRowHeight() - 3);
			}
			if(!m_pActiveEditor.hasFocus()){
				m_pActiveEditor.requestFocus();
			}
		}
	}
	
	
	/**
	 * Gets if active row is modified.
	 * @return If some of the active row values modified, returns true. If no active row, returns false.
	 */
	public boolean IsActiveRowModified()
	{
		if(m_ActiveRowValues != null && m_pActiveRow != null){			
			for(int i=0;i<m_ActiveRowValues.length;i++){
				if(m_ActiveRowValues[i] == null || m_pActiveRow.getRowSource().get(i) == null){
					if(m_ActiveRowValues[i] == null && m_pActiveRow.getRowSource().get(i) != null){
			//			System.out.println("1 true v:" + m_pActiveRow.getRowSource().get(i));
						return true;
					}
				}
				else if(!m_ActiveRowValues[i].equals(m_pActiveRow.getRowSource().get(i))){
					return true;
				}
			}
		}
		if(m_pActiveEditor != null && m_pActiveEditor.getIsModified()){
			return true;
		}
		
		return false;
	}
		
		
	/**
	 * Deletes active row.
	 *
	 */
	public void DeleteActiveRow()
	{
		if(m_pActiveRow != null && !m_pActiveRow.getRowSource().getIsNew()){
			// Raise delete row event and see if allowed or canceled
			if(m_pGrid.RaiseDeleteRow(m_pActiveRow.getRowSource())){
				RowInfo newActiveRow = null;
				int index = m_pRowInfos.indexOf(m_pActiveRow);			
				if(index < m_pRowInfos.size()){
					newActiveRow = m_pRowInfos.get(index + 1);
				}
				else if(index > 0){
					newActiveRow = m_pRowInfos.get(index - 1);
				}			
				
				this.HideEditor();
				m_pRowInfos.remove(m_pActiveRow);
				m_pActiveRow.getRowSource().Delete();
				m_pActiveRow = null;			
				
				this.Calculate(true);
				SelectRow(newActiveRow,m_pActiveColumn,true);
			}
		}
	}
			
	/**
	 * Moves current column to next column or if last column, then moves to next row.
	 *
	 */
	public void MoveNext()
	{
		// No columns, we can't move
		if(m_pColumns.getCount() == 0){
			return;
		}
		// There is no rows where to move
		if(m_pDataSource == null || m_pDataSource.getCount() == 0){
			return;
		}
		
		// There is no active row, make first row as active.
		if(m_pActiveRow == null && m_pRowInfos.size() > 0 && getColumns().getVisibleColumns().length > 0){
			SelectRow(m_pRowInfos.get(0),getColumns().getVisibleColumns()[0],true);			
			return;
		}
		
		int activeColIndex = -1;
		if(m_pActiveColumn != null){
			activeColIndex = m_pColumns.indexOf(m_pActiveColumn);
		}
	
		// If view is editable and current cell is modified, call validate cell value event.
		if(m_pActiveEditor != null && m_pActiveEditor.getIsModified() && !m_pGrid.RaiseValidateCellValue(m_pActiveColumn,m_pActiveRow.getRowSource(),m_pActiveEditor.getEditValue())){
			return;
		}
		HideEditor();
				
		boolean moveNextRow = false;						
		// Move next column.
		if(activeColIndex < m_pColumns.getCount() - 1){
			WGridColumn column = m_pColumns.get(activeColIndex + 1);
				
			// If column not visible or view is editable and column isn't, move next editable column or row.
			if(!column.getVisible() || (getAllowEdit() && !column.getAllowEdit())){
				moveNextRow = true;
					
				// Find next visible and editable column, if exists.
				while(m_pColumns.indexOf(column) < m_pColumns.getCount() - 1){
					column = m_pColumns.get(m_pColumns.indexOf(column) + 1);						
					if((column.getVisible() && !getAllowEdit()) || (column.getVisible() && column.getAllowEdit())){
						moveNextRow = false;
						break;
					}
				}					
			}
				
			if(!moveNextRow){
				SelectRow(m_pActiveRow,column,false);
			}
		}
		// No next editable column.
		else{
			moveNextRow = true;
		}
			
		// Move next row
		if(moveNextRow){
			// Update or Add new row
			if(getAllowEdit()){										
				if(m_pActiveRow.getRowSource().getIsNew()){
					if(m_pGrid.RaiseAddNewRow(m_pActiveRow.getRowSource())){
						m_pActiveRow.getRowSource().EndEdit();
							
						// Append new item row
						if(getAllowEdit()){
							AddNewItemRow();
							this.Calculate(true);
						}
						
						m_pActiveColumn = null;
						m_ActiveRowValues = null;
						// Move to first visible column of if ediatable then to first ediatable column
						MoveNext();
						SelectNextRow();
					}
				}
				else{
					// Call update only if active row is modified.
					if(this.IsActiveRowModified()){
						// Update row canceled
						if(!m_pGrid.RaiseUpdateRow(m_pActiveRow.getRowSource())){
							return;
						}						
					}
					
					m_pActiveColumn = null;
					m_ActiveRowValues = null;
					// Move to first visible column of if ediatable then to first ediatable column
					MoveNext();
					SelectNextRow();
				}
			}
			else{
				m_pActiveColumn = null;
				m_ActiveRowValues = null;
				// Move to first visible column of if ediatable then to first ediatable column
				MoveNext();
				SelectNextRow();
			}		
		}
	}
	
	/**
	 * Expands detail view.
	 * @param rowInfo
	 */
	private void ExpandRow(RowInfo rowInfo)
	{
		try{
			if(getMasterDetail() && rowInfo.getDetailView() == null){
				WGridTableView detailView = m_pDetailView.Clone();
				detailView.m_pParentView = this;
				detailView.m_pParentRow = rowInfo;
				detailView.setDataSource(rowInfo.getRowSource().CreateChildView(m_pRelation.getRelationName()));
				m_pGrid.m_pActiveViews.add(detailView);			
				rowInfo.setDetailView(detailView);
				
				this.Calculate(true);
			}
		}
		catch(Exception x){
			// This never should happen
			x.printStackTrace();
		}
	}
	
	/**
	 * Collapses detail view.
	 * @param rowInfo
	 */
	private void CollapseRow(RowInfo rowInfo)
	{
		if(rowInfo.getDetailView() != null){
			m_pGrid.m_pFocusedView = this;
			m_pGrid.m_pActiveViews.remove(rowInfo.getDetailView());
			rowInfo.getDetailView().Dispose();
			rowInfo.setDetailView(null);
			rowInfo.setBounds(new Rectangle(rowInfo.getBounds().x,rowInfo.getBounds().y,rowInfo.getBounds().width,rowInfo.getRowHeight()));
					
			this.Calculate(true);
		}
	}
	
	/**
	 * Toggles detail view. If view is open then closes it, if view is closed then opens it.
	 * @param rowInfo
	 */
	private void ToggleRow(RowInfo rowInfo)
	{
		if(rowInfo.getDetailView() == null){
			ExpandRow(rowInfo);
		}
		else{
			CollapseRow(rowInfo);
		}
	}
	
	/**
	 * Restores active row default values.
	 *
	 */
	public void UndoRow()
	{
		if(m_pActiveRow != null){
			m_pActiveRow.getRowSource().getRow().setItemArray(m_ActiveRowValues);

			if(m_pActiveEditor != null){
				m_pActiveEditor.setValue(m_pActiveRow.getRowSource().get(m_pActiveColumn.getMappingName()));
			}
			
			SelectFirstEditableColumn();
		}
	}
	
	/**
	 * Shows new item row if allowed.
	 *
	 */
	private void AddNewItemRow()
	{
		// Append new item row
		if(getAllowAppendNewRow()){
			DataRowView dr = m_pDataSource.AddNew();

			// Raise NewItemRowAdded event
			m_pGrid.RaiseNewItemRowAdded(dr);
			
			// This is child grid, we need to fill relation column
			if(m_pParentRow != null && m_pRelation != null){
				dr.set(m_pRelation.getChildColumns()[0].getColumnName(),m_pParentRow.getRowSource().get(m_pRelation.getParentColumns()[0].getColumnName()));
			}

			m_pRowInfos.add(new RowInfo(this,dr));
		}
	}
	
	private Rectangle getMasterDetailRect()
	{
		if(getMasterDetail()){
			if(getShowRowIndicators()){
				return new Rectangle(getBounds().x + 20,getBounds().y,20,getBounds().height);
			}
			else{
				return new Rectangle(getBounds().x,getBounds().y,20,getBounds().height);
			}
		}
		else{
			return new Rectangle(-1,-1,0,0);
		}
	}
	
	private Rectangle getColumnsRect()
	{	
		if(getShowColumns()){
			return new Rectangle(m_ColumnsStartX,getBounds().y,this.getBounds().width - m_pVtScrollBar.getBounds().width,20);
		}
		else{
			return new Rectangle(-1,-1,0,0);
		}
	}
	
	private Rectangle getFooterRect()
	{
		if(getShowFooter()){
			return new Rectangle(getBounds().x,(int)getBounds().getMaxY() - 20 - m_pHzScrollBar.getBounds().height,this.getBounds().width - m_pVtScrollBar.getBounds().width,21);
		}
		else{
			return new Rectangle(-1,-1,0,0);
		}
	}
	
	private Rectangle getRowsRect()
	{
		if(!m_pVtScrollBar.isVisible()){
			return new Rectangle(getBounds().x,getBounds().y + getColumnsRect().height,getBounds().width - 1 - m_pVtScrollBar.getBounds().width,getBounds().height - getColumnsRect().height - getFooterRect().height - m_pHzScrollBar.getBounds().height + 1);
		}
		else{
			return new Rectangle(getBounds().x,getBounds().y + getColumnsRect().height,getBounds().width - m_pVtScrollBar.getBounds().width,getBounds().height - getColumnsRect().height - getFooterRect().height - m_pHzScrollBar.getBounds().height + 1);
		}
	}
	
	
	/**
	 * Returns components owned by this view.
	 * @return 
	 */
	protected ArrayList getComponents()
	{
		ArrayList c = new ArrayList();
		c.add(m_pVtScrollBar);
		c.add(m_pHzScrollBar);
		
		return c;
	}
	
	
	
	/**
	 * Gets grid data source.
	 * @return
	 */
	public DataView getDataSource()
	{
		return m_pDataSource;
	}
	/**
	 * Sets grid data source.
	 * @param table
	 */
	public void setDataSource(DataTable table)
	{
		if(table != null){
			setDataSource(table.getDefaultView());
		}
		else{
			setDataSource((DataView)null);
		}
	}
	/**
	 * Sets grid data source.
	 * @param table
	 */
	public void setDataSource(DataView table)
	{
		HideEditor();
		m_pRowInfos.clear();
		m_pSelectedRows.clear();
		m_FirstVisibleRow = 0;
		m_LastVisibleRow  = -1;
		m_pVtScrollBar.setPosition(0);
		m_pVtScrollBar.setMaximum(0);
		m_pHzScrollBar.setPosition(0);
		m_pHzScrollBar.setMaximum(0);
		m_pActiveRow      = null;
		m_pActiveColumn   = null;
		m_pRelation       = null;
				
		// If no data source, create new empty placeholder table
		if(table == null){
			try{
				DataSet ds = new DataSet();
				table = ds.getTables().Add("empty").getDefaultView();
			}
			catch(Exception x){				
			}
		}
		
		// Remove old event listener
		if(m_pDataSource != null && m_pDataViewListener != null){
			m_pDataSource.removeListChangedListener(m_pDataViewListener);
		}
		// Create new event listener
		m_pDataViewListener = new ActionListener(){
			public void actionPerformed(ActionEvent e){
				OnDataViewChanged();
			}
		};
		table.addListChangedListener(m_pDataViewListener);
		
		m_pDataSource = table;	
		
		// See if there is any datarelation for this table
	/*	DataRelationCollection relations = m_pDataSource.getTable().getDataSet().getRelations();
		for(int i=0;i<relations.getCount();i++){
			DataRelation relation = relations.get(i);
			if(relation.getParentColumns()[0].getTable().equals(m_pDataSource.getTable())){
				m_pRelation = relation;
				m_MasterDetail = true;
				break;
			}
		}*/		
				
		// Construct row info holder
		for(int i=0;i<m_pDataSource.getCount();i++){
			m_pRowInfos.add(new RowInfo(this,m_pDataSource.get(i)));
		}
		
		// Append new item row
		if(getAllowAppendNewRow()){
			AddNewItemRow();
		}
		
	
		Calculate(true);
			
		if(m_pParentView == null){		
			MoveNext();
		}
	}
	
	/**
	 * Gets detail view of this view.
	 * @return
	 */
	public WGridTableView getDetailView()
	{
		return m_pDetailView;
	}
	/**
	 * Sets detail view of this view. NOTE: you also need to specify this.setDataRelation to make view master/detail.
	 * @param value
	 */
	public void setDetailView(WGridTableView value)
	{
		if(m_pDetailView != value){
			m_pDetailView = value;
			
			Calculate(true);
		}
	}
	
	/**
	 * Gets detail view datarelation.
	 * @return
	 */
	public DataRelation getDataRelation()
	{
		return m_pRelation;
	}
	/**
	 * Sets detail view datarelation.
	 * @param value
	 */
	public void setDataRelation(DataRelation value)
	{
		if(m_pRelation != value){
			m_pRelation = value;
			
			Calculate(true);
		}
	}
	
	/**
	 * Gets is view is master/detail view.
	 * @return
	 */
	public boolean getMasterDetail()
	{
		return (m_pRelation != null && m_pRelation != null);
	}
	
	/**
	 * Gets if data is editable.
	 * @return
	 */
	public boolean getAllowEdit()
	{
		return m_AllowEdit;
	}
	/**
	 * Sets if data is editable.
	 * @param value
	 */
	public void setAllowEdit(boolean value)
	{
		if(m_AllowEdit != value){
			m_AllowEdit = value;
			
			if(m_pDataSource == null){
				return;
			}
			
			if(!m_AllowEdit){
				HideEditor();
				
		/*		// Hide NewItem row
				for(DataRowView dr : m_pDataSource){
					if(dr.getIsNew()){
						// Delete this row from RowInfos
						for(int r=0;r<m_pRowInfos.size();r++){
							RowInfo rowInfo = (RowInfo)m_pRowInfos.get(r);
							if(rowInfo.getRowSource().equals(dr)){
								m_pRowInfos.remove(rowInfo);
								break;
							}
						}
						
						dr.Delete();
						break;
					}
				}	*/			
			}
			else if(getAllowAppendNewRow()){
				// Show NewItem row
		//		AddNewItemRow();			
			}
							
		//	m_pActiveRow    = null;
		//	m_pActiveColumn = null;
		//	MoveNext();
			SelectFirstEditableColumn();
			
			Calculate(true);			
		}
	}
	
	/**
	 * Gets if view allows user to add new rows.
	 * @return
	 */
	public boolean getAllowAppendNewRow()
	{
		return m_AllowAppendNewRow;
	}
	/**
	 * Sets if view allows user to add new rows.
	 * @param value
	 */
	public void setAllowAppendNewRow(boolean value)
	{
		if(m_AllowAppendNewRow != value){
			m_AllowAppendNewRow = value;
			
		//	if(getAllowEdit()){
			if(m_pDataSource != null){
				if(m_AllowAppendNewRow){
					// Show NewItem row
					AddNewItemRow();
				}
				else{
					HideEditor();
				
					// Hide NewItem row
					for(DataRowView dr : m_pDataSource){
						if(dr.getIsNew()){
							// Delete this row from _RowInfos
							for(int r=0;r<m_pRowInfos.size();r++){
								RowInfo rowInfo = (RowInfo)m_pRowInfos.get(r);
								if(rowInfo.getRowSource().equals(dr)){
									m_pRowInfos.remove(rowInfo);
									break;
								}
							}
							
							dr.Delete();
							break;
						}
					}
				}

				Calculate(true);
			}
		}
	}
	
	/**
	 * Gets if view is sortable.
	 * @return
	 */
	public boolean getAllowSort()
	{
		return m_AllowSort;
	}
	/**
	 * Sets is view is sortable.
	 * @param value
	 */
	public void setAllowSort(boolean value)
	{
		if(m_AllowSort != value){
			m_AllowSort = value;
			
			Calculate(true);
		}
	}
	
	/**
	 * Gets if multiple selection is allowed.
	 * @return
	 */
	public boolean getAllowMultiSelect()
	{
		return m_AllowMultiSelect;
	}
	/**
	 * Sets if multiple selection is allowed.
	 * @param value
	 */
	public void setAllowMultiSelect(boolean value)
	{
		if(m_AllowMultiSelect != value){
			m_AllowMultiSelect = value;
		}
	}
	
	/**
	 * Gets if columns reordering is allowed.
	 * @return
	 */
	public boolean getAllowReorderColumns()
	{
		return m_AllowReorderColumns;
	}
	/**
	 * Sets if columns reordering is allowed.
	 * @param value
	 */
	public void setAllowReorderColumns(boolean value)
	{
		if(m_AllowReorderColumns != value){
			m_AllowReorderColumns = value;
		}
	}
				
	/**
	 * Gets if enter key simulates mouse double click or moves next.
	 * @return
	 */
	public boolean getEnterKeyDoubleClicks()
	{
		return m_EnterKeyDoubleClicks;
	}
	/**
	 * Sets if enter key simulates mouse double click or moves next.
	 * @param value
	 */
	public void setEnterKeyDoubleClicks(boolean value)
	{
		if(m_EnterKeyDoubleClicks != value){
			m_EnterKeyDoubleClicks = value;
		}
	}
	
	/**
	 * Gets if maximize/minimaize button is shown to Main grid view.
	 * NOTE: This property works for Main View only.
	 * @return
	 */
	public boolean getShowMaximizeForMainView()
	{
		return m_ShowMaximizeForMainView;
	}
	/**
	 * Sets if maximize/minimaize button is shown to Main grid view.
	 * NOTE: This property works for Main View only.
	 * @return
	 */
	public void setShowMaximizeForMainView(boolean value)
	{
		m_ShowMaximizeForMainView = value;
	}
	
	/**
	 * Gets if columns are visible.
	 * @return
	 */
	public boolean getShowColumns()
	{
		return m_ShowColumns;
	}
	/**
	 * Sets if columns are visible.
	 * @param value
	 */
	public void setShowColumns(boolean value)
	{
		if(m_ShowColumns != value){
			m_ShowColumns = value;
			
			Calculate(true);
		}
	}
	
	/**
	 * Gets if row indicators are visible.
	 * @return
	 */
	public boolean getShowRowIndicators()
	{
		return m_ShowRowIndicators;
	}
	/**
	 * Sets if row indicators are visible.
	 * @param value
	 */
	public void setShowRowIndicators(boolean value)
	{
		if(m_ShowRowIndicators != value){
			m_ShowRowIndicators = value;
			
			Calculate(true);
		}
	}
	
	/**
	 * Gets if footer is visible.
	 * @return
	 */
	public boolean getShowFooter()
	{
		return m_pShowFooter;
	}
	/**
	 * Sets if foother is visible.
	 * @param value
	 */
	public void setShowFooter(boolean value)
	{
		if(m_pShowFooter != value){
			m_pShowFooter = value;
			
			Calculate(true);
		}
	}
	
	/**
	 * Gets if current cell is shown.
	 * NOTE: This property works only if View is not editable, otherwise it has no effect.
	 * @return
	 */
	public boolean getShowCurrentCell()
	{
		return m_ShowCurrentCell;
	}
	/**
	 * Sets if current cell is shown.
	 * NOTE: This property works only if View is not editable, otherwise it has no effect.
	 * @param value
	 */
	public void setShowCurrentCell(boolean value)
	{
		if(m_ShowCurrentCell != value){
			m_ShowCurrentCell = value;
			
			Calculate(true);
		}
	}
	
	/**
	 * Gets if vertical grid lines are shwon.
	 * @return
	 */
	public boolean getShowVerticalGridlines()
	{
		return m_ShowVerticalGridlines;
	}
	/**
	 * Sets if vertical grid lines are shwon.
	 * @param value
	 */
	public void setShowVerticalGridlines(boolean value)
	{
		if(m_ShowVerticalGridlines != value){
			m_ShowVerticalGridlines = value;
			
			Calculate(true);
		}
	}
	
	/**
	 * Gets if vertical grid lines are shwon.
	 * @return
	 */
	public boolean getShowHorizontalGridlines()
	{
		return m_ShowHorizontalGridlines;
	}
	/**
	 * Sets if vertical grid lines are shwon.
	 * @param value
	 */
	public void setShowHorizontalGridlines(boolean value)
	{
		if(m_ShowHorizontalGridlines != value){
			m_ShowHorizontalGridlines = value;
			
			Calculate(true);
		}
	}
					
	/**
	 * Gets columns collection.
	 * @return
	 */
	public WGridColumns getColumns()
	{
		return m_pColumns;
	}
	
	/**
	 * Gets view bounding rectangle.
	 * @return
	 */
	public Rectangle getBounds()
	{
		return m_pBounds;
	}
	/**
	 * Sets table view bounding rectangle.
	 * @param value
	 */
	protected void setBounds(Rectangle value)
	{
		if(!m_pBounds.equals(value)){			
			m_pBounds = value;				
			this.Calculate(false);
		}
	}
	
	/**
	 * Gets currently active editor or null if no active editor.
	 * @return
	 */
	public WBaseEditor getActiveEditor()
	{
		return m_pActiveEditor;
	}
	
	/**
	 * Gets active cell column or null if no current cell.
	 * @return
	 */
	public WGridColumn getActiveColumn()
	{
		return m_pActiveColumn;
	}
	
	/**
	 * Gets currently active row or null if no active row.
	 * @return
	 */
	public DataRowView getActiveRow()
	{
		if(m_pActiveRow != null){
			return m_pActiveRow.getRowSource();
		}
		else{
			return null;
		}
	}
	
	/**
	 * Returns currently selected rows.
	 * @return
	 */
	public DataRowView[] getSelectedRows()
	{
		DataRowView[] retVal = new DataRowView[m_pSelectedRows.size()];
		for(int i=0;i<m_pSelectedRows.size();i++){
			retVal[i] = m_pSelectedRows.get(i).getRowSource();
		}
		
		return retVal;
	}
	
	/**
	 * Gets tag (user data).
	 * @return
	 */
	public Object getTag()
	{
		return m_pTag;
	}
	/**
	 * Sets tag (user data).
	 * @param value
	 */
	protected void setTag(Object value)
	{
		if(m_pTag != value){
			m_pTag = value;
		}
	}
	
	/**
	 * Gets view owner grid control.
	 * @return
	 */
	public WGridControl getGrid()
	{
		return m_pGrid;
	}

}
