[Gtk-sharp-list] VfsNodeView

Jeroen Zwartepoorte jeroen.zwartepoorte at gmail.com
Wed Sep 28 03:16:12 EDT 2005


Hey guys,

I've written a little widget that creates a custom Gtk.TreeView that
displays the same folders/drives/volumes as the FileChooser. See the
attached code & screenshot. Feel free to use the code in other
projects.

The code isn't bug free. Atm it crashes when you try to access the CD
drive when there's no volume mounted. I plan to fix this real soon.
This is also a good example on how to "lazy load" he nodes in a
treeview.

I propose using this widget in MonoDevelop instead of the current
filebrowser widget. Comments?

Jeroen
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Screenshot-VfsNodeView.png
Type: image/png
Size: 33880 bytes
Desc: not available
Url : http://lists.ximian.com/pipermail/gtk-sharp-list/attachments/20050928/547b4d9f/Screenshot-VfsNodeView-0001.png
-------------- next part --------------
using Gtk;
using Gnome.Vfs;
using System;
using System.Collections;
using System.Runtime.InteropServices;

namespace Utils {
	public class VfsNodeView : TreeView {
		internal enum NodeType {
			Desktop,
			Home,
			Uri,
			Volume,
			Drive,
			Loading
		}
		
		internal class NodeData : IComparable {
			private static NodeData emptyNode = null;
			private NodeType type;
			private Gnome.Vfs.Uri uri;
			private Drive drive;
			private Gnome.Vfs.FileInfo info;
			private Volume volume;
			private string shortName;
			
			public static NodeData Empty {
				get {
					if (emptyNode == null)
						emptyNode = new NodeData (NodeType.Loading, null);
					return emptyNode;
				}
			}
			
			public NodeData (Gnome.Vfs.Uri uri) : this (NodeType.Uri, uri) {}
			
			public NodeData (NodeType type, Gnome.Vfs.Uri uri)
			{
				this.type = type;
				this.uri = uri;
			}
			
			public NodeData (Drive drive)
			{
				type = NodeType.Drive;
				this.drive = drive;
				if (drive.ActivationUri == null)
					uri = new Gnome.Vfs.Uri (drive.DevicePath);
				else
					uri = new Gnome.Vfs.Uri (drive.ActivationUri);
			}
			
			public NodeData (Gnome.Vfs.Uri uri, Gnome.Vfs.FileInfo info)
			{
				type = NodeType.Uri;
				this.uri = uri;
				this.info = info;
			}
			
			public NodeData (Volume volume)
			{
				type = NodeType.Volume;
				this.uri = new Gnome.Vfs.Uri (volume.ActivationUri);
				this.volume = volume;
			}
			
			public NodeType NodeType {
				get {
					return type;
				}
			}
			
			public Gnome.Vfs.Uri Uri {
				get {
					return uri;
				}
			}
			
			public Drive Drive {
				get {
					return drive;
				}
			}
			
			public Gnome.Vfs.FileInfo FileInfo {
				get {
					if (uri != null && info == null)
						info = uri.GetFileInfo (FileInfoOptions.Default |
									FileInfoOptions.GetMimeType);
					return info;
				}
			}
			
			public Volume Volume {
				get {
					return volume;
				}
			}
			
			public string ShortName {
				get {
					if (uri != null && shortName == null)
						shortName = uri.ExtractShortName ();
					return shortName;
				}
			}
			
			public int CompareTo (object obj)
			{
				NodeData node = obj as NodeData;
				
				if (type == node.NodeType) {
					if (FileInfo.Type == node.FileInfo.Type) {
						
						return String.Compare (ShortName, node.ShortName, true);
					} else {
						return info.Type == FileType.Directory ? -1 : 1;
					}
				} else {
					return type.CompareTo (node.NodeType);
				}
			}
		}

		private TreeStore store = null;
		private TreeModelFilter filter;
		private Gtk.IconTheme theme = null;
		private int iconSize;
		private Hashtable iconCache = null;
		private Gnome.Vfs.Uri desktopUri;
		private Gnome.Vfs.Uri homeUri;

		public VfsNodeView () : base ()
		{
			if (!Vfs.Initialized)
				Vfs.Initialize ();

			theme = Gtk.IconTheme.GetForScreen (Screen);

			int width, height;
			Settings settings = Settings.GetForScreen (Screen);
			Gtk.Icon.SizeLookupForSettings (settings, IconSize.Menu, out width, out height);
			iconSize = Math.Max (width, height);

			PopulateStore ();
			store.SetSortFunc (0, new TreeIterCompareFunc (SortFunc));
			store.SetSortColumnId (0, SortType.Ascending);
			filter = new TreeModelFilter (store, null);
			filter.VisibleFunc = new TreeModelFilterVisibleFunc (FilterVisibleFunc);

			Model = filter;
			HeadersVisible = false;
			RowExpanded += new RowExpandedHandler (OnRowExpanded);
			
			TreeViewColumn column = new TreeViewColumn ();
			CellRenderer renderer = new CellRendererPixbuf ();
			column.PackStart (renderer, false);
			column.SetCellDataFunc (renderer, new TreeCellDataFunc (IconDataFunc));
			
			renderer = new CellRendererText ();
			column.PackEnd (renderer, true);
			column.SetCellDataFunc (renderer, new TreeCellDataFunc (TextDataFunc));
			
			AppendColumn (column);
		}
		
		private bool FilterVisibleFunc (TreeModel model, TreeIter iter)
		{
			NodeData node = (NodeData) store.GetValue (iter, 0);
			// Why is node null sometimes?
			if (node == null)
				return true;

			if (node.NodeType == NodeType.Loading)
				return true;
			else
				// FIXME: Make show hidden files an option.
				return !node.ShortName.StartsWith (".");
		}
		
		private int SortFunc (TreeModel model, TreeIter a, TreeIter b)
		{
			NodeData nodeA = (NodeData) store.GetValue (a, 0);
			NodeData nodeB = (NodeData) store.GetValue (b, 0);
			
			return nodeA.CompareTo (nodeB);
		}
		
		private void OnRowExpanded (object o, RowExpandedArgs args)
		{
			TreeIter iter = filter.ConvertIterToChildIter (args.Iter);
			NodeData node = (NodeData) store.GetValue (iter, 0);
			TreeIter childIter;
			store.IterChildren (out childIter, iter);
			NodeData child = (NodeData)store.GetValue (childIter, 0);
			
			if (child.NodeType == NodeType.Loading) {
				Gnome.Vfs.FileInfo[] entries = Directory.GetEntries (node.Uri,
					FileInfoOptions.Default | FileInfoOptions.GetMimeType);
				foreach (FileInfo info in entries) {
					// Skip "." and "..".
					if (info.Name.Equals (".") || info.Name.Equals (".."))
						continue;
					
					child = new NodeData (node.Uri.AppendPath (info.Name), info);
					TreeIter nodeIter = store.AppendValues (iter, child);
					if (info.Type == FileType.Directory)
						store.AppendValues (nodeIter, NodeData.Empty);
				}

				store.Remove (ref childIter);
			}
		}

		private Gdk.Pixbuf GetCachedIcon (string icon)
		{
			if (iconCache == null)
				iconCache = new Hashtable ();
			
			if (!iconCache.Contains (icon)) {
				Gdk.Pixbuf pixbuf = theme.LoadIcon (icon, iconSize, 0);
				if (pixbuf != null)
					iconCache.Add (icon, pixbuf);
				return pixbuf;
			} else {
				return (Gdk.Pixbuf)iconCache[icon];
			}
		}

		[DllImport ("gnomeui-2")]
		private static extern string gnome_icon_lookup (IntPtr icon_theme,
								 IntPtr thumbnail_factory,
								 string file_uri,
								 string custom_icon,
								 IntPtr file_info,
								 string mimetype,
								 Gnome.IconLookupFlags flags,
								 out Gnome.IconLookupResultFlags result);

		private void IconDataFunc (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			CellRendererPixbuf renderer = cell as CellRendererPixbuf;
			NodeData node = (NodeData) filter.GetValue (iter, 0);
			switch (node.NodeType) {
				case NodeType.Drive:
					if (node.Drive.IsMounted) {
						Volume volume = node.Drive.MountedVolume;
						renderer.Pixbuf = GetCachedIcon (volume.Icon);
					} else {
						renderer.Pixbuf = GetCachedIcon (node.Drive.Icon);
					}
					break;
				case NodeType.Loading:
					renderer.Pixbuf = null;
					break;
				case NodeType.Volume:
					string uri = node.Volume.ActivationUri;
					if (uri.StartsWith ("file:///"))
						renderer.Pixbuf = GetCachedIcon ("gnome-dev-harddisk");
					else
						renderer.Pixbuf = GetCachedIcon (node.Volume.Icon);
					break;
				default:
					string icon = null;
					if (node.Uri.Equals (desktopUri)) {
						icon = "gnome-fs-desktop";
					} else if (node.Uri.Equals (homeUri)) {
						icon = "gnome-fs-home";
					} else {
						Gnome.IconLookupResultFlags result;
						icon = gnome_icon_lookup (theme.Handle, IntPtr.Zero,
									  null, null,
									  node.FileInfo.Handle,
									  node.FileInfo.MimeType,
									  0, out result);
					}
					
					renderer.Pixbuf = GetCachedIcon (icon);
					break;
			}
		}

		private void TextDataFunc (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			CellRendererText renderer = cell as CellRendererText;
			NodeData node = (NodeData) filter.GetValue (iter, 0);
			switch (node.NodeType) {
				case NodeType.Drive:
					renderer.Text = node.Drive.DisplayName;
					break;
				case NodeType.Home:
					renderer.Text = "Home";
					break;
				case NodeType.Loading:
					renderer.Text = "Retrieving file list...";
					break;
				case NodeType.Volume:
					if (node.Volume.ActivationUri.Equals ("file:///"))
						renderer.Text = "File System";
					else
						renderer.Text = node.Volume.DisplayName;
					break;
				default:
					renderer.Text = node.ShortName;
					break;
			}
		}

		private void AddRootNode (NodeData node)
		{
			TreeIter iter = store.AppendValues (node);
			store.AppendValues (iter, NodeData.Empty);
		}

		private void PopulateStore ()
		{
			if (store != null)
				return;

			store = new TreeStore (typeof (NodeData));

			// Add both the Desktop and Home custom node types.
			desktopUri = new Gnome.Vfs.Uri (Environment.GetFolderPath (Environment.SpecialFolder.Desktop));
			homeUri = new Gnome.Vfs.Uri (Environment.GetFolderPath (Environment.SpecialFolder.Personal));
			
			AddRootNode (new NodeData (NodeType.Desktop, desktopUri));
			AddRootNode (new NodeData (NodeType.Home, homeUri));

			VolumeMonitor monitor = VolumeMonitor.Get ();

			// Add root filesystem.
			AddRootNode (new NodeData (monitor.GetVolumeForPath ("/")));
			
			foreach (Drive d in monitor.ConnectedDrives) {
				if (d.IsUserVisible) {
					AddRootNode (new NodeData (d));
				}
			}
			
			foreach (Volume v in monitor.MountedVolumes) {
				if (v.Drive == null && v.IsUserVisible) {
					AddRootNode (new NodeData (v));
				}
			}
		}
	}
}


More information about the Gtk-sharp-list mailing list