#region Disclaimer / License
// Copyright (C) 2010, Jackie Ng
// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Aga.Controls.Tree;
using OSGeo.MapGuide.MaestroAPI.Services;
using OSGeo.MapGuide.MaestroAPI;
using OSGeo.MapGuide.ObjectModels.Common;
using System.Security.AccessControl;
using OSGeo.MapGuide.MaestroAPI.Resource;
namespace Maestro.Editors.Generic
{
///
/// A generic dialog for selecting folders or resource documents
///
public partial class ResourcePicker : Form
{
private ResourceTypes[] _resTypes;
private ResourcePicker()
{
InitializeComponent();
_resTypes = new ResourceTypes[]
{
ResourceTypes.ApplicationDefinition,
ResourceTypes.DrawingSource,
ResourceTypes.FeatureSource,
ResourceTypes.Folder,
ResourceTypes.LayerDefinition,
ResourceTypes.LoadProcedure,
ResourceTypes.MapDefinition,
ResourceTypes.PrintLayout,
ResourceTypes.SymbolDefinition,
ResourceTypes.SymbolLibrary,
ResourceTypes.WebLayout,
ResourceTypes.WatermarkDefinition
};
cmbResourceFilter.DataSource = _resTypes;
RepositoryIcons.PopulateImageList(resImageList);
RepositoryIcons.PopulateImageList(folderImageList);
}
private IResourceService _resSvc;
private bool _resourceMode = false;
private RepositoryFolderTreeModel _model;
///
/// Constructs a new instance. Use this overload to select any resource type. If only
/// folder selection is desired, set to true before
/// showing the dialog
///
/// The res SVC.
/// The mode.
public ResourcePicker(IResourceService resSvc, ResourcePickerMode mode)
: this()
{
_resSvc = resSvc;
_model = new RepositoryFolderTreeModel(_resSvc, trvFolders);
_model.FolderSelected += OnFolderSelected;
this.UseFilter = true;
this.Mode = mode;
SetStartingPoint(LastSelectedFolder.FolderId);
}
void OnFolderSelected(object sender, EventArgs e)
{
UpdateDocumentList();
}
///
/// Sets the starting point.
///
/// The folder id.
/// If the specified folder does not exist, it will fallback to Library://
public void SetStartingPoint(string folderId)
{
if (string.IsNullOrEmpty(folderId))
return;
if (!ResourceIdentifier.IsFolderResource(folderId))
throw new ArgumentException(string.Format(Properties.Resources.NotAFolder, folderId));
// Library:// will *always* exist, so fallback to this if given folder doesn't check out
if (!_resSvc.ResourceExists(folderId))
folderId = "Library://";
this.ActiveControl = trvFolders;
_model.NavigateTo(folderId);
this.SelectedFolder = folderId;
//HACK: Navigating to the specified folder takes away the focus to the
//name field
this.ActiveControl = txtName;
}
///
/// Gets the selected folder.
///
public string SelectedFolder { get; private set; }
private ResourcePickerMode _mode = ResourcePickerMode.OpenResource;
///
/// Gets or sets the mode.
///
/// The mode.
public ResourcePickerMode Mode
{
get { return _mode; }
private set
{
_mode = value;
switch (_mode)
{
case ResourcePickerMode.OpenFolder:
{
this.Text = Properties.Resources.SelectFolder;
this.SelectFoldersOnly = true;
}
break;
case ResourcePickerMode.SaveResource:
{
this.Text = Properties.Resources.SaveResource;
}
break;
}
}
}
///
/// Constructs a new instance. Use this overload to select only resources of a specific type.
/// You cannot select folders in this mode. Attempting to set to
/// true will throw an
///
/// The res SVC.
/// The res filter.
/// The mode.
public ResourcePicker(IResourceService resSvc, ResourceTypes resFilter, ResourcePickerMode mode)
: this(resSvc, mode)
{
if (mode == ResourcePickerMode.OpenFolder)
throw new InvalidOperationException(string.Format(Properties.Resources.ModeNotAllowed, mode));
this.Filter = resFilter;
this.UseFilter = true;
_resourceMode = true;
cmbResourceFilter.Enabled = false;
}
///
/// Gets or sets the resource filter. If a filter value is specified, browsing
/// is locked to that particular resource type, otherwise all resource type can be
/// selected
///
public ResourceTypes Filter
{
get { return (ResourceTypes)cmbResourceFilter.SelectedItem; }
set
{
if (Array.IndexOf(_resTypes, value) < 0)
throw new InvalidOperationException("Cannot use specified resource type as filter: " + value); //LOCALIZE
cmbResourceFilter.SelectedItem = value;
}
}
///
/// Gets or sets whether to use a resource filter. If set to false, when selecting a folder
/// all resource types are returned, otherwise only children of the specified type are returned
///
internal bool UseFilter
{
get { return cmbResourceFilter.Visible; }
set
{
if (value && this.SelectFoldersOnly)
throw new InvalidOperationException("Cannot specify a filter when SelectFoldersOnly is true"); //LOCALIZE
cmbResourceFilter.Visible = value; lblFilter.Visible = value;
}
}
///
/// Gets or sets whether to select folders only. If true, the document view is disabled and
/// is set to false
///
private bool SelectFoldersOnly
{
get { return splitContainer1.Panel2Collapsed; }
set
{
if (_resourceMode && value)
throw new InvalidOperationException("Cannot specify to select folders when dialog is initialized with a resource filter"); //LOCALIZE
if (value)
this.UseFilter = false;
splitContainer1.Panel2Collapsed = value;
resIdComponentPanel.Visible = !value;
if (value)
txtResourceId.Text = string.Empty;
}
}
///
/// Gets the resource id of the selected item
///
public string ResourceID
{
get { return txtResourceId.Text; }
}
private void UpdateResourceId()
{
btnOK.Enabled = false;
this.SelectedFolder = txtFolder.Text;
if (this.SelectFoldersOnly)
{
txtResourceId.Text = txtFolder.Text;
if (!string.IsNullOrEmpty(txtFolder.Text) && ResourceIdentifier.IsFolderResource(txtResourceId.Text))
{
btnOK.Enabled = true;
}
}
else
{
txtResourceId.Text = txtFolder.Text + txtName.Text + "." + cmbResourceFilter.SelectedItem.ToString();
if (!ResourceIdentifier.IsFolderResource(txtResourceId.Text) && !string.IsNullOrEmpty(txtName.Text))
{
btnOK.Enabled = true;
}
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
private void btnOK_Click(object sender, EventArgs e)
{
if (_mode == ResourcePickerMode.OpenResource)
{
if (!_resSvc.ResourceExists(txtResourceId.Text))
{
MessageBox.Show(Properties.Resources.ResourceDoesntExist);
return;
}
}
else if (_mode == ResourcePickerMode.SaveResource)
{
if (ResourceIdentifier.IsFolderResource(txtResourceId.Text))
{
MessageBox.Show(Properties.Resources.InvalidResourceIdFolder);
return;
}
else
{
if (!ResourceIdentifier.Validate(txtResourceId.Text))
{
MessageBox.Show(Properties.Resources.InvalidResourceId);
return;
}
else
{
if (ResourceIdentifier.GetResourceType(txtResourceId.Text) != (ResourceTypes)cmbResourceFilter.SelectedItem)
{
MessageBox.Show(Properties.Resources.InvalidResourceIdNotSpecifiedType);
return;
}
}
if (_resSvc.ResourceExists(txtResourceId.Text))
{
if (MessageBox.Show(Properties.Resources.OverwriteResource, Properties.Resources.SaveResource, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
return;
}
}
}
if (ResourceIdentifier.IsFolderResource(txtResourceId.Text))
LastSelectedFolder.FolderId = txtResourceId.Text;
else
LastSelectedFolder.FolderId = (txtResourceId.Text != "Library://") ? ResourceIdentifier.GetParentFolder(txtResourceId.Text) : "Library://";
this.DialogResult = DialogResult.OK;
}
private void UpdateDocumentList()
{
if (_model != null)
{
RepositoryFolder folder = _model.SelectedFolder;
if (folder != null)
{
txtFolder.Text = folder.ResourceId;
if (!this.SelectFoldersOnly)
{
ResourceList list = null;
if (!this.UseFilter)
list = _resSvc.GetRepositoryResources(folder.ResourceId, 1);
else
list = _resSvc.GetRepositoryResources(folder.ResourceId, this.Filter.ToString(), 1);
PopulateDocumentList(list);
}
}
}
}
private void PopulateDocumentList(ResourceList list)
{
lstResources.Clear();
SortedList items = new SortedList();
foreach (var item in list.Items)
{
var doc = item as ResourceListResourceDocument;
if (doc != null)
{
string sortKey = doc.Name + "." + doc.ResourceType;
items.Add(sortKey, doc);
}
}
foreach (var doc in items.Values)
{
var li = new ListViewItem(doc.Name);
li.Tag = doc;
try
{
var rt = ResourceIdentifier.GetResourceType(doc.ResourceId);
li.ImageIndex = RepositoryIcons.GetImageIndexForResourceType(rt);
}
catch
{
li.ImageIndex = RepositoryIcons.RES_UNKNOWN;
}
lstResources.Items.Add(li);
}
}
private void lstResources_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstResources.SelectedItems.Count == 1)
{
var item = lstResources.SelectedItems[0];
var doc = item.Tag as ResourceListResourceDocument;
if (doc != null)
{
txtName.Text = ResourceIdentifier.GetName(doc.ResourceId);
}
}
}
private void txtName_TextChanged(object sender, EventArgs e)
{
UpdateResourceId();
}
private void txtFolder_TextChanged(object sender, EventArgs e)
{
UpdateResourceId();
}
private void cmbResourceFilter_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateResourceId();
UpdateDocumentList();
}
private void lstResources_MouseDoubleClick(object sender, MouseEventArgs e)
{
var item = lstResources.GetItemAt(e.X, e.Y);
if (item != null)
{
var doc = item.Tag as ResourceListResourceDocument;
if (doc != null)
{
txtName.Text = ResourceIdentifier.GetName(doc.ResourceId);
btnOK.PerformClick();
}
}
}
}
///
/// Defines the various modes this resource picker can be in
///
public enum ResourcePickerMode
{
///
///
///
OpenResource,
///
///
///
SaveResource,
///
///
///
OpenFolder
}
internal class RepositoryFolder
{
private IRepositoryItem _item;
public RepositoryFolder(IRepositoryItem item)
{
_item = item;
}
public string Name { get { return _item.Name; } }
public string ResourceId { get { return _item.ResourceId; } }
public bool HasChildren { get { return _item.HasChildren; } }
public Image Icon
{
get
{
if (IsRoot)
{
return Properties.Resources.server;
}
else
{
return Properties.Resources.folder_horizontal;
}
}
}
public bool IsRoot
{
get { return this.ResourceId == "Library://"; }
}
}
internal class RepositoryFolderTreeModel
{
class DummyNode : TreeNode
{
}
internal event EventHandler FolderSelected;
private IResourceService _resSvc;
private TreeView _tree;
public RepositoryFolder SelectedFolder
{
get;
private set;
}
private void StartUpdate()
{
_tree.BeginUpdate();
_tree.Cursor = Cursors.WaitCursor;
}
private void EndUpdate()
{
_tree.EndUpdate();
_tree.Cursor = Cursors.Default;
}
public RepositoryFolderTreeModel(IResourceService resSvc, TreeView tree)
{
_resSvc = resSvc;
_tree = tree;
_tree.AfterExpand += new TreeViewEventHandler(OnNodeAfterExpand);
_tree.AfterSelect += new TreeViewEventHandler(OnNodeAfterSelect);
StartUpdate();
foreach (RepositoryFolder folder in GetChildren(null))
{
var node = CreateNode(folder);
_tree.Nodes.Add(node);
}
EndUpdate();
}
void OnNodeAfterSelect(object sender, TreeViewEventArgs e)
{
RepositoryFolder folder = (RepositoryFolder)e.Node.Tag;
SetSelectedFolder(folder);
}
private void SetSelectedFolder(RepositoryFolder folder)
{
this.SelectedFolder = folder;
var handler = this.FolderSelected;
if (handler != null)
handler(this, EventArgs.Empty);
}
bool IsNodeNotPopulated(TreeNode node)
{
return node.Nodes.Count == 1 && node.Nodes[0].GetType() == typeof(DummyNode);
}
void OnNodeAfterExpand(object sender, TreeViewEventArgs e)
{
UpdateNode(e.Node);
}
private void UpdateNode(TreeNode nodeToUpdate)
{
RepositoryFolder folder = (RepositoryFolder)nodeToUpdate.Tag;
if (IsNodeNotPopulated(nodeToUpdate))
nodeToUpdate.Nodes.Clear();
if (folder.HasChildren && nodeToUpdate.Nodes.Count == 0)
{
StartUpdate();
foreach (RepositoryFolder f in GetChildren(folder))
{
var node = CreateNode(f);
nodeToUpdate.Nodes.Add(node);
}
EndUpdate();
}
}
private static TreeNode CreateNode(RepositoryFolder folder)
{
var node = new TreeNode();
node.Name = folder.Name;
node.Text = folder.Name;
node.Tag = folder;
node.ImageIndex = node.SelectedImageIndex = folder.IsRoot ? RepositoryIcons.RES_ROOT : RepositoryIcons.RES_FOLDER;
node.Nodes.Add(new DummyNode());
return node;
}
private System.Collections.IEnumerable GetSorted(ResourceList list)
{
//Sort them before returning them
SortedList folders = new SortedList();
foreach (var item in list.Children)
{
if (item.IsFolder)
folders.Add(item.Name, new RepositoryFolder(item));
}
foreach (var folder in folders.Values)
{
yield return folder;
}
}
public System.Collections.IEnumerable GetChildren(RepositoryFolder folder)
{
if (folder == null)
{
var list = _resSvc.GetRepositoryResources("Library://", 0);
return GetSorted(list);
}
else
{
if (folder.HasChildren)
{
var list = _resSvc.GetRepositoryResources(folder.ResourceId, ResourceTypes.Folder.ToString(), 1, true);
return GetSorted(list);
}
else
{
return new RepositoryFolder[0];
}
}
}
internal void NavigateTo(string folderId)
{
NavigateTo(folderId, null);
}
internal void NavigateTo(string folderId, TreeNode currentNode)
{
TreeNodeCollection nodeList = null;
if (currentNode == null)
{
nodeList = _tree.Nodes;
}
else
{
var folder = (RepositoryFolder)currentNode.Tag;
if (folderId.Equals(folder.ResourceId))
{
_tree.SelectedNode = currentNode;
SetSelectedFolder(folder);
return;
}
nodeList = currentNode.Nodes;
}
foreach (TreeNode node in nodeList)
{
var folder = (RepositoryFolder)node.Tag;
if (folderId.StartsWith(folder.ResourceId))
{
UpdateNode(node);
node.Expand();
NavigateTo(folderId, node);
break;
}
}
}
}
}