/src/DocumentStrip.cs
C# | 626 lines | 508 code | 105 blank | 13 comment | 67 complexity | 23a068297e5a4abc0432ef5564fa963a MD5 | raw file
Possible License(s): Unlicense
- /////////////////////////////////////////////////////////////////////////////////
- // Paint.NET //
- // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
- // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
- // See src/Resources/Files/License.txt for full licensing and attribution //
- // details. //
- // . //
- /////////////////////////////////////////////////////////////////////////////////
-
- using PaintDotNet.SystemLayer;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Windows.Forms;
-
- namespace PaintDotNet
- {
- internal class DocumentStrip
- : ImageStrip,
- IDocumentList
- {
- private object thumbsLock = new object();
-
- private List<DocumentWorkspace> documents = new List<DocumentWorkspace>();
- private List<ImageStrip.Item> documentButtons = new List<ImageStrip.Item>();
- private Dictionary<DocumentWorkspace, ImageStrip.Item> dw2button = new Dictionary<DocumentWorkspace, ImageStrip.Item>();
- private Dictionary<DocumentWorkspace, RenderArgs> thumbs = new Dictionary<DocumentWorkspace, RenderArgs>();
- private ThumbnailManager thumbnailManager;
- private DocumentWorkspace selectedDocument = null;
- private bool ensureSelectedIsVisible = true;
- private int suspendThumbnailUpdates = 0;
-
- public void SuspendThumbnailUpdates()
- {
- ++this.suspendThumbnailUpdates;
- }
-
- public void ResumeThumbnailUpdates()
- {
- --this.suspendThumbnailUpdates;
- }
-
- public void SyncThumbnails()
- {
- if (!this.thumbnailManager.IsDisposed)
- {
- using (new WaitCursorChanger(this))
- {
- this.thumbnailManager.DrainQueue();
- }
-
- Refresh();
- }
- }
-
- public int ThumbnailUpdateLatency
- {
- get
- {
- return this.thumbnailManager.UpdateLatency;
- }
-
- set
- {
- this.thumbnailManager.UpdateLatency = value;
- }
- }
-
- public bool EnsureSelectedIsVisible
- {
- get
- {
- return this.ensureSelectedIsVisible;
- }
-
- set
- {
- if (this.ensureSelectedIsVisible != value)
- {
- this.ensureSelectedIsVisible = value;
- PerformLayout();
- }
- }
- }
-
- public DocumentWorkspace[] DocumentList
- {
- get
- {
- return this.documents.ToArray();
- }
- }
-
- public Image[] DocumentThumbnails
- {
- get
- {
- SyncThumbnails();
-
- Image[] thumbnails = new Image[this.documents.Count];
-
- for (int i = 0; i < thumbnails.Length; ++i)
- {
- DocumentWorkspace dw = this.documents[i];
- RenderArgs ra;
-
- if (!this.thumbs.TryGetValue(dw, out ra))
- {
- thumbnails[i] = null;
- }
- else
- {
- thumbnails[i] = ra.Bitmap;
- }
- }
-
- return thumbnails;
- }
- }
-
- public int DocumentCount
- {
- get
- {
- return this.documents.Count;
- }
- }
-
- public event EventHandler DocumentListChanged;
- protected virtual void OnDocumentListChanged()
- {
- if (DocumentListChanged != null)
- {
- DocumentListChanged(this, EventArgs.Empty);
- }
- }
-
- public DocumentWorkspace SelectedDocument
- {
- get
- {
- return this.selectedDocument;
- }
-
- set
- {
- if (!this.documents.Contains(value))
- {
- throw new ArgumentException("DocumentWorkspace isn't being tracked by this instance of DocumentStrip");
- }
-
- if (this.selectedDocument != value)
- {
- SelectDocumentWorkspace(value);
- OnDocumentClicked(value, DocumentClickAction.Select);
- Refresh();
- }
- }
- }
-
- public int SelectedDocumentIndex
- {
- get
- {
- return this.documents.IndexOf(this.selectedDocument);
- }
- }
-
- public override Size GetPreferredSize(Size proposedSize)
- {
- Size itemSize = ItemSize;
- int preferredWidth;
-
- if (this.ItemCount == 0)
- {
- preferredWidth = 0;
- }
- else
- {
- preferredWidth = itemSize.Width * DocumentCount;
- }
-
- Size preferredSize = new Size(preferredWidth, itemSize.Height);
- return preferredSize;
- }
-
- private bool OnDigitHotKeyPressed(Keys keys)
- {
- // strip off the Alt and Control stuff
- keys &= ~Keys.Alt;
- keys &= ~Keys.Control;
-
- if (keys < Keys.D0 || keys > Keys.D9)
- {
- return false;
- }
-
- int digit = (int)keys - (int)Keys.D0;
- int index;
-
- if (digit == 0)
- {
- index = 9;
- }
- else
- {
- index = digit - 1;
- }
-
- if (index < this.documents.Count)
- {
- PerformItemClick(index, ItemPart.Image, MouseButtons.Left);
- return true;
- }
- else
- {
- return false;
- }
- }
-
- public DocumentStrip()
- {
- PdnBaseForm.RegisterFormHotKey(Keys.Control | Keys.Tab, OnNextTabHotKeyPressed);
- PdnBaseForm.RegisterFormHotKey(Keys.Control | Keys.PageDown, OnNextTabHotKeyPressed);
- PdnBaseForm.RegisterFormHotKey(Keys.Control | Keys.Shift | Keys.Tab, OnPreviousTabHotKeyPressed);
- PdnBaseForm.RegisterFormHotKey(Keys.Control | Keys.PageUp, OnPreviousTabHotKeyPressed);
-
- thumbnailManager = new ThumbnailManager(this); // allow for a 1px black border
-
- InitializeComponent();
-
- for (int i = 0; i <= 9; ++i)
- {
- Keys digit = Utility.LetterOrDigitCharToKeys((char)(i + '0'));
- PdnBaseForm.RegisterFormHotKey(Keys.Control | digit, OnDigitHotKeyPressed);
- PdnBaseForm.RegisterFormHotKey(Keys.Alt | digit, OnDigitHotKeyPressed);
- }
-
- this.ShowCloseButtons = true;
- }
-
- private bool OnNextTabHotKeyPressed(Keys keys)
- {
- bool processed = NextTab();
- return processed;
- }
-
- private bool OnPreviousTabHotKeyPressed(Keys keys)
- {
- bool processed = PreviousTab();
- return processed;
- }
-
- public bool NextTab()
- {
- bool changed = false;
-
- if (this.selectedDocument != null)
- {
- int currentIndex = this.documents.IndexOf(this.selectedDocument);
- int newIndex = (currentIndex + 1) % this.documents.Count;
- SelectedDocument = this.documents[newIndex];
- changed = true;
- }
-
- return changed;
- }
-
- public bool PreviousTab()
- {
- bool changed = false;
-
- if (this.selectedDocument != null)
- {
- int currentIndex = this.documents.IndexOf(this.selectedDocument);
- int newIndex = (currentIndex + (this.documents.Count - 1)) % this.documents.Count;
- SelectedDocument = this.documents[newIndex];
- changed = true;
- }
-
- return changed;
- }
-
- private void InitializeComponent()
- {
- this.Name = "DocumentStrip";
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- while (this.documents.Count > 0)
- {
- RemoveDocumentWorkspace(this.documents[this.documents.Count - 1]);
- }
-
- if (this.thumbnailManager != null)
- {
- this.thumbnailManager.Dispose();
- this.thumbnailManager = null;
- }
-
- foreach (DocumentWorkspace dw in this.thumbs.Keys)
- {
- RenderArgs ra = this.thumbs[dw];
- ra.Dispose();
- }
-
- this.thumbs.Clear();
- this.thumbs = null;
- }
-
- base.Dispose(disposing);
- }
-
- protected override void OnScrollArrowClicked(ArrowDirection arrowDirection)
- {
- int sign = 0;
-
- switch (arrowDirection)
- {
- case ArrowDirection.Left:
- sign = -1;
- break;
-
- case ArrowDirection.Right:
- sign = +1;
- break;
- }
-
- int delta = ItemSize.Width;
-
- ScrollOffset += sign * delta;
-
- base.OnScrollArrowClicked(arrowDirection);
- }
-
- protected override void OnItemClicked(Item item, ItemPart itemPart, MouseButtons mouseButtons)
- {
- DocumentWorkspace dw = item.Tag as DocumentWorkspace;
-
- if (dw != null)
- {
- switch (itemPart)
- {
- case ItemPart.None:
- // do nothing
- break;
-
- case ItemPart.CloseButton:
- if (mouseButtons == MouseButtons.Left)
- {
- OnDocumentClicked(dw, DocumentClickAction.Close);
- }
- break;
-
- case ItemPart.Image:
- if (mouseButtons == MouseButtons.Left)
- {
- SelectedDocument = dw;
- }
- else if (mouseButtons == MouseButtons.Right)
- {
- // TODO: right click menu
- }
- break;
-
- default:
- throw new InvalidEnumArgumentException();
- }
- }
-
- base.OnItemClicked(item, itemPart, mouseButtons);
- }
-
- public event EventHandler<EventArgs<Pair<DocumentWorkspace, DocumentClickAction>>> DocumentClicked;
- protected virtual void OnDocumentClicked(DocumentWorkspace dw, DocumentClickAction action)
- {
- if (DocumentClicked != null)
- {
- DocumentClicked(this, new EventArgs<Pair<DocumentWorkspace, DocumentClickAction>>(
- Pair.Create(dw, action)));
- }
- }
-
- public void UnlockDocumentWorkspaceDirtyValue(DocumentWorkspace unlockMe)
- {
- Item docItem = this.dw2button[unlockMe];
- docItem.UnlockDirtyValue();
- }
-
- public void LockDocumentWorkspaceDirtyValue(DocumentWorkspace lockMe, bool forceDirtyValue)
- {
- Item docItem = this.dw2button[lockMe];
- docItem.LockDirtyValue(forceDirtyValue);
- }
-
- public void AddDocumentWorkspace(DocumentWorkspace addMe)
- {
- this.documents.Add(addMe);
-
- ImageStrip.Item docButton = new ImageStrip.Item();
- docButton.Image = null;
- docButton.Tag = addMe;
-
- AddItem(docButton);
- this.documentButtons.Add(docButton);
-
- addMe.CompositionUpdated += Workspace_CompositionUpdated;
-
- this.dw2button.Add(addMe, docButton);
-
- if (addMe.Document != null)
- {
- QueueThumbnailUpdate(addMe);
- docButton.Dirty = addMe.Document.Dirty;
- addMe.Document.DirtyChanged += Document_DirtyChanged;
- }
-
- addMe.DocumentChanging += Workspace_DocumentChanging;
- addMe.DocumentChanged += Workspace_DocumentChanged;
-
- OnDocumentListChanged();
- }
-
- private void Workspace_DocumentChanging(object sender, EventArgs<Document> e)
- {
- if (e.Data != null)
- {
- e.Data.DirtyChanged -= Document_DirtyChanged;
- }
- }
-
- private void Workspace_DocumentChanged(object sender, EventArgs e)
- {
- DocumentWorkspace dw = (DocumentWorkspace)sender;
- ImageStrip.Item docButton = this.dw2button[dw];
-
- if (dw.Document != null)
- {
- docButton.Dirty = dw.Document.Dirty;
- dw.Document.DirtyChanged += Document_DirtyChanged;
- }
- else
- {
- docButton.Dirty = false;
- }
- }
-
- private void Document_DirtyChanged(object sender, EventArgs e)
- {
- for (int i = 0; i < this.documents.Count; ++i)
- {
- if (object.ReferenceEquals(sender, this.documents[i].Document))
- {
- ImageStrip.Item docButton = this.dw2button[this.documents[i]];
- docButton.Dirty = ((Document)sender).Dirty;
- }
- }
- }
-
- private void Workspace_CompositionUpdated(object sender, EventArgs e)
- {
- DocumentWorkspace dw = (DocumentWorkspace)sender;
- QueueThumbnailUpdate(dw);
- }
-
- public void RemoveDocumentWorkspace(DocumentWorkspace removeMe)
- {
- removeMe.CompositionUpdated -= Workspace_CompositionUpdated;
-
- if (this.selectedDocument == removeMe)
- {
- this.selectedDocument = null;
- }
-
- removeMe.DocumentChanging -= Workspace_DocumentChanging;
- removeMe.DocumentChanged -= Workspace_DocumentChanged;
-
- if (removeMe.Document != null)
- {
- removeMe.Document.DirtyChanged -= Document_DirtyChanged;
- }
-
- this.documents.Remove(removeMe);
- this.thumbnailManager.RemoveFromQueue(removeMe);
-
- ImageStrip.Item docButton = this.dw2button[removeMe];
- this.RemoveItem(docButton);
- this.dw2button.Remove(removeMe);
- this.documentButtons.Remove(docButton);
-
- if (this.thumbs.ContainsKey(removeMe))
- {
- RenderArgs thumbRA = this.thumbs[removeMe];
- Surface surface = thumbRA.Surface;
- thumbRA.Dispose();
- this.thumbs.Remove(removeMe);
- surface.Dispose();
- }
-
- OnDocumentListChanged();
- }
-
- protected override void OnLayout(LayoutEventArgs levent)
- {
- base.OnLayout(levent);
-
- if (this.ensureSelectedIsVisible &&
- (!Focused && !LeftScrollButton.Focused && !RightScrollButton.Focused))
- {
- int index = this.documents.IndexOf(this.selectedDocument);
- EnsureItemFullyVisible(index);
- }
- }
-
- public void SelectDocumentWorkspace(DocumentWorkspace selectMe)
- {
- UI.SuspendControlPainting(this);
-
- this.selectedDocument = selectMe;
-
- if (this.thumbs.ContainsKey(selectMe))
- {
- RenderArgs thumb = this.thumbs[selectMe];
- Bitmap bitmap = thumb.Bitmap;
- }
- else
- {
- QueueThumbnailUpdate(selectMe);
- }
-
- foreach (ImageStrip.Item docItem in this.documentButtons)
- {
- if ((docItem.Tag as DocumentWorkspace) == selectMe)
- {
- EnsureItemFullyVisible(docItem);
- docItem.Checked = true;
- }
- else
- {
- docItem.Checked = false;
- }
- }
-
- UI.ResumeControlPainting(this);
- Invalidate(true);
- }
-
- public void RefreshThumbnail(DocumentWorkspace dw)
- {
- if (this.documents.Contains(dw))
- {
- QueueThumbnailUpdate(dw);
- }
- }
-
- public void RefreshAllThumbnails()
- {
- foreach (DocumentWorkspace dw in this.documents)
- {
- QueueThumbnailUpdate(dw);
- }
- }
-
- private void OnThumbnailUpdated(DocumentWorkspace dw)
- {
- // We must double check that the DW is still around, because there's a chance
- // that the DW was been removed while the thumbnail was being rendered.
- if (this.dw2button.ContainsKey(dw))
- {
- ImageStrip.Item docButton = this.dw2button[dw];
- RenderArgs docRA = this.thumbs[dw];
- docButton.Image = docRA.Bitmap;
- docButton.Update();
- }
- }
-
- public void QueueThumbnailUpdate(DocumentWorkspace dw)
- {
- if (this.suspendThumbnailUpdates <= 0)
- {
- this.thumbnailManager.QueueThumbnailUpdate(dw, PreferredImageSize.Width - 2, OnThumbnailRendered);
- }
- }
-
- private void OnThumbnailRendered(object sender, EventArgs<Pair<IThumbnailProvider, Surface>> e)
- {
- RenderArgs ra = null;
- DocumentWorkspace dw = (DocumentWorkspace)e.Data.First;
-
- Size desiredSize = new Size(e.Data.Second.Width + 2, e.Data.Second.Height + 2);
-
- if (this.thumbs.ContainsKey(dw))
- {
- ra = this.thumbs[dw];
-
- if (ra.Size != desiredSize)
- {
- ra.Dispose();
- ra = null;
-
- this.thumbs.Remove(dw);
- }
- }
-
- if (ra == null)
- {
- Surface surface = new Surface(desiredSize);
- ra = new RenderArgs(surface);
- this.thumbs.Add(dw, ra);
- }
-
- ra.Surface.Clear(ColorBgra.Black);
- ra.Surface.CopySurface(e.Data.Second, new Point(1, 1));
- e.Data.Second.Dispose();
-
- OnThumbnailUpdated(dw);
- }
- }
- }