TextTool.cs 52 KB

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