1 | // @file PlatQt.cpp |
2 | // Copyright (c) 1990-2011, Scientific Toolworks, Inc. |
3 | // |
4 | // The License.txt file describes the conditions under which this software may be distributed. |
5 | // |
6 | // Author: Jason Haslam |
7 | // |
8 | // Additions Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware |
9 | // Scintilla platform layer for Qt |
10 | |
11 | #include <cstdio> |
12 | |
13 | #include "PlatQt.h" |
14 | #include "Scintilla.h" |
15 | #include "UniConversion.h" |
16 | #include "DBCS.h" |
17 | |
18 | #include <QApplication> |
19 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) |
20 | #include <QScreen> |
21 | #endif |
22 | #include <QFont> |
23 | #include <QColor> |
24 | #include <QRect> |
25 | #include <QPaintDevice> |
26 | #include <QPaintEngine> |
27 | #include <QWidget> |
28 | #include <QPixmap> |
29 | #include <QPainter> |
30 | #include <QPainterPath> |
31 | #include <QMenu> |
32 | #include <QAction> |
33 | #include <QTime> |
34 | #include <QMessageBox> |
35 | #include <QTextCodec> |
36 | #include <QListWidget> |
37 | #include <QVarLengthArray> |
38 | #include <QScrollBar> |
39 | #include <QDesktopWidget> |
40 | #include <QTextLayout> |
41 | #include <QTextLine> |
42 | #include <QLibrary> |
43 | |
44 | using namespace Scintilla; |
45 | |
46 | namespace Scintilla::Internal { |
47 | |
48 | //---------------------------------------------------------------------- |
49 | |
50 | // Convert from a Scintilla characterSet value to a Qt codec name. |
51 | const char *CharacterSetID(CharacterSet characterSet) |
52 | { |
53 | switch (characterSet) { |
54 | //case CharacterSet::Ansi: |
55 | // return ""; |
56 | case CharacterSet::Default: |
57 | return "ISO 8859-1" ; |
58 | case CharacterSet::Baltic: |
59 | return "ISO 8859-13" ; |
60 | case CharacterSet::ChineseBig5: |
61 | return "Big5" ; |
62 | case CharacterSet::EastEurope: |
63 | return "ISO 8859-2" ; |
64 | case CharacterSet::GB2312: |
65 | return "GB18030-0" ; |
66 | case CharacterSet::Greek: |
67 | return "ISO 8859-7" ; |
68 | case CharacterSet::Hangul: |
69 | return "CP949" ; |
70 | case CharacterSet::Mac: |
71 | return "Apple Roman" ; |
72 | //case SC_CHARSET_OEM: |
73 | // return "ASCII"; |
74 | case CharacterSet::Russian: |
75 | return "KOI8-R" ; |
76 | case CharacterSet::Cyrillic: |
77 | return "Windows-1251" ; |
78 | case CharacterSet::ShiftJis: |
79 | return "Shift-JIS" ; |
80 | //case SC_CHARSET_SYMBOL: |
81 | // return ""; |
82 | case CharacterSet::Turkish: |
83 | return "ISO 8859-9" ; |
84 | //case SC_CHARSET_JOHAB: |
85 | // return "CP1361"; |
86 | case CharacterSet::Hebrew: |
87 | return "ISO 8859-8" ; |
88 | case CharacterSet::Arabic: |
89 | return "ISO 8859-6" ; |
90 | case CharacterSet::Vietnamese: |
91 | return "Windows-1258" ; |
92 | case CharacterSet::Thai: |
93 | return "TIS-620" ; |
94 | case CharacterSet::Iso8859_15: |
95 | return "ISO 8859-15" ; |
96 | default: |
97 | return "ISO 8859-1" ; |
98 | } |
99 | } |
100 | |
101 | QString UnicodeFromText(QTextCodec *codec, std::string_view text) { |
102 | return codec->toUnicode(text.data(), static_cast<int>(text.length())); |
103 | } |
104 | |
105 | static QFont::StyleStrategy ChooseStrategy(FontQuality eff) |
106 | { |
107 | switch (eff) { |
108 | case FontQuality::QualityDefault: return QFont::PreferDefault; |
109 | case FontQuality::QualityNonAntialiased: return QFont::NoAntialias; |
110 | case FontQuality::QualityAntialiased: return QFont::PreferAntialias; |
111 | case FontQuality::QualityLcdOptimized: return QFont::PreferAntialias; |
112 | default: return QFont::PreferDefault; |
113 | } |
114 | } |
115 | |
116 | class FontAndCharacterSet : public Font { |
117 | public: |
118 | CharacterSet characterSet = CharacterSet::Ansi; |
119 | std::unique_ptr<QFont> pfont; |
120 | explicit FontAndCharacterSet(const FontParameters &fp) : characterSet(fp.characterSet) { |
121 | pfont = std::make_unique<QFont>(); |
122 | pfont->setStyleStrategy(ChooseStrategy(fp.extraFontFlag)); |
123 | pfont->setFamily(QString::fromUtf8(fp.faceName)); |
124 | pfont->setPointSizeF(fp.size); |
125 | pfont->setBold(static_cast<int>(fp.weight) > 500); |
126 | pfont->setItalic(fp.italic); |
127 | } |
128 | }; |
129 | |
130 | namespace { |
131 | |
132 | const Supports SupportsQt[] = { |
133 | Supports::LineDrawsFinal, |
134 | Supports::FractionalStrokeWidth, |
135 | Supports::TranslucentStroke, |
136 | Supports::PixelModification, |
137 | }; |
138 | |
139 | const FontAndCharacterSet *AsFontAndCharacterSet(const Font *f) { |
140 | return dynamic_cast<const FontAndCharacterSet *>(f); |
141 | } |
142 | |
143 | QFont *FontPointer(const Font *f) |
144 | { |
145 | return AsFontAndCharacterSet(f)->pfont.get(); |
146 | } |
147 | |
148 | } |
149 | |
150 | std::shared_ptr<Font> Font::Allocate(const FontParameters &fp) |
151 | { |
152 | return std::make_shared<FontAndCharacterSet>(fp); |
153 | } |
154 | |
155 | SurfaceImpl::SurfaceImpl() = default; |
156 | |
157 | SurfaceImpl::SurfaceImpl(int width, int height, SurfaceMode mode_) |
158 | { |
159 | if (width < 1) width = 1; |
160 | if (height < 1) height = 1; |
161 | deviceOwned = true; |
162 | device = new QPixmap(width, height); |
163 | mode = mode_; |
164 | } |
165 | |
166 | SurfaceImpl::~SurfaceImpl() |
167 | { |
168 | Clear(); |
169 | } |
170 | |
171 | void SurfaceImpl::Clear() |
172 | { |
173 | if (painterOwned && painter) { |
174 | delete painter; |
175 | } |
176 | |
177 | if (deviceOwned && device) { |
178 | delete device; |
179 | } |
180 | device = nullptr; |
181 | painter = nullptr; |
182 | deviceOwned = false; |
183 | painterOwned = false; |
184 | } |
185 | |
186 | void SurfaceImpl::Init(WindowID wid) |
187 | { |
188 | Release(); |
189 | device = static_cast<QWidget *>(wid); |
190 | } |
191 | |
192 | void SurfaceImpl::Init(SurfaceID sid, WindowID /*wid*/) |
193 | { |
194 | Release(); |
195 | device = static_cast<QPaintDevice *>(sid); |
196 | } |
197 | |
198 | std::unique_ptr<Surface> SurfaceImpl::AllocatePixMap(int width, int height) |
199 | { |
200 | return std::make_unique<SurfaceImpl>(width, height, mode); |
201 | } |
202 | |
203 | void SurfaceImpl::SetMode(SurfaceMode mode_) |
204 | { |
205 | mode = mode_; |
206 | } |
207 | |
208 | void SurfaceImpl::Release() noexcept |
209 | { |
210 | Clear(); |
211 | } |
212 | |
213 | int SurfaceImpl::SupportsFeature(Supports feature) noexcept |
214 | { |
215 | for (const Supports f : SupportsQt) { |
216 | if (f == feature) |
217 | return 1; |
218 | } |
219 | return 0; |
220 | } |
221 | |
222 | bool SurfaceImpl::Initialised() |
223 | { |
224 | return device != nullptr; |
225 | } |
226 | |
227 | void SurfaceImpl::PenColour(ColourRGBA fore) |
228 | { |
229 | QPen penOutline(QColorFromColourRGBA(fore)); |
230 | penOutline.setCapStyle(Qt::FlatCap); |
231 | GetPainter()->setPen(penOutline); |
232 | } |
233 | |
234 | void SurfaceImpl::PenColourWidth(ColourRGBA fore, XYPOSITION strokeWidth) { |
235 | QPen penOutline(QColorFromColourRGBA(fore)); |
236 | penOutline.setCapStyle(Qt::FlatCap); |
237 | penOutline.setJoinStyle(Qt::MiterJoin); |
238 | penOutline.setWidthF(strokeWidth); |
239 | GetPainter()->setPen(penOutline); |
240 | } |
241 | |
242 | void SurfaceImpl::BrushColour(ColourRGBA back) |
243 | { |
244 | GetPainter()->setBrush(QBrush(QColorFromColourRGBA(back))); |
245 | } |
246 | |
247 | void SurfaceImpl::SetCodec(const Font *font) |
248 | { |
249 | const FontAndCharacterSet *pfacs = AsFontAndCharacterSet(font); |
250 | if (pfacs && pfacs->pfont) { |
251 | const char *csid = "UTF-8" ; |
252 | if (!(mode.codePage == SC_CP_UTF8)) |
253 | csid = CharacterSetID(pfacs->characterSet); |
254 | if (csid != codecName) { |
255 | codecName = csid; |
256 | codec = QTextCodec::codecForName(csid); |
257 | } |
258 | } |
259 | } |
260 | |
261 | void SurfaceImpl::SetFont(const Font *font) |
262 | { |
263 | const FontAndCharacterSet *pfacs = AsFontAndCharacterSet(font); |
264 | if (pfacs && pfacs->pfont) { |
265 | GetPainter()->setFont(*(pfacs->pfont)); |
266 | SetCodec(font); |
267 | } |
268 | } |
269 | |
270 | int SurfaceImpl::LogPixelsY() |
271 | { |
272 | return device->logicalDpiY(); |
273 | } |
274 | |
275 | int SurfaceImpl::PixelDivisions() |
276 | { |
277 | // Qt uses device pixels. |
278 | return 1; |
279 | } |
280 | |
281 | int SurfaceImpl::DeviceHeightFont(int points) |
282 | { |
283 | return points; |
284 | } |
285 | |
286 | void SurfaceImpl::LineDraw(Point start, Point end, Stroke stroke) |
287 | { |
288 | PenColourWidth(stroke.colour, stroke.width); |
289 | QLineF line(start.x, start.y, end.x, end.y); |
290 | GetPainter()->drawLine(line); |
291 | } |
292 | |
293 | void SurfaceImpl::PolyLine(const Point *pts, size_t npts, Stroke stroke) |
294 | { |
295 | // TODO: set line joins and caps |
296 | PenColourWidth(stroke.colour, stroke.width); |
297 | std::vector<QPointF> qpts; |
298 | std::transform(pts, pts + npts, std::back_inserter(qpts), QPointFFromPoint); |
299 | GetPainter()->drawPolyline(&qpts[0], static_cast<int>(npts)); |
300 | } |
301 | |
302 | void SurfaceImpl::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) |
303 | { |
304 | PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width); |
305 | BrushColour(fillStroke.fill.colour); |
306 | |
307 | std::vector<QPointF> qpts; |
308 | std::transform(pts, pts + npts, std::back_inserter(qpts), QPointFFromPoint); |
309 | |
310 | GetPainter()->drawPolygon(&qpts[0], static_cast<int>(npts)); |
311 | } |
312 | |
313 | void SurfaceImpl::RectangleDraw(PRectangle rc, FillStroke fillStroke) |
314 | { |
315 | PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width); |
316 | BrushColour(fillStroke.fill.colour); |
317 | const QRectF rect = QRectFFromPRect(rc.Inset(fillStroke.stroke.width / 2)); |
318 | GetPainter()->drawRect(rect); |
319 | } |
320 | |
321 | void SurfaceImpl::RectangleFrame(PRectangle rc, Stroke stroke) { |
322 | PenColourWidth(stroke.colour, stroke.width); |
323 | // Default QBrush is Qt::NoBrush so does not fill |
324 | GetPainter()->setBrush(QBrush()); |
325 | const QRectF rect = QRectFFromPRect(rc.Inset(stroke.width / 2)); |
326 | GetPainter()->drawRect(rect); |
327 | } |
328 | |
329 | void SurfaceImpl::FillRectangle(PRectangle rc, Fill fill) |
330 | { |
331 | GetPainter()->fillRect(QRectFFromPRect(rc), QColorFromColourRGBA(fill.colour)); |
332 | } |
333 | |
334 | void SurfaceImpl::FillRectangleAligned(PRectangle rc, Fill fill) |
335 | { |
336 | FillRectangle(PixelAlign(rc, 1), fill); |
337 | } |
338 | |
339 | void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) |
340 | { |
341 | // Tile pattern over rectangle |
342 | SurfaceImpl *surface = dynamic_cast<SurfaceImpl *>(&surfacePattern); |
343 | // Currently assumes 8x8 pattern |
344 | int widthPat = 8; |
345 | int heightPat = 8; |
346 | for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) { |
347 | int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat; |
348 | for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) { |
349 | int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat; |
350 | QRect source(0, 0, widthx, heighty); |
351 | QRect target(xTile, yTile, widthx, heighty); |
352 | QPixmap *pixmap = static_cast<QPixmap *>(surface->GetPaintDevice()); |
353 | GetPainter()->drawPixmap(target, *pixmap, source); |
354 | } |
355 | } |
356 | } |
357 | |
358 | void SurfaceImpl::RoundedRectangle(PRectangle rc, FillStroke fillStroke) |
359 | { |
360 | PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width); |
361 | BrushColour(fillStroke.fill.colour); |
362 | GetPainter()->drawRoundedRect(QRectFFromPRect(rc), 3.0f, 3.0f); |
363 | } |
364 | |
365 | void SurfaceImpl::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) |
366 | { |
367 | QColor qFill = QColorFromColourRGBA(fillStroke.fill.colour); |
368 | QBrush brushFill(qFill); |
369 | GetPainter()->setBrush(brushFill); |
370 | if (fillStroke.fill.colour == fillStroke.stroke.colour) { |
371 | painter->setPen(Qt::NoPen); |
372 | QRectF rect = QRectFFromPRect(rc); |
373 | if (cornerSize > 0.0f) { |
374 | // A radius of 1 shows no curve so add 1 |
375 | qreal radius = cornerSize+1; |
376 | GetPainter()->drawRoundedRect(rect, radius, radius); |
377 | } else { |
378 | GetPainter()->fillRect(rect, brushFill); |
379 | } |
380 | } else { |
381 | QColor qOutline = QColorFromColourRGBA(fillStroke.stroke.colour); |
382 | QPen penOutline(qOutline); |
383 | penOutline.setWidthF(fillStroke.stroke.width); |
384 | GetPainter()->setPen(penOutline); |
385 | |
386 | QRectF rect = QRectFFromPRect(rc.Inset(fillStroke.stroke.width / 2)); |
387 | if (cornerSize > 0.0f) { |
388 | // A radius of 1 shows no curve so add 1 |
389 | qreal radius = cornerSize+1; |
390 | GetPainter()->drawRoundedRect(rect, radius, radius); |
391 | } else { |
392 | GetPainter()->drawRect(rect); |
393 | } |
394 | } |
395 | } |
396 | |
397 | void SurfaceImpl::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { |
398 | QRectF rect = QRectFFromPRect(rc); |
399 | QLinearGradient linearGradient; |
400 | switch (options) { |
401 | case GradientOptions::leftToRight: |
402 | linearGradient = QLinearGradient(rc.left, rc.top, rc.right, rc.top); |
403 | break; |
404 | case GradientOptions::topToBottom: |
405 | default: |
406 | linearGradient = QLinearGradient(rc.left, rc.top, rc.left, rc.bottom); |
407 | break; |
408 | } |
409 | linearGradient.setSpread(QGradient::RepeatSpread); |
410 | for (const ColourStop &stop : stops) { |
411 | linearGradient.setColorAt(stop.position, QColorFromColourRGBA(stop.colour)); |
412 | } |
413 | QBrush brush = QBrush(linearGradient); |
414 | GetPainter()->fillRect(rect, brush); |
415 | } |
416 | |
417 | static std::vector<unsigned char> ImageByteSwapped(int width, int height, const unsigned char *pixelsImage) |
418 | { |
419 | // Input is RGBA, but Format_ARGB32 is BGRA, so swap the red bytes and blue bytes |
420 | size_t bytes = width * height * 4; |
421 | std::vector<unsigned char> imageBytes(pixelsImage, pixelsImage+bytes); |
422 | for (size_t i=0; i<bytes; i+=4) |
423 | std::swap(imageBytes[i], imageBytes[i+2]); |
424 | return imageBytes; |
425 | } |
426 | |
427 | void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) |
428 | { |
429 | std::vector<unsigned char> imageBytes = ImageByteSwapped(width, height, pixelsImage); |
430 | QImage image(&imageBytes[0], width, height, QImage::Format_ARGB32); |
431 | QPoint pt(rc.left, rc.top); |
432 | GetPainter()->drawImage(pt, image); |
433 | } |
434 | |
435 | void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke) |
436 | { |
437 | PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width); |
438 | BrushColour(fillStroke.fill.colour); |
439 | const QRectF rect = QRectFFromPRect(rc.Inset(fillStroke.stroke.width / 2)); |
440 | GetPainter()->drawEllipse(rect); |
441 | } |
442 | |
443 | void SurfaceImpl::Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) { |
444 | const XYPOSITION halfStroke = fillStroke.stroke.width / 2.0f; |
445 | const XYPOSITION radius = rc.Height() / 2.0f - halfStroke; |
446 | PRectangle rcInner = rc; |
447 | rcInner.left += radius; |
448 | rcInner.right -= radius; |
449 | const XYPOSITION arcHeight = rc.Height() - fillStroke.stroke.width; |
450 | |
451 | PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width); |
452 | BrushColour(fillStroke.fill.colour); |
453 | |
454 | QPainterPath path; |
455 | |
456 | const Ends leftSide = static_cast<Ends>(static_cast<unsigned int>(ends) & 0xfu); |
457 | const Ends rightSide = static_cast<Ends>(static_cast<unsigned int>(ends) & 0xf0u); |
458 | switch (leftSide) { |
459 | case Ends::leftFlat: |
460 | path.moveTo(rc.left + halfStroke, rc.top + halfStroke); |
461 | path.lineTo(rc.left + halfStroke, rc.bottom - halfStroke); |
462 | break; |
463 | case Ends::leftAngle: |
464 | path.moveTo(rcInner.left + halfStroke, rc.top + halfStroke); |
465 | path.lineTo(rc.left + halfStroke, rc.Centre().y); |
466 | path.lineTo(rcInner.left + halfStroke, rc.bottom - halfStroke); |
467 | break; |
468 | case Ends::semiCircles: |
469 | default: |
470 | path.moveTo(rcInner.left + halfStroke, rc.top + halfStroke); |
471 | QRectF rectangleArc(rc.left + halfStroke, rc.top + halfStroke, |
472 | arcHeight, arcHeight); |
473 | path.arcTo(rectangleArc, 90, 180); |
474 | break; |
475 | } |
476 | |
477 | switch (rightSide) { |
478 | case Ends::rightFlat: |
479 | path.lineTo(rc.right - halfStroke, rc.bottom - halfStroke); |
480 | path.lineTo(rc.right - halfStroke, rc.top + halfStroke); |
481 | break; |
482 | case Ends::rightAngle: |
483 | path.lineTo(rcInner.right - halfStroke, rc.bottom - halfStroke); |
484 | path.lineTo(rc.right - halfStroke, rc.Centre().y); |
485 | path.lineTo(rcInner.right - halfStroke, rc.top + halfStroke); |
486 | break; |
487 | case Ends::semiCircles: |
488 | default: |
489 | path.lineTo(rcInner.right - halfStroke, rc.bottom - halfStroke); |
490 | QRectF rectangleArc(rc.right - arcHeight - halfStroke, rc.top + halfStroke, |
491 | arcHeight, arcHeight); |
492 | path.arcTo(rectangleArc, 270, 180); |
493 | break; |
494 | } |
495 | |
496 | // Close the path to enclose it for stroking and for filling, then draw it |
497 | path.closeSubpath(); |
498 | GetPainter()->drawPath(path); |
499 | } |
500 | |
501 | void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) |
502 | { |
503 | SurfaceImpl *source = dynamic_cast<SurfaceImpl *>(&surfaceSource); |
504 | QPixmap *pixmap = static_cast<QPixmap *>(source->GetPaintDevice()); |
505 | |
506 | GetPainter()->drawPixmap(rc.left, rc.top, *pixmap, from.x, from.y, -1, -1); |
507 | } |
508 | |
509 | std::unique_ptr<IScreenLineLayout> SurfaceImpl::Layout(const IScreenLine *) |
510 | { |
511 | return {}; |
512 | } |
513 | |
514 | void SurfaceImpl::DrawTextNoClip(PRectangle rc, |
515 | const Font *font, |
516 | XYPOSITION ybase, |
517 | std::string_view text, |
518 | ColourRGBA fore, |
519 | ColourRGBA back) |
520 | { |
521 | SetFont(font); |
522 | PenColour(fore); |
523 | |
524 | GetPainter()->setBackground(QColorFromColourRGBA(back)); |
525 | GetPainter()->setBackgroundMode(Qt::OpaqueMode); |
526 | QString su = UnicodeFromText(codec, text); |
527 | GetPainter()->drawText(QPointF(rc.left, ybase), su); |
528 | } |
529 | |
530 | void SurfaceImpl::DrawTextClipped(PRectangle rc, |
531 | const Font *font, |
532 | XYPOSITION ybase, |
533 | std::string_view text, |
534 | ColourRGBA fore, |
535 | ColourRGBA back) |
536 | { |
537 | SetClip(rc); |
538 | DrawTextNoClip(rc, font, ybase, text, fore, back); |
539 | PopClip(); |
540 | } |
541 | |
542 | void SurfaceImpl::DrawTextTransparent(PRectangle rc, |
543 | const Font *font, |
544 | XYPOSITION ybase, |
545 | std::string_view text, |
546 | ColourRGBA fore) |
547 | { |
548 | SetFont(font); |
549 | PenColour(fore); |
550 | |
551 | GetPainter()->setBackgroundMode(Qt::TransparentMode); |
552 | QString su = UnicodeFromText(codec, text); |
553 | GetPainter()->drawText(QPointF(rc.left, ybase), su); |
554 | } |
555 | |
556 | void SurfaceImpl::SetClip(PRectangle rc) |
557 | { |
558 | GetPainter()->save(); |
559 | GetPainter()->setClipRect(QRectFFromPRect(rc)); |
560 | } |
561 | |
562 | void SurfaceImpl::PopClip() |
563 | { |
564 | GetPainter()->restore(); |
565 | } |
566 | |
567 | void SurfaceImpl::MeasureWidths(const Font *font, |
568 | std::string_view text, |
569 | XYPOSITION *positions) |
570 | { |
571 | if (!font) |
572 | return; |
573 | SetCodec(font); |
574 | QString su = UnicodeFromText(codec, text); |
575 | QTextLayout tlay(su, *FontPointer(font), GetPaintDevice()); |
576 | tlay.beginLayout(); |
577 | QTextLine tl = tlay.createLine(); |
578 | tlay.endLayout(); |
579 | if (mode.codePage == SC_CP_UTF8) { |
580 | int fit = su.size(); |
581 | int ui=0; |
582 | size_t i=0; |
583 | while (ui<fit) { |
584 | const unsigned char uch = text[i]; |
585 | const unsigned int byteCount = UTF8BytesOfLead[uch]; |
586 | const int codeUnits = UTF16LengthFromUTF8ByteCount(byteCount); |
587 | qreal xPosition = tl.cursorToX(ui+codeUnits); |
588 | for (size_t bytePos=0; (bytePos<byteCount) && (i<text.length()); bytePos++) { |
589 | positions[i++] = xPosition; |
590 | } |
591 | ui += codeUnits; |
592 | } |
593 | XYPOSITION lastPos = 0; |
594 | if (i > 0) |
595 | lastPos = positions[i-1]; |
596 | while (i<text.length()) { |
597 | positions[i++] = lastPos; |
598 | } |
599 | } else if (mode.codePage) { |
600 | // DBCS |
601 | int ui = 0; |
602 | for (size_t i=0; i<text.length();) { |
603 | size_t lenChar = DBCSIsLeadByte(mode.codePage, text[i]) ? 2 : 1; |
604 | qreal xPosition = tl.cursorToX(ui+1); |
605 | for (unsigned int bytePos=0; (bytePos<lenChar) && (i<text.length()); bytePos++) { |
606 | positions[i++] = xPosition; |
607 | } |
608 | ui++; |
609 | } |
610 | } else { |
611 | // Single byte encoding |
612 | for (int i=0; i<static_cast<int>(text.length()); i++) { |
613 | positions[i] = tl.cursorToX(i+1); |
614 | } |
615 | } |
616 | } |
617 | |
618 | XYPOSITION SurfaceImpl::WidthText(const Font *font, std::string_view text) |
619 | { |
620 | QFontMetricsF metrics(*FontPointer(font), device); |
621 | SetCodec(font); |
622 | QString su = UnicodeFromText(codec, text); |
623 | return metrics.width(su); |
624 | } |
625 | |
626 | void SurfaceImpl::DrawTextNoClipUTF8(PRectangle rc, |
627 | const Font *font, |
628 | XYPOSITION ybase, |
629 | std::string_view text, |
630 | ColourRGBA fore, |
631 | ColourRGBA back) |
632 | { |
633 | SetFont(font); |
634 | PenColour(fore); |
635 | |
636 | GetPainter()->setBackground(QColorFromColourRGBA(back)); |
637 | GetPainter()->setBackgroundMode(Qt::OpaqueMode); |
638 | QString su = QString::fromUtf8(text.data(), static_cast<int>(text.length())); |
639 | GetPainter()->drawText(QPointF(rc.left, ybase), su); |
640 | } |
641 | |
642 | void SurfaceImpl::DrawTextClippedUTF8(PRectangle rc, |
643 | const Font *font, |
644 | XYPOSITION ybase, |
645 | std::string_view text, |
646 | ColourRGBA fore, |
647 | ColourRGBA back) |
648 | { |
649 | SetClip(rc); |
650 | DrawTextNoClip(rc, font, ybase, text, fore, back); |
651 | PopClip(); |
652 | } |
653 | |
654 | void SurfaceImpl::DrawTextTransparentUTF8(PRectangle rc, |
655 | const Font *font, |
656 | XYPOSITION ybase, |
657 | std::string_view text, |
658 | ColourRGBA fore) |
659 | { |
660 | SetFont(font); |
661 | PenColour(fore); |
662 | |
663 | GetPainter()->setBackgroundMode(Qt::TransparentMode); |
664 | QString su = QString::fromUtf8(text.data(), static_cast<int>(text.length())); |
665 | GetPainter()->drawText(QPointF(rc.left, ybase), su); |
666 | } |
667 | |
668 | void SurfaceImpl::MeasureWidthsUTF8(const Font *font, |
669 | std::string_view text, |
670 | XYPOSITION *positions) |
671 | { |
672 | if (!font) |
673 | return; |
674 | QString su = QString::fromUtf8(text.data(), static_cast<int>(text.length())); |
675 | QTextLayout tlay(su, *FontPointer(font), GetPaintDevice()); |
676 | tlay.beginLayout(); |
677 | QTextLine tl = tlay.createLine(); |
678 | tlay.endLayout(); |
679 | int fit = su.size(); |
680 | int ui=0; |
681 | size_t i=0; |
682 | while (ui<fit) { |
683 | const unsigned char uch = text[i]; |
684 | const unsigned int byteCount = UTF8BytesOfLead[uch]; |
685 | const int codeUnits = UTF16LengthFromUTF8ByteCount(byteCount); |
686 | qreal xPosition = tl.cursorToX(ui+codeUnits); |
687 | for (size_t bytePos=0; (bytePos<byteCount) && (i<text.length()); bytePos++) { |
688 | positions[i++] = xPosition; |
689 | } |
690 | ui += codeUnits; |
691 | } |
692 | XYPOSITION lastPos = 0; |
693 | if (i > 0) |
694 | lastPos = positions[i-1]; |
695 | while (i<text.length()) { |
696 | positions[i++] = lastPos; |
697 | } |
698 | } |
699 | |
700 | XYPOSITION SurfaceImpl::WidthTextUTF8(const Font *font, std::string_view text) |
701 | { |
702 | QFontMetricsF metrics(*FontPointer(font), device); |
703 | QString su = QString::fromUtf8(text.data(), static_cast<int>(text.length())); |
704 | return metrics.width(su); |
705 | } |
706 | |
707 | XYPOSITION SurfaceImpl::Ascent(const Font *font) |
708 | { |
709 | QFontMetricsF metrics(*FontPointer(font), device); |
710 | return metrics.ascent(); |
711 | } |
712 | |
713 | XYPOSITION SurfaceImpl::Descent(const Font *font) |
714 | { |
715 | QFontMetricsF metrics(*FontPointer(font), device); |
716 | // Qt returns 1 less than true descent |
717 | // See: QFontEngineWin::descent which says: |
718 | // ### we subtract 1 to even out the historical +1 in QFontMetrics's |
719 | // ### height=asc+desc+1 equation. Fix in Qt5. |
720 | return metrics.descent() + 1; |
721 | } |
722 | |
723 | XYPOSITION SurfaceImpl::InternalLeading(const Font * /* font */) |
724 | { |
725 | return 0; |
726 | } |
727 | |
728 | XYPOSITION SurfaceImpl::Height(const Font *font) |
729 | { |
730 | QFontMetricsF metrics(*FontPointer(font), device); |
731 | return metrics.height(); |
732 | } |
733 | |
734 | XYPOSITION SurfaceImpl::AverageCharWidth(const Font *font) |
735 | { |
736 | QFontMetricsF metrics(*FontPointer(font), device); |
737 | return metrics.averageCharWidth(); |
738 | } |
739 | |
740 | void SurfaceImpl::FlushCachedState() |
741 | { |
742 | if (device->paintingActive()) { |
743 | GetPainter()->setPen(QPen()); |
744 | GetPainter()->setBrush(QBrush()); |
745 | } |
746 | } |
747 | |
748 | void SurfaceImpl::FlushDrawing() |
749 | { |
750 | } |
751 | |
752 | QPaintDevice *SurfaceImpl::GetPaintDevice() |
753 | { |
754 | return device; |
755 | } |
756 | |
757 | QPainter *SurfaceImpl::GetPainter() |
758 | { |
759 | Q_ASSERT(device); |
760 | if (!painter) { |
761 | if (device->paintingActive()) { |
762 | painter = device->paintEngine()->painter(); |
763 | } else { |
764 | painterOwned = true; |
765 | painter = new QPainter(device); |
766 | } |
767 | |
768 | // Set text antialiasing unconditionally. |
769 | // The font's style strategy will override. |
770 | painter->setRenderHint(QPainter::TextAntialiasing, true); |
771 | |
772 | painter->setRenderHint(QPainter::Antialiasing, true); |
773 | } |
774 | |
775 | return painter; |
776 | } |
777 | |
778 | std::unique_ptr<Surface> Surface::Allocate(Technology) |
779 | { |
780 | return std::make_unique<SurfaceImpl>(); |
781 | } |
782 | |
783 | |
784 | //---------------------------------------------------------------------- |
785 | |
786 | namespace { |
787 | |
788 | QWidget *window(WindowID wid) noexcept |
789 | { |
790 | return static_cast<QWidget *>(wid); |
791 | } |
792 | |
793 | QRect ScreenRectangleForPoint(QPoint posGlobal) |
794 | { |
795 | #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) |
796 | const QScreen *screen = QGuiApplication::screenAt(posGlobal); |
797 | return screen->availableGeometry(); |
798 | #else |
799 | const QDesktopWidget *desktop = QApplication::desktop(); |
800 | return desktop->availableGeometry(posGlobal); |
801 | #endif |
802 | } |
803 | |
804 | } |
805 | |
806 | Window::~Window() noexcept = default; |
807 | |
808 | void Window::Destroy() noexcept |
809 | { |
810 | if (wid) |
811 | delete window(wid); |
812 | wid = nullptr; |
813 | } |
814 | PRectangle Window::GetPosition() const |
815 | { |
816 | // Before any size allocated pretend its 1000 wide so not scrolled |
817 | return wid ? PRectFromQRect(window(wid)->frameGeometry()) : PRectangle(0, 0, 1000, 1000); |
818 | } |
819 | |
820 | void Window::SetPosition(PRectangle rc) |
821 | { |
822 | if (wid) |
823 | window(wid)->setGeometry(QRectFromPRect(rc)); |
824 | } |
825 | |
826 | void Window::SetPositionRelative(PRectangle rc, const Window *relativeTo) |
827 | { |
828 | QPoint oPos = window(relativeTo->wid)->mapToGlobal(QPoint(0,0)); |
829 | int ox = oPos.x(); |
830 | int oy = oPos.y(); |
831 | ox += rc.left; |
832 | oy += rc.top; |
833 | |
834 | const QRect rectDesk = ScreenRectangleForPoint(QPoint(ox, oy)); |
835 | /* do some corrections to fit into screen */ |
836 | int sizex = rc.right - rc.left; |
837 | int sizey = rc.bottom - rc.top; |
838 | int screenWidth = rectDesk.width(); |
839 | if (ox < rectDesk.x()) |
840 | ox = rectDesk.x(); |
841 | if (sizex > screenWidth) |
842 | ox = rectDesk.x(); /* the best we can do */ |
843 | else if (ox + sizex > rectDesk.right()) |
844 | ox = rectDesk.right() - sizex; |
845 | if (oy + sizey > rectDesk.bottom()) |
846 | oy = rectDesk.bottom() - sizey; |
847 | |
848 | Q_ASSERT(wid); |
849 | window(wid)->move(ox, oy); |
850 | window(wid)->resize(sizex, sizey); |
851 | } |
852 | |
853 | PRectangle Window::GetClientPosition() const |
854 | { |
855 | // The client position is the window position |
856 | return GetPosition(); |
857 | } |
858 | |
859 | void Window::Show(bool show) |
860 | { |
861 | if (wid) |
862 | window(wid)->setVisible(show); |
863 | } |
864 | |
865 | void Window::InvalidateAll() |
866 | { |
867 | if (wid) |
868 | window(wid)->update(); |
869 | } |
870 | |
871 | void Window::InvalidateRectangle(PRectangle rc) |
872 | { |
873 | if (wid) |
874 | window(wid)->update(QRectFromPRect(rc)); |
875 | } |
876 | |
877 | void Window::SetCursor(Cursor curs) |
878 | { |
879 | if (wid) { |
880 | Qt::CursorShape shape; |
881 | |
882 | switch (curs) { |
883 | case Cursor::text: shape = Qt::IBeamCursor; break; |
884 | case Cursor::arrow: shape = Qt::ArrowCursor; break; |
885 | case Cursor::up: shape = Qt::UpArrowCursor; break; |
886 | case Cursor::wait: shape = Qt::WaitCursor; break; |
887 | case Cursor::horizontal: shape = Qt::SizeHorCursor; break; |
888 | case Cursor::vertical: shape = Qt::SizeVerCursor; break; |
889 | case Cursor::hand: shape = Qt::PointingHandCursor; break; |
890 | default: shape = Qt::ArrowCursor; break; |
891 | } |
892 | |
893 | QCursor cursor = QCursor(shape); |
894 | |
895 | if (curs != cursorLast) { |
896 | window(wid)->setCursor(cursor); |
897 | cursorLast = curs; |
898 | } |
899 | } |
900 | } |
901 | |
902 | /* Returns rectangle of monitor pt is on, both rect and pt are in Window's |
903 | window coordinates */ |
904 | PRectangle Window::GetMonitorRect(Point pt) |
905 | { |
906 | const QPoint posGlobal = window(wid)->mapToGlobal(QPoint(pt.x, pt.y)); |
907 | const QPoint originGlobal = window(wid)->mapToGlobal(QPoint(0, 0)); |
908 | QRect rectScreen = ScreenRectangleForPoint(posGlobal); |
909 | rectScreen.translate(-originGlobal.x(), -originGlobal.y()); |
910 | return PRectFromQRect(rectScreen); |
911 | } |
912 | |
913 | //---------------------------------------------------------------------- |
914 | class ListWidget : public QListWidget { |
915 | public: |
916 | explicit ListWidget(QWidget *parent); |
917 | |
918 | void setDelegate(IListBoxDelegate *lbDelegate); |
919 | |
920 | int currentSelection(); |
921 | |
922 | protected: |
923 | void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override; |
924 | void mouseDoubleClickEvent(QMouseEvent *event) override; |
925 | QStyleOptionViewItem viewOptions() const override; |
926 | |
927 | private: |
928 | IListBoxDelegate *delegate; |
929 | }; |
930 | |
931 | class ListBoxImpl : public ListBox { |
932 | public: |
933 | ListBoxImpl() noexcept; |
934 | |
935 | void SetFont(const Font *font) override; |
936 | void Create(Window &parent, int ctrlID, Point location, |
937 | int lineHeight, bool unicodeMode_, Technology technology) override; |
938 | void SetAverageCharWidth(int width) override; |
939 | void SetVisibleRows(int rows) override; |
940 | int GetVisibleRows() const override; |
941 | PRectangle GetDesiredRect() override; |
942 | int CaretFromEdge() override; |
943 | void Clear() noexcept override; |
944 | void Append(char *s, int type) override; |
945 | int Length() override; |
946 | void Select(int n) override; |
947 | int GetSelection() override; |
948 | int Find(const char *prefix) override; |
949 | std::string GetValue(int n) override; |
950 | void RegisterImage(int type, const char *xpmData) override; |
951 | void RegisterRGBAImage(int type, int width, int height, |
952 | const unsigned char *pixelsImage) override; |
953 | virtual void RegisterQPixmapImage(int type, const QPixmap& pm); |
954 | void ClearRegisteredImages() override; |
955 | void SetDelegate(IListBoxDelegate *lbDelegate) override; |
956 | void SetList(const char *list, char separator, char typesep) override; |
957 | void SetOptions(ListOptions options_) override; |
958 | |
959 | [[nodiscard]] ListWidget *GetWidget() const noexcept; |
960 | private: |
961 | bool unicodeMode{false}; |
962 | int visibleRows{5}; |
963 | QMap<int,QPixmap> images; |
964 | }; |
965 | ListBoxImpl::ListBoxImpl() noexcept = default; |
966 | |
967 | void ListBoxImpl::Create(Window &parent, |
968 | int /*ctrlID*/, |
969 | Point location, |
970 | int /*lineHeight*/, |
971 | bool unicodeMode_, |
972 | Technology) |
973 | { |
974 | unicodeMode = unicodeMode_; |
975 | |
976 | QWidget *qparent = static_cast<QWidget *>(parent.GetID()); |
977 | ListWidget *list = new ListWidget(qparent); |
978 | |
979 | #if defined(Q_OS_WIN) |
980 | // On Windows, Qt::ToolTip causes a crash when the list is clicked on |
981 | // so Qt::Tool is used. |
982 | list->setParent(nullptr, Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint |
983 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) |
984 | | Qt::WindowDoesNotAcceptFocus |
985 | #endif |
986 | ); |
987 | #else |
988 | // On OS X, Qt::Tool takes focus so main window loses focus so |
989 | // keyboard stops working. Qt::ToolTip works but its only really |
990 | // documented for tooltips. |
991 | // On Linux / X this setting allows clicking on list items. |
992 | list->setParent(nullptr, static_cast<Qt::WindowFlags>(Qt::ToolTip | Qt::FramelessWindowHint)); |
993 | #endif |
994 | list->setAttribute(Qt::WA_ShowWithoutActivating); |
995 | list->setFocusPolicy(Qt::NoFocus); |
996 | list->setUniformItemSizes(true); |
997 | list->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); |
998 | list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
999 | list->move(location.x, location.y); |
1000 | |
1001 | int maxIconWidth = 0; |
1002 | int maxIconHeight = 0; |
1003 | foreach (QPixmap im, images) { |
1004 | if (maxIconWidth < im.width()) |
1005 | maxIconWidth = im.width(); |
1006 | if (maxIconHeight < im.height()) |
1007 | maxIconHeight = im.height(); |
1008 | } |
1009 | list->setIconSize(QSize(maxIconWidth, maxIconHeight)); |
1010 | |
1011 | wid = list; |
1012 | } |
1013 | void ListBoxImpl::SetFont(const Font *font) |
1014 | { |
1015 | ListWidget *list = GetWidget(); |
1016 | const FontAndCharacterSet *pfacs = AsFontAndCharacterSet(font); |
1017 | if (pfacs && pfacs->pfont) { |
1018 | list->setFont(*(pfacs->pfont)); |
1019 | } |
1020 | } |
1021 | void ListBoxImpl::SetAverageCharWidth(int /*width*/) {} |
1022 | |
1023 | void ListBoxImpl::SetVisibleRows(int rows) |
1024 | { |
1025 | visibleRows = rows; |
1026 | } |
1027 | |
1028 | int ListBoxImpl::GetVisibleRows() const |
1029 | { |
1030 | return visibleRows; |
1031 | } |
1032 | PRectangle ListBoxImpl::GetDesiredRect() |
1033 | { |
1034 | ListWidget *list = GetWidget(); |
1035 | int rows = Length(); |
1036 | if (rows == 0 || rows > visibleRows) { |
1037 | rows = visibleRows; |
1038 | } |
1039 | int rowHeight = list->sizeHintForRow(0); |
1040 | int height = (rows * rowHeight) + (2 * list->frameWidth()); |
1041 | |
1042 | QStyle *style = QApplication::style(); |
1043 | int width = list->sizeHintForColumn(0) + (2 * list->frameWidth()); |
1044 | if (Length() > rows) { |
1045 | width += style->pixelMetric(QStyle::PM_ScrollBarExtent); |
1046 | } |
1047 | |
1048 | return PRectangle(0, 0, width, height); |
1049 | } |
1050 | int ListBoxImpl::CaretFromEdge() |
1051 | { |
1052 | ListWidget *list = GetWidget(); |
1053 | int maxIconWidth = 0; |
1054 | foreach (QPixmap im, images) { |
1055 | if (maxIconWidth < im.width()) |
1056 | maxIconWidth = im.width(); |
1057 | } |
1058 | |
1059 | int ; |
1060 | // The 12 is from trial and error on OS X and the 7 |
1061 | // is from trial and error on Windows - there may be |
1062 | // a better programmatic way to find any padding factors. |
1063 | #ifdef Q_OS_DARWIN |
1064 | extra = 12; |
1065 | #else |
1066 | extra = 7; |
1067 | #endif |
1068 | return maxIconWidth + (2 * list->frameWidth()) + extra; |
1069 | } |
1070 | void ListBoxImpl::Clear() noexcept |
1071 | { |
1072 | ListWidget *list = GetWidget(); |
1073 | list->clear(); |
1074 | } |
1075 | void ListBoxImpl::Append(char *s, int type) |
1076 | { |
1077 | ListWidget *list = GetWidget(); |
1078 | QString str = unicodeMode ? QString::fromUtf8(s) : QString::fromLocal8Bit(s); |
1079 | QIcon icon; |
1080 | if (type >= 0) { |
1081 | Q_ASSERT(images.contains(type)); |
1082 | icon = images.value(type); |
1083 | } |
1084 | new QListWidgetItem(icon, str, list); |
1085 | } |
1086 | int ListBoxImpl::Length() |
1087 | { |
1088 | ListWidget *list = GetWidget(); |
1089 | return list->count(); |
1090 | } |
1091 | void ListBoxImpl::Select(int n) |
1092 | { |
1093 | ListWidget *list = GetWidget(); |
1094 | QModelIndex index = list->model()->index(n, 0); |
1095 | if (index.isValid()) { |
1096 | QRect row_rect = list->visualRect(index); |
1097 | if (!list->viewport()->rect().contains(row_rect)) { |
1098 | list->scrollTo(index, QAbstractItemView::PositionAtTop); |
1099 | } |
1100 | } |
1101 | list->setCurrentRow(n); |
1102 | } |
1103 | int ListBoxImpl::GetSelection() |
1104 | { |
1105 | ListWidget *list = GetWidget(); |
1106 | return list->currentSelection(); |
1107 | } |
1108 | int ListBoxImpl::Find(const char *prefix) |
1109 | { |
1110 | ListWidget *list = GetWidget(); |
1111 | QString sPrefix = unicodeMode ? QString::fromUtf8(prefix) : QString::fromLocal8Bit(prefix); |
1112 | QList<QListWidgetItem *> ms = list->findItems(sPrefix, Qt::MatchStartsWith); |
1113 | int result = -1; |
1114 | if (!ms.isEmpty()) { |
1115 | result = list->row(ms.first()); |
1116 | } |
1117 | |
1118 | return result; |
1119 | } |
1120 | std::string ListBoxImpl::GetValue(int n) |
1121 | { |
1122 | ListWidget *list = GetWidget(); |
1123 | QListWidgetItem *item = list->item(n); |
1124 | QString str = item->data(Qt::DisplayRole).toString(); |
1125 | QByteArray bytes = unicodeMode ? str.toUtf8() : str.toLocal8Bit(); |
1126 | return std::string(bytes.constData()); |
1127 | } |
1128 | |
1129 | void ListBoxImpl::RegisterQPixmapImage(int type, const QPixmap& pm) |
1130 | { |
1131 | images[type] = pm; |
1132 | ListWidget *list = GetWidget(); |
1133 | if (list) { |
1134 | QSize iconSize = list->iconSize(); |
1135 | if (pm.width() > iconSize.width() || pm.height() > iconSize.height()) |
1136 | list->setIconSize(QSize(qMax(pm.width(), iconSize.width()), |
1137 | qMax(pm.height(), iconSize.height()))); |
1138 | } |
1139 | |
1140 | } |
1141 | |
1142 | void ListBoxImpl::RegisterImage(int type, const char *xpmData) |
1143 | { |
1144 | RegisterQPixmapImage(type, QPixmap(reinterpret_cast<const char * const *>(xpmData))); |
1145 | } |
1146 | |
1147 | void ListBoxImpl::RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) |
1148 | { |
1149 | std::vector<unsigned char> imageBytes = ImageByteSwapped(width, height, pixelsImage); |
1150 | QImage image(&imageBytes[0], width, height, QImage::Format_ARGB32); |
1151 | RegisterQPixmapImage(type, QPixmap::fromImage(image)); |
1152 | } |
1153 | |
1154 | void ListBoxImpl::ClearRegisteredImages() |
1155 | { |
1156 | images.clear(); |
1157 | ListWidget *list = GetWidget(); |
1158 | if (list) |
1159 | list->setIconSize(QSize(0, 0)); |
1160 | } |
1161 | void ListBoxImpl::SetDelegate(IListBoxDelegate *lbDelegate) |
1162 | { |
1163 | ListWidget *list = GetWidget(); |
1164 | list->setDelegate(lbDelegate); |
1165 | } |
1166 | void ListBoxImpl::SetList(const char *list, char separator, char typesep) |
1167 | { |
1168 | // This method is *not* platform dependent. |
1169 | // It is borrowed from the GTK implementation. |
1170 | Clear(); |
1171 | size_t count = strlen(list) + 1; |
1172 | std::vector<char> words(list, list+count); |
1173 | char *startword = &words[0]; |
1174 | char *numword = nullptr; |
1175 | int i = 0; |
1176 | for (; words[i]; i++) { |
1177 | if (words[i] == separator) { |
1178 | words[i] = '\0'; |
1179 | if (numword) |
1180 | *numword = '\0'; |
1181 | Append(startword, numword?atoi(numword + 1):-1); |
1182 | startword = &words[0] + i + 1; |
1183 | numword = nullptr; |
1184 | } else if (words[i] == typesep) { |
1185 | numword = &words[0] + i; |
1186 | } |
1187 | } |
1188 | if (startword) { |
1189 | if (numword) |
1190 | *numword = '\0'; |
1191 | Append(startword, numword?atoi(numword + 1):-1); |
1192 | } |
1193 | } |
1194 | void ListBoxImpl::SetOptions(ListOptions) |
1195 | { |
1196 | } |
1197 | ListWidget *ListBoxImpl::GetWidget() const noexcept |
1198 | { |
1199 | return static_cast<ListWidget *>(wid); |
1200 | } |
1201 | |
1202 | ListBox::ListBox() noexcept = default; |
1203 | ListBox::~ListBox() noexcept = default; |
1204 | |
1205 | std::unique_ptr<ListBox> ListBox::Allocate() |
1206 | { |
1207 | return std::make_unique<ListBoxImpl>(); |
1208 | } |
1209 | ListWidget::ListWidget(QWidget *parent) |
1210 | : QListWidget(parent), delegate(nullptr) |
1211 | {} |
1212 | |
1213 | void ListWidget::setDelegate(IListBoxDelegate *lbDelegate) |
1214 | { |
1215 | delegate = lbDelegate; |
1216 | } |
1217 | |
1218 | void ListWidget::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { |
1219 | QListWidget::selectionChanged(selected, deselected); |
1220 | if (delegate) { |
1221 | const int selection = currentSelection(); |
1222 | if (selection >= 0) { |
1223 | ListBoxEvent event(ListBoxEvent::EventType::selectionChange); |
1224 | delegate->ListNotify(&event); |
1225 | } |
1226 | } |
1227 | } |
1228 | |
1229 | int ListWidget::currentSelection() { |
1230 | const QModelIndexList indices = selectionModel()->selectedRows(); |
1231 | foreach (const QModelIndex ind, indices) { |
1232 | return ind.row(); |
1233 | } |
1234 | return -1; |
1235 | } |
1236 | |
1237 | void ListWidget::mouseDoubleClickEvent(QMouseEvent * /* event */) |
1238 | { |
1239 | if (delegate) { |
1240 | ListBoxEvent event(ListBoxEvent::EventType::doubleClick); |
1241 | delegate->ListNotify(&event); |
1242 | } |
1243 | } |
1244 | |
1245 | QStyleOptionViewItem ListWidget::viewOptions() const |
1246 | { |
1247 | QStyleOptionViewItem result = QListWidget::viewOptions(); |
1248 | result.state |= QStyle::State_Active; |
1249 | return result; |
1250 | } |
1251 | //---------------------------------------------------------------------- |
1252 | Menu::() noexcept : mid(nullptr) {} |
1253 | void Menu::() |
1254 | { |
1255 | Destroy(); |
1256 | mid = new QMenu(); |
1257 | } |
1258 | |
1259 | void Menu::() noexcept |
1260 | { |
1261 | if (mid) { |
1262 | QMenu * = static_cast<QMenu *>(mid); |
1263 | delete menu; |
1264 | } |
1265 | mid = nullptr; |
1266 | } |
1267 | void Menu::(Point pt, const Window & /*w*/) |
1268 | { |
1269 | QMenu * = static_cast<QMenu *>(mid); |
1270 | menu->exec(QPoint(pt.x, pt.y)); |
1271 | Destroy(); |
1272 | } |
1273 | |
1274 | //---------------------------------------------------------------------- |
1275 | |
1276 | ColourRGBA Platform::Chrome() |
1277 | { |
1278 | QColor c(Qt::gray); |
1279 | return ColourRGBA(c.red(), c.green(), c.blue()); |
1280 | } |
1281 | |
1282 | ColourRGBA Platform::ChromeHighlight() |
1283 | { |
1284 | QColor c(Qt::lightGray); |
1285 | return ColourRGBA(c.red(), c.green(), c.blue()); |
1286 | } |
1287 | |
1288 | const char *Platform::DefaultFont() |
1289 | { |
1290 | static char fontNameDefault[200] = "" ; |
1291 | if (!fontNameDefault[0]) { |
1292 | QFont font = QApplication::font(); |
1293 | strcpy(fontNameDefault, font.family().toUtf8()); |
1294 | } |
1295 | return fontNameDefault; |
1296 | } |
1297 | |
1298 | int Platform::DefaultFontSize() |
1299 | { |
1300 | QFont font = QApplication::font(); |
1301 | return font.pointSize(); |
1302 | } |
1303 | |
1304 | unsigned int Platform::DoubleClickTime() |
1305 | { |
1306 | return QApplication::doubleClickInterval(); |
1307 | } |
1308 | |
1309 | void Platform::DebugDisplay(const char *s) noexcept |
1310 | { |
1311 | qWarning("Scintilla: %s" , s); |
1312 | } |
1313 | |
1314 | void Platform::DebugPrintf(const char *format, ...) noexcept |
1315 | { |
1316 | char buffer[2000]; |
1317 | va_list pArguments{}; |
1318 | va_start(pArguments, format); |
1319 | vsprintf(buffer, format, pArguments); |
1320 | va_end(pArguments); |
1321 | Platform::DebugDisplay(buffer); |
1322 | } |
1323 | |
1324 | bool Platform::(bool /*assertionPopUps*/) noexcept |
1325 | { |
1326 | return false; |
1327 | } |
1328 | |
1329 | void Platform::Assert(const char *c, const char *file, int line) noexcept |
1330 | { |
1331 | char buffer[2000]; |
1332 | sprintf(buffer, "Assertion [%s] failed at %s %d" , c, file, line); |
1333 | if (Platform::ShowAssertionPopUps(false)) { |
1334 | QMessageBox mb("Assertion Failure" , buffer, QMessageBox::NoIcon, |
1335 | QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); |
1336 | mb.exec(); |
1337 | } else { |
1338 | strcat(buffer, "\n" ); |
1339 | Platform::DebugDisplay(buffer); |
1340 | } |
1341 | } |
1342 | |
1343 | } |
1344 | |