1 // This module implements the portability layer for the Qt port of Scintilla.
2 //
3 // Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
4 //
5 // This file is part of QScintilla.
6 //
7 // This file may be used under the terms of the GNU General Public License
8 // version 3.0 as published by the Free Software Foundation and appearing in
9 // the file LICENSE included in the packaging of this file.  Please review the
10 // following information to ensure the GNU General Public License version 3.0
11 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 //
13 // If you do not wish to use this file under the terms of the GPL version 3.0
14 // then you may purchase a commercial license.  For more information contact
15 // info@riverbankcomputing.com.
16 //
17 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 
20 
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 
25 #include <qapplication.h>
26 #include <qcursor.h>
27 #include <qdatetime.h>
28 #include <qdesktopwidget.h>
29 #include <qfont.h>
30 #include <qimage.h>
31 #include <qlibrary.h>
32 #include <qpainter.h>
33 #include <qpixmap.h>
34 #include <qpolygon.h>
35 #include <qstring.h>
36 #include <qtextlayout.h>
37 #include <qwidget.h>
38 
39 #include "Platform.h"
40 #include "XPM.h"
41 
42 #include "Qsci/qsciscintillabase.h"
43 #include "SciClasses.h"
44 
45 #include "FontQuality.h"
46 
47 
48 namespace Scintilla {
49 
50 // Type convertors.
PFont(FontID fid)51 static QFont *PFont(FontID fid)
52 {
53     return reinterpret_cast<QFont *>(fid);
54 }
55 
PWindow(WindowID wid)56 static QWidget *PWindow(WindowID wid)
57 {
58     return reinterpret_cast<QWidget *>(wid);
59 }
60 
PMenu(MenuID mid)61 static QsciSciPopup *PMenu(MenuID mid)
62 {
63     return reinterpret_cast<QsciSciPopup *>(mid);
64 }
65 
66 
67 // Font management.
Font()68 Font::Font() noexcept : fid(0)
69 {
70 }
71 
~Font()72 Font::~Font()
73 {
74 }
75 
Create(const FontParameters & fp)76 void Font::Create(const FontParameters &fp)
77 {
78     Release();
79 
80     QFont *f = new QFont();
81 
82     QFont::StyleStrategy strategy;
83 
84     switch (fp.extraFontFlag & SC_EFF_QUALITY_MASK)
85     {
86     case SC_EFF_QUALITY_NON_ANTIALIASED:
87         strategy = QFont::NoAntialias;
88         break;
89 
90     case SC_EFF_QUALITY_ANTIALIASED:
91         strategy = QFont::PreferAntialias;
92         break;
93 
94     default:
95         strategy = QFont::PreferDefault;
96     }
97 
98 #if defined(Q_OS_MAC) && QT_VERSION < 0x050000
99 #if QT_VERSION >= 0x040700
100     strategy = static_cast<QFont::StyleStrategy>(strategy | QFont::ForceIntegerMetrics);
101 #else
102 #warning "Correct handling of QFont metrics requires Qt v4.7.0 or later"
103 #endif
104 #endif
105 
106     f->setStyleStrategy(strategy);
107 
108     // If name of the font begins with a '-', assume, that it is an XLFD.
109     if (fp.faceName[0] == '-')
110     {
111         f->setRawName(fp.faceName);
112     }
113     else
114     {
115         f->setFamily(fp.faceName);
116         f->setPointSizeF(fp.size);
117 
118         // See if the Qt weight has been passed via the back door.   Otherwise
119         // map Scintilla weights to Qt weights ensuring that the SC_WEIGHT_*
120         // values get mapped to the correct QFont::Weight values.
121         int qt_weight;
122 
123         if (fp.weight < 0)
124             qt_weight = -fp.weight;
125         else if (fp.weight <= 200)
126             qt_weight = QFont::Light;
127         else if (fp.weight <= QsciScintillaBase::SC_WEIGHT_NORMAL)
128             qt_weight = QFont::Normal;
129         else if (fp.weight <= 600)
130             qt_weight = QFont::DemiBold;
131         else if (fp.weight <= 850)
132             qt_weight = QFont::Bold;
133         else
134             qt_weight = QFont::Black;
135 
136         f->setWeight(qt_weight);
137 
138         f->setItalic(fp.italic);
139     }
140 
141     fid = f;
142 }
143 
Release()144 void Font::Release()
145 {
146     if (fid)
147     {
148         delete PFont(fid);
149         fid = 0;
150     }
151 }
152 
153 
154 // A surface abstracts a place to draw.
155 class SurfaceImpl : public Surface
156 {
157 public:
158     SurfaceImpl();
159     virtual ~SurfaceImpl();
160 
161     void Init(WindowID wid);
162     void Init(SurfaceID sid, WindowID);
163     void Init(QPainter *p);
164     void InitPixMap(int width, int height, Surface *sid, WindowID wid);
165 
166     void Release();
Initialised()167     bool Initialised() {return painter;}
168     void PenColour(ColourDesired fore);
LogPixelsY()169     int LogPixelsY() {return pd->logicalDpiY();}
DeviceHeightFont(int points)170     int DeviceHeightFont(int points) {return points;}
171     void MoveTo(int x_,int y_);
172     void LineTo(int x_,int y_);
173     void Polygon(Point *pts, size_t npts, ColourDesired fore,
174             ColourDesired back);
175     void RectangleDraw(PRectangle rc, ColourDesired fore,
176             ColourDesired back);
177     void FillRectangle(PRectangle rc, ColourDesired back);
178     void FillRectangle(PRectangle rc, Surface &surfacePattern);
179     void RoundedRectangle(PRectangle rc, ColourDesired fore,
180             ColourDesired back);
181     void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill,
182             int alphaFill, ColourDesired outline, int alphaOutline,
183             int flags);
184     void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops,
185             GradientOptions options);
186     void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage);
187     void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back);
188     void Copy(PRectangle rc, Point from, Surface &surfaceSource);
189 
190     void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase,
191             const char *s, int len, ColourDesired fore, ColourDesired back);
192     void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase,
193             const char *s, int len, ColourDesired fore, ColourDesired back);
194     void DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase,
195             const char *s, int len, ColourDesired fore);
196     void MeasureWidths(Font &font_, const char *s, int len,
197             XYPOSITION *positions);
198     XYPOSITION WidthText(Font &font_, const char *s, int len);
199     XYPOSITION Ascent(Font &font_);
200     XYPOSITION Descent(Font &font_);
InternalLeading(Font & font_)201     XYPOSITION InternalLeading(Font &font_) {Q_UNUSED(font_); return 0;}
202     XYPOSITION Height(Font &font_);
203     XYPOSITION AverageCharWidth(Font &font_);
204 
205     void SetClip(PRectangle rc);
206     void FlushCachedState();
207 
SetUnicodeMode(bool unicodeMode_)208     void SetUnicodeMode(bool unicodeMode_) {unicodeMode = unicodeMode_;}
SetDBCSMode(int codePage)209     void SetDBCSMode(int codePage) {Q_UNUSED(codePage);}
210 
211     void DrawXPM(PRectangle rc, const XPM *xpm);
212 
213 private:
214     void drawRect(const PRectangle &rc);
215     void drawText(const PRectangle &rc, Font &font_, XYPOSITION ybase,
216             const char *s, int len, ColourDesired fore);
217     static QFont convertQFont(Font &font);
218     QFontMetricsF metrics(Font &font_);
219     QString convertText(const char *s, int len);
220     static QColor convertQColor(const ColourDesired &col,
221             unsigned alpha = 255);
222 
223     bool unicodeMode;
224     QPaintDevice *pd;
225     QPainter *painter;
226     bool my_resources;
227     int pen_x, pen_y;
228 };
229 
Allocate(int)230 Surface *Surface::Allocate(int)
231 {
232     return new SurfaceImpl;
233 }
234 
SurfaceImpl()235 SurfaceImpl::SurfaceImpl()
236     : unicodeMode(false), pd(0), painter(0), my_resources(false), pen_x(0),
237       pen_y(0)
238 {
239 }
240 
~SurfaceImpl()241 SurfaceImpl::~SurfaceImpl()
242 {
243     Release();
244 }
245 
Init(WindowID wid)246 void SurfaceImpl::Init(WindowID wid)
247 {
248     Release();
249 
250     pd = reinterpret_cast<QWidget *>(wid);
251 }
252 
Init(SurfaceID sid,WindowID)253 void SurfaceImpl::Init(SurfaceID sid, WindowID)
254 {
255     Release();
256 
257     // This method, and the SurfaceID type, is only used when printing.  As it
258     // is actually a void * we pass (when using SCI_FORMATRANGE) a pointer to a
259     // QPainter rather than a pointer to a SurfaceImpl as might be expected.
260     QPainter *p = reinterpret_cast<QPainter *>(sid);
261 
262     pd = p->device();
263     painter = p;
264 }
265 
Init(QPainter * p)266 void SurfaceImpl::Init(QPainter *p)
267 {
268     Release();
269 
270     pd = p->device();
271     painter = p;
272 }
273 
InitPixMap(int width,int height,Surface * sid,WindowID wid)274 void SurfaceImpl::InitPixMap(int width, int height, Surface *sid, WindowID wid)
275 {
276     Release();
277 
278 #if QT_VERSION >= 0x050100
279     int dpr = PWindow(wid)->devicePixelRatio();
280     QPixmap *pixmap = new QPixmap(width * dpr, height * dpr);
281     pixmap->setDevicePixelRatio(dpr);
282 #else
283     QPixmap *pixmap = new QPixmap(width, height);
284     Q_UNUSED(wid);
285 #endif
286 
287     pd = pixmap;
288 
289     painter = new QPainter(pd);
290     my_resources = true;
291 
292     SetUnicodeMode(static_cast<SurfaceImpl *>(sid)->unicodeMode);
293 }
294 
Release()295 void SurfaceImpl::Release()
296 {
297     if (my_resources)
298     {
299         if (painter)
300             delete painter;
301 
302         if (pd)
303             delete pd;
304 
305         my_resources = false;
306     }
307 
308     painter = 0;
309     pd = 0;
310 }
311 
MoveTo(int x_,int y_)312 void SurfaceImpl::MoveTo(int x_, int y_)
313 {
314     Q_ASSERT(painter);
315 
316     pen_x = x_;
317     pen_y = y_;
318 }
319 
LineTo(int x_,int y_)320 void SurfaceImpl::LineTo(int x_, int y_)
321 {
322     Q_ASSERT(painter);
323 
324     painter->drawLine(pen_x, pen_y, x_, y_);
325 
326     pen_x = x_;
327     pen_y = y_;
328 }
329 
PenColour(ColourDesired fore)330 void SurfaceImpl::PenColour(ColourDesired fore)
331 {
332     Q_ASSERT(painter);
333 
334     painter->setPen(convertQColor(fore));
335 }
336 
Polygon(Point * pts,size_t npts,ColourDesired fore,ColourDesired back)337 void SurfaceImpl::Polygon(Point *pts, size_t npts, ColourDesired fore,
338         ColourDesired back)
339 {
340     Q_ASSERT(painter);
341 
342     QPolygonF qpts(npts);
343 
344     for (size_t i = 0; i < npts; ++i)
345         qpts[i] = QPointF(pts[i].x, pts[i].y);
346 
347     painter->setPen(convertQColor(fore));
348     painter->setBrush(convertQColor(back));
349     painter->drawPolygon(qpts);
350 }
351 
RectangleDraw(PRectangle rc,ColourDesired fore,ColourDesired back)352 void SurfaceImpl::RectangleDraw(PRectangle rc, ColourDesired fore,
353         ColourDesired back)
354 {
355     Q_ASSERT(painter);
356 
357     painter->setPen(convertQColor(fore));
358     painter->setBrush(convertQColor(back));
359     drawRect(rc);
360 }
361 
FillRectangle(PRectangle rc,ColourDesired back)362 void SurfaceImpl::FillRectangle(PRectangle rc, ColourDesired back)
363 {
364     Q_ASSERT(painter);
365 
366     painter->setPen(Qt::NoPen);
367     painter->setBrush(convertQColor(back));
368     drawRect(rc);
369 }
370 
FillRectangle(PRectangle rc,Surface & surfacePattern)371 void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern)
372 {
373     Q_ASSERT(painter);
374 
375     SurfaceImpl &si = static_cast<SurfaceImpl &>(surfacePattern);
376     QPixmap *pm = static_cast<QPixmap *>(si.pd);
377 
378     if (pm)
379     {
380         QBrush brsh(Qt::black, *pm);
381 
382         painter->setPen(Qt::NoPen);
383         painter->setBrush(brsh);
384         drawRect(rc);
385     }
386     else
387     {
388         FillRectangle(rc, ColourDesired(0));
389     }
390 }
391 
RoundedRectangle(PRectangle rc,ColourDesired fore,ColourDesired back)392 void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourDesired fore,
393         ColourDesired back)
394 {
395     Q_ASSERT(painter);
396 
397     painter->setPen(convertQColor(fore));
398     painter->setBrush(convertQColor(back));
399     painter->drawRoundRect(
400             QRectF(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top));
401 }
402 
AlphaRectangle(PRectangle rc,int cornerSize,ColourDesired fill,int alphaFill,ColourDesired outline,int alphaOutline,int)403 void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize,
404         ColourDesired fill, int alphaFill, ColourDesired outline,
405         int alphaOutline, int)
406 {
407     Q_ASSERT(painter);
408 
409     QColor outline_colour = convertQColor(outline, alphaOutline);
410     QColor fill_colour = convertQColor(fill, alphaFill);
411 
412     // There was a report of Qt seeming to ignore the alpha value of the pen so
413     // so we disable the pen if the outline and fill colours are the same.
414     if (outline_colour == fill_colour)
415         painter->setPen(Qt::NoPen);
416     else
417         painter->setPen(outline_colour);
418 
419     painter->setBrush(fill_colour);
420 
421     const int radius = (cornerSize ? 25 : 0);
422 
423     painter->drawRoundRect(
424             QRectF(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top),
425             radius, radius);
426 }
427 
GradientRectangle(PRectangle rc,const std::vector<ColourStop> & stops,GradientOptions options)428 void SurfaceImpl::GradientRectangle(PRectangle rc,
429         const std::vector<ColourStop> &stops, GradientOptions options)
430 {
431     Q_ASSERT(painter);
432 
433     QLinearGradient gradient;
434 
435     switch (options)
436     {
437     case GradientOptions::leftToRight:
438         gradient = QLinearGradient(rc.left, rc.top, rc.right, rc.top);
439         break;
440 
441     case GradientOptions::topToBottom:
442     default:
443         gradient = QLinearGradient(rc.left, rc.top, rc.left, rc.bottom);
444     }
445 
446     gradient.setSpread(QGradient::RepeatSpread);
447 
448     for (const ColourStop &stop : stops)
449         gradient.setColorAt(stop.position,
450                 convertQColor(stop.colour, stop.colour.GetAlpha()));
451 
452     painter->fillRect(
453             QRectF(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top),
454             QBrush(gradient));
455 }
456 
drawRect(const PRectangle & rc)457 void SurfaceImpl::drawRect(const PRectangle &rc)
458 {
459     painter->drawRect(
460             QRectF(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top));
461 }
462 
Ellipse(PRectangle rc,ColourDesired fore,ColourDesired back)463 void SurfaceImpl::Ellipse(PRectangle rc, ColourDesired fore,
464         ColourDesired back)
465 {
466     Q_ASSERT(painter);
467 
468     painter->setPen(convertQColor(fore));
469     painter->setBrush(convertQColor(back));
470     painter->drawEllipse(
471             QRectF(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top));
472 }
473 
Copy(PRectangle rc,Point from,Surface & surfaceSource)474 void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource)
475 {
476     Q_ASSERT(painter);
477 
478     SurfaceImpl &si = static_cast<SurfaceImpl &>(surfaceSource);
479 
480     if (si.pd)
481     {
482         QPixmap *pm = static_cast<QPixmap *>(si.pd);
483         qreal x = from.x;
484         qreal y = from.y;
485         qreal width = rc.right - rc.left;
486         qreal height = rc.bottom - rc.top;
487 
488 #if QT_VERSION >= 0x050100
489         qreal dpr = pm->devicePixelRatio();
490 
491         x *= dpr;
492         y *= dpr;
493         width *= dpr;
494         height *= dpr;
495 #endif
496 
497         painter->drawPixmap(QPointF(rc.left, rc.top), *pm,
498                 QRectF(x, y, width, height));
499     }
500 }
501 
DrawTextNoClip(PRectangle rc,Font & font_,XYPOSITION ybase,const char * s,int len,ColourDesired fore,ColourDesired back)502 void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase,
503         const char *s, int len, ColourDesired fore, ColourDesired back)
504 {
505     Q_ASSERT(painter);
506 
507     FillRectangle(rc, back);
508     drawText(rc, font_, ybase, s, len, fore);
509 }
510 
DrawTextClipped(PRectangle rc,Font & font_,XYPOSITION ybase,const char * s,int len,ColourDesired fore,ColourDesired back)511 void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase,
512         const char *s, int len, ColourDesired fore, ColourDesired back)
513 {
514     Q_ASSERT(painter);
515 
516     SetClip(rc);
517     DrawTextNoClip(rc, font_, ybase, s, len, fore, back);
518     painter->setClipping(false);
519 }
520 
DrawTextTransparent(PRectangle rc,Font & font_,XYPOSITION ybase,const char * s,int len,ColourDesired fore)521 void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_,
522         XYPOSITION ybase, const char *s, int len, ColourDesired fore)
523 {
524     // Only draw if there is a non-space.
525     for (int i = 0; i < len; ++i)
526         if (s[i] != ' ')
527         {
528             drawText(rc, font_, ybase, s, len, fore);
529             return;
530         }
531 }
532 
drawText(const PRectangle & rc,Font & font_,XYPOSITION ybase,const char * s,int len,ColourDesired fore)533 void SurfaceImpl::drawText(const PRectangle &rc, Font &font_, XYPOSITION ybase,
534         const char *s, int len, ColourDesired fore)
535 {
536     QString qs = convertText(s, len);
537 
538     QFont *f = PFont(font_.GetID());
539 
540     if (f)
541         painter->setFont(*f);
542 
543     painter->setPen(convertQColor(fore));
544     painter->drawText(QPointF(rc.left, ybase), qs);
545 }
546 
DrawXPM(PRectangle rc,const XPM * xpm)547 void SurfaceImpl::DrawXPM(PRectangle rc, const XPM *xpm)
548 {
549     Q_ASSERT(painter);
550 
551     XYPOSITION x, y;
552     const QPixmap &qpm = xpm->Pixmap();
553 
554     x = rc.left + (rc.Width() - qpm.width()) / 2.0;
555     y = rc.top + (rc.Height() - qpm.height()) / 2.0;
556 
557     painter->drawPixmap(QPointF(x, y), qpm);
558 }
559 
DrawRGBAImage(PRectangle rc,int width,int height,const unsigned char * pixelsImage)560 void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height,
561         const unsigned char *pixelsImage)
562 {
563     Q_UNUSED(width);
564     Q_UNUSED(height);
565     Q_ASSERT(painter);
566 
567     const QImage *qim = reinterpret_cast<const QImage *>(pixelsImage);
568 
569     painter->drawImage(QPointF(rc.left, rc.top), *qim);
570 }
571 
MeasureWidths(Font & font_,const char * s,int len,XYPOSITION * positions)572 void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len,
573         XYPOSITION *positions)
574 {
575     QString qs = convertText(s, len);
576     QTextLayout text_layout(qs, convertQFont(font_), pd);
577 
578     text_layout.beginLayout();
579     QTextLine text_line = text_layout.createLine();
580     text_layout.endLayout();
581 
582     if (unicodeMode)
583     {
584         int i_char = 0, i_byte = 0;;
585 
586         while (i_char < qs.size())
587         {
588             unsigned char byte = s[i_byte];
589             int nbytes, code_units;
590 
591             // Work out character sizes by looking at the byte stream.
592             if (byte >= 0xf0)
593             {
594                 nbytes = 4;
595                 code_units = 2;
596             }
597             else
598             {
599                 if (byte >= 0xe0)
600                     nbytes = 3;
601                 else if (byte >= 0x80)
602                     nbytes = 2;
603                 else
604                     nbytes = 1;
605 
606                 code_units = 1;
607             }
608 
609             XYPOSITION position = text_line.cursorToX(i_char + code_units);
610 
611             // Set the same position for each byte of the character.
612             for (int i = 0; i < nbytes && i_byte < len; ++i)
613                 positions[i_byte++] = position;
614 
615             i_char += code_units;
616         }
617 
618         // This shouldn't be necessary...
619         XYPOSITION last_position = ((i_byte > 0) ? positions[i_byte - 1] : 0);
620 
621         while (i_byte < len)
622             positions[i_byte++] = last_position;
623     }
624     else
625     {
626         for (int i = 0; i < len; ++i)
627             positions[i] = text_line.cursorToX(i + 1);
628     }
629 }
630 
WidthText(Font & font_,const char * s,int len)631 XYPOSITION SurfaceImpl::WidthText(Font &font_, const char *s, int len)
632 {
633     return metrics(font_).width(convertText(s, len));
634 
635 }
636 
Ascent(Font & font_)637 XYPOSITION SurfaceImpl::Ascent(Font &font_)
638 {
639     return metrics(font_).ascent();
640 }
641 
Descent(Font & font_)642 XYPOSITION SurfaceImpl::Descent(Font &font_)
643 {
644     // Qt doesn't include the baseline in the descent, so add it.  Note that
645     // a descent from Qt4 always seems to be 2 pixels larger (irrespective of
646     // font or size) than the same descent from Qt3.  This means that text is a
647     // little more spaced out with Qt4 - and will be more noticeable with
648     // smaller fonts.
649     return metrics(font_).descent() + 1;
650 }
651 
Height(Font & font_)652 XYPOSITION SurfaceImpl::Height(Font &font_)
653 {
654     return metrics(font_).height();
655 }
656 
AverageCharWidth(Font & font_)657 XYPOSITION SurfaceImpl::AverageCharWidth(Font &font_)
658 {
659 #if QT_VERSION >= 0x040200
660     return metrics(font_).averageCharWidth();
661 #else
662     return metrics(font_).width('n');
663 #endif
664 }
665 
SetClip(PRectangle rc)666 void SurfaceImpl::SetClip(PRectangle rc)
667 {
668     Q_ASSERT(painter);
669 
670     painter->setClipRect(
671             QRectF(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top));
672 }
673 
FlushCachedState()674 void SurfaceImpl::FlushCachedState()
675 {
676 }
677 
678 // Return the QFont for a Font.
convertQFont(Font & font)679 QFont SurfaceImpl::convertQFont(Font &font)
680 {
681     QFont *f = PFont(font.GetID());
682 
683     if (f)
684         return *f;
685 
686     return QApplication::font();
687 }
688 
689 // Get the metrics for a font.
metrics(Font & font_)690 QFontMetricsF SurfaceImpl::metrics(Font &font_)
691 {
692     QFont fnt = convertQFont(font_);
693 
694     return QFontMetricsF(fnt, pd);
695 }
696 
697 // Convert a Scintilla string to a Qt Unicode string.
convertText(const char * s,int len)698 QString SurfaceImpl::convertText(const char *s, int len)
699 {
700     if (unicodeMode)
701         return QString::fromUtf8(s, len);
702 
703     return QString::fromLatin1(s, len);
704 }
705 
706 
707 // Convert a Scintilla colour, and alpha component, to a Qt QColor.
convertQColor(const ColourDesired & col,unsigned alpha)708 QColor SurfaceImpl::convertQColor(const ColourDesired &col, unsigned alpha)
709 {
710     int c = col.AsInteger();
711 
712     unsigned r = c & 0xff;
713     unsigned g = (c >> 8) & 0xff;
714     unsigned b = (c >> 16) & 0xff;
715 
716     return QColor(r, g, b, alpha);
717 }
718 
719 
720 // Window (widget) management.
~Window()721 Window::~Window()
722 {
723 }
724 
Destroy()725 void Window::Destroy()
726 {
727     QWidget *w = PWindow(wid);
728 
729     if (w)
730     {
731         // Delete the widget next time round the event loop rather than
732         // straight away.  This gets round a problem when auto-completion lists
733         // are cancelled after an entry has been double-clicked, ie. the list's
734         // dtor is called from one of the list's slots.  There are other ways
735         // around the problem but this is the simplest and doesn't seem to
736         // cause problems of its own.
737         w->deleteLater();
738         wid = 0;
739     }
740 }
741 
GetPosition() const742 PRectangle Window::GetPosition() const
743 {
744     QWidget *w = PWindow(wid);
745 
746     // Before any size allocated pretend its big enough not to be scrolled.
747     PRectangle rc(0,0,5000,5000);
748 
749     if (w)
750     {
751         const QRect &r = w->geometry();
752 
753         rc.right = r.right() - r.left() + 1;
754         rc.bottom = r.bottom() - r.top() + 1;
755     }
756 
757     return rc;
758 }
759 
SetPosition(PRectangle rc)760 void Window::SetPosition(PRectangle rc)
761 {
762     PWindow(wid)->setGeometry(rc.left, rc.top, rc.right - rc.left,
763             rc.bottom - rc.top);
764 }
765 
SetPositionRelative(PRectangle rc,const Window * relativeTo)766 void Window::SetPositionRelative(PRectangle rc, const Window *relativeTo)
767 {
768     QWidget *rel = PWindow(relativeTo->wid);
769     QPoint pos = rel->mapToGlobal(rel->pos());
770 
771     int x = pos.x() + rc.left;
772     int y = pos.y() + rc.top;
773 
774     PWindow(wid)->setGeometry(x, y, rc.right - rc.left, rc.bottom - rc.top);
775 }
776 
GetClientPosition() const777 PRectangle Window::GetClientPosition() const
778 {
779     return GetPosition();
780 }
781 
Show(bool show)782 void Window::Show(bool show)
783 {
784     QWidget *w = PWindow(wid);
785 
786     if (show)
787         w->show();
788     else
789         w->hide();
790 }
791 
InvalidateAll()792 void Window::InvalidateAll()
793 {
794     QWidget *w = PWindow(wid);
795 
796     if (w)
797         w->update();
798 }
799 
InvalidateRectangle(PRectangle rc)800 void Window::InvalidateRectangle(PRectangle rc)
801 {
802     QWidget *w = PWindow(wid);
803 
804     if (w)
805         w->update(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
806 }
807 
SetFont(Font & font)808 void Window::SetFont(Font &font)
809 {
810     PWindow(wid)->setFont(*PFont(font.GetID()));
811 }
812 
SetCursor(Cursor curs)813 void Window::SetCursor(Cursor curs)
814 {
815     Qt::CursorShape qc;
816 
817     switch (curs)
818     {
819     case cursorText:
820         qc = Qt::IBeamCursor;
821         break;
822 
823     case cursorUp:
824         qc = Qt::UpArrowCursor;
825         break;
826 
827     case cursorWait:
828         qc = Qt::WaitCursor;
829         break;
830 
831     case cursorHoriz:
832         qc = Qt::SizeHorCursor;
833         break;
834 
835     case cursorVert:
836         qc = Qt::SizeVerCursor;
837         break;
838 
839     case cursorHand:
840         qc = Qt::PointingHandCursor;
841         break;
842 
843     default:
844         // Note that Qt doesn't have a standard cursor that could be used to
845         // implement cursorReverseArrow.
846         qc = Qt::ArrowCursor;
847     }
848 
849     PWindow(wid)->setCursor(qc);
850 }
851 
GetMonitorRect(Point pt)852 PRectangle Window::GetMonitorRect(Point pt)
853 {
854     QPoint qpt = PWindow(wid)->mapToGlobal(QPoint(pt.x, pt.y));
855     QRect qr = QApplication::desktop()->availableGeometry(qpt);
856     qpt = PWindow(wid)->mapFromGlobal(qr.topLeft());
857 
858     return PRectangle(qpt.x(), qpt.y(), qpt.x() + qr.width(), qpt.y() + qr.height());
859 }
860 
861 
862 // Menu management.
Menu()863 Menu::Menu() noexcept : mid(0)
864 {
865 }
866 
CreatePopUp()867 void Menu::CreatePopUp()
868 {
869     Destroy();
870     mid = new QsciSciPopup();
871 }
872 
Destroy()873 void Menu::Destroy()
874 {
875     QsciSciPopup *m = PMenu(mid);
876 
877     if (m)
878     {
879         delete m;
880         mid = 0;
881     }
882 }
883 
Show(Point pt,Window &)884 void Menu::Show(Point pt, Window &)
885 {
886     PMenu(mid)->popup(QPoint(pt.x, pt.y));
887 }
888 
889 
890 class DynamicLibraryImpl : public DynamicLibrary
891 {
892 public:
DynamicLibraryImpl(const char * modulePath)893     DynamicLibraryImpl(const char *modulePath)
894     {
895         m = new QLibrary(modulePath);
896         m->load();
897     }
898 
~DynamicLibraryImpl()899     virtual ~DynamicLibraryImpl()
900     {
901         if (m)
902             delete m;
903     }
904 
FindFunction(const char * name)905     virtual Function FindFunction(const char *name)
906     {
907         if (m)
908             return (Function)m->resolve(name);
909 
910         return 0;
911     }
912 
IsValid()913     virtual bool IsValid()
914     {
915         return m && m->isLoaded();
916     }
917 
918 private:
919     QLibrary* m;
920 };
921 
Load(const char * modulePath)922 DynamicLibrary *DynamicLibrary::Load(const char *modulePath)
923 {
924     return new DynamicLibraryImpl(modulePath);
925 }
926 
927 
928 // Manage system wide parameters.
Chrome()929 ColourDesired Platform::Chrome()
930 {
931     return ColourDesired(0xe0,0xe0,0xe0);
932 }
933 
ChromeHighlight()934 ColourDesired Platform::ChromeHighlight()
935 {
936     return ColourDesired(0xff,0xff,0xff);
937 }
938 
DefaultFont()939 const char *Platform::DefaultFont()
940 {
941     static QByteArray def_font;
942 
943     def_font = QApplication::font().family().toLatin1();
944 
945     return def_font.constData();
946 }
947 
DefaultFontSize()948 int Platform::DefaultFontSize()
949 {
950     return QApplication::font().pointSize();
951 }
952 
DoubleClickTime()953 unsigned int Platform::DoubleClickTime()
954 {
955     return QApplication::doubleClickInterval();
956 }
957 
DebugDisplay(const char * s)958 void Platform::DebugDisplay(const char *s)
959 {
960     qDebug("%s", s);
961 }
962 
963 //#define TRACE
964 
965 #ifdef TRACE
DebugPrintf(const char * format,...)966 void Platform::DebugPrintf(const char *format, ...)
967 {
968     char buffer[2000];
969     va_list pArguments;
970 
971     va_start(pArguments, format);
972     vsprintf(buffer, format, pArguments);
973     va_end(pArguments);
974 
975     DebugDisplay(buffer);
976 }
977 #else
DebugPrintf(const char *,...)978 void Platform::DebugPrintf(const char *, ...)
979 {
980 }
981 #endif
982 
983 static bool assertionPopUps = true;
984 
ShowAssertionPopUps(bool assertionPopUps_)985 bool Platform::ShowAssertionPopUps(bool assertionPopUps_)
986 {
987     bool ret = assertionPopUps;
988 
989     assertionPopUps = assertionPopUps_;
990 
991     return ret;
992 }
993 
Assert(const char * c,const char * file,int line)994 void Platform::Assert(const char *c, const char *file, int line)
995 {
996     qFatal("Assertion [%s] failed at %s %d\n", c, file, line);
997 }
998 
999 }
1000