PageRenderTime 60ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/src/tools/TextTool.cs

https://bitbucket.org/tuldok89/openpdn
C# | 1599 lines | 1297 code | 234 blank | 68 comment | 282 complexity | fa81dd48fc8b95a75820e4dc60c62374 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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 PaintDotNet.Base;
  10. using PaintDotNet.HistoryMementos;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Drawing;
  14. using System.Windows.Forms;
  15. using System.Collections;
  16. using System.ComponentModel;
  17. namespace PaintDotNet.Tools
  18. {
  19. internal class TextTool
  20. : Tool
  21. {
  22. private enum EditingMode
  23. {
  24. NotEditing,
  25. EmptyEdit,
  26. Editing
  27. }
  28. private readonly string _statusBarTextFormat = PdnResources.GetString("TextTool.StatusText.TextInfo.Format");
  29. private Point _startMouseXy;
  30. private Point _startClickPoint;
  31. private bool _tracking;
  32. private MoveNubRenderer _moveNub;
  33. private int _ignoreRedraw;
  34. private RenderArgs _ra;
  35. private EditingMode _mode;
  36. private ArrayList _lines;
  37. private int _linePos;
  38. private int _textPos;
  39. private Point _clickPoint;
  40. private Font _font;
  41. private TextAlignment _alignment;
  42. private IrregularSurface _saved;
  43. private const int CursorInterval = 300;
  44. private bool _pulseEnabled;
  45. private DateTime _startTime;
  46. private bool _lastPulseCursorState;
  47. private Cursor _textToolCursor;
  48. private Threading.ThreadPool _threadPool;
  49. private bool _enableNub = true;
  50. private CompoundHistoryMemento _currentHa;
  51. private bool _controlKeyDown;
  52. private DateTime _controlKeyDownTime = DateTime.MinValue;
  53. private readonly TimeSpan _controlKeyDownThreshold = new TimeSpan(0, 0, 0, 0, 400);
  54. private void AlphaBlendingChangedHandler(object sender, EventArgs e)
  55. {
  56. if (_mode != EditingMode.NotEditing)
  57. {
  58. RedrawText(true);
  59. }
  60. }
  61. private EventHandler _fontChangedDelegate;
  62. private void FontChangedHandler(object sender, EventArgs a)
  63. {
  64. _font = AppEnvironment.FontInfo.CreateFont();
  65. if (_mode == EditingMode.NotEditing) return;
  66. _sizes = null;
  67. RedrawText(true);
  68. }
  69. private EventHandler _fontSmoothingChangedDelegate;
  70. private void FontSmoothingChangedHandler(object sender, EventArgs e)
  71. {
  72. if (_mode == EditingMode.NotEditing) return;
  73. _sizes = null;
  74. RedrawText(true);
  75. }
  76. private EventHandler _alignmentChangedDelegate;
  77. private void AlignmentChangedHandler(object sender, EventArgs a)
  78. {
  79. _alignment = AppEnvironment.TextAlignment;
  80. if (_mode == EditingMode.NotEditing) return;
  81. _sizes = null;
  82. RedrawText(true);
  83. }
  84. private EventHandler _brushChangedDelegate;
  85. private void BrushChangedHandler(object sender, EventArgs a)
  86. {
  87. if (_mode != EditingMode.NotEditing)
  88. {
  89. RedrawText(true);
  90. }
  91. }
  92. private EventHandler _antiAliasChangedDelegate;
  93. private void AntiAliasChangedHandler(object sender, EventArgs a)
  94. {
  95. if (_mode == EditingMode.NotEditing) return;
  96. _sizes = null;
  97. RedrawText(true);
  98. }
  99. private EventHandler _foreColorChangedDelegate;
  100. private void ForeColorChangedHandler(object sender, EventArgs e)
  101. {
  102. if (_mode != EditingMode.NotEditing)
  103. {
  104. RedrawText(true);
  105. }
  106. }
  107. private void BackColorChangedHandler(object sender, EventArgs e)
  108. {
  109. if (_mode != EditingMode.NotEditing)
  110. {
  111. RedrawText(true);
  112. }
  113. }
  114. private bool OnBackspaceTyped(Keys keys)
  115. {
  116. if (!DocumentWorkspace.Visible)
  117. {
  118. return false;
  119. }
  120. if (_mode != EditingMode.NotEditing)
  121. {
  122. OnKeyPress(Keys.Back);
  123. return true;
  124. }
  125. return false;
  126. }
  127. protected override void OnActivate()
  128. {
  129. PdnBaseForm.RegisterFormHotKey(Keys.Back, OnBackspaceTyped);
  130. base.OnActivate();
  131. _textToolCursor = new Cursor(PdnResources.GetResourceStream("Cursors.TextToolCursor.cur"));
  132. Cursor = _textToolCursor;
  133. _fontChangedDelegate = new EventHandler(FontChangedHandler);
  134. _fontSmoothingChangedDelegate = new EventHandler(FontSmoothingChangedHandler);
  135. _alignmentChangedDelegate = new EventHandler(AlignmentChangedHandler);
  136. _brushChangedDelegate = new EventHandler(BrushChangedHandler);
  137. _antiAliasChangedDelegate = new EventHandler(AntiAliasChangedHandler);
  138. _foreColorChangedDelegate = new EventHandler(ForeColorChangedHandler);
  139. _ra = new RenderArgs(((BitmapLayer)ActiveLayer).Surface);
  140. _mode = EditingMode.NotEditing;
  141. _font = AppEnvironment.FontInfo.CreateFont();
  142. _alignment = AppEnvironment.TextAlignment;
  143. AppEnvironment.BrushInfoChanged += _brushChangedDelegate;
  144. AppEnvironment.FontInfoChanged += _fontChangedDelegate;
  145. AppEnvironment.FontSmoothingChanged += _fontSmoothingChangedDelegate;
  146. AppEnvironment.TextAlignmentChanged += _alignmentChangedDelegate;
  147. AppEnvironment.AntiAliasingChanged += _antiAliasChangedDelegate;
  148. AppEnvironment.PrimaryColorChanged += _foreColorChangedDelegate;
  149. AppEnvironment.SecondaryColorChanged += BackColorChangedHandler;
  150. AppEnvironment.AlphaBlendingChanged += AlphaBlendingChangedHandler;
  151. _threadPool = new Threading.ThreadPool();
  152. _moveNub = new MoveNubRenderer(RendererList)
  153. {
  154. Shape = MoveNubShape.Compass,
  155. Size = new SizeF(10, 10),
  156. Visible = false
  157. };
  158. RendererList.Add(_moveNub, false);
  159. }
  160. protected override void OnDeactivate()
  161. {
  162. PdnBaseForm.UnregisterFormHotKey(Keys.Back, OnBackspaceTyped);
  163. base.OnDeactivate();
  164. switch (_mode)
  165. {
  166. case EditingMode.Editing:
  167. SaveHistoryMemento();
  168. break;
  169. case EditingMode.EmptyEdit:
  170. RedrawText(false);
  171. break;
  172. case EditingMode.NotEditing:
  173. break;
  174. default:
  175. throw new InvalidEnumArgumentException("Invalid Editing Mode");
  176. }
  177. if (_ra != null)
  178. {
  179. _ra.Dispose();
  180. _ra = null;
  181. }
  182. if (_saved != null)
  183. {
  184. _saved.Dispose();
  185. _saved = null;
  186. }
  187. AppEnvironment.BrushInfoChanged -= _brushChangedDelegate;
  188. AppEnvironment.FontInfoChanged -= _fontChangedDelegate;
  189. AppEnvironment.FontSmoothingChanged -= _fontSmoothingChangedDelegate;
  190. AppEnvironment.TextAlignmentChanged -= _alignmentChangedDelegate;
  191. AppEnvironment.AntiAliasingChanged -= _antiAliasChangedDelegate;
  192. AppEnvironment.PrimaryColorChanged -= _foreColorChangedDelegate;
  193. AppEnvironment.SecondaryColorChanged -= BackColorChangedHandler;
  194. AppEnvironment.AlphaBlendingChanged -= AlphaBlendingChangedHandler;
  195. StopEditing();
  196. _threadPool = null;
  197. RendererList.Remove(_moveNub);
  198. _moveNub.Dispose();
  199. _moveNub = null;
  200. if (_textToolCursor == null) return;
  201. _textToolCursor.Dispose();
  202. _textToolCursor = null;
  203. }
  204. private void StopEditing()
  205. {
  206. _mode = EditingMode.NotEditing;
  207. _pulseEnabled = false;
  208. _lines = null;
  209. _moveNub.Visible = false;
  210. }
  211. private void StartEditing()
  212. {
  213. _linePos = 0;
  214. _textPos = 0;
  215. _lines = new ArrayList();
  216. _sizes = null;
  217. _lines.Add(string.Empty);
  218. _startTime = DateTime.Now;
  219. _mode = EditingMode.EmptyEdit;
  220. _pulseEnabled = true;
  221. UpdateStatusText();
  222. }
  223. private void UpdateStatusText()
  224. {
  225. string text;
  226. ImageResource image;
  227. if (_tracking)
  228. {
  229. text = GetStatusBarXyText();
  230. image = Image;
  231. }
  232. else
  233. {
  234. text = PdnResources.GetString("TextTool.StatusText.StartTyping");
  235. image = null;
  236. }
  237. SetStatus(image, text);
  238. }
  239. private void PerformEnter()
  240. {
  241. if (_lines == null)
  242. {
  243. return;
  244. }
  245. var currentLine = (string)_lines[_linePos]; //Change to string if var fails
  246. if (_textPos == currentLine.Length)
  247. {
  248. // If we are at the end of a line, insert an empty line at the next line
  249. _lines.Insert(_linePos + 1, string.Empty);
  250. }
  251. else
  252. {
  253. _lines.Insert(_linePos + 1, currentLine.Substring(_textPos, currentLine.Length - _textPos));
  254. _lines[_linePos] = ((string)_lines[_linePos]).Substring(0, _textPos);
  255. }
  256. _linePos++;
  257. _textPos = 0;
  258. _sizes = null;
  259. }
  260. private void PerformBackspace()
  261. {
  262. if (_textPos == 0 && _linePos > 0)
  263. {
  264. int ntp = ((string)_lines[_linePos - 1]).Length;
  265. _lines[_linePos - 1] = ((string)_lines[_linePos - 1]) + ((string)_lines[_linePos]);
  266. _lines.RemoveAt(_linePos);
  267. _linePos--;
  268. _textPos = ntp;
  269. _sizes = null;
  270. }
  271. else if (_textPos > 0)
  272. {
  273. var ln = (string)_lines[_linePos]; //Flip back to string if var fails
  274. // If we are at the end of a line, we don't need to place a compound string
  275. if (_textPos == ln.Length)
  276. {
  277. _lines[_linePos] = ln.Substring(0, ln.Length - 1);
  278. }
  279. else
  280. {
  281. _lines[_linePos] = ln.Substring(0, _textPos - 1) + ln.Substring(_textPos);
  282. }
  283. _textPos--;
  284. _sizes = null;
  285. }
  286. }
  287. private void PerformControlBackspace()
  288. {
  289. if (_textPos == 0 && _linePos > 0)
  290. {
  291. PerformBackspace();
  292. }
  293. else if (_textPos > 0)
  294. {
  295. var currentLine = (string)_lines[_linePos]; //Flip back to string if var fails
  296. int ntp = _textPos;
  297. if (Char.IsLetterOrDigit(currentLine[ntp - 1]))
  298. {
  299. while (ntp > 0 && (Char.IsLetterOrDigit(currentLine[ntp - 1])))
  300. {
  301. ntp--;
  302. }
  303. }
  304. else if (Char.IsWhiteSpace(currentLine[ntp - 1]))
  305. {
  306. while (ntp > 0 && (Char.IsWhiteSpace(currentLine[ntp - 1])))
  307. {
  308. ntp--;
  309. }
  310. }
  311. else if (Char.IsPunctuation(currentLine[ntp - 1]))
  312. {
  313. while (ntp > 0 && (Char.IsPunctuation(currentLine[ntp - 1])))
  314. {
  315. ntp--;
  316. }
  317. }
  318. else
  319. {
  320. ntp--;
  321. }
  322. _lines[_linePos] = currentLine.Substring(0, ntp) + currentLine.Substring(_textPos);
  323. _textPos = ntp;
  324. _sizes = null;
  325. }
  326. }
  327. private void PerformDelete()
  328. {
  329. // Where are we?!
  330. if ((_linePos == _lines.Count - 1) && (_textPos == ((string)_lines[_lines.Count - 1]).Length))
  331. {
  332. // If the cursor is at the end of the text block
  333. return;
  334. }
  335. if (_textPos == ((string)_lines[_linePos]).Length)
  336. {
  337. // End of a line, must merge strings
  338. _lines[_linePos] = ((string)_lines[_linePos]) + ((string)_lines[_linePos + 1]);
  339. _lines.RemoveAt(_linePos + 1);
  340. }
  341. else
  342. {
  343. // Middle of a line somewhere
  344. _lines[_linePos] = ((string)_lines[_linePos]).Substring(0, _textPos) + ((string)_lines[_linePos]).Substring(_textPos + 1);
  345. }
  346. // Check for state change
  347. if (_lines.Count == 1 && ((string)_lines[0]) == "")
  348. {
  349. _mode = EditingMode.EmptyEdit;
  350. }
  351. _sizes = null;
  352. }
  353. private void PerformControlDelete()
  354. {
  355. // where are we?!
  356. if ((_linePos == _lines.Count - 1) && (_textPos == ((string)_lines[_lines.Count - 1]).Length))
  357. {
  358. // If the cursor is at the end of the text block
  359. return;
  360. }
  361. if (_textPos == ((string)_lines[_linePos]).Length)
  362. {
  363. // End of a line, must merge strings
  364. _lines[_linePos] = ((string)_lines[_linePos]) + ((string)_lines[_linePos + 1]);
  365. _lines.RemoveAt(_linePos + 1);
  366. }
  367. else
  368. {
  369. // Middle of a line somewhere
  370. int ntp = _textPos;
  371. var currentLine = (string)_lines[_linePos]; //Flip back to string if var fails
  372. if (Char.IsLetterOrDigit(currentLine[ntp]))
  373. {
  374. while (ntp < currentLine.Length && (Char.IsLetterOrDigit(currentLine[ntp])))
  375. {
  376. currentLine = currentLine.Remove(ntp, 1);
  377. }
  378. }
  379. else if (Char.IsWhiteSpace(currentLine[ntp]))
  380. {
  381. while (ntp < currentLine.Length && (Char.IsWhiteSpace(currentLine[ntp])))
  382. {
  383. currentLine = currentLine.Remove(ntp, 1);
  384. }
  385. }
  386. else if (Char.IsPunctuation(currentLine[ntp]))
  387. {
  388. while (ntp < currentLine.Length && (Char.IsPunctuation(currentLine[ntp])))
  389. {
  390. currentLine = currentLine.Remove(ntp, 1);
  391. }
  392. }
  393. else
  394. {
  395. ntp--;
  396. }
  397. _lines[_linePos] = currentLine;
  398. }
  399. // Check for state change
  400. if (_lines.Count == 1 && ((string)_lines[0]) == "")
  401. {
  402. _mode = EditingMode.EmptyEdit;
  403. }
  404. _sizes = null;
  405. }
  406. private void PerformLeft()
  407. {
  408. if (_textPos > 0)
  409. {
  410. _textPos--;
  411. }
  412. else if (_textPos == 0 && _linePos > 0)
  413. {
  414. _linePos--;
  415. _textPos = ((string)_lines[_linePos]).Length;
  416. }
  417. }
  418. private void PerformControlLeft()
  419. {
  420. if (_textPos > 0)
  421. {
  422. int ntp = _textPos;
  423. var currentLine = (string)_lines[_linePos]; //Flip to string if var fails
  424. if (Char.IsLetterOrDigit(currentLine[ntp - 1]))
  425. {
  426. while (ntp > 0 && (Char.IsLetterOrDigit(currentLine[ntp - 1])))
  427. {
  428. ntp--;
  429. }
  430. }
  431. else if (Char.IsWhiteSpace(currentLine[ntp - 1]))
  432. {
  433. while (ntp > 0 && (Char.IsWhiteSpace(currentLine[ntp - 1])))
  434. {
  435. ntp--;
  436. }
  437. }
  438. else if (ntp > 0 && Char.IsPunctuation(currentLine[ntp - 1]))
  439. {
  440. while (ntp > 0 && Char.IsPunctuation(currentLine[ntp - 1]))
  441. {
  442. ntp--;
  443. }
  444. }
  445. else
  446. {
  447. ntp--;
  448. }
  449. _textPos = ntp;
  450. }
  451. else if (_textPos == 0 && _linePos > 0)
  452. {
  453. _linePos--;
  454. _textPos = ((string)_lines[_linePos]).Length;
  455. }
  456. }
  457. private void PerformRight()
  458. {
  459. if (_textPos < ((string)_lines[_linePos]).Length)
  460. {
  461. _textPos++;
  462. }
  463. else if (_textPos == ((string)_lines[_linePos]).Length && _linePos < _lines.Count - 1)
  464. {
  465. _linePos++;
  466. _textPos = 0;
  467. }
  468. }
  469. private void PerformControlRight()
  470. {
  471. if (_textPos < ((string)_lines[_linePos]).Length)
  472. {
  473. int ntp = _textPos;
  474. var currentLine = (string)_lines[_linePos]; //Flip to string if var fails
  475. if (Char.IsLetterOrDigit(currentLine[ntp]))
  476. {
  477. while (ntp < currentLine.Length && (Char.IsLetterOrDigit(currentLine[ntp])))
  478. {
  479. ntp++;
  480. }
  481. }
  482. else if (Char.IsWhiteSpace(currentLine[ntp]))
  483. {
  484. while (ntp < currentLine.Length && (Char.IsWhiteSpace(currentLine[ntp])))
  485. {
  486. ntp++;
  487. }
  488. }
  489. else if (ntp > 0 && Char.IsPunctuation(currentLine[ntp]))
  490. {
  491. while (ntp < currentLine.Length && Char.IsPunctuation(currentLine[ntp]))
  492. {
  493. ntp++;
  494. }
  495. }
  496. else
  497. {
  498. ntp++;
  499. }
  500. _textPos = ntp;
  501. }
  502. else if (_textPos == ((string)_lines[_linePos]).Length && _linePos < _lines.Count - 1)
  503. {
  504. _linePos++;
  505. _textPos = 0;
  506. }
  507. }
  508. private void PerformUp()
  509. {
  510. PointF p = TextPositionToPoint(new Position(_linePos, _textPos));
  511. p.Y -= _sizes[0].Height; //font.Height;
  512. Position np = PointToTextPosition(p);
  513. _linePos = np.Line;
  514. _textPos = np.Offset;
  515. }
  516. private void PerformDown()
  517. {
  518. if (_linePos == _lines.Count - 1)
  519. {
  520. // last line -> don't do squat
  521. }
  522. else
  523. {
  524. PointF p = TextPositionToPoint(new Position(_linePos, _textPos));
  525. p.Y += _sizes[0].Height; //font.Height;
  526. Position np = PointToTextPosition(p);
  527. _linePos = np.Line;
  528. _textPos = np.Offset;
  529. }
  530. }
  531. private Point GetUpperLeft(Size sz, int line)
  532. {
  533. Point p = _clickPoint;
  534. p.Y = (int)(p.Y - (0.5 * sz.Height) + (line * sz.Height));
  535. switch (_alignment)
  536. {
  537. case TextAlignment.Center:
  538. p.X = (int)(p.X - (0.5) * sz.Width);
  539. break;
  540. case TextAlignment.Right:
  541. p.X = p.X - sz.Width;
  542. break;
  543. }
  544. return p;
  545. }
  546. private Size StringSize(string s)
  547. {
  548. // We measure using a 1x1 device context to avoid performance problems that arise otherwise with large images.
  549. using (Surface window = ScratchSurface.CreateWindow(new Rectangle(0, 0, 1, 1)))
  550. {
  551. using (var ra2 = new RenderArgs(window)) //Flip back to RendeArgs if var fails
  552. {
  553. return SystemLayer.Fonts.MeasureString(
  554. ra2.Graphics,
  555. _font,
  556. s,
  557. AppEnvironment.AntiAliasing,
  558. AppEnvironment.FontSmoothing);
  559. }
  560. }
  561. }
  562. private sealed class Position
  563. {
  564. private int line;
  565. public int Line
  566. {
  567. get
  568. {
  569. return line;
  570. }
  571. set {
  572. /*if (value >= 0)
  573. {
  574. line = value;
  575. }
  576. else
  577. {
  578. line = 0;*/
  579. //NEW CODE IS THE SINGLE LINE BELOW, ABOVE CODE IS KEPT IN CASE BUGS APPEAR USING THE BOTTOM ONE.
  580. line = value >= 0 ? value : 0;
  581. }
  582. }
  583. private int _offset;
  584. public int Offset
  585. {
  586. get
  587. {
  588. return _offset;
  589. }
  590. set {
  591. _offset = value >= 0 ? value : 0;
  592. }
  593. }
  594. public Position(int line, int offset)
  595. {
  596. this.line = line;
  597. _offset = offset;
  598. }
  599. }
  600. private void SaveHistoryMemento()
  601. {
  602. _pulseEnabled = false;
  603. RedrawText(false);
  604. if (_saved == null) return;
  605. PdnRegion hitTest = Selection.CreateRegion();
  606. hitTest.Intersect(_saved.Region);
  607. if (!hitTest.IsEmpty())
  608. {
  609. var bha = new BitmapHistoryMemento(Name, Image, DocumentWorkspace, //Change to BitmapHistoryMemento if var fails
  610. ActiveLayerIndex, _saved);
  611. if (_currentHa == null)
  612. {
  613. HistoryStack.PushNewMemento(bha);
  614. }
  615. else
  616. {
  617. _currentHa.PushNewAction(bha);
  618. _currentHa = null;
  619. }
  620. }
  621. hitTest.Dispose();
  622. _saved.Dispose();
  623. _saved = null;
  624. }
  625. private void DrawText(Surface dst, Font textFont, string text, Point pt, Size measuredSize, bool antiAliasing, Brush brush)
  626. {
  627. var dstRect = new Rectangle(pt, measuredSize); //Change to Rectangle if var fails
  628. Rectangle dstRectClipped = Rectangle.Intersect(dstRect, ScratchSurface.Bounds);
  629. if (dstRectClipped.Width == 0 || dstRectClipped.Height == 0)
  630. {
  631. return;
  632. }
  633. using (var surface = new Surface(8, 8)) //Change to Surface if var fails
  634. {
  635. using (var renderArgs = new RenderArgs(surface)) //Change to RenderArgs if var fails
  636. {
  637. renderArgs.Graphics.FillRectangle(brush, 0, 0, surface.Width, surface.Height);
  638. }
  639. DrawText(dst, textFont, text, pt, measuredSize, surface);
  640. }
  641. }
  642. private unsafe void DrawText(Surface dst, Font textFont, string text, Point pt, Size measuredSize, Surface brush8X8)
  643. {
  644. Point pt2 = pt;
  645. Size measuredSize2 = measuredSize;
  646. var offset = textFont.Height; //Change to int if var fails
  647. pt.X -= offset;
  648. measuredSize.Width += 2 * offset;
  649. var dstRect = new Rectangle(pt, measuredSize); //Change to Rectangle if var fails
  650. Rectangle dstRectClipped = Rectangle.Intersect(dstRect, ScratchSurface.Bounds);
  651. if (dstRectClipped.Width == 0 || dstRectClipped.Height == 0)
  652. {
  653. return;
  654. }
  655. // We only use the first 8,8 of brush
  656. using (var renderArgs = new RenderArgs(ScratchSurface))
  657. {
  658. renderArgs.Graphics.FillRectangle(Brushes.White, pt.X, pt.Y, measuredSize.Width, measuredSize.Height);
  659. if (measuredSize.Width > 0 && measuredSize.Height > 0)
  660. {
  661. using (Surface s2 = renderArgs.Surface.CreateWindow(dstRectClipped))
  662. {
  663. using (var renderArgs2 = new RenderArgs(s2))
  664. {
  665. SystemLayer.Fonts.DrawText(
  666. renderArgs2.Graphics,
  667. _font,
  668. text,
  669. new Point(dstRect.X - dstRectClipped.X + offset, dstRect.Y - dstRectClipped.Y),
  670. AppEnvironment.AntiAliasing,
  671. AppEnvironment.FontSmoothing);
  672. }
  673. }
  674. }
  675. // Mask out anything that isn't within the user's clip region (selected region)
  676. using (PdnRegion clip = Selection.CreateRegion())
  677. {
  678. clip.Xor(renderArgs.Surface.Bounds); // invert
  679. clip.Intersect(new Rectangle(pt, measuredSize));
  680. renderArgs.Graphics.FillRegion(Brushes.White, clip.GetRegionReadOnly());
  681. }
  682. int skipX;
  683. if (pt.X < 0)
  684. {
  685. skipX = -pt.X;
  686. }
  687. else
  688. {
  689. skipX = 0;
  690. }
  691. int xEnd = Math.Min(dst.Width, pt.X + measuredSize.Width);
  692. bool blending = AppEnvironment.AlphaBlending;
  693. if (!dst.IsColumnVisible(pt.X + skipX)) return;
  694. for (int y = pt.Y; y < pt.Y + measuredSize.Height; ++y)
  695. {
  696. if (!dst.IsRowVisible(y))
  697. {
  698. continue;
  699. }
  700. ColorBgra *dstPtr = dst.GetPointAddressUnchecked(pt.X + skipX, y);
  701. ColorBgra *srcPtr = ScratchSurface.GetPointAddress(pt.X + skipX, y);
  702. ColorBgra *brushPtr = brush8X8.GetRowAddressUnchecked(y & 7);
  703. for (int x = pt.X + skipX; x < xEnd; ++x)
  704. {
  705. ColorBgra srcPixel = *srcPtr;
  706. ColorBgra dstPixel = *dstPtr;
  707. ColorBgra brushPixel = brushPtr[x & 7];
  708. int alpha = ((255 - srcPixel.R) * brushPixel.A) / 255; // we could use srcPixel.R, .G, or .B -- the choice here is arbitrary
  709. brushPixel.A = (byte)alpha;
  710. if (srcPtr->R == 255) // could use R, G, or B -- arbitrary choice
  711. {
  712. // do nothing -- leave dst alone
  713. }
  714. else if (alpha == 255 || !blending)
  715. {
  716. // copy it straight over
  717. *dstPtr = brushPixel;
  718. }
  719. else
  720. {
  721. // do expensive blending
  722. *dstPtr = UserBlendOps.NormalBlendOp.ApplyStatic(dstPixel, brushPixel);
  723. }
  724. ++dstPtr;
  725. ++srcPtr;
  726. }
  727. }
  728. }
  729. }
  730. /// <summary>
  731. /// Redraws the Text on the screen
  732. /// </summary>
  733. /// <remarks>
  734. /// assumes that the <b>font</b> and the <b>alignment</b> are already set
  735. /// </remarks>
  736. /// <param name="cursorOn"></param>
  737. private void RedrawText(bool cursorOn)
  738. {
  739. if (_ignoreRedraw > 0)
  740. {
  741. return;
  742. }
  743. if (_saved != null)
  744. {
  745. _saved.Draw(_ra.Surface);
  746. ActiveLayer.Invalidate(_saved.Region);
  747. _saved.Dispose();
  748. _saved = null;
  749. }
  750. // Save the Space behind the lines
  751. var rects = new Rectangle[_lines.Count + 1];
  752. var localUls = new Point[_lines.Count];
  753. // All Lines
  754. bool recalcSizes = false;
  755. if (_sizes == null)
  756. {
  757. recalcSizes = true;
  758. _sizes = new Size[_lines.Count + 1];
  759. }
  760. if (recalcSizes)
  761. {
  762. for (int i = 0; i < _lines.Count; ++i)
  763. {
  764. _threadPool.QueueUserWorkItem(MeasureText,
  765. BoxedConstants.GetInt32(i));
  766. }
  767. _threadPool.Drain();
  768. }
  769. for (int i = 0; i < _lines.Count; ++i)
  770. {
  771. Point upperLeft = GetUpperLeft(_sizes[i], i);
  772. localUls[i] = upperLeft;
  773. var rect = new Rectangle(upperLeft, _sizes[i]);
  774. rects[i] = rect;
  775. }
  776. // The Cursor Line
  777. string cursorLine = ((string)_lines[_linePos]).Substring(0, _textPos);
  778. Size cursorLineSize;
  779. Point cursorUl;
  780. Rectangle cursorRect;
  781. bool emptyCursorLineFlag;
  782. if (cursorLine.Length == 0)
  783. {
  784. emptyCursorLineFlag = true;
  785. Size fullLineSize = _sizes[_linePos];
  786. cursorLineSize = new Size(2, (int)(Math.Ceiling(_font.GetHeight())));
  787. cursorUl = GetUpperLeft(fullLineSize, _linePos);
  788. cursorRect = new Rectangle(cursorUl, cursorLineSize);
  789. }
  790. else if (cursorLine.Length == ((string)_lines[_linePos]).Length)
  791. {
  792. emptyCursorLineFlag = false;
  793. cursorLineSize = _sizes[_linePos];
  794. cursorUl = localUls[_linePos];
  795. cursorRect = new Rectangle(cursorUl, cursorLineSize);
  796. }
  797. else
  798. {
  799. emptyCursorLineFlag = false;
  800. cursorLineSize = StringSize(cursorLine);
  801. cursorUl = localUls[_linePos];
  802. cursorRect = new Rectangle(cursorUl, cursorLineSize);
  803. }
  804. rects[_lines.Count] = cursorRect;
  805. // Account for overhang on italic or fancy fonts
  806. var offset = _font.Height;
  807. for (int i = 0; i < rects.Length; ++i)
  808. {
  809. rects[i].X -= offset;
  810. rects[i].Width += 2 * offset;
  811. }
  812. // Set the saved region
  813. using (PdnRegion reg = Utility.RectanglesToRegion(Utility.InflateRectangles(rects, 3)))
  814. {
  815. _saved = new IrregularSurface(_ra.Surface, reg);
  816. }
  817. // Draw the Lines
  818. _uls = localUls;
  819. for (int i = 0; i < _lines.Count; i++)
  820. {
  821. _threadPool.QueueUserWorkItem(RenderText, BoxedConstants.GetInt32(i));
  822. }
  823. _threadPool.Drain();
  824. // Draw the Cursor
  825. if (cursorOn)
  826. {
  827. using (var cursorPen = new Pen(Color.FromArgb(255, AppEnvironment.PrimaryColor.ToColor()), 2))
  828. {
  829. if (emptyCursorLineFlag)
  830. {
  831. _ra.Graphics.FillRectangle(cursorPen.Brush, cursorRect);
  832. }
  833. else
  834. {
  835. _ra.Graphics.DrawLine(cursorPen, new Point(cursorRect.Right, cursorRect.Top), new Point(cursorRect.Right, cursorRect.Bottom));
  836. }
  837. }
  838. }
  839. PlaceMoveNub();
  840. UpdateStatusText();
  841. ActiveLayer.Invalidate(_saved.Region);
  842. Update();
  843. }
  844. private string GetStatusBarXyText()
  845. {
  846. string unitsAbbreviationXy;
  847. string xString;
  848. string yString;
  849. Document.CoordinatesToStrings(AppWorkspace.Units, _uls[0].X, _uls[0].Y, out xString, out yString, out unitsAbbreviationXy);
  850. string statusBarText = string.Format(
  851. _statusBarTextFormat,
  852. xString,
  853. unitsAbbreviationXy,
  854. yString,
  855. unitsAbbreviationXy);
  856. return statusBarText;
  857. }
  858. // Only used when measuring via background threads
  859. private void MeasureText(object lineNumberObj)
  860. {
  861. var lineNumber = (int)lineNumberObj;
  862. _sizes[lineNumber] = StringSize((string)_lines[lineNumber]);
  863. }
  864. // Only used when rendering via background threads
  865. private Point[] _uls;
  866. private Size[] _sizes;
  867. private void RenderText(object lineNumberObj)
  868. {
  869. var lineNumber = (int)lineNumberObj;
  870. using (Brush brush = AppEnvironment.CreateBrush(false))
  871. {
  872. DrawText(_ra.Surface, _font, (string)_lines[lineNumber], _uls[lineNumber], _sizes[lineNumber], AppEnvironment.AntiAliasing, brush);
  873. }
  874. }
  875. private void PlaceMoveNub()
  876. {
  877. if (_uls == null || _uls.Length <= 0) return;
  878. Point pt = _uls[_uls.Length - 1];
  879. pt.X += _sizes[_uls.Length - 1].Width;
  880. pt.Y += _sizes[_uls.Length - 1].Height;
  881. pt.X += (int)(10.0 / DocumentWorkspace.ScaleFactor.Ratio);
  882. pt.Y += (int)(10.0 / DocumentWorkspace.ScaleFactor.Ratio);
  883. pt.X = (int)Math.Round(Math.Min(_ra.Surface.Width - _moveNub.Size.Width, pt.X));
  884. pt.X = (int)Math.Round(Math.Max(_moveNub.Size.Width, pt.X));
  885. pt.Y = (int)Math.Round(Math.Min(_ra.Surface.Height - _moveNub.Size.Height, pt.Y));
  886. pt.Y = (int)Math.Round(Math.Max(_moveNub.Size.Height, pt.Y));
  887. _moveNub.Location = pt;
  888. }
  889. protected override void OnKeyDown(KeyEventArgs e)
  890. {
  891. switch (e.KeyCode)
  892. {
  893. case Keys.Space:
  894. if (_mode != EditingMode.NotEditing)
  895. {
  896. // Prevent pan cursor from flicking to 'hand w/ the X' whenever use types a space in their text
  897. e.Handled = true;
  898. }
  899. break;
  900. case Keys.ControlKey:
  901. if (!_controlKeyDown)
  902. {
  903. _controlKeyDown = true;
  904. _controlKeyDownTime = DateTime.Now;
  905. }
  906. break;
  907. // Make sure these are not used to scroll the document around
  908. case Keys.Home | Keys.Shift:
  909. case Keys.Home:
  910. case Keys.End:
  911. case Keys.End | Keys.Shift:
  912. case Keys.Next | Keys.Shift:
  913. case Keys.Next:
  914. case Keys.Prior | Keys.Shift:
  915. case Keys.Prior:
  916. if (_mode != EditingMode.NotEditing)
  917. {
  918. OnKeyPress(e.KeyCode);
  919. e.Handled = true;
  920. }
  921. break;
  922. case Keys.Tab:
  923. if ((e.Modifiers & Keys.Control) == 0)
  924. {
  925. if (_mode != EditingMode.NotEditing)
  926. {
  927. OnKeyPress(e.KeyCode);
  928. e.Handled = true;
  929. }
  930. }
  931. break;
  932. case Keys.Back:
  933. case Keys.Delete:
  934. if (_mode != EditingMode.NotEditing)
  935. {
  936. OnKeyPress(e.KeyCode);
  937. e.Handled = true;
  938. }
  939. break;
  940. }
  941. // Ensure text is on screen when they are typing
  942. if (_mode != EditingMode.NotEditing)
  943. {
  944. Point p = Point.Truncate(TextPositionToPoint(new Position(_linePos, _textPos)));
  945. Rectangle bounds = Utility.RoundRectangle(DocumentWorkspace.VisibleDocumentRectangleF);
  946. bounds.Inflate(-_font.Height, -_font.Height);
  947. if (!bounds.Contains(p))
  948. {
  949. PointF newCenterPt = Utility.GetRectangleCenter((RectangleF)bounds);
  950. // horizontally off
  951. if (p.X > bounds.Right || p.Y < bounds.Left)
  952. {
  953. newCenterPt.X = p.X;
  954. }
  955. // vertically off
  956. if (p.Y > bounds.Bottom || p.Y < bounds.Top)
  957. {
  958. newCenterPt.Y = p.Y;
  959. }
  960. DocumentWorkspace.DocumentCenterPointF = newCenterPt;
  961. }
  962. }
  963. base.OnKeyDown (e);
  964. }
  965. protected override void OnKeyUp(KeyEventArgs e)
  966. {
  967. switch (e.KeyCode)
  968. {
  969. case Keys.ControlKey:
  970. TimeSpan heldDuration = (DateTime.Now - _controlKeyDownTime);
  971. // If the user taps Ctrl, then we should toggle the visiblity of the moveNub
  972. if (heldDuration < _controlKeyDownThreshold)
  973. {
  974. _enableNub = !_enableNub;
  975. }
  976. _controlKeyDown = false;
  977. break;
  978. }
  979. base.OnKeyUp(e);
  980. }
  981. protected override void OnKeyPress(KeyPressEventArgs e)
  982. {
  983. switch (e.KeyChar)
  984. {
  985. case (char)13: // Enter
  986. if (_tracking)
  987. {
  988. e.Handled = true;
  989. }
  990. break;
  991. case (char)27: // Escape
  992. if (_tracking)
  993. {
  994. e.Handled = true;
  995. }
  996. else
  997. {
  998. switch (_mode)
  999. {
  1000. case EditingMode.Editing:
  1001. SaveHistoryMemento();
  1002. break;
  1003. case EditingMode.EmptyEdit:
  1004. RedrawText(false);
  1005. break;
  1006. }
  1007. if (_mode != EditingMode.NotEditing)
  1008. {
  1009. e.Handled = true;
  1010. StopEditing();
  1011. }
  1012. }
  1013. break;
  1014. }
  1015. if (!e.Handled && _mode != EditingMode.NotEditing && !_tracking)
  1016. {
  1017. e.Handled = true;
  1018. if (_mode == EditingMode.EmptyEdit)
  1019. {
  1020. _mode = EditingMode.Editing;
  1021. var cha = new CompoundHistoryMemento(Name, Image, new List<HistoryMemento>());
  1022. _currentHa = cha;
  1023. HistoryStack.PushNewMemento(cha);
  1024. }
  1025. if (!char.IsControl(e.KeyChar))
  1026. {
  1027. InsertCharIntoString(e.KeyChar);
  1028. _textPos++;
  1029. RedrawText(true);
  1030. }
  1031. }
  1032. base.OnKeyPress (e);
  1033. }
  1034. protected override void OnKeyPress(Keys keyData)
  1035. {
  1036. bool keyHandled = true;
  1037. Keys key = keyData & Keys.KeyCode;
  1038. Keys modifier = keyData & Keys.Modifiers;
  1039. if (_tracking)
  1040. {
  1041. keyHandled = false;
  1042. }
  1043. else if (modifier == Keys.Alt)
  1044. {
  1045. // ignore so they can use Alt+#### to type special characters
  1046. }
  1047. else if (_mode != EditingMode.NotEditing)
  1048. {
  1049. switch (key)
  1050. {
  1051. case Keys.Back:
  1052. if (modifier == Keys.Control)
  1053. {
  1054. PerformControlBackspace();
  1055. }
  1056. else
  1057. {
  1058. PerformBackspace();
  1059. }
  1060. break;
  1061. case Keys.Delete:
  1062. if (modifier == Keys.Control)
  1063. {
  1064. PerformControlDelete();
  1065. }
  1066. else
  1067. {
  1068. PerformDelete();
  1069. }
  1070. break;
  1071. case Keys.Enter:
  1072. PerformEnter();
  1073. break;
  1074. case Keys.Left:
  1075. if (modifier == Keys.Control)
  1076. {
  1077. PerformControlLeft();
  1078. }
  1079. else
  1080. {
  1081. PerformLeft();
  1082. }
  1083. break;
  1084. case Keys.Right:
  1085. if (modifier == Keys.Control)
  1086. {
  1087. PerformControlRight();
  1088. }
  1089. else
  1090. {
  1091. PerformRight();
  1092. }
  1093. break;
  1094. case Keys.Up:
  1095. PerformUp();
  1096. break;
  1097. case Keys.Down:
  1098. PerformDown();
  1099. break;
  1100. case Keys.Home:
  1101. if (modifier == Keys.Control)
  1102. {
  1103. _linePos = 0;
  1104. }
  1105. _textPos = 0;
  1106. break;
  1107. case Keys.End:
  1108. if (modifier == Keys.Control)
  1109. {
  1110. _linePos = _lines.Count - 1;
  1111. }
  1112. _textPos = ((string)_lines[_linePos]).Length;
  1113. break;
  1114. default:
  1115. keyHandled = false;
  1116. break;
  1117. }
  1118. _startTime = DateTime.Now;
  1119. if (_mode != EditingMode.NotEditing && keyHandled)
  1120. {
  1121. RedrawText(true);
  1122. }
  1123. }
  1124. if (!keyHandled)
  1125. {
  1126. base.OnKeyPress(keyData);
  1127. }
  1128. }
  1129. private PointF TextPositionToPoint(Position p)
  1130. {
  1131. var pf = new PointF(0,0);
  1132. Size sz = StringSize(((string)_lines[p.Line]).Substring(0, p.Offset));
  1133. Size fullSz = StringSize((string)_lines[p.Line]);
  1134. switch (_alignment)
  1135. {
  1136. case TextAlignment.Left:
  1137. pf = new PointF(_clickPoint.X + sz.Width, _clickPoint.Y + (sz.Height * p.Line));
  1138. break;
  1139. case TextAlignment.Center:
  1140. pf = new PointF(_clickPoint.X + (sz.Width - (fullSz.Width/2)), _clickPoint.Y + (sz.Height * p.Line));
  1141. break;
  1142. case TextAlignment.Right:
  1143. pf = new PointF(_clickPoint.X + (sz.Width - fullSz.Width), _clickPoint.Y + (sz.Height * p.Line));
  1144. break;
  1145. default:
  1146. throw new InvalidEnumArgumentException("Invalid Alignment");
  1147. }
  1148. return pf;
  1149. }
  1150. private int FindOffsetPosition(float offset, string line, int lno)
  1151. {
  1152. for (int i = 0; i < line.Length; i++)
  1153. {
  1154. PointF pf = TextPositionToPoint(new Position(lno, i));
  1155. float dx = pf.X - _clickPoint.X;
  1156. if (dx >= offset)
  1157. {
  1158. return i;
  1159. }
  1160. }
  1161. return line.Length;
  1162. }
  1163. private Position PointToTextPosition(PointF pf)
  1164. {
  1165. float dx = pf.X - _clickPoint.X;
  1166. float dy = pf.Y - _clickPoint.Y;
  1167. var line = (int)Math.Floor(dy / _sizes[0].Height);
  1168. if (line < 0)
  1169. {
  1170. line = 0;
  1171. }
  1172. else if (line >= _lines.Count)
  1173. {
  1174. line = _lines.Count - 1;
  1175. }
  1176. int offset = FindOffsetPosition(dx, (string)_lines[line], line);
  1177. var p = new Position(line, offset);
  1178. if (p.Offset >= ((string)_lines[p.Line]).Length)
  1179. {
  1180. p.Offset = ((string)_lines[p.Line]).Length;
  1181. }
  1182. return p;
  1183. }
  1184. protected override void OnMouseMove(MouseEventArgs e)
  1185. {
  1186. if (_tracking)
  1187. {
  1188. var newMouseXy = new Point(e.X, e.Y);
  1189. var delta = new Size(newMouseXy.X - _startMouseXy.X, newMouseXy.Y - _startMouseXy.Y);
  1190. _clickPoint = new Point(_startClickPoint.X + delta.Width, _startClickPoint.Y + delta.Height);
  1191. RedrawText(false);
  1192. UpdateStatusText();
  1193. }
  1194. else
  1195. {
  1196. bool touchingNub = _moveNub.IsPointTouching(new Point(e.X, e.Y), false);
  1197. if (touchingNub && _moveNub.Visible)
  1198. {
  1199. Cursor = HandCursor;
  1200. }
  1201. else
  1202. {
  1203. Cursor = _textToolCursor;
  1204. }
  1205. }
  1206. base.OnMouseMove (e);
  1207. }
  1208. protected override void OnMouseUp(MouseEventArgs e)
  1209. {
  1210. if (_tracking)
  1211. {
  1212. OnMouseMove(e);
  1213. _tracking = false;
  1214. UpdateStatusText();
  1215. }
  1216. base.OnMouseUp (e);
  1217. }
  1218. protected override void OnMouseDown(MouseEventArgs e)
  1219. {
  1220. base.OnMouseDown (e);
  1221. bool touchingMoveNub = _moveNub.IsPointTouching(new Point(e.X, e.Y), false);
  1222. if (_mode != EditingMode.NotEditing && (e.Button == MouseButtons.Right || touchingMoveNub))
  1223. {
  1224. _tracking = true;
  1225. _startMouseXy = new Point(e.X, e.Y);
  1226. _startClickPoint = _clickPoint;
  1227. Cursor = HandCursorMouseDown;
  1228. UpdateStatusText();
  1229. }
  1230. else if (e.Button == MouseButtons.Left)
  1231. {
  1232. if (_saved != null)
  1233. {
  1234. Rectangle bounds = Utility.GetRegionBounds(_saved.Region);
  1235. bounds.Inflate(_font.Height, _font.Height);
  1236. if (_lines != null && bounds.Contains(e.X, e.Y))
  1237. {
  1238. Position p = PointToTextPosition(new PointF(e.X, e.Y + (_font.Height / 2)));
  1239. _linePos = p.Line;
  1240. _textPos = p.Offset;
  1241. RedrawText(true);
  1242. return;
  1243. }
  1244. }
  1245. switch (_mode)
  1246. {
  1247. case EditingMode.Editing:
  1248. SaveHistoryMemento();
  1249. StopEditing();
  1250. break;
  1251. case EditingMode.EmptyEdi

Large files files are truncated, but you can click here to view the full file