2009-07-19 20:30:15 +00:00
|
|
|
|
|
2009-07-17 15:06:15 +00:00
|
|
|
|
#region ================== Copyright (c) 2007 Pascal vd Heiden
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
|
|
|
|
|
* This program is released under GNU General Public License
|
|
|
|
|
*
|
|
|
|
|
* This program 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.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Namespaces
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using Microsoft.Win32;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using CodeImp.DoomBuilder.Actions;
|
|
|
|
|
using CodeImp.DoomBuilder.Data;
|
|
|
|
|
using CodeImp.DoomBuilder.Config;
|
|
|
|
|
using CodeImp.DoomBuilder.Rendering;
|
|
|
|
|
using SlimDX.Direct3D9;
|
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
using System.Drawing.Drawing2D;
|
|
|
|
|
using CodeImp.DoomBuilder.Map;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
namespace CodeImp.DoomBuilder.Controls
|
|
|
|
|
{
|
2009-07-19 20:30:15 +00:00
|
|
|
|
internal partial class DockersControl : UserControl
|
2009-07-17 15:06:15 +00:00
|
|
|
|
{
|
|
|
|
|
#region ================== Constants
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-17 15:06:15 +00:00
|
|
|
|
#endregion
|
2009-07-21 13:42:36 +00:00
|
|
|
|
|
|
|
|
|
#region ================== Delegates
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-21 13:42:36 +00:00
|
|
|
|
public event EventHandler MouseContainerEnter;
|
|
|
|
|
public event EventHandler MouseContainerLeave;
|
|
|
|
|
public event EventHandler Collapsed;
|
|
|
|
|
public event EventHandler Expanded;
|
2009-07-21 15:15:38 +00:00
|
|
|
|
public event EventHandler UserResize;
|
2009-07-21 13:42:36 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-21 13:42:36 +00:00
|
|
|
|
#region ================== Variables
|
2009-07-21 15:15:38 +00:00
|
|
|
|
|
|
|
|
|
// Behaviour
|
2009-07-20 15:16:40 +00:00
|
|
|
|
private bool rightalign;
|
2009-07-21 13:42:36 +00:00
|
|
|
|
private bool iscollapsed;
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-21 15:15:38 +00:00
|
|
|
|
// Collapsing
|
2009-07-20 15:16:40 +00:00
|
|
|
|
private int expandedwidth; // width when expanded
|
|
|
|
|
private int expandedtab; // selected tab index when expanded
|
|
|
|
|
|
2009-07-21 15:15:38 +00:00
|
|
|
|
// Splitting
|
|
|
|
|
private int splitstartoffset;
|
|
|
|
|
|
2009-07-22 15:16:28 +00:00
|
|
|
|
// Selection
|
|
|
|
|
private string currentselected;
|
|
|
|
|
private string previousselected;
|
|
|
|
|
|
2009-07-17 15:06:15 +00:00
|
|
|
|
#endregion
|
2009-07-19 20:30:15 +00:00
|
|
|
|
|
2009-07-20 15:16:40 +00:00
|
|
|
|
#region ================== Properties
|
|
|
|
|
|
2009-07-21 13:42:36 +00:00
|
|
|
|
public bool IsCollpased { get { return iscollapsed; } }
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-23 08:53:52 +00:00
|
|
|
|
// This returns true when the focus is here, but not in some special cases
|
|
|
|
|
public bool IsFocused
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
Control ac = FindActiveControl();
|
|
|
|
|
|
|
|
|
|
// We have focus when we need the keyboard for input
|
|
|
|
|
// Otherwise we don't want the focus and the docker may collapse
|
|
|
|
|
return (ac is TextBox) || (ac is RichTextBox) || (ac is NumericUpDown);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-20 15:16:40 +00:00
|
|
|
|
#endregion
|
|
|
|
|
|
2009-07-17 15:06:15 +00:00
|
|
|
|
#region ================== Constructor
|
|
|
|
|
|
|
|
|
|
// Constructor
|
|
|
|
|
public DockersControl()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
2009-07-20 15:16:40 +00:00
|
|
|
|
expandedwidth = (int)((float)this.Width * (this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width));
|
2009-07-17 15:06:15 +00:00
|
|
|
|
}
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-17 15:06:15 +00:00
|
|
|
|
#endregion
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-17 15:06:15 +00:00
|
|
|
|
#region ================== Methods
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-23 08:53:52 +00:00
|
|
|
|
// This returns the active child control
|
|
|
|
|
private Control FindActiveControl()
|
|
|
|
|
{
|
|
|
|
|
Control c = this.ActiveControl;
|
|
|
|
|
|
|
|
|
|
while(c is IContainerControl)
|
|
|
|
|
{
|
|
|
|
|
IContainerControl cc = (c as IContainerControl);
|
|
|
|
|
if(cc.ActiveControl != null)
|
|
|
|
|
c = cc.ActiveControl;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-20 15:16:40 +00:00
|
|
|
|
// This sets up the controls for left or right alignment
|
|
|
|
|
public void Setup(bool right)
|
|
|
|
|
{
|
|
|
|
|
rightalign = right;
|
|
|
|
|
if(rightalign)
|
|
|
|
|
{
|
|
|
|
|
splitter.Dock = DockStyle.Left;
|
|
|
|
|
tabs.Alignment = TabAlignment.Right;
|
2009-07-21 13:42:36 +00:00
|
|
|
|
tabs.Location = new Point(0, 0);
|
|
|
|
|
tabs.Size = new Size(this.ClientRectangle.Width + 2, this.ClientRectangle.Height);
|
2009-07-20 15:16:40 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
splitter.Dock = DockStyle.Right;
|
|
|
|
|
tabs.Alignment = TabAlignment.Left;
|
2009-07-21 13:42:36 +00:00
|
|
|
|
tabs.Location = new Point(-2, 0);
|
|
|
|
|
tabs.Size = new Size(this.ClientRectangle.Width + 2, this.ClientRectangle.Height);
|
2009-07-20 15:16:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tabs.SendToBack();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This collapses the docker
|
|
|
|
|
public void Collapse()
|
|
|
|
|
{
|
2009-07-21 13:42:36 +00:00
|
|
|
|
if(iscollapsed) return;
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-21 18:48:00 +00:00
|
|
|
|
splitter.Enabled = false;
|
|
|
|
|
splitter.BackColor = SystemColors.Control;
|
|
|
|
|
splitter.Width = (int)(2.0f * (this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width));
|
2009-07-20 15:16:40 +00:00
|
|
|
|
expandedtab = tabs.SelectedIndex;
|
|
|
|
|
expandedwidth = this.Width;
|
|
|
|
|
tabs.SelectedIndex = -1;
|
2009-07-21 18:48:00 +00:00
|
|
|
|
General.MainWindow.LockUpdate();
|
2009-07-20 15:16:40 +00:00
|
|
|
|
if(rightalign) this.Left = this.Right - GetCollapsedWidth();
|
|
|
|
|
this.Width = GetCollapsedWidth();
|
2009-07-21 18:48:00 +00:00
|
|
|
|
General.MainWindow.UnlockUpdate();
|
|
|
|
|
this.Invalidate(true);
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-21 13:42:36 +00:00
|
|
|
|
iscollapsed = true;
|
|
|
|
|
|
|
|
|
|
if(Collapsed != null) Collapsed(this, EventArgs.Empty);
|
2009-07-20 15:16:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This expands the docker
|
|
|
|
|
public void Expand()
|
|
|
|
|
{
|
2009-07-21 13:42:36 +00:00
|
|
|
|
if(!iscollapsed) return;
|
2009-07-21 18:48:00 +00:00
|
|
|
|
|
|
|
|
|
splitter.Enabled = true;
|
|
|
|
|
splitter.BackColor = Color.Transparent;
|
|
|
|
|
splitter.Width = (int)(4.0f * (this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width));
|
|
|
|
|
General.MainWindow.LockUpdate();
|
2009-07-20 15:16:40 +00:00
|
|
|
|
if(rightalign) this.Left = this.Right - expandedwidth;
|
|
|
|
|
this.Width = expandedwidth;
|
2009-07-21 18:48:00 +00:00
|
|
|
|
General.MainWindow.UnlockUpdate();
|
2009-07-20 15:16:40 +00:00
|
|
|
|
tabs.SelectedIndex = expandedtab;
|
2009-07-21 18:48:00 +00:00
|
|
|
|
tabs.Invalidate(true);
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-21 13:42:36 +00:00
|
|
|
|
iscollapsed = false;
|
|
|
|
|
|
|
|
|
|
if(Expanded != null) Expanded(this, EventArgs.Empty);
|
2009-07-20 15:16:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This calculates the collapsed width
|
|
|
|
|
public int GetCollapsedWidth()
|
|
|
|
|
{
|
2009-07-21 18:48:00 +00:00
|
|
|
|
Rectangle r;
|
|
|
|
|
if(tabs.TabPages.Count > 0)
|
|
|
|
|
r = tabs.GetTabRect(0);
|
|
|
|
|
else
|
|
|
|
|
r = new Rectangle(0, 0, 26, 26);
|
|
|
|
|
return r.Width + (int)(1.0f * (this.CurrentAutoScaleDimensions.Width / this.AutoScaleDimensions.Width));
|
2009-07-20 15:16:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This adds a docker
|
|
|
|
|
public void Add(Docker d)
|
|
|
|
|
{
|
2009-07-22 21:15:14 +00:00
|
|
|
|
// Set up page
|
2009-07-20 15:16:40 +00:00
|
|
|
|
TabPage page = new TabPage(d.Title);
|
2009-07-22 15:16:28 +00:00
|
|
|
|
page.SuspendLayout();
|
2009-07-20 15:16:40 +00:00
|
|
|
|
page.Font = this.Font;
|
|
|
|
|
page.Tag = d;
|
2009-07-21 13:42:36 +00:00
|
|
|
|
page.UseVisualStyleBackColor = false;
|
2009-07-22 15:16:28 +00:00
|
|
|
|
page.Controls.Add(d.Control);
|
|
|
|
|
d.Control.Dock = DockStyle.Fill;
|
2009-07-20 15:16:40 +00:00
|
|
|
|
tabs.TabPages.Add(page);
|
2009-07-22 15:16:28 +00:00
|
|
|
|
page.ResumeLayout(true);
|
2009-07-23 11:44:32 +00:00
|
|
|
|
if(iscollapsed) tabs.SelectedIndex = -1;
|
2009-07-22 21:15:14 +00:00
|
|
|
|
|
|
|
|
|
// Go for all controls to add events
|
|
|
|
|
Queue<Control> todo = new Queue<Control>();
|
|
|
|
|
todo.Enqueue(d.Control);
|
|
|
|
|
while(todo.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
Control c = todo.Dequeue();
|
|
|
|
|
c.MouseEnter += RaiseMouseContainerEnter;
|
|
|
|
|
c.MouseLeave += RaiseMouseContainerLeave;
|
|
|
|
|
foreach(Control cc in c.Controls)
|
|
|
|
|
todo.Enqueue(cc);
|
|
|
|
|
}
|
2009-07-20 15:16:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This removes a docker
|
|
|
|
|
public bool Remove(Docker d)
|
|
|
|
|
{
|
|
|
|
|
foreach(TabPage page in tabs.TabPages)
|
|
|
|
|
{
|
|
|
|
|
if((page.Tag as Docker) == d)
|
|
|
|
|
{
|
2009-07-22 21:15:14 +00:00
|
|
|
|
// Go for all controls to remove events
|
|
|
|
|
Queue<Control> todo = new Queue<Control>();
|
|
|
|
|
todo.Enqueue(d.Control);
|
|
|
|
|
while(todo.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
Control c = todo.Dequeue();
|
|
|
|
|
c.MouseEnter -= RaiseMouseContainerEnter;
|
|
|
|
|
c.MouseLeave -= RaiseMouseContainerLeave;
|
|
|
|
|
foreach(Control cc in c.Controls)
|
|
|
|
|
todo.Enqueue(cc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Take down that page
|
2009-07-22 15:16:28 +00:00
|
|
|
|
if(page == tabs.SelectedTab) SelectPrevious();
|
2009-07-20 15:16:40 +00:00
|
|
|
|
page.Controls.Clear();
|
|
|
|
|
tabs.TabPages.Remove(page);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-22 15:16:28 +00:00
|
|
|
|
// This selects a docker
|
|
|
|
|
public bool SelectDocker(Docker d)
|
|
|
|
|
{
|
|
|
|
|
foreach(TabPage page in tabs.TabPages)
|
|
|
|
|
{
|
|
|
|
|
if((page.Tag as Docker) == d)
|
|
|
|
|
{
|
|
|
|
|
page.Select();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This selectes the previous docker
|
|
|
|
|
public void SelectPrevious()
|
|
|
|
|
{
|
|
|
|
|
if(!string.IsNullOrEmpty(previousselected))
|
|
|
|
|
{
|
|
|
|
|
foreach(TabPage page in tabs.TabPages)
|
|
|
|
|
{
|
|
|
|
|
if((page.Tag as Docker).FullName == previousselected)
|
|
|
|
|
{
|
|
|
|
|
page.Select();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-20 15:16:40 +00:00
|
|
|
|
// This sorts tabs by their full name
|
|
|
|
|
public void SortTabs(IEnumerable<string> fullnames)
|
|
|
|
|
{
|
|
|
|
|
Dictionary<string, TabPage> pages = new Dictionary<string, TabPage>(tabs.TabPages.Count);
|
|
|
|
|
foreach(TabPage p in tabs.TabPages) pages.Add((p.Tag as Docker).FullName, p);
|
|
|
|
|
tabs.TabPages.Clear();
|
|
|
|
|
|
|
|
|
|
// Add tabs in order as in fullnames
|
|
|
|
|
foreach(string name in fullnames)
|
|
|
|
|
{
|
|
|
|
|
if(pages.ContainsKey(name))
|
|
|
|
|
{
|
|
|
|
|
tabs.TabPages.Add(pages[name]);
|
|
|
|
|
pages.Remove(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add remaining tabs
|
|
|
|
|
foreach(KeyValuePair<string, TabPage> p in pages)
|
|
|
|
|
tabs.TabPages.Add(p.Value);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-17 15:06:15 +00:00
|
|
|
|
#endregion
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-17 15:06:15 +00:00
|
|
|
|
#region ================== Events
|
2009-07-20 15:16:40 +00:00
|
|
|
|
|
2009-07-21 13:42:36 +00:00
|
|
|
|
// This raises the MouseContainerEnter event
|
|
|
|
|
private void RaiseMouseContainerEnter(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if(MouseContainerEnter != null)
|
|
|
|
|
MouseContainerEnter(sender, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This raises the MouseContainerLeave event
|
|
|
|
|
private void RaiseMouseContainerLeave(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if(MouseContainerLeave != null)
|
|
|
|
|
MouseContainerLeave(sender, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't want the focus
|
|
|
|
|
private void tabs_Enter(object sender, EventArgs e) { General.MainWindow.FocusDisplay(); }
|
|
|
|
|
private void tabs_MouseUp(object sender, MouseEventArgs e) { General.MainWindow.FocusDisplay(); }
|
2009-07-21 15:15:38 +00:00
|
|
|
|
private void tabs_Selected(object sender, TabControlEventArgs e) { General.MainWindow.FocusDisplay(); }
|
2009-07-21 13:42:36 +00:00
|
|
|
|
|
2009-07-22 15:16:28 +00:00
|
|
|
|
// Tab selected
|
|
|
|
|
private void tabs_SelectedIndexChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
// Keep track of previous selected tab
|
|
|
|
|
previousselected = currentselected;
|
|
|
|
|
if(tabs.SelectedTab != null)
|
|
|
|
|
{
|
|
|
|
|
Docker d = (tabs.SelectedTab.Tag as Docker);
|
|
|
|
|
currentselected = d.FullName;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
currentselected = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
General.MainWindow.FocusDisplay();
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-21 15:15:38 +00:00
|
|
|
|
// Splitting begins
|
|
|
|
|
private void splitter_MouseDown(object sender, MouseEventArgs e)
|
2009-07-21 13:42:36 +00:00
|
|
|
|
{
|
2009-07-21 15:15:38 +00:00
|
|
|
|
if(e.Button == MouseButtons.Left)
|
|
|
|
|
{
|
|
|
|
|
splitstartoffset = e.X;
|
2009-07-21 18:48:00 +00:00
|
|
|
|
splitter.BackColor = SystemColors.Highlight;
|
2009-07-21 15:15:38 +00:00
|
|
|
|
}
|
2009-07-21 13:42:36 +00:00
|
|
|
|
}
|
2009-07-21 15:15:38 +00:00
|
|
|
|
|
|
|
|
|
// Splitting ends
|
|
|
|
|
private void splitter_MouseUp(object sender, MouseEventArgs e)
|
2009-07-21 13:42:36 +00:00
|
|
|
|
{
|
2009-07-21 18:48:00 +00:00
|
|
|
|
splitter.BackColor = Color.Transparent;
|
|
|
|
|
tabs.Invalidate(true);
|
2009-07-21 15:15:38 +00:00
|
|
|
|
this.Update();
|
|
|
|
|
General.MainWindow.RedrawDisplay();
|
|
|
|
|
General.MainWindow.Update();
|
2009-07-21 13:42:36 +00:00
|
|
|
|
}
|
2009-07-21 15:15:38 +00:00
|
|
|
|
|
|
|
|
|
// Splitting dragged
|
|
|
|
|
private void splitter_MouseMove(object sender, MouseEventArgs e)
|
2009-07-21 13:42:36 +00:00
|
|
|
|
{
|
2009-07-21 15:15:38 +00:00
|
|
|
|
if(e.Button == MouseButtons.Left)
|
|
|
|
|
{
|
2009-07-21 18:48:00 +00:00
|
|
|
|
General.MainWindow.LockUpdate();
|
2009-07-21 15:15:38 +00:00
|
|
|
|
|
|
|
|
|
// Resize the control
|
|
|
|
|
int delta = e.X - splitstartoffset;
|
2009-07-21 18:48:00 +00:00
|
|
|
|
int collapsedwidth = GetCollapsedWidth();
|
2009-07-21 15:15:38 +00:00
|
|
|
|
if(rightalign)
|
|
|
|
|
{
|
|
|
|
|
this.Left += delta;
|
|
|
|
|
this.Width -= delta;
|
2009-07-21 18:48:00 +00:00
|
|
|
|
if(this.Width < collapsedwidth)
|
|
|
|
|
{
|
|
|
|
|
this.Left -= collapsedwidth - this.Width;
|
|
|
|
|
this.Width = collapsedwidth;
|
|
|
|
|
}
|
2009-07-21 15:15:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this.Width += delta;
|
2009-07-21 18:48:00 +00:00
|
|
|
|
if(this.Width < collapsedwidth)
|
|
|
|
|
this.Width = collapsedwidth;
|
2009-07-21 15:15:38 +00:00
|
|
|
|
}
|
2009-07-21 18:48:00 +00:00
|
|
|
|
|
|
|
|
|
General.MainWindow.UnlockUpdate();
|
2009-07-21 15:15:38 +00:00
|
|
|
|
this.Update();
|
|
|
|
|
General.MainWindow.RedrawDisplay();
|
|
|
|
|
General.MainWindow.Update();
|
|
|
|
|
|
|
|
|
|
// Raise event
|
|
|
|
|
if(UserResize != null)
|
|
|
|
|
UserResize(this, EventArgs.Empty);
|
|
|
|
|
}
|
2009-07-21 13:42:36 +00:00
|
|
|
|
}
|
2009-07-21 15:15:38 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
2009-07-17 15:06:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|