diff options
author | nyamatongwe <unknown> | 2012-05-17 12:46:29 +1000 |
---|---|---|
committer | nyamatongwe <unknown> | 2012-05-17 12:46:29 +1000 |
commit | bf8b35542c4be4b8eebb71ca2ad518da2e12b850 (patch) | |
tree | eab094df23f84c33c7ed03f3412a63a060d31eac | |
parent | 29c92749f25f24795ecd9fec2b6705129ebbd654 (diff) | |
download | scintilla-mirror-bf8b35542c4be4b8eebb71ca2ad518da2e12b850.tar.gz |
Qt platform layer added. Based on an implementation from Jason Haslam
at Scientific Toolworks, Inc. with additions performed for Wingware.
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> |