PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/src/DocumentStrip.cs

https://bitbucket.org/tuldok89/openpdn
C# | 605 lines | 488 code | 105 blank | 12 comment | 61 complexity | 1eb24b9f6a662eb66dd5cf8a7f2db863 MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using System.Linq;
  10. using PaintDotNet.Base;
  11. using PaintDotNet.SystemLayer;
  12. using System;
  13. using System.Collections.Generic;
  14. using System.ComponentModel;
  15. using System.Drawing;
  16. using System.Windows.Forms;
  17. namespace PaintDotNet
  18. {
  19. internal class DocumentStrip
  20. : ImageStrip,
  21. IDocumentList
  22. {
  23. private object _thumbsLock = new object();
  24. private readonly List<DocumentWorkspace> _documents = new List<DocumentWorkspace>();
  25. private readonly List<Item> _documentButtons = new List<Item>();
  26. private readonly Dictionary<DocumentWorkspace, Item> _dw2Button = new Dictionary<DocumentWorkspace, Item>();
  27. private Dictionary<DocumentWorkspace, RenderArgs> _thumbs = new Dictionary<DocumentWorkspace, RenderArgs>();
  28. private ThumbnailManager _thumbnailManager;
  29. private DocumentWorkspace _selectedDocument;
  30. private bool _ensureSelectedIsVisible = true;
  31. private int _suspendThumbnailUpdates;
  32. public void SuspendThumbnailUpdates()
  33. {
  34. ++_suspendThumbnailUpdates;
  35. }
  36. public void ResumeThumbnailUpdates()
  37. {
  38. --_suspendThumbnailUpdates;
  39. }
  40. public void SyncThumbnails()
  41. {
  42. if (_thumbnailManager.IsDisposed) return;
  43. using (new WaitCursorChanger(this))
  44. {
  45. _thumbnailManager.DrainQueue();
  46. }
  47. Refresh();
  48. }
  49. public int ThumbnailUpdateLatency
  50. {
  51. get
  52. {
  53. return _thumbnailManager.UpdateLatency;
  54. }
  55. set
  56. {
  57. _thumbnailManager.UpdateLatency = value;
  58. }
  59. }
  60. public bool EnsureSelectedIsVisible
  61. {
  62. get
  63. {
  64. return _ensureSelectedIsVisible;
  65. }
  66. set
  67. {
  68. if (_ensureSelectedIsVisible == value) return;
  69. _ensureSelectedIsVisible = value;
  70. PerformLayout();
  71. }
  72. }
  73. public DocumentWorkspace[] DocumentList
  74. {
  75. get
  76. {
  77. return _documents.ToArray();
  78. }
  79. }
  80. public Image[] DocumentThumbnails
  81. {
  82. get
  83. {
  84. SyncThumbnails();
  85. var thumbnails = new Image[_documents.Count];
  86. for (int i = 0; i < thumbnails.Length; ++i)
  87. {
  88. DocumentWorkspace dw = _documents[i];
  89. RenderArgs ra;
  90. if (!_thumbs.TryGetValue(dw, out ra))
  91. {
  92. thumbnails[i] = null;
  93. }
  94. else
  95. {
  96. thumbnails[i] = ra.Bitmap;
  97. }
  98. }
  99. return thumbnails;
  100. }
  101. }
  102. public int DocumentCount
  103. {
  104. get
  105. {
  106. return _documents.Count;
  107. }
  108. }
  109. public event EventHandler DocumentListChanged;
  110. protected virtual void OnDocumentListChanged()
  111. {
  112. if (DocumentListChanged != null)
  113. {
  114. DocumentListChanged(this, EventArgs.Empty);
  115. }
  116. }
  117. public DocumentWorkspace SelectedDocument
  118. {
  119. get
  120. {
  121. return _selectedDocument;
  122. }
  123. set
  124. {
  125. if (!_documents.Contains(value))
  126. {
  127. throw new ArgumentException("DocumentWorkspace isn't being tracked by this instance of DocumentStrip");
  128. }
  129. if (_selectedDocument == value) return;
  130. SelectDocumentWorkspace(value);
  131. OnDocumentClicked(value, DocumentClickAction.Select);
  132. Refresh();
  133. }
  134. }
  135. public int SelectedDocumentIndex
  136. {
  137. get
  138. {
  139. return _documents.IndexOf(_selectedDocument);
  140. }
  141. }
  142. public override Size GetPreferredSize(Size proposedSize)
  143. {
  144. Size itemSize = ItemSize;
  145. int preferredWidth;
  146. if (ItemCount == 0)
  147. {
  148. preferredWidth = 0;
  149. }
  150. else
  151. {
  152. preferredWidth = itemSize.Width * DocumentCount;
  153. }
  154. var preferredSize = new Size(preferredWidth, itemSize.Height);
  155. return preferredSize;
  156. }
  157. private bool OnDigitHotKeyPressed(Keys keys)
  158. {
  159. // strip off the Alt and Control stuff
  160. keys &= ~Keys.Alt;
  161. keys &= ~Keys.Control;
  162. if (keys < Keys.D0 || keys > Keys.D9)
  163. {
  164. return false;
  165. }
  166. int digit = (int)keys - (int)Keys.D0;
  167. int index;
  168. if (digit == 0)
  169. {
  170. index = 9;
  171. }
  172. else
  173. {
  174. index = digit - 1;
  175. }
  176. if (index < _documents.Count)
  177. {
  178. PerformItemClick(index, ItemPart.Image, MouseButtons.Left);
  179. return true;
  180. }
  181. return false;
  182. }
  183. public DocumentStrip()
  184. {
  185. PdnBaseForm.RegisterFormHotKey(Keys.Control | Keys.Tab, OnNextTabHotKeyPressed);
  186. PdnBaseForm.RegisterFormHotKey(Keys.Control | Keys.PageDown, OnNextTabHotKeyPressed);
  187. PdnBaseForm.RegisterFormHotKey(Keys.Control | Keys.Shift | Keys.Tab, OnPreviousTabHotKeyPressed);
  188. PdnBaseForm.RegisterFormHotKey(Keys.Control | Keys.PageUp, OnPreviousTabHotKeyPressed);
  189. _thumbnailManager = new ThumbnailManager(this); // allow for a 1px black border
  190. InitializeComponent();
  191. for (int i = 0; i <= 9; ++i)
  192. {
  193. Keys digit = Utility.LetterOrDigitCharToKeys((char)(i + '0'));
  194. PdnBaseForm.RegisterFormHotKey(Keys.Control | digit, OnDigitHotKeyPressed);
  195. PdnBaseForm.RegisterFormHotKey(Keys.Alt | digit, OnDigitHotKeyPressed);
  196. }
  197. ShowCloseButtons = true;
  198. }
  199. private bool OnNextTabHotKeyPressed(Keys keys)
  200. {
  201. bool processed = NextTab();
  202. return processed;
  203. }
  204. private bool OnPreviousTabHotKeyPressed(Keys keys)
  205. {
  206. bool processed = PreviousTab();
  207. return processed;
  208. }
  209. public bool NextTab()
  210. {
  211. bool changed = false;
  212. if (_selectedDocument != null)
  213. {
  214. int currentIndex = _documents.IndexOf(_selectedDocument);
  215. int newIndex = (currentIndex + 1) % _documents.Count;
  216. SelectedDocument = _documents[newIndex];
  217. changed = true;
  218. }
  219. return changed;
  220. }
  221. public bool PreviousTab()
  222. {
  223. bool changed = false;
  224. if (_selectedDocument != null)
  225. {
  226. int currentIndex = _documents.IndexOf(_selectedDocument);
  227. int newIndex = (currentIndex + (_documents.Count - 1)) % _documents.Count;
  228. SelectedDocument = _documents[newIndex];
  229. changed = true;
  230. }
  231. return changed;
  232. }
  233. private void InitializeComponent()
  234. {
  235. Name = "DocumentStrip";
  236. }
  237. protected override void Dispose(bool disposing)
  238. {
  239. if (disposing)
  240. {
  241. while (_documents.Count > 0)
  242. {
  243. RemoveDocumentWorkspace(_documents[_documents.Count - 1]);
  244. }
  245. if (_thumbnailManager != null)
  246. {
  247. _thumbnailManager.Dispose();
  248. _thumbnailManager = null;
  249. }
  250. foreach (RenderArgs ra in _thumbs.Keys.Select(dw => _thumbs[dw]))
  251. {
  252. ra.Dispose();
  253. }
  254. _thumbs.Clear();
  255. _thumbs = null;
  256. }
  257. base.Dispose(disposing);
  258. }
  259. protected new void OnScrollArrowClicked(ArrowDirection arrowDirection)
  260. {
  261. int sign = 0;
  262. switch (arrowDirection)
  263. {
  264. case ArrowDirection.Left:
  265. sign = -1;
  266. break;
  267. case ArrowDirection.Right:
  268. sign = +1;
  269. break;
  270. }
  271. int delta = ItemSize.Width;
  272. ScrollOffset += sign * delta;
  273. base.OnScrollArrowClicked(arrowDirection);
  274. }
  275. protected override void OnItemClicked(Item item, ItemPart itemPart, MouseButtons mouseButtons)
  276. {
  277. var dw = item.Tag as DocumentWorkspace;
  278. if (dw != null)
  279. {
  280. switch (itemPart)
  281. {
  282. case ItemPart.None:
  283. // do nothing
  284. break;
  285. case ItemPart.CloseButton:
  286. if (mouseButtons == MouseButtons.Left)
  287. {
  288. OnDocumentClicked(dw, DocumentClickAction.Close);
  289. }
  290. break;
  291. case ItemPart.Image:
  292. switch (mouseButtons)
  293. {
  294. case MouseButtons.Left:
  295. SelectedDocument = dw;
  296. break;
  297. case MouseButtons.Right: //TODO: Right-click menu
  298. break;
  299. }
  300. break;
  301. default:
  302. throw new InvalidEnumArgumentException();
  303. }
  304. }
  305. base.OnItemClicked(item, itemPart, mouseButtons);
  306. }
  307. public event EventHandler<EventArgs<Pair<DocumentWorkspace, DocumentClickAction>>> DocumentClicked;
  308. protected virtual void OnDocumentClicked(DocumentWorkspace dw, DocumentClickAction action)
  309. {
  310. if (DocumentClicked != null)
  311. {
  312. DocumentClicked(this, new EventArgs<Pair<DocumentWorkspace, DocumentClickAction>>(
  313. Pair.Create(dw, action)));
  314. }
  315. }
  316. public void UnlockDocumentWorkspaceDirtyValue(DocumentWorkspace unlockMe)
  317. {
  318. Item docItem = _dw2Button[unlockMe];
  319. docItem.UnlockDirtyValue();
  320. }
  321. public void LockDocumentWorkspaceDirtyValue(DocumentWorkspace lockMe, bool forceDirtyValue)
  322. {
  323. Item docItem = _dw2Button[lockMe];
  324. docItem.LockDirtyValue(forceDirtyValue);
  325. }
  326. public void AddDocumentWorkspace(DocumentWorkspace addMe)
  327. {
  328. _documents.Add(addMe);
  329. var docButton = new Item {Image = null, Tag = addMe};
  330. AddItem(docButton);
  331. _documentButtons.Add(docButton);
  332. addMe.CompositionUpdated += WorkspaceCompositionUpdated;
  333. _dw2Button.Add(addMe, docButton);
  334. if (addMe.Document != null)
  335. {
  336. QueueThumbnailUpdate(addMe);
  337. docButton.Dirty = addMe.Document.Dirty;
  338. addMe.Document.DirtyChanged += DocumentDirtyChanged;
  339. }
  340. addMe.DocumentChanging += WorkspaceDocumentChanging;
  341. addMe.DocumentChanged += WorkspaceDocumentChanged;
  342. OnDocumentListChanged();
  343. }
  344. private void WorkspaceDocumentChanging(object sender, EventArgs<Document> e)
  345. {
  346. if (e.Data != null)
  347. {
  348. e.Data.DirtyChanged -= DocumentDirtyChanged;
  349. }
  350. }
  351. private void WorkspaceDocumentChanged(object sender, EventArgs e)
  352. {
  353. var dw = (DocumentWorkspace)sender;
  354. Item docButton = _dw2Button[dw];
  355. if (dw.Document != null)
  356. {
  357. docButton.Dirty = dw.Document.Dirty;
  358. dw.Document.DirtyChanged += DocumentDirtyChanged;
  359. }
  360. else
  361. {
  362. docButton.Dirty = false;
  363. }
  364. }
  365. private void DocumentDirtyChanged(object sender, EventArgs e)
  366. {
  367. foreach (Item docButton in
  368. from t in _documents where ReferenceEquals(sender, t.Document) select _dw2Button[t])
  369. {
  370. docButton.Dirty = ((Document)sender).Dirty;
  371. }
  372. }
  373. private void WorkspaceCompositionUpdated(object sender, EventArgs e)
  374. {
  375. var dw = (DocumentWorkspace)sender;
  376. QueueThumbnailUpdate(dw);
  377. }
  378. public void RemoveDocumentWorkspace(DocumentWorkspace removeMe)
  379. {
  380. removeMe.CompositionUpdated -= WorkspaceCompositionUpdated;
  381. if (_selectedDocument == removeMe)
  382. {
  383. _selectedDocument = null;
  384. }
  385. removeMe.DocumentChanging -= WorkspaceDocumentChanging;
  386. removeMe.DocumentChanged -= WorkspaceDocumentChanged;
  387. if (removeMe.Document != null)
  388. {
  389. removeMe.Document.DirtyChanged -= DocumentDirtyChanged;
  390. }
  391. _documents.Remove(removeMe);
  392. _thumbnailManager.RemoveFromQueue(removeMe);
  393. Item docButton = _dw2Button[removeMe];
  394. RemoveItem(docButton);
  395. _dw2Button.Remove(removeMe);
  396. _documentButtons.Remove(docButton);
  397. if (_thumbs.ContainsKey(removeMe))
  398. {
  399. RenderArgs thumbRa = _thumbs[removeMe];
  400. Surface surface = thumbRa.Surface;
  401. thumbRa.Dispose();
  402. _thumbs.Remove(removeMe);
  403. surface.Dispose();
  404. }
  405. OnDocumentListChanged();
  406. }
  407. protected override void OnLayout(LayoutEventArgs levent)
  408. {
  409. base.OnLayout(levent);
  410. if (!_ensureSelectedIsVisible || (Focused || LeftScrollButton.Focused || RightScrollButton.Focused)) return;
  411. int index = _documents.IndexOf(_selectedDocument);
  412. EnsureItemFullyVisible(index);
  413. }
  414. public void SelectDocumentWorkspace(DocumentWorkspace selectMe)
  415. {
  416. UI.SuspendControlPainting(this);
  417. _selectedDocument = selectMe;
  418. if (_thumbs.ContainsKey(selectMe))
  419. {
  420. RenderArgs thumb = _thumbs[selectMe];
  421. Bitmap bitmap = thumb.Bitmap;
  422. }
  423. else
  424. {
  425. QueueThumbnailUpdate(selectMe);
  426. }
  427. foreach (Item docItem in _documentButtons)
  428. {
  429. if ((docItem.Tag as DocumentWorkspace) == selectMe)
  430. {
  431. EnsureItemFullyVisible(docItem);
  432. docItem.Checked = true;
  433. }
  434. else
  435. {
  436. docItem.Checked = false;
  437. }
  438. }
  439. UI.ResumeControlPainting(this);
  440. Invalidate(true);
  441. }
  442. public void RefreshThumbnail(DocumentWorkspace dw)
  443. {
  444. if (_documents.Contains(dw))
  445. {
  446. QueueThumbnailUpdate(dw);
  447. }
  448. }
  449. public void RefreshAllThumbnails()
  450. {
  451. foreach (DocumentWorkspace dw in _documents)
  452. {
  453. QueueThumbnailUpdate(dw);
  454. }
  455. }
  456. private void OnThumbnailUpdated(DocumentWorkspace dw)
  457. {
  458. // We must double check that the DW is still around, because there's a chance
  459. // that the DW was been removed while the thumbnail was being rendered.
  460. if (!_dw2Button.ContainsKey(dw)) return;
  461. Item docButton = _dw2Button[dw];
  462. RenderArgs docRA = _thumbs[dw];
  463. docButton.Image = docRA.Bitmap;
  464. docButton.Update();
  465. }
  466. public void QueueThumbnailUpdate(DocumentWorkspace dw)
  467. {
  468. if (_suspendThumbnailUpdates <= 0)
  469. {
  470. _thumbnailManager.QueueThumbnailUpdate(dw, PreferredImageSize.Width - 2, OnThumbnailRendered);
  471. }
  472. }
  473. private void OnThumbnailRendered(object sender, EventArgs<Pair<IThumbnailProvider, Surface>> e)
  474. {
  475. RenderArgs ra = null;
  476. var dw = (DocumentWorkspace)e.Data.First;
  477. var desiredSize = new Size(e.Data.Second.Width + 2, e.Data.Second.Height + 2);
  478. if (_thumbs.ContainsKey(dw))
  479. {
  480. ra = _thumbs[dw];
  481. if (ra.Size != desiredSize)
  482. {
  483. ra.Dispose();
  484. ra = null;
  485. _thumbs.Remove(dw);
  486. }
  487. }
  488. if (ra == null)
  489. {
  490. var surface = new Surface(desiredSize);
  491. ra = new RenderArgs(surface);
  492. _thumbs.Add(dw, ra);
  493. }
  494. ra.Surface.Clear(ColorBgra.Black);
  495. ra.Surface.CopySurface(e.Data.Second, new Point(1, 1));
  496. e.Data.Second.Dispose();
  497. OnThumbnailUpdated(dw);
  498. }
  499. }
  500. }