OSDN Git Service

初コミット
[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 event ChangedRenderResourceEventHandler ChangedRenderResource;
353
354         public void DrawCachedBitmap(Rectangle rect)
355         {
356             return;
357         }
358
359         public void CacheContent()
360         {
361             return;
362         }
363
364         public bool IsVaildCache()
365         {
366             return false;
367         }
368
369         public void SetDrawingContext(DrawingContext dc)
370         {
371             this.Context = dc;
372         }
373
374         public void BegineDraw()
375         {
376             this.visual = new DrawingVisual();
377             this.Context = this.visual.RenderOpen();
378         }
379
380         public void EndDraw()
381         {
382             this.Context.Close();
383             this.host.AddVisual(this.visual);
384         }
385
386         public bool Resize(double width, double height)
387         {
388             return true;
389         }
390
391         public void DrawString(string str, double x, double y, StringAlignment align, Size layoutRect)
392         {
393             TextAlignment TextAlign = TextAlignment.Left;
394             switch (align)
395             {
396                 case StringAlignment.Left:
397                     TextAlign = TextAlignment.Left;
398                     break;
399                 case StringAlignment.Center:
400                     TextAlign = TextAlignment.Center;
401                     break;
402                 case StringAlignment.Right:
403                     TextAlign = TextAlignment.Right;
404                     break;
405             }
406             TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.ForegroundColor], layoutRect.Width, TextAlign);
407             layout.FlowDirection = this.RightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
408             layout.Draw(this.Context, x, y);
409             layout.Dispose();
410         }
411
412         public void FillRectangle(Rectangle rect,FillRectType type)
413         {
414             if (this.Printing)
415                 return;
416             switch(type)
417             {
418                 case FillRectType.OverwriteCaret:
419                     this.Context.DrawRectangle(this.Brushes[this.OverwriteCaret], null, rect);
420                     break;
421                 case FillRectType.InsertCaret:
422                     this.Context.DrawRectangle(this.Brushes[this.InsertCaret], null, rect);
423                     break;
424                 case FillRectType.InsertPoint:
425                     break;
426                 case FillRectType.LineMarker:
427                     this.Context.DrawRectangle(this.Brushes[this.LineMarker], null, rect);
428                     break;
429             }
430         }
431
432         public void DrawFoldingMark(bool expand, double x, double y)
433         {
434             string str = expand ? "-" : "+";
435             TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.ForegroundColor], this.FoldingWidth);
436             layout.Draw(this.Context, x, y);
437         }
438
439         public void DrawLine(Point from, Point to)
440         {
441             Brush brush = this.Brushes[this.ForegroundColor];
442             Pen pen = this.Pens.Get(brush,HilightType.Sold);
443             this.Context.DrawLine(pen, from, to);
444         }
445
446         public void FillBackground(Rectangle rect)
447         {
448             if (this.Printing)
449                 return;
450             this.Context.DrawRectangle(this.Brushes[this.Background], null, rect);
451         }
452
453         public void DrawOneLine(LineToIndexTable lti, int row, double x, double y, IEnumerable<Selection> SelectRanges)
454         {
455             TextLayout layout = (TextLayout)lti.GetData(row).Layout;
456             LineToIndexTableData lineData = lti.GetData(row);
457
458             if (lineData.Length == 0)
459                 return;
460
461             if (this.Printing == false)
462             {
463                 foreach (Selection sel in SelectRanges)
464                 {
465                     if (sel.length == 0 || sel.start == -1)
466                         continue;
467
468                     foreach (TextBounds bound in layout.GetTextBounds(sel.start, sel.length))
469                     {
470                         Rect rect = new Rect(x,y,bound.Rectangle.Width,bound.Rectangle.Height);
471                         this.Context.DrawRectangle(this.Brushes[this.Hilight], null, rect);
472                     }
473                 }
474             }
475
476             layout.Draw(this.Context, x, y);
477         }
478
479         public List<LineToIndexTableData> BreakLine(Document doc, int startIndex, int endIndex, double wrapwidth)
480         {
481             List<LineToIndexTableData> output = new List<LineToIndexTableData>();
482
483             foreach (string str in doc.GetLines(startIndex, endIndex))
484             {
485                 TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.Foreground], wrapwidth);
486
487                 int i = startIndex;
488                 foreach (TextLine line in layout.Lines)
489                 {
490                     if (line.Length == 0 && line.NewlineLength == 0)
491                         continue;
492
493                     int length = line.Length;
494
495                     IList<TextSpan<TextRun>> runs = line.GetTextRunSpans();
496                     if (runs.Last().Value is TextEndOfParagraph)
497                         length--;
498
499                     bool lineend = false;
500                     if (line.NewlineLength > 0)
501                         lineend = true;
502
503                     output.Add(new LineToIndexTableData(i, length, lineend, null));
504                     i += line.Length;
505                 }
506
507                 layout.Dispose();
508
509                 startIndex += str.Length;
510             }
511
512             if (output.Count > 0)
513                 output.Last().LineEnd = true;
514
515             return output;
516         }
517
518         public ITextLayout CreateLaytout(string str, SyntaxInfo[] syntaxCollection, IEnumerable<Marker> MarkerRanges)
519         {
520             TextLayout layout = new TextLayout(str,this.FontFamily,this.FontSize,Brushes[this.Foreground],this.TextArea.Width);
521             layout.TextWarpping = TextWrapping.NoWrap;
522             layout.FlowDirection = this.RightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
523
524             if (syntaxCollection != null)
525             {
526                 foreach (SyntaxInfo s in syntaxCollection)
527                 {
528                     Brush brush = this.Brushes[this.Foreground];
529                     switch (s.type)
530                     {
531                         case TokenType.Comment:
532                             brush = this.Brushes[this.Comment];
533                             break;
534                         case TokenType.Keyword1:
535                             brush = this.Brushes[this.Keyword1];
536                             break;
537                         case TokenType.Keyword2:
538                             brush = this.Brushes[this.Keyword2];
539                             break;
540                         case TokenType.Literal:
541                             brush = this.Brushes[this.Literal];
542                             break;
543                     }
544                     TextEffect effect = new TextEffect(null, brush, null, s.index, s.length);
545                     effect.Freeze();
546                     layout.SetTextEffect(effect);
547                 }
548             }
549
550             if (MarkerRanges != null)
551             {
552                 foreach (Marker m in MarkerRanges)
553                 {
554                     if (m.start == -1 || m.length == 0)
555                         continue;
556
557                     Brush brush;
558                     if (m.hilight == HilightType.Url)
559                     {
560                         brush = this.Brushes[this.Url];
561                         TextEffect effect = new TextEffect(null, brush, null, m.start, m.length);
562                         effect.Freeze();
563                         layout.SetTextEffect(effect);
564                     }
565                     else
566                     {
567                         System.Windows.Media.Color color = new System.Windows.Media.Color();
568                         color.A = m.A;
569                         color.R = m.R;
570                         color.G = m.G;
571                         color.B = m.B;
572                         brush = this.Brushes[color];
573                     }
574
575                     Pen pen = this.Pens.Get(brush,m.hilight);
576
577                     TextDecorationCollection collection = new TextDecorationCollection();
578                     TextDecoration decoration = new TextDecoration();
579                     decoration.Pen = pen;
580                     decoration.Location = TextDecorationLocation.Underline;
581                     decoration.Freeze();
582                     collection.Add(decoration);
583
584                     if (m.hilight == HilightType.Squiggle)
585                         layout.SetSquilleLine(m.start, m.length, collection);
586                     else
587                         layout.SetTextDecoration(m.start, m.length, collection);
588                 }
589             }
590
591             return layout;
592         }
593
594         public void Dispose()
595         {
596             this.Pens = null;
597             this.Brushes = null;
598         }
599     }
600
601     class BrushStockes
602     {
603         ResourceManager<System.Windows.Media.Color, Brush> collection = new ResourceManager<System.Windows.Media.Color, Brush>();
604         public Brush this[System.Windows.Media.Color c]
605         {
606             get
607             {
608                 Brush brush;
609                 if (this.collection.TryGetValue(c, out brush))
610                     return brush;
611                 brush = new SolidColorBrush(c);
612                 brush.Freeze();
613                 this.collection.Add(c, brush);
614                 return brush;
615             }
616         }
617     }
618
619     class PenStockes
620     {
621         ResourceManager<Brush, ResourceManager<HilightType, Pen>> cache = new ResourceManager<Brush, ResourceManager<HilightType, Pen>>();
622         public Pen Get(Brush brush, HilightType type)
623         {
624             ResourceManager<HilightType, Pen> hilights;
625             Pen effect;
626             if (this.cache.TryGetValue(brush, out hilights))
627             {
628                 if (hilights.TryGetValue(type, out effect))
629                     return effect;
630                 effect = CreatePen(brush,type);
631                 hilights.Add(type, effect);
632                 return effect;
633             }
634             effect = CreatePen(brush, type);
635             hilights = new ResourceManager<HilightType, Pen>();
636             hilights.Add(type, effect);
637             this.cache.Add(brush, hilights);
638             return effect;
639         }
640         Pen CreatePen(Brush brush, HilightType type)
641         {
642             Pen pen = new Pen(brush, 1.0f);
643             switch (type)
644             {
645                 case HilightType.Sold:
646                 case HilightType.Url:
647                 case HilightType.Squiggle:
648                     pen.DashStyle = DashStyles.Solid;
649                     break;
650                 case HilightType.Dash:
651                     pen.DashStyle = DashStyles.Dash;
652                     break;
653                 case HilightType.DashDot:
654                     pen.DashStyle = DashStyles.DashDot;
655                     break;
656                 case HilightType.DashDotDot:
657                     pen.DashStyle = DashStyles.DashDotDot;
658                     break;
659                 case HilightType.Dot:
660                     pen.DashStyle = DashStyles.Dot;
661                     break;
662             }
663             pen.Freeze();
664             return pen;
665         }
666     }
667 }