OSDN Git Service

MVVM対応に備えてシンボル系列のプロパティもDocumentに移行した
[fooeditengine/FooEditEngine.git] / WPF / FooEditEngine / WPF / WPFRender.cs
1 /*
2  * Copyright (C) 2013 FooProject
3  * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
5
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
9 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
10  */
11 using System;
12 using System.Collections.Generic;
13 using System.Linq;
14 using System.Windows;
15 using System.Windows.Controls;
16 using System.Windows.Media;
17 using System.Windows.Media.TextFormatting;
18
19 namespace FooEditEngine.WPF
20 {
21     sealed class WPFRender : IPrintableTextRender, IDisposable
22     {
23         FontFamily _Font;
24         double _FontSize, _LineHeight;
25         DrawingContext Context;
26         int _TabWidthChar;
27         double TabWidth;
28         bool _RightToLeft;
29         System.Windows.Media.Color ForegroundColor, BackgroundColor, HilightColor, Keyword1Color, Keyword2Color, LiteralColor, UrlColor, ControlCharColor, CommentColor, InsertCaretColor, OverwriteCaretColor, LineMarkerColor;
30         BrushStockes Brushes = new BrushStockes();
31         PenStockes Pens = new PenStockes();
32         VisualHost host;
33         DrawingVisual visual;
34
35         public WPFRender(FontFamily font, double fontSize)
36         {
37             this.ChangedRenderResource += (s, e) => { };
38             this.ChangedRightToLeft += (s, e) => { };
39             this.FontFamily = font;
40             this.FontSize = fontSize;
41         }
42
43         public WPFRender(FooTextBox textbox, double width, double height,FrameworkElement image)
44         {
45             this.ChangedRenderResource += (s, e) => { };
46             this.ChangedRightToLeft += (s, e) => { };
47             
48             this.FontFamily = textbox.FontFamily;
49             this.FontSize = textbox.FontSize;
50             this.ForegroundColor = textbox.Foreground;
51             this.BackgroundColor = textbox.Background;
52             this.ControlCharColor = textbox.ControlChar;
53             this.HilightColor = textbox.Hilight;
54             this.CommentColor = textbox.Comment;
55             this.UrlColor = textbox.URL;
56             this.Keyword1Color = textbox.Keyword1;
57             this.Keyword2Color = textbox.Keyword2;
58             this.LiteralColor = textbox.Literal;
59             this.InsertCaretColor = textbox.InsertCaret;
60             this.OverwriteCaretColor = textbox.OverwriteCaret;
61             this.LineMarkerColor = textbox.LineMarker;
62
63             this.host = (VisualHost)image;
64         }
65
66         public event EventHandler ChangedRightToLeft;
67
68         public TextAntialiasMode TextAntialiasMode
69         {
70             get;
71             set;
72         }
73
74         public bool ShowFullSpace
75         {
76             get;
77             set;
78         }
79
80         public bool ShowHalfSpace
81         {
82             get;
83             set;
84         }
85
86         public bool ShowTab
87         {
88             get;
89             set;
90         }
91
92         public bool RightToLeft
93         {
94             get { return this._RightToLeft; }
95             set
96             {
97                 this._RightToLeft = value;
98                 this.ChangedRightToLeft(this, null);
99             }
100         }
101
102         public bool InsertMode
103         {
104             get;
105             set;
106         }
107
108         public Rectangle TextArea
109         {
110             get;
111             set;
112         }
113
114         public FontFamily FontFamily
115         {
116             get
117             {
118                 return this._Font;
119             }
120             set
121             {
122                 this._Font = value;
123                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Font));
124             }
125         }
126
127         public double FontSize
128         {
129             get
130             {
131                 return this._FontSize;
132             }
133             set
134             {
135                 this._FontSize = value;
136
137                 TextLayout layout = new TextLayout("0", this.FontFamily, this.FontSize, Brushes[this.Foreground], 0);
138                 this.LineNemberWidth = layout.Lines[0].Width * EditView.LineNumberLength;
139                 this._LineHeight = layout.Lines[0].Height;
140                 this.emSize = new Size(layout.Lines[0].Width, layout.Lines[0].Height);
141                 layout.Dispose();
142                 
143                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Font));
144             }
145         }
146
147         public double LineNemberWidth
148         {
149             get;
150             private set;
151         }
152
153         public double FoldingWidth
154         {
155             get
156             {
157                 return 0;
158             }
159         }
160
161         public int TabWidthChar
162         {
163             get
164             {
165                 return this._TabWidthChar;
166             }
167             set
168             {
169                 TextLayout layout = new TextLayout("a", this.FontFamily, this.FontSize, this.Brushes[this.ForegroundColor], 0);
170                 double width = layout.Lines[0].WidthIncludingTrailingWhitespace;
171                 layout.Dispose();
172                 this.TabWidth = width * value;
173                 this._TabWidthChar = value;
174             }
175         }
176
177         public Size emSize
178         {
179             get;
180             private set;
181         }
182
183         public System.Windows.Media.Color Foreground
184         {
185             get
186             {
187                 return this.ForegroundColor;
188             }
189             set
190             {
191                 this.ForegroundColor = value;
192                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
193             }
194         }
195
196         public System.Windows.Media.Color Background
197         {
198             get
199             {
200                 return this.BackgroundColor;
201             }
202             set
203             {
204                 this.BackgroundColor = value;
205                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
206             }
207         }
208
209         public System.Windows.Media.Color InsertCaret
210         {
211             get
212             {
213                 return this.InsertCaretColor;
214             }
215             set
216             {
217                 this.InsertCaretColor = value;
218             }
219         }
220
221         public System.Windows.Media.Color OverwriteCaret
222         {
223             get
224             {
225                 return this.OverwriteCaretColor;
226             }
227             set
228             {
229                 this.OverwriteCaretColor = value;
230             }
231         }
232
233         public System.Windows.Media.Color LineMarker
234         {
235             get
236             {
237                 return this.LineMarkerColor;
238             }
239             set
240             {
241                 this.LineMarkerColor = value;
242             }
243         }
244
245         public System.Windows.Media.Color ControlChar
246         {
247             get
248             {
249                 return this.ControlCharColor;
250             }
251             set
252             {
253                 this.ControlCharColor = value;
254                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
255             }
256         }
257
258         public System.Windows.Media.Color Url
259         {
260             get
261             {
262                 return this.UrlColor;
263             }
264             set
265             {
266                 this.UrlColor = value;
267                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
268             }
269         }
270
271         public System.Windows.Media.Color Hilight
272         {
273             get
274             {
275                 return this.HilightColor;
276             }
277             set
278             {
279                 this.HilightColor = value;
280                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
281             }
282         }
283
284         public System.Windows.Media.Color Comment
285         {
286             get
287             {
288                 return this.CommentColor;
289             }
290             set
291             {
292                 this.CommentColor = value;
293                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
294             }
295         }
296
297         public System.Windows.Media.Color Literal
298         {
299             get
300             {
301                 return this.LiteralColor;
302             }
303             set
304             {
305                 this.LiteralColor = value;
306                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
307             }
308         }
309
310         public System.Windows.Media.Color Keyword1
311         {
312             get
313             {
314                 return this.Keyword1Color;
315             }
316             set
317             {
318                 this.Keyword1Color = value;
319                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
320             }
321         }
322
323         public System.Windows.Media.Color Keyword2
324         {
325             get
326             {
327                 return this.Keyword2Color;
328             }
329             set
330             {
331                 this.Keyword2Color = value;
332                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
333             }
334         }
335
336         public float HeaderHeight
337         {
338             get { return (float)this._LineHeight; }
339         }
340
341         public float FooterHeight
342         {
343             get { return (float)this._LineHeight; }
344         }
345
346         public bool Printing
347         {
348             get;
349             set;
350         }
351
352         public bool ShowLineBreak
353         {
354             get
355             {
356                 throw new NotImplementedException();
357             }
358
359             set
360             {
361                 throw new NotImplementedException();
362             }
363         }
364
365         public event ChangedRenderResourceEventHandler ChangedRenderResource;
366
367         public void DrawCachedBitmap(Rectangle rect)
368         {
369             return;
370         }
371
372         public void CacheContent()
373         {
374             return;
375         }
376
377         public bool IsVaildCache()
378         {
379             return false;
380         }
381
382         public void SetDrawingContext(DrawingContext dc)
383         {
384             this.Context = dc;
385         }
386
387         public void BegineDraw()
388         {
389             this.visual = new DrawingVisual();
390             this.Context = this.visual.RenderOpen();
391         }
392
393         public void EndDraw()
394         {
395             this.Context.Close();
396             this.host.AddVisual(this.visual);
397         }
398
399         public bool Resize(double width, double height)
400         {
401             return true;
402         }
403
404         public void DrawString(string str, double x, double y, StringAlignment align, Size layoutRect, StringColorType colorType = StringColorType.Forground)
405         {
406             TextAlignment TextAlign = TextAlignment.Left;
407             switch (align)
408             {
409                 case StringAlignment.Left:
410                     TextAlign = TextAlignment.Left;
411                     break;
412                 case StringAlignment.Center:
413                     TextAlign = TextAlignment.Center;
414                     break;
415                 case StringAlignment.Right:
416                     TextAlign = TextAlignment.Right;
417                     break;
418             }
419             TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.ForegroundColor], layoutRect.Width, TextAlign);
420             layout.FlowDirection = this.RightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
421             layout.Draw(this.Context, x, y);
422             layout.Dispose();
423         }
424
425         public void FillRectangle(Rectangle rect,FillRectType type)
426         {
427             if (this.Printing)
428                 return;
429             switch(type)
430             {
431                 case FillRectType.OverwriteCaret:
432                     this.Context.DrawRectangle(this.Brushes[this.OverwriteCaret], null, rect);
433                     break;
434                 case FillRectType.InsertCaret:
435                     this.Context.DrawRectangle(this.Brushes[this.InsertCaret], null, rect);
436                     break;
437                 case FillRectType.InsertPoint:
438                     break;
439                 case FillRectType.LineMarker:
440                     this.Context.DrawRectangle(this.Brushes[this.LineMarker], null, rect);
441                     break;
442             }
443         }
444
445         public void DrawFoldingMark(bool expand, double x, double y)
446         {
447             string str = expand ? "-" : "+";
448             TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.ForegroundColor], this.FoldingWidth);
449             layout.Draw(this.Context, x, y);
450         }
451
452         public void DrawLine(Point from, Point to)
453         {
454             Brush brush = this.Brushes[this.ForegroundColor];
455             Pen pen = this.Pens.Get(brush,HilightType.Sold);
456             this.Context.DrawLine(pen, from, to);
457         }
458
459         public void FillBackground(Rectangle rect)
460         {
461             if (this.Printing)
462                 return;
463             this.Context.DrawRectangle(this.Brushes[this.Background], null, rect);
464         }
465
466         public void DrawOneLine(LineToIndexTable lti, int row, double x, double y, IEnumerable<Selection> SelectRanges)
467         {
468             TextLayout layout = (TextLayout)lti.GetLayout(row);
469
470             if (lti.GetLengthFromLineNumber(row) == 0)
471                 return;
472
473             if (this.Printing == false)
474             {
475                 foreach (Selection sel in SelectRanges)
476                 {
477                     if (sel.length == 0 || sel.start == -1)
478                         continue;
479
480                     foreach (TextBounds bound in layout.GetTextBounds(sel.start, sel.length))
481                     {
482                         Rect rect = new Rect(x,y,bound.Rectangle.Width,bound.Rectangle.Height);
483                         this.Context.DrawRectangle(this.Brushes[this.Hilight], null, rect);
484                     }
485                 }
486             }
487
488             layout.Draw(this.Context, x, y);
489         }
490
491         public List<LineToIndexTableData> BreakLine(Document doc, LineToIndexTable layoutLineCollection, int startIndex, int endIndex, double wrapwidth)
492         {
493             List<LineToIndexTableData> output = new List<LineToIndexTableData>();
494
495             foreach (string str in doc.GetLines(startIndex, endIndex))
496             {
497                 TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.Foreground], wrapwidth);
498
499                 int i = startIndex;
500                 foreach (TextLine line in layout.Lines)
501                 {
502                     if (line.Length == 0 && line.NewlineLength == 0)
503                         continue;
504
505                     int length = line.Length;
506
507                     IList<TextSpan<TextRun>> runs = line.GetTextRunSpans();
508                     if (runs.Last().Value is TextEndOfParagraph)
509                         length--;
510
511                     bool lineend = false;
512                     if (line.NewlineLength > 0)
513                         lineend = true;
514
515                     output.Add(layoutLineCollection.CreateLineToIndexTableData(i, length, lineend, null));
516                     i += line.Length;
517                 }
518
519                 layout.Dispose();
520
521                 startIndex += str.Length;
522             }
523
524             if (output.Count > 0)
525                 output.Last().LineEnd = true;
526
527             return output;
528         }
529
530         public ITextLayout CreateLaytout(string str, SyntaxInfo[] syntaxCollection, IEnumerable<Marker> MarkerRanges)
531         {
532             TextLayout layout = new TextLayout(str,this.FontFamily,this.FontSize,Brushes[this.Foreground],this.TextArea.Width);
533             layout.TextWarpping = TextWrapping.NoWrap;
534             layout.FlowDirection = this.RightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
535
536             if (syntaxCollection != null)
537             {
538                 foreach (SyntaxInfo s in syntaxCollection)
539                 {
540                     Brush brush = this.Brushes[this.Foreground];
541                     switch (s.type)
542                     {
543                         case TokenType.Comment:
544                             brush = this.Brushes[this.Comment];
545                             break;
546                         case TokenType.Keyword1:
547                             brush = this.Brushes[this.Keyword1];
548                             break;
549                         case TokenType.Keyword2:
550                             brush = this.Brushes[this.Keyword2];
551                             break;
552                         case TokenType.Literal:
553                             brush = this.Brushes[this.Literal];
554                             break;
555                     }
556                     TextEffect effect = new TextEffect(null, brush, null, s.index, s.length);
557                     effect.Freeze();
558                     layout.SetTextEffect(effect);
559                 }
560             }
561
562             if (MarkerRanges != null)
563             {
564                 foreach (Marker m in MarkerRanges)
565                 {
566                     if (m.start == -1 || m.length == 0)
567                         continue;
568
569                     Brush brush;
570                     if (m.hilight == HilightType.Url)
571                     {
572                         brush = this.Brushes[this.Url];
573                         TextEffect effect = new TextEffect(null, brush, null, m.start, m.length);
574                         effect.Freeze();
575                         layout.SetTextEffect(effect);
576                     }
577                     else
578                     {
579                         System.Windows.Media.Color color = new System.Windows.Media.Color();
580                         color.A = m.color.A;
581                         color.R = m.color.R;
582                         color.G = m.color.G;
583                         color.B = m.color.B;
584                         brush = this.Brushes[color];
585                     }
586
587                     Pen pen = this.Pens.Get(brush,m.hilight);
588
589                     TextDecorationCollection collection = new TextDecorationCollection();
590                     TextDecoration decoration = new TextDecoration();
591                     decoration.Pen = pen;
592                     decoration.Location = TextDecorationLocation.Underline;
593                     decoration.Freeze();
594                     collection.Add(decoration);
595
596                     if (m.hilight == HilightType.Squiggle)
597                         layout.SetSquilleLine(m.start, m.length, collection);
598                     else
599                         layout.SetTextDecoration(m.start, m.length, collection);
600                 }
601             }
602
603             return layout;
604         }
605
606         public void DrawGripper(Point p, double radius)
607         {
608             //タッチには対応していないので実装する必要はない
609             throw new NotImplementedException();
610         }
611
612         public void Dispose()
613         {
614             this.Pens = null;
615             this.Brushes = null;
616         }
617     }
618
619     class BrushStockes
620     {
621         ResourceManager<System.Windows.Media.Color, Brush> collection = new ResourceManager<System.Windows.Media.Color, Brush>();
622         public Brush this[System.Windows.Media.Color c]
623         {
624             get
625             {
626                 Brush brush;
627                 if (this.collection.TryGetValue(c, out brush))
628                     return brush;
629                 brush = new SolidColorBrush(c);
630                 brush.Freeze();
631                 this.collection.Add(c, brush);
632                 return brush;
633             }
634         }
635     }
636
637     class PenStockes
638     {
639         ResourceManager<Brush, ResourceManager<HilightType, Pen>> cache = new ResourceManager<Brush, ResourceManager<HilightType, Pen>>();
640         public Pen Get(Brush brush, HilightType type)
641         {
642             ResourceManager<HilightType, Pen> hilights;
643             Pen effect;
644             if (this.cache.TryGetValue(brush, out hilights))
645             {
646                 if (hilights.TryGetValue(type, out effect))
647                     return effect;
648                 effect = CreatePen(brush,type);
649                 hilights.Add(type, effect);
650                 return effect;
651             }
652             effect = CreatePen(brush, type);
653             hilights = new ResourceManager<HilightType, Pen>();
654             hilights.Add(type, effect);
655             this.cache.Add(brush, hilights);
656             return effect;
657         }
658         Pen CreatePen(Brush brush, HilightType type)
659         {
660             Pen pen = new Pen(brush, 1.0f);
661             switch (type)
662             {
663                 case HilightType.Sold:
664                 case HilightType.Url:
665                 case HilightType.Squiggle:
666                     pen.DashStyle = DashStyles.Solid;
667                     break;
668                 case HilightType.Dash:
669                     pen.DashStyle = DashStyles.Dash;
670                     break;
671                 case HilightType.DashDot:
672                     pen.DashStyle = DashStyles.DashDot;
673                     break;
674                 case HilightType.DashDotDot:
675                     pen.DashStyle = DashStyles.DashDotDot;
676                     break;
677                 case HilightType.Dot:
678                     pen.DashStyle = DashStyles.Dot;
679                     break;
680             }
681             pen.Freeze();
682             return pen;
683         }
684     }
685 }