2016-12-22 15:04:40 +00:00
|
|
|
|
#region ================== Namespaces
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Drawing.Drawing2D;
|
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
namespace CodeImp.DoomBuilder.Controls
|
|
|
|
|
{
|
|
|
|
|
//mxd. Based on (but heavily reworked since) TextureListPanel from Sledge (https://github.com/LogicAndTrick/sledge)
|
|
|
|
|
internal class ImageSelectorPanel : Panel
|
|
|
|
|
{
|
|
|
|
|
#region ================== Constants
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Variables
|
|
|
|
|
|
|
|
|
|
private VScrollBar scrollbar;
|
|
|
|
|
private List<ImageBrowserItem> items;
|
|
|
|
|
private List<ImageBrowserItem> selection;
|
|
|
|
|
private List<Rectangle> rectangles;
|
|
|
|
|
private ImageBrowserItem lastselecteditem;
|
|
|
|
|
private int imagesize = 128;
|
2017-01-09 04:47:58 +00:00
|
|
|
|
private bool classicview = false;
|
2017-01-09 05:00:27 +00:00
|
|
|
|
private bool usedtexturesfirst = false;
|
2017-01-09 04:47:58 +00:00
|
|
|
|
private string contenttype = "Textures";
|
2016-12-22 15:04:40 +00:00
|
|
|
|
private string title;
|
2017-01-09 05:11:01 +00:00
|
|
|
|
private int titleheight
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return classicview ? 0 : SystemFonts.MessageBoxFont.Height + 6;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
|
|
|
|
//mxd. Tooltips
|
|
|
|
|
private ToolTip tooltip;
|
|
|
|
|
private Point lasttooltippos;
|
|
|
|
|
private const int tooltipreshowdistance = 48;
|
|
|
|
|
|
|
|
|
|
//mxd. Textures cache
|
|
|
|
|
private static Dictionary<int, Dictionary<long, Image>> texturecache = new Dictionary<int, Dictionary<long, Image>>(); // <imagesize, < texture longname, preview image>>
|
2017-03-02 18:14:24 +00:00
|
|
|
|
private static Dictionary<int, Dictionary<long, Image>> flatcache = new Dictionary<int, Dictionary<long, Image>>(); // <imagesize, < texture longname, preview image>>
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
2017-03-02 18:14:24 +00:00
|
|
|
|
// Selection
|
|
|
|
|
private bool allowselection;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
private bool allowmultipleselection;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Event handlers
|
|
|
|
|
|
|
|
|
|
public delegate void ItemSelectedEventHandler(object sender, ImageBrowserItem item);
|
|
|
|
|
public delegate void SelectionChangedEventHandler(object sender, List<ImageBrowserItem> selection);
|
|
|
|
|
|
|
|
|
|
/*public event ItemSelectedEventHandler ItemSelected;
|
|
|
|
|
private void OnItemSelected(ImageBrowserItem item)
|
|
|
|
|
{
|
|
|
|
|
if(ItemSelected != null) ItemSelected(this, item);
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
public event SelectionChangedEventHandler SelectionChanged;
|
|
|
|
|
private void OnSelectionChanged(List<ImageBrowserItem> selection)
|
|
|
|
|
{
|
|
|
|
|
if(SelectionChanged != null) SelectionChanged(this, selection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public event ItemSelectedEventHandler ItemDoubleClicked;
|
|
|
|
|
private void OnItemDoubleClicked(ImageBrowserItem item)
|
|
|
|
|
{
|
|
|
|
|
if(ItemDoubleClicked != null) ItemDoubleClicked(this, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Properties
|
|
|
|
|
|
|
|
|
|
public bool HideSelection
|
|
|
|
|
{
|
|
|
|
|
get { return !allowselection; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
allowselection = !value;
|
|
|
|
|
if(!allowselection && selection.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
selection.Clear();
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool MultiSelect
|
|
|
|
|
{
|
|
|
|
|
get { return allowmultipleselection; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
allowmultipleselection = value;
|
|
|
|
|
if(!allowmultipleselection && selection.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var first = selection[0];
|
|
|
|
|
selection.Clear();
|
|
|
|
|
selection.Add(first);
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ImageSize
|
|
|
|
|
{
|
|
|
|
|
get { return imagesize; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
imagesize = value;
|
|
|
|
|
UpdateRectangles();
|
|
|
|
|
if(selection.Count > 0) ScrollToItem(selection[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-09 04:47:58 +00:00
|
|
|
|
public bool ClassicView
|
|
|
|
|
{
|
|
|
|
|
get { return classicview; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
classicview = value;
|
|
|
|
|
UpdateRectangles();
|
|
|
|
|
if (selection.Count > 0) ScrollToItem(selection[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-09 05:00:27 +00:00
|
|
|
|
public bool UsedTexturesFirst
|
|
|
|
|
{
|
|
|
|
|
get { return usedtexturesfirst; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
usedtexturesfirst = value;
|
|
|
|
|
UpdateRectangles();
|
|
|
|
|
if (selection.Count > 0) ScrollToItem(selection[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-22 15:04:40 +00:00
|
|
|
|
public List<ImageBrowserItem> Items { get { return items; } }
|
|
|
|
|
public List<ImageBrowserItem> SelectedItems { get { return selection; } }
|
|
|
|
|
public string Title { get { return title; } set { title = value; } }
|
2017-01-09 04:47:58 +00:00
|
|
|
|
public string ContentType { get { return contenttype; } set { contenttype = value; } } // why do encapsulation when it's not needed?
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Constructor / Disposer
|
|
|
|
|
|
|
|
|
|
public ImageSelectorPanel()
|
|
|
|
|
{
|
|
|
|
|
VScroll = true;
|
|
|
|
|
AutoScroll = true;
|
|
|
|
|
DoubleBuffered = true;
|
|
|
|
|
|
|
|
|
|
scrollbar = new VScrollBar { Dock = DockStyle.Right };
|
|
|
|
|
scrollbar.ValueChanged += (sender, e) => Refresh();
|
|
|
|
|
tooltip = new ToolTip(); //mxd
|
|
|
|
|
items = new List<ImageBrowserItem>();
|
|
|
|
|
selection = new List<ImageBrowserItem>();
|
|
|
|
|
imagesize = 128;
|
|
|
|
|
rectangles = new List<Rectangle>();
|
2016-12-23 12:39:09 +00:00
|
|
|
|
title = "All images:";
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
|
|
|
|
Controls.Add(scrollbar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void Dispose(bool disposing)
|
|
|
|
|
{
|
|
|
|
|
if(disposing) Clear();
|
|
|
|
|
base.Dispose(disposing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Add/Remove/Get Textures
|
|
|
|
|
|
2016-12-23 12:39:09 +00:00
|
|
|
|
//mxd. Clears the list without redrawing it
|
2016-12-22 15:04:40 +00:00
|
|
|
|
public void Clear()
|
|
|
|
|
{
|
|
|
|
|
selection.Clear();
|
|
|
|
|
items.Clear();
|
|
|
|
|
lastselecteditem = null;
|
|
|
|
|
rectangles.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//mxd
|
|
|
|
|
public void ClearSelection()
|
|
|
|
|
{
|
|
|
|
|
selection.Clear();
|
|
|
|
|
lastselecteditem = null;
|
|
|
|
|
|
|
|
|
|
OnSelectionChanged(selection);
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetItems(IEnumerable<ImageBrowserItem> items)
|
|
|
|
|
{
|
|
|
|
|
this.items.Clear();
|
|
|
|
|
lastselecteditem = null;
|
|
|
|
|
selection.Clear();
|
|
|
|
|
this.items.AddRange(items);
|
|
|
|
|
|
2017-03-02 18:14:24 +00:00
|
|
|
|
OnSelectionChanged(selection);
|
2016-12-22 15:04:40 +00:00
|
|
|
|
UpdateRectangles();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetSelectedItem(ImageBrowserItem item)
|
|
|
|
|
{
|
|
|
|
|
SetSelectedItems(new List<ImageBrowserItem> { item } );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetSelectedItems(List<ImageBrowserItem> items)
|
|
|
|
|
{
|
|
|
|
|
selection.Clear();
|
|
|
|
|
if(items.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
selection.AddRange(items);
|
|
|
|
|
ScrollToItem(items[0]); //mxd
|
|
|
|
|
Refresh(); //mxd
|
|
|
|
|
}
|
|
|
|
|
OnSelectionChanged(selection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ScrollToItem(ImageBrowserItem item)
|
|
|
|
|
{
|
|
|
|
|
int index = items.IndexOf(item);
|
|
|
|
|
if(index < 0) return;
|
|
|
|
|
|
|
|
|
|
Rectangle rec = rectangles[index];
|
|
|
|
|
|
|
|
|
|
//mxd. Already visible?
|
|
|
|
|
int ymin = scrollbar.Value - titleheight;
|
|
|
|
|
int ymax = ymin + this.ClientRectangle.Height + titleheight;
|
|
|
|
|
if(rec.Top - 3 >= ymin && rec.Bottom + 3 <= ymax) return;
|
|
|
|
|
|
|
|
|
|
int yscroll = Math.Max(0, Math.Min(rec.Top - titleheight - 3, scrollbar.Maximum - ClientRectangle.Height));
|
|
|
|
|
scrollbar.Value = yscroll;
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SelectNextItem(SearchDirectionHint dir)
|
|
|
|
|
{
|
|
|
|
|
if(!allowselection) return;
|
|
|
|
|
|
|
|
|
|
if(selection.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
if(items.Count > 0) SetSelectedItem(items[0]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int targetindex = items.IndexOf(selection[0]);
|
|
|
|
|
Rectangle rect = rectangles[targetindex];
|
|
|
|
|
int index, newindex, tx, cx, cy;
|
|
|
|
|
|
|
|
|
|
switch(dir)
|
|
|
|
|
{
|
|
|
|
|
case SearchDirectionHint.Right:
|
|
|
|
|
// Just select the next item
|
|
|
|
|
if(targetindex < items.Count - 1) SetSelectedItem(items[targetindex + 1]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SearchDirectionHint.Left:
|
|
|
|
|
// Just select the previous item
|
|
|
|
|
if(targetindex > 0) SetSelectedItem(items[targetindex - 1]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SearchDirectionHint.Up:
|
|
|
|
|
// Skip current row...
|
|
|
|
|
index = targetindex - 1;
|
|
|
|
|
if(index < 0) break;
|
|
|
|
|
while(index > 0)
|
|
|
|
|
{
|
|
|
|
|
if(rectangles[index].Y != rect.Y) break;
|
|
|
|
|
index--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check upper row for best match
|
|
|
|
|
tx = rect.X + rect.Width / 2;
|
|
|
|
|
cx = int.MaxValue;
|
|
|
|
|
cy = rectangles[index].Y;
|
|
|
|
|
newindex = int.MaxValue;
|
|
|
|
|
|
2017-01-30 04:20:48 +00:00
|
|
|
|
while(index >= 0 && rectangles[index].Y == cy)
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
|
|
|
|
int ccx = Math.Abs(rectangles[index].X + rectangles[index].Width / 2 - tx);
|
|
|
|
|
if(ccx < cx)
|
|
|
|
|
{
|
|
|
|
|
cx = ccx;
|
|
|
|
|
newindex = index;
|
|
|
|
|
}
|
|
|
|
|
index--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Select item
|
|
|
|
|
if(newindex != int.MaxValue) SetSelectedItem(items[newindex]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SearchDirectionHint.Down:
|
|
|
|
|
// Skip current row...
|
|
|
|
|
index = targetindex + 1;
|
|
|
|
|
if(index > rectangles.Count - 1) break;
|
|
|
|
|
while(index < rectangles.Count - 1)
|
|
|
|
|
{
|
|
|
|
|
if(rectangles[index].Y != rect.Y) break;
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check upper row for best match
|
|
|
|
|
tx = rect.X + rect.Width / 2;
|
|
|
|
|
cx = int.MaxValue;
|
|
|
|
|
cy = rectangles[index].Y;
|
|
|
|
|
newindex = int.MaxValue;
|
|
|
|
|
|
2017-01-30 04:20:48 +00:00
|
|
|
|
while(index < rectangles.Count && rectangles[index].Y == cy)
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
|
|
|
|
int ccx = Math.Abs(rectangles[index].X + rectangles[index].Width / 2 - tx);
|
|
|
|
|
if(ccx < cx)
|
|
|
|
|
{
|
|
|
|
|
cx = ccx;
|
|
|
|
|
newindex = index;
|
|
|
|
|
}
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Select item
|
|
|
|
|
if(newindex != int.MaxValue) SetSelectedItem(items[newindex]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnMouseDoubleClick(MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnMouseDoubleClick(e);
|
|
|
|
|
if(General.Interface.CtrlState || General.Interface.ShiftState || selection.Count != 1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int index = GetIndexAt(e.X, scrollbar.Value + e.Y);
|
|
|
|
|
if(index == -1) return;
|
|
|
|
|
|
|
|
|
|
OnItemDoubleClicked(items[index]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnMouseDown(e);
|
|
|
|
|
this.Focus();
|
|
|
|
|
|
|
|
|
|
if(!allowselection) return;
|
|
|
|
|
if(!allowmultipleselection || !General.Interface.CtrlState)
|
|
|
|
|
selection.Clear();
|
|
|
|
|
|
|
|
|
|
int x = e.X;
|
|
|
|
|
int y = scrollbar.Value + e.Y;
|
|
|
|
|
|
|
|
|
|
int clickedIndex = GetIndexAt(x, y);
|
|
|
|
|
var item = (clickedIndex >= 0 && clickedIndex < items.Count ? items[clickedIndex] : null);
|
|
|
|
|
|
|
|
|
|
if(item == null)
|
|
|
|
|
{
|
|
|
|
|
selection.Clear();
|
|
|
|
|
}
|
|
|
|
|
else if(allowmultipleselection && General.Interface.CtrlState && selection.Contains(item))
|
|
|
|
|
{
|
|
|
|
|
selection.Remove(item);
|
|
|
|
|
lastselecteditem = null;
|
|
|
|
|
}
|
|
|
|
|
else if(allowmultipleselection && General.Interface.ShiftState && lastselecteditem != null)
|
|
|
|
|
{
|
|
|
|
|
int bef = items.IndexOf(lastselecteditem);
|
|
|
|
|
var start = Math.Min(bef, clickedIndex);
|
|
|
|
|
var count = Math.Abs(clickedIndex - bef) + 1;
|
|
|
|
|
selection.AddRange(items.GetRange(start, count).Where(i => !selection.Contains(i)));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
selection.Add(item);
|
|
|
|
|
lastselecteditem = item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OnSelectionChanged(selection);
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnMouseEnter(EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Focus();
|
|
|
|
|
base.OnMouseEnter(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//mxd
|
|
|
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnMouseMove(e);
|
|
|
|
|
|
|
|
|
|
int index = GetIndexAt(e.X, scrollbar.Value + e.Y);
|
|
|
|
|
if(index == -1 || items[index].ItemType != ImageBrowserItemType.IMAGE || string.IsNullOrEmpty(items[index].ToolTip))
|
|
|
|
|
{
|
|
|
|
|
if(tooltip.Active) tooltip.Hide(this);
|
|
|
|
|
}
|
|
|
|
|
else if(!tooltip.Active || tooltip.GetToolTip(this) != items[index].ToolTip
|
|
|
|
|
|| Math.Abs(lasttooltippos.X - e.Location.X) > tooltipreshowdistance
|
|
|
|
|
|| Math.Abs(lasttooltippos.Y - e.Location.Y) > tooltipreshowdistance)
|
|
|
|
|
{
|
|
|
|
|
Point pos = new Point(e.Location.X, e.Location.Y + Cursor.Size.Height + 4);
|
|
|
|
|
tooltip.Show(items[index].ToolTip, this, pos, 999999);
|
|
|
|
|
lasttooltippos = e.Location;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int GetIndexAt(int x, int y)
|
|
|
|
|
{
|
2017-01-09 04:47:58 +00:00
|
|
|
|
int padhorz = classicview?13:2;
|
|
|
|
|
int padvert = 2;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
int font = 4 + SystemFonts.MessageBoxFont.Height;
|
|
|
|
|
|
|
|
|
|
for(var i = 0; i < rectangles.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var rec = rectangles[i];
|
2017-01-09 04:47:58 +00:00
|
|
|
|
if(rec.Left - padhorz <= x
|
|
|
|
|
&& rec.Right + padhorz >= x
|
|
|
|
|
&& rec.Top - padvert <= y
|
|
|
|
|
&& rec.Bottom + padvert + font >= y)
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Scrolling
|
|
|
|
|
|
|
|
|
|
private void ScrollByAmount(int value)
|
|
|
|
|
{
|
|
|
|
|
int newvalue = Math.Max(0, scrollbar.Value + value);
|
|
|
|
|
scrollbar.Value = Math.Min(newvalue, Math.Max(0, scrollbar.Maximum - ClientRectangle.Height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnMouseWheel(MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnMouseWheel(e);
|
|
|
|
|
ScrollByAmount(scrollbar.SmallChange * (e.Delta / -120));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//mxd. Otherwise arrow keys won't be handled by OnKeyDown
|
|
|
|
|
protected override bool IsInputKey(Keys keyData)
|
|
|
|
|
{
|
|
|
|
|
switch(keyData)
|
|
|
|
|
{
|
|
|
|
|
case Keys.Right: case Keys.Left:
|
|
|
|
|
case Keys.Up: case Keys.Down:
|
|
|
|
|
case Keys.Return: return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return base.IsInputKey(keyData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnKeyDown(KeyEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
switch(e.KeyCode)
|
|
|
|
|
{
|
|
|
|
|
//mxd. Cursor keys
|
2016-12-24 21:52:42 +00:00
|
|
|
|
case Keys.Left: SelectNextItem(SearchDirectionHint.Left); break;
|
|
|
|
|
case Keys.Right: SelectNextItem(SearchDirectionHint.Right); break;
|
|
|
|
|
case Keys.Up: SelectNextItem(SearchDirectionHint.Up); break;
|
|
|
|
|
case Keys.Down: SelectNextItem(SearchDirectionHint.Down); break;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
2016-12-24 21:52:42 +00:00
|
|
|
|
case Keys.PageDown: ScrollByAmount(scrollbar.LargeChange); break;
|
|
|
|
|
case Keys.PageUp: ScrollByAmount(-scrollbar.LargeChange); break;
|
|
|
|
|
case Keys.End: ScrollByAmount(int.MaxValue); break;
|
|
|
|
|
case Keys.Home: ScrollByAmount(-int.MaxValue); break;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
2016-12-24 21:52:42 +00:00
|
|
|
|
case Keys.Enter: if(selection.Count > 0) OnItemDoubleClicked(selection[0]); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base.OnKeyDown(e);
|
2016-12-22 15:04:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Updating Rectangles & Dimensions
|
|
|
|
|
|
|
|
|
|
protected override void OnResize(EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnResize(e);
|
|
|
|
|
UpdateRectangles();
|
|
|
|
|
|
|
|
|
|
//mxd
|
|
|
|
|
if(selection.Count > 0) ScrollToItem(selection[0]);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-08 22:04:55 +00:00
|
|
|
|
internal void UpdateRectangles()
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
|
|
|
|
int w = ClientRectangle.Width - scrollbar.Width;
|
2017-01-09 04:47:58 +00:00
|
|
|
|
int padhorz = classicview?13:2;
|
|
|
|
|
int padvert = 2;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
int font = 4 + SystemFonts.MessageBoxFont.Height;
|
|
|
|
|
int cx = 0;
|
|
|
|
|
int cy = titleheight;
|
|
|
|
|
int my = 0;
|
|
|
|
|
rectangles.Clear();
|
|
|
|
|
|
2017-01-09 04:47:58 +00:00
|
|
|
|
//
|
|
|
|
|
ImageBrowserItemType currentType = ImageBrowserItemType.IMAGE; // initialized to random value
|
|
|
|
|
bool currentUsedInMap = false;
|
|
|
|
|
var firstItem = (items.Count > 0) ? items[0] : null;
|
|
|
|
|
|
|
|
|
|
foreach (var ti in items)
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
|
|
|
|
Image preview = GetPreview(ti, imagesize);
|
2017-01-09 12:32:00 +00:00
|
|
|
|
if (classicview && (ti == firstItem || ((currentType == ImageBrowserItemType.IMAGE) != (ti.ItemType == ImageBrowserItemType.IMAGE)) || (usedtexturesfirst && currentUsedInMap != ti.Icon.UsedInMap)))
|
2017-01-09 04:47:58 +00:00
|
|
|
|
{
|
|
|
|
|
// new row, also provide space for category name.
|
|
|
|
|
cx = 0;
|
2017-01-09 05:11:01 +00:00
|
|
|
|
cy += SystemFonts.MessageBoxFont.Height + 6 + my + ((ti != firstItem) ? 16 : 0);
|
2017-01-09 04:47:58 +00:00
|
|
|
|
my = 0;
|
|
|
|
|
currentType = ti.ItemType;
|
|
|
|
|
currentUsedInMap = ti.Icon.UsedInMap;
|
|
|
|
|
}
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
|
|
|
|
int rw = w - cx;
|
2017-01-09 04:47:58 +00:00
|
|
|
|
int wid = Math.Max((imagesize > 0 ? imagesize : preview.Width), ti.TextureNameWidth) + padhorz + padhorz;
|
|
|
|
|
int hei = (imagesize > 0 ? imagesize : preview.Height) + padvert + padvert + font;
|
2017-02-01 17:10:22 +00:00
|
|
|
|
// if we draw height below, add another font
|
2017-02-08 17:21:17 +00:00
|
|
|
|
if (General.Settings.ShowTextureSizes && General.Settings.TextureSizesBelow && ti.ItemType == ImageBrowserItemType.IMAGE)
|
2017-02-01 17:10:22 +00:00
|
|
|
|
hei += font;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
|
|
|
|
if(rw < wid)
|
|
|
|
|
{
|
|
|
|
|
// New row
|
|
|
|
|
cx = 0;
|
|
|
|
|
cy += my;
|
|
|
|
|
my = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my = Math.Max(my, hei);
|
2017-01-09 04:47:58 +00:00
|
|
|
|
var rect = new Rectangle(cx + padhorz, cy + padvert, wid - padhorz - padhorz, hei - padvert - padvert - font);
|
2016-12-22 15:04:40 +00:00
|
|
|
|
rectangles.Add(rect);
|
|
|
|
|
cx += wid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(rectangles.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
scrollbar.Maximum = cy + my;
|
2017-01-09 04:47:58 +00:00
|
|
|
|
scrollbar.SmallChange = (imagesize > 0 ? imagesize : 128) + padvert + padvert + font;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
scrollbar.LargeChange = ClientRectangle.Height;
|
|
|
|
|
scrollbar.Visible = (scrollbar.Maximum > ClientRectangle.Height);
|
|
|
|
|
|
|
|
|
|
if(scrollbar.Value > scrollbar.Maximum - ClientRectangle.Height)
|
|
|
|
|
{
|
|
|
|
|
scrollbar.Value = Math.Max(0, scrollbar.Maximum - ClientRectangle.Height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scrollbar.Visible = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Rendering
|
|
|
|
|
|
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnPaint(e);
|
|
|
|
|
DrawTextures(e.Graphics);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-09 04:47:58 +00:00
|
|
|
|
private void DrawTextureHeader(Graphics g, string text, Rectangle rec, bool background)
|
|
|
|
|
{
|
|
|
|
|
bool blackbrowsers = (General.Settings != null && General.Settings.BlackBrowsers);
|
|
|
|
|
|
|
|
|
|
if (background)
|
|
|
|
|
{
|
|
|
|
|
// Draw group name bg
|
|
|
|
|
Color bgcolor = (blackbrowsers ? Color.Gray : SystemColors.Control);
|
|
|
|
|
using (Brush bg = new SolidBrush(Color.FromArgb(192, bgcolor)))
|
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(bg, rec.X, rec.Y, rec.Width, rec.Height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Draw underline
|
|
|
|
|
Color underlinecolor = blackbrowsers ? Color.FromArgb(0x7FFFFFFF) : Color.FromArgb((SystemColors.ControlText.ToArgb()&0xFFFFFF)|0x7F000000);
|
|
|
|
|
g.DrawLine(new Pen(underlinecolor), rec.Left, rec.Bottom, rec.Right - 2, rec.Bottom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw group name
|
|
|
|
|
Brush fgbrush = (blackbrowsers ? Brushes.White : SystemBrushes.ControlText);
|
|
|
|
|
Font bf = new Font(SystemFonts.MessageBoxFont, FontStyle.Bold);
|
|
|
|
|
g.DrawString(text, bf, fgbrush, rec.X, rec.Y);
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-22 15:04:40 +00:00
|
|
|
|
private void DrawTextures(Graphics g)
|
|
|
|
|
{
|
2017-01-09 04:47:58 +00:00
|
|
|
|
// Draw items
|
|
|
|
|
int scrollwidth = (scrollbar.Visible ? scrollbar.Width : 0);
|
|
|
|
|
|
|
|
|
|
if (items.Count > 0)
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
|
|
|
|
int y = scrollbar.Value;
|
2017-01-15 01:39:39 +00:00
|
|
|
|
int height = ClientRectangle.Height;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
2017-01-09 04:47:58 +00:00
|
|
|
|
ImageBrowserItemType currentType = ImageBrowserItemType.IMAGE; // initialized to random value
|
|
|
|
|
bool currentUsedInMap = false;
|
|
|
|
|
|
2017-02-08 16:36:40 +00:00
|
|
|
|
ImageBrowserItem.SetBrushes(classicview, rectangles[0].X, rectangles[0].Y - y, rectangles[0].Width, rectangles[0].Height);
|
2017-01-09 04:47:58 +00:00
|
|
|
|
for (var i = 0; i < items.Count; i++)
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
2017-01-09 12:32:00 +00:00
|
|
|
|
if (classicview && (i == 0 || ((currentType == ImageBrowserItemType.IMAGE) != (items[i].ItemType == ImageBrowserItemType.IMAGE)) || (usedtexturesfirst && currentUsedInMap != items[i].Icon.UsedInMap)))
|
2017-01-09 04:47:58 +00:00
|
|
|
|
{
|
|
|
|
|
// draw corresponding title right above this item.
|
|
|
|
|
string hdrname;
|
|
|
|
|
if (items[i].ItemType == ImageBrowserItemType.IMAGE)
|
|
|
|
|
{
|
2017-01-09 12:32:00 +00:00
|
|
|
|
if (usedtexturesfirst && items[i].Icon.UsedInMap) hdrname = "Used " + contenttype + ":";
|
2017-01-09 04:47:58 +00:00
|
|
|
|
else hdrname = "All " + contenttype + ":";
|
|
|
|
|
}
|
|
|
|
|
else hdrname = "Directories:";
|
2017-01-09 05:11:01 +00:00
|
|
|
|
DrawTextureHeader(g, hdrname, new Rectangle(2, rectangles[i].Y - (SystemFonts.MessageBoxFont.Height + 6) - y, ClientRectangle.Width - scrollwidth - 4, SystemFonts.MessageBoxFont.Height), false);
|
2017-01-09 04:47:58 +00:00
|
|
|
|
currentType = items[i].ItemType;
|
|
|
|
|
currentUsedInMap = items[i].Icon.UsedInMap;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-22 15:04:40 +00:00
|
|
|
|
Rectangle rec = rectangles[i];
|
|
|
|
|
if(rec.Bottom < y) continue;
|
|
|
|
|
if(rec.Top > y + height) break;
|
|
|
|
|
|
|
|
|
|
Image bmp = GetPreview(items[i], imagesize);
|
2017-01-09 04:47:58 +00:00
|
|
|
|
items[i].Draw(g, bmp, rec.X, rec.Y - y, rec.Width, rec.Height, selection.Contains(items[i]), items[i].Icon.UsedInMap, classicview);
|
2016-12-22 15:04:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw title on top of items
|
2017-01-09 05:11:01 +00:00
|
|
|
|
if(!string.IsNullOrEmpty(title) && !classicview)
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
2017-01-09 04:47:58 +00:00
|
|
|
|
DrawTextureHeader(g, title, new Rectangle(2, 2, ClientRectangle.Width - scrollwidth - 4, SystemFonts.MessageBoxFont.Height), true);
|
2016-12-22 15:04:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Image Caching
|
|
|
|
|
|
2017-03-02 18:14:24 +00:00
|
|
|
|
// [ZZ] used during resource reload.
|
|
|
|
|
public static void ClearCachedPreviews()
|
|
|
|
|
{
|
|
|
|
|
foreach (Dictionary<long, Image> imgdict in texturecache.Values)
|
|
|
|
|
foreach (Image img in imgdict.Values)
|
|
|
|
|
img.Dispose();
|
|
|
|
|
foreach (Dictionary<long, Image> imgdict in flatcache.Values)
|
|
|
|
|
foreach (Image img in imgdict.Values)
|
|
|
|
|
img.Dispose();
|
|
|
|
|
texturecache.Clear();
|
|
|
|
|
flatcache.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-22 15:04:40 +00:00
|
|
|
|
private static Image GetPreview(ImageBrowserItem item, int imagesize)
|
|
|
|
|
{
|
|
|
|
|
if(!item.IsPreviewLoaded) return item.Icon.GetPreview();
|
2017-03-02 18:14:24 +00:00
|
|
|
|
Dictionary<int, Dictionary<long, Image>> cache = item.Icon.IsFlat ? flatcache : texturecache;
|
|
|
|
|
|
|
|
|
|
if(!cache.ContainsKey(imagesize)) cache.Add(imagesize, new Dictionary<long, Image>());
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
|
|
|
|
// Generate preview?
|
2017-03-02 18:14:24 +00:00
|
|
|
|
// [ZZ] how does this work when texture images change?
|
|
|
|
|
if(!cache[imagesize].ContainsKey(item.Icon.LongName))
|
2016-12-22 15:04:40 +00:00
|
|
|
|
{
|
|
|
|
|
Image img = item.Icon.GetPreview();
|
|
|
|
|
|
|
|
|
|
// Determine preview size
|
|
|
|
|
float scalex, scaley;
|
|
|
|
|
if(item.ItemType == ImageBrowserItemType.IMAGE)
|
|
|
|
|
{
|
|
|
|
|
scalex = (imagesize == 0 ? 1.0f : (imagesize / (float)img.Width));
|
|
|
|
|
scaley = (imagesize == 0 ? 1.0f :(imagesize / (float)img.Height));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Don't upscale folder icons
|
|
|
|
|
scalex = (imagesize == 0 ? 1.0f : ((img.Width > imagesize) ? (imagesize / (float)img.Width) : 1.0f));
|
|
|
|
|
scaley = (imagesize == 0 ? 1.0f : ((img.Height > imagesize) ? (imagesize / (float)img.Height) : 1.0f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float scale = Math.Min(scalex, scaley);
|
|
|
|
|
int previewwidth = (int)(img.Width * scale);
|
|
|
|
|
int previewheight = (int)(img.Height * scale);
|
|
|
|
|
if(previewwidth < 1) previewwidth = 1;
|
|
|
|
|
if(previewheight < 1) previewheight = 1;
|
|
|
|
|
|
|
|
|
|
// Make new image
|
|
|
|
|
Bitmap preview = new Bitmap(previewwidth, previewheight, PixelFormat.Format32bppArgb);
|
|
|
|
|
using(Graphics g = Graphics.FromImage(preview))
|
|
|
|
|
{
|
|
|
|
|
g.PageUnit = GraphicsUnit.Pixel;
|
|
|
|
|
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
2016-12-23 12:39:09 +00:00
|
|
|
|
g.PixelOffsetMode = PixelOffsetMode.Half;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
|
|
|
|
|
g.DrawImage(img, new Rectangle(0, 0, previewwidth, previewheight));
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-02 18:14:24 +00:00
|
|
|
|
cache[imagesize][item.Icon.LongName] = preview;
|
2016-12-22 15:04:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get preview
|
2017-03-02 18:14:24 +00:00
|
|
|
|
return cache[imagesize][item.Icon.LongName];
|
2016-12-22 15:04:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|