context: // created on 6/14/2004 at 10:35 PM
  /*
   *   Copyright (c) 2004, Alexandros Frantzis (alf82 [at] freemail [dot] gr)
   *
   *   This file is part of Bless.
   *
   *   Bless is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   Bless is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with Bless; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  using System.Collections;
  using System;
  using Gtk;
  using Bless.Buffers;
  using Bless.Gui.Areas;
  using Bless.Gui.Drawers;
  using Bless.Util;
  using Bless.Tools;
  
  namespace Bless.Gui {
  
  ///<summary>An interface to a widget that displays data from a buffer</summary>
  public class DataView {
  	Layout layout;
  	ByteBuffer byteBuffer;
  	Gtk.VScrollbar vscroll;
  	bool widgetRealized;
  	string prefID;
  
  	DataViewControl dvControl;
  	DataViewDisplay dvDisplay;
  
  	// clipboard related
  	byte[] clipdata;
  	Gtk.Clipboard clipboard;
  
  	static TargetEntry[] clipboardTargets = new TargetEntry[]{
  												new TargetEntry("application/octet-stream", 0, 0),
  												new TargetEntry("UTF8_STRING", 0, 0)
  											};
  
  	bool overwrite;
  	bool notification;
  
  	//undo-redo related
  	Deque<CursorState> cursorUndoDeque;
  	Deque<CursorState> cursorRedoDeque;
  
  	public Deque<CursorState> CursorUndoDeque {
  		get { return cursorUndoDeque; }
  	}
  
  	public Deque<CursorState> CursorRedoDeque {
  		get { return cursorRedoDeque; }
  	}
  
  	public DataViewControl Control {
  		get { return dvControl; }
  	}
  
  	public DataViewDisplay Display {
  		get { return dvDisplay; }
  	}
  
  	public ByteBuffer Buffer {
  		get { return byteBuffer; }
  		set {
  			if (value != null)
  				SetupByteBuffer(value);
  			else
  				CleanupByteBuffer();
  		}
  	}
  
  	public bool Overwrite {
  		set {
  			overwrite = value;
  			if (OverwriteChanged != null)
  				OverwriteChanged(this);
  		}
  
  		get { return overwrite; }
  	}
  
  	public Area FocusedArea {
  		get { return dvDisplay.Layout.AreaGroup.FocusedArea; }
  	}
  
  	///<summary>
  	/// Whether the DataView should notify the user
  	/// that something important is going on
  	///</summary>
  	public bool Notification  {
  		get { return notification; }
  		set {
  			notification = value;
  			if (NotificationChanged != null)
  				NotificationChanged(this);
  		}
  	}
  
  	private void OnByteBufferChanged(ByteBuffer bb)
  	{
  		Gtk.Application.Invoke(delegate {
  			if (byteBuffer.ReadAllowed)
  				dvDisplay.Layout.AreaGroup.RedrawNow();
  		});
  	}
  
  	private void OnByteBufferFileChanged(ByteBuffer bb)
  	{
  		Gtk.Application.Invoke(delegate {
  			dvDisplay.ShowFileChangedBar();
  			Notification = true;
  			byteBuffer.FileOperationsAllowed = false;
  		});
  	}
  
  
  	void OnPreferencesChanged(Preferences prefs)
  	{
  		if (byteBuffer == null)
  			return;
  
  		string undoLimited = prefs["Undo.Limited"];
  		string undoActions = prefs["Undo.Actions"];
  
  		if (undoLimited == "True") {
  			byteBuffer.MaxUndoActions = Int32.Parse(undoActions);
  			while (cursorUndoDeque.Count > byteBuffer.MaxUndoActions)
  				cursorUndoDeque.RemoveEnd();
  		}
  		else
  			byteBuffer.MaxUndoActions = -1;
  
  		// temp dir
  		byteBuffer.TempDir = prefs["ByteBuffer.TempDir"];
  	}
  
  	void AddUndoCursorState(CursorState state)
  	{
  		if (byteBuffer.MaxUndoActions != -1)
  			while (cursorUndoDeque.Count >= byteBuffer.MaxUndoActions)
  				cursorUndoDeque.RemoveEnd();
  
  		cursorUndoDeque.AddFront(state);
  	}
  
  	private void DeleteSelectionInternal()
  	{
  		AreaGroup areaGroup = dvDisplay.Layout.AreaGroup;
  		
  		//
  		// set cursor and selection before deleting so that the view remains in a consistent state
  		// (eg the selection isn't beyond the EOF)
  		//
  		Util.Range prevSelection = this.Selection;
  		AddUndoCursorState(new CursorState(areaGroup.CursorOffset, 0, areaGroup.Selection.Start, 0));
  		cursorRedoDeque.Clear();
  
  		this.MoveCursor(areaGroup.Selection.Start, 0);
  		this.SetSelection(-1, -1);
  		
  		byteBuffer.Delete(prevSelection.Start, prevSelection.End);		
  	}
  
  	//<summary>Called when an application wants to get our clipboard data</summary>
  	private void OnClipboardGet(Clipboard cb, SelectionData sd, uint n)
  	{
  		// if application accepts text
  		if (sd.Target.Name == "UTF8_STRING") {
  			switch (this.FocusedArea.Type) {
  				case "hexadecimal":
  					sd.Text = ByteArray.ToString(clipdata, 16);
  					break;
  				case "decimal":
  					sd.Text = ByteArray.ToString(clipdata, 10);
  					break;
  				case "octal":
  					sd.Text = ByteArray.ToString(clipdata, 8);
  					break;
  				case "binary":
  					sd.Text = ByteArray.ToString(clipdata, 2);
  					break;
  				case "ascii":
  					sd.Set(sd.Target, 8, clipdata);
  					break;
  				default:
  					break;
  			}
  
  		} // if application accepts raw bytes
  		else if (sd.Target.Name == "application/octet-stream") {
  			sd.Set(sd.Target, 8, clipdata);
  		}
  	}
  
  	private void OnClipboardClear(Clipboard cb)
  	{
  
  	}
  
  	///<summary>Returns any clipboard data we can accept</summary>
  	private byte[] GetPasteData()
  	{
  		byte[] data = null;
  
  		SelectionData sd = clipboard.WaitForContents(Gdk.Atom.Intern("application/octet-stream", false));
  
  		// if there are no octet-stream data, try UTF8_STRING
  		// and try to convert it to bytes
  		if (sd == null) {
  			sd = clipboard.WaitForContents(Gdk.Atom.Intern("UTF8_STRING", false));
  
gt: 			if (sd != null) {