1 // The implementation of the Qt specific subclass of ScintillaBase.
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 <string.h>
22 
23 #include <qapplication.h>
24 #include <qbytearray.h>
25 #include <qdrag.h>
26 #include <qevent.h>
27 #include <qmimedata.h>
28 #include <qpainter.h>
29 #include <qscrollbar.h>
30 #include <qstring.h>
31 
32 #include "Qsci/qsciscintillabase.h"
33 #include "ScintillaQt.h"
34 #if !defined(QT_NO_ACCESSIBILITY)
35 #include "SciAccessibility.h"
36 #endif
37 #include "SciClasses.h"
38 
39 
40 // We want to use the Scintilla notification names as Qt signal names.
41 #undef  SCEN_CHANGE
42 #undef  SCN_AUTOCCANCELLED
43 #undef  SCN_AUTOCCHARDELETED
44 #undef  SCN_AUTOCCOMPLETED
45 #undef  SCN_AUTOCSELECTION
46 #undef  SCN_AUTOCSELECTIONCHANGE
47 #undef  SCN_CALLTIPCLICK
48 #undef  SCN_CHARADDED
49 #undef  SCN_DOUBLECLICK
50 #undef  SCN_DWELLEND
51 #undef  SCN_DWELLSTART
52 #undef  SCN_FOCUSIN
53 #undef  SCN_FOCUSOUT
54 #undef  SCN_HOTSPOTCLICK
55 #undef  SCN_HOTSPOTDOUBLECLICK
56 #undef  SCN_HOTSPOTRELEASECLICK
57 #undef  SCN_INDICATORCLICK
58 #undef  SCN_INDICATORRELEASE
59 #undef  SCN_MACRORECORD
60 #undef  SCN_MARGINCLICK
61 #undef  SCN_MARGINRIGHTCLICK
62 #undef  SCN_MODIFIED
63 #undef  SCN_MODIFYATTEMPTRO
64 #undef  SCN_NEEDSHOWN
65 #undef  SCN_PAINTED
66 #undef  SCN_SAVEPOINTLEFT
67 #undef  SCN_SAVEPOINTREACHED
68 #undef  SCN_STYLENEEDED
69 #undef  SCN_UPDATEUI
70 #undef  SCN_USERLISTSELECTION
71 #undef  SCN_ZOOM
72 
73 enum
74 {
75     SCEN_CHANGE = 768,
76     SCN_AUTOCCANCELLED = 2025,
77     SCN_AUTOCCHARDELETED = 2026,
78     SCN_AUTOCCOMPLETED = 2030,
79     SCN_AUTOCSELECTION = 2022,
80     SCN_AUTOCSELECTIONCHANGE = 2032,
81     SCN_CALLTIPCLICK = 2021,
82     SCN_CHARADDED = 2001,
83     SCN_DOUBLECLICK = 2006,
84     SCN_DWELLEND = 2017,
85     SCN_DWELLSTART = 2016,
86     SCN_FOCUSIN = 2028,
87     SCN_FOCUSOUT = 2029,
88     SCN_HOTSPOTCLICK = 2019,
89     SCN_HOTSPOTDOUBLECLICK = 2020,
90     SCN_HOTSPOTRELEASECLICK = 2027,
91     SCN_INDICATORCLICK = 2023,
92     SCN_INDICATORRELEASE = 2024,
93     SCN_MACRORECORD = 2009,
94     SCN_MARGINCLICK = 2010,
95     SCN_MARGINRIGHTCLICK = 2031,
96     SCN_MODIFIED = 2008,
97     SCN_MODIFYATTEMPTRO = 2004,
98     SCN_NEEDSHOWN = 2011,
99     SCN_PAINTED = 2013,
100     SCN_SAVEPOINTLEFT = 2003,
101     SCN_SAVEPOINTREACHED = 2002,
102     SCN_STYLENEEDED = 2000,
103     SCN_UPDATEUI = 2007,
104     SCN_USERLISTSELECTION = 2014,
105     SCN_ZOOM = 2018
106 };
107 
108 
109 // The ctor.
QsciScintillaQt(QsciScintillaBase * qsb_)110 QsciScintillaQt::QsciScintillaQt(QsciScintillaBase *qsb_)
111     : vMax(0), hMax(0), vPage(0), hPage(0), capturedMouse(false), qsb(qsb_)
112 {
113     wMain = qsb->viewport();
114 
115     // This is ignored.
116     imeInteraction = imeInline;
117 
118     // Using pixmaps screws things up when moving to a different display
119     // (although this could be because we haven't got the pixmap code right).
120     // However Qt shouldn't need buffered drawing anyway.
121     WndProc(SCI_SETBUFFEREDDRAW, 0, 0);
122 
123     for (int i = 0; i <= static_cast<int>(tickPlatform); ++i)
124         timers[i] = 0;
125 
126     Initialise();
127 }
128 
129 
130 // The dtor.
~QsciScintillaQt()131 QsciScintillaQt::~QsciScintillaQt()
132 {
133     Finalise();
134 }
135 
136 
137 // Initialise the instance.
Initialise()138 void QsciScintillaQt::Initialise()
139 {
140 }
141 
142 
143 // Tidy up the instance.
Finalise()144 void QsciScintillaQt::Finalise()
145 {
146     for (int i = 0; i <= static_cast<int>(tickPlatform); ++i)
147         FineTickerCancel(static_cast<TickReason>(i));
148 
149     ScintillaBase::Finalise();
150 }
151 
152 
153 // Start a drag.
StartDrag()154 void QsciScintillaQt::StartDrag()
155 {
156     inDragDrop = ddDragging;
157 
158     QDrag *qdrag = new QDrag(qsb);
159     qdrag->setMimeData(mimeSelection(drag));
160 
161 #if QT_VERSION >= 0x040300
162     Qt::DropAction action = qdrag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction);
163 #else
164     Qt::DropAction action = qdrag->start(Qt::MoveAction);
165 #endif
166 
167     // Remove the dragged text if it was a move to another widget or
168     // application.
169     if (action == Qt::MoveAction && qdrag->target() != qsb->viewport())
170         ClearSelection();
171 
172     SetDragPosition(Scintilla::SelectionPosition());
173     inDragDrop = ddNone;
174 }
175 
176 
177 // Re-implement to trap certain messages.
WndProc(unsigned int iMessage,uptr_t wParam,sptr_t lParam)178 sptr_t QsciScintillaQt::WndProc(unsigned int iMessage, uptr_t wParam,
179         sptr_t lParam)
180 {
181     switch (iMessage)
182     {
183     case SCI_GETDIRECTFUNCTION:
184         return reinterpret_cast<sptr_t>(DirectFunction);
185 
186     case SCI_GETDIRECTPOINTER:
187         return reinterpret_cast<sptr_t>(this);
188     }
189 
190     return ScintillaBase::WndProc(iMessage, wParam, lParam);
191 }
192 
193 
194 // Windows nonsense.
DefWndProc(unsigned int,uptr_t,sptr_t)195 sptr_t QsciScintillaQt::DefWndProc(unsigned int, uptr_t, sptr_t)
196 {
197     return 0;
198 }
199 
200 
201 // Grab or release the mouse (and keyboard).
SetMouseCapture(bool on)202 void QsciScintillaQt::SetMouseCapture(bool on)
203 {
204     if (mouseDownCaptures)
205     {
206         if (on)
207             qsb->viewport()->grabMouse();
208         else
209             qsb->viewport()->releaseMouse();
210     }
211 
212     capturedMouse = on;
213 }
214 
215 
216 // Return true if the mouse/keyboard are currently grabbed.
HaveMouseCapture()217 bool QsciScintillaQt::HaveMouseCapture()
218 {
219     return capturedMouse;
220 }
221 
222 
223 // Set the position of the vertical scrollbar.
SetVerticalScrollPos()224 void QsciScintillaQt::SetVerticalScrollPos()
225 {
226     QScrollBar *sb = qsb->verticalScrollBar();
227     bool was_blocked = sb->blockSignals(true);
228 
229     sb->setValue(topLine);
230 
231     sb->blockSignals(was_blocked);
232 }
233 
234 
235 // Set the position of the horizontal scrollbar.
SetHorizontalScrollPos()236 void QsciScintillaQt::SetHorizontalScrollPos()
237 {
238     QScrollBar *sb = qsb->horizontalScrollBar();
239     bool was_blocked = sb->blockSignals(true);
240 
241     sb->setValue(xOffset);
242 
243     sb->blockSignals(was_blocked);
244 }
245 
246 
247 // Set the extent of the vertical and horizontal scrollbars and return true if
248 // the view needs re-drawing.
ModifyScrollBars(Sci::Line nMax,Sci::Line nPage)249 bool QsciScintillaQt::ModifyScrollBars(Sci::Line nMax, Sci::Line nPage)
250 {
251     bool modified = false;
252     QScrollBar *sb;
253 
254     int vNewPage = nPage;
255     int vNewMax = nMax - vNewPage + 1;
256 
257     if (vMax != vNewMax || vPage != vNewPage)
258     {
259         vMax = vNewMax;
260         vPage = vNewPage;
261         modified = true;
262 
263         sb = qsb->verticalScrollBar();
264         sb->setMaximum(vMax);
265         sb->setPageStep(vPage);
266     }
267 
268     int hNewPage = GetTextRectangle().Width();
269     int hNewMax = (scrollWidth > hNewPage) ? scrollWidth - hNewPage : 0;
270     int charWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
271 
272     sb = qsb->horizontalScrollBar();
273 
274     if (hMax != hNewMax || hPage != hNewPage || sb->singleStep() != charWidth)
275     {
276         hMax = hNewMax;
277         hPage = hNewPage;
278         modified = true;
279 
280         sb->setMaximum(hMax);
281         sb->setPageStep(hPage);
282         sb->setSingleStep(charWidth);
283     }
284 
285     return modified;
286 }
287 
288 
289 // Called after SCI_SETWRAPMODE and SCI_SETHSCROLLBAR.
ReconfigureScrollBars()290 void QsciScintillaQt::ReconfigureScrollBars()
291 {
292     // Hide or show the scrollbars if needed.
293     bool hsb = (horizontalScrollBarVisible && !Wrapping());
294 
295     qsb->setHorizontalScrollBarPolicy(hsb ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff);
296     qsb->setVerticalScrollBarPolicy(verticalScrollBarVisible ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff);
297 }
298 
299 
300 // Notify interested parties of any change in the document.
NotifyChange()301 void QsciScintillaQt::NotifyChange()
302 {
303     emit qsb->SCEN_CHANGE();
304 }
305 
306 
307 // Notify interested parties of various events.  This is the main mapping
308 // between Scintilla notifications and Qt signals.
NotifyParent(SCNotification scn)309 void QsciScintillaQt::NotifyParent(SCNotification scn)
310 {
311     switch (scn.nmhdr.code)
312     {
313     case SCN_CALLTIPCLICK:
314         emit qsb->SCN_CALLTIPCLICK(scn.position);
315         break;
316 
317     case SCN_AUTOCCANCELLED:
318         emit qsb->SCN_AUTOCCANCELLED();
319         break;
320 
321     case SCN_AUTOCCHARDELETED:
322         emit qsb->SCN_AUTOCCHARDELETED();
323         break;
324 
325     case SCN_AUTOCCOMPLETED:
326         emit qsb->SCN_AUTOCCOMPLETED(scn.text, scn.position, scn.ch,
327                 scn.listCompletionMethod);
328         break;
329 
330     case SCN_AUTOCSELECTION:
331         emit qsb->SCN_AUTOCSELECTION(scn.text, scn.position, scn.ch,
332                 scn.listCompletionMethod);
333         emit qsb->SCN_AUTOCSELECTION(scn.text, scn.position);
334         break;
335 
336     case SCN_AUTOCSELECTIONCHANGE:
337         emit qsb->SCN_AUTOCSELECTIONCHANGE(scn.text, scn.listType,
338                 scn.position);
339         break;
340 
341     case SCN_CHARADDED:
342         emit qsb->SCN_CHARADDED(scn.ch);
343         break;
344 
345     case SCN_DOUBLECLICK:
346         emit qsb->SCN_DOUBLECLICK(scn.position, scn.line, scn.modifiers);
347         break;
348 
349     case SCN_DWELLEND:
350         emit qsb->SCN_DWELLEND(scn.position, scn.x, scn.y);
351         break;
352 
353     case SCN_DWELLSTART:
354         emit qsb->SCN_DWELLSTART(scn.position, scn.x, scn.y);
355         break;
356 
357     case SCN_FOCUSIN:
358         emit qsb->SCN_FOCUSIN();
359         break;
360 
361     case SCN_FOCUSOUT:
362         emit qsb->SCN_FOCUSOUT();
363         break;
364 
365     case SCN_HOTSPOTCLICK:
366         emit qsb->SCN_HOTSPOTCLICK(scn.position, scn.modifiers);
367         break;
368 
369     case SCN_HOTSPOTDOUBLECLICK:
370         emit qsb->SCN_HOTSPOTDOUBLECLICK(scn.position, scn.modifiers);
371         break;
372 
373     case SCN_HOTSPOTRELEASECLICK:
374         emit qsb->SCN_HOTSPOTRELEASECLICK(scn.position, scn.modifiers);
375         break;
376 
377     case SCN_INDICATORCLICK:
378         emit qsb->SCN_INDICATORCLICK(scn.position, scn.modifiers);
379         break;
380 
381     case SCN_INDICATORRELEASE:
382         emit qsb->SCN_INDICATORRELEASE(scn.position, scn.modifiers);
383         break;
384 
385     case SCN_MACRORECORD:
386         emit qsb->SCN_MACRORECORD(scn.message, scn.wParam,
387                 reinterpret_cast<void *>(scn.lParam));
388         break;
389 
390     case SCN_MARGINCLICK:
391         emit qsb->SCN_MARGINCLICK(scn.position, scn.modifiers, scn.margin);
392         break;
393 
394     case SCN_MARGINRIGHTCLICK:
395         emit qsb->SCN_MARGINRIGHTCLICK(scn.position, scn.modifiers,
396                 scn.margin);
397         break;
398 
399     case SCN_MODIFIED:
400         {
401             char *text;
402 
403 #if !defined(QT_NO_ACCESSIBILITY)
404             if ((scn.modificationType & SC_MOD_INSERTTEXT) != 0)
405                 QsciAccessibleScintillaBase::textInserted(qsb, scn.position,
406                         scn.text, scn.length);
407             else if ((scn.modificationType & SC_MOD_DELETETEXT) != 0)
408                 QsciAccessibleScintillaBase::textDeleted(qsb, scn.position,
409                         scn.text, scn.length);
410 #endif
411 
412             // Give some protection to the Python bindings.
413             if (scn.text && (scn.modificationType & (SC_MOD_INSERTTEXT|SC_MOD_DELETETEXT)) != 0)
414             {
415                 text = new char[scn.length + 1];
416                 memcpy(text, scn.text, scn.length);
417                 text[scn.length] = '\0';
418             }
419             else
420             {
421                 text = 0;
422             }
423 
424             emit qsb->SCN_MODIFIED(scn.position, scn.modificationType, text,
425                     scn.length, scn.linesAdded, scn.line, scn.foldLevelNow,
426                     scn.foldLevelPrev, scn.token, scn.annotationLinesAdded);
427 
428             if (text)
429                 delete[] text;
430 
431             break;
432         }
433 
434     case SCN_MODIFYATTEMPTRO:
435         emit qsb->SCN_MODIFYATTEMPTRO();
436         break;
437 
438     case SCN_NEEDSHOWN:
439         emit qsb->SCN_NEEDSHOWN(scn.position, scn.length);
440         break;
441 
442     case SCN_PAINTED:
443         emit qsb->SCN_PAINTED();
444         break;
445 
446     case SCN_SAVEPOINTLEFT:
447         emit qsb->SCN_SAVEPOINTLEFT();
448         break;
449 
450     case SCN_SAVEPOINTREACHED:
451         emit qsb->SCN_SAVEPOINTREACHED();
452         break;
453 
454     case SCN_STYLENEEDED:
455         emit qsb->SCN_STYLENEEDED(scn.position);
456         break;
457 
458     case SCN_UPDATEUI:
459 #if !defined(QT_NO_ACCESSIBILITY)
460         QsciAccessibleScintillaBase::updated(qsb);
461 #endif
462         emit qsb->SCN_UPDATEUI(scn.updated);
463         break;
464 
465     case SCN_USERLISTSELECTION:
466         emit qsb->SCN_USERLISTSELECTION(scn.text, scn.listType, scn.ch,
467                 scn.listCompletionMethod, scn.position);
468         emit qsb->SCN_USERLISTSELECTION(scn.text, scn.listType, scn.ch,
469                 scn.listCompletionMethod);
470         emit qsb->SCN_USERLISTSELECTION(scn.text, scn.listType);
471         break;
472 
473     case SCN_ZOOM:
474         emit qsb->SCN_ZOOM();
475         break;
476 
477     default:
478         qWarning("Unknown notification: %u", scn.nmhdr.code);
479     }
480 }
481 
482 
483 // Convert a selection to mime data.
mimeSelection(const Scintilla::SelectionText & text) const484 QMimeData *QsciScintillaQt::mimeSelection(
485         const Scintilla::SelectionText &text) const
486 {
487     return qsb->toMimeData(QByteArray(text.Data()), text.rectangular);
488 }
489 
490 
491 // Copy the selected text to the clipboard.
CopyToClipboard(const Scintilla::SelectionText & selectedText)492 void QsciScintillaQt::CopyToClipboard(
493         const Scintilla::SelectionText &selectedText)
494 {
495     QApplication::clipboard()->setMimeData(mimeSelection(selectedText));
496 }
497 
498 
499 // Implement copy.
Copy()500 void QsciScintillaQt::Copy()
501 {
502     if (!sel.Empty())
503     {
504         Scintilla::SelectionText text;
505 
506         CopySelectionRange(&text);
507         CopyToClipboard(text);
508     }
509 }
510 
511 
512 // Implement pasting text.
Paste()513 void QsciScintillaQt::Paste()
514 {
515     pasteFromClipboard(QClipboard::Clipboard);
516 }
517 
518 
519 // Paste text from either the clipboard or selection.
pasteFromClipboard(QClipboard::Mode mode)520 void QsciScintillaQt::pasteFromClipboard(QClipboard::Mode mode)
521 {
522     int len;
523     const char *s;
524     bool rectangular;
525 
526     const QMimeData *source = QApplication::clipboard()->mimeData(mode);
527 
528     if (!source || !qsb->canInsertFromMimeData(source))
529         return;
530 
531     QByteArray text = qsb->fromMimeData(source, rectangular);
532     len = text.length();
533     s = text.data();
534 
535     std::string dest = Scintilla::Document::TransformLineEnds(s, len,
536             pdoc->eolMode);
537 
538     Scintilla::SelectionText selText;
539     selText.Copy(dest, (IsUnicodeMode() ? SC_CP_UTF8 : 0),
540             vs.styles[STYLE_DEFAULT].characterSet, rectangular, false);
541 
542     Scintilla::UndoGroup ug(pdoc);
543 
544     ClearSelection();
545     InsertPasteShape(selText.Data(), selText.Length(),
546             selText.rectangular ? pasteRectangular : pasteStream);
547     EnsureCaretVisible();
548 }
549 
550 
551 // Create a call tip window.
CreateCallTipWindow(Scintilla::PRectangle rc)552 void QsciScintillaQt::CreateCallTipWindow(Scintilla::PRectangle rc)
553 {
554     if (!ct.wCallTip.Created())
555         ct.wCallTip = new QsciSciCallTip(qsb, this);
556 
557     QsciSciCallTip *w = reinterpret_cast<QsciSciCallTip *>(ct.wCallTip.GetID());
558 
559     w->resize(rc.right - rc.left, rc.bottom - rc.top);
560     ct.wCallTip.Show();
561 }
562 
563 
564 // Add an item to the right button menu.
AddToPopUp(const char * label,int cmd,bool enabled)565 void QsciScintillaQt::AddToPopUp(const char *label, int cmd, bool enabled)
566 {
567     QsciSciPopup *pm = static_cast<QsciSciPopup *>(popup.GetID());
568 
569     if (*label)
570         pm->addItem(qApp->translate("ContextMenu", label), cmd, enabled, this);
571     else
572         pm->addSeparator();
573 }
574 
575 
576 // Claim the selection.
ClaimSelection()577 void QsciScintillaQt::ClaimSelection()
578 {
579     bool isSel = !sel.Empty();
580 
581     if (isSel)
582     {
583         QClipboard *cb = QApplication::clipboard();
584 
585         // If we support X11 style selection then make it available now.
586         if (cb->supportsSelection())
587         {
588             Scintilla::SelectionText text;
589 
590             CopySelectionRange(&text);
591 
592             if (text.Data())
593                 cb->setMimeData(mimeSelection(text), QClipboard::Selection);
594         }
595 
596         primarySelection = true;
597     }
598     else
599         primarySelection = false;
600 
601 #if !defined(QT_NO_ACCESSIBILITY)
602     QsciAccessibleScintillaBase::selectionChanged(qsb, isSel);
603 #endif
604 
605     emit qsb->QSCN_SELCHANGED(isSel);
606 }
607 
608 
609 // Unclaim the selection.
UnclaimSelection()610 void QsciScintillaQt::UnclaimSelection()
611 {
612     if (primarySelection)
613     {
614         primarySelection = false;
615         qsb->viewport()->update();
616     }
617 }
618 
619 
620 // Implemented to provide compatibility with the Windows version.
DirectFunction(QsciScintillaQt * sciThis,unsigned int iMessage,uptr_t wParam,sptr_t lParam)621 sptr_t QsciScintillaQt::DirectFunction(QsciScintillaQt *sciThis, unsigned int iMessage,
622         uptr_t wParam, sptr_t lParam)
623 {
624     return sciThis->WndProc(iMessage,wParam,lParam);
625 }
626 
627 
628 // Draw the contents of the widget.
paintEvent(QPaintEvent * e)629 void QsciScintillaQt::paintEvent(QPaintEvent *e)
630 {
631     Scintilla::Surface *sw;
632 
633     const QRect &qr = e->rect();
634 
635     rcPaint.left = qr.left();
636     rcPaint.top = qr.top();
637     rcPaint.right = qr.right() + 1;
638     rcPaint.bottom = qr.bottom() + 1;
639 
640     Scintilla::PRectangle rcClient = GetClientRectangle();
641     paintingAllText = rcPaint.Contains(rcClient);
642 
643     sw = Scintilla::Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
644     if (!sw)
645         return;
646 
647     QPainter painter(qsb->viewport());
648 
649     paintState = painting;
650     sw->Init(&painter);
651     sw->SetUnicodeMode(CodePage() == SC_CP_UTF8);
652     Paint(sw, rcPaint);
653 
654     delete sw;
655 
656     // If the painting area was insufficient to cover the new style or brace
657     // highlight positions then repaint the whole thing.
658     if (paintState == paintAbandoned)
659     {
660         // Do a full re-paint immediately.  This may only be needed on OS X (to
661         // avoid flicker).
662         paintingAllText = true;
663 
664         sw = Scintilla::Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
665         if (!sw)
666             return;
667 
668         QPainter painter(qsb->viewport());
669 
670         paintState = painting;
671         sw->Init(&painter);
672         sw->SetUnicodeMode(CodePage() == SC_CP_UTF8);
673         Paint(sw, rcPaint);
674 
675         delete sw;
676 
677         qsb->viewport()->update();
678     }
679 
680     paintState = notPainting;
681 }
682 
683 
684 // Re-implemented to drive the tickers.
timerEvent(QTimerEvent * e)685 void QsciScintillaQt::timerEvent(QTimerEvent *e)
686 {
687     for (int i = 0; i <= static_cast<int>(tickPlatform); ++i)
688         if (timers[i] == e->timerId())
689             TickFor(static_cast<TickReason>(i));
690 }
691 
692 
693 // Re-implemented to say we support fine tickers.
FineTickerAvailable()694 bool QsciScintillaQt::FineTickerAvailable()
695 {
696     return true;
697 }
698 
699 
700 // Re-implemented to stop a ticker.
FineTickerCancel(TickReason reason)701 void QsciScintillaQt::FineTickerCancel(TickReason reason)
702 {
703     int &ticker = timers[static_cast<int>(reason)];
704 
705     if (ticker != 0)
706     {
707         killTimer(ticker);
708         ticker = 0;
709     }
710 }
711 
712 
713 // Re-implemented to check if a particular ticker is running.
FineTickerRunning(TickReason reason)714 bool QsciScintillaQt::FineTickerRunning(TickReason reason)
715 {
716     return (timers[static_cast<int>(reason)] != 0);
717 }
718 
719 
720 // Re-implemented to start a ticker.
FineTickerStart(TickReason reason,int ms,int)721 void QsciScintillaQt::FineTickerStart(TickReason reason, int ms, int)
722 {
723     int &ticker = timers[static_cast<int>(reason)];
724 
725     if (ticker != 0)
726         killTimer(ticker);
727 
728     ticker = startTimer(ms);
729 }
730 
731 
732 // Re-implemented to support idle processing.
SetIdle(bool on)733 bool QsciScintillaQt::SetIdle(bool on)
734 {
735     if (on)
736     {
737         if (!idler.state)
738         {
739             QTimer *timer = reinterpret_cast<QTimer *>(idler.idlerID);
740 
741             if (!timer)
742             {
743                 idler.idlerID = timer = new QTimer(this);
744                 connect(timer, SIGNAL(timeout()), this, SLOT(onIdle()));
745             }
746 
747             timer->start(0);
748             idler.state = true;
749         }
750     }
751     else if (idler.state)
752     {
753         reinterpret_cast<QTimer *>(idler.idlerID)->stop();
754         idler.state = false;
755     }
756 
757     return true;
758 }
759 
760 
761 // Invoked to trigger any idle processing.
onIdle()762 void QsciScintillaQt::onIdle()
763 {
764     if (!Idle())
765         SetIdle(false);
766 }
767