1 // This module implements the "official" low-level API.
2 //
3 // Copyright (c) 2021 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 "Qsci/qsciscintillabase.h"
22 
23 #include <QApplication>
24 #include <QClipboard>
25 #include <QColor>
26 #include <QContextMenuEvent>
27 #include <QDragEnterEvent>
28 #include <QDragMoveEvent>
29 #include <QDropEvent>
30 #include <QDragLeaveEvent>
31 #include <QFocusEvent>
32 #include <QKeyEvent>
33 #include <QList>
34 #include <QMimeData>
35 #include <QMouseEvent>
36 #include <QPaintEvent>
37 #include <QScrollBar>
38 #include <QStyle>
39 
40 #include "SciAccessibility.h"
41 #include "ScintillaQt.h"
42 
43 
44 // The #defines in Scintilla.h and the enums in qsciscintillabase.h conflict
45 // (because we want to use the same names) so we have to undefine those we use
46 // in this file.
47 #undef  SCI_SETCARETPERIOD
48 #undef  SCK_DOWN
49 #undef  SCK_UP
50 #undef  SCK_LEFT
51 #undef  SCK_RIGHT
52 #undef  SCK_HOME
53 #undef  SCK_END
54 #undef  SCK_PRIOR
55 #undef  SCK_NEXT
56 #undef  SCK_DELETE
57 #undef  SCK_INSERT
58 #undef  SCK_ESCAPE
59 #undef  SCK_BACK
60 #undef  SCK_TAB
61 #undef  SCK_RETURN
62 #undef  SCK_ADD
63 #undef  SCK_SUBTRACT
64 #undef  SCK_DIVIDE
65 #undef  SCK_WIN
66 #undef  SCK_RWIN
67 #undef  SCK_MENU
68 #undef  SCN_URIDROPPED
69 
70 
71 // Remember if we have linked the lexers.
72 static bool lexersLinked = false;
73 
74 // The list of instances.
75 static QList<QsciScintillaBase *> poolList;
76 
77 // Mime support.
78 static const QLatin1String mimeTextPlain("text/plain");
79 static const QLatin1String mimeRectangularWin("MSDEVColumnSelect");
80 static const QLatin1String mimeRectangular("text/x-qscintilla-rectangular");
81 
82 #if QT_VERSION < 0x060000 && defined(Q_OS_OSX)
83 extern void initialiseRectangularPasteboardMime();
84 #endif
85 
86 
87 // The ctor.
QsciScintillaBase(QWidget * parent)88 QsciScintillaBase::QsciScintillaBase(QWidget *parent)
89     : QAbstractScrollArea(parent), preeditPos(-1), preeditNrBytes(0),
90             clickCausedFocus(false)
91 {
92 #if !defined(QT_NO_ACCESSIBILITY)
93     QsciAccessibleScintillaBase::initialise();
94 #endif
95 
96     connectVerticalScrollBar();
97     connectHorizontalScrollBar();
98 
99     setAcceptDrops(true);
100     setFocusPolicy(Qt::WheelFocus);
101     setAttribute(Qt::WA_KeyCompression);
102     setAttribute(Qt::WA_InputMethodEnabled);
103     setInputMethodHints(
104             Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhMultiLine);
105 
106     viewport()->setBackgroundRole(QPalette::Base);
107     viewport()->setMouseTracking(true);
108     viewport()->setAttribute(Qt::WA_NoSystemBackground);
109 
110     triple_click.setSingleShot(true);
111 
112 #if QT_VERSION < 0x060000 && defined(Q_OS_OSX)
113     initialiseRectangularPasteboardMime();
114 #endif
115 
116     sci = new QsciScintillaQt(this);
117 
118     SendScintilla(SCI_SETCARETPERIOD, QApplication::cursorFlashTime() / 2);
119 
120     // Make sure the lexers are linked in.
121     if (!lexersLinked)
122     {
123         Scintilla_LinkLexers();
124         lexersLinked = true;
125     }
126 
127     // Add it to the pool.
128     poolList.append(this);
129 }
130 
131 
132 // The dtor.
~QsciScintillaBase()133 QsciScintillaBase::~QsciScintillaBase()
134 {
135     // The QsciScintillaQt object isn't a child so delete it explicitly.
136     delete sci;
137 
138     // Remove it from the pool.
139     poolList.removeAt(poolList.indexOf(this));
140 }
141 
142 
143 // Return an instance from the pool.
pool()144 QsciScintillaBase *QsciScintillaBase::pool()
145 {
146     return poolList.first();
147 }
148 
149 
150 // Tell Scintilla to update the scroll bars.  Scintilla should be doing this
151 // itself.
setScrollBars()152 void QsciScintillaBase::setScrollBars()
153 {
154     sci->SetScrollBars();
155 }
156 
157 
158 // Send a message to the real Scintilla widget using the low level Scintilla
159 // API.
SendScintilla(unsigned int msg,unsigned long wParam,long lParam) const160 long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
161         long lParam) const
162 {
163     return sci->WndProc(msg, wParam, lParam);
164 }
165 
166 
167 // Overloaded message send.
SendScintilla(unsigned int msg,unsigned long wParam,void * lParam) const168 long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
169         void *lParam) const
170 {
171     return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(lParam));
172 }
173 
174 
175 // Overloaded message send.
SendScintilla(unsigned int msg,uintptr_t wParam,const char * lParam) const176 long QsciScintillaBase::SendScintilla(unsigned int msg, uintptr_t wParam,
177         const char *lParam) const
178 {
179     return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(lParam));
180 }
181 
182 
183 // Overloaded message send.
SendScintilla(unsigned int msg,const char * lParam) const184 long QsciScintillaBase::SendScintilla(unsigned int msg,
185         const char *lParam) const
186 {
187     return sci->WndProc(msg, static_cast<uptr_t>(0),
188             reinterpret_cast<sptr_t>(lParam));
189 }
190 
191 
192 // Overloaded message send.
SendScintilla(unsigned int msg,const char * wParam,const char * lParam) const193 long QsciScintillaBase::SendScintilla(unsigned int msg, const char *wParam,
194         const char *lParam) const
195 {
196     return sci->WndProc(msg, reinterpret_cast<uptr_t>(wParam),
197             reinterpret_cast<sptr_t>(lParam));
198 }
199 
200 
201 // Overloaded message send.
SendScintilla(unsigned int msg,long wParam) const202 long QsciScintillaBase::SendScintilla(unsigned int msg, long wParam) const
203 {
204     return sci->WndProc(msg, static_cast<uptr_t>(wParam),
205             static_cast<sptr_t>(0));
206 }
207 
208 
209 // Overloaded message send.
SendScintilla(unsigned int msg,int wParam) const210 long QsciScintillaBase::SendScintilla(unsigned int msg, int wParam) const
211 {
212     return sci->WndProc(msg, static_cast<uptr_t>(wParam),
213             static_cast<sptr_t>(0));
214 }
215 
216 
217 // Overloaded message send.
SendScintilla(unsigned int msg,long cpMin,long cpMax,char * lpstrText) const218 long QsciScintillaBase::SendScintilla(unsigned int msg, long cpMin, long cpMax,
219         char *lpstrText) const
220 {
221     Sci_TextRange tr;
222 
223     tr.chrg.cpMin = cpMin;
224     tr.chrg.cpMax = cpMax;
225     tr.lpstrText = lpstrText;
226 
227     return sci->WndProc(msg, static_cast<uptr_t>(0),
228             reinterpret_cast<sptr_t>(&tr));
229 }
230 
231 
232 // Overloaded message send.
SendScintilla(unsigned int msg,unsigned long wParam,const QColor & col) const233 long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
234         const QColor &col) const
235 {
236     sptr_t lParam = (col.blue() << 16) | (col.green() << 8) | col.red();
237 
238     return sci->WndProc(msg, wParam, lParam);
239 }
240 
241 
242 // Overloaded message send.
SendScintilla(unsigned int msg,const QColor & col) const243 long QsciScintillaBase::SendScintilla(unsigned int msg, const QColor &col) const
244 {
245     uptr_t wParam = (col.blue() << 16) | (col.green() << 8) | col.red();
246 
247     return sci->WndProc(msg, wParam, static_cast<sptr_t>(0));
248 }
249 
250 
251 // Overloaded message send.
SendScintilla(unsigned int msg,unsigned long wParam,QPainter * hdc,const QRect & rc,long cpMin,long cpMax) const252 long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
253         QPainter *hdc, const QRect &rc, long cpMin, long cpMax) const
254 {
255     Sci_RangeToFormat rf;
256 
257     rf.hdc = rf.hdcTarget = reinterpret_cast<Scintilla::SurfaceID>(hdc);
258 
259     rf.rc.left = rc.left();
260     rf.rc.top = rc.top();
261     rf.rc.right = rc.right() + 1;
262     rf.rc.bottom = rc.bottom() + 1;
263 
264     rf.chrg.cpMin = cpMin;
265     rf.chrg.cpMax = cpMax;
266 
267     return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(&rf));
268 }
269 
270 
271 // Overloaded message send.
SendScintilla(unsigned int msg,unsigned long wParam,const QPixmap & lParam) const272 long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
273         const QPixmap &lParam) const
274 {
275     return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(&lParam));
276 }
277 
278 
279 // Overloaded message send.
SendScintilla(unsigned int msg,unsigned long wParam,const QImage & lParam) const280 long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
281         const QImage &lParam) const
282 {
283     return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(&lParam));
284 }
285 
286 
287 // Send a message to the real Scintilla widget using the low level Scintilla
288 // API that returns a pointer result.
SendScintillaPtrResult(unsigned int msg) const289 void *QsciScintillaBase::SendScintillaPtrResult(unsigned int msg) const
290 {
291     return reinterpret_cast<void *>(sci->WndProc(msg, static_cast<uptr_t>(0),
292             static_cast<sptr_t>(0)));
293 }
294 
295 
296 // Re-implemented to handle font changes
changeEvent(QEvent * e)297 void QsciScintillaBase::changeEvent(QEvent *e)
298 {
299     if (e->type() == QEvent::FontChange || e->type() == QEvent::ApplicationFontChange)
300         sci->InvalidateStyleRedraw();
301 
302     QAbstractScrollArea::changeEvent(e);
303 }
304 
305 
306 // Re-implemented to handle the context menu.
contextMenuEvent(QContextMenuEvent * e)307 void QsciScintillaBase::contextMenuEvent(QContextMenuEvent *e)
308 {
309     sci->ContextMenu(Scintilla::Point(e->globalX(), e->globalY()));
310 }
311 
312 
313 // Re-implemented to tell the widget it has the focus.
focusInEvent(QFocusEvent * e)314 void QsciScintillaBase::focusInEvent(QFocusEvent *e)
315 {
316     sci->SetFocusState(true);
317     clickCausedFocus = (e->reason() == Qt::MouseFocusReason);
318     QAbstractScrollArea::focusInEvent(e);
319 }
320 
321 
322 // Re-implemented to tell the widget it has lost the focus.
focusOutEvent(QFocusEvent * e)323 void QsciScintillaBase::focusOutEvent(QFocusEvent *e)
324 {
325     if (e->reason() == Qt::ActiveWindowFocusReason)
326     {
327         // Only tell Scintilla we have lost focus if the new active window
328         // isn't our auto-completion list.  This is probably only an issue on
329         // Linux and there are still problems because subsequent focus out
330         // events don't always seem to get generated (at least with Qt5).
331 
332         QWidget *aw = QApplication::activeWindow();
333 
334         if (!aw || aw->parent() != this || !aw->inherits("QsciSciListBox"))
335             sci->SetFocusState(false);
336     }
337     else
338     {
339         sci->SetFocusState(false);
340     }
341 
342     QAbstractScrollArea::focusOutEvent(e);
343 }
344 
345 
346 // Re-implemented to make sure tabs are passed to the editor.
focusNextPrevChild(bool next)347 bool QsciScintillaBase::focusNextPrevChild(bool next)
348 {
349     if (!sci->pdoc->IsReadOnly())
350         return false;
351 
352     return QAbstractScrollArea::focusNextPrevChild(next);
353 }
354 
355 
356 // Handle key presses.
keyPressEvent(QKeyEvent * e)357 void QsciScintillaBase::keyPressEvent(QKeyEvent *e)
358 {
359     int modifiers = 0;
360 
361     if (e->modifiers() & Qt::ShiftModifier)
362         modifiers |= SCMOD_SHIFT;
363 
364     if (e->modifiers() & Qt::ControlModifier)
365         modifiers |= SCMOD_CTRL;
366 
367     if (e->modifiers() & Qt::AltModifier)
368         modifiers |= SCMOD_ALT;
369 
370     if (e->modifiers() & Qt::MetaModifier)
371         modifiers |= SCMOD_META;
372 
373     int key = commandKey(e->key(), modifiers);
374 
375     if (key)
376     {
377         bool consumed = false;
378 
379         sci->KeyDownWithModifiers(key, modifiers, &consumed);
380 
381         if (consumed)
382         {
383             e->accept();
384             return;
385         }
386     }
387 
388     QString text = e->text();
389 
390     if (!text.isEmpty() && text[0].isPrint())
391     {
392         ScintillaBytes bytes = textAsBytes(text);
393         sci->AddCharUTF(bytes.data(), bytes.length());
394         e->accept();
395     }
396     else
397     {
398         QAbstractScrollArea::keyPressEvent(e);
399     }
400 }
401 
402 
403 // Map a Qt key to a valid Scintilla command key, or 0 if none.
commandKey(int qt_key,int & modifiers)404 int QsciScintillaBase::commandKey(int qt_key, int &modifiers)
405 {
406     int key;
407 
408     switch (qt_key)
409     {
410     case Qt::Key_Down:
411         key = SCK_DOWN;
412         break;
413 
414     case Qt::Key_Up:
415         key = SCK_UP;
416         break;
417 
418     case Qt::Key_Left:
419         key = SCK_LEFT;
420         break;
421 
422     case Qt::Key_Right:
423         key = SCK_RIGHT;
424         break;
425 
426     case Qt::Key_Home:
427         key = SCK_HOME;
428         break;
429 
430     case Qt::Key_End:
431         key = SCK_END;
432         break;
433 
434     case Qt::Key_PageUp:
435         key = SCK_PRIOR;
436         break;
437 
438     case Qt::Key_PageDown:
439         key = SCK_NEXT;
440         break;
441 
442     case Qt::Key_Delete:
443         key = SCK_DELETE;
444         break;
445 
446     case Qt::Key_Insert:
447         key = SCK_INSERT;
448         break;
449 
450     case Qt::Key_Escape:
451         key = SCK_ESCAPE;
452         break;
453 
454     case Qt::Key_Backspace:
455         key = SCK_BACK;
456         break;
457 
458     case Qt::Key_Tab:
459         key = SCK_TAB;
460         break;
461 
462     case Qt::Key_Backtab:
463         // Scintilla assumes a backtab is shift-tab.
464         key = SCK_TAB;
465         modifiers |= SCMOD_SHIFT;
466         break;
467 
468     case Qt::Key_Return:
469     case Qt::Key_Enter:
470         key = SCK_RETURN;
471         break;
472 
473     case Qt::Key_Super_L:
474         key = SCK_WIN;
475         break;
476 
477     case Qt::Key_Super_R:
478         key = SCK_RWIN;
479         break;
480 
481     case Qt::Key_Menu:
482         key = SCK_MENU;
483         break;
484 
485     default:
486         if ((key = qt_key) > 0x7f)
487             key = 0;
488     }
489 
490     return key;
491 }
492 
493 
494 // Encode a QString as bytes.
textAsBytes(const QString & text) const495 QsciScintillaBase::ScintillaBytes QsciScintillaBase::textAsBytes(const QString &text) const
496 {
497     if (sci->IsUnicodeMode())
498         return text.toUtf8();
499 
500     return text.toLatin1();
501 }
502 
503 
504 // Decode bytes as a QString.
bytesAsText(const char * bytes) const505 QString QsciScintillaBase::bytesAsText(const char *bytes) const
506 {
507     if (sci->IsUnicodeMode())
508         return QString::fromUtf8(bytes);
509 
510     return QString::fromLatin1(bytes);
511 }
512 
513 
514 // Handle a mouse button double click.
mouseDoubleClickEvent(QMouseEvent * e)515 void QsciScintillaBase::mouseDoubleClickEvent(QMouseEvent *e)
516 {
517     if (e->button() != Qt::LeftButton)
518     {
519         e->ignore();
520         return;
521     }
522 
523     setFocus();
524 
525     // Make sure Scintilla will interpret this as a double-click.
526     unsigned clickTime = sci->lastClickTime + Scintilla::Platform::DoubleClickTime() - 1;
527 
528     sci->ButtonDownWithModifiers(Scintilla::Point(e->x(), e->y()), clickTime,
529             eventModifiers(e));
530 
531     // Remember the current position and time in case it turns into a triple
532     // click.
533     triple_click_at = e->globalPos();
534     triple_click.start(QApplication::doubleClickInterval());
535 }
536 
537 
538 // Handle a mouse move.
mouseMoveEvent(QMouseEvent * e)539 void QsciScintillaBase::mouseMoveEvent(QMouseEvent *e)
540 {
541     sci->ButtonMoveWithModifiers(Scintilla::Point(e->x(), e->y()), 0,
542             eventModifiers(e));
543 }
544 
545 
546 // Handle a mouse button press.
mousePressEvent(QMouseEvent * e)547 void QsciScintillaBase::mousePressEvent(QMouseEvent *e)
548 {
549     setFocus();
550 
551     Scintilla::Point pt(e->x(), e->y());
552 
553     if (e->button() == Qt::LeftButton || e->button() == Qt::RightButton)
554     {
555         unsigned clickTime;
556 
557         // It is a triple click if the timer is running and the mouse hasn't
558         // moved too much.
559         if (triple_click.isActive() && (e->globalPos() - triple_click_at).manhattanLength() < QApplication::startDragDistance())
560             clickTime = sci->lastClickTime + Scintilla::Platform::DoubleClickTime() - 1;
561         else
562             clickTime = sci->lastClickTime + Scintilla::Platform::DoubleClickTime() + 1;
563 
564         triple_click.stop();
565 
566         // Scintilla uses the Alt modifier to initiate rectangular selection.
567         // However the GTK port (under X11, not Windows) uses the Control
568         // modifier (by default, although it is configurable).  It does this
569         // because most X11 window managers hijack Alt-drag to move the window.
570         // We do the same, except that (for the moment at least) we don't allow
571         // the modifier to be configured.
572         bool shift = e->modifiers() & Qt::ShiftModifier;
573         bool ctrl = e->modifiers() & Qt::ControlModifier;
574 #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
575         bool alt = e->modifiers() & Qt::AltModifier;
576 #else
577         bool alt = ctrl;
578 #endif
579 
580         if (e->button() == Qt::LeftButton)
581             sci->ButtonDownWithModifiers(pt, clickTime,
582                     QsciScintillaQt::ModifierFlags(shift, ctrl, alt));
583         else
584             sci->RightButtonDownWithModifiers(pt, clickTime,
585                     QsciScintillaQt::ModifierFlags(shift, ctrl, alt));
586     }
587     else if (e->button() == Qt::MiddleButton)
588     {
589         QClipboard *cb = QApplication::clipboard();
590 
591         if (cb->supportsSelection())
592         {
593             int pos = sci->PositionFromLocation(pt);
594 
595             sci->sel.Clear();
596             sci->SetSelection(pos, pos);
597 
598             sci->pasteFromClipboard(QClipboard::Selection);
599         }
600     }
601 }
602 
603 
604 // Handle a mouse button releases.
mouseReleaseEvent(QMouseEvent * e)605 void QsciScintillaBase::mouseReleaseEvent(QMouseEvent *e)
606 {
607     if (e->button() != Qt::LeftButton)
608         return;
609 
610     Scintilla::Point pt(e->x(), e->y());
611 
612     if (sci->HaveMouseCapture())
613     {
614         bool ctrl = e->modifiers() & Qt::ControlModifier;
615 
616         sci->ButtonUpWithModifiers(pt, 0,
617                 QsciScintillaQt::ModifierFlags(false, ctrl, false));
618     }
619 
620     if (!sci->pdoc->IsReadOnly() && !sci->PointInSelMargin(pt) && qApp->autoSipEnabled())
621     {
622         QStyle::RequestSoftwareInputPanel rsip = QStyle::RequestSoftwareInputPanel(style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
623 
624         if (!clickCausedFocus || rsip == QStyle::RSIP_OnMouseClick)
625             qApp->inputMethod()->show();
626     }
627 
628     clickCausedFocus = false;
629 }
630 
631 
632 // Handle paint events.
paintEvent(QPaintEvent * e)633 void QsciScintillaBase::paintEvent(QPaintEvent *e)
634 {
635     sci->paintEvent(e);
636 }
637 
638 
639 // Handle resize events.
resizeEvent(QResizeEvent *)640 void QsciScintillaBase::resizeEvent(QResizeEvent *)
641 {
642     sci->ChangeSize();
643 }
644 
645 
646 // Re-implemented to suppress the default behaviour as Scintilla works at a
647 // more fundamental level.  Note that this means that replacing the scrollbars
648 // with custom versions does not work.
scrollContentsBy(int,int)649 void QsciScintillaBase::scrollContentsBy(int, int)
650 {
651 }
652 
653 
654 // Handle the vertical scrollbar.
handleVSb(int value)655 void QsciScintillaBase::handleVSb(int value)
656 {
657     sci->ScrollTo(value);
658 }
659 
660 
661 // Handle the horizontal scrollbar.
handleHSb(int value)662 void QsciScintillaBase::handleHSb(int value)
663 {
664     sci->HorizontalScrollTo(value);
665 }
666 
667 
668 // Handle drag enters.
dragEnterEvent(QDragEnterEvent * e)669 void QsciScintillaBase::dragEnterEvent(QDragEnterEvent *e)
670 {
671     QsciScintillaBase::dragMoveEvent(e);
672 }
673 
674 
675 // Handle drag leaves.
dragLeaveEvent(QDragLeaveEvent *)676 void QsciScintillaBase::dragLeaveEvent(QDragLeaveEvent *)
677 {
678     sci->SetDragPosition(Scintilla::SelectionPosition());
679 }
680 
681 
682 // Handle drag moves.
dragMoveEvent(QDragMoveEvent * e)683 void QsciScintillaBase::dragMoveEvent(QDragMoveEvent *e)
684 {
685     if (e->mimeData()->hasUrls())
686     {
687         e->acceptProposedAction();
688     }
689     else
690     {
691         sci->SetDragPosition(
692                 sci->SPositionFromLocation(
693                         Scintilla::Point(e->pos().x(), e->pos().y()), false,
694                         false, sci->UserVirtualSpace()));
695 
696         acceptAction(e);
697     }
698 }
699 
700 
701 // Handle drops.
dropEvent(QDropEvent * e)702 void QsciScintillaBase::dropEvent(QDropEvent *e)
703 {
704     if (e->mimeData()->hasUrls())
705     {
706         e->acceptProposedAction();
707 
708         foreach (const QUrl &url, e->mimeData()->urls())
709             emit SCN_URIDROPPED(url);
710 
711         return;
712     }
713 
714     acceptAction(e);
715 
716     if (!e->isAccepted())
717         return;
718 
719     bool moving;
720     int len;
721     const char *s;
722     bool rectangular;
723 
724     moving = (e->dropAction() == Qt::MoveAction);
725 
726     QByteArray text = fromMimeData(e->mimeData(), rectangular);
727     len = text.length();
728     s = text.data();
729 
730     std::string dest = Scintilla::Document::TransformLineEnds(s, len,
731                 sci->pdoc->eolMode);
732 
733     sci->DropAt(sci->posDrop, dest.c_str(), dest.length(), moving,
734             rectangular);
735 
736     sci->Redraw();
737 }
738 
739 
acceptAction(QDropEvent * e)740 void QsciScintillaBase::acceptAction(QDropEvent *e)
741 {
742     if (sci->pdoc->IsReadOnly() || !canInsertFromMimeData(e->mimeData()))
743         e->ignore();
744     else
745         e->acceptProposedAction();
746 }
747 
748 
749 // See if a MIME data object can be decoded.
canInsertFromMimeData(const QMimeData * source) const750 bool QsciScintillaBase::canInsertFromMimeData(const QMimeData *source) const
751 {
752     return source->hasFormat(mimeTextPlain);
753 }
754 
755 
756 // Create text from a MIME data object.
fromMimeData(const QMimeData * source,bool & rectangular) const757 QByteArray QsciScintillaBase::fromMimeData(const QMimeData *source, bool &rectangular) const
758 {
759     // See if it is rectangular.  We try all of the different formats that
760     // Scintilla supports in case we are working across different platforms.
761     if (source->hasFormat(mimeRectangularWin))
762         rectangular = true;
763     else if (source->hasFormat(mimeRectangular))
764         rectangular = true;
765     else
766         rectangular = false;
767 
768     // Note that we don't support Scintilla's hack of adding a '\0' as Qt
769     // strips it off under the covers when pasting from another process.
770     QString utf8 = source->text();
771     QByteArray text;
772 
773     if (sci->IsUnicodeMode())
774         text = utf8.toUtf8();
775     else
776         text = utf8.toLatin1();
777 
778     return text;
779 }
780 
781 
782 // Create a MIME data object for some text.
toMimeData(const QByteArray & text,bool rectangular) const783 QMimeData *QsciScintillaBase::toMimeData(const QByteArray &text, bool rectangular) const
784 {
785     QMimeData *mime = new QMimeData;
786 
787     QString utf8;
788 
789     if (sci->IsUnicodeMode())
790         utf8 = QString::fromUtf8(text.constData(), text.size());
791     else
792         utf8 = QString::fromLatin1(text.constData(), text.size());
793 
794     mime->setText(utf8);
795 
796     if (rectangular)
797     {
798         // Use the platform specific "standard" for specifying a rectangular
799         // selection.
800 #if defined(Q_OS_WIN)
801         mime->setData(mimeRectangularWin, QByteArray());
802 #else
803         mime->setData(mimeRectangular, QByteArray());
804 #endif
805     }
806 
807     return mime;
808 }
809 
810 
811 // Connect up the vertical scroll bar.
connectVerticalScrollBar()812 void QsciScintillaBase::connectVerticalScrollBar()
813 {
814     connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
815             SLOT(handleVSb(int)));
816 }
817 
818 
819 // Connect up the horizontal scroll bar.
connectHorizontalScrollBar()820 void QsciScintillaBase::connectHorizontalScrollBar()
821 {
822     connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
823             SLOT(handleHSb(int)));
824 }
825 
826 
827 //! Replace the vertical scroll bar.
replaceVerticalScrollBar(QScrollBar * scrollBar)828 void QsciScintillaBase::replaceVerticalScrollBar(QScrollBar *scrollBar)
829 {
830     setVerticalScrollBar(scrollBar);
831     connectVerticalScrollBar();
832 }
833 
834 
835 // Replace the horizontal scroll bar.
replaceHorizontalScrollBar(QScrollBar * scrollBar)836 void QsciScintillaBase::replaceHorizontalScrollBar(QScrollBar *scrollBar)
837 {
838     setHorizontalScrollBar(scrollBar);
839     connectHorizontalScrollBar();
840 }
841 
842 
843 // Return true if a context menu should be displayed.  This is provided as a
844 // helper to QsciScintilla::contextMenuEvent().  A proper design would break
845 // backwards compatibility.
contextMenuNeeded(int x,int y) const846 bool QsciScintillaBase::contextMenuNeeded(int x, int y) const
847 {
848     Scintilla::Point pt(x, y);
849 
850     // Clear any selection if the mouse is outside.
851     if (!sci->PointInSelection(pt))
852         sci->SetEmptySelection(sci->PositionFromLocation(pt));
853 
854     // Respect SC_POPUP_*.
855     return sci->ShouldDisplayPopup(pt);
856 }
857 
858 
859 // Return the Scintilla keyboard modifiers set for a mouse event.
eventModifiers(QMouseEvent * e)860 int QsciScintillaBase::eventModifiers(QMouseEvent *e)
861 {
862     bool shift = e->modifiers() & Qt::ShiftModifier;
863     bool ctrl = e->modifiers() & Qt::ControlModifier;
864     bool alt = e->modifiers() & Qt::AltModifier;
865 
866     return QsciScintillaQt::ModifierFlags(shift, ctrl, alt);
867 }
868