diff options
| author | nyamatongwe <unknown> | 2012-05-17 12:46:29 +1000 | 
|---|---|---|
| committer | nyamatongwe <unknown> | 2012-05-17 12:46:29 +1000 | 
| commit | bf8b35542c4be4b8eebb71ca2ad518da2e12b850 (patch) | |
| tree | eab094df23f84c33c7ed03f3412a63a060d31eac /qt/ScintillaEditBase/ScintillaEditBase.cpp | |
| parent | 29c92749f25f24795ecd9fec2b6705129ebbd654 (diff) | |
| download | scintilla-mirror-bf8b35542c4be4b8eebb71ca2ad518da2e12b850.tar.gz | |
Qt platform layer added. Based on an implementation from Jason Haslam
at Scientific Toolworks, Inc. with additions performed for Wingware.
Diffstat (limited to 'qt/ScintillaEditBase/ScintillaEditBase.cpp')
| -rw-r--r-- | qt/ScintillaEditBase/ScintillaEditBase.cpp | 635 | 
1 files changed, 635 insertions, 0 deletions
| diff --git a/qt/ScintillaEditBase/ScintillaEditBase.cpp b/qt/ScintillaEditBase/ScintillaEditBase.cpp new file mode 100644 index 000000000..b6cca8166 --- /dev/null +++ b/qt/ScintillaEditBase/ScintillaEditBase.cpp @@ -0,0 +1,635 @@ +// +//          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 +// ScintillaEditBase.cpp - Qt widget that wraps ScintillaQt and provides events and scrolling + +#include "ScintillaEditBase.h" +#include "ScintillaQt.h" +#include "PlatQt.h" + +#include <QApplication> +#include <QInputContext> +#include <QPainter> +#include <QScrollBar> +#include <QTextFormat> +#include <QVarLengthArray> + +#define INDIC_INPUTMETHOD 24 + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +ScintillaEditBase::ScintillaEditBase(QWidget *parent) +: QAbstractScrollArea(parent), sqt(0), preeditPos(-1), wheelDelta(0) +{ +	sqt = new ScintillaQt(this); + +	time.start(); + +	// Set Qt defaults. +	setAcceptDrops(true); +	setMouseTracking(true); +	setAutoFillBackground(false); +	setFrameStyle(QFrame::NoFrame); +	setFocusPolicy(Qt::StrongFocus); +	setAttribute(Qt::WA_StaticContents); +	setAttribute(Qt::WA_OpaquePaintEvent); +	setAttribute(Qt::WA_KeyCompression); +	setAttribute(Qt::WA_InputMethodEnabled); + +	connect(sqt, SIGNAL(notifyParent(SCNotification)), +	        this, SLOT(notifyParent(SCNotification))); + +	// Connect scroll bars. +	connect(verticalScrollBar(), SIGNAL(valueChanged(int)), +	        this, SLOT(scrollVertical(int))); +	connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), +	        this, SLOT(scrollHorizontal(int))); + +	// Connect pass-through signals. +	connect(sqt, SIGNAL(horizontalRangeChanged(int,int)), +	        this, SIGNAL(horizontalRangeChanged(int,int))); +	connect(sqt, SIGNAL(verticalRangeChanged(int,int)), +	        this, SIGNAL(verticalRangeChanged(int,int))); +	connect(sqt, SIGNAL(horizontalScrolled(int)), +	        this, SIGNAL(horizontalScrolled(int))); +	connect(sqt, SIGNAL(verticalScrolled(int)), +	        this, SIGNAL(verticalScrolled(int))); + +	connect(sqt, SIGNAL(notifyChange()), +	        this, SIGNAL(notifyChange())); + +	connect(sqt, SIGNAL(command(uptr_t, sptr_t)), +	        this, SLOT(event_command(uptr_t, sptr_t))); + +	connect(sqt, SIGNAL(aboutToCopy(QMimeData *)), +	        this, SIGNAL(aboutToCopy(QMimeData *))); +} + +ScintillaEditBase::~ScintillaEditBase() {} + +sptr_t ScintillaEditBase::send( +	unsigned int iMessage, +	uptr_t wParam, +	sptr_t lParam) const +{ +	return sqt->WndProc(iMessage, wParam, lParam); +} + +sptr_t ScintillaEditBase::sends( +    unsigned int iMessage, +    uptr_t wParam, +    const char *s) const +{ +	return sqt->WndProc(iMessage, wParam, (sptr_t)s); +} + +void ScintillaEditBase::scrollHorizontal(int value) +{ +	sqt->HorizontalScrollTo(value); +} + +void ScintillaEditBase::scrollVertical(int value) +{ +	sqt->ScrollTo(value); +} + +bool ScintillaEditBase::event(QEvent *event) +{ +	bool result = false; + +	if (event->type() == QEvent::KeyPress) { +		// Circumvent the tab focus convention. +		keyPressEvent(static_cast<QKeyEvent *>(event)); +		result = event->isAccepted(); +	} else { +		result = QAbstractScrollArea::event(event); +	} + +	return result; +} + +void ScintillaEditBase::paintEvent(QPaintEvent *event) +{ +	sqt->PartialPaint(PRectFromQRect(event->rect())); +} + +void ScintillaEditBase::wheelEvent(QWheelEvent *event) +{ +	if (event->orientation() == Qt::Horizontal) { +		if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) +			event->ignore(); +		else +			QAbstractScrollArea::wheelEvent(event); +	} else { +		if (event->modifiers() & Qt::ControlModifier) { +			// Zoom! We play with the font sizes in the styles. +			// Number of steps/line is ignored, we just care if sizing up or down +			if (event->delta() > 0) { +				sqt->KeyCommand(SCI_ZOOMIN); +			} else { +				sqt->KeyCommand(SCI_ZOOMOUT); +			} +		} else { +			// Ignore wheel events when the scroll bars are disabled. +			if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) { +				event->ignore(); +			} else { +				// Scroll +				QAbstractScrollArea::wheelEvent(event); +			} +		} +	} +} + +void ScintillaEditBase::focusInEvent(QFocusEvent *event) +{ +	sqt->SetFocusState(true); +	emit updateUi(); + +	QAbstractScrollArea::focusInEvent(event); +} + +void ScintillaEditBase::focusOutEvent(QFocusEvent *event) +{ +	sqt->SetFocusState(false); + +	QAbstractScrollArea::focusOutEvent(event); +} + +void ScintillaEditBase::resizeEvent(QResizeEvent *) +{ +	sqt->ChangeSize(); +	emit resized(); +} + +void ScintillaEditBase::keyPressEvent(QKeyEvent *event) +{ +	// All keystrokes containing the meta modifier are +	// assumed to be shortcuts not handled by scintilla. +	if (event->modifiers() & Qt::MetaModifier) { +		QAbstractScrollArea::keyPressEvent(event); +		emit keyPressed(event); +		return; +	} + +	int key = 0; +	switch (event->key()) { +		case Qt::Key_Down:          key = SCK_DOWN;     break; +		case Qt::Key_Up:            key = SCK_UP;       break; +		case Qt::Key_Left:          key = SCK_LEFT;     break; +		case Qt::Key_Right:         key = SCK_RIGHT;    break; +		case Qt::Key_Home:          key = SCK_HOME;     break; +		case Qt::Key_End:           key = SCK_END;      break; +		case Qt::Key_PageUp:        key = SCK_PRIOR;    break; +		case Qt::Key_PageDown:      key = SCK_NEXT;     break; +		case Qt::Key_Delete:        key = SCK_DELETE;   break; +		case Qt::Key_Insert:        key = SCK_INSERT;   break; +		case Qt::Key_Escape:        key = SCK_ESCAPE;   break; +		case Qt::Key_Backspace:     key = SCK_BACK;     break; +		case Qt::Key_Plus:          key = SCK_ADD;      break; +		case Qt::Key_Minus:         key = SCK_SUBTRACT; break; +		case Qt::Key_Backtab:       // fall through +		case Qt::Key_Tab:           key = SCK_TAB;      break; +		case Qt::Key_Enter:         // fall through +		case Qt::Key_Return:        key = SCK_RETURN;   break; +		case Qt::Key_Control:       key = 0;            break; +		case Qt::Key_Alt:           key = 0;            break; +		case Qt::Key_Shift:         key = 0;            break; +		case Qt::Key_Meta:          key = 0;            break; +		default:                    key = event->key(); break; +	} + +	bool shift = event->modifiers() & Qt::ShiftModifier; +	bool ctrl  = event->modifiers() & Qt::ControlModifier; +	bool alt   = event->modifiers() & Qt::AltModifier; + +	bool consumed = false; +	bool added = sqt->KeyDown(key, shift, ctrl, alt, &consumed) != 0; +	if (!consumed) +		consumed = added; + +	if (!consumed) { +		// Don't insert text if the control key was pressed unless +		// it was pressed in conjunction with alt for AltGr emulation. +		bool input = (!ctrl || alt); + +		// Additionally, on non-mac platforms, don't insert text +		// if the alt key was pressed unless control is also present. +		// On mac alt can be used to insert special characters. +#ifndef Q_WS_MAC +		input &= (!alt || ctrl); +#endif + +		QString text = event->text(); +		if (input && !text.isEmpty() && text[0].isPrint()) { +			QByteArray utext = sqt->BytesForDocument(text); +			sqt->AddCharUTF(utext.data(), utext.size()); +		} else { +			event->ignore(); +		} +	} + +	emit keyPressed(event); +} + +static int modifierTranslated(int sciModifier) +{ +	switch (sciModifier) { +		case SCMOD_SHIFT: +			return Qt::ShiftModifier; +		case SCMOD_CTRL: +			return Qt::ControlModifier; +		case SCMOD_ALT: +			return Qt::AltModifier; +		case SCMOD_SUPER: +			return Qt::MetaModifier; +		default: +			return 0; +	} +} + +void ScintillaEditBase::mousePressEvent(QMouseEvent *event) +{ +	Point pos = PointFromQPoint(event->pos()); + +	emit buttonPressed(event); + +	if (event->button() == Qt::MidButton && +	    QApplication::clipboard()->supportsSelection()) { +		SelectionPosition selPos = sqt->SPositionFromLocation( +					pos, false, false, sqt->UserVirtualSpace()); +		sqt->sel.Clear(); +		sqt->SetSelection(selPos, selPos); +		sqt->PasteFromMode(QClipboard::Selection); +		return; +	} + +	bool button = event->button() == Qt::LeftButton; + +	if (button) { +		bool shift = event->modifiers() & Qt::ShiftModifier; +		bool ctrl  = event->modifiers() & Qt::ControlModifier; +#ifdef Q_WS_X11 +		// On X allow choice of rectangular modifier since most window +		// managers grab alt + click for moving windows. +		bool alt   = event->modifiers() & modifierTranslated(sqt->rectangularSelectionModifier); +#else +		bool alt   = event->modifiers() & Qt::AltModifier; +#endif + +		sqt->ButtonDown(pos, time.elapsed(), shift, ctrl, alt); +	} +} + +void ScintillaEditBase::mouseReleaseEvent(QMouseEvent *event) +{ +	Point point = PointFromQPoint(event->pos()); +	bool ctrl  = event->modifiers() & Qt::ControlModifier; +	if (event->button() == Qt::LeftButton) +		sqt->ButtonUp(point, time.elapsed(), ctrl); + +	int pos = send(SCI_POSITIONFROMPOINT, point.x, point.y); +	int line = send(SCI_LINEFROMPOSITION, pos); +	int modifiers = event->modifiers(); + +	emit textAreaClicked(line, modifiers); +	emit buttonReleased(event); +} + +void ScintillaEditBase::mouseDoubleClickEvent(QMouseEvent *event) +{ +	// Scintilla does its own double-click detection. +	mousePressEvent(event); +} + +void ScintillaEditBase::mouseMoveEvent(QMouseEvent *event) +{ +	Point pos = PointFromQPoint(event->pos()); +	sqt->ButtonMove(pos); +} + +void ScintillaEditBase::contextMenuEvent(QContextMenuEvent *event) +{ +	Point pos = PointFromQPoint(event->globalPos()); +	Point pt = PointFromQPoint(event->pos()); +	if (!sqt->PointInSelection(pt)) +		sqt->SetEmptySelection(sqt->PositionFromLocation(pt)); +	sqt->ContextMenu(pos); +} + +void ScintillaEditBase::dragEnterEvent(QDragEnterEvent *event) +{ +	if (event->mimeData()->hasText()) { +		event->acceptProposedAction(); + +		Point point = PointFromQPoint(event->pos()); +		sqt->DragEnter(point); +	} else { +		event->ignore(); +	} +} + +void ScintillaEditBase::dragLeaveEvent(QDragLeaveEvent * /* event */) +{ +	sqt->DragLeave(); +} + +void ScintillaEditBase::dragMoveEvent(QDragMoveEvent *event) +{ +	if (event->mimeData()->hasText()) { +		event->acceptProposedAction(); + +		Point point = PointFromQPoint(event->pos()); +		sqt->DragMove(point); +	} else { +		event->ignore(); +	} +} + +void ScintillaEditBase::dropEvent(QDropEvent *event) +{ +	if (event->mimeData()->hasText()) { +		event->acceptProposedAction(); + +		Point point = PointFromQPoint(event->pos()); +		bool move = (event->source() == this && +                 event->proposedAction() == Qt::MoveAction); +		sqt->Drop(point, event->mimeData(), move); +	} else { +		event->ignore(); +	} +} + +void ScintillaEditBase::inputMethodEvent(QInputMethodEvent *event) +{ +	// Clear the current selection. +	sqt->ClearSelection(); +	if (preeditPos >= 0) +		sqt->SetSelection(preeditPos, preeditPos); + +	// Insert the commit string. +	if (!event->commitString().isEmpty() || event->replacementLength()) { +		// Select the text to be removed. +		int commitPos = send(SCI_GETCURRENTPOS); +		int start = commitPos + event->replacementStart(); +		int end = start + event->replacementLength(); +		sqt->SetSelection(start, end); + +		// Replace the selection with the commit string. +		QByteArray commitBytes = sqt->BytesForDocument(event->commitString()); +		char *commitData = commitBytes.data(); +		sqt->AddCharUTF(commitData, strlen(commitData)); +	} + +	// Select the previous preedit string. +	int pos = send(SCI_GETCURRENTPOS); +	int length = sqt->BytesForDocument(preeditString).length(); +	sqt->SetSelection(pos, pos + length); + +	// Replace the selection with the new preedit string. +	QByteArray bytes = sqt->BytesForDocument(event->preeditString()); +	char *data = bytes.data(); +	bool recording = sqt->recordingMacro; +	sqt->recordingMacro = false; +	send(SCI_SETUNDOCOLLECTION, false); +	sqt->AddCharUTF(data, strlen(data)); +	send(SCI_SETUNDOCOLLECTION, true); +	sqt->recordingMacro = recording; +	sqt->SetSelection(pos, pos); + +	// Store the state of the current preedit string. +	preeditString = event->preeditString(); +	preeditPos = !preeditString.isEmpty() ? send(SCI_GETCURRENTPOS) : -1; + +	if (!preeditString.isEmpty()) { +		// Apply attributes to the preedit string. +		int indicNum = 0; +		sqt->ShowCaretAtCurrentPosition(); +		foreach (QInputMethodEvent::Attribute a, event->attributes()) { +			QString prefix = preeditString.left(a.start); +			QByteArray prefixBytes = sqt->BytesForDocument(prefix); +			int prefixLength = prefixBytes.length(); +			int caretPos = preeditPos + prefixLength; + +			if (a.type == QInputMethodEvent::Cursor) { +				sqt->SetSelection(caretPos, caretPos); +				if (!a.length) +					sqt->DropCaret(); + +			} else if (a.type == QInputMethodEvent::TextFormat) { +				Q_ASSERT(a.value.canConvert(QVariant::TextFormat)); +				QTextFormat format = a.value.value<QTextFormat>(); +				Q_ASSERT(format.isCharFormat()); +				QTextCharFormat charFormat = format.toCharFormat(); + +				QString sub = preeditString.mid(a.start, a.length); +				QByteArray subBytes = sqt->BytesForDocument(sub); +				int subLength = subBytes.length(); + +				if (charFormat.underlineStyle() != QTextCharFormat::NoUnderline) { +					// Set temporary indicator for underline style. +					QColor uc = charFormat.underlineColor(); +					int style = INDIC_PLAIN; +					if (charFormat.underlineStyle() == QTextCharFormat::DashUnderline) +						style = INDIC_DASH; +					send(SCI_INDICSETSTYLE, INDIC_INPUTMETHOD + indicNum, style); +					send(SCI_INDICSETFORE, INDIC_INPUTMETHOD + indicNum, uc.rgb()); +					send(SCI_SETINDICATORCURRENT, INDIC_INPUTMETHOD + indicNum); +					send(SCI_INDICATORFILLRANGE, caretPos, subLength); +					indicNum++; +				} +			} +		} +	} +} + +QVariant ScintillaEditBase::inputMethodQuery(Qt::InputMethodQuery query) const +{ +	int pos = send(SCI_GETCURRENTPOS); +	int line = send(SCI_LINEFROMPOSITION, pos); + +	switch (query) { +		case Qt::ImMicroFocus: +		{ +			int startPos = (preeditPos >= 0) ? preeditPos : pos; +			Point pt = sqt->LocationFromPosition(startPos); +			int width = send(SCI_GETCARETWIDTH); +			int height = send(SCI_TEXTHEIGHT, line); +			return QRect(pt.x, pt.y, width, height); +		} + +		case Qt::ImFont: +		{ +			char fontName[64]; +			int style = send(SCI_GETSTYLEAT, pos); +			int len = send(SCI_STYLEGETFONT, style, (long)fontName); +			int size = send(SCI_STYLEGETSIZE, style); +			bool italic = send(SCI_STYLEGETITALIC, style); +			int weight = send(SCI_STYLEGETBOLD, style) ? QFont::Bold : -1; +			return QFont(QString::fromUtf8(fontName, len), size, weight, italic); +		} + +		case Qt::ImCursorPosition: +		{ +			int paraStart = sqt->pdoc->ParaUp(pos); +			return pos - paraStart; +		} + +		case Qt::ImSurroundingText: +		{ +			int paraStart = sqt->pdoc->ParaUp(pos); +			int paraEnd = sqt->pdoc->ParaDown(pos); +			QVarLengthArray<char,1024> buffer(paraEnd - paraStart + 1); + +			Sci_CharacterRange charRange; +			charRange.cpMin = paraStart; +			charRange.cpMax = paraEnd; + +			Sci_TextRange textRange; +			textRange.chrg = charRange; +			textRange.lpstrText = buffer.data(); + +			send(SCI_GETTEXTRANGE, 0, (long)&textRange); + +			return sqt->StringFromDocument(buffer.constData()); +		} + +		case Qt::ImCurrentSelection: +		{ +			QVarLengthArray<char,1024> buffer(send(SCI_GETSELTEXT)); +			send(SCI_GETSELTEXT, 0, (long)buffer.data()); + +			return sqt->StringFromDocument(buffer.constData()); +		} + +		default: +			return QVariant(); +	} +} + +void ScintillaEditBase::notifyParent(SCNotification scn) +{ +	emit notify(&scn); +	switch (scn.nmhdr.code) { +		case SCN_STYLENEEDED: +			emit styleNeeded(scn.position); +			break; + +		case SCN_CHARADDED: +			emit charAdded(scn.ch); +			break; + +		case SCN_SAVEPOINTREACHED: +			emit savePointChanged(false); +			break; + +		case SCN_SAVEPOINTLEFT: +			emit savePointChanged(true); +			break; + +		case SCN_MODIFYATTEMPTRO: +			emit modifyAttemptReadOnly(); +			break; + +		case SCN_KEY: +			emit key(scn.ch); +			break; + +		case SCN_DOUBLECLICK: +			emit doubleClick(scn.position, scn.line); +			break; + +		case SCN_UPDATEUI: +			emit updateUi(); +			break; + +		case SCN_MODIFIED: +		{ +			bool added = scn.modificationType & SC_MOD_INSERTTEXT; +			bool deleted = scn.modificationType & SC_MOD_DELETETEXT; + +			int length = send(SCI_GETTEXTLENGTH); +			bool firstLineAdded = (added && length == 1) || +			                      (deleted && length == 0); + +			if (scn.linesAdded != 0) { +				emit linesAdded(scn.linesAdded); +			} else if (firstLineAdded) { +				emit linesAdded(added ? 1 : -1); +			} + +			const QByteArray bytes = QByteArray::fromRawData(scn.text, scn.length); +			emit modified(scn.modificationType, scn.position, scn.length, +			              scn.linesAdded, bytes, scn.line, +			              scn.foldLevelNow, scn.foldLevelPrev); +			break; +		} + +		case SCN_MACRORECORD: +			emit macroRecord(scn.message, scn.wParam, scn.lParam); +			break; + +		case SCN_MARGINCLICK: +			emit marginClicked(scn.position, scn.modifiers, scn.margin); +			break; + +		case SCN_NEEDSHOWN: +			emit needShown(scn.position, scn.length); +			break; + +		case SCN_PAINTED: +			emit painted(); +			break; + +		case SCN_USERLISTSELECTION: +			emit userListSelection(); +			break; + +		case SCN_URIDROPPED: +			emit uriDropped(); +			break; + +		case SCN_DWELLSTART: +			emit dwellStart(scn.x, scn.y); +			break; + +		case SCN_DWELLEND: +			emit dwellEnd(scn.x, scn.y); +			break; + +		case SCN_ZOOM: +			emit zoom(send(SCI_GETZOOM)); +			break; + +		case SCN_HOTSPOTCLICK: +			emit hotSpotClick(scn.position, scn.modifiers); +			break; + +		case SCN_HOTSPOTDOUBLECLICK: +			emit hotSpotDoubleClick(scn.position, scn.modifiers); +			break; + +		case SCN_CALLTIPCLICK: +			emit callTipClick(); +			break; + +		case SCN_AUTOCSELECTION: +			emit autoCompleteSelection(scn.lParam, QString::fromUtf8(scn.text)); +			break; + +		default: +			return; +	} +} + +void ScintillaEditBase::event_command(uptr_t wParam, sptr_t lParam) +{ +	emit command(wParam, lParam); +} | 
