OSDN Git Service

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