aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/GraphicObjects/Base/TemplatedGroup.cs
diff options
context:
space:
mode:
authorjpbruyere <jp.bruyere@hotmail.com>2017-03-15 05:59:51 +0100
committerjpbruyere <jp.bruyere@hotmail.com>2017-03-15 05:59:51 +0100
commit45f74150dd79db5a162034ab0c43e6abdb83beea (patch)
tree1d9f5af88c574c75117a47992302bab70f0999b6 /src/GraphicObjects/Base/TemplatedGroup.cs
parent20ce11875dc27c7399e3f5bf2a060e2c7cbeaad7 (diff)
split graphic objects dirRearangeDirs
Diffstat (limited to 'src/GraphicObjects/Base/TemplatedGroup.cs')
-rw-r--r--src/GraphicObjects/Base/TemplatedGroup.cs489
1 files changed, 489 insertions, 0 deletions
diff --git a/src/GraphicObjects/Base/TemplatedGroup.cs b/src/GraphicObjects/Base/TemplatedGroup.cs
new file mode 100644
index 00000000..e4447168
--- /dev/null
+++ b/src/GraphicObjects/Base/TemplatedGroup.cs
@@ -0,0 +1,489 @@
+//
+// TemplatedGroup.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2013-2017 Jean-Philippe Bruyère
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Xml.Serialization;
+using System.Xml;
+using System.Reflection;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Collections;
+using System.Threading;
+using System.Linq;
+
+namespace Crow
+{
+ public abstract class TemplatedGroup : TemplatedControl
+ {
+ #region CTOR
+ public TemplatedGroup () : base(){}
+ #endregion
+
+ protected Group items;
+ string _itemTemplate, _dataTest;
+
+ #region events
+ public event EventHandler<SelectionChangeEventArgs> SelectedItemChanged;
+ public event EventHandler Loaded;
+ #endregion
+
+ IList data;
+ int _selectedIndex = -1;
+ Color selBackground, selForeground;
+
+ int itemPerPage = 50;
+ CrowThread loadingThread = null;
+ volatile bool cancelLoading = false;
+
+ bool isPaged = false;
+
+ #region Templating
+ //TODO: dont instantiate ItemTemplates if not used
+ //but then i should test if null in msil gen
+ public Dictionary<string, ItemTemplate> ItemTemplates = new Dictionary<string, Crow.ItemTemplate>();
+
+ /// <summary>
+ /// Default item template
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue("#Crow.Templates.ItemTemplate.goml")]
+ public string ItemTemplate {
+ get { return _itemTemplate; }
+ set {
+ if (value == _itemTemplate)
+ return;
+
+ _itemTemplate = value;
+
+ //TODO:reload list with new template?
+ NotifyValueChanged("ItemTemplate", _itemTemplate);
+ }
+ }
+ protected override void loadTemplate(GraphicObject template = null)
+ {
+ base.loadTemplate (template);
+
+ items = this.child.FindByName ("ItemsContainer") as Group;
+ if (items == null)
+ throw new Exception ("TemplatedGroup template Must contain a Group named 'ItemsContainer'");
+ if (items.Children.Count == 0)
+ NotifyValueChanged ("HasChildren", false);
+ else
+ NotifyValueChanged ("HasChildren", true);
+ }
+ /// <summary>
+ /// Use to define condition on Data item for selecting among ItemTemplates.
+ /// Default value is 'TypeOf' for selecting Template depending on Type of Data.
+ /// Other possible values are properties of Data
+ /// </summary>
+ /// <value>The data property test.</value>
+ [XmlAttributeAttribute][DefaultValue("TypeOf")]
+ public string DataTest {
+ get { return _dataTest; }
+ set {
+ if (value == _dataTest)
+ return;
+
+ _dataTest = value;
+
+ NotifyValueChanged("DataTest", _dataTest);
+ }
+ }
+ #endregion
+
+ public virtual List<GraphicObject> Items{
+ get {
+ return isPaged ? items.Children.SelectMany(x => (x as Group).Children).ToList()
+ : items.Children;
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(-1)]public int SelectedIndex{
+ get { return _selectedIndex; }
+ set {
+ if (value == _selectedIndex)
+ return;
+
+ if (_selectedIndex >= 0 && Items.Count > _selectedIndex) {
+ Items[_selectedIndex].Foreground = Color.Transparent;
+ Items[_selectedIndex].Background = Color.Transparent;
+ }
+
+ _selectedIndex = value;
+
+ if (_selectedIndex >= 0 && Items.Count > _selectedIndex) {
+ Items[_selectedIndex].Foreground = SelectionForeground;
+ Items[_selectedIndex].Background = SelectionBackground;
+ }
+
+ NotifyValueChanged ("SelectedIndex", _selectedIndex);
+ NotifyValueChanged ("SelectedItem", SelectedItem);
+ SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
+ }
+ }
+ [XmlIgnore]public virtual object SelectedItem{
+ get { return data == null ? null : _selectedIndex < 0 ? null : data[_selectedIndex]; }
+ }
+ [XmlIgnore]public bool HasItems {
+ get { return Items.Count > 0; }
+ }
+ [XmlAttributeAttribute]public IList Data {
+ get { return data; }
+ set {
+ if (value == data)
+ return;
+
+ cancelLoadingThread ();
+
+ data = value;
+
+ NotifyValueChanged ("Data", data);
+
+ lock (CurrentInterface.LayoutMutex)
+ ClearItems ();
+
+ if (data == null)
+ return;
+
+ loadingThread = new CrowThread (this, loading);
+ loadingThread.Finished += (object sender, EventArgs e) => (sender as TemplatedGroup).Loaded.Raise (sender, e);
+ loadingThread.Start ();
+
+ NotifyValueChanged ("SelectedIndex", _selectedIndex);
+ NotifyValueChanged ("SelectedItem", SelectedItem);
+ SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
+ NotifyValueChanged ("HasItems", HasItems);
+ }
+ }
+
+ [XmlAttributeAttribute][DefaultValue("SteelBlue")]
+ public virtual Color SelectionBackground {
+ get { return selBackground; }
+ set {
+ if (value == selBackground)
+ return;
+ selBackground = value;
+ NotifyValueChanged ("SelectionBackground", selBackground);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue("White")]
+ public virtual Color SelectionForeground {
+ get { return selForeground; }
+ set {
+ if (value == selForeground)
+ return;
+ selForeground = value;
+ NotifyValueChanged ("SelectionForeground", selForeground);
+ RegisterForRedraw ();
+ }
+ }
+
+ protected void raiseSelectedItemChanged(){
+ SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
+ }
+
+
+ public virtual void AddItem(GraphicObject g){
+ items.AddChild (g);
+ g.LogicalParent = this;
+ NotifyValueChanged ("HasChildren", true);
+ }
+ public virtual void RemoveItem(GraphicObject g)
+ {
+ g.LogicalParent = null;
+ items.RemoveChild (g);
+ if (items.Children.Count == 0)
+ NotifyValueChanged ("HasChildren", false);
+ }
+
+ public virtual void ClearItems()
+ {
+ _selectedIndex = -1;
+ NotifyValueChanged ("SelectedIndex", _selectedIndex);
+ NotifyValueChanged ("SelectedItem", null);
+
+ items.ClearChildren ();
+ NotifyValueChanged ("HasChildren", false);
+ }
+
+
+ #region GraphicObject overrides
+ public override GraphicObject FindByName (string nameToFind)
+ {
+ if (Name == nameToFind)
+ return this;
+
+ foreach (GraphicObject w in Items) {
+ GraphicObject r = w.FindByName (nameToFind);
+ if (r != null)
+ return r;
+ }
+ return null;
+ }
+ public override bool Contains (GraphicObject goToFind)
+ {
+ foreach (GraphicObject w in Items) {
+ if (w == goToFind)
+ return true;
+ if (w.Contains (goToFind))
+ return true;
+ }
+ return base.Contains(goToFind);
+ }
+// public override void ClearBinding ()
+// {
+// if (items != null)
+// items.ClearBinding ();
+//
+// base.ClearBinding ();
+// }
+// public override void ResolveBindings ()
+// {
+// base.ResolveBindings ();
+// if (items != null)
+// items.ResolveBindings ();
+// }
+ #endregion
+
+ #region IXmlSerialisation Overrides
+ public override void ReadXml(System.Xml.XmlReader reader)
+ {
+ using (System.Xml.XmlReader subTree = reader.ReadSubtree ()) {
+ subTree.Read ();
+ string tmp = subTree.ReadOuterXml ();
+
+ //seek for template tag
+ using (XmlReader xr = new XmlTextReader (tmp, XmlNodeType.Element, null)) {
+ xr.Read ();
+ base.ReadXml (xr);
+ }
+ //process content
+ using (XmlReader xr = new XmlTextReader (tmp, XmlNodeType.Element, null)) {
+ xr.Read (); //skip current node
+
+ while (!xr.EOF) {
+ xr.Read (); //read first child
+
+ if (!xr.IsStartElement ())
+ continue;
+
+ if (xr.Name == "Template" || Name == "ItemTemplate"){
+ xr.Skip ();
+ if (!xr.IsStartElement ())
+ continue;
+ }
+
+ Type t = Type.GetType ("Crow." + xr.Name);
+ if (t == null) {
+ Assembly a = Assembly.GetEntryAssembly ();
+ foreach (Type expT in a.GetExportedTypes ()) {
+ if (expT.Name == xr.Name) {
+ t = expT;
+ break;
+ }
+ }
+ }
+ if (t == null)
+ throw new Exception (xr.Name + " type not found");
+
+ GraphicObject go = (GraphicObject)Activator.CreateInstance (t);
+
+ (go as IXmlSerializable).ReadXml (xr);
+
+ AddItem (go);
+
+ xr.Read (); //closing tag
+ }
+
+ }
+ }
+ }
+ public override void WriteXml(System.Xml.XmlWriter writer)
+ {
+ throw new NotImplementedException ();
+ }
+ #endregion
+
+ void loading(){
+ if (ItemTemplates == null)
+ ItemTemplates = new Dictionary<string, ItemTemplate> ();
+ if (!ItemTemplates.ContainsKey ("default"))
+ ItemTemplates ["default"] = Interface.GetItemTemplate (ItemTemplate);
+
+ for (int i = 1; i <= (data.Count / itemPerPage) + 1; i++) {
+ if (cancelLoading)
+ return;
+ loadPage (i);
+ Thread.Sleep (1);
+ }
+ }
+ void cancelLoadingThread(){
+ if (loadingThread != null)
+ loadingThread.Cancel ();
+ }
+ void loadPage(int pageNum)
+ {
+ #if DEBUG_LOAD
+ Stopwatch loadingTime = new Stopwatch ();
+ loadingTime.Start ();
+ #endif
+
+ Group page;
+ if (typeof(Wrapper).IsAssignableFrom (items.GetType ())) {
+ page = items;
+ itemPerPage = int.MaxValue;
+ } else if (typeof(GenericStack).IsAssignableFrom (items.GetType ())) {
+ GenericStack gs = new GenericStack ();
+ gs.CurrentInterface = items.CurrentInterface;
+ gs.Initialize ();
+ gs.Orientation = (items as GenericStack).Orientation;
+ gs.Width = items.Width;
+ gs.Height = items.Height;
+ gs.VerticalAlignment = items.VerticalAlignment;
+ gs.HorizontalAlignment = items.HorizontalAlignment;
+ page = gs;
+ page.Name = "page" + pageNum;
+ isPaged = true;
+ } else {
+ page = Activator.CreateInstance (items.GetType ()) as Group;
+ page.Name = "page" + pageNum;
+ isPaged = true;
+ }
+
+ for (int i = (pageNum - 1) * itemPerPage; i < pageNum * itemPerPage; i++) {
+ if (i >= data.Count)
+ break;
+ if (cancelLoading)
+ return;
+
+ loadItem (i, page);
+ }
+
+ if (page == items)
+ return;
+ lock (CurrentInterface.LayoutMutex)
+ items.AddChild (page);
+
+ #if DEBUG_LOAD
+ loadingTime.Stop ();
+ Debug.WriteLine("Listbox {2} Loading: {0} ticks \t, {1} ms",
+ loadingTime.ElapsedTicks,
+ loadingTime.ElapsedMilliseconds, this.ToString());
+ #endif
+ }
+ string getItempKey(Type dataType, object o){
+ try {
+ return dataType.GetProperty (_dataTest).GetGetMethod ().Invoke (o, null).ToString();
+ } catch (Exception ex) {
+ return dataType.FullName;
+ }
+ }
+ protected void loadItem(int i, Group page){
+ if (data [i] == null)//TODO:surely a threading sync problem
+ return;
+ GraphicObject g = null;
+ ItemTemplate iTemp = null;
+ Type dataType = data [i].GetType ();
+ string itempKey = dataType.FullName;
+
+ if (_dataTest != "TypeOf")
+ itempKey = getItempKey (dataType, data [i]);
+
+ if (ItemTemplates.ContainsKey (itempKey))
+ iTemp = ItemTemplates [itempKey];
+ else {
+ foreach (string it in ItemTemplates.Keys) {
+ Type t = Type.GetType (it);
+ if (t == null) {
+ Assembly a = Assembly.GetEntryAssembly ();
+ foreach (Type expT in a.GetExportedTypes ()) {
+ if (expT.Name == it) {
+ t = expT;
+ break;
+ }
+ }
+ }
+ if (t == null)
+ continue;
+ if (t.IsAssignableFrom (dataType)) {//TODO:types could be cached
+ iTemp = ItemTemplates [it];
+ break;
+ }
+ }
+ if (iTemp == null)
+ iTemp = ItemTemplates ["default"];
+ }
+
+ lock (CurrentInterface.LayoutMutex) {
+ g = iTemp.CreateInstance(CurrentInterface);
+ page.AddChild (g);
+ //g.LogicalParent = this;
+ registerItemClick (g);
+ }
+
+ if (iTemp.Expand != null && g is Expandable) {
+ (g as Expandable).Expand += iTemp.Expand;
+ (g as Expandable).GetIsExpandable = iTemp.HasSubItems;
+ }
+
+ g.DataSource = data [i];
+ }
+ protected virtual void registerItemClick(GraphicObject g){
+ g.MouseClick += itemClick;
+ }
+// protected void _list_LayoutChanged (object sender, LayoutingEventArgs e)
+// {
+// #if DEBUG_LAYOUTING
+// Debug.WriteLine("list_LayoutChanged");
+// #endif
+// if (_gsList.Orientation == Orientation.Horizontal) {
+// if (e.LayoutType == LayoutingType.Width)
+// _gsList.Width = approxSize;
+// } else if (e.LayoutType == LayoutingType.Height)
+// _gsList.Height = approxSize;
+// }
+ int approxSize
+ {
+ get {
+ if (data == null)
+ return -1;
+ GenericStack page1 = items.FindByName ("page1") as GenericStack;
+ if (page1 == null)
+ return -1;
+
+ return page1.Orientation == Orientation.Horizontal ?
+ data.Count < itemPerPage ?
+ -1:
+ (int)Math.Ceiling ((double)page1.Slot.Width / (double)itemPerPage * (double)(data.Count+1)):
+ data.Count < itemPerPage ?
+ -1:
+ (int)Math.Ceiling ((double)page1.Slot.Height / (double)itemPerPage * (double)(data.Count+1));
+ }
+ }
+ internal virtual void itemClick(object sender, MouseButtonEventArgs e){
+ SelectedIndex = data.IndexOf((sender as GraphicObject).DataSource);
+ }
+ }
+}