text: //
  //          Copyright (c) 1990-2011, Scientific Toolworks, Inc.
  //
  // The License.txt file describes the conditions under which this software may be distributed.
  //
  // Author: Jason Haslam
  //
  // Additions Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware
  // ScintillaQt.cpp - Qt specific subclass of ScintillaBase
  
  #include "PlatQt.h"
  #include "ScintillaQt.h"
  #ifdef SCI_LEXER
  #include "LexerModule.h"
  #include "ExternalLexer.h"
  #endif
  
  #include <QApplication>
  #include <QDrag>
  #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
  #include <QInputContext>
  #endif
  #include <QMimeData>
  #include <QMenu>
  #include <QScrollBar>
  #include <QTimer>
  #include <QtCore5Compat/QTextCodec>
  
  #ifdef SCI_NAMESPACE
  using namespace Scintilla;
  #endif
  
  
  ScintillaQt::ScintillaQt(QAbstractScrollArea *parent)
  : QObject(parent), scrollArea(parent), vMax(0),  hMax(0), vPage(0), hPage(0),
   haveMouseCapture(false), dragWasDropped(false)
  {
  
  	wMain = scrollArea->viewport();
  
  	imeInteraction = imeInline;
  
  	// On OS X drawing text into a pixmap moves it around 1 pixel to
  	// the right compared to drawing it directly onto a window.
  	// Buffered drawing turned off by default to avoid this.
  	WndProc(SCI_SETBUFFEREDDRAW, false, 0);
  
  	Initialise();
  
  	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
  		timers[tr] = 0;
  	}
  }
  
  ScintillaQt::~ScintillaQt()
  {
  	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
  		FineTickerCancel(tr);
  	}
  	SetIdle(false);
  }
  
  void ScintillaQt::execCommand(QAction *action)
  {
  	int command = action->data().toInt();
  	Command(command);
  }
  
  #if defined(Q_OS_WIN)
  static const QString sMSDEVColumnSelect(QStringLiteral("MSDEVColumnSelect"));
  static const QString sWrappedMSDEVColumnSelect(QStringLiteral("application/x-qt-windows-mime;value=\"MSDEVColumnSelect\""));
  #elif defined(Q_OS_MAC)
  static const QString sScintillaRecPboardType(QStringLiteral("com.scintilla.utf16-plain-text.rectangular"));
  static const QString sScintillaRecMimeType(QStringLiteral("text/x-scintilla.utf16-plain-text.rectangular"));
  #else
  // Linux
  static const QString sMimeRectangularMarker(QStringLiteral("text/x-rectangular-marker"));
  #endif
  
  #if defined(Q_OS_MAC) && QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
  
  class ScintillaRectangularMime : public QMacPasteboardMime {
  public:
  	ScintillaRectangularMime() : QMacPasteboardMime(MIME_ALL) {
  	}
  
  	QString convertorName() {
  		return QString("ScintillaRectangularMime");
  	}
  
  	bool canConvert(const QString &mime, QString flav) {
  		return mimeFor(flav) == mime;
  	}
  
  	QString mimeFor(QString flav) {
  		if (flav == sScintillaRecPboardType)
  			return sScintillaRecMimeType;
  		return QString();
  	}
  
  	QString flavorFor(const QString &mime) {
  		if (mime == sScintillaRecMimeType)
  			return sScintillaRecPboardType;
  		return QString();
  	}
  
  	QVariant convertToMime(const QString & /* mime */, QList<QByteArray> data, QString /* flav */) {
  		QByteArray all;
  		foreach (QByteArray i, data) {
  			all += i;
  		}
  		return QVariant(all);
  	}
  
  	QList<QByteArray> convertFromMime(const QString & /* mime */, QVariant data, QString /* flav */) {
  		QByteArray a = data.toByteArray();
  		QList<QByteArray> l;
  		l.append(a);
  		return l;
  	}
  
  };
  
  // The Mime object registers itself but only want one for all Scintilla instances.
  // Should delete at exit to help memory leak detection but that would be extra work
  // and, since the clipboard exists after last Scintilla instance, may be complex.
  static ScintillaRectangularMime *singletonMime = 0;
  
  #endif
  
  void ScintillaQt::Initialise()
  {
  #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
  	rectangularSelectionModifier = SCMOD_ALT;
  #else
  	rectangularSelectionModifier = SCMOD_CTRL;
  #endif
  
  #if defined(Q_OS_MAC) && QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
  	if (!singletonMime) {
  		singletonMime = new ScintillaRectangularMime();
  
  		QStringList slTypes(sScintillaRecPboardType);
  		qRegisterDraggedTypes(slTypes);
  	}
  #endif
  
  	connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
  		this, SLOT(SelectionChanged()));
  }
  
  void ScintillaQt::Finalise()
  {
  	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
  		FineTickerCancel(tr);
  	}
  	ScintillaBase::Finalise();
  }
  
  void ScintillaQt::SelectionChanged()
  {
  	bool nowPrimary = QApplication::clipboard()->ownsSelection();
  	if (nowPrimary != primarySelection) {
  		primarySelection = nowPrimary;
  		Redraw();
  	}
  }
  
  bool ScintillaQt::DragThreshold(Point ptStart, Point ptNow)
  {
  	int xMove = std::abs(ptStart.x - ptNow.x);
  	int yMove = std::abs(ptStart.y - ptNow.y);
  	return (xMove > QApplication::startDragDistance()) ||
  		(yMove > QApplication::startDragDistance());
  }
  
  static QString StringFromSelectedText(const SelectionText &selectedText)
  {
  	if (selectedText.codePage == SC_CP_UTF8) {
  		return QString::fromUtf8(selectedText.Data(), static_cast<int>(selectedText.Length()));
  	} else {
  		QTextCodec *codec = QTextCodec::codecForName(
  				CharacterSetID(selectedText.characterSet));
  		return codec->toUnicode(selectedText.Data(), static_cast<int>(selectedText.Length()));
  	}
  }
  
  static void AddRectangularToMime(QMimeData *mimeData, QString su)
  {
  #if defined(Q_OS_WIN)
  	// Add an empty marker
  	mimeData->setData(sMSDEVColumnSelect, QByteArray());
  #elif defined(Q_OS_MAC)
  	// OS X gets marker + data to work with other implementations.
  	// Don't understand how this works but it does - the
  	// clipboard format is supposed to be UTF-16, not UTF-8.
  	mimeData->setData(sScintillaRecMimeType, su.toUtf8());
  #else
  	Q_UNUSED(su);
  	// Linux
  	// Add an empty marker
  	mimeData->setData(sMimeRectangularMarker, QByteArray());
  #endif
  }
  
  static bool IsRectangularInMime(const QMimeData *mimeData)
  {
  	QStringList formats = mimeData->formats();
  	for (int i = 0; i < formats.size(); ++i) {
  #if defined(Q_OS_WIN)
  		// Windows rectangular markers
  		// If rectangular copies made by this application, see base name.
  		if (formats[i] == sMSDEVColumnSelect)
  			return true;
  		// Otherwise see wrapped name.
  		if (formats[i] == sWrappedMSDEVColumnSelect)
  			return true;
  #elif defined(Q_OS_MAC)
  		if (formats[i] == sScintillaRecMimeType)
  			return true;
  #else
  		// Linux
  		if (formats[i] == sMimeRectangularMarker)
  			return true;
  #endif
  	}
  	return false;
  }
  
  bool ScintillaQt::ValidCodePage(int codePage) const
  {
  	return codePage == 0
  	|| codePage == SC_CP_UTF8
  	|| codePage == 932
  	|| codePage == 936
  	|| codePage == 949
  	|| codePage == 950
  	|| codePage == 1361;
  }
  
  
  void ScintillaQt::ScrollText(int linesToMove)
  {
  	int dy = vs.lineHeight * (linesToMove);
  	scrollArea->viewport()->scroll(0, dy);
  }
  
  void ScintillaQt::SetVerticalScrollPos()
  {
  	scrollArea->verticalScrollBar()->setValue(topLine);
  	emit verticalScrolled(topLine);
  }
  
  void ScintillaQt::SetHorizontalScrollPos()
  {
  	scrollArea->horizontalScrollBar()->setValue(xOffset);
  	emit horizontalScrolled(xOffset);
  }
  
  bool ScintillaQt::ModifyScrollBars(int nMax, int nPage)
  {
  	bool modified = false;
  
  	int vNewPage = nPage;
  	int vNewMax = nMax - vNewPage + 1;
  	if (vMax != vNewMax || vPage != vNewPage) {
  		vMax = vNewMax;
  		vPage = vNewPage;
  		modified = true;
  
  		scrollArea->verticalScrollBar()->setMaximum(vMax);
  		scrollArea->verticalScrollBar()->setPageStep(vPage);
  		emit verticalRangeChanged(vMax, vPage);
  	}
  
  	int hNewPage = GetTextRectangle().Width();
  	int hNewMax = (scrollWidth > hNewPage) ? scrollWidth - hNewPage : 0;
  	int charWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
  	if (hMax != hNewMax || hPage != hNewPage ||
  	    scrollArea->horizontalScrollBar()->singleStep() != charWidth) {
  		hMax = hNewMax;
  		hPage = hNewPage;
  		modified = true;
  
  		scrollArea->horizontalScrollBar()->setMaximum(hMax);
  		scrollArea->horizontalScrollBar()->setPageStep(hPage);
  		scrollArea->horizontalScrollBar()->setSingleStep(charWidth);
  		emit horizontalRangeChanged(hMax, hPage);
  	}
  
  	return modified;
  }
  
  void ScintillaQt::ReconfigureScrollBars()
  {
  	if (verticalScrollBarVisible) {
  		scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  	} else {
  		scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  	}
  
  	if (horizontalScrollBarVisible && !Wrapping()) {
  		scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  	} else {
  		scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  	}
  }
  
  void ScintillaQt::CopyToModeClipboard(const SelectionText &selectedText, QClipboard::Mode clipboardMode_)
  {
  	QClipboard *clipboard = QApplication::clipboard();
  	clipboard->clear(clipboardMode_);
  	QString su = StringFromSelectedText(selectedText);
  	QMimeData *mimeData = new QMimeData();
  	mimeData->setText(su);
  	if (selectedText.rectangular) {
  		AddRectangularToMime(mimeData, su);
  	}
  
  	// Allow client code to add additional data (e.g rich text).
  	emit aboutToCopy(mimeData);
  
  	clipboard->setMimeData(mimeData, clipboardMode_);
  }
  
  void ScintillaQt::Copy()
  {
  	if (!sel.Empty()) {
  		SelectionText st;
  		CopySelectionRange(&st);
  		CopyToClipboard(st);
  	}
  }
  
  void ScintillaQt::CopyToClipboard(const SelectionText &selectedText)
  {
  	CopyToModeClipboard(selectedText, QClipboard::Clipboard);
  }
  
  void ScintillaQt::PasteFromMode(QClipboard::Mode clipboardMode_)
  {
  	QClipboard *clipboard = QApplication::clipboard();
  	const QMimeData *mimeData = clipboard->mimeData(clipboardMode_);
  	bool isRectangular = IsRectangularInMime(mimeData);
  	QString text = clipboard->text(clipboardMode_);
  	QByteArray utext = BytesForDocument(text);
  	std::string dest(utext.constData(), utext.length());
  	SelectionText selText;
  	selText.Copy(dest, pdoc->dbcsCodePage, CharacterSetOfDocument(), isRectangular, false);
  
  	UndoGroup ug(pdoc);
  	ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
  	InsertPasteShape(selText.Data(), static_cast<int>(selText.Length()),
  		selText.rectangular ? pasteRectangular : pasteStream);
  	EnsureCaretVisible();
  }
  
  void ScintillaQt::Paste()
  {
  	PasteFromMode(QClipboard::Clipboard);
  }
  
  void ScintillaQt::ClaimSelection()
  {
  	if (QApplication::clipboard()->supportsSelection()) {
  		// X Windows has a 'primary selection' as well as the clipboard.
  		// Whenever the user selects some text, we become the primary selection
  		if (!sel.Empty()) {
  			primarySelection = true;
  			SelectionText st;
  			CopySelectionRange(&st);
  			CopyToModeClipboard(st, QClipboard::Selection);
  		} else {
  			primarySelection = false;
  		}
  	}
  }
  
  void ScintillaQt::NotifyChange()
  {
  	emit notifyChange();
  	emit command(
  			Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE),
  			reinterpret_cast<sptr_t>(wMain.GetID()));
  }
  
  void ScintillaQt::NotifyFocus(bool focus)
  {
  	emit command(
  			Platform::LongFromTwoShorts
  					(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS),
  			reinterpret_cast<sptr_t>(wMain.GetID()));
  
  	Editor::NotifyFocus(focus);
  }
  
  void ScintillaQt::NotifyParent(SCNotification scn)
  {
  	scn.nmhdr.hwndFrom = wMain.GetID();
  	scn.nmhdr.idFrom = GetCtrlID();
  	emit notifyParent(scn);
  }
  
  /**
  * Report that this Editor subclass has a working implementation of FineTickerStart.
  */
  bool ScintillaQt::FineTickerAvailable()
  {
  	return true;
  }
  
  bool ScintillaQt::FineTickerRunning(TickReason reason)
  {
  	return timers[reason] != 0;
  }
  
  void ScintillaQt::FineTickerStart(TickReason reason, int millis, int /* tolerance */)
  {
  	FineTickerCancel(reason);
  	timers[reason] = startTimer(millis);
  }
  
  void ScintillaQt::FineTickerCancel(TickReason reason)
  {
  	if (timers[reason]) {
  		killTimer(timers[reason]);
  		timers[reason] = 0;
  	}
  }
  
  void ScintillaQt::onIdle()
  {
  	bool continueIdling = Idle();
  	if (!continueIdling) {
  		SetIdle(false);
  	}
  }
  
  bool ScintillaQt::SetIdle(bool on)
  {
  	QTimer *qIdle;
  	if (on) {
  		// Start idler, if it's not running.
  		if (!idler.state) {
  			idler.state = true;
  			qIdle = new QTimer;
  			connect(qIdle, SIGNAL(timeout()), this, SLOT(onIdle()));
  			qIdle->start(0);
  			idler.idlerID = qIdle;
  		}
  	} else {
  		// Stop idler, if it's running
  		if (idler.state) {
  			idler.state = false;
  			qIdle = static_cast<QTimer *>(idler.idlerID);
  			qIdle->stop();
  			disconnect(qIdle, SIGNAL(timeout()), 0, 0);
  			delete qIdle;
  			idler.idlerID = 0;
  		}
  	}
  	return true;
  }
  
  int ScintillaQt::CharacterSetOfDocument() const
  {
  	return vs.styles[STYLE_DEFAULT].characterSet;
  }
  
  const char *ScintillaQt::CharacterSetIDOfDocument() const
  {
  	return CharacterSetID(CharacterSetOfDocument());
  }
  
  QString ScintillaQt::StringFromDocument(const char *s) const
  {
  	if (IsUnicodeMode()) {
  		return QString::fromUtf8(s);
  	} else {
  		QTextCodec *codec = QTextCodec::codecForName(
  				CharacterSetID(CharacterSetOfDocument()));
  		return codec->toUnicode(s);
  	}
  }
  
  QByteArray ScintillaQt::BytesForDocument(const QString &text) const
  {
  	if (IsUnicodeMode()) {
  		return text.toUtf8();
  	} else {
  		QTextCodec *codec = QTextCodec::codecForName(
  				CharacterSetID(CharacterSetOfDocument()));
  		return codec->fromUnicode(text);
  	}
  }
  
  
  class CaseFolderDBCS : public CaseFolderTable {
  	QTextCodec *codec;
  public:
  	explicit CaseFolderDBCS(QTextCodec *codec_) : codec(codec_) {
  		StandardASCII();
  	}
  	virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
  		if ((lenMixed == 1) && (sizeFolded > 0)) {
  			folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
  			return 1;
  		} else if (codec) {
  			QString su = codec->toUnicode(mixed, static_cast<int>(lenMixed));
  			QString suFolded = su.toCaseFolded();
  			QByteArray bytesFolded = codec->fromUnicode(suFolded);
  
  			if (bytesFolded.length() < static_cast<int>(sizeFolded)) {
  				memcpy(folded, bytesFolded,  bytesFolded.length());
  				return bytesFolded.length();
  			}
  		}
  		// Something failed so return a single NUL byte
  		folded[0] = '\0';
  		return 1;
  	}
  };
  
  CaseFolder *ScintillaQt::CaseFolderForEncoding()
  {
  	if (pdoc->dbcsCodePage == SC_CP_UTF8) {
  		return new CaseFolderUnicode();
  	} else {
  		const char *charSetBuffer = CharacterSetIDOfDocument();
  		if (charSetBuffer) {
  			if (pdoc->dbcsCodePage == 0) {
  				CaseFolderTable *pcf = new CaseFolderTable();
  				pcf->StandardASCII();
  				QTextCodec *codec = QTextCodec::codecForName(charSetBuffer);
  				// Only for single byte encodings
  				for (int i=0x80; i<0x100; i++) {
  					char sCharacter[2] = "A";
  					sCharacter[0] = i;
  					QString su = codec->toUnicode(sCharacter, 1);
  					QString suFolded = su.toCaseFolded();
  					QByteArray bytesFolded = codec->fromUnicode(suFolded);
  					if (bytesFolded.length() == 1) {
  						pcf->SetTranslation(sCharacter[0], bytesFolded[0]);
  					}
  				}
  				return pcf;
  			} else {
  				return new CaseFolderDBCS(QTextCodec::codecForName(charSetBuffer));
  			}
  		}
  		return 0;
  	}
  }
  
  std::string ScintillaQt::CaseMapString(const std::string &s, int caseMapping)
  {
  	if ((s.size() == 0) || (caseMapping == cmSame))
  		return s;
  
  	if (IsUnicodeMode()) {
  		std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
  		size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
  			(caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
  		retMapped.resize(lenMapped);
  		return retMapped;
  	}
  
  	QTextCodec *codec = QTextCodec::codecForName(CharacterSetIDOfDocument());
  	QString text = codec->toUnicode(s.c_str(), static_cast<int>(s.length()));
  
  	if (caseMapping == cmUpper) {
  		text = text.toUpper();
  	} else {
  		text = text.toLower();
  	}
  
  	QByteArray bytes = BytesForDocument(text);
  	return std::string(bytes.data(), bytes.length());
  }
  
  void ScintillaQt::SetMouseCapture(bool on)
  {
  	// This is handled automatically by Qt
  	if (mouseDownCaptures) {
  		haveMouseCapture = on;
  	}
  }
  
  bool ScintillaQt::HaveMouseCapture()
  {
  	return haveMouseCapture;
  }
  
  void ScintillaQt::StartDrag()
  {
  	inDragDrop = ddDragging;
  	dropWentOutside = true;
  	if (drag.Length()) {
  		QMimeData *mimeData = new QMimeData;
  		QString sText = StringFromSelectedText(drag);
  		mimeData->setText(sText);
  		if (drag.rectangular) {
  			AddRectangularToMime(mimeData, sText);
  		}
  		// This QDrag is not freed as that causes a crash on Linux
  		QDrag *dragon = new QDrag(scrollArea);
  		dragon->setMimeData(mimeData);
  
  		Qt::DropAction dropAction = dragon->exec(Qt::CopyAction|Qt::MoveAction);
  		if ((dropAction == Qt::MoveAction) && dropWentOutside) {
  			// Remove dragged out text
  			ClearSelection();
  		}
  	}
  	inDragDrop = ddNone;
  	SetDragPosition(SelectionPosition(invalidPosition));
  }
  
  void ScintillaQt::CreateCallTipWindow(PRectangle rc)
  {
  
  	if (!ct.wCallTip.Created()) {
  		QWidget *pCallTip =  new QWidget(0, Qt::ToolTip);
  		ct.wCallTip = pCallTip;
  		pCallTip->move(rc.left, rc.top);
  		pCallTip->resize(rc.Width(), rc.Height());
  	}
  }
  
  void ScintillaQt::AddToPopUp(const char *label,
                               int cmd,
                               bool enabled)
  {
  	QMenu *menu = static_cast<QMenu *>(popup.GetID());
  	QString text = QString::fromLatin1(label);
  
  	if (text.isEmpty()) {
  		menu->addSeparator();
  	} else {
  		QAction *action = menu->addAction(text);
  		action->setData(cmd);
  		action->setEnabled(enabled);
  	}
  
  	// Make sure the menu's signal is connected only once.
  	menu->disconnect();
  	connect(menu, SIGNAL(triggered(QAction *)),
  	        this, SLOT(execCommand(QAction *)));
  }
  
  sptr_t ScintillaQt::WndProc(unsigned int message, uptr_t wParam, sptr_t lParam)
  {
  	try {
  		switch (message) {
  
  		case SCI_SETIMEINTERACTION:
  			// Only inline IME supported on Qt
  			break;
  
  		case SCI_GRABFOCUS:
  			scrollArea->setFocus(Qt::OtherFocusReason);
  			break;
  
  		case SCI_GETDIRECTFUNCTION:
  			return reinterpret_cast<sptr_t>(DirectFunction);
  
  		case SCI_GETDIRECTPOINTER:
  			return reinterpret_cast<sptr_t>(this);
  
  #ifdef SCI_LEXER
  		case SCI_LOADLEXERLIBRARY:
  			LexerManager::GetInstance()->Load(reinterpret_cast<const char *>(lParam));
  			break;
  #endif
  
  		default:
  			return ScintillaBase::WndProc(message, wParam, lParam);
  		}
  	} catch (std::bad_alloc &) {
  		errorStatus = SC_STATUS_BADALLOC;
  	} catch (...) {
  		errorStatus = SC_STATUS_FAILURE;
  	}
  	return 0l;
  }
  
  sptr_t ScintillaQt::DefWndProc(unsigned int, uptr_t, sptr_t)
  {
  	return 0;
  }
  
  sptr_t ScintillaQt::DirectFunction(
      sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam)
  {
  	return reinterpret_cast<ScintillaQt *>(ptr)->WndProc(iMessage, wParam, lParam);
  }
  
  // Additions to merge in Scientific Toolworks widget structure
  
  void ScintillaQt::PartialPaint(const PRectangle &rect)
  {
  	rcPaint = rect;
  	paintState = painting;
  	PRectangle rcClient = GetClientRectangle();
  	paintingAllText = rcPaint.Contains(rcClient);
  
  	AutoSurface surface(this);
  	Paint(surface, rcPaint);
  	surface->Release();
  
  	if (paintState == paintAbandoned) {
  		// FIXME: Failure to paint the requested rectangle in each
  		// paint event causes flicker on some platforms (Mac?)
  		// Paint rect immediately.
  		paintState = painting;
  		paintingAllText = true;
  
  		AutoSurface surface(this);
  		Paint(surface, rcPaint);
  		surface->Release();
  
  		// Queue a full repaint.
  		scrollArea->viewport()->update();
  	}
  
  	paintState = notPainting;
  }
  
  void ScintillaQt::DragEnter(const Point &point)
  {
  	SetDragPosition(SPositionFromLocation(point,
  					      false, false, UserVirtualSpace()));
  }
  
  void ScintillaQt::DragMove(const Point &point)
  {
  	SetDragPosition(SPositionFromLocation(point,
  					      false, false, UserVirtualSpace()));
  }
  
  void ScintillaQt::DragLeave()
  {
  	SetDragPosition(SelectionPosition(invalidPosition));
  }
  
  void ScintillaQt::Drop(const Point &point, const QMimeData *data, bool move)
  {
  	QString text = data->text();
  	bool rectangular = IsRectangularInMime(data);
  	QByteArray bytes = BytesForDocument(text);
  	int len = bytes.length();
  
  	SelectionPosition movePos = SPositionFromLocation(point,
  				false, false, UserVirtualSpace());
  
  	DropAt(movePos, bytes, len, move, rectangular);
  }
  
  void ScintillaQt::timerEvent(QTimerEvent *event)
  {
  	for (TickReason tr=tickCaret; tr<=tickDwell; tr = static_cast<TickReason>(tr+1)) {
  		if (timers[tr] == event->timerId()) {
  			TickFor(tr);
  		}
  	}
  }
  