diff options
22 files changed, 4838 insertions, 0 deletions
| diff --git a/qt/README b/qt/README new file mode 100644 index 000000000..a39008b38 --- /dev/null +++ b/qt/README @@ -0,0 +1,36 @@ +README for building of Scintilla on Qt
 +
 +There are three different Scintilla libraries that can be produced:
 +
 +	ScintillaEditBase
 +A basic widget callable from C++ which is small and can be used just as is 
 +or with higher level functionality added.
 +
 +	ScintillaEdit
 +A more complete C++ widget with a method for every Scintilla API and a
 +secondary API allowing direct access to document objects.
 +
 +	ScintillaEditPy
 +A Python callable version of ScintillaEdit using the PySide bindings.
 +
 +	Building a library
 +
 +ScintillaEditBase can be built without performing any generation steps.
 +The ScintillaEditBase/ScintillaEditBase.pro project can be loaded into 
 +Qt Creator and the "Build All" command performed.
 +Alternatively, run "qmake" to build make files and then use the platform
 +make to build. Most commonly, use "make" on Unix and "nmake"
 +on Windows.
 +
 +ScintillaEdit requires a generation command be run first. From the 
 +ScintillaEdit directory:
 +
 +python WidgetGen.py
 +
 +After the generation command has run, the ScintillaEdit.h and
 +ScintillaEdit.cpp files will have been populated with the Scintilla API
 +methods. 
 +To build, use Qt Creator or qmake and make as for ScintillaEditBase.
 +
 +ScintillaEditPy is more complex and instructions are found in 
 +ScintillaEditPy/README.
 diff --git a/qt/ScintillaEdit/ScintillaDocument.cpp b/qt/ScintillaEdit/ScintillaDocument.cpp new file mode 100644 index 000000000..801f6a385 --- /dev/null +++ b/qt/ScintillaEdit/ScintillaDocument.cpp @@ -0,0 +1,286 @@ +// ScintillaDocument.cpp +// Wrapper for Scintilla document object so it can be manipulated independently. +// Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware + +#include <vector> +#include <map> + +#include "ScintillaDocument.h" + +#include "Platform.h" + +#include "ILexer.h" +#include "Scintilla.h" + +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" +#include "ContractionState.h" +#include "CellBuffer.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "XPM.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "CharClassify.h" +#include "Decoration.h" +#include "Document.h" + +class WatcherHelper : public DocWatcher { +	ScintillaDocument *owner; +public: +	WatcherHelper(ScintillaDocument *owner_); +	virtual ~WatcherHelper(); + +	void NotifyModifyAttempt(Document *doc, void *userData); +	void NotifySavePoint(Document *doc, void *userData, bool atSavePoint); +	void NotifyModified(Document *doc, DocModification mh, void *userData); +	void NotifyDeleted(Document *doc, void *userData); +	void NotifyStyleNeeded(Document *doc, void *userData, int endPos); +	void NotifyLexerChanged(Document *doc, void *userData); +	void NotifyErrorOccurred(Document *doc, void *userData, int status); +}; + +WatcherHelper::WatcherHelper(ScintillaDocument *owner_) : owner(owner_) { +} + +WatcherHelper::~WatcherHelper() { +} + +void WatcherHelper::NotifyModifyAttempt(Document *, void *) { +	owner->emit_modify_attempt(); +} + +void WatcherHelper::NotifySavePoint(Document *, void *, bool atSavePoint) { +	owner->emit_save_point(atSavePoint); +} + +void WatcherHelper::NotifyModified(Document *, DocModification mh, void *) { +    int length = mh.length; +    if (!mh.text) +        length = 0; +    QByteArray ba = QByteArray::fromRawData(mh.text, length); +    owner->emit_modified(mh.position, mh.modificationType, ba, length, +        mh.linesAdded, mh.line, mh.foldLevelNow, mh.foldLevelPrev); +} + +void WatcherHelper::NotifyDeleted(Document *, void *) { +} + +void WatcherHelper::NotifyStyleNeeded(Document *, void *, int endPos) { +	owner->emit_style_needed(endPos); +} + +void WatcherHelper::NotifyLexerChanged(Document *, void *) { +    owner->emit_lexer_changed(); +} + +void WatcherHelper::NotifyErrorOccurred(Document *, void *, int status) { +    owner->emit_error_occurred(status); +} + +ScintillaDocument::ScintillaDocument(QObject *parent, void *pdoc_) : +    QObject(parent), pdoc(pdoc_), docWatcher(0) { +    if (!pdoc) { +        pdoc = new Document(); +    } +    docWatcher = new WatcherHelper(this); +    ((Document *)pdoc)->AddRef(); +    ((Document *)pdoc)->AddWatcher(docWatcher, pdoc); +} + +ScintillaDocument::~ScintillaDocument() { +    Document *doc = static_cast<Document *>(pdoc); +    if (doc) { +        doc->RemoveWatcher(docWatcher, doc); +        doc->Release(); +    } +    pdoc = NULL; +    delete docWatcher; +    docWatcher = NULL; +} + +void *ScintillaDocument::pointer() { +	return pdoc; +} + +int ScintillaDocument::line_from_position(int pos) { +    return ((Document *)pdoc)->LineFromPosition(pos); +} + +bool ScintillaDocument::is_cr_lf(int pos) { +    return ((Document *)pdoc)->IsCrLf(pos); +} + +bool ScintillaDocument::delete_chars(int pos, int len) { +    return ((Document *)pdoc)->DeleteChars(pos, len); +} + +int ScintillaDocument::undo() { +    return ((Document *)pdoc)->Undo(); +} + +int ScintillaDocument::redo() { +    return ((Document *)pdoc)->Redo(); +} + +bool ScintillaDocument::can_undo() { +    return ((Document *)pdoc)->CanUndo(); +} + +bool ScintillaDocument::can_redo() { +    return ((Document *)pdoc)->CanRedo(); +} + +void ScintillaDocument::delete_undo_history() { +    ((Document *)pdoc)->DeleteUndoHistory(); +} + +bool ScintillaDocument::set_undo_collection(bool collect_undo) { +    return ((Document *)pdoc)->SetUndoCollection(collect_undo); +} + +bool ScintillaDocument::is_collecting_undo() { +    return ((Document *)pdoc)->IsCollectingUndo(); +} + +void ScintillaDocument::begin_undo_action() { +    ((Document *)pdoc)->BeginUndoAction(); +} + +void ScintillaDocument::end_undo_action() { +    ((Document *)pdoc)->EndUndoAction(); +} + +void ScintillaDocument::set_save_point() { +    ((Document *)pdoc)->SetSavePoint(); +} + +bool ScintillaDocument::is_save_point() { +    return ((Document *)pdoc)->IsSavePoint(); +} + +void ScintillaDocument::set_read_only(bool read_only) { +    ((Document *)pdoc)->SetReadOnly(read_only); +} + +bool ScintillaDocument::is_read_only() { +    return ((Document *)pdoc)->IsReadOnly(); +} + +void ScintillaDocument::insert_string(int position, QByteArray &str) { +    ((Document *)pdoc)->InsertString(position, str.data(), str.size()); +} + +QByteArray ScintillaDocument::get_char_range(int position, int length) { +    Document *doc = (Document *)pdoc; + +    if (position < 0 || length <= 0 || position + length > doc->Length()) +        return QByteArray(); + +    QByteArray ba(length, '\0'); +    doc->GetCharRange(ba.data(), position, length); +    return ba; +} + +char ScintillaDocument::style_at(int position) { +    return ((Document *)pdoc)->StyleAt(position); +} + +int ScintillaDocument::line_start(int lineno) { +    return ((Document *)pdoc)->LineStart(lineno); +} + +int ScintillaDocument::line_end(int lineno) { +    return ((Document *)pdoc)->LineEnd(lineno); +} + +int ScintillaDocument::line_end_position(int pos) { +    return ((Document *)pdoc)->LineEndPosition(pos); +} + +int ScintillaDocument::length() { +    return ((Document *)pdoc)->Length(); +} + +int ScintillaDocument::lines_total() { +    return ((Document *)pdoc)->LinesTotal(); +} + +void ScintillaDocument::start_styling(int position, char flags) { +    ((Document *)pdoc)->StartStyling(position, flags); +} + +bool ScintillaDocument::set_style_for(int length, char style) { +    return ((Document *)pdoc)->SetStyleFor(length, style); +} + +int ScintillaDocument::get_end_styled() { +    return ((Document *)pdoc)->GetEndStyled(); +} + +void ScintillaDocument::ensure_styled_to(int position) { +    ((Document *)pdoc)->EnsureStyledTo(position); +} + +void ScintillaDocument::set_current_indicator(int indic) { +    ((Document *)pdoc)->decorations.SetCurrentIndicator(indic); +} + +void ScintillaDocument::decoration_fill_range(int position, int value, int fillLength) { +    ((Document *)pdoc)->DecorationFillRange(position, value, fillLength); +} + +int ScintillaDocument::decorations_value_at(int indic, int position) { +    return ((Document *)pdoc)->decorations.ValueAt(indic, position); +} + +int ScintillaDocument::decorations_start(int indic, int position) { +    return ((Document *)pdoc)->decorations.Start(indic, position); +} + +int ScintillaDocument::decorations_end(int indic, int position) { +    return ((Document *)pdoc)->decorations.End(indic, position); +} + +int ScintillaDocument::get_code_page() { +    return ((Document *)pdoc)->CodePage(); +} + +void ScintillaDocument::set_code_page(int code_page) { +    ((Document *)pdoc)->dbcsCodePage = code_page; +} + +int ScintillaDocument::move_position_outside_char(int pos, int move_dir, bool check_line_end) { +    return ((Document *)pdoc)->MovePositionOutsideChar(pos, move_dir, check_line_end); +} + +// Signal emitters + +void ScintillaDocument::emit_modify_attempt() { +    emit modify_attempt(); +} + +void ScintillaDocument::emit_save_point(bool atSavePoint) { +    emit save_point(atSavePoint); +} + +void ScintillaDocument::emit_modified(int position, int modification_type, const QByteArray& text, int length, +    int linesAdded, int line, int foldLevelNow, int foldLevelPrev) { +    emit modified(position, modification_type, text, length, +        linesAdded, line, foldLevelNow, foldLevelPrev); +} + +void ScintillaDocument::emit_style_needed(int pos) { +    emit style_needed(pos); +} + +void ScintillaDocument::emit_lexer_changed() { +    emit lexer_changed(); +} + +void ScintillaDocument::emit_error_occurred(int status) { +    emit error_occurred(status); +} + diff --git a/qt/ScintillaEdit/ScintillaDocument.h b/qt/ScintillaEdit/ScintillaDocument.h new file mode 100644 index 000000000..3cf16e334 --- /dev/null +++ b/qt/ScintillaEdit/ScintillaDocument.h @@ -0,0 +1,97 @@ +// ScintillaDocument.h +// Wrapper for Scintilla document object so it can be manipulated independently. +// Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware + +#ifndef SCINTILLADOCUMENT_H +#define SCINTILLADOCUMENT_H + +#include <QObject> + +class WatcherHelper; + +#ifndef EXPORT_IMPORT_API +#ifdef WIN32 +#ifdef MAKING_LIBRARY +#define EXPORT_IMPORT_API __declspec(dllexport) +#else +// Defining dllimport upsets moc +#define EXPORT_IMPORT_API __declspec(dllimport) +//#define EXPORT_IMPORT_API +#endif +#else +#define EXPORT_IMPORT_API +#endif +#endif + +class EXPORT_IMPORT_API ScintillaDocument : public QObject +{ +    Q_OBJECT + +    void *pdoc; +    WatcherHelper *docWatcher; + +public: +    explicit ScintillaDocument(QObject *parent = 0, void *pdoc_=0); +    virtual ~ScintillaDocument(); +    void *pointer(); + +    int line_from_position(int pos); +    bool is_cr_lf(int pos); +    bool delete_chars(int pos, int len); +    int undo(); +    int redo(); +    bool can_undo(); +    bool can_redo(); +    void delete_undo_history(); +    bool set_undo_collection(bool collect_undo); +    bool is_collecting_undo(); +    void begin_undo_action(); +    void end_undo_action(); +    void set_save_point(); +    bool is_save_point(); +    void set_read_only(bool read_only); +    bool is_read_only(); +    void insert_string(int position, QByteArray &str); +    QByteArray get_char_range(int position, int length); +    char style_at(int position); +    int line_start(int lineno); +    int line_end(int lineno); +    int line_end_position(int pos); +    int length(); +    int lines_total(); +    void start_styling(int position, char flags); +    bool set_style_for(int length, char style); +    int get_end_styled(); +    void ensure_styled_to(int position); +    void set_current_indicator(int indic); +    void decoration_fill_range(int position, int value, int fillLength); +    int decorations_value_at(int indic, int position); +    int decorations_start(int indic, int position); +    int decorations_end(int indic, int position); +    int get_code_page(); +    void set_code_page(int code_page); +    int move_position_outside_char(int pos, int move_dir, bool check_line_end); + +private: +    void emit_modify_attempt(); +    void emit_save_point(bool atSavePoint); +    void emit_modified(int position, int modification_type, const QByteArray& text, int length, +	int linesAdded, int line, int foldLevelNow, int foldLevelPrev); +    void emit_style_needed(int pos); +    void emit_lexer_changed(); +    void emit_error_occurred(int status); + +signals: +    void modify_attempt(); +    void save_point(bool atSavePoint); +    void modified(int position, int modification_type, const QByteArray& text, int length, +	int linesAdded, int line, int foldLevelNow, int foldLevelPrev); +    void style_needed(int pos); +    void lexer_changed(); +    void error_occurred(int status); + +    friend class WatcherHelper; + +}; + +#endif // SCINTILLADOCUMENT_H diff --git a/qt/ScintillaEdit/ScintillaEdit.cpp.template b/qt/ScintillaEdit/ScintillaEdit.cpp.template new file mode 100644 index 000000000..431e79c4e --- /dev/null +++ b/qt/ScintillaEdit/ScintillaEdit.cpp.template @@ -0,0 +1,59 @@ +// ScintillaEdit.cpp +// Extended version of ScintillaEditBase with a method for each API +// Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware + +#include "ScintillaEdit.h" + +ScintillaEdit::ScintillaEdit(QWidget *parent) : ScintillaEditBase(parent) { +} + +ScintillaEdit::~ScintillaEdit() { +} + +QByteArray ScintillaEdit::TextReturner(int message, uptr_t wParam) const { +    int length = send(message, wParam, 0); +    QByteArray ba(length, '\0'); +    send(message, wParam, (sptr_t)ba.data()); +    // Remove extra NULs +    if (ba.size() > 0 && ba.at(ba.size()-1) == 0) +        ba.chop(1); +    return ba; +} + +QPair<int, int>ScintillaEdit::find_text(int flags, const char *text, int cpMin, int cpMax) { +    struct TextToFind ft = {{0, 0}, 0, {0, 0}}; +    ft.chrg.cpMin = cpMin; +    ft.chrg.cpMax = cpMax; +    ft.chrgText.cpMin = cpMin; +    ft.chrgText.cpMax = cpMax; +    ft.lpstrText = const_cast<char *>(text); + +    send(SCI_FINDTEXT, flags, (uptr_t) (&ft)); + +    return QPair<int,int>(ft.chrgText.cpMin, ft.chrgText.cpMax); +} + +QByteArray ScintillaEdit::get_text_range(int start, int end) { +    if (start > end) +        start = end; + +    int length = end-start; +    QByteArray ba(length+1, '\0'); +    struct TextRange tr = {{start, end}, ba.data()}; + +    length = send(SCI_GETTEXTRANGE, 0, (sptr_t)&tr); +    ba.chop(1); // Remove extra NUL + +    return ba; +} + +ScintillaDocument *ScintillaEdit::get_doc() { +    return new ScintillaDocument(0, (void *)send(SCI_GETDOCPOINTER, 0, 0)); +} + +void ScintillaEdit::set_doc(ScintillaDocument *pdoc_) { +    send(SCI_SETDOCPOINTER, 0, (sptr_t)(pdoc_->pointer())); +} + +/* ++Autogenerated -- start of section automatically generated from Scintilla.iface */ +/* --Autogenerated -- end of section automatically generated from Scintilla.iface */ diff --git a/qt/ScintillaEdit/ScintillaEdit.h.template b/qt/ScintillaEdit/ScintillaEdit.h.template new file mode 100644 index 000000000..32c8a7726 --- /dev/null +++ b/qt/ScintillaEdit/ScintillaEdit.h.template @@ -0,0 +1,63 @@ +// ScintillaEdit.h +// Extended version of ScintillaEditBase with a method for each API +// Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware + +#ifndef SCINTILLAEDIT_H +#define SCINTILLAEDIT_H + +#include <QPair> + +#include "ScintillaEditBase.h" +#include "ScintillaDocument.h" + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +#ifndef EXPORT_IMPORT_API +#ifdef WIN32 +#ifdef MAKING_LIBRARY +#define EXPORT_IMPORT_API __declspec(dllexport) +#else +// Defining dllimport upsets moc +#define EXPORT_IMPORT_API __declspec(dllimport) +//#define EXPORT_IMPORT_API +#endif +#else +#define EXPORT_IMPORT_API +#endif +#endif + +class EXPORT_IMPORT_API ScintillaEdit : public ScintillaEditBase { +	Q_OBJECT + +public: +	ScintillaEdit(QWidget *parent = 0); +	virtual ~ScintillaEdit(); + +	QByteArray TextReturner(int message, uptr_t wParam) const; + +	QPair<int, int>find_text(int flags, const char *text, int cpMin, int cpMax); +	QByteArray get_text_range(int start, int end); +        ScintillaDocument *get_doc(); +        void set_doc(ScintillaDocument *pdoc_); + +	// Same as previous two methods but with Qt style names +	QPair<int, int>findText(int flags, const char *text, int cpMin, int cpMax) { +		return find_text(flags, text, cpMin, cpMax); +	} + +	QByteArray textRange(int start, int end) { +		return get_text_range(start, end); +	} + +/* ++Autogenerated -- start of section automatically generated from Scintilla.iface */ +/* --Autogenerated -- end of section automatically generated from Scintilla.iface */ + +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif /* SCINTILLAEDIT_H */ diff --git a/qt/ScintillaEdit/ScintillaEdit.pro b/qt/ScintillaEdit/ScintillaEdit.pro new file mode 100644 index 000000000..d6422ac54 --- /dev/null +++ b/qt/ScintillaEdit/ScintillaEdit.pro @@ -0,0 +1,72 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-05-05T12:41:23 +# +#------------------------------------------------- + +QT       += core gui + +TARGET = ScintillaEdit +TEMPLATE = lib +CONFIG += lib_bundle + +VERSION = 3.1.0 + +SOURCES += \ +    ScintillaEdit.cpp \ +    ScintillaDocument.cpp \ +    ../ScintillaEditBase/PlatQt.cpp \ +    ../ScintillaEditBase/ScintillaQt.cpp \ +    ../ScintillaEditBase/ScintillaEditBase.cpp \ +    ../../src/XPM.cxx \ +    ../../src/ViewStyle.cxx \ +    ../../src/UniConversion.cxx \ +    ../../src/Style.cxx \ +    ../../src/Selection.cxx \ +    ../../src/ScintillaBase.cxx \ +    ../../src/RunStyles.cxx \ +    ../../src/RESearch.cxx \ +    ../../src/PositionCache.cxx \ +    ../../src/PerLine.cxx \ +    ../../src/LineMarker.cxx \ +    ../../src/KeyMap.cxx \ +    ../../src/Indicator.cxx \ +    ../../src/ExternalLexer.cxx \ +    ../../src/Editor.cxx \ +    ../../src/Document.cxx \ +    ../../src/Decoration.cxx \ +    ../../src/ContractionState.cxx \ +    ../../src/CharClassify.cxx \ +    ../../src/CellBuffer.cxx \ +    ../../src/Catalogue.cxx \ +    ../../src/CallTip.cxx \ +    ../../src/AutoComplete.cxx \ +    ../../lexlib/WordList.cxx \ +    ../../lexlib/StyleContext.cxx \ +    ../../lexlib/PropSetSimple.cxx \ +    ../../lexlib/LexerSimple.cxx \ +    ../../lexlib/LexerNoExceptions.cxx \ +    ../../lexlib/LexerModule.cxx \ +    ../../lexlib/LexerBase.cxx \ +    ../../lexlib/CharacterSet.cxx \ +    ../../lexlib/Accessor.cxx \ +    ../../lexers/*.cxx + +HEADERS  += \ +    ScintillaEdit.h \ +    ScintillaDocument.h \ +    ../ScintillaEditBase/ScintillaEditBase.h \ +    ../ScintillaEditBase/ScintillaQt.h + +OTHER_FILES += + +INCLUDEPATH += ../ScintillaEditBase ../../include ../../src ../../lexlib + +DEFINES += SCINTILLA_QT=1 MAKING_LIBRARY=1 SCI_LEXER=1 _CRT_SECURE_NO_DEPRECATE=1 + +DESTDIR = ../../bin +DLLDESTDIR = ../../bin + +macx { +	QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../Frameworks/ +} diff --git a/qt/ScintillaEdit/WidgetGen.py b/qt/ScintillaEdit/WidgetGen.py new file mode 100644 index 000000000..8065b5f9d --- /dev/null +++ b/qt/ScintillaEdit/WidgetGen.py @@ -0,0 +1,275 @@ +#!/usr/bin/env python +# WidgetGen.py - regenerate the ScintillaWidgetCpp.cpp and ScintillaWidgetCpp.h files +# Check that API includes all gtkscintilla2 functions + +import sys +import os +import getopt + +scintillaDirectory = "../.." +scintillaIncludeDirectory = os.path.join(scintillaDirectory, "include") +sys.path.append(scintillaIncludeDirectory) +import Face + +def Contains(s,sub): +	return s.find(sub) != -1 + +def underscoreName(s): +	# Name conversion fixes to match gtkscintilla2 +	irregular = ['WS', 'EOL', 'AutoC', 'KeyWords', 'BackSpace', 'UnIndents', 'RE', 'RGBA'] +	for word in irregular: +		replacement = word[0] + word[1:].lower() +		s = s.replace(word, replacement) + +	out = "" +	for c in s: +		if c.isupper(): +			if out: +				out += "_" +			out += c.lower() +		else: +			out += c +	return out + +def normalisedName(s, options, role=None): +	if options["qtStyle"]: +		if role == "get": +			s = s.replace("Get", "") +		return s[0].lower() + s[1:] +	else: +		return underscoreName(s) + +typeAliases = { +	"position": "int", +	"colour": "int", +	"keymod": "int", +	"string": "const char *", +	"stringresult": "const char *", +	"cells": "const char *", +} + +def cppAlias(s): +	if s in typeAliases: +		return typeAliases[s] +	else: +		return s + +understoodTypes = ["", "void", "int", "bool", "position", +	"colour", "keymod", "string", "stringresult", "cells"] + +def checkTypes(name, v): +	understandAllTypes = True +	if v["ReturnType"] not in understoodTypes: +		#~ print("Do not understand", v["ReturnType"], "for", name) +		understandAllTypes = False +	if v["Param1Type"] not in understoodTypes: +		#~ print("Do not understand", v["Param1Type"], "for", name) +		understandAllTypes = False +	if v["Param2Type"] not in understoodTypes: +		#~ print("Do not understand", v["Param2Type"], "for", name) +		understandAllTypes = False +	return understandAllTypes + +def arguments(v, stringResult, options): +	ret = "" +	p1Type = cppAlias(v["Param1Type"]) +	if p1Type: +		ret = ret + p1Type + " " + normalisedName(v["Param1Name"], options) +	p2Type = cppAlias(v["Param2Type"]) +	if p2Type and not stringResult: +		if p1Type: +			ret = ret + ", " +		ret = ret + p2Type + " " + normalisedName(v["Param2Name"], options) +	return ret + +def printPyFile(f,out, options): +	for name in f.order: +		v = f.features[name] +		if v["Category"] != "Deprecated": +			feat = v["FeatureType"] +			if feat in ["val"]: +				out.write(name + "=" + v["Value"] + "\n") +			if feat in ["evt"]: +				out.write("SCN_" + name.upper() + "=" + v["Value"] + "\n") + +def printHFile(f,out, options): +	for name in f.order: +		v = f.features[name] +		if v["Category"] != "Deprecated": +			feat = v["FeatureType"] +			if feat in ["fun", "get", "set"]: +				if checkTypes(name, v): +					constDeclarator = " const" if feat == "get" else "" +					returnType = cppAlias(v["ReturnType"]) +					stringResult = v["Param2Type"] == "stringresult" +					if stringResult: +						returnType = "QByteArray" +					out.write("\t" + returnType + " " + normalisedName(name, options, feat) + "(") +					out.write(arguments(v, stringResult, options)) +					out.write(")" + constDeclarator + ";\n") + +def methodNames(f, options): +	for name in f.order: +		v = f.features[name] +		if v["Category"] != "Deprecated": +			feat = v["FeatureType"] +			if feat in ["fun", "get", "set"]: +				if checkTypes(name, v): +					yield normalisedName(name, options) + +def printCPPFile(f,out, options): +	for name in f.order: +		v = f.features[name] +		if v["Category"] != "Deprecated": +			feat = v["FeatureType"] +			if feat in ["fun", "get", "set"]: +				if checkTypes(name, v): +					constDeclarator = " const" if feat == "get" else "" +					featureDefineName = "SCI_" + name.upper() +					returnType = cppAlias(v["ReturnType"]) +					stringResult = v["Param2Type"] == "stringresult" +					if stringResult: +						returnType = "QByteArray" +					returnStatement = "" +					if returnType != "void": +						returnStatement = "return " +					out.write(returnType + " ScintillaEdit::" + normalisedName(name, options, feat) + "(") +					out.write(arguments(v, stringResult, options)) +					out.write(")" + constDeclarator + " {\n") +					if stringResult: +						out.write("    " + returnStatement + "TextReturner(" + featureDefineName + ", ") +						if "*" in cppAlias(v["Param1Type"]): +							out.write("(uptr_t)") +						if v["Param1Name"]: +							out.write(normalisedName(v["Param1Name"], options)) +						else: +							out.write("0") +						out.write(");\n") +					else: +						out.write("    " + returnStatement + "send(" + featureDefineName + ", ") +						if "*" in cppAlias(v["Param1Type"]): +							out.write("(uptr_t)") +						if v["Param1Name"]: +							out.write(normalisedName(v["Param1Name"], options)) +						else: +							out.write("0") +						out.write(", ") +						if "*" in cppAlias(v["Param2Type"]): +							out.write("(sptr_t)") +						if v["Param2Name"]: +							out.write(normalisedName(v["Param2Name"], options)) +						else: +							out.write("0") +						out.write(");\n") +					out.write("}\n") +					out.write("\n") + +def CopyWithInsertion(input, output, genfn, definition, options): +	copying = 1 +	for line in input.readlines(): +		if copying: +			output.write(line) +		if "/* ++Autogenerated" in line or "# ++Autogenerated" in line or "<!-- ++Autogenerated" in line: +			copying = 0 +			genfn(definition, output, options) +		# ~~ form needed as XML comments can not contain -- +		if "/* --Autogenerated" in line or "# --Autogenerated" in line or "<!-- ~~Autogenerated" in line: +			copying = 1 +			output.write(line) + +def contents(filename): +	with open(filename, "U") as f: +		t = f.read() +		return t + +def Generate(templateFile, destinationFile, genfn, definition, options): +	inText = contents(templateFile) +	try: +		currentText = contents(destinationFile) +	except IOError: +		currentText = "" +	tempname = "WidgetGen.tmp" +	with open(tempname, "w") as out: +		with open(templateFile, "U") as hfile: +			CopyWithInsertion(hfile, out, genfn, definition, options) +	outText = contents(tempname) +	if currentText == outText: +		os.unlink(tempname) +	else: +		try: +			os.unlink(destinationFile) +		except OSError: +			# Will see failure if file does not yet exist +			pass +		os.rename(tempname, destinationFile) + +def gtkNames(): +	# The full path on my machine: should be altered for anyone else +	p = "C:/Users/Neil/Downloads/wingide-source-4.0.1-1/wingide-source-4.0.1-1/external/gtkscintilla2/gtkscintilla.c" +	with open(p) as f: +		for l in f.readlines(): +			if "gtk_scintilla_" in l: +				name = l.split()[1][14:] +				if '(' in name: +					name = name.split('(')[0] +					yield name + +def usage(): +	print("WidgetGen.py [-c|--clean][-h|--help][-u|--underscore-names]") +	print("") +	print("Generate full APIs for ScintillaEdit class and ScintillaConstants.py.") +	print("") +	print("options:") +	print("") +	print("-c --clean remove all generated code from files") +	print("-h --help  display this text") +	print("-u --underscore-names  use method_names consistent with GTK+ standards") + +def readInterface(cleanGenerated): +	f = Face.Face() +	if not cleanGenerated: +		f.ReadFromFile("../../include/Scintilla.iface") +	return f + +def main(argv): +	# Using local path for gtkscintilla2 so don't default to checking +	checkGTK = False +	cleanGenerated = False +	qtStyleInterface = True +	# The --gtk-check option checks for full coverage of the gtkscintilla2 API but +	# depends on a particular directory so is not mentioned in --help. +	opts, args = getopt.getopt(argv, "hcgu", ["help", "clean", "gtk-check", "underscore-names"]) +	for opt, arg in opts: +		if opt in ("-h", "--help"): +			usage() +			sys.exit() +		elif opt in ("-c", "--clean"): +			cleanGenerated = True +		elif opt in ("-g", "--gtk-check"): +			checkGTK = True +		elif opt in ("-u", "--underscore-names"): +			qtStyleInterface = False + +	options = {"qtStyle": qtStyleInterface} +	f = readInterface(cleanGenerated) +	try: +		Generate("ScintillaEdit.cpp.template", "ScintillaEdit.cpp", printCPPFile, f, options) +		Generate("ScintillaEdit.h.template", "ScintillaEdit.h", printHFile, f, options) +		Generate("../ScintillaEditPy/ScintillaConstants.py.template", +			"../ScintillaEditPy/ScintillaConstants.py", +			printPyFile, f, options) +		if checkGTK: +			names = set(methodNames(f)) +			#~ print("\n".join(names)) +			namesGtk = set(gtkNames()) +			for name in namesGtk: +				if name not in names: +					print(name, "not found in Qt version") +			for name in names: +				if name not in namesGtk: +					print(name, "not found in GTK+ version") +	except: +		raise + +if __name__ == "__main__": +	main(sys.argv[1:]) diff --git a/qt/ScintillaEditBase/Notes.txt b/qt/ScintillaEditBase/Notes.txt new file mode 100644 index 000000000..6658aaa7a --- /dev/null +++ b/qt/ScintillaEditBase/Notes.txt @@ -0,0 +1,18 @@ + +Issues with Scintilla for Qt + +Qt reports character descenders are 1 pixel shorter than they really are. +There is a tweak in the code to add a pixel in. This may have to be reviewed for Qt 5. +There's a comment in the Qt code for Windows: +       // ### we substract 1 to even out the historical +1 in QFontMetrics's +       // ### height=asc+desc+1 equation. Fix in Qt5. + +The clocks used aren't great. QTime is a time since midnight clock so wraps around and +is only accurate to, at best, milliseconds. + +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. +Reported as QTBUG-19483. + +Only one QPainter can be active on any widget at a time. Scintilla only draws into one +widget but reenters for measurement. diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp new file mode 100644 index 000000000..b386df5a0 --- /dev/null +++ b/qt/ScintillaEditBase/PlatQt.cpp @@ -0,0 +1,1282 @@ +// +//          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 +// Scintilla platform layer for Qt + +#include "PlatQt.h" +#include "Scintilla.h" +#include "FontQuality.h" + +#include <QApplication> +#include <QFont> +#include <QColor> +#include <QRect> +#include <QPaintDevice> +#include <QPaintEngine> +#include <QWidget> +#include <QPixmap> +#include <QPainter> +#include <QMenu> +#include <QAction> +#include <QTime> +#include <QMessageBox> +#include <QTextCodec> +#include <QListWidget> +#include <QVarLengthArray> +#include <QScrollBar> +#include <QDesktopWidget> +#include <QTextLayout> +#include <QTextLine> +#include <QLibrary> +#include <cstdio> + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +//---------------------------------------------------------------------- + +// Convert from a Scintilla characterSet value to a Qt codec name. +const char *CharacterSetID(int characterSet) +{ +	switch (characterSet) { +		//case SC_CHARSET_ANSI: +		//	return ""; +	case SC_CHARSET_DEFAULT: +		return "ISO 8859-1"; +	case SC_CHARSET_BALTIC: +		return "ISO 8859-13"; +	case SC_CHARSET_CHINESEBIG5: +		return "Big5"; +	case SC_CHARSET_EASTEUROPE: +		return "ISO 8859-2"; +	case SC_CHARSET_GB2312: +		return "GB18030-0"; +	case SC_CHARSET_GREEK: +		return "ISO 8859-7"; +	case SC_CHARSET_HANGUL: +		return "CP949"; +	case SC_CHARSET_MAC: +		return "Apple Roman"; +		//case SC_CHARSET_OEM: +		//	return "ASCII"; +	case SC_CHARSET_RUSSIAN: +		return "KOI8-R"; +	case SC_CHARSET_CYRILLIC: +		return "Windows-1251"; +	case SC_CHARSET_SHIFTJIS: +		return "Shift-JIS"; +		//case SC_CHARSET_SYMBOL: +		//        return ""; +	case SC_CHARSET_TURKISH: +		return "ISO 8859-9"; +		//case SC_CHARSET_JOHAB: +		//        return "CP1361"; +	case SC_CHARSET_HEBREW: +		return "ISO 8859-8"; +	case SC_CHARSET_ARABIC: +		return "ISO 8859-6"; +	case SC_CHARSET_VIETNAMESE: +		return "Windows-1258"; +	case SC_CHARSET_THAI: +		return "TIS-620"; +	case SC_CHARSET_8859_15: +		return "ISO 8859-15"; +	default: +		return "ISO 8859-1"; +	} +} + +class FontAndCharacterSet { +public: +	int characterSet; +	QFont *pfont; +	FontAndCharacterSet(int characterSet_, QFont *pfont): +		characterSet(characterSet_), pfont(pfont) { +	} +	~FontAndCharacterSet() { +		delete pfont; +		pfont = 0; +	} +}; + +static int FontCharacterSet(Font &f) +{ +	return reinterpret_cast<FontAndCharacterSet *>(f.GetID())->characterSet; +} + +static QFont *FontPointer(Font &f) +{ +	return reinterpret_cast<FontAndCharacterSet *>(f.GetID())->pfont; +} + +Font::Font() : fid(0) {} + +Font::~Font() +{ +	delete reinterpret_cast<FontAndCharacterSet *>(fid); +	fid = 0; +} + +static QFont::StyleStrategy ChooseStrategy(int eff) +{ +	switch (eff) { +		case SC_EFF_QUALITY_DEFAULT:         return QFont::PreferDefault; +		case SC_EFF_QUALITY_NON_ANTIALIASED: return QFont::NoAntialias; +		case SC_EFF_QUALITY_ANTIALIASED:     return QFont::PreferAntialias; +		case SC_EFF_QUALITY_LCD_OPTIMIZED:   return QFont::PreferAntialias; +		default:                             return QFont::PreferDefault; +	} +} + +void Font::Create(const FontParameters &fp) +{ +	Release(); + +	QFont *font = new QFont; +	font->setStyleStrategy(ChooseStrategy(fp.extraFontFlag)); +	font->setFamily(QString::fromUtf8(fp.faceName)); +	font->setPointSize(fp.size); +	font->setBold(fp.weight > 500); +	font->setItalic(fp.italic); + +	fid = new FontAndCharacterSet(fp.characterSet, font); +} + +void Font::Release() +{ +	if (fid) +		delete reinterpret_cast<FontAndCharacterSet *>(fid); + +	fid = 0; +} + + +SurfaceImpl::SurfaceImpl() +: device(0), painter(0), deviceOwned(false), painterOwned(false), x(0), y(0), +	  unicodeMode(false), codePage(0), codecName(0), codec(0) +{} + +SurfaceImpl::~SurfaceImpl() +{ +	Release(); +} + +void SurfaceImpl::Init(WindowID wid) +{ +	Release(); +	device = static_cast<QWidget *>(wid); +} + +void SurfaceImpl::Init(SurfaceID sid, WindowID /*wid*/) +{ +	Release(); +	device = static_cast<QPaintDevice *>(sid); +} + +void SurfaceImpl::InitPixMap(int width, +        int height, +        Surface * /*surface*/, +        WindowID /*wid*/) +{ +	Release(); +	if (width < 1) width = 1; +	if (height < 1) height = 1; +	deviceOwned = true; +	device = new QPixmap(width, height); +} + +void SurfaceImpl::Release() +{ +	if (painterOwned && painter) { +		delete painter; +	} + +	if (deviceOwned && device) { +		delete device; +	} + +	device = 0; +	painter = 0; +	deviceOwned = false; +	painterOwned = false; +} + +bool SurfaceImpl::Initialised() +{ +	return device != 0; +} + +void SurfaceImpl::PenColour(ColourDesired fore) +{ +	QPen penOutline(QColorFromCA(fore)); +	penOutline.setCapStyle(Qt::FlatCap); +	GetPainter()->setPen(penOutline); +} + +void SurfaceImpl::BrushColour(ColourDesired back) +{ +	GetPainter()->setBrush(QBrush(QColorFromCA(back))); +} + +void SurfaceImpl::SetCodec(Font &font) +{ +	if (font.GetID()) { +		const char *csid = "UTF-8"; +		if (!unicodeMode) +			csid = CharacterSetID(FontCharacterSet(font)); +		if (csid != codecName) { +			codecName = csid; +			codec = QTextCodec::codecForName(csid); +		} +	} +} + +void SurfaceImpl::SetFont(Font &font) +{ +	if (font.GetID()) { +		GetPainter()->setFont(*FontPointer(font)); +		SetCodec(font); +	} +} + +int SurfaceImpl::LogPixelsY() +{ +	return device->logicalDpiY(); +} + +int SurfaceImpl::DeviceHeightFont(int points) +{ +	return points; +} + +void SurfaceImpl::MoveTo(int x_, int y_) +{ +	x = x_; +	y = y_; +} + +void SurfaceImpl::LineTo(int x_, int y_) +{ +	QLineF line(x, y, x_, y_); +	GetPainter()->drawLine(line); +	x = x_; +	y = y_; +} + +void SurfaceImpl::Polygon(Point *pts, +                          int npts, +                          ColourDesired fore, +                          ColourDesired back) +{ +	PenColour(fore); +	BrushColour(back); + +	QPoint *qpts = new QPoint[npts]; +	for (int i = 0; i < npts; i++) { +		qpts[i] = QPoint(pts[i].x, pts[i].y); +	} + +	GetPainter()->drawPolygon(qpts, npts); +	delete [] qpts; +} + +void SurfaceImpl::RectangleDraw(PRectangle rc, +                                ColourDesired fore, +                                ColourDesired back) +{ +	PenColour(fore); +	BrushColour(back); +	QRect rect = QRect(rc.left, rc.top, rc.Width() - 1, rc.Height() - 1); +	GetPainter()->drawRect(rect); +} + +void SurfaceImpl::FillRectangle(PRectangle rc, ColourDesired back) +{ +	BrushColour(back); +	GetPainter()->setPen(Qt::NoPen); +	GetPainter()->drawRect(QRectFromPRect(rc)); +} + +void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) +{ +	// Tile pattern over rectangle +	SurfaceImpl *surface = static_cast<SurfaceImpl *>(&surfacePattern); +	// Currently assumes 8x8 pattern +	int widthPat = 8; +	int heightPat = 8; +	for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) { +		int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat; +		for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) { +			int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat; +			QRect source(0, 0, widthx, heighty); +			QRect target(xTile, yTile, widthx, heighty); +			QPixmap *pixmap = static_cast<QPixmap *>(surface->GetPaintDevice()); +			GetPainter()->drawPixmap(target, *pixmap, source); +		} +	} +} + +void SurfaceImpl::RoundedRectangle(PRectangle rc, +                                   ColourDesired fore, +                                   ColourDesired back) +{ +	PenColour(fore); +	BrushColour(back); +	GetPainter()->drawRoundRect(QRectFromPRect(rc)); +} + +void SurfaceImpl::AlphaRectangle(PRectangle rc, +                                 int cornerSize, +                                 ColourDesired fill, +                                 int alphaFill, +                                 ColourDesired outline, +                                 int alphaOutline, +                                 int /*flags*/) +{ +	QColor qOutline = QColorFromCA(outline); +	qOutline.setAlpha(alphaOutline); +	GetPainter()->setPen(QPen(qOutline)); + +	QColor qFill = QColorFromCA(fill); +	qFill.setAlpha(alphaFill); +	GetPainter()->setBrush(QBrush(qFill)); + +	// A radius of 1 shows no curve so add 1 +	qreal radius = cornerSize+1; +	QRect rect(rc.left, rc.top, rc.Width() - 1, rc.Height() - 1); +	GetPainter()->drawRoundedRect(rect, radius, radius); +} + +static std::vector<unsigned char> ImageByteSwapped(int width, int height, const unsigned char *pixelsImage) +{ +	// Input is RGBA, but Format_ARGB32 is BGRA, so swap the red bytes and blue bytes +	size_t bytes = width * height * 4; +	std::vector<unsigned char> imageBytes(pixelsImage, pixelsImage+bytes); +	for (size_t i=0; i<bytes; i+=4) +		std::swap(imageBytes[i], imageBytes[i+2]); +	return imageBytes; +} + +void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) +{ +	std::vector<unsigned char> imageBytes = ImageByteSwapped(width, height, pixelsImage); +	QImage image(&imageBytes[0], width, height, QImage::Format_ARGB32); +	QPoint pt(rc.left, rc.top); +	GetPainter()->drawImage(pt, image); +} + +void SurfaceImpl::Ellipse(PRectangle rc, +                          ColourDesired fore, +                          ColourDesired back) +{ +	PenColour(fore); +	BrushColour(back); +	GetPainter()->drawEllipse(QRectFromPRect(rc)); +} + +void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) +{ +	SurfaceImpl *source = static_cast<SurfaceImpl *>(&surfaceSource); +	QPixmap *pixmap = static_cast<QPixmap *>(source->GetPaintDevice()); + +	GetPainter()->drawPixmap(rc.left, rc.top, *pixmap, from.x, from.y, -1, -1); +} + +void SurfaceImpl::DrawTextNoClip(PRectangle rc, +                                 Font &font, +                                 XYPOSITION ybase, +                                 const char *s, +                                 int len, +                                 ColourDesired fore, +                                 ColourDesired back) +{ +	SetFont(font); +	PenColour(fore); + +	GetPainter()->setBackground(QColorFromCA(back)); +	GetPainter()->setBackgroundMode(Qt::OpaqueMode); +	QString su = codec->toUnicode(s, len); +	GetPainter()->drawText(QPointF(rc.left, ybase), su); +} + +void SurfaceImpl::DrawTextClipped(PRectangle rc, +                                  Font &font, +                                  XYPOSITION ybase, +                                  const char *s, +                                  int len, +                                  ColourDesired fore, +                                  ColourDesired back) +{ +	SetClip(rc); +	DrawTextNoClip(rc, font, ybase, s, len, fore, back); +	GetPainter()->setClipping(false); +} + +void SurfaceImpl::DrawTextTransparent(PRectangle rc, +                                      Font &font, +                                      XYPOSITION ybase, +                                      const char *s, +                                      int len, +        ColourDesired fore) +{ +	SetFont(font); +	PenColour(fore); + +	GetPainter()->setBackgroundMode(Qt::TransparentMode); +	QString su = codec->toUnicode(s, len); +	GetPainter()->drawText(QPointF(rc.left, ybase), su); +} + +void SurfaceImpl::SetClip(PRectangle rc) +{ +	GetPainter()->setClipRect(QRectFromPRect(rc)); +} + +static size_t utf8LengthFromLead(unsigned char uch) +{ +	if (uch >= (0x80 + 0x40 + 0x20 + 0x10)) { +		return 4; +	} else if (uch >= (0x80 + 0x40 + 0x20)) { +		return 3; +	} else if (uch >= (0x80)) { +		return 2; +	} else { +		return 1; +	} +} + +void SurfaceImpl::MeasureWidths(Font &font, +                                const char *s, +                                int len, +                                XYPOSITION *positions) +{ +	if (!font.GetID()) +		return; +	SetCodec(font); +	QString su = codec->toUnicode(s, len); +	QTextLayout tlay(su, *FontPointer(font)); +	tlay.beginLayout(); +	QTextLine tl = tlay.createLine(); +	tlay.endLayout(); +	if (unicodeMode) { +		int fit = su.size(); +		int ui=0; +		const unsigned char *us = reinterpret_cast<const unsigned char *>(s); +		int i=0; +		while (ui<fit) { +			size_t lenChar = utf8LengthFromLead(us[i]); +			size_t codeUnits = (lenChar < 4) ? 1 : 2; +			qreal xPosition = tl.cursorToX(ui+codeUnits); +			for (unsigned int bytePos=0; (bytePos<lenChar) && (i<len); bytePos++) { +				positions[i++] = qRound(xPosition); +			} +			ui += codeUnits; +		} +		int lastPos = 0; +		if (i > 0) +			lastPos = positions[i-1]; +		while (i<len) { +			positions[i++] = lastPos; +		} +	} else if (codePage) { +		// DBCS +		int ui = 0; +		for (int i=0; i<len;) { +			size_t lenChar = Platform::IsDBCSLeadByte(codePage, s[i]) ? 2 : 1; +			qreal xPosition = tl.cursorToX(ui+1); +			for (unsigned int bytePos=0; (bytePos<lenChar) && (i<len); bytePos++) { +				positions[i++] = qRound(xPosition); +			} +			ui++; +		} +	} else { +		// Single byte encoding +		for (int i=0; i<len; i++) { +			positions[i] = qRound(tl.cursorToX(i+1)); +		} +	} +} + +XYPOSITION SurfaceImpl::WidthText(Font &font, const char *s, int len) +{ +	QFontMetrics metrics(*FontPointer(font), device); +	SetCodec(font); +	QString string = codec->toUnicode(s, len); +	return metrics.width(string); +} + +XYPOSITION SurfaceImpl::WidthChar(Font &font, char ch) +{ +	QFontMetrics metrics(*FontPointer(font), device); +	return metrics.width(ch); +} + +XYPOSITION SurfaceImpl::Ascent(Font &font) +{ +	QFontMetrics metrics(*FontPointer(font), device); +	return metrics.ascent(); +} + +XYPOSITION SurfaceImpl::Descent(Font &font) +{ +	QFontMetrics metrics(*FontPointer(font), device); +	// Qt returns 1 less than true descent +	// See: QFontEngineWin::descent which says: +	// ### we substract 1 to even out the historical +1 in QFontMetrics's +	// ### height=asc+desc+1 equation. Fix in Qt5. +	return metrics.descent() + 1; +} + +XYPOSITION SurfaceImpl::InternalLeading(Font & /* font */) +{ +	return 0; +} + +XYPOSITION SurfaceImpl::ExternalLeading(Font &font) +{ +	QFontMetrics metrics(*FontPointer(font), device); +	return metrics.leading(); +} + +XYPOSITION SurfaceImpl::Height(Font &font) +{ +	QFontMetrics metrics(*FontPointer(font), device); +	return metrics.height(); +} + +XYPOSITION SurfaceImpl::AverageCharWidth(Font &font) +{ +	QFontMetrics metrics(*FontPointer(font), device); +	return metrics.averageCharWidth(); +} + +void SurfaceImpl::FlushCachedState() +{ +	if (device->paintingActive()) { +		GetPainter()->setPen(QPen()); +		GetPainter()->setBrush(QBrush()); +	} +} + +void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) +{ +	unicodeMode=unicodeMode_; +} + +void SurfaceImpl::SetDBCSMode(int codePage_) +{ +	codePage = codePage_; +} + +QPaintDevice *SurfaceImpl::GetPaintDevice() +{ +	return device; +} + +QPainter *SurfaceImpl::GetPainter() +{ +	Q_ASSERT(device); + +	if (painter == 0) { +		if (device->paintingActive()) { +			painter = device->paintEngine()->painter(); +		} else { +			painterOwned = true; +			painter = new QPainter(device); +		} + +		// Set text antialiasing unconditionally. +		// The font's style strategy will override. +		painter->setRenderHint(QPainter::TextAntialiasing, true); +	} + +	return painter; +} + +Surface *Surface::Allocate(int) +{ +	return new SurfaceImpl; +} + + +//---------------------------------------------------------------------- + +namespace { +QWidget *window(WindowID wid) +{ +	return static_cast<QWidget *>(wid); +} +} + +Window::~Window() {} + +void Window::Destroy() +{ +	if (wid) +		delete window(wid); + +	wid = 0; +} + +bool Window::HasFocus() +{ +	return wid ? window(wid)->hasFocus() : false; +} + +PRectangle Window::GetPosition() +{ +	// Before any size allocated pretend its 1000 wide so not scrolled +	return wid ? PRectFromQRect(window(wid)->frameGeometry()) : PRectangle(0, 0, 1000, 1000); +} + +void Window::SetPosition(PRectangle rc) +{ +	if (wid) +		window(wid)->setGeometry(QRectFromPRect(rc)); +} + +void Window::SetPositionRelative(PRectangle rc, Window relativeTo) +{ +	QPoint oPos = window(relativeTo.wid)->mapToGlobal(QPoint(0,0)); +	int ox = oPos.x(); +	int oy = oPos.y(); +	ox += rc.left; +	if (ox < 0) +		ox = 0; +	oy += rc.top; +	if (oy < 0) +		oy = 0; + +	QDesktopWidget *desktop = QApplication::desktop(); +	QRect rectDesk = desktop->availableGeometry(window(wid)); +	/* do some corrections to fit into screen */ +	int sizex = rc.right - rc.left; +	int sizey = rc.bottom - rc.top; +	int screenWidth = rectDesk.width(); +	int screenHeight = rectDesk.height(); +	if (sizex > screenWidth) +		ox = 0; /* the best we can do */ +	else if (ox + sizex > screenWidth) +		ox = screenWidth - sizex; +	if (oy + sizey > screenHeight) +		oy = screenHeight - sizey; + +	Q_ASSERT(wid); +	window(wid)->move(ox, oy); +	window(wid)->resize(sizex, sizey); +} + +PRectangle Window::GetClientPosition() +{ +	// The client position is the window position +	return GetPosition(); +} + +void Window::Show(bool show) +{ +	if (wid) +		window(wid)->setVisible(show); +} + +void Window::InvalidateAll() +{ +	if (wid) +		window(wid)->update(); +} + +void Window::InvalidateRectangle(PRectangle rc) +{ +	if (wid) +		window(wid)->update(QRectFromPRect(rc)); +} + +void Window::SetFont(Font &font) +{ +	if (wid) +		window(wid)->setFont(*FontPointer(font)); +} + +void Window::SetCursor(Cursor curs) +{ +	if (wid) { +		Qt::CursorShape shape; + +		switch (curs) { +			case cursorText:  shape = Qt::IBeamCursor;        break; +			case cursorArrow: shape = Qt::ArrowCursor;        break; +			case cursorUp:    shape = Qt::UpArrowCursor;      break; +			case cursorWait:  shape = Qt::WaitCursor;         break; +			case cursorHoriz: shape = Qt::SizeHorCursor;      break; +			case cursorVert:  shape = Qt::SizeVerCursor;      break; +			case cursorHand:  shape = Qt::PointingHandCursor; break; +			default:          shape = Qt::ArrowCursor;        break; +		} + +		QCursor cursor = QCursor(shape); + +		if (curs != cursorLast) { +			window(wid)->setCursor(cursor); +			cursorLast = curs; +		} +	} +} + +void Window::SetTitle(const char *s) +{ +	if (wid) +		window(wid)->setWindowTitle(s); +} + +/* Returns rectangle of monitor pt is on, both rect and pt are in Window's +   window coordinates */ +PRectangle Window::GetMonitorRect(Point pt) +{ +	QPoint originGlobal = window(wid)->mapToGlobal(QPoint(0, 0)); +	QPoint posGlobal = window(wid)->mapToGlobal(QPoint(pt.x, pt.y)); +	QDesktopWidget *desktop = QApplication::desktop(); +	QRect rectScreen = desktop->availableGeometry(posGlobal); +	rectScreen.moveLeft(-originGlobal.x()); +	rectScreen.moveTop(-originGlobal.y()); +	return PRectangle(rectScreen.left(), rectScreen.top(), +	        rectScreen.right(), rectScreen.bottom()); +} + + +//---------------------------------------------------------------------- + +class ListBoxImpl : public ListBox { +public: +	ListBoxImpl(); +	~ListBoxImpl(); + +	virtual void SetFont(Font &font); +	virtual void Create(Window &parent, int ctrlID, Point location, +						int lineHeight, bool unicodeMode, int technology); +	virtual void SetAverageCharWidth(int width); +	virtual void SetVisibleRows(int rows); +	virtual int GetVisibleRows() const; +	virtual PRectangle GetDesiredRect(); +	virtual int CaretFromEdge(); +	virtual void Clear(); +	virtual void Append(char *s, int type = -1); +	virtual int Length(); +	virtual void Select(int n); +	virtual int GetSelection(); +	virtual int Find(const char *prefix); +	virtual void GetValue(int n, char *value, int len); +	virtual void RegisterImage(int type, const char *xpmData); +	virtual void RegisterRGBAImage(int type, int width, int height, +		const unsigned char *pixelsImage); +	virtual void ClearRegisteredImages(); +	virtual void SetDoubleClickAction(CallBackAction action, void *data); +	virtual void SetList(const char *list, char separator, char typesep); +private: +	bool unicodeMode; +	int visibleRows; +	QMap<int,QPixmap> images; +}; + +class ListWidget : public QListWidget { +public: +	ListWidget(QWidget *parent); +	virtual ~ListWidget(); + +	void setDoubleClickAction(CallBackAction action, void *data); + +protected: +	virtual void mouseDoubleClickEvent(QMouseEvent *event); +	virtual QStyleOptionViewItem viewOptions() const; + +private: +	CallBackAction doubleClickAction; +	void *doubleClickActionData; +}; + + +ListBoxImpl::ListBoxImpl() +: unicodeMode(false), visibleRows(5) +{} + +ListBoxImpl::~ListBoxImpl() {} + +void ListBoxImpl::Create(Window &parent, +                         int /*ctrlID*/, +                         Point location, +                         int /*lineHeight*/, +                         bool unicodeMode_, +			 int) +{ +	unicodeMode = unicodeMode_; + +	QWidget *qparent = static_cast<QWidget *>(parent.GetID()); +	ListWidget *list = new ListWidget(qparent); + +#if defined(Q_OS_WIN) +	// On Windows, Qt::ToolTip causes a crash when the list is clicked on +	// so Qt::Tool is used. +	list->setParent(0, Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); +#else +	// On OS X, Qt::Tool takes focus so main window loses focus so +	// keyboard stops working. Qt::ToolTip works but its only really +	// documented for tooltips. +	// On Linux / X this setting allows clicking on list items. +	list->setParent(0, Qt::ToolTip | Qt::FramelessWindowHint); +#endif +	list->setAttribute(Qt::WA_ShowWithoutActivating); +	list->setFocusPolicy(Qt::NoFocus); +	list->setUniformItemSizes(true); +	list->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); +	list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +	list->move(location.x, location.y); + +	wid = list; +} + +void ListBoxImpl::SetFont(Font &font) +{ +	ListWidget *list = static_cast<ListWidget *>(wid); +	list->setFont(*FontPointer(font)); +} + +void ListBoxImpl::SetAverageCharWidth(int /*width*/) {} + +void ListBoxImpl::SetVisibleRows(int rows) +{ +	visibleRows = rows; +} + +int ListBoxImpl::GetVisibleRows() const +{ +	return visibleRows; +} + +PRectangle ListBoxImpl::GetDesiredRect() +{ +	ListWidget *list = static_cast<ListWidget *>(wid); + +	int rows = Length(); +	if (rows == 0 || rows > visibleRows) { +		rows = visibleRows; +	} +	int rowHeight = list->sizeHintForRow(0); +	int height = (rows * rowHeight) + (2 * list->frameWidth()); + +	QStyle *style = QApplication::style(); +	int width = list->sizeHintForColumn(0) + (2 * list->frameWidth()); +	if (Length() > rows) { +		width += style->pixelMetric(QStyle::PM_ScrollBarExtent); +	} + +	return PRectangle(0, 0, width, height); +} + +int ListBoxImpl::CaretFromEdge() +{ +	ListWidget *list = static_cast<ListWidget *>(wid); + +	int maxIconWidth = 0; +	foreach (QPixmap im, images) { +		if (maxIconWidth < im.width()) +			maxIconWidth = im.width(); +	} + +	// The '7' is from trial and error on Windows - there may be +	// a better programmatic way to find any padding factors. +	return maxIconWidth  + (2 * list->frameWidth()) + 7; +} + +void ListBoxImpl::Clear() +{ +	ListWidget *list = static_cast<ListWidget *>(wid); +	list->clear(); +} + +void ListBoxImpl::Append(char *s, int type) +{ +	ListWidget *list = static_cast<ListWidget *>(wid); + +	QString str = unicodeMode ? QString::fromUtf8(s) : QString::fromLocal8Bit(s); +	QIcon icon; +	if (type >= 0) { +		Q_ASSERT(images.contains(type)); +		icon = images.value(type); +	} +	new QListWidgetItem(icon, str, list); +} + +int ListBoxImpl::Length() +{ +	ListWidget *list = static_cast<ListWidget *>(wid); +	return list->count(); +} + +void ListBoxImpl::Select(int n) +{ +	ListWidget *list = static_cast<ListWidget *>(wid); +	list->setCurrentRow(n); +} + +int ListBoxImpl::GetSelection() +{ +	ListWidget *list = static_cast<ListWidget *>(wid); +	return list->currentRow(); +} + +int ListBoxImpl::Find(const char *prefix) +{ +	ListWidget *list = static_cast<ListWidget *>(wid); +	QString sPrefix = unicodeMode ? QString::fromUtf8(prefix) : QString::fromLocal8Bit(prefix); +	QList<QListWidgetItem *> ms = list->findItems(sPrefix, Qt::MatchStartsWith); + +	int result = -1; +	if (!ms.isEmpty()) { +		result = list->row(ms.first()); +	} + +	return result; +} + +void ListBoxImpl::GetValue(int n, char *value, int len) +{ +	ListWidget *list = static_cast<ListWidget *>(wid); +	QListWidgetItem *item = list->item(n); +	QString str = item->data(Qt::DisplayRole).toString(); +	QByteArray bytes = unicodeMode ? str.toUtf8() : str.toLocal8Bit(); + +	strncpy(value, bytes.constData(), len); +	value[len-1] = '\0'; +} + +void ListBoxImpl::RegisterImage(int type, const char *xpmData) +{ +	images[type] = QPixmap(reinterpret_cast<const char * const *>(xpmData)); +} + +void ListBoxImpl::RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) +{ +	std::vector<unsigned char> imageBytes = ImageByteSwapped(width, height, pixelsImage); +	QImage image(&imageBytes[0], width, height, QImage::Format_ARGB32); +	images[type] = QPixmap::fromImage(image); +} + +void ListBoxImpl::ClearRegisteredImages() +{ +	images.clear(); +} + +void ListBoxImpl::SetDoubleClickAction(CallBackAction action, void *data) +{ +	ListWidget *list = static_cast<ListWidget *>(wid); +	list->setDoubleClickAction(action, data); +} + +void ListBoxImpl::SetList(const char *list, char separator, char typesep) +{ +	// This method is *not* platform dependent. +	// It is borrowed from the GTK implementation. +	Clear(); +	int count = strlen(list) + 1; +	char *words = new char[count]; +	if (words) { +		memcpy(words, list, count); +		char *startword = words; +		char *numword = NULL; +		int i = 0; +		for (; words[i]; i++) { +			if (words[i] == separator) { +				words[i] = '\0'; +				if (numword) +					*numword = '\0'; +				Append(startword, numword?atoi(numword + 1):-1); +				startword = words + i + 1; +				numword = NULL; +			} else if (words[i] == typesep) { +				numword = words + i; +			} +		} +		if (startword) { +			if (numword) +				*numword = '\0'; +			Append(startword, numword?atoi(numword + 1):-1); +		} +		delete []words; +	} +} + +ListBox::ListBox() {} + +ListBox::~ListBox() {} + +ListBox *ListBox::Allocate() +{ +	return new ListBoxImpl(); +} + +ListWidget::ListWidget(QWidget *parent) +: QListWidget(parent), doubleClickActionData(0) +{} + +ListWidget::~ListWidget() {} + +void ListWidget::setDoubleClickAction(CallBackAction action, void *data) +{ +	doubleClickAction = action; +	doubleClickActionData = data; +} + +void ListWidget::mouseDoubleClickEvent(QMouseEvent * /* event */) +{ +	if (doubleClickAction != 0) { +		doubleClickAction(doubleClickActionData); +	} +} + +QStyleOptionViewItem ListWidget::viewOptions() const +{ +	QStyleOptionViewItem result = QListWidget::viewOptions(); +	result.state |= QStyle::State_Active; +	return result; +} + +//---------------------------------------------------------------------- + +Menu::Menu() : mid(0) {} + +void Menu::CreatePopUp() +{ +	Destroy(); +	mid = new QMenu(); +} + +void Menu::Destroy() +{ +	if (mid) { +		QMenu *menu = static_cast<QMenu *>(mid); +		delete menu; +	} +	mid = 0; +} + +void Menu::Show(Point pt, Window & /*w*/) +{ +	QMenu *menu = static_cast<QMenu *>(mid); +	menu->exec(QPoint(pt.x, pt.y)); +	Destroy(); +} + +//---------------------------------------------------------------------- + +class DynamicLibraryImpl : public DynamicLibrary { +protected: +	QLibrary *lib; +public: +	DynamicLibraryImpl(const char *modulePath) { +		QString path = QString::fromUtf8(modulePath); +		lib = new QLibrary(path); +	} + +	virtual ~DynamicLibraryImpl() { +		if (lib) +			lib->unload(); +		lib = 0; +	} + +	virtual Function FindFunction(const char *name) { +		if (lib) { +			void *fnAddress = lib->resolve(name); +			return static_cast<Function>(fnAddress); +		} +		return NULL; +	} + +	virtual bool IsValid() { +		return lib != NULL; +	} +}; + +DynamicLibrary *DynamicLibrary::Load(const char *modulePath) +{ +	return static_cast<DynamicLibrary *>(new DynamicLibraryImpl(modulePath)); +} + +ColourDesired Platform::Chrome() +{ +	QColor c(Qt::gray); +	return ColourDesired(c.red(), c.green(), c.blue()); +} + +ColourDesired Platform::ChromeHighlight() +{ +	QColor c(Qt::lightGray); +	return ColourDesired(c.red(), c.green(), c.blue()); +} + +const char *Platform::DefaultFont() +{ +	static char fontNameDefault[200] = ""; +	if (!fontNameDefault[0]) { +		QFont font = QApplication::font(); +		strcpy(fontNameDefault, font.family().toAscii()); +	} +	return fontNameDefault; +} + +int Platform::DefaultFontSize() +{ +	QFont font = QApplication::font(); +	return font.pointSize(); +} + +unsigned int Platform::DoubleClickTime() +{ +	return QApplication::doubleClickInterval(); +} + +bool Platform::MouseButtonBounce() +{ +	return false; +} + +bool Platform::IsKeyDown(int /*key*/) +{ +	return false; +} + +long Platform::SendScintilla(WindowID /*w*/, +                             unsigned int /*msg*/, +                             unsigned long /*wParam*/, +                             long /*lParam*/) +{ +	return 0; +} + +long Platform::SendScintillaPointer(WindowID /*w*/, +                                    unsigned int /*msg*/, +                                    unsigned long /*wParam*/, +                                    void * /*lParam*/) +{ +	return 0; +} + +int Platform::Minimum(int a, int b) +{ +	return qMin(a, b); +} + +int Platform::Maximum(int a, int b) +{ +	return qMax(a, b); +} + +int Platform::Clamp(int val, int minVal, int maxVal) +{ +	return qBound(minVal, val, maxVal); +} + +void Platform::DebugDisplay(const char *s) +{ +	qWarning("Scintilla: %s", s); +} + +void Platform::DebugPrintf(const char *format, ...) +{ +	char buffer[2000]; +	va_list pArguments; +	va_start(pArguments, format); +	vsprintf(buffer, format, pArguments); +	va_end(pArguments); +	Platform::DebugDisplay(buffer); +} + +bool Platform::ShowAssertionPopUps(bool /*assertionPopUps*/) +{ +	return false; +} + +void Platform::Assert(const char *c, const char *file, int line) +{ +	char buffer[2000]; +	sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line); +	if (Platform::ShowAssertionPopUps(false)) { +		QMessageBox mb("Assertion Failure", buffer, QMessageBox::NoIcon, +			QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); +		mb.exec(); +	} else { +		strcat(buffer, "\n"); +		Platform::DebugDisplay(buffer); +	} +} + + +bool Platform::IsDBCSLeadByte(int codePage, char ch) +{ +	// Byte ranges found in Wikipedia articles with relevant search strings in each case +	unsigned char uch = static_cast<unsigned char>(ch); +	switch (codePage) { +	case 932: +		// Shift_jis +		return ((uch >= 0x81) && (uch <= 0x9F)) || +		       ((uch >= 0xE0) && (uch <= 0xEF)); +	case 936: +		// GBK +		return (uch >= 0x81) && (uch <= 0xFE); +	case 949: +		// Korean Wansung KS C-5601-1987 +		return (uch >= 0x81) && (uch <= 0xFE); +	case 950: +		// Big5 +		return (uch >= 0x81) && (uch <= 0xFE); +	case 1361: +		// Korean Johab KS C-5601-1992 +		return +		    ((uch >= 0x84) && (uch <= 0xD3)) || +		    ((uch >= 0xD8) && (uch <= 0xDE)) || +		    ((uch >= 0xE0) && (uch <= 0xF9)); +	} +	return false; +} + +int Platform::DBCSCharLength(int codePage, const char *s) +{ +	if (codePage == 932 || codePage == 936 || codePage == 949 || +	        codePage == 950 || codePage == 1361) { +		return IsDBCSLeadByte(codePage, s[0]) ? 2 : 1; +	} else { +		return 1; +	} +} + +int Platform::DBCSCharMaxLength() +{ +	return 2; +} + + +//---------------------------------------------------------------------- + +static QTime timer; + +ElapsedTime::ElapsedTime() +{ +	timer.start(); +} + +double ElapsedTime::Duration(bool reset) +{ +	double result = timer.elapsed(); +	if (reset) { +		timer.restart(); +	} +	result /= 1000.0; +	return result; +} + +#ifdef SCI_NAMESPACE +} +#endif diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h new file mode 100644 index 000000000..60a7e7300 --- /dev/null +++ b/qt/ScintillaEditBase/PlatQt.h @@ -0,0 +1,127 @@ +// +//          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 +// Scintilla platform layer for Qt + +#ifndef PLATQT_H +#define PLATQT_H + +#include "Platform.h" + +#include <QPaintDevice> +#include <QPainter> +#include <QHash> + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +const char *CharacterSetID(int characterSet); + +inline QColor QColorFromCA(ColourDesired ca) +{ +	long c = ca.AsLong(); +	return QColor(c & 0xff, (c >> 8) & 0xff, (c >> 16) & 0xff); +} + +inline QRect QRectFromPRect(PRectangle pr) +{ +	return QRect(pr.left, pr.top, pr.Width(), pr.Height()); +} + +inline PRectangle PRectFromQRect(QRect qr) +{ +	return PRectangle(qr.x(), qr.y(), qr.x() + qr.width(), qr.y() + qr.height()); +} + +inline Point PointFromQPoint(QPoint qp) +{ +	return Point(qp.x(), qp.y()); +} + +class SurfaceImpl : public Surface { +private: +	QPaintDevice *device; +	QPainter *painter; +	bool deviceOwned; +	bool painterOwned; +	float x, y; +	bool unicodeMode; +	int codePage; +	const char *codecName; +	QTextCodec *codec; + +public: +	SurfaceImpl(); +	virtual ~SurfaceImpl(); + +	virtual void Init(WindowID wid); +	virtual void Init(SurfaceID sid, WindowID wid); +	virtual void InitPixMap(int width, int height, +		Surface *surface, WindowID wid); + +	virtual void Release(); +	virtual bool Initialised(); +	virtual void PenColour(ColourDesired fore); +	virtual int LogPixelsY(); +	virtual int DeviceHeightFont(int points); +	virtual void MoveTo(int x, int y); +	virtual void LineTo(int x, int y); +	virtual void Polygon(Point *pts, int npts, ColourDesired fore, +		ColourDesired back); +	virtual void RectangleDraw(PRectangle rc, ColourDesired fore, +		ColourDesired back); +	virtual void FillRectangle(PRectangle rc, ColourDesired back); +	virtual void FillRectangle(PRectangle rc, Surface &surfacePattern); +	virtual void RoundedRectangle(PRectangle rc, ColourDesired fore, +		ColourDesired back); +	virtual void AlphaRectangle(PRectangle rc, int corner, ColourDesired fill, +		int alphaFill, ColourDesired outline, int alphaOutline, int flags); +	virtual void DrawRGBAImage(PRectangle rc, int width, int height, +		const unsigned char *pixelsImage); +	virtual void Ellipse(PRectangle rc, ColourDesired fore, +		ColourDesired back); +	virtual void Copy(PRectangle rc, Point from, Surface &surfaceSource); + +	virtual void DrawTextNoClip(PRectangle rc, Font &font, XYPOSITION ybase, +		const char *s, int len, ColourDesired fore, ColourDesired back); +	virtual void DrawTextClipped(PRectangle rc, Font &font, XYPOSITION ybase, +		const char *s, int len, ColourDesired fore, ColourDesired back); +	virtual void DrawTextTransparent(PRectangle rc, Font &font, XYPOSITION ybase, +		const char *s, int len, ColourDesired fore); +	virtual void MeasureWidths(Font &font, const char *s, int len, +		XYPOSITION *positions); +	virtual XYPOSITION WidthText(Font &font, const char *s, int len); +	virtual XYPOSITION WidthChar(Font &font, char ch); +	virtual XYPOSITION Ascent(Font &font); +	virtual XYPOSITION Descent(Font &font); +	virtual XYPOSITION InternalLeading(Font &font); +	virtual XYPOSITION ExternalLeading(Font &font); +	virtual XYPOSITION Height(Font &font); +	virtual XYPOSITION AverageCharWidth(Font &font); + +	virtual void SetClip(PRectangle rc); +	virtual void FlushCachedState(); + +	virtual void SetUnicodeMode(bool unicodeMode); +	virtual void SetDBCSMode(int codePage); + +	void BrushColour(ColourDesired back); +	void SetCodec(Font &font); +	void SetFont(Font &font); + +	QPaintDevice *GetPaintDevice(); +	void SetPainter(QPainter *painter); +	QPainter *GetPainter(); +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif 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); +} diff --git a/qt/ScintillaEditBase/ScintillaEditBase.h b/qt/ScintillaEditBase/ScintillaEditBase.h new file mode 100644 index 000000000..0f625eba3 --- /dev/null +++ b/qt/ScintillaEditBase/ScintillaEditBase.h @@ -0,0 +1,151 @@ +// +//          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 +// ScintillaWidget.h - Qt widget that wraps ScintillaQt and provides events and scrolling + + +#ifndef SCINTILLAEDITBASE_H +#define SCINTILLAEDITBASE_H + +#include "Platform.h" +#include "Scintilla.h" + +#include <QAbstractScrollArea> +#include <QMimeData> +#include <QTime> + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +class ScintillaQt; +class SurfaceImpl; +struct SCNotification; + +#ifdef WIN32 +#ifdef MAKING_LIBRARY +#define EXPORT_IMPORT_API __declspec(dllexport) +#else +// Defining dllimport upsets moc +#define EXPORT_IMPORT_API __declspec(dllimport) +//#define EXPORT_IMPORT_API +#endif +#else +#define EXPORT_IMPORT_API +#endif + +class EXPORT_IMPORT_API ScintillaEditBase : public QAbstractScrollArea { +	Q_OBJECT + +public: +	ScintillaEditBase(QWidget *parent = 0); +	virtual ~ScintillaEditBase(); + +	virtual sptr_t send( +		unsigned int iMessage, +		uptr_t wParam = 0, +		sptr_t lParam = 0) const; + +	virtual sptr_t sends( +		unsigned int iMessage, +		uptr_t wParam = 0, +		const char *s = 0) const; + +public slots: +	// Scroll events coming from GUI to be sent to Scintilla. +	void scrollHorizontal(int value); +	void scrollVertical(int value); + +	// Emit Scintilla notifications as signals. +	void notifyParent(SCNotification scn); +	void event_command(uptr_t wParam, sptr_t lParam); + +signals: +	void horizontalScrolled(int value); +	void verticalScrolled(int value); +	void horizontalRangeChanged(int max, int page); +	void verticalRangeChanged(int max, int page); +	void notifyChange(); +	void linesAdded(int linesAdded); + +	// Clients can use this hook to add additional +	// formats (e.g. rich text) to the MIME data. +	void aboutToCopy(QMimeData *data); + +	// Scintilla Notifications +	void styleNeeded(int position); +	void charAdded(int ch); +	void savePointChanged(bool dirty); +	void modifyAttemptReadOnly(); +	void key(int key); +	void doubleClick(int position, int line); +	void updateUi(); +	void modified(int type, int position, int length, int linesAdded, +	              const QByteArray &text, int line, int foldNow, int foldPrev); +	void macroRecord(int message, uptr_t wParam, sptr_t lParam); +	void marginClicked(int position, int modifiers, int margin); +	void textAreaClicked(int line, int modifiers); +	void needShown(int position, int length); +	void painted(); +	void userListSelection(); // Wants some args. +	void uriDropped();        // Wants some args. +	void dwellStart(int x, int y); +	void dwellEnd(int x, int y); +	void zoom(int zoom); +	void hotSpotClick(int position, int modifiers); +	void hotSpotDoubleClick(int position, int modifiers); +	void callTipClick(); +	void autoCompleteSelection(int position, const QString &text); + +	// Base notifications for compatibility with other Scintilla implementations +	void notify(SCNotification *pscn); +	void command(uptr_t wParam, sptr_t lParam); + +	// GUI event notifications needed under Qt +	void buttonPressed(QMouseEvent *event); +	void buttonReleased(QMouseEvent *event); +	void keyPressed(QKeyEvent *event); +	void resized(); + +protected: +	virtual bool event(QEvent *event); +	virtual void paintEvent(QPaintEvent *event); +	virtual void wheelEvent(QWheelEvent *event); +	virtual void focusInEvent(QFocusEvent *event); +	virtual void focusOutEvent(QFocusEvent *event); +	virtual void resizeEvent(QResizeEvent *event); +	virtual void keyPressEvent(QKeyEvent *event); +	virtual void mousePressEvent(QMouseEvent *event); +	virtual void mouseReleaseEvent(QMouseEvent *event); +	virtual void mouseDoubleClickEvent(QMouseEvent *event); +	virtual void mouseMoveEvent(QMouseEvent *event); +	virtual void contextMenuEvent(QContextMenuEvent *event); +	virtual void dragEnterEvent(QDragEnterEvent *event); +	virtual void dragLeaveEvent(QDragLeaveEvent *event); +	virtual void dragMoveEvent(QDragMoveEvent *event); +	virtual void dropEvent(QDropEvent *event); +	virtual void inputMethodEvent(QInputMethodEvent *event); +	virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; +	virtual void scrollContentsBy(int, int) {} + +private: +	ScintillaQt *sqt; + +	QTime time; + +	int preeditPos; +	QString preeditString; + +	int wheelDelta; +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif /* SCINTILLAEDITBASE_H */ diff --git a/qt/ScintillaEditBase/ScintillaEditBase.pro b/qt/ScintillaEditBase/ScintillaEditBase.pro new file mode 100644 index 000000000..cdf0b685e --- /dev/null +++ b/qt/ScintillaEditBase/ScintillaEditBase.pro @@ -0,0 +1,111 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-05-05T12:41:23 +# +#------------------------------------------------- + +QT       += core gui + +TARGET = ScintillaEditBase +TEMPLATE = lib +CONFIG += lib_bundle + +VERSION = 3.1.0 + +SOURCES += \ +    PlatQt.cpp \ +    ScintillaQt.cpp \ +    ScintillaEditBase.cpp \ +    ../../src/XPM.cxx \ +    ../../src/ViewStyle.cxx \ +    ../../src/UniConversion.cxx \ +    ../../src/Style.cxx \ +    ../../src/Selection.cxx \ +    ../../src/ScintillaBase.cxx \ +    ../../src/RunStyles.cxx \ +    ../../src/RESearch.cxx \ +    ../../src/PositionCache.cxx \ +    ../../src/PerLine.cxx \ +    ../../src/LineMarker.cxx \ +    ../../src/KeyMap.cxx \ +    ../../src/Indicator.cxx \ +    ../../src/ExternalLexer.cxx \ +    ../../src/Editor.cxx \ +    ../../src/Document.cxx \ +    ../../src/Decoration.cxx \ +    ../../src/ContractionState.cxx \ +    ../../src/CharClassify.cxx \ +    ../../src/CellBuffer.cxx \ +    ../../src/Catalogue.cxx \ +    ../../src/CallTip.cxx \ +    ../../src/AutoComplete.cxx \ +    ../../lexlib/WordList.cxx \ +    ../../lexlib/StyleContext.cxx \ +    ../../lexlib/PropSetSimple.cxx \ +    ../../lexlib/LexerSimple.cxx \ +    ../../lexlib/LexerNoExceptions.cxx \ +    ../../lexlib/LexerModule.cxx \ +    ../../lexlib/LexerBase.cxx \ +    ../../lexlib/CharacterSet.cxx \ +    ../../lexlib/Accessor.cxx \ +    ../../lexers/*.cxx + +HEADERS  += \ +    PlatQt.h \ +    ScintillaQt.h \ +    ScintillaEditBase.h \ +    ../../src/XPM.h \ +    ../../src/ViewStyle.h \ +    ../../src/UniConversion.h \ +    ../../src/SVector.h \ +    ../../src/Style.h \ +    ../../src/SplitVector.h \ +    ../../src/Selection.h \ +    ../../src/ScintillaBase.h \ +    ../../src/RunStyles.h \ +    ../../src/RESearch.h \ +    ../../src/PositionCache.h \ +    ../../src/PerLine.h \ +    ../../src/Partitioning.h \ +    ../../src/LineMarker.h \ +    ../../src/KeyMap.h \ +    ../../src/Indicator.h \ +    ../../src/FontQuality.h \ +    ../../src/ExternalLexer.h \ +    ../../src/Editor.h \ +    ../../src/Document.h \ +    ../../src/Decoration.h \ +    ../../src/ContractionState.h \ +    ../../src/CharClassify.h \ +    ../../src/CellBuffer.h \ +    ../../src/Catalogue.h \ +    ../../src/CallTip.h \ +    ../../src/AutoComplete.h \ +    ../../include/Scintilla.h \ +    ../../include/SciLexer.h \ +    ../../include/Platform.h \ +    ../../include/ILexer.h \ +    ../../lexlib/WordList.h \ +    ../../lexlib/StyleContext.h \ +    ../../lexlib/SparseState.h \ +    ../../lexlib/PropSetSimple.h \ +    ../../lexlib/OptionSet.h \ +    ../../lexlib/LexerSimple.h \ +    ../../lexlib/LexerNoExceptions.h \ +    ../../lexlib/LexerModule.h \ +    ../../lexlib/LexerBase.h \ +    ../../lexlib/LexAccessor.h \ +    ../../lexlib/CharacterSet.h \ +    ../../lexlib/Accessor.h + +OTHER_FILES += + +INCLUDEPATH += ../../include ../../src ../../lexlib + +DEFINES += SCINTILLA_QT=1 MAKING_LIBRARY=1 SCI_LEXER=1 _CRT_SECURE_NO_DEPRECATE=1 + +DESTDIR = ../../bin + +macx { +	QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../Frameworks/ +} diff --git a/qt/ScintillaEditBase/ScintillaQt.cpp b/qt/ScintillaEditBase/ScintillaQt.cpp new file mode 100644 index 000000000..e65390f4a --- /dev/null +++ b/qt/ScintillaEditBase/ScintillaQt.cpp @@ -0,0 +1,772 @@ +// +//          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> +#include <QInputContext> +#include <QMimeData> +#include <QMenu> +#include <QScrollBar> +#include <QTimer> +#include <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(); + +	// 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(); +} + +ScintillaQt::~ScintillaQt() +{ +	SetTicking(false); +} + +void ScintillaQt::tick() +{ +	Tick(); +} + +void ScintillaQt::execCommand(QAction *action) +{ +	int command = action->data().toInt(); +	Command(command); +} + +#if defined(Q_OS_WIN) +static const QString sMSDEVColumnSelect("MSDEVColumnSelect"); +static const QString sWrappedMSDEVColumnSelect("application/x-qt-windows-mime;value=\"MSDEVColumnSelect\""); +#elif defined(Q_OS_MAC) +static const QString sScintillaRecPboardType("com.scintilla.utf16-plain-text.rectangular"); +static const QString sScintillaRecMimeType("text/x-scintilla.utf16-plain-text.rectangular"); +#else +// Linux +static const QString sMimeRectangularMarker("text/x-rectangular-marker"); +#endif + +#ifdef Q_OS_MAC + +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 + +#ifdef Q_OS_MAC +	if (!singletonMime) { +		singletonMime = new ScintillaRectangularMime(); + +		QStringList slTypes(sScintillaRecPboardType); +		qRegisterDraggedTypes(slTypes); +	} +#endif + +	connect(QApplication::clipboard(), SIGNAL(selectionChanged()), +		this, SLOT(SelectionChanged())); +} + +void ScintillaQt::Finalise() +{ +	SetTicking(false); +	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 = abs(ptStart.x - ptNow.x); +	int yMove = 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.s, selectedText.len-1); +	} else { +		QTextCodec *codec = QTextCodec::codecForName( +				CharacterSetID(selectedText.characterSet)); +		return codec->toUnicode(selectedText.s, selectedText.len-1); +	} +} + +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 && (wrapState == eWrapNone)) { +		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); +	int len = utext.length(); +	char *dest = Document::TransformLineEnds(&len, utext, len, pdoc->eolMode); +	SelectionText selText; +	selText.Set(dest, len, pdoc->dbcsCodePage, CharacterSetOfDocument(), isRectangular, false); + +	UndoGroup ug(pdoc); +	ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH); +	SelectionPosition selStart = sel.IsRectangular() ? +		sel.Rectangular().Start() : +		sel.Range(sel.Main()).Start(); +	if (selText.rectangular) { +		PasteRectangular(selStart, selText.s, selText.len); +	} else { +		InsertPaste(selStart, selText.s, selText.len); +	} +	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())); +} + +void ScintillaQt::NotifyParent(SCNotification scn) +{ +	scn.nmhdr.hwndFrom = wMain.GetID(); +	scn.nmhdr.idFrom = GetCtrlID(); +	emit notifyParent(scn); +} + +void ScintillaQt::SetTicking(bool on) +{ +	QTimer *qTimer; +	if (timer.ticking != on) { +		timer.ticking = on; +		if (timer.ticking) { +			qTimer = new QTimer; +			connect(qTimer, SIGNAL(timeout()), this, SLOT(tick())); +			qTimer->start(timer.tickSize); +			timer.tickerID = qTimer; +		} else { +			qTimer = static_cast<QTimer *>(timer.tickerID); +			qTimer->stop(); +			disconnect(qTimer, SIGNAL(timeout()), 0, 0); +			delete qTimer; +			timer.tickerID = 0; +		} +	} +	timer.ticksToWait = caret.period; +} + +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 CaseFolderUTF8 : public CaseFolderTable { +public: +	CaseFolderUTF8() { +		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 { +			QString su = QString::fromUtf8(mixed, lenMixed); +			QString suFolded = su.toCaseFolded(); +			QByteArray bytesFolded = suFolded.toUtf8(); +			if (bytesFolded.length() < static_cast<int>(sizeFolded)) { +				memcpy(folded, bytesFolded,  bytesFolded.length()); +				return bytesFolded.length(); +			} else { +				folded[0] = '\0'; +				return 0; +			} +		} +	} +}; + +class CaseFolderDBCS : public CaseFolderTable { +	QTextCodec *codec; +public: +	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, 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 CaseFolderUTF8(); +	} 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) +		return std::string(); + +	if (caseMapping == cmSame) +		return s; + +	QTextCodec *codec = 0; +	QString text; +	if (IsUnicodeMode()) { +		text = QString::fromUtf8(s.c_str(), s.length()); +	} else { +		codec = QTextCodec::codecForName(CharacterSetIDOfDocument()); +		text = codec->toUnicode(s.c_str(), 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.len) { +		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(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_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( +    ScintillaQt *sciThis, unsigned int iMessage, uptr_t wParam, sptr_t lParam) +{ +	return sciThis->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(); +	char *dest = Document::TransformLineEnds(&len, bytes, len, pdoc->eolMode); + +	SelectionPosition movePos = SPositionFromLocation(point, +				false, false, UserVirtualSpace()); + +	DropAt(movePos, dest, move, rectangular); + +	delete []dest; +} diff --git a/qt/ScintillaEditBase/ScintillaQt.h b/qt/ScintillaEditBase/ScintillaQt.h new file mode 100644 index 000000000..61e24290d --- /dev/null +++ b/qt/ScintillaEditBase/ScintillaQt.h @@ -0,0 +1,158 @@ +// +//          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.h - Qt specific subclass of ScintillaBase + +#ifndef SCINTILLAQT_H +#define SCINTILLAQT_H + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <time.h> +#include <string> +#include <vector> +#include <map> + +#include "Scintilla.h" +#include "Platform.h" +#include "ILexer.h" +#include "SVector.h" +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" +#include "ContractionState.h" +#include "CellBuffer.h" +#include "CallTip.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "XPM.h" +#include "LineMarker.h" +#include "Style.h" +#include "AutoComplete.h" +#include "ViewStyle.h" +#include "CharClassify.h" +#include "Decoration.h" +#include "Document.h" +#include "Selection.h" +#include "PositionCache.h" +#include "Editor.h" +#include "ScintillaBase.h" + +#ifdef SCI_LEXER +#include "SciLexer.h" +#include "PropSetSimple.h" +#endif + +#include <QObject> +#include <QAbstractScrollArea> +#include <QAction> +#include <QClipboard> +#include <QPaintEvent> + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +class ScintillaQt : public QObject, public ScintillaBase { +	Q_OBJECT + +public: +	ScintillaQt(QAbstractScrollArea *parent); +	virtual ~ScintillaQt(); + +signals: +	void horizontalScrolled(int value); +	void verticalScrolled(int value); +	void horizontalRangeChanged(int max, int page); +	void verticalRangeChanged(int max, int page); + +	void notifyParent(SCNotification scn); +	void notifyChange(); + +	// Clients can use this hook to add additional +	// formats (e.g. rich text) to the MIME data. +	void aboutToCopy(QMimeData *data); + +	void command(uptr_t wParam, sptr_t lParam); + +private slots: +	void tick(); +	void onIdle(); +	void execCommand(QAction *action); +	void SelectionChanged(); + +private: +	virtual void Initialise(); +	virtual void Finalise(); +	virtual bool DragThreshold(Point ptStart, Point ptNow); +	virtual bool ValidCodePage(int codePage) const; + +private: +	virtual void ScrollText(int linesToMove); +	virtual void SetVerticalScrollPos(); +	virtual void SetHorizontalScrollPos(); +	virtual bool ModifyScrollBars(int nMax, int nPage); +	virtual void ReconfigureScrollBars(); +	void CopyToModeClipboard(const SelectionText &selectedText, QClipboard::Mode clipboardMode_); +	virtual void Copy(); +	virtual void CopyToClipboard(const SelectionText &selectedText); +	void PasteFromMode(QClipboard::Mode clipboardMode_); +	virtual void Paste(); +	virtual void ClaimSelection(); +	virtual void NotifyChange(); +	virtual void NotifyFocus(bool focus); +	virtual void NotifyParent(SCNotification scn); +	virtual void SetTicking(bool on); +	virtual bool SetIdle(bool on); +	virtual void SetMouseCapture(bool on); +	virtual bool HaveMouseCapture(); +	virtual void StartDrag(); +	int CharacterSetOfDocument() const; +	const char *CharacterSetIDOfDocument() const; +	QString StringFromDocument(const char *s) const; +	QByteArray BytesForDocument(const QString &text) const; +	virtual CaseFolder *CaseFolderForEncoding(); +	virtual std::string CaseMapString(const std::string &s, int caseMapping); + +	virtual void CreateCallTipWindow(PRectangle rc); +	virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true); +	virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); +	virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); + +	static sptr_t DirectFunction(ScintillaQt *sciThis, +				     unsigned int iMessage, uptr_t wParam, sptr_t lParam); + +protected: + +	void PartialPaint(const PRectangle &rect); + +	void DragEnter(const Point &point); +	void DragMove(const Point &point); +	void DragLeave(); +	void Drop(const Point &point, const QMimeData *data, bool move); + +private: +	QAbstractScrollArea *scrollArea; + +	int vMax, hMax;   // Scroll bar maximums. +	int vPage, hPage; // Scroll bar page sizes. + +	bool haveMouseCapture; +	bool dragWasDropped; +	int rectangularSelectionModifier; + +	friend class ScintillaEditBase; +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif // SCINTILLAQT_H diff --git a/qt/ScintillaEditPy/README b/qt/ScintillaEditPy/README new file mode 100644 index 000000000..c07397031 --- /dev/null +++ b/qt/ScintillaEditPy/README @@ -0,0 +1,85 @@ +README for building of ScintillaEditPy on Qt with PySide
 +
 +This directory is for building a Python encapsulation of Scintilla for use
 +with PySide. For C++ libraries see the README in the parent directory.
 +
 +	Prerequisites
 +	
 +PySide and ScintillaEditPy currently only support Python 2.x.
 +
 +CMake may be used to rebuild PySide and is required on Windows.
 +It can be downloaded from
 +http://www.cmake.org/cmake/resources/software.html
 +
 +On Windows, PySide only supports Visual C++ 2008. The "Visual Studio 2008
 +Command Prompt" should be used to run all build commands.
 +Visual C++ 2008 Express Edition can be downloaded from
 +http://msdn.microsoft.com/en-us/express/future/bb421473
 +
 +Download and install PySide. Instructions are on the PySide web site
 +http://developer.qt.nokia.com/wiki/Category:LanguageBindings::PySide::Downloads
 +
 +For Linux, there may be both PySide library packages and PySide development
 +files. Both should be installed as ScintillaEditPy needs the headers and
 +libraries from the development package and runs with the normal library package.
 +On apt-based systems, the packages are libshiboken-dev, shiboken, libpyside-dev,
 +and python-pyside. python-dev is also needed for Python language headers.
 +On yum-based systems the libpyside-dev package is called python-pyside-devel.
 +The qmake program may not be in the path and can be found with
 +"find /usr -name qmake".
 +
 +On Windows, the PySide library packages can be downloaded from 
 +http://developer.qt.nokia.com/wiki/PySide_Binaries_Windows
 +The PySide development files must be built from source using CMake as 
 +described on the PySide site. This will create a Unix-style set of [bin, include,
 +lib, and share] directories in packaging\setuptools\install-py<ver>-qt<qver>. 
 +There is no standard place for the PySide development files so copy them
 +to "\usr", creating it if needed.
 +
 +On OS X, a combined package with PySide libraries and PySide development
 +files can be downloaded from 
 +http://developer.qt.nokia.com/wiki/PySide_Binaries_MacOSX
 +This package works best in combination with the Qt libraries for Mac from
 +http://qt.nokia.com/downloads/downloads#qt-lib
 +
 +The path should be modified so that a Python 2.x interpreter and Qt's "qmake"
 +program can be run by typing "python" or "qmake".
 +
 +	Building
 +
 +There are several steps to building and they are encapsulated in the sepbuild.py
 +script which is run:
 +
 +python sepbuild.py
 +
 +This script first runs the WidgetGen.py script to fill out the ScintillaEdit.h, 
 +ScintillaEdit.cpp and ScintillaConstants.py files.
 +
 +A short file "sepbuild.pri" is written out which contains a series of version and 
 +path properties discovered by sepbuild.py which are used by qmake.
 +
 +Then it runs PySide's "shiboken" program to create C++ code that will act as a
 +bridge between Python and the C++ libraries. This code goes into the 
 +ScintillaEditPy/ScintillaEditPy directory. Several log files are produced which can
 +help uncover problems in the bridge if it fails to build.
 +
 +The qmake program is run to produce make files from ScintillaEditPy.pro.
 +
 +The system make program is then run to build the library. The library is located in
 +the scintilla/bin directory as ScintillaEditPy.so for Unix systems and 
 +ScintillaEditPy.pyd for Windows.
 +
 +A demonstration program can be run:
 +
 +python testswp.py
 +
 +The individual steps in the script can be run manually if wanted although the
 +shiboken program has complex arguments and differs between systems so run 
 +sepbuild.py and copy the section starting with a line containing "generatorrunner"
 +and continuing to "typesystem_ScintillaEdit.xml".
 +
 +On Windows, it is more difficult to set up an environment to debug ScintillaEditPy
 +since all the libraries have to be debug or all have to be release. The easy path 
 +is to always build for release with "nmake release".
 +
 +To remove generated code, run "python sepbuild.py --clean".
 diff --git a/qt/ScintillaEditPy/ScintillaConstants.py.template b/qt/ScintillaEditPy/ScintillaConstants.py.template new file mode 100644 index 000000000..31b82398d --- /dev/null +++ b/qt/ScintillaEditPy/ScintillaConstants.py.template @@ -0,0 +1,6 @@ +# ScintillaConstants.py +# Define all the symbolic constants from Scintilla.iface so Python code can use them +# Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware + +# ++Autogenerated -- start of section automatically generated from Scintilla.iface */ +# --Autogenerated -- end of section automatically generated from Scintilla.iface */ diff --git a/qt/ScintillaEditPy/ScintillaEditPy.pro b/qt/ScintillaEditPy/ScintillaEditPy.pro new file mode 100644 index 000000000..c578a314c --- /dev/null +++ b/qt/ScintillaEditPy/ScintillaEditPy.pro @@ -0,0 +1,116 @@ +TEMPLATE = lib +QT += core gui + +TARGET = ScintillaEditPy + +# Clear debug & release so that sepbuild.pri can set one or the other +CONFIG -= debug release + +include(sepbuild.pri) + +VERSION = $$SCINTILLA_VERSION + +win32 { +	DebugBuild { +		TARGET_EXT = _d.pyd +	} +	else { +		TARGET_EXT = .pyd +	} +} + +INCLUDEPATH += ../ScintillaEdit +INCLUDEPATH += ../ScintillaEditBase +INCLUDEPATH += ../../include ../../lexlib ../../src + +INCLUDEPATH += $$PY_INCLUDES + +INCLUDEPATH += $$SHIBOKEN_INCLUDES +INCLUDEPATH += $$PYSIDE_INCLUDES +INCLUDEPATH += $$PYSIDE_INCLUDES/QtCore +INCLUDEPATH += $$PYSIDE_INCLUDES/QtGui + +unix:!mac { +	LIBS += -ldl +	LIBS += `pkg-config pyside --libs` +} + +macx { +	# Only build for x64 for now +	# QMAKE_CFLAGS = -arch i386 -arch x86_64 +	# QMAKE_CXXFLAGS = -arch i386 -arch x86_64 +	# QMAKE_LFLAGS = -arch i386 -arch x86_64 +	LIBS += -L$$PY_LIBDIR -lpython$$PY_VERSION_SUFFIX +	LIBS += -L$$PYSIDE_LIB +	debug { +		LIBS += -lshiboken-python$$PY_VERSION_SUFFIX-dbg +		LIBS += -lpyside-python$$PY_VERSION_SUFFIX-dbg +	} +	else { +		LIBS += -lshiboken-python$$PY_VERSION_SUFFIX +		LIBS += -lpyside-python$$PY_VERSION_SUFFIX +	} +} + +win32 { +	DebugBuild { +		DEFINES += DEBUG +		LIBS += -lQtCored4 +	} +	else { +		LIBS += -lQtCore +	} +	LIBS += -L$$PY_PREFIX/libs # Note python lib is pulled in via a #pragma +	LIBS += -L$$PYSIDE_LIB +	# PySide uses x.y suffix on Windows even though Python uses xy +	DebugBuild { +		LIBS += -lshiboken-python$${PY_VERSION}_d +		LIBS += -lpyside-python$${PY_VERSION}_d +	} +	else { +		LIBS += -lshiboken-python$${PY_VERSION} +		LIBS += -lpyside-python$${PY_VERSION} +	} +} + +# Wrapper sources; notifyheader commented out due to shiboken bug +SOURCES += \ +	ScintillaEditPy/scintillaeditpy_module_wrapper.cpp \ +	ScintillaEditPy/sci_notifyheader_wrapper.cpp \ +	ScintillaEditPy/scnotification_wrapper.cpp \ +	ScintillaEditPy/scintillaeditbase_wrapper.cpp \ +	ScintillaEditPy/scintillaedit_wrapper.cpp \ +	ScintillaEditPy/scintilladocument_wrapper.cpp + +# ScintillaEdit sources + +SOURCES += \ +    ../ScintillaEdit/ScintillaEdit.cpp \ +    ../ScintillaEdit/ScintillaDocument.cpp \ +    ../ScintillaEditBase/PlatQt.cpp \ +    ../ScintillaEditBase/ScintillaQt.cpp \ +    ../ScintillaEditBase/ScintillaEditBase.cpp \ +    ../../src/*.cxx \ +    ../../lexlib/*.cxx \ +    ../../lexers/*.cxx + +# HEADERS is used to find what needs to be run through moc +HEADERS  += \ +    ../ScintillaEdit/ScintillaEdit.h \ +    ../ScintillaEdit/ScintillaDocument.h \ +    ../ScintillaEditBase/ScintillaQt.h \ +    ../ScintillaEditBase/ScintillaEditBase.h + +DEFINES += SCINTILLA_QT=1 MAKING_LIBRARY=1 SCI_LEXER=1 _CRT_SECURE_NO_DEPRECATE=1 + +DESTDIR = ../../bin + +unix:!mac { +	# Rename to not have 'lib' at start +	QMAKE_POST_LINK += rm -rf ../../bin/ScintillaEditPy.so && ln -s libScintillaEditPy.so ../../bin/ScintillaEditPy.so +} + +macx { +	# Rename to .so and not have 'lib' at start +	QMAKE_POST_LINK += rm -rf ../../bin/ScintillaEditPy.so && ln -s libScintillaEditPy.dylib ../../bin/ScintillaEditPy.so +} diff --git a/qt/ScintillaEditPy/global.h b/qt/ScintillaEditPy/global.h new file mode 100644 index 000000000..8b4be843a --- /dev/null +++ b/qt/ScintillaEditPy/global.h @@ -0,0 +1,4 @@ +#include "pyside_global.h" + +#include "ScintillaEditBase.h" +#include "ScintillaEdit.h" diff --git a/qt/ScintillaEditPy/sepbuild.py b/qt/ScintillaEditPy/sepbuild.py new file mode 100644 index 000000000..b694f15b0 --- /dev/null +++ b/qt/ScintillaEditPy/sepbuild.py @@ -0,0 +1,312 @@ +import distutils.sysconfig +import getopt +import glob +import os +import platform +import shutil +import subprocess +import stat +import sys + +sys.path.append(os.path.join("..", "ScintillaEdit")) +import WidgetGen + +# Decide up front which platform, treat anything other than Windows or OS X as Linux +PLAT_WINDOWS = platform.system() == "Windows" +PLAT_DARWIN = platform.system() == "Darwin" +PLAT_LINUX = not (PLAT_DARWIN or PLAT_WINDOWS) + +def IsFileNewer(name1, name2): +	""" Returns whether file with name1 is newer than file with name2.  Returns 1 +	if name2 doesn't exist. """ + +	if not os.path.exists(name1): +		return 0 + +	if not os.path.exists(name2): +		return 1 + +	mod_time1 = os.stat(name1)[stat.ST_MTIME] +	mod_time2 = os.stat(name2)[stat.ST_MTIME] +	return (mod_time1 > mod_time2) + +def textFromRun(args): +	(stdoutdata, stderrdata) = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE).communicate() +	return stdoutdata + +def runProgram(args, exitOnFailure): +	print(" ".join(args)) +	retcode = subprocess.call(" ".join(args), shell=True, stderr=subprocess.STDOUT) +	if retcode: +		print("Failed in " + " ".join(args) + " return code = " + str(retcode)) +		if exitOnFailure: +			sys.exit() + +def usage(): +	print("sepbuild.py [-h|--help][-c|--clean][-u|--underscore-names]") +	print("") +	print("Generate PySide wappers and build them.") +	print("") +	print("options:") +	print("") +	print("-c --clean remove all object and generated files") +	print("-b --pyside-base  Location of the PySide+Qt4 sandbox to use") +	print("-h --help  display this text") +	print("-d --debug=yes|no  force debug build (or non-debug build)") +	print("-u --underscore-names  use method_names consistent with GTK+ standards") + +modifyFunctionElement = """		<modify-function signature="%s">%s +		</modify-function> +""" + +injectCode = """ +			<inject-code class="target" position="beginning">%s +			</inject-code>""" + +injectCheckN = """ +				if (!cppArg%d) { +					PyErr_SetString(PyExc_ValueError, "Null string argument"); +					return 0; +				}""" + +def methodSignature(name, v, options): +	argTypes = "" +	p1Type = WidgetGen.cppAlias(v["Param1Type"]) +	if p1Type: +		argTypes = argTypes + p1Type +	p2Type = WidgetGen.cppAlias(v["Param2Type"]) +	if p2Type and v["Param2Type"] != "stringresult": +		if p1Type: +			argTypes = argTypes + ", " +		argTypes = argTypes + p2Type +	methodName = WidgetGen.normalisedName(name, options, v["FeatureType"]) +	constDeclarator = " const" if v["FeatureType"] == "get" else "" +	return methodName + "(" + argTypes + ")" + constDeclarator + +def printTypeSystemFile(f,out, options): +	for name in f.order: +		v = f.features[name] +		if v["Category"] != "Deprecated": +			feat = v["FeatureType"] +			if feat in ["fun", "get", "set"]: +				checks = "" +				if v["Param1Type"] == "string": +					checks = checks + (injectCheckN % 0) +				if v["Param2Type"] == "string": +					if v["Param1Type"] == "":	# Only arg 2 -> treat as first +						checks = checks + (injectCheckN % 0) +					else: +						checks = checks + (injectCheckN % 1) +				if checks: +					inject = injectCode % checks +					out.write(modifyFunctionElement % (methodSignature(name, v, options), inject)) +				#if v["Param1Type"] == "string": +				#	out.write("<string-xml>" + name + "</string-xml>\n") + +def doubleBackSlashes(s): +	# Quote backslashes so qmake does not produce warnings +	return s.replace("\\", "\\\\") + +class SepBuilder: +	def __init__(self): +		# Discover configuration parameters +		self.ScintillaEditIncludes = [".", "../ScintillaEdit", "../ScintillaEditBase", "../../include"] +		if PLAT_WINDOWS: +			self.MakeCommand = "nmake" +			self.MakeTarget = "release" +		else: +			self.MakeCommand = "make" +			self.MakeTarget = "" + +		if PLAT_DARWIN: +			self.QMakeOptions = "-spec macx-g++" +		else: +			self.QMakeOptions = "" + +		# Default to debug build if running in a debug build interpreter +		self.DebugBuild = hasattr(sys, 'getobjects') + +		# Python +		self.PyVersion = "%d.%d" % sys.version_info[:2] +		self.PyVersionSuffix = distutils.sysconfig.get_config_var("VERSION") +		self.PyIncludes = distutils.sysconfig.get_python_inc() +		self.PyPrefix = distutils.sysconfig.get_config_var("prefix") +		self.PyLibDir = distutils.sysconfig.get_config_var( +			("LIBDEST" if sys.platform == 'win32' else "LIBDIR")) + +		# Scintilla +		with open("../../version.txt") as f: +			version = f.read() +			self.ScintillaVersion = version[0] + '.' + version[1] + '.' + version[2] + +		# Qt default location from qmake +		self._SetQtIncludeBase(textFromRun("qmake -query QT_INSTALL_HEADERS").rstrip()) + +		# PySide default location +		# No standard for installing PySide development headers and libs on Windows so +		# choose /usr to be like Linux +		self._setPySideBase('\\usr' if PLAT_WINDOWS else '/usr') + +		self.ProInclude = "sepbuild.pri" + +		self.qtStyleInterface = True + +	def _setPySideBase(self, base): +		 +		self.PySideBase = base +		if PLAT_LINUX: +			self.PySideTypeSystem = textFromRun("pkg-config --variable=typesystemdir pyside").rstrip() +			self.PySideIncludeBase = textFromRun("pkg-config --variable=includedir pyside").rstrip() +			self.ShibokenIncludeBase = textFromRun("pkg-config --variable=includedir shiboken").rstrip() +		else: +			self.PySideTypeSystem = os.path.join(self.PySideBase, "share", "PySide", "typesystems") +			self.ShibokenIncludeBase = os.path.join(self.PySideBase, "include", "shiboken") +			self.PySideIncludeBase = os.path.join(self.PySideBase, "include", "PySide") + +		self.PySideIncludes = [ +			self.ShibokenIncludeBase, +			self.PySideIncludeBase, +			os.path.join(self.PySideIncludeBase, "QtCore"), +			os.path.join(self.PySideIncludeBase, "QtGui")] + +		self.PySideLibDir = os.path.join(self.PySideBase, "lib") +		self.AllIncludes = os.pathsep.join(self.QtIncludes + self.ScintillaEditIncludes + self.PySideIncludes) + +		self.ShibokenGenerator = "shiboken" +		# Is this still needed? It doesn't work with latest shiboken sources +		#if PLAT_DARWIN: +		#	# On OS X, can not automatically find Shiboken dylib so provide a full path +		#	self.ShibokenGenerator = os.path.join(self.PySideLibDir, "generatorrunner", "shiboken") + +	def generateAPI(self, args): +		os.chdir(os.path.join("..", "ScintillaEdit")) +		if not self.qtStyleInterface: +			args.insert(0, '--underscore-names') +		WidgetGen.main(args) +		f = WidgetGen.readInterface(False) +		os.chdir(os.path.join("..", "ScintillaEditPy")) +		options = {"qtStyle": self.qtStyleInterface} +		WidgetGen.Generate("typesystem_ScintillaEdit.xml.template", "typesystem_ScintillaEdit.xml", printTypeSystemFile, f, options) + +	def runGenerator(self): +		generatorrunner = "shiboken" +		for name in ('shiboken', 'generatorrunner'): +			if PLAT_WINDOWS: +				name += '.exe' +			name = os.path.join(self.PySideBase, "bin", name) +			if os.path.exists(name): +				generatorrunner = name +				break +				 +		args = [ +			generatorrunner, +			"--generator-set=" + self.ShibokenGenerator, +			"global.h ", +			"--avoid-protected-hack", +			"--enable-pyside-extensions", +			"--include-paths=" + self.AllIncludes, +			"--typesystem-paths=" + self.PySideTypeSystem, +			"--output-directory=.", +			"typesystem_ScintillaEdit.xml"] +		print(" ".join(args)) +		retcode = subprocess.call(" ".join(args), shell=True, stderr=subprocess.STDOUT) +		if retcode: +			print("Failed in generatorrunner", retcode) +			sys.exit() + +	def writeVariables(self): +		# Write variables needed into file to be included from project so it does not have to discover much +		with open(self.ProInclude, "w") as f: +			f.write("SCINTILLA_VERSION=" + self.ScintillaVersion + "\n") +			f.write("PY_VERSION=" + self.PyVersion + "\n") +			f.write("PY_VERSION_SUFFIX=" + self.PyVersionSuffix + "\n") +			f.write("PY_PREFIX=" + doubleBackSlashes(self.PyPrefix) + "\n") +			f.write("PY_INCLUDES=" + doubleBackSlashes(self.PyIncludes) + "\n") +			f.write("PY_LIBDIR=" + doubleBackSlashes(self.PyLibDir) + "\n") +			f.write("PYSIDE_INCLUDES=" + doubleBackSlashes(self.PySideIncludeBase) + "\n") +			f.write("PYSIDE_LIB=" + doubleBackSlashes(self.PySideLibDir) + "\n") +			f.write("SHIBOKEN_INCLUDES=" + doubleBackSlashes(self.ShibokenIncludeBase) + "\n") +			if self.DebugBuild: +				f.write("CONFIG += debug\n") +			else: +				f.write("CONFIG += release\n") + +	def make(self): +		runProgram(["qmake", self.QMakeOptions], exitOnFailure=True) +		runProgram([self.MakeCommand, self.MakeTarget], exitOnFailure=True) + +	def cleanEverything(self): +		self.generateAPI(["--clean"]) +		runProgram([self.MakeCommand, "distclean"], exitOnFailure=False) +		try: +			os.remove(self.ProInclude) +		except OSError: +			pass +		for logFile in glob.glob("*.log"): +			try: +				os.remove(logFile) +			except OSError: +				pass +		shutil.rmtree("debug", ignore_errors=True) +		shutil.rmtree("release", ignore_errors=True) +		shutil.rmtree("ScintillaEditPy", ignore_errors=True) + +	def buildEverything(self): +		cleanGenerated = False +		opts, args = getopt.getopt(sys.argv[1:], "hcdub", +					["help", "clean", "debug=",  +					"underscore-names", "pyside-base="]) +		for opt, arg in opts: +			if opt in ("-h", "--help"): +				usage() +				sys.exit() +			elif opt in ("-c", "--clean"): +				cleanGenerated = True +			elif opt in ("-d", "--debug"): +				self.DebugBuild = (arg == '' or arg.lower() == 'yes') +				if self.DebugBuild and sys.platform == 'win32': +					self.MakeTarget = 'debug' +			elif opt in ("-b", '--pyside-base'): +				self._SetQtIncludeBase(os.path.join(os.path.normpath(arg), 'include')) +				self._setPySideBase(os.path.normpath(arg)) +			elif opt in ("-u", "--underscore-names"): +				self.qtStyleInterface = False + +		if cleanGenerated: +			self.cleanEverything() +		else: +			self.writeVariables() +			self.generateAPI([""]) +			self.runGenerator() +			self.make() +			self.copyScintillaConstants() +			 +	def copyScintillaConstants(self): +		 +		orig = 'ScintillaConstants.py' +		dest = '../../bin/' + orig +		if IsFileNewer(dest, orig): +			return +		 +		f = open(orig, 'r') +		contents = f.read() +		f.close() +		 +		f = open(dest, 'w') +		f.write(contents) +		f.close() + +	def _SetQtIncludeBase(self, base): + +		self.QtIncludeBase = base +		self.QtIncludes = [self.QtIncludeBase] + [os.path.join(self.QtIncludeBase, sub) for sub in ["QtCore", "QtGui"]] +		# Set path so correct qmake is found +		path = os.environ.get('PATH', '').split(os.pathsep) +		qt_bin_dir = os.path.join(os.path.dirname(base), 'bin') +		if qt_bin_dir not in path: +			path.insert(0, qt_bin_dir) +			os.environ['PATH'] = os.pathsep.join(path) + +if __name__ == "__main__": +	sepBuild = SepBuilder() +	sepBuild.buildEverything() diff --git a/qt/ScintillaEditPy/testsepq.py b/qt/ScintillaEditPy/testsepq.py new file mode 100644 index 000000000..a3849295d --- /dev/null +++ b/qt/ScintillaEditPy/testsepq.py @@ -0,0 +1,157 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import sys + +from PySide.QtCore import * +from PySide.QtGui import * + +import ScintillaConstants as sci + +sys.path.append("../..") +from bin import ScintillaEditPy + +txtInit =	"int main(int argc, char **argv) {\n" \ +		"    // Start up the gnome\n" \ +		"    gnome_init(\"stest\", \"1.0\", argc, argv);\n}\n"; + +keywords = \ +		"and and_eq asm auto bitand bitor bool break " \ +		"case catch char class compl const const_cast continue " \ +		"default delete do double dynamic_cast else enum explicit export extern false float for " \ +		"friend goto if inline int long mutable namespace new not not_eq " \ +		"operator or or_eq private protected public " \ +		"register reinterpret_cast return short signed sizeof static static_cast struct switch " \ +		"template this throw true try typedef typeid typename union unsigned using " \ +		"virtual void volatile wchar_t while xor xor_eq"; + +def uriDropped(): +	print "uriDropped" + +class Form(QDialog): + +	def __init__(self, parent=None): +		super(Form, self).__init__(parent) +		self.resize(460,300) +		# Create widgets +		self.edit = ScintillaEditPy.ScintillaEdit(self) +		self.edit.uriDropped.connect(uriDropped) +		self.edit.command.connect(self.receive_command) +		self.edit.notify.connect(self.receive_notification) + +		self.edit.styleClearAll() +		self.edit.setMarginWidthN(0, 35) +		self.edit.setScrollWidth(200) +		self.edit.setScrollWidthTracking(1) +		self.edit.setLexer(sci.SCLEX_CPP) +		self.edit.styleSetFore(sci.SCE_C_COMMENT, 0x008000) +		self.edit.styleSetFore(sci.SCE_C_COMMENTLINE, 0x008000) +		self.edit.styleSetFore(sci.SCE_C_COMMENTDOC, 0x008040) +		self.edit.styleSetItalic(sci.SCE_C_COMMENTDOC, 1) +		self.edit.styleSetFore(sci.SCE_C_NUMBER, 0x808000) +		self.edit.styleSetFore(sci.SCE_C_WORD, 0x800000) +		self.edit.styleSetBold(sci.SCE_C_WORD, True) +		self.edit.styleSetFore(sci.SCE_C_STRING, 0x800080) +		self.edit.styleSetFore(sci.SCE_C_PREPROCESSOR, 0x008080) +		self.edit.styleSetBold(sci.SCE_C_OPERATOR, True) +		self.edit.setMultipleSelection(1) +		self.edit.setVirtualSpaceOptions( +			sci.SCVS_RECTANGULARSELECTION | sci.SCVS_USERACCESSIBLE) +		self.edit.setAdditionalSelectionTyping(1) + +		self.edit.styleSetFore(sci.STYLE_INDENTGUIDE, 0x808080) +		self.edit.setIndentationGuides(sci.SC_IV_LOOKBOTH) + +		self.edit.setKeyWords(0, keywords) +		self.edit.addText(len(txtInit), txtInit) +		self.edit.setSel(1,10) +		retriever = str(self.edit.getLine(1)) +		print(type(retriever), len(retriever)) +		print('[' + retriever + ']') +		someText = str(self.edit.textRange(2,5)) +		print(len(someText), '[' + someText + ']') +		someText = self.edit.getCurLine(100) +		print(len(someText), '[' + someText + ']') +		someText = self.edit.styleGetFont(1) +		print(len(someText), '[' + someText + ']') +		someText = self.edit.getSelText() +		print(len(someText), '[' + someText + ']') +		someText = self.edit.getTag(1) +		print(len(someText), '[' + someText + ']') +		someText = self.edit.autoCGetCurrentText() +		print(len(someText), '[' + someText + ']') +		someText = self.edit.annotationText(1) +		print(len(someText), '[' + someText + ']') +		someText = self.edit.annotationStyles(1) +		print(len(someText), '[' + someText + ']') +		someText = self.edit.describeKeyWordSets() +		print(len(someText), '[' + someText + ']') +		someText = self.edit.propertyNames() +		print(len(someText), '[' + someText + ']') +		self.edit.setProperty("fold", "1") +		someText = self.edit.getProperty("fold") +		print(len(someText), '[' + someText + ']') +		someText = self.edit.getPropertyExpanded("fold") +		print(len(someText), '[' + someText + ']') +		someText = self.edit.lexerLanguage() +		print(len(someText), '[' + someText + ']') +		someText = self.edit.describeProperty("styling.within.preprocessor") +		print(len(someText), '[' + someText + ']') + +		xx = self.edit.findText(0, "main", 0, 25) +		print(type(xx), xx) +		print("isBold", self.edit.styleBold(sci.SCE_C_WORD)) + +		# Retrieve the document and write into it +		doc = self.edit.get_doc() +		doc.insert_string(40, "***") +		stars = doc.get_char_range(40,3) +		assert stars == "***" + +		# Create a new independent document and attach it to the editor +		doc = ScintillaEditPy.ScintillaDocument() +		doc.insert_string(0, "/***/\nif(a)\n") +		self.edit.set_doc(doc) +		self.edit.setLexer(sci.SCLEX_CPP) + +	def Call(self, message, wParam=0, lParam=0): +		return self.edit.send(message, wParam, lParam) + +	def resizeEvent(self, e): +		self.edit.resize(e.size().width(), e.size().height()) + +	def receive_command(self, wParam, lParam): +		# Show underline at start when focussed +		notifyCode = wParam >> 16 +		if (notifyCode == sci.SCEN_SETFOCUS) or (notifyCode == sci.SCEN_KILLFOCUS): +			self.edit.setIndicatorCurrent(sci.INDIC_CONTAINER); +			self.edit.indicatorClearRange(0, self.edit.length()) +			if notifyCode == sci.SCEN_SETFOCUS: +				self.edit.indicatorFillRange(0, 2); + +	def receive_notification(self, scn): +		if scn.nmhdr.code == sci.SCN_CHARADDED: +			print "Char %02X" % scn.ch +		elif scn.nmhdr.code == sci.SCN_SAVEPOINTREACHED: +			print "Saved" +		elif scn.nmhdr.code == sci.SCN_SAVEPOINTLEFT: +			print "Unsaved" +		elif scn.nmhdr.code == sci.SCN_MODIFIED: +			print "Modified" +		elif scn.nmhdr.code == sci.SCN_UPDATEUI: +			print "Update UI" +		elif scn.nmhdr.code == sci.SCN_PAINTED: +			#print "Painted" +			pass +		else: +			print "Notification", scn.nmhdr.code +			pass + +if __name__ == '__main__': +    # Create the Qt Application +    app = QApplication(sys.argv) +    # Create and show the form +    form = Form() +    form.show() +    # Run the main Qt loop +    sys.exit(app.exec_()) diff --git a/qt/ScintillaEditPy/typesystem_ScintillaEdit.xml.template b/qt/ScintillaEditPy/typesystem_ScintillaEdit.xml.template new file mode 100644 index 000000000..4c2c897a2 --- /dev/null +++ b/qt/ScintillaEditPy/typesystem_ScintillaEdit.xml.template @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<typesystem package="ScintillaEditPy"> +	<load-typesystem name="typesystem_core.xml" generate="no" /> +	<load-typesystem name="typesystem_gui_common.xml" generate="no"/> +	<primitive-type name="sptr_t"/> +	<primitive-type name="uptr_t"/> +	<value-type name="Sci_NotifyHeader" /> +	<rejection class="Sci_NotifyHeader" field-name="hwndFrom" /> +	<value-type name="SCNotification" /> +	<object-type name="ScintillaEditBase" /> +	<object-type name="ScintillaEdit"> +	<!-- ++Autogenerated start of section automatically generated from Scintilla.iface --> +	<!-- ~~Autogenerated end of section automatically generated from Scintilla.iface --> +	</object-type> +	<object-type name="ScintillaDocument" /> +</typesystem> | 
