From aa11c3ea48a67eee384605cace71e3111745b68f Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Mon, 5 Dec 2016 14:23:34 +0100 Subject: [PATCH] Update Scintilla to version 3.7.1 --- scintilla/Makefile.am | 4 + scintilla/gtk/PlatGTK.cxx | 7 +- scintilla/gtk/ScintillaGTK.cxx | 401 ++++------ scintilla/gtk/ScintillaGTK.h | 287 +++++++ scintilla/gtk/ScintillaGTKAccessible.cxx | 1205 ++++++++++++++++++++++++++++++ scintilla/gtk/ScintillaGTKAccessible.h | 171 +++++ scintilla/include/Platform.h | 4 - scintilla/include/SciLexer.h | 10 + scintilla/include/Scintilla.h | 66 +- scintilla/include/Scintilla.iface | 59 +- scintilla/lexers/LexCPP.cxx | 2 +- scintilla/lexers/LexMatlab.cxx | 117 ++- scintilla/lexers/LexPerl.cxx | 2 +- scintilla/lexers/LexYAML.cxx | 76 +- scintilla/lexlib/LexerModule.h | 2 +- scintilla/scintilla_changes.patch | 5 +- scintilla/src/CaseConvert.cxx | 9 + scintilla/src/CaseConvert.h | 3 + scintilla/src/CellBuffer.h | 16 - scintilla/src/ContractionState.cxx | 32 +- scintilla/src/ContractionState.h | 8 + scintilla/src/Document.cxx | 12 +- scintilla/src/EditModel.cxx | 1 + scintilla/src/EditModel.h | 1 + scintilla/src/EditView.cxx | 236 +++++- scintilla/src/EditView.h | 8 +- scintilla/src/Editor.cxx | 78 +- scintilla/src/Editor.h | 9 +- scintilla/src/Indicator.cxx | 15 +- scintilla/src/Indicator.h | 2 +- scintilla/src/PositionCache.cxx | 8 +- scintilla/src/PositionCache.h | 12 +- scintilla/src/ScintillaBase.cxx | 14 +- scintilla/src/ScintillaBase.h | 4 +- scintilla/src/SparseVector.h | 186 +++++ scintilla/src/ViewStyle.cxx | 13 + scintilla/src/ViewStyle.h | 4 + scintilla/version.txt | 2 +- 38 files changed, 2596 insertions(+), 495 deletions(-) create mode 100644 scintilla/gtk/ScintillaGTK.h create mode 100644 scintilla/gtk/ScintillaGTKAccessible.cxx create mode 100644 scintilla/gtk/ScintillaGTKAccessible.h create mode 100644 scintilla/src/SparseVector.h diff --git a/scintilla/Makefile.am b/scintilla/Makefile.am index e5b0ce037..03d5c1419 100644 --- a/scintilla/Makefile.am +++ b/scintilla/Makefile.am @@ -53,6 +53,9 @@ SRCS= \ gtk/Converter.h \ gtk/PlatGTK.cxx \ gtk/ScintillaGTK.cxx \ +gtk/ScintillaGTK.h \ +gtk/ScintillaGTKAccessible.cxx \ +gtk/ScintillaGTKAccessible.h \ gtk/scintilla-marshal.c \ gtk/scintilla-marshal.h \ lexlib/Accessor.cxx \ @@ -132,6 +135,7 @@ src/ScintillaBase.cxx \ src/ScintillaBase.h \ src/Selection.cxx \ src/Selection.h \ +src/SparseVector.h \ src/SplitVector.h \ src/Style.cxx \ src/Style.h \ diff --git a/scintilla/gtk/PlatGTK.cxx b/scintilla/gtk/PlatGTK.cxx index 1629818ef..055a318a4 100644 --- a/scintilla/gtk/PlatGTK.cxx +++ b/scintilla/gtk/PlatGTK.cxx @@ -28,11 +28,6 @@ #include "XPM.h" #include "UniConversion.h" -#if defined(__clang__) -// Clang 3.0 incorrectly displays sentinel warnings. Fixed by clang 3.1. -#pragma GCC diagnostic ignored "-Wsentinel" -#endif - #include "Converter.h" static const double kPi = 3.14159265358979323846; @@ -550,7 +545,7 @@ static void PathRoundRectangle(cairo_t *context, double left, double top, double } void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill, - ColourDesired outline, int alphaOutline, int flags) { + ColourDesired outline, int alphaOutline, int /*flags*/) { if (context && rc.Width() > 0) { ColourDesired cdFill(fill.AsLong()); cairo_set_source_rgba(context, diff --git a/scintilla/gtk/ScintillaGTK.cxx b/scintilla/gtk/ScintillaGTK.cxx index a4cabc458..b9e5f4fd4 100644 --- a/scintilla/gtk/ScintillaGTK.cxx +++ b/scintilla/gtk/ScintillaGTK.cxx @@ -72,15 +72,12 @@ #include "ExternalLexer.h" #endif +#include "ScintillaGTK.h" #include "scintilla-marshal.h" +#include "ScintillaGTKAccessible.h" #include "Converter.h" -#if defined(__clang__) -// Clang 3.0 incorrectly displays sentinel warnings. Fixed by clang 3.1. -#pragma GCC diagnostic ignored "-Wsentinel" -#endif - #if GTK_CHECK_VERSION(2,20,0) #define IS_WIDGET_REALIZED(w) (gtk_widget_get_realized(GTK_WIDGET(w))) #define IS_WIDGET_MAPPED(w) (gtk_widget_get_mapped(GTK_WIDGET(w))) @@ -105,8 +102,6 @@ static GdkWindow *WindowFromWidget(GtkWidget *w) { #pragma warning(disable: 4505) #endif -#define OBJECT_CLASS GObjectClass - #ifdef SCI_NAMESPACE using namespace Scintilla; #endif @@ -118,230 +113,6 @@ static GdkWindow *PWindow(const Window &w) { extern std::string UTF8FromLatin1(const char *s, int len); -class ScintillaGTK : public ScintillaBase { - _ScintillaObject *sci; - Window wText; - Window scrollbarv; - Window scrollbarh; - GtkAdjustment *adjustmentv; - GtkAdjustment *adjustmenth; - int verticalScrollBarWidth; - int horizontalScrollBarHeight; - - SelectionText primary; - - GdkEventButton *evbtn; - bool capturedMouse; - bool dragWasDropped; - int lastKey; - int rectangularSelectionModifier; - - GtkWidgetClass *parentClass; - - static GdkAtom atomClipboard; - static GdkAtom atomUTF8; - static GdkAtom atomString; - static GdkAtom atomUriList; - static GdkAtom atomDROPFILES_DND; - GdkAtom atomSought; - -#if PLAT_GTK_WIN32 - CLIPFORMAT cfColumnSelect; -#endif - - Window wPreedit; - Window wPreeditDraw; - GtkIMContext *im_context; - PangoScript lastNonCommonScript; - - // Wheel mouse support - unsigned int linesPerScroll; - GTimeVal lastWheelMouseTime; - gint lastWheelMouseDirection; - gint wheelMouseIntensity; - -#if GTK_CHECK_VERSION(3,0,0) - cairo_rectangle_list_t *rgnUpdate; -#else - GdkRegion *rgnUpdate; -#endif - bool repaintFullWindow; - - guint styleIdleID; - - // Private so ScintillaGTK objects can not be copied - ScintillaGTK(const ScintillaGTK &); - ScintillaGTK &operator=(const ScintillaGTK &); - -public: - explicit ScintillaGTK(_ScintillaObject *sci_); - virtual ~ScintillaGTK(); - static void ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class); -private: - virtual void Initialise(); - virtual void Finalise(); - virtual bool AbandonPaint(); - virtual void DisplayCursor(Window::Cursor c); - virtual bool DragThreshold(Point ptStart, Point ptNow); - virtual void StartDrag(); - int TargetAsUTF8(char *text); - int EncodedFromUTF8(char *utf8, char *encoded) const; - virtual bool ValidCodePage(int codePage) const; -public: // Public for scintilla_send_message - virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); -private: - virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); - struct TimeThunk { - TickReason reason; - ScintillaGTK *scintilla; - guint timer; - TimeThunk() : reason(tickCaret), scintilla(NULL), timer(0) {} - }; - TimeThunk timers[tickDwell+1]; - virtual bool FineTickerAvailable(); - virtual bool FineTickerRunning(TickReason reason); - virtual void FineTickerStart(TickReason reason, int millis, int tolerance); - virtual void FineTickerCancel(TickReason reason); - virtual bool SetIdle(bool on); - virtual void SetMouseCapture(bool on); - virtual bool HaveMouseCapture(); - virtual bool PaintContains(PRectangle rc); - void FullPaint(); - virtual PRectangle GetClientRectangle() const; - virtual void ScrollText(int linesToMove); - virtual void SetVerticalScrollPos(); - virtual void SetHorizontalScrollPos(); - virtual bool ModifyScrollBars(int nMax, int nPage); - void ReconfigureScrollBars(); - virtual void NotifyChange(); - virtual void NotifyFocus(bool focus); - virtual void NotifyParent(SCNotification scn); - void NotifyKey(int key, int modifiers); - void NotifyURIDropped(const char *list); - const char *CharacterSetID() const; - virtual CaseFolder *CaseFolderForEncoding(); - virtual std::string CaseMapString(const std::string &s, int caseMapping); - virtual int KeyDefault(int key, int modifiers); - virtual void CopyToClipboard(const SelectionText &selectedText); - virtual void Copy(); - virtual void Paste(); - virtual void CreateCallTipWindow(PRectangle rc); - virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true); - bool OwnPrimarySelection(); - virtual void ClaimSelection(); - void GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText); - void ReceivedSelection(GtkSelectionData *selection_data); - void ReceivedDrop(GtkSelectionData *selection_data); - static void GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *selected); - void StoreOnClipboard(SelectionText *clipText); - static void ClipboardGetSelection(GtkClipboard* clip, GtkSelectionData *selection_data, guint info, void *data); - static void ClipboardClearSelection(GtkClipboard* clip, void *data); - - void UnclaimSelection(GdkEventSelection *selection_event); - void Resize(int width, int height); - - // Callback functions - void RealizeThis(GtkWidget *widget); - static void Realize(GtkWidget *widget); - void UnRealizeThis(GtkWidget *widget); - static void UnRealize(GtkWidget *widget); - void MapThis(); - static void Map(GtkWidget *widget); - void UnMapThis(); - static void UnMap(GtkWidget *widget); - gint FocusInThis(GtkWidget *widget); - static gint FocusIn(GtkWidget *widget, GdkEventFocus *event); - gint FocusOutThis(GtkWidget *widget); - static gint FocusOut(GtkWidget *widget, GdkEventFocus *event); - static void SizeRequest(GtkWidget *widget, GtkRequisition *requisition); -#if GTK_CHECK_VERSION(3,0,0) - static void GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth); - static void GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight); -#endif - static void SizeAllocate(GtkWidget *widget, GtkAllocation *allocation); -#if GTK_CHECK_VERSION(3,0,0) - gboolean DrawTextThis(cairo_t *cr); - static gboolean DrawText(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis); - gboolean DrawThis(cairo_t *cr); - static gboolean DrawMain(GtkWidget *widget, cairo_t *cr); -#else - gboolean ExposeTextThis(GtkWidget *widget, GdkEventExpose *ose); - static gboolean ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis); - gboolean Expose(GtkWidget *widget, GdkEventExpose *ose); - static gboolean ExposeMain(GtkWidget *widget, GdkEventExpose *ose); -#endif - void ForAll(GtkCallback callback, gpointer callback_data); - static void MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); - - static void ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis); - static void ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis); - gint PressThis(GdkEventButton *event); - static gint Press(GtkWidget *widget, GdkEventButton *event); - static gint MouseRelease(GtkWidget *widget, GdkEventButton *event); - static gint ScrollEvent(GtkWidget *widget, GdkEventScroll *event); - static gint Motion(GtkWidget *widget, GdkEventMotion *event); - gboolean KeyThis(GdkEventKey *event); - static gboolean KeyPress(GtkWidget *widget, GdkEventKey *event); - static gboolean KeyRelease(GtkWidget *widget, GdkEventKey *event); -#if GTK_CHECK_VERSION(3,0,0) - gboolean DrawPreeditThis(GtkWidget *widget, cairo_t *cr); - static gboolean DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis); -#else - gboolean ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose); - static gboolean ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis); -#endif - - bool KoreanIME(); - void CommitThis(char *str); - static void Commit(GtkIMContext *context, char *str, ScintillaGTK *sciThis); - void PreeditChangedInlineThis(); - void PreeditChangedWindowedThis(); - static void PreeditChanged(GtkIMContext *context, ScintillaGTK *sciThis); - void MoveImeCarets(int pos); - void DrawImeIndicator(int indicator, int len); - void SetCandidateWindowPos(); - - static void StyleSetText(GtkWidget *widget, GtkStyle *previous, void*); - static void RealizeText(GtkWidget *widget, void*); - static void Dispose(GObject *object); - static void Destroy(GObject *object); - static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data, - guint time); - static void ClipboardReceived(GtkClipboard *clipboard, GtkSelectionData *selection_data, - gpointer data); - static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data, - guint info, guint time); - static gint SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event); - gboolean DragMotionThis(GdkDragContext *context, gint x, gint y, guint dragtime); - static gboolean DragMotion(GtkWidget *widget, GdkDragContext *context, - gint x, gint y, guint dragtime); - static void DragLeave(GtkWidget *widget, GdkDragContext *context, - guint time); - static void DragEnd(GtkWidget *widget, GdkDragContext *context); - static gboolean Drop(GtkWidget *widget, GdkDragContext *context, - gint x, gint y, guint time); - static void DragDataReceived(GtkWidget *widget, GdkDragContext *context, - gint x, gint y, GtkSelectionData *selection_data, guint info, guint time); - static void DragDataGet(GtkWidget *widget, GdkDragContext *context, - GtkSelectionData *selection_data, guint info, guint time); - static gboolean TimeOut(gpointer ptt); - static gboolean IdleCallback(gpointer pSci); - static gboolean StyleIdle(gpointer pSci); - virtual void IdleWork(); - virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo); - static void PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis); - -#if GTK_CHECK_VERSION(3,0,0) - static gboolean DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip); -#else - static gboolean ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ct); -#endif - static gboolean PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis); - - static sptr_t DirectFunction(sptr_t ptr, - unsigned int iMessage, uptr_t wParam, sptr_t lParam); -}; - enum { COMMAND_SIGNAL, NOTIFY_SIGNAL, @@ -381,7 +152,7 @@ static GtkWidget *PWidget(Window &w) { return static_cast(w.GetID()); } -static ScintillaGTK *ScintillaFromWidget(GtkWidget *widget) { +ScintillaGTK *ScintillaGTK::FromWidget(GtkWidget *widget) { ScintillaObject *scio = SCINTILLA(widget); return static_cast(scio->pscin); } @@ -396,7 +167,8 @@ ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) : wheelMouseIntensity(0), rgnUpdate(0), repaintFullWindow(false), - styleIdleID(0) { + styleIdleID(0), + accessible(0) { sci = sci_; wMain = GTK_WIDGET(sci); @@ -529,7 +301,7 @@ void ScintillaGTK::RealizeThis(GtkWidget *widget) { } void ScintillaGTK::Realize(GtkWidget *widget) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); sciThis->RealizeThis(widget); } @@ -564,7 +336,7 @@ void ScintillaGTK::UnRealizeThis(GtkWidget *widget) { } void ScintillaGTK::UnRealize(GtkWidget *widget) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); sciThis->UnRealizeThis(widget); } @@ -598,7 +370,7 @@ void ScintillaGTK::MapThis() { } void ScintillaGTK::Map(GtkWidget *widget) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); sciThis->MapThis(); } @@ -623,7 +395,7 @@ void ScintillaGTK::UnMapThis() { } void ScintillaGTK::UnMap(GtkWidget *widget) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); sciThis->UnMapThis(); } @@ -640,7 +412,7 @@ void ScintillaGTK::ForAll(GtkCallback callback, gpointer callback_data) { } void ScintillaGTK::MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { - ScintillaGTK *sciThis = ScintillaFromWidget((GtkWidget *)container); + ScintillaGTK *sciThis = FromWidget((GtkWidget *)container); if (callback != NULL && include_internals) { sciThis->ForAll(callback, callback_data); @@ -674,7 +446,7 @@ public: } -gint ScintillaGTK::FocusInThis(GtkWidget *widget) { +gint ScintillaGTK::FocusInThis(GtkWidget *) { try { SetFocusState(true); if (im_context != NULL) { @@ -696,11 +468,11 @@ gint ScintillaGTK::FocusInThis(GtkWidget *widget) { } gint ScintillaGTK::FocusIn(GtkWidget *widget, GdkEventFocus * /*event*/) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); return sciThis->FocusInThis(widget); } -gint ScintillaGTK::FocusOutThis(GtkWidget *widget) { +gint ScintillaGTK::FocusOutThis(GtkWidget *) { try { SetFocusState(false); @@ -716,12 +488,12 @@ gint ScintillaGTK::FocusOutThis(GtkWidget *widget) { } gint ScintillaGTK::FocusOut(GtkWidget *widget, GdkEventFocus * /*event*/) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); return sciThis->FocusOutThis(widget); } void ScintillaGTK::SizeRequest(GtkWidget *widget, GtkRequisition *requisition) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); requisition->width = 1; requisition->height = 1; GtkRequisition child_requisition; @@ -751,7 +523,7 @@ void ScintillaGTK::GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gi #endif void ScintillaGTK::SizeAllocate(GtkWidget *widget, GtkAllocation *allocation) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { gtk_widget_set_allocation(widget, allocation); if (IS_WIDGET_REALIZED(widget)) @@ -888,6 +660,12 @@ void ScintillaGTK::Finalise() { for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { FineTickerCancel(tr); } + if (accessible) { + gtk_accessible_set_widget(GTK_ACCESSIBLE(accessible), NULL); + g_object_unref(accessible); + accessible = 0; + } + ScintillaBase::Finalise(); } @@ -931,8 +709,11 @@ void ScintillaGTK::StartDrag() { #endif } -static std::string ConvertText(const char *s, size_t len, const char *charSetDest, - const char *charSetSource, bool transliterations, bool silent=false) { +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif +std::string ConvertText(const char *s, size_t len, const char *charSetDest, + const char *charSetSource, bool transliterations, bool silent) { // s is not const because of different versions of iconv disagreeing about const std::string destForm; Converter conv(charSetDest, charSetSource, transliterations); @@ -963,6 +744,9 @@ static std::string ConvertText(const char *s, size_t len, const char *charSetDes } return destForm; } +#ifdef SCI_NAMESPACE +} +#endif // Returns the target converted to UTF8. // Return the length in bytes. @@ -1063,6 +847,17 @@ sptr_t ScintillaGTK::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam case SCI_GETRECTANGULARSELECTIONMODIFIER: return rectangularSelectionModifier; + case SCI_SETREADONLY: { + sptr_t ret = ScintillaBase::WndProc(iMessage, wParam, lParam); + if (accessible) { + ScintillaGTKAccessible *sciAccessible = ScintillaGTKAccessible::FromAccessible(accessible); + if (sciAccessible) { + sciAccessible->NotifyReadOnly(); + } + } + return ret; + } + default: return ScintillaBase::WndProc(iMessage, wParam, lParam); } @@ -1456,18 +1251,37 @@ void ScintillaGTK::Copy() { } } -void ScintillaGTK::ClipboardReceived(GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data) { - ScintillaGTK *sciThis = static_cast(data); - sciThis->ReceivedSelection(selection_data); -} - void ScintillaGTK::Paste() { atomSought = atomUTF8; GtkClipboard *clipBoard = gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain)), atomClipboard); if (clipBoard == NULL) return; - gtk_clipboard_request_contents(clipBoard, atomSought, ClipboardReceived, this); + + // helper class for the asynchronous paste not to risk calling in a destroyed ScintillaGTK + class Helper : GObjectWatcher { + ScintillaGTK *sci; + + virtual void Destroyed() { + sci = 0; + } + + public: + Helper(ScintillaGTK *sci_) : + GObjectWatcher(G_OBJECT(PWidget(sci_->wMain))), + sci(sci_) { + } + + static void ClipboardReceived(GtkClipboard *, GtkSelectionData *selection_data, gpointer data) { + Helper *self = static_cast(data); + if (self->sci != 0) { + self->sci->ReceivedSelection(selection_data); + } + delete self; + } + }; + + gtk_clipboard_request_contents(clipBoard, atomSought, Helper::ClipboardReceived, new Helper(this)); } void ScintillaGTK::CreateCallTipWindow(PRectangle rc) { @@ -1889,7 +1703,7 @@ gint ScintillaGTK::PressThis(GdkEventButton *event) { } else if (event->button == 3) { if (!PointInSelection(pt)) SetEmptySelection(PositionFromLocation(pt)); - if (displayPopupMenu) { + if (ShouldDisplayPopup(pt)) { // PopUp menu // Convert to screen int ox = 0; @@ -1897,6 +1711,15 @@ gint ScintillaGTK::PressThis(GdkEventButton *event) { gdk_window_get_origin(PWindow(wMain), &ox, &oy); ContextMenu(Point(pt.x + ox, pt.y + oy)); } else { +#if PLAT_GTK_MACOSX + bool meta = ctrl; + // GDK reports the Command modifer key as GDK_MOD2_MASK for button events, + // not GDK_META_MASK like in key events. + ctrl = (event->state & GDK_MOD2_MASK) != 0; +#else + bool meta = false; +#endif + RightButtonDownWithModifiers(pt, event->time, ModifierFlags(shift, ctrl, alt, meta)); return FALSE; } } else if (event->button == 4) { @@ -1921,12 +1744,12 @@ gint ScintillaGTK::PressThis(GdkEventButton *event) { gint ScintillaGTK::Press(GtkWidget *widget, GdkEventButton *event) { if (event->window != WindowFromWidget(widget)) return FALSE; - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); return sciThis->PressThis(event); } gint ScintillaGTK::MouseRelease(GtkWidget *widget, GdkEventButton *event) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { //Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state); if (!sciThis->HaveMouseCapture()) @@ -1952,7 +1775,7 @@ gint ScintillaGTK::MouseRelease(GtkWidget *widget, GdkEventButton *event) { // win32gtk and GTK >= 2 use SCROLL_* events instead of passing the // button4/5/6/7 events to the GTK app gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { if (widget == NULL || event == NULL) @@ -2036,7 +1859,7 @@ gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) { } gint ScintillaGTK::Motion(GtkWidget *widget, GdkEventMotion *event) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { //Platform::DebugPrintf("Motion %x %d\n",sciThis,event->time); if (event->window != WindowFromWidget(widget)) @@ -2267,13 +2090,13 @@ gboolean ScintillaGTK::KeyThis(GdkEventKey *event) { } gboolean ScintillaGTK::KeyPress(GtkWidget *widget, GdkEventKey *event) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); return sciThis->KeyThis(event); } gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) { //Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string); - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); if (gtk_im_context_filter_keypress(sciThis->im_context, event)) { return TRUE; } @@ -2282,7 +2105,7 @@ gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) { #if GTK_CHECK_VERSION(3,0,0) -gboolean ScintillaGTK::DrawPreeditThis(GtkWidget *widget, cairo_t *cr) { +gboolean ScintillaGTK::DrawPreeditThis(GtkWidget *, cairo_t *cr) { try { PreEditString pes(im_context); PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str); @@ -2304,7 +2127,7 @@ gboolean ScintillaGTK::DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK #else -gboolean ScintillaGTK::ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose) { +gboolean ScintillaGTK::ExposePreeditThis(GtkWidget *widget, GdkEventExpose *) { try { PreEditString pes(im_context); PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str); @@ -2721,7 +2544,7 @@ gboolean ScintillaGTK::DrawThis(cairo_t *cr) { } gboolean ScintillaGTK::DrawMain(GtkWidget *widget, cairo_t *cr) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); return sciThis->DrawThis(cr); } @@ -2771,7 +2594,7 @@ gboolean ScintillaGTK::ExposeText(GtkWidget *widget, GdkEventExpose *ose, Scinti } gboolean ScintillaGTK::ExposeMain(GtkWidget *widget, GdkEventExpose *ose) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); //Platform::DebugPrintf("Expose Main %0d,%0d %0d,%0d\n", //ose->area.x, ose->area.y, ose->area.width, ose->area.height); return sciThis->Expose(widget, ose); @@ -2814,14 +2637,14 @@ void ScintillaGTK::ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) { void ScintillaGTK::SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data, guint) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); //Platform::DebugPrintf("Selection received\n"); sciThis->ReceivedSelection(selection_data); } void ScintillaGTK::SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data, guint info, guint) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { //Platform::DebugPrintf("Selection get\n"); if (SelectionOfGSD(selection_data) == GDK_SELECTION_PRIMARY) { @@ -2836,7 +2659,7 @@ void ScintillaGTK::SelectionGet(GtkWidget *widget, } gint ScintillaGTK::SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); //Platform::DebugPrintf("Selection clear\n"); sciThis->UnclaimSelection(selection_event); if (GTK_WIDGET_CLASS(sciThis->parentClass)->selection_clear_event) { @@ -2875,12 +2698,12 @@ gboolean ScintillaGTK::DragMotionThis(GdkDragContext *context, gboolean ScintillaGTK::DragMotion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint dragtime) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); return sciThis->DragMotionThis(context, x, y, dragtime); } void ScintillaGTK::DragLeave(GtkWidget *widget, GdkDragContext * /*context*/, guint) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { sciThis->SetDragPosition(SelectionPosition(invalidPosition)); //Platform::DebugPrintf("DragLeave %x\n", sciThis); @@ -2890,7 +2713,7 @@ void ScintillaGTK::DragLeave(GtkWidget *widget, GdkDragContext * /*context*/, gu } void ScintillaGTK::DragEnd(GtkWidget *widget, GdkDragContext * /*context*/) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { // If drag did not result in drop here or elsewhere if (!sciThis->dragWasDropped) @@ -2905,7 +2728,7 @@ void ScintillaGTK::DragEnd(GtkWidget *widget, GdkDragContext * /*context*/) { gboolean ScintillaGTK::Drop(GtkWidget *widget, GdkDragContext * /*context*/, gint, gint, guint) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { //Platform::DebugPrintf("Drop %x\n", sciThis); sciThis->SetDragPosition(SelectionPosition(invalidPosition)); @@ -2917,7 +2740,7 @@ gboolean ScintillaGTK::Drop(GtkWidget *widget, GdkDragContext * /*context*/, void ScintillaGTK::DragDataReceived(GtkWidget *widget, GdkDragContext * /*context*/, gint, gint, GtkSelectionData *selection_data, guint /*info*/, guint) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { sciThis->ReceivedDrop(selection_data); sciThis->SetDragPosition(SelectionPosition(invalidPosition)); @@ -2928,7 +2751,7 @@ void ScintillaGTK::DragDataReceived(GtkWidget *widget, GdkDragContext * /*contex void ScintillaGTK::DragDataGet(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint) { - ScintillaGTK *sciThis = ScintillaFromWidget(widget); + ScintillaGTK *sciThis = FromWidget(widget); try { sciThis->dragWasDropped = true; if (!sciThis->sel.Empty()) { @@ -2997,6 +2820,28 @@ void ScintillaGTK::QueueIdleWork(WorkNeeded::workItems items, int upTo) { } } +void ScintillaGTK::SetDocPointer(Document *document) { + Document *oldDoc = 0; + ScintillaGTKAccessible *sciAccessible = 0; + if (accessible) { + sciAccessible = ScintillaGTKAccessible::FromAccessible(accessible); + if (sciAccessible && pdoc) { + oldDoc = pdoc; + oldDoc->AddRef(); + } + } + + Editor::SetDocPointer(document); + + if (sciAccessible) { + // the accessible needs have the old Document, but also the new one active + sciAccessible->ChangeDocument(oldDoc, pdoc); + } + if (oldDoc) { + oldDoc->Release(); + } +} + void ScintillaGTK::PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis) { guint action = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menuItem), "CmdNum")); if (action) { @@ -3062,6 +2907,14 @@ gboolean ScintillaGTK::ExposeCT(GtkWidget *widget, GdkEventExpose * /*ose*/, Cal #endif +AtkObject* ScintillaGTK::GetAccessibleThis(GtkWidget *widget) { + return ScintillaGTKAccessible::WidgetGetAccessibleImpl(widget, &accessible, scintilla_class_parent_class); +} + +AtkObject* ScintillaGTK::GetAccessible(GtkWidget *widget) { + return FromWidget(widget)->GetAccessibleThis(widget); +} + sptr_t ScintillaGTK::DirectFunction( sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam) { return reinterpret_cast(ptr)->WndProc(iMessage, wParam, lParam); @@ -3174,6 +3027,8 @@ void ScintillaGTK::ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_ widget_class->map = Map; widget_class->unmap = UnMap; + widget_class->get_accessible = GetAccessible; + container_class->forall = MainForAll; } @@ -3252,7 +3107,7 @@ void scintilla_release_resources(void) { * recognize gpointer-derived types. Note that SCNotificaiton * is always allocated on stack so copying is not appropriate. */ static void *copy_(void *src) { return src; } -static void free_(void *doc) { } +static void free_(void *) { } GEANY_API_SYMBOL GType scnotification_get_type(void) { diff --git a/scintilla/gtk/ScintillaGTK.h b/scintilla/gtk/ScintillaGTK.h new file mode 100644 index 000000000..35778c520 --- /dev/null +++ b/scintilla/gtk/ScintillaGTK.h @@ -0,0 +1,287 @@ +// Scintilla source code edit control +// ScintillaGTK.h - GTK+ specific subclass of ScintillaBase +// Copyright 1998-2004 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SCINTILLAGTK_H +#define SCINTILLAGTK_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +class ScintillaGTKAccessible; + +#define OBJECT_CLASS GObjectClass + +class ScintillaGTK : public ScintillaBase { + friend class ScintillaGTKAccessible; + + _ScintillaObject *sci; + Window wText; + Window scrollbarv; + Window scrollbarh; + GtkAdjustment *adjustmentv; + GtkAdjustment *adjustmenth; + int verticalScrollBarWidth; + int horizontalScrollBarHeight; + + SelectionText primary; + + GdkEventButton *evbtn; + bool capturedMouse; + bool dragWasDropped; + int lastKey; + int rectangularSelectionModifier; + + GtkWidgetClass *parentClass; + + static GdkAtom atomClipboard; + static GdkAtom atomUTF8; + static GdkAtom atomString; + static GdkAtom atomUriList; + static GdkAtom atomDROPFILES_DND; + GdkAtom atomSought; + +#if PLAT_GTK_WIN32 + CLIPFORMAT cfColumnSelect; +#endif + + Window wPreedit; + Window wPreeditDraw; + GtkIMContext *im_context; + PangoScript lastNonCommonScript; + + // Wheel mouse support + unsigned int linesPerScroll; + GTimeVal lastWheelMouseTime; + gint lastWheelMouseDirection; + gint wheelMouseIntensity; + +#if GTK_CHECK_VERSION(3,0,0) + cairo_rectangle_list_t *rgnUpdate; +#else + GdkRegion *rgnUpdate; +#endif + bool repaintFullWindow; + + guint styleIdleID; + AtkObject *accessible; + + // Private so ScintillaGTK objects can not be copied + ScintillaGTK(const ScintillaGTK &); + ScintillaGTK &operator=(const ScintillaGTK &); + +public: + explicit ScintillaGTK(_ScintillaObject *sci_); + virtual ~ScintillaGTK(); + static ScintillaGTK *FromWidget(GtkWidget *widget); + static void ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class); +private: + virtual void Initialise(); + virtual void Finalise(); + virtual bool AbandonPaint(); + virtual void DisplayCursor(Window::Cursor c); + virtual bool DragThreshold(Point ptStart, Point ptNow); + virtual void StartDrag(); + int TargetAsUTF8(char *text); + int EncodedFromUTF8(char *utf8, char *encoded) const; + virtual bool ValidCodePage(int codePage) const; +public: // Public for scintilla_send_message + virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); +private: + virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); + struct TimeThunk { + TickReason reason; + ScintillaGTK *scintilla; + guint timer; + TimeThunk() : reason(tickCaret), scintilla(NULL), timer(0) {} + }; + TimeThunk timers[tickDwell+1]; + virtual bool FineTickerAvailable(); + virtual bool FineTickerRunning(TickReason reason); + virtual void FineTickerStart(TickReason reason, int millis, int tolerance); + virtual void FineTickerCancel(TickReason reason); + virtual bool SetIdle(bool on); + virtual void SetMouseCapture(bool on); + virtual bool HaveMouseCapture(); + virtual bool PaintContains(PRectangle rc); + void FullPaint(); + virtual PRectangle GetClientRectangle() const; + virtual void ScrollText(int linesToMove); + virtual void SetVerticalScrollPos(); + virtual void SetHorizontalScrollPos(); + virtual bool ModifyScrollBars(int nMax, int nPage); + void ReconfigureScrollBars(); + virtual void NotifyChange(); + virtual void NotifyFocus(bool focus); + virtual void NotifyParent(SCNotification scn); + void NotifyKey(int key, int modifiers); + void NotifyURIDropped(const char *list); + const char *CharacterSetID() const; + virtual CaseFolder *CaseFolderForEncoding(); + virtual std::string CaseMapString(const std::string &s, int caseMapping); + virtual int KeyDefault(int key, int modifiers); + virtual void CopyToClipboard(const SelectionText &selectedText); + virtual void Copy(); + virtual void Paste(); + virtual void CreateCallTipWindow(PRectangle rc); + virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true); + bool OwnPrimarySelection(); + virtual void ClaimSelection(); + void GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText); + void ReceivedSelection(GtkSelectionData *selection_data); + void ReceivedDrop(GtkSelectionData *selection_data); + static void GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *selected); + void StoreOnClipboard(SelectionText *clipText); + static void ClipboardGetSelection(GtkClipboard* clip, GtkSelectionData *selection_data, guint info, void *data); + static void ClipboardClearSelection(GtkClipboard* clip, void *data); + + void UnclaimSelection(GdkEventSelection *selection_event); + void Resize(int width, int height); + + // Callback functions + void RealizeThis(GtkWidget *widget); + static void Realize(GtkWidget *widget); + void UnRealizeThis(GtkWidget *widget); + static void UnRealize(GtkWidget *widget); + void MapThis(); + static void Map(GtkWidget *widget); + void UnMapThis(); + static void UnMap(GtkWidget *widget); + gint FocusInThis(GtkWidget *widget); + static gint FocusIn(GtkWidget *widget, GdkEventFocus *event); + gint FocusOutThis(GtkWidget *widget); + static gint FocusOut(GtkWidget *widget, GdkEventFocus *event); + static void SizeRequest(GtkWidget *widget, GtkRequisition *requisition); +#if GTK_CHECK_VERSION(3,0,0) + static void GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth); + static void GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight); +#endif + static void SizeAllocate(GtkWidget *widget, GtkAllocation *allocation); +#if GTK_CHECK_VERSION(3,0,0) + gboolean DrawTextThis(cairo_t *cr); + static gboolean DrawText(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis); + gboolean DrawThis(cairo_t *cr); + static gboolean DrawMain(GtkWidget *widget, cairo_t *cr); +#else + gboolean ExposeTextThis(GtkWidget *widget, GdkEventExpose *ose); + static gboolean ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis); + gboolean Expose(GtkWidget *widget, GdkEventExpose *ose); + static gboolean ExposeMain(GtkWidget *widget, GdkEventExpose *ose); +#endif + void ForAll(GtkCallback callback, gpointer callback_data); + static void MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); + + static void ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis); + static void ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis); + gint PressThis(GdkEventButton *event); + static gint Press(GtkWidget *widget, GdkEventButton *event); + static gint MouseRelease(GtkWidget *widget, GdkEventButton *event); + static gint ScrollEvent(GtkWidget *widget, GdkEventScroll *event); + static gint Motion(GtkWidget *widget, GdkEventMotion *event); + gboolean KeyThis(GdkEventKey *event); + static gboolean KeyPress(GtkWidget *widget, GdkEventKey *event); + static gboolean KeyRelease(GtkWidget *widget, GdkEventKey *event); +#if GTK_CHECK_VERSION(3,0,0) + gboolean DrawPreeditThis(GtkWidget *widget, cairo_t *cr); + static gboolean DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis); +#else + gboolean ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose); + static gboolean ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis); +#endif + AtkObject* GetAccessibleThis(GtkWidget *widget); + static AtkObject* GetAccessible(GtkWidget *widget); + + bool KoreanIME(); + void CommitThis(char *str); + static void Commit(GtkIMContext *context, char *str, ScintillaGTK *sciThis); + void PreeditChangedInlineThis(); + void PreeditChangedWindowedThis(); + static void PreeditChanged(GtkIMContext *context, ScintillaGTK *sciThis); + void MoveImeCarets(int pos); + void DrawImeIndicator(int indicator, int len); + void SetCandidateWindowPos(); + + static void StyleSetText(GtkWidget *widget, GtkStyle *previous, void*); + static void RealizeText(GtkWidget *widget, void*); + static void Dispose(GObject *object); + static void Destroy(GObject *object); + static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data, + guint time); + static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data, + guint info, guint time); + static gint SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event); + gboolean DragMotionThis(GdkDragContext *context, gint x, gint y, guint dragtime); + static gboolean DragMotion(GtkWidget *widget, GdkDragContext *context, + gint x, gint y, guint dragtime); + static void DragLeave(GtkWidget *widget, GdkDragContext *context, + guint time); + static void DragEnd(GtkWidget *widget, GdkDragContext *context); + static gboolean Drop(GtkWidget *widget, GdkDragContext *context, + gint x, gint y, guint time); + static void DragDataReceived(GtkWidget *widget, GdkDragContext *context, + gint x, gint y, GtkSelectionData *selection_data, guint info, guint time); + static void DragDataGet(GtkWidget *widget, GdkDragContext *context, + GtkSelectionData *selection_data, guint info, guint time); + static gboolean TimeOut(gpointer ptt); + static gboolean IdleCallback(gpointer pSci); + static gboolean StyleIdle(gpointer pSci); + virtual void IdleWork(); + virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo); + virtual void SetDocPointer(Document *document); + static void PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis); + +#if GTK_CHECK_VERSION(3,0,0) + static gboolean DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip); +#else + static gboolean ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ct); +#endif + static gboolean PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis); + + static sptr_t DirectFunction(sptr_t ptr, + unsigned int iMessage, uptr_t wParam, sptr_t lParam); +}; + +// helper class to watch a GObject lifetime and get notified when it dies +class GObjectWatcher { + GObject *weakRef; + + void WeakNotifyThis(GObject *obj G_GNUC_UNUSED) { + PLATFORM_ASSERT(obj == weakRef); + + Destroyed(); + weakRef = 0; + } + + static void WeakNotify(gpointer data, GObject *obj) { + static_cast(data)->WeakNotifyThis(obj); + } + +public: + GObjectWatcher(GObject *obj) : + weakRef(obj) { + g_object_weak_ref(weakRef, WeakNotify, this); + } + + virtual ~GObjectWatcher() { + if (weakRef) { + g_object_weak_unref(weakRef, WeakNotify, this); + } + } + + virtual void Destroyed() {} + + bool IsDestroyed() const { + return weakRef != 0; + } +}; + +std::string ConvertText(const char *s, size_t len, const char *charSetDest, + const char *charSetSource, bool transliterations, bool silent=false); + +#ifdef SCI_NAMESPACE +} +#endif + +#endif diff --git a/scintilla/gtk/ScintillaGTKAccessible.cxx b/scintilla/gtk/ScintillaGTKAccessible.cxx new file mode 100644 index 000000000..e1f450467 --- /dev/null +++ b/scintilla/gtk/ScintillaGTKAccessible.cxx @@ -0,0 +1,1205 @@ +/* Scintilla source code edit control */ +/* ScintillaGTKAccessible.c - GTK+ accessibility for ScintillaGTK */ +/* Copyright 2016 by Colomban Wendling + * The License.txt file describes the conditions under which this software may be distributed. */ + +// On GTK < 3.2, we need to use the AtkObjectFactory. We need to query +// the factory to see what type we should derive from, thus making use of +// dynamic inheritance. It's tricky, but it works so long as it's done +// carefully enough. +// +// On GTK 3.2 through 3.6, we need to hack around because GTK stopped +// registering its accessible types in the factory, so we can't query +// them that way. Unfortunately, the accessible types aren't exposed +// yet (not until 3.8), so there's no proper way to know which type to +// inherit from. To work around this, we instantiate the parent's +// AtkObject temporarily, and use it's type. It means creating an extra +// throwaway object and being able to pass the type information up to the +// type registration code, but it's the only solution I could find. +// +// On GTK 3.8 onward, we use the proper exposed GtkContainerAccessible as +// parent, and so a straightforward class. +// +// To hide and contain the complexity in type creation arising from the +// hackish support for GTK 3.2 to 3.8, the actual implementation for the +// widget's get_accessible() is located in the accessibility layer itself. + +// Initially based on GtkTextViewAccessible from GTK 3.20 +// Inspiration for the GTK < 3.2 part comes from Evince 2.24, thanks. + +// FIXME: optimize character/byte offset conversion (with a cache?) + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +// whether we have widget_set() and widget_unset() +#define HAVE_WIDGET_SET_UNSET (GTK_CHECK_VERSION(3, 3, 6)) +// whether GTK accessibility is available through the ATK factory +#define HAVE_GTK_FACTORY (! GTK_CHECK_VERSION(3, 1, 9)) +// whether we have gtk-a11y.h and the public GTK accessible types +#define HAVE_GTK_A11Y_H (GTK_CHECK_VERSION(3, 7, 6)) + +#if HAVE_GTK_A11Y_H +# include +#endif + +#if defined(__WIN32__) || defined(_MSC_VER) +#include +#endif + +// ScintillaGTK.h and stuff it needs +#include "Platform.h" + +#include "ILexer.h" +#include "Scintilla.h" +#include "ScintillaWidget.h" +#ifdef SCI_LEXER +#include "SciLexer.h" +#endif +#include "StringCopy.h" +#ifdef SCI_LEXER +#include "LexerModule.h" +#endif +#include "Position.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 "ViewStyle.h" +#include "CharClassify.h" +#include "Decoration.h" +#include "CaseFolder.h" +#include "Document.h" +#include "CaseConvert.h" +#include "UniConversion.h" +#include "UnicodeFromUTF8.h" +#include "Selection.h" +#include "PositionCache.h" +#include "EditModel.h" +#include "MarginView.h" +#include "EditView.h" +#include "Editor.h" +#include "AutoComplete.h" +#include "ScintillaBase.h" + +#include "ScintillaGTK.h" +#include "ScintillaGTKAccessible.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +struct ScintillaObjectAccessiblePrivate { + ScintillaGTKAccessible *pscin; +}; + +typedef GtkAccessible ScintillaObjectAccessible; +typedef GtkAccessibleClass ScintillaObjectAccessibleClass; + +#define SCINTILLA_OBJECT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SCINTILLA_TYPE_OBJECT_ACCESSIBLE, ScintillaObjectAccessible)) +#define SCINTILLA_TYPE_OBJECT_ACCESSIBLE (scintilla_object_accessible_get_type(0)) + +// We can't use priv member because of dynamic inheritance, so we don't actually know the offset. Meh. +#define SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(inst) (G_TYPE_INSTANCE_GET_PRIVATE((inst), SCINTILLA_TYPE_OBJECT_ACCESSIBLE, ScintillaObjectAccessiblePrivate)) + +static GType scintilla_object_accessible_get_type(GType parent_type); + +ScintillaGTKAccessible *ScintillaGTKAccessible::FromAccessible(GtkAccessible *accessible) { + // FIXME: do we need the check below? GTK checks that in all methods, so maybe + GtkWidget *widget = gtk_accessible_get_widget(accessible); + if (! widget) { + return 0; + } + + return SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible)->pscin; +} + +ScintillaGTKAccessible::ScintillaGTKAccessible(GtkAccessible *accessible_, GtkWidget *widget_) : + accessible(accessible_), + sci(ScintillaGTK::FromWidget(widget_)), + old_pos(-1) { + g_signal_connect(widget_, "sci-notify", G_CALLBACK(SciNotify), this); +} + +ScintillaGTKAccessible::~ScintillaGTKAccessible() { +} + +gchar *ScintillaGTKAccessible::GetTextRangeUTF8(Position startByte, Position endByte) { + g_return_val_if_fail(startByte >= 0, NULL); + // FIXME: should we swap start/end if necessary? + g_return_val_if_fail(endByte >= startByte, NULL); + + gchar *utf8Text = NULL; + const char *charSetBuffer; + + // like TargetAsUTF8, but avoids a double conversion + if (sci->IsUnicodeMode() || ! *(charSetBuffer = sci->CharacterSetID())) { + int len = endByte - startByte; + utf8Text = (char *) g_malloc(len + 1); + sci->pdoc->GetCharRange(utf8Text, startByte, len); + utf8Text[len] = '\0'; + } else { + // Need to convert + std::string s = sci->RangeText(startByte, endByte); + std::string tmputf = ConvertText(&s[0], s.length(), "UTF-8", charSetBuffer, false); + size_t len = tmputf.length(); + utf8Text = (char *) g_malloc(len + 1); + memcpy(utf8Text, tmputf.c_str(), len); + utf8Text[len] = '\0'; + } + + return utf8Text; +} + +gchar *ScintillaGTKAccessible::GetText(int startChar, int endChar) { + Position startByte, endByte; + if (endChar == -1) { + startByte = ByteOffsetFromCharacterOffset(startChar); + endByte = sci->pdoc->Length(); + } else { + ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); + } + return GetTextRangeUTF8(startByte, endByte); +} + +gchar *ScintillaGTKAccessible::GetTextAfterOffset(int charOffset, + AtkTextBoundary boundaryType, int *startChar, int *endChar) { + g_return_val_if_fail(charOffset >= 0, NULL); + + Position startByte, endByte; + Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + + switch (boundaryType) { + case ATK_TEXT_BOUNDARY_CHAR: + startByte = PositionAfter(byteOffset); + endByte = PositionAfter(startByte); + // FIXME: optimize conversion back, as we can reasonably assume +1 char? + break; + + case ATK_TEXT_BOUNDARY_WORD_START: + startByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 1); + startByte = sci->WndProc(SCI_WORDENDPOSITION, startByte, 0); + endByte = sci->WndProc(SCI_WORDENDPOSITION, startByte, 1); + endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 0); + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + startByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 0); + startByte = sci->WndProc(SCI_WORDENDPOSITION, startByte, 1); + endByte = sci->WndProc(SCI_WORDENDPOSITION, startByte, 0); + endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 1); + break; + + case ATK_TEXT_BOUNDARY_LINE_START: { + int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); + startByte = sci->WndProc(SCI_POSITIONFROMLINE, line + 1, 0); + endByte = sci->WndProc(SCI_POSITIONFROMLINE, line + 2, 0); + break; + } + + case ATK_TEXT_BOUNDARY_LINE_END: { + int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); + startByte = sci->WndProc(SCI_GETLINEENDPOSITION, line, 0); + endByte = sci->WndProc(SCI_GETLINEENDPOSITION, line + 1, 0); + break; + } + + default: + *startChar = *endChar = -1; + return NULL; + } + + CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); + return GetTextRangeUTF8(startByte, endByte); +} + +gchar *ScintillaGTKAccessible::GetTextBeforeOffset(int charOffset, + AtkTextBoundary boundaryType, int *startChar, int *endChar) { + g_return_val_if_fail(charOffset >= 0, NULL); + + Position startByte, endByte; + Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + + switch (boundaryType) { + case ATK_TEXT_BOUNDARY_CHAR: + endByte = PositionBefore(byteOffset); + startByte = PositionBefore(endByte); + break; + + case ATK_TEXT_BOUNDARY_WORD_START: + endByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 0); + endByte = sci->WndProc(SCI_WORDSTARTPOSITION, endByte, 1); + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, endByte, 0); + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 1); + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + endByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 1); + endByte = sci->WndProc(SCI_WORDSTARTPOSITION, endByte, 0); + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, endByte, 1); + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 0); + break; + + case ATK_TEXT_BOUNDARY_LINE_START: { + int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); + endByte = sci->WndProc(SCI_POSITIONFROMLINE, line, 0); + if (line > 0) { + startByte = sci->WndProc(SCI_POSITIONFROMLINE, line - 1, 0); + } else { + startByte = endByte; + } + break; + } + + case ATK_TEXT_BOUNDARY_LINE_END: { + int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); + if (line > 0) { + endByte = sci->WndProc(SCI_GETLINEENDPOSITION, line - 1, 0); + } else { + endByte = 0; + } + if (line > 1) { + startByte = sci->WndProc(SCI_GETLINEENDPOSITION, line - 2, 0); + } else { + startByte = endByte; + } + break; + } + + default: + *startChar = *endChar = -1; + return NULL; + } + + CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); + return GetTextRangeUTF8(startByte, endByte); +} + +gchar *ScintillaGTKAccessible::GetTextAtOffset(int charOffset, + AtkTextBoundary boundaryType, int *startChar, int *endChar) { + g_return_val_if_fail(charOffset >= 0, NULL); + + Position startByte, endByte; + Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + + switch (boundaryType) { + case ATK_TEXT_BOUNDARY_CHAR: + startByte = byteOffset; + endByte = sci->WndProc(SCI_POSITIONAFTER, byteOffset, 0); + break; + + case ATK_TEXT_BOUNDARY_WORD_START: + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 1); + endByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 1); + if (! sci->WndProc(SCI_ISRANGEWORD, startByte, endByte)) { + // if the cursor was not on a word, forward back + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 0); + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 1); + } + endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 0); + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 1); + endByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 1); + if (! sci->WndProc(SCI_ISRANGEWORD, startByte, endByte)) { + // if the cursor was not on a word, forward back + endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 0); + endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 1); + } + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 0); + break; + + case ATK_TEXT_BOUNDARY_LINE_START: { + int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); + startByte = sci->WndProc(SCI_POSITIONFROMLINE, line, 0); + endByte = sci->WndProc(SCI_POSITIONFROMLINE, line + 1, 0); + break; + } + + case ATK_TEXT_BOUNDARY_LINE_END: { + int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); + if (line > 0) { + startByte = sci->WndProc(SCI_GETLINEENDPOSITION, line - 1, 0); + } else { + startByte = 0; + } + endByte = sci->WndProc(SCI_GETLINEENDPOSITION, line, 0); + break; + } + + default: + *startChar = *endChar = -1; + return NULL; + } + + CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); + return GetTextRangeUTF8(startByte, endByte); +} + +#if ATK_CHECK_VERSION(2, 10, 0) +gchar *ScintillaGTKAccessible::GetStringAtOffset(int charOffset, + AtkTextGranularity granularity, int *startChar, int *endChar) { + g_return_val_if_fail(charOffset >= 0, NULL); + + Position startByte, endByte; + Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + + switch (granularity) { + case ATK_TEXT_GRANULARITY_CHAR: + startByte = byteOffset; + endByte = sci->WndProc(SCI_POSITIONAFTER, byteOffset, 0); + break; + case ATK_TEXT_GRANULARITY_WORD: + startByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 1); + endByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 1); + break; + case ATK_TEXT_GRANULARITY_LINE: { + gint line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); + startByte = sci->WndProc(SCI_POSITIONFROMLINE, line, 0); + endByte = sci->WndProc(SCI_GETLINEENDPOSITION, line, 0); + break; + } + default: + *startChar = *endChar = -1; + return NULL; + } + + CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); + return GetTextRangeUTF8(startByte, endByte); +} +#endif + +gunichar ScintillaGTKAccessible::GetCharacterAtOffset(int charOffset) { + g_return_val_if_fail(charOffset >= 0, 0); + + Position startByte = ByteOffsetFromCharacterOffset(charOffset); + Position endByte = PositionAfter(startByte); + gchar *ch = GetTextRangeUTF8(startByte, endByte); + gunichar unichar = g_utf8_get_char_validated(ch, -1); + g_free(ch); + + return unichar; +} + +gint ScintillaGTKAccessible::GetCharacterCount() { + return sci->pdoc->CountCharacters(0, sci->pdoc->Length()); +} + +gint ScintillaGTKAccessible::GetCaretOffset() { + return CharacterOffsetFromByteOffset(sci->WndProc(SCI_GETCURRENTPOS, 0, 0)); +} + +gboolean ScintillaGTKAccessible::SetCaretOffset(int charOffset) { + sci->WndProc(SCI_GOTOPOS, ByteOffsetFromCharacterOffset(charOffset), 0); + return TRUE; +} + +gint ScintillaGTKAccessible::GetOffsetAtPoint(gint x, gint y, AtkCoordType coords) { + gint x_widget, y_widget, x_window, y_window; + GtkWidget *widget = gtk_accessible_get_widget(accessible); + + GdkWindow *window = gtk_widget_get_window(widget); + gdk_window_get_origin(window, &x_widget, &y_widget); + if (coords == ATK_XY_SCREEN) { + x = x - x_widget; + y = y - y_widget; + } else if (coords == ATK_XY_WINDOW) { + window = gdk_window_get_toplevel(window); + gdk_window_get_origin(window, &x_window, &y_window); + + x = x - x_widget + x_window; + y = y - y_widget + y_window; + } else { + return -1; + } + + // FIXME: should we handle scrolling? + return CharacterOffsetFromByteOffset(sci->WndProc(SCI_CHARPOSITIONFROMPOINTCLOSE, x, y)); +} + +void ScintillaGTKAccessible::GetCharacterExtents(int charOffset, + gint *x, gint *y, gint *width, gint *height, AtkCoordType coords) { + *x = *y = *height = *width = 0; + + Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + + // FIXME: should we handle scrolling? + *x = sci->WndProc(SCI_POINTXFROMPOSITION, 0, byteOffset); + *y = sci->WndProc(SCI_POINTYFROMPOSITION, 0, byteOffset); + + int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); + *height = sci->WndProc(SCI_TEXTHEIGHT, line, 0); + + int nextByteOffset = PositionAfter(byteOffset); + int next_x = sci->WndProc(SCI_POINTXFROMPOSITION, 0, nextByteOffset); + if (next_x > *x) { + *width = next_x - *x; + } else if (nextByteOffset > byteOffset) { + /* maybe next position was on the next line or something. + * just compute the expected character width */ + int style = sci->pdoc->StyleAt(byteOffset); + int len = nextByteOffset - byteOffset; + char *ch = new char[len + 1]; + sci->pdoc->GetCharRange(ch, byteOffset, len); + ch[len] = '\0'; + *width = sci->TextWidth(style, ch); + delete[] ch; + } + + GtkWidget *widget = gtk_accessible_get_widget(accessible); + GdkWindow *window = gtk_widget_get_window(widget); + int x_widget, y_widget; + gdk_window_get_origin(window, &x_widget, &y_widget); + if (coords == ATK_XY_SCREEN) { + *x += x_widget; + *y += y_widget; + } else if (coords == ATK_XY_WINDOW) { + window = gdk_window_get_toplevel(window); + int x_window, y_window; + gdk_window_get_origin(window, &x_window, &y_window); + + *x += x_widget - x_window; + *y += y_widget - y_window; + } else { + *x = *y = *height = *width = 0; + } +} + +static AtkAttributeSet *AddTextAttribute(AtkAttributeSet *attributes, AtkTextAttribute attr, gchar *value) { + AtkAttribute *at = g_new(AtkAttribute, 1); + at->name = g_strdup(atk_text_attribute_get_name(attr)); + at->value = value; + + return g_slist_prepend(attributes, at); +} + +static AtkAttributeSet *AddTextIntAttribute(AtkAttributeSet *attributes, AtkTextAttribute attr, gint i) { + return AddTextAttribute(attributes, attr, g_strdup(atk_text_attribute_get_value(attr, i))); +} + +static AtkAttributeSet *AddTextColorAttribute(AtkAttributeSet *attributes, AtkTextAttribute attr, const ColourDesired &colour) { + return AddTextAttribute(attributes, attr, + g_strdup_printf("%u,%u,%u", colour.GetRed() * 257, colour.GetGreen() * 257, colour.GetBlue() * 257)); +} + +AtkAttributeSet *ScintillaGTKAccessible::GetAttributesForStyle(unsigned int styleNum) { + AtkAttributeSet *attr_set = NULL; + + if (styleNum >= sci->vs.styles.size()) + return NULL; + Style &style = sci->vs.styles[styleNum]; + + attr_set = AddTextAttribute(attr_set, ATK_TEXT_ATTR_FAMILY_NAME, g_strdup(style.fontName)); + attr_set = AddTextAttribute(attr_set, ATK_TEXT_ATTR_SIZE, g_strdup_printf("%d", style.size / SC_FONT_SIZE_MULTIPLIER)); + attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_WEIGHT, CLAMP(style.weight, 100, 1000)); + attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_STYLE, style.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); + attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_UNDERLINE, style.underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE); + attr_set = AddTextColorAttribute(attr_set, ATK_TEXT_ATTR_FG_COLOR, style.fore); + attr_set = AddTextColorAttribute(attr_set, ATK_TEXT_ATTR_BG_COLOR, style.back); + attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_INVISIBLE, style.visible ? 0 : 1); + attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_EDITABLE, style.changeable ? 1 : 0); + + return attr_set; +} + +AtkAttributeSet *ScintillaGTKAccessible::GetRunAttributes(int charOffset, int *startChar, int *endChar) { + g_return_val_if_fail(charOffset >= -1, NULL); + + Position byteOffset; + if (charOffset == -1) { + byteOffset = sci->WndProc(SCI_GETCURRENTPOS, 0, 0); + } else { + byteOffset = ByteOffsetFromCharacterOffset(charOffset); + } + int length = sci->pdoc->Length(); + + g_return_val_if_fail(byteOffset < length, NULL); + + const char style = sci->pdoc->StyleAt(byteOffset); + // compute the range for this style + Position startByte = byteOffset; + while (startByte > 0 && sci->pdoc->StyleAt((startByte) - 1) == style) + (startByte)--; + Position endByte = byteOffset; + while ((endByte) + 1 < length && sci->pdoc->StyleAt((endByte) + 1) == style) + (endByte)++; + + CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); + return GetAttributesForStyle((unsigned int) style); +} + +AtkAttributeSet *ScintillaGTKAccessible::GetDefaultAttributes() { + return GetAttributesForStyle(0); +} + +gint ScintillaGTKAccessible::GetNSelections() { + return sci->sel.Empty() ? 0 : sci->sel.Count(); +} + +gchar *ScintillaGTKAccessible::GetSelection(gint selection_num, int *startChar, int *endChar) { + if (selection_num < 0 || (unsigned int) selection_num >= sci->sel.Count()) + return NULL; + + Position startByte = sci->sel.Range(selection_num).Start().Position(); + Position endByte = sci->sel.Range(selection_num).End().Position(); + + CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); + return GetTextRangeUTF8(startByte, endByte); +} + +gboolean ScintillaGTKAccessible::AddSelection(int startChar, int endChar) { + size_t n_selections = sci->sel.Count(); + Position startByte, endByte; + ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); + // use WndProc() to set the selections so it notifies as needed + if (n_selections > 1 || ! sci->sel.Empty()) { + sci->WndProc(SCI_ADDSELECTION, startByte, endByte); + } else { + sci->WndProc(SCI_SETSELECTION, startByte, endByte); + } + + return TRUE; +} + +gboolean ScintillaGTKAccessible::RemoveSelection(gint selection_num) { + size_t n_selections = sci->sel.Count(); + if (selection_num < 0 || (unsigned int) selection_num >= n_selections) + return FALSE; + + if (n_selections > 1) { + sci->WndProc(SCI_DROPSELECTIONN, selection_num, 0); + } else if (sci->sel.Empty()) { + return FALSE; + } else { + sci->WndProc(SCI_CLEARSELECTIONS, 0, 0); + } + + return TRUE; +} + +gboolean ScintillaGTKAccessible::SetSelection(gint selection_num, int startChar, int endChar) { + if (selection_num < 0 || (unsigned int) selection_num >= sci->sel.Count()) + return FALSE; + + Position startByte, endByte; + ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); + + sci->WndProc(SCI_SETSELECTIONNSTART, selection_num, startByte); + sci->WndProc(SCI_SETSELECTIONNEND, selection_num, endByte); + + return TRUE; +} + +void ScintillaGTKAccessible::AtkTextIface::init(::AtkTextIface *iface) { + iface->get_text = GetText; + iface->get_text_after_offset = GetTextAfterOffset; + iface->get_text_at_offset = GetTextAtOffset; + iface->get_text_before_offset = GetTextBeforeOffset; +#if ATK_CHECK_VERSION(2, 10, 0) + iface->get_string_at_offset = GetStringAtOffset; +#endif + iface->get_character_at_offset = GetCharacterAtOffset; + iface->get_character_count = GetCharacterCount; + iface->get_caret_offset = GetCaretOffset; + iface->set_caret_offset = SetCaretOffset; + iface->get_offset_at_point = GetOffsetAtPoint; + iface->get_character_extents = GetCharacterExtents; + iface->get_n_selections = GetNSelections; + iface->get_selection = GetSelection; + iface->add_selection = AddSelection; + iface->remove_selection = RemoveSelection; + iface->set_selection = SetSelection; + iface->get_run_attributes = GetRunAttributes; + iface->get_default_attributes = GetDefaultAttributes; +} + +/* atkeditabletext.h */ + +void ScintillaGTKAccessible::SetTextContents(const gchar *contents) { + // FIXME: it's probably useless to check for READONLY here, SETTEXT probably does it just fine? + if (! sci->pdoc->IsReadOnly()) { + sci->WndProc(SCI_SETTEXT, 0, (sptr_t) contents); + } +} + +bool ScintillaGTKAccessible::InsertStringUTF8(Position bytePos, const gchar *utf8, int lengthBytes) { + if (sci->pdoc->IsReadOnly()) { + return false; + } + + // like EncodedFromUTF8(), but avoids an extra copy + // FIXME: update target? + const char *charSetBuffer; + if (sci->IsUnicodeMode() || ! *(charSetBuffer = sci->CharacterSetID())) { + sci->pdoc->InsertString(bytePos, utf8, lengthBytes); + } else { + // conversion needed + std::string encoded = ConvertText(utf8, lengthBytes, charSetBuffer, "UTF-8", true); + sci->pdoc->InsertString(bytePos, encoded.c_str(), encoded.length()); + } + + return true; +} + +void ScintillaGTKAccessible::InsertText(const gchar *text, int lengthBytes, int *charPosition) { + Position bytePosition = ByteOffsetFromCharacterOffset(*charPosition); + + // FIXME: should we update the target? + if (InsertStringUTF8(bytePosition, text, lengthBytes)) { + (*charPosition) += sci->pdoc->CountCharacters(bytePosition, lengthBytes); + } +} + +void ScintillaGTKAccessible::CopyText(int startChar, int endChar) { + Position startByte, endByte; + ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); + sci->CopyRangeToClipboard(startByte, endByte); +} + +void ScintillaGTKAccessible::CutText(int startChar, int endChar) { + g_return_if_fail(endChar >= startChar); + + if (! sci->pdoc->IsReadOnly()) { + // FIXME: have a byte variant of those and convert only once? + CopyText(startChar, endChar); + DeleteText(startChar, endChar); + } +} + +void ScintillaGTKAccessible::DeleteText(int startChar, int endChar) { + g_return_if_fail(endChar >= startChar); + + if (! sci->pdoc->IsReadOnly()) { + Position startByte, endByte; + ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); + + if (! sci->RangeContainsProtected(startByte, endByte)) { + // FIXME: restore the target? + sci->pdoc->DeleteChars(startByte, endByte - startByte); + } + } +} + +void ScintillaGTKAccessible::PasteText(int charPosition) { + if (sci->pdoc->IsReadOnly()) + return; + + // Helper class holding the position for the asynchronous paste operation. + // We can only hope that when the callback gets called scia is still valid, but ScintillaGTK + // has always done that without problems, so let's guess it's a fairly safe bet. + struct Helper : GObjectWatcher { + ScintillaGTKAccessible *scia; + Position bytePosition; + + virtual void Destroyed() { + scia = 0; + } + + Helper(ScintillaGTKAccessible *scia_, Position bytePos_) : + GObjectWatcher(G_OBJECT(scia_->sci->sci)), + scia(scia_), + bytePosition(bytePos_) { + } + + void TextReceived(GtkClipboard *, const gchar *text) { + if (text) { + size_t len = strlen(text); + std::string convertedText; + if (len > 0 && scia->sci->convertPastes) { + // Convert line endings of the paste into our local line-endings mode + convertedText = Document::TransformLineEnds(text, len, scia->sci->pdoc->eolMode); + len = convertedText.length(); + text = convertedText.c_str(); + } + scia->InsertStringUTF8(bytePosition, text, static_cast(len)); + } + } + + static void TextReceivedCallback(GtkClipboard *clipboard, const gchar *text, gpointer data) { + Helper *helper = reinterpret_cast(data); + try { + if (helper->scia != 0) { + helper->TextReceived(clipboard, text); + } + } catch (...) {} + delete helper; + } + }; + + Helper *helper = new Helper(this, ByteOffsetFromCharacterOffset(charPosition)); + GtkWidget *widget = gtk_accessible_get_widget(accessible); + GtkClipboard *clipboard = gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD); + gtk_clipboard_request_text(clipboard, helper->TextReceivedCallback, helper); +} + +void ScintillaGTKAccessible::AtkEditableTextIface::init(::AtkEditableTextIface *iface) { + iface->set_text_contents = SetTextContents; + iface->insert_text = InsertText; + iface->copy_text = CopyText; + iface->cut_text = CutText; + iface->delete_text = DeleteText; + iface->paste_text = PasteText; + //~ iface->set_run_attributes = SetRunAttributes; +} + +// Callbacks + +void ScintillaGTKAccessible::UpdateCursor() { + Position pos = sci->WndProc(SCI_GETCURRENTPOS, 0, 0); + if (old_pos != pos) { + int charPosition = CharacterOffsetFromByteOffset(pos); + g_signal_emit_by_name(accessible, "text-caret-moved", charPosition); + old_pos = pos; + } + + size_t n_selections = sci->sel.Count(); + size_t prev_n_selections = old_sels.size(); + bool selection_changed = n_selections != prev_n_selections; + + old_sels.resize(n_selections); + for (size_t i = 0; i < n_selections; i++) { + SelectionRange &sel = sci->sel.Range(i); + + if (i < prev_n_selections && ! selection_changed) { + SelectionRange &old_sel = old_sels[i]; + // do not consider a caret move to be a selection change + selection_changed = ((! old_sel.Empty() || ! sel.Empty()) && ! (old_sel == sel)); + } + + old_sels[i] = sel; + } + + if (selection_changed) + g_signal_emit_by_name(accessible, "text-selection-changed"); +} + +void ScintillaGTKAccessible::ChangeDocument(Document *oldDoc, Document *newDoc) { + if (oldDoc == newDoc) { + return; + } + + if (oldDoc) { + int charLength = oldDoc->CountCharacters(0, oldDoc->Length()); + g_signal_emit_by_name(accessible, "text-changed::delete", 0, charLength); + } + + if (newDoc) { + PLATFORM_ASSERT(newDoc == sci->pdoc); + + int charLength = newDoc->CountCharacters(0, newDoc->Length()); + g_signal_emit_by_name(accessible, "text-changed::insert", 0, charLength); + + if ((oldDoc ? oldDoc->IsReadOnly() : false) != newDoc->IsReadOnly()) { + NotifyReadOnly(); + } + + // update cursor and selection + old_pos = -1; + old_sels.clear(); + UpdateCursor(); + } +} + +void ScintillaGTKAccessible::NotifyReadOnly() { + bool readonly = sci->pdoc->IsReadOnly(); + atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_EDITABLE, ! readonly); +#if ATK_CHECK_VERSION(2, 16, 0) + atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_READ_ONLY, readonly); +#endif +} + +void ScintillaGTKAccessible::Notify(GtkWidget *, gint, SCNotification *nt) { + switch (nt->nmhdr.code) { + case SCN_MODIFIED: { + if (nt->modificationType & SC_MOD_INSERTTEXT) { + int startChar = CharacterOffsetFromByteOffset(nt->position); + int lengthChar = sci->pdoc->CountCharacters(nt->position, nt->position + nt->length); + g_signal_emit_by_name(accessible, "text-changed::insert", startChar, lengthChar); + UpdateCursor(); + } + if (nt->modificationType & SC_MOD_DELETETEXT) { + int startChar = CharacterOffsetFromByteOffset(nt->position); + int lengthChar = sci->pdoc->CountCharacters(nt->position, nt->position + nt->length); + g_signal_emit_by_name(accessible, "text-changed::delete", startChar, lengthChar); + UpdateCursor(); + } + if (nt->modificationType & SC_MOD_CHANGESTYLE) { + g_signal_emit_by_name(accessible, "text-attributes-changed"); + } + } break; + case SCN_UPDATEUI: { + if (nt->updated & SC_UPDATE_SELECTION) { + UpdateCursor(); + } + } break; + } +} + +// ATK method wrappers + +// wraps a call from the accessible object to the ScintillaGTKAccessible, and avoid leaking any exception +#define WRAPPER_METHOD_BODY(accessible, call, defret) \ + try { \ + ScintillaGTKAccessible *thisAccessible = FromAccessible(reinterpret_cast(accessible)); \ + if (thisAccessible) { \ + return thisAccessible->call; \ + } else { \ + return defret; \ + } \ + } catch (...) { \ + return defret; \ + } + +// AtkText +gchar *ScintillaGTKAccessible::AtkTextIface::GetText(AtkText *text, int start_offset, int end_offset) { + WRAPPER_METHOD_BODY(text, GetText(start_offset, end_offset), NULL); +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetTextAfterOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset) { + WRAPPER_METHOD_BODY(text, GetTextAfterOffset(offset, boundary_type, start_offset, end_offset), NULL) +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetTextBeforeOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset) { + WRAPPER_METHOD_BODY(text, GetTextBeforeOffset(offset, boundary_type, start_offset, end_offset), NULL) +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetTextAtOffset(AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) { + WRAPPER_METHOD_BODY(text, GetTextAtOffset(offset, boundary_type, start_offset, end_offset), NULL) +} +#if ATK_CHECK_VERSION(2, 10, 0) +gchar *ScintillaGTKAccessible::AtkTextIface::GetStringAtOffset(AtkText *text, gint offset, AtkTextGranularity granularity, gint *start_offset, gint *end_offset) { + WRAPPER_METHOD_BODY(text, GetStringAtOffset(offset, granularity, start_offset, end_offset), NULL) +} +#endif +gunichar ScintillaGTKAccessible::AtkTextIface::GetCharacterAtOffset(AtkText *text, gint offset) { + WRAPPER_METHOD_BODY(text, GetCharacterAtOffset(offset), 0) +} +gint ScintillaGTKAccessible::AtkTextIface::GetCharacterCount(AtkText *text) { + WRAPPER_METHOD_BODY(text, GetCharacterCount(), 0) +} +gint ScintillaGTKAccessible::AtkTextIface::GetCaretOffset(AtkText *text) { + WRAPPER_METHOD_BODY(text, GetCaretOffset(), 0) +} +gboolean ScintillaGTKAccessible::AtkTextIface::SetCaretOffset(AtkText *text, gint offset) { + WRAPPER_METHOD_BODY(text, SetCaretOffset(offset), FALSE) +} +gint ScintillaGTKAccessible::AtkTextIface::GetOffsetAtPoint(AtkText *text, gint x, gint y, AtkCoordType coords) { + WRAPPER_METHOD_BODY(text, GetOffsetAtPoint(x, y, coords), -1) +} +void ScintillaGTKAccessible::AtkTextIface::GetCharacterExtents(AtkText *text, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords) { + WRAPPER_METHOD_BODY(text, GetCharacterExtents(offset, x, y, width, height, coords), ) +} +AtkAttributeSet *ScintillaGTKAccessible::AtkTextIface::GetRunAttributes(AtkText *text, gint offset, gint *start_offset, gint *end_offset) { + WRAPPER_METHOD_BODY(text, GetRunAttributes(offset, start_offset, end_offset), NULL) +} +AtkAttributeSet *ScintillaGTKAccessible::AtkTextIface::GetDefaultAttributes(AtkText *text) { + WRAPPER_METHOD_BODY(text, GetDefaultAttributes(), NULL) +} +gint ScintillaGTKAccessible::AtkTextIface::GetNSelections(AtkText *text) { + WRAPPER_METHOD_BODY(text, GetNSelections(), 0) +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetSelection(AtkText *text, gint selection_num, gint *start_pos, gint *end_pos) { + WRAPPER_METHOD_BODY(text, GetSelection(selection_num, start_pos, end_pos), NULL) +} +gboolean ScintillaGTKAccessible::AtkTextIface::AddSelection(AtkText *text, gint start, gint end) { + WRAPPER_METHOD_BODY(text, AddSelection(start, end), FALSE) +} +gboolean ScintillaGTKAccessible::AtkTextIface::RemoveSelection(AtkText *text, gint selection_num) { + WRAPPER_METHOD_BODY(text, RemoveSelection(selection_num), FALSE) +} +gboolean ScintillaGTKAccessible::AtkTextIface::SetSelection(AtkText *text, gint selection_num, gint start, gint end) { + WRAPPER_METHOD_BODY(text, SetSelection(selection_num, start, end), FALSE) +} +// AtkEditableText +void ScintillaGTKAccessible::AtkEditableTextIface::SetTextContents(AtkEditableText *text, const gchar *contents) { + WRAPPER_METHOD_BODY(text, SetTextContents(contents), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::InsertText(AtkEditableText *text, const gchar *contents, gint length, gint *position) { + WRAPPER_METHOD_BODY(text, InsertText(contents, length, position), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::CopyText(AtkEditableText *text, gint start, gint end) { + WRAPPER_METHOD_BODY(text, CopyText(start, end), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::CutText(AtkEditableText *text, gint start, gint end) { + WRAPPER_METHOD_BODY(text, CutText(start, end), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::DeleteText(AtkEditableText *text, gint start, gint end) { + WRAPPER_METHOD_BODY(text, DeleteText(start, end), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::PasteText(AtkEditableText *text, gint position) { + WRAPPER_METHOD_BODY(text, PasteText(position), ) +} + +// GObject glue + +#if HAVE_GTK_FACTORY +static GType scintilla_object_accessible_factory_get_type(void); +#endif + +static void scintilla_object_accessible_init(ScintillaObjectAccessible *accessible); +static void scintilla_object_accessible_class_init(ScintillaObjectAccessibleClass *klass); +static gpointer scintilla_object_accessible_parent_class = NULL; + + +// @p parent_type is only required on GTK 3.2 to 3.6, and only on the first call +static GType scintilla_object_accessible_get_type(GType parent_type G_GNUC_UNUSED) { + static volatile gsize type_id_result = 0; + + if (g_once_init_enter(&type_id_result)) { + GTypeInfo tinfo = { + 0, /* class size */ + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) scintilla_object_accessible_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + 0, /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) scintilla_object_accessible_init, /* instance init */ + NULL /* value table */ + }; + + const GInterfaceInfo atk_text_info = { + (GInterfaceInitFunc) ScintillaGTKAccessible::AtkTextIface::init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + const GInterfaceInfo atk_editable_text_info = { + (GInterfaceInitFunc) ScintillaGTKAccessible::AtkEditableTextIface::init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + +#if HAVE_GTK_A11Y_H + // good, we have gtk-a11y.h, we can use that + GType derived_atk_type = GTK_TYPE_CONTAINER_ACCESSIBLE; + tinfo.class_size = sizeof (GtkContainerAccessibleClass); + tinfo.instance_size = sizeof (GtkContainerAccessible); +#else // ! HAVE_GTK_A11Y_H +# if HAVE_GTK_FACTORY + // Figure out the size of the class and instance we are deriving from through the registry + GType derived_type = g_type_parent(SCINTILLA_TYPE_OBJECT); + AtkObjectFactory *factory = atk_registry_get_factory(atk_get_default_registry(), derived_type); + GType derived_atk_type = atk_object_factory_get_accessible_type(factory); +# else // ! HAVE_GTK_FACTORY + // We're kind of screwed and can't determine the parent (no registry, and no public type) + // Hack your way around by requiring the caller to give us our parent type. The caller + // might be able to trick its way into doing that, by e.g. instantiating the parent's + // accessible type and get its GType. It's ugly but we can't do better on GTK 3.2 to 3.6. + g_assert(parent_type != 0); + + GType derived_atk_type = parent_type; +# endif // ! HAVE_GTK_FACTORY + + GTypeQuery query; + g_type_query(derived_atk_type, &query); + tinfo.class_size = query.class_size; + tinfo.instance_size = query.instance_size; +#endif // ! HAVE_GTK_A11Y_H + + GType type_id = g_type_register_static(derived_atk_type, "ScintillaObjectAccessible", &tinfo, (GTypeFlags) 0); + g_type_add_interface_static(type_id, ATK_TYPE_TEXT, &atk_text_info); + g_type_add_interface_static(type_id, ATK_TYPE_EDITABLE_TEXT, &atk_editable_text_info); + + g_once_init_leave(&type_id_result, type_id); + } + + return type_id_result; +} + +static AtkObject *scintilla_object_accessible_new(GType parent_type, GObject *obj) { + g_return_val_if_fail(SCINTILLA_IS_OBJECT(obj), NULL); + + AtkObject *accessible = (AtkObject *) g_object_new(scintilla_object_accessible_get_type(parent_type), +#if HAVE_WIDGET_SET_UNSET + "widget", obj, +#endif + NULL); + atk_object_initialize(accessible, obj); + + return accessible; +} + +// implementation for gtk_widget_get_accessible(). +// See the comment at the top of the file for details on the implementation +// @p widget the widget. +// @p cache pointer to store the AtkObject between repeated calls. Might or might not be filled. +// @p widget_parent_class pointer to the widget's parent class (to chain up method calls). +AtkObject *ScintillaGTKAccessible::WidgetGetAccessibleImpl(GtkWidget *widget, AtkObject **cache, gpointer widget_parent_class G_GNUC_UNUSED) { +#if HAVE_GTK_A11Y_H // just instantiate the accessible + if (*cache == NULL) { + *cache = scintilla_object_accessible_new(0, G_OBJECT(widget)); + } +#elif HAVE_GTK_FACTORY // register in the factory and let GTK instantiate + static volatile gsize registered = 0; + + if (g_once_init_enter(®istered)) { + // Figure out whether accessibility is enabled by looking at the type of the accessible + // object which would be created for the parent type of ScintillaObject. + GType derived_type = g_type_parent(SCINTILLA_TYPE_OBJECT); + + AtkRegistry *registry = atk_get_default_registry(); + AtkObjectFactory *factory = atk_registry_get_factory(registry, derived_type); + GType derived_atk_type = atk_object_factory_get_accessible_type(factory); + if (g_type_is_a(derived_atk_type, GTK_TYPE_ACCESSIBLE)) { + atk_registry_set_factory_type(registry, SCINTILLA_TYPE_OBJECT, + scintilla_object_accessible_factory_get_type()); + } + g_once_init_leave(®istered, 1); + } + *cache = GTK_WIDGET_CLASS(widget_parent_class)->get_accessible(widget); +#else // no public API, no factory, so guess from the parent and instantiate + if (*cache == NULL) { + static GType parent_atk_type = 0; + + if (parent_atk_type == 0) { + AtkObject *parent_obj = GTK_WIDGET_CLASS(widget_parent_class)->get_accessible(widget); + if (parent_obj) { + GType parent_atk_type = G_OBJECT_TYPE(parent_obj); + + // Figure out whether accessibility is enabled by looking at the type of the accessible + // object which would be created for the parent type of ScintillaObject. + if (g_type_is_a(parent_atk_type, GTK_TYPE_ACCESSIBLE)) { + *cache = scintilla_object_accessible_new(parent_atk_type, G_OBJECT(widget)); + g_object_unref(parent_obj); + } else { + *cache = parent_obj; + } + } + } + } +#endif + return *cache; +} + +static AtkStateSet *scintilla_object_accessible_ref_state_set(AtkObject *accessible) { + AtkStateSet *state_set = ATK_OBJECT_CLASS(scintilla_object_accessible_parent_class)->ref_state_set(accessible); + + GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(accessible)); + if (widget == NULL) { + atk_state_set_add_state(state_set, ATK_STATE_DEFUNCT); + } else { + if (! scintilla_send_message(SCINTILLA_OBJECT(widget), SCI_GETREADONLY, 0, 0)) + atk_state_set_add_state(state_set, ATK_STATE_EDITABLE); +#if ATK_CHECK_VERSION(2, 16, 0) + else + atk_state_set_add_state(state_set, ATK_STATE_READ_ONLY); +#endif + atk_state_set_add_state(state_set, ATK_STATE_MULTI_LINE); + atk_state_set_add_state(state_set, ATK_STATE_MULTISELECTABLE); + atk_state_set_add_state(state_set, ATK_STATE_SELECTABLE_TEXT); + /*atk_state_set_add_state(state_set, ATK_STATE_SUPPORTS_AUTOCOMPLETION);*/ + } + + return state_set; +} + +static void scintilla_object_accessible_widget_set(GtkAccessible *accessible) { + GtkWidget *widget = gtk_accessible_get_widget(accessible); + if (widget == NULL) + return; + + ScintillaObjectAccessiblePrivate *priv = SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible); + if (priv->pscin != 0) + delete priv->pscin; + priv->pscin = new ScintillaGTKAccessible(accessible, widget); +} + +#if HAVE_WIDGET_SET_UNSET +static void scintilla_object_accessible_widget_unset(GtkAccessible *accessible) { + GtkWidget *widget = gtk_accessible_get_widget(accessible); + if (widget == NULL) + return; + + ScintillaObjectAccessiblePrivate *priv = SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible); + delete priv->pscin; + priv->pscin = 0; +} +#endif + +static void scintilla_object_accessible_initialize(AtkObject *obj, gpointer data) { + ATK_OBJECT_CLASS(scintilla_object_accessible_parent_class)->initialize(obj, data); + +#if ! HAVE_WIDGET_SET_UNSET + scintilla_object_accessible_widget_set(GTK_ACCESSIBLE(obj)); +#endif + + obj->role = ATK_ROLE_TEXT; +} + +static void scintilla_object_accessible_finalize(GObject *object) { + ScintillaObjectAccessiblePrivate *priv = SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(object); + + if (priv->pscin) { + delete priv->pscin; + priv->pscin = 0; + } + + G_OBJECT_CLASS(scintilla_object_accessible_parent_class)->finalize(object); +} + +static void scintilla_object_accessible_class_init(ScintillaObjectAccessibleClass *klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + AtkObjectClass *object_class = ATK_OBJECT_CLASS(klass); + +#if HAVE_WIDGET_SET_UNSET + GtkAccessibleClass *accessible_class = GTK_ACCESSIBLE_CLASS(klass); + accessible_class->widget_set = scintilla_object_accessible_widget_set; + accessible_class->widget_unset = scintilla_object_accessible_widget_unset; +#endif + + object_class->ref_state_set = scintilla_object_accessible_ref_state_set; + object_class->initialize = scintilla_object_accessible_initialize; + + gobject_class->finalize = scintilla_object_accessible_finalize; + + scintilla_object_accessible_parent_class = g_type_class_peek_parent(klass); + + g_type_class_add_private(klass, sizeof (ScintillaObjectAccessiblePrivate)); +} + +static void scintilla_object_accessible_init(ScintillaObjectAccessible *accessible) { + ScintillaObjectAccessiblePrivate *priv = SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible); + + priv->pscin = 0; +} + +#if HAVE_GTK_FACTORY +// Object factory +typedef AtkObjectFactory ScintillaObjectAccessibleFactory; +typedef AtkObjectFactoryClass ScintillaObjectAccessibleFactoryClass; + +G_DEFINE_TYPE(ScintillaObjectAccessibleFactory, scintilla_object_accessible_factory, ATK_TYPE_OBJECT_FACTORY) + +static void scintilla_object_accessible_factory_init(ScintillaObjectAccessibleFactory *) { +} + +static GType scintilla_object_accessible_factory_get_accessible_type(void) { + return SCINTILLA_TYPE_OBJECT_ACCESSIBLE; +} + +static AtkObject *scintilla_object_accessible_factory_create_accessible(GObject *obj) { + return scintilla_object_accessible_new(0, obj); +} + +static void scintilla_object_accessible_factory_class_init(AtkObjectFactoryClass * klass) { + klass->create_accessible = scintilla_object_accessible_factory_create_accessible; + klass->get_accessible_type = scintilla_object_accessible_factory_get_accessible_type; +} +#endif diff --git a/scintilla/gtk/ScintillaGTKAccessible.h b/scintilla/gtk/ScintillaGTKAccessible.h new file mode 100644 index 000000000..5614c88e2 --- /dev/null +++ b/scintilla/gtk/ScintillaGTKAccessible.h @@ -0,0 +1,171 @@ +/* Scintilla source code edit control */ +/* ScintillaGTKAccessible.h - GTK+ accessibility for ScintillaGTK */ +/* Copyright 2016 by Colomban Wendling + * The License.txt file describes the conditions under which this software may be distributed. */ + +#ifndef SCINTILLAGTKACCESSIBLE_H +#define SCINTILLAGTKACCESSIBLE_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +#ifndef ATK_CHECK_VERSION +# define ATK_CHECK_VERSION(x, y, z) 0 +#endif + +class ScintillaGTKAccessible { +private: + GtkAccessible *accessible; + ScintillaGTK *sci; + + // local state for comparing + Position old_pos; + std::vector old_sels; + + void UpdateCursor(); + void Notify(GtkWidget *widget, gint code, SCNotification *nt); + static void SciNotify(GtkWidget *widget, gint code, SCNotification *nt, gpointer data) { + try { + reinterpret_cast(data)->Notify(widget, code, nt); + } catch (...) {} + } + + Position ByteOffsetFromCharacterOffset(Position startByte, int characterOffset) { + Position pos = sci->pdoc->GetRelativePosition(startByte, characterOffset); + if (pos == INVALID_POSITION) { + // clamp invalid positions inside the document + if (characterOffset > 0) { + return sci->pdoc->Length(); + } else { + return 0; + } + } + return pos; + } + + Position ByteOffsetFromCharacterOffset(int characterOffset) { + return ByteOffsetFromCharacterOffset(0, characterOffset); + } + + int CharacterOffsetFromByteOffset(Position byteOffset) { + return sci->pdoc->CountCharacters(0, byteOffset); + } + + void CharacterRangeFromByteRange(Position startByte, Position endByte, int *startChar, int *endChar) { + *startChar = CharacterOffsetFromByteOffset(startByte); + *endChar = *startChar + sci->pdoc->CountCharacters(startByte, endByte); + } + + void ByteRangeFromCharacterRange(int startChar, int endChar, Position& startByte, Position& endByte) { + startByte = ByteOffsetFromCharacterOffset(startChar); + endByte = ByteOffsetFromCharacterOffset(startByte, endChar - startChar); + } + + Position PositionBefore(Position pos) { + return sci->pdoc->MovePositionOutsideChar(pos - 1, -1, true); + } + + Position PositionAfter(Position pos) { + return sci->pdoc->MovePositionOutsideChar(pos + 1, 1, true); + } + + // For AtkText + gchar *GetTextRangeUTF8(Position startByte, Position endByte); + gchar *GetText(int startChar, int endChar); + gchar *GetTextAfterOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); + gchar *GetTextBeforeOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); + gchar *GetTextAtOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); +#if ATK_CHECK_VERSION(2, 10, 0) + gchar *GetStringAtOffset(int charOffset, AtkTextGranularity granularity, int *startChar, int *endChar); +#endif + gunichar GetCharacterAtOffset(int charOffset); + gint GetCharacterCount(); + gint GetCaretOffset(); + gboolean SetCaretOffset(int charOffset); + gint GetOffsetAtPoint(gint x, gint y, AtkCoordType coords); + void GetCharacterExtents(int charOffset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords); + AtkAttributeSet *GetAttributesForStyle(unsigned int style); + AtkAttributeSet *GetRunAttributes(int charOffset, int *startChar, int *endChar); + AtkAttributeSet *GetDefaultAttributes(); + gint GetNSelections(); + gchar *GetSelection(gint selection_num, int *startChar, int *endChar); + gboolean AddSelection(int startChar, int endChar); + gboolean RemoveSelection(int selection_num); + gboolean SetSelection(gint selection_num, int startChar, int endChar); + // for AtkEditableText + bool InsertStringUTF8(Position bytePos, const gchar *utf8, int lengthBytes); + void SetTextContents(const gchar *contents); + void InsertText(const gchar *contents, int lengthBytes, int *charPosition); + void CopyText(int startChar, int endChar); + void CutText(int startChar, int endChar); + void DeleteText(int startChar, int endChar); + void PasteText(int charPosition); + +public: + ScintillaGTKAccessible(GtkAccessible *accessible, GtkWidget *widget); + ~ScintillaGTKAccessible(); + + static ScintillaGTKAccessible *FromAccessible(GtkAccessible *accessible); + static ScintillaGTKAccessible *FromAccessible(AtkObject *accessible) { + return FromAccessible(GTK_ACCESSIBLE(accessible)); + } + // So ScintillaGTK can notify us + void ChangeDocument(Document *oldDoc, Document *newDoc); + void NotifyReadOnly(); + + // Helper GtkWidget methods + static AtkObject *WidgetGetAccessibleImpl(GtkWidget *widget, AtkObject **cache, gpointer widget_parent_class); + + // ATK methods + + class AtkTextIface { + public: + static void init(::AtkTextIface *iface); + + private: + AtkTextIface(); + + static gchar *GetText(AtkText *text, int start_offset, int end_offset); + static gchar *GetTextAfterOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset); + static gchar *GetTextBeforeOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset); + static gchar *GetTextAtOffset(AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); +#if ATK_CHECK_VERSION(2, 10, 0) + static gchar *GetStringAtOffset(AtkText *text, gint offset, AtkTextGranularity granularity, gint *start_offset, gint *end_offset); +#endif + static gunichar GetCharacterAtOffset(AtkText *text, gint offset); + static gint GetCharacterCount(AtkText *text); + static gint GetCaretOffset(AtkText *text); + static gboolean SetCaretOffset(AtkText *text, gint offset); + static gint GetOffsetAtPoint(AtkText *text, gint x, gint y, AtkCoordType coords); + static void GetCharacterExtents(AtkText *text, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords); + static AtkAttributeSet *GetRunAttributes(AtkText *text, gint offset, gint *start_offset, gint *end_offset); + static AtkAttributeSet *GetDefaultAttributes(AtkText *text); + static gint GetNSelections(AtkText *text); + static gchar *GetSelection(AtkText *text, gint selection_num, gint *start_pos, gint *end_pos); + static gboolean AddSelection(AtkText *text, gint start, gint end); + static gboolean RemoveSelection(AtkText *text, gint selection_num); + static gboolean SetSelection(AtkText *text, gint selection_num, gint start, gint end); + }; + class AtkEditableTextIface { + public: + static void init(::AtkEditableTextIface *iface); + + private: + AtkEditableTextIface(); + + static void SetTextContents(AtkEditableText *text, const gchar *contents); + static void InsertText(AtkEditableText *text, const gchar *contents, gint length, gint *position); + static void CopyText(AtkEditableText *text, gint start, gint end); + static void CutText(AtkEditableText *text, gint start, gint end); + static void DeleteText(AtkEditableText *text, gint start, gint end); + static void PasteText(AtkEditableText *text, gint position); + }; +}; + +#ifdef SCI_NAMESPACE +} +#endif + + +#endif /* SCINTILLAGTKACCESSIBLE_H */ diff --git a/scintilla/include/Platform.h b/scintilla/include/Platform.h index 63269d003..1ff48ecb1 100644 --- a/scintilla/include/Platform.h +++ b/scintilla/include/Platform.h @@ -526,8 +526,4 @@ public: } #endif -#if defined(__GNUC__) && defined(SCINTILLA_QT) -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif - #endif diff --git a/scintilla/include/SciLexer.h b/scintilla/include/SciLexer.h index 0eb0b2956..44c02a84a 100644 --- a/scintilla/include/SciLexer.h +++ b/scintilla/include/SciLexer.h @@ -133,6 +133,7 @@ #define SCLEX_IHEX 118 #define SCLEX_TEHEX 119 #define SCLEX_JSON 120 +#define SCLEX_EDIFACT 121 #define SCLEX_AUTOMATIC 1000 #define SCE_P_DEFAULT 0 #define SCE_P_COMMENTLINE 1 @@ -1797,6 +1798,15 @@ #define SCE_JSON_KEYWORD 11 #define SCE_JSON_LDKEYWORD 12 #define SCE_JSON_ERROR 13 +#define SCE_EDI_DEFAULT 0 +#define SCE_EDI_SEGMENTSTART 1 +#define SCE_EDI_SEGMENTEND 2 +#define SCE_EDI_SEP_ELEMENT 3 +#define SCE_EDI_SEP_COMPOSITE 4 +#define SCE_EDI_SEP_RELEASE 5 +#define SCE_EDI_UNA 6 +#define SCE_EDI_UNH 7 +#define SCE_EDI_BADSEGMENT 8 /* --Autogenerated -- end of section automatically generated from Scintilla.iface */ #endif diff --git a/scintilla/include/Scintilla.h b/scintilla/include/Scintilla.h index 9fd519f6d..6a36d24f4 100644 --- a/scintilla/include/Scintilla.h +++ b/scintilla/include/Scintilla.h @@ -11,8 +11,6 @@ #ifndef SCINTILLA_H #define SCINTILLA_H -#include "Sci_Position.h" - #ifdef __cplusplus extern "C" { #endif @@ -28,20 +26,21 @@ int Scintilla_LinkLexers(void); } #endif -/* Here should be placed typedefs for uptr_t, an unsigned integer type large enough to - * hold a pointer and sptr_t, a signed integer large enough to hold a pointer. - * May need to be changed for 64 bit platforms. */ -#if defined(_WIN32) -#include -#endif -#ifdef MAXULONG_PTR -typedef ULONG_PTR uptr_t; -typedef LONG_PTR sptr_t; +// Include header that defines basic numeric types. +#if defined(_MSC_VER) +// Older releases of MSVC did not have stdint.h. +#include #else -typedef unsigned long uptr_t; -typedef long sptr_t; +#include #endif +// Define uptr_t, an unsigned integer type large enough to hold a pointer. +typedef uintptr_t uptr_t; +// Define sptr_t, a signed integer large enough to hold a pointer. +typedef intptr_t sptr_t; + +#include "Sci_Position.h" + typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam); /* ++Autogenerated -- start of section automatically generated from Scintilla.iface */ @@ -76,6 +75,10 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCWS_VISIBLEONLYININDENT 3 #define SCI_GETVIEWWS 2020 #define SCI_SETVIEWWS 2021 +#define SCTD_LONGARROW 0 +#define SCTD_STRIKEOUT 1 +#define SCI_GETTABDRAWMODE 2698 +#define SCI_SETTABDRAWMODE 2699 #define SCI_POSITIONFROMPOINT 2022 #define SCI_POSITIONFROMPOINTCLOSE 2023 #define SCI_GOTOLINE 2024 @@ -189,6 +192,7 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define STYLE_CONTROLCHAR 36 #define STYLE_INDENTGUIDE 37 #define STYLE_CALLTIP 38 +#define STYLE_FOLDDISPLAYTEXT 39 #define STYLE_LASTPREDEFINED 39 #define STYLE_MAX 255 #define SC_CHARSET_ANSI 0 @@ -287,6 +291,8 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define INDIC_COMPOSITIONTHIN 15 #define INDIC_FULLBOX 16 #define INDIC_TEXTFORE 17 +#define INDIC_POINT 18 +#define INDIC_POINTCHARACTER 19 #define INDIC_IME 32 #define INDIC_IME_MAX 35 #define INDIC_MAX 35 @@ -480,6 +486,11 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_SETFOLDEXPANDED 2229 #define SCI_GETFOLDEXPANDED 2230 #define SCI_TOGGLEFOLD 2231 +#define SCI_TOGGLEFOLDSHOWTEXT 2700 +#define SC_FOLDDISPLAYTEXT_HIDDEN 0 +#define SC_FOLDDISPLAYTEXT_STANDARD 1 +#define SC_FOLDDISPLAYTEXT_BOXED 2 +#define SCI_FOLDDISPLAYTEXTSETSTYLE 2701 #define SC_FOLDACTION_CONTRACT 0 #define SC_FOLDACTION_EXPAND 1 #define SC_FOLDACTION_TOGGLE 2 @@ -668,6 +679,9 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_SEARCHNEXT 2367 #define SCI_SEARCHPREV 2368 #define SCI_LINESONSCREEN 2370 +#define SC_POPUP_NEVER 0 +#define SC_POPUP_ALL 1 +#define SC_POPUP_TEXT 2 #define SCI_USEPOPUP 2371 #define SCI_SELECTIONISRECTANGLE 2372 #define SCI_SETZOOM 2373 @@ -1080,16 +1094,13 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCN_FOCUSIN 2028 #define SCN_FOCUSOUT 2029 #define SCN_AUTOCCOMPLETED 2030 +#define SCN_MARGINRIGHTCLICK 2031 /* --Autogenerated -- end of section automatically generated from Scintilla.iface */ /* These structures are defined to be exactly the same shape as the Win32 * CHARRANGE, TEXTRANGE, FINDTEXTEX, FORMATRANGE, and NMHDR structs. * So older code that treats Scintilla as a RichEdit will work. */ -#if defined(__cplusplus) && defined(SCI_NAMESPACE) -namespace Scintilla { -#endif - struct Sci_CharacterRange { Sci_PositionCR cpMin; Sci_PositionCR cpMax; @@ -1106,10 +1117,6 @@ struct Sci_TextToFind { struct Sci_CharacterRange chrgText; }; -#define CharacterRange Sci_CharacterRange -#define TextRange Sci_TextRange -#define TextToFind Sci_TextToFind - typedef void *Sci_SurfaceID; struct Sci_Rectangle { @@ -1130,8 +1137,6 @@ struct Sci_RangeToFormat { struct Sci_CharacterRange chrg; }; -#define RangeToFormat Sci_RangeToFormat - #ifndef __cplusplus /* For the GTK+ platform, g-ir-scanner needs to have these typedefs. This * is not required in C++ code and actually seems to break ScintillaEditPy */ @@ -1148,8 +1153,6 @@ struct Sci_NotifyHeader { unsigned int code; }; -#define NotifyHeader Sci_NotifyHeader - struct SCNotification { Sci_NotifyHeader nmhdr; Sci_Position position; @@ -1189,18 +1192,17 @@ struct SCNotification { /* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION, */ }; -#if defined(__cplusplus) && defined(SCI_NAMESPACE) -} -#endif - #ifdef INCLUDE_DEPRECATED_FEATURES -#define SC_CP_DBCS 1 -#define SCI_SETUSEPALETTE 2039 -#define SCI_GETUSEPALETTE 2139 #define SCI_SETKEYSUNICODE 2521 #define SCI_GETKEYSUNICODE 2522 +#define CharacterRange Sci_CharacterRange +#define TextRange Sci_TextRange +#define TextToFind Sci_TextToFind +#define RangeToFormat Sci_RangeToFormat +#define NotifyHeader Sci_NotifyHeader + #endif #endif diff --git a/scintilla/include/Scintilla.iface b/scintilla/include/Scintilla.iface index de0d88142..6b9a77f66 100644 --- a/scintilla/include/Scintilla.iface +++ b/scintilla/include/Scintilla.iface @@ -168,6 +168,17 @@ get int GetViewWS=2020(,) # Make white space characters invisible, always visible or visible outside indentation. set void SetViewWS=2021(int viewWS,) +enu TabDrawMode=SCTD_ +val SCTD_LONGARROW=0 +val SCTD_STRIKEOUT=1 + +# Retrieve the current tab draw mode. +# Returns one of SCTD_* constants. +get int GetTabDrawMode=2698(,) + +# Set how tabs are drawn when visible. +set void SetTabDrawMode=2699(int tabDrawMode,) + # Find the position from a point within the window. fun position PositionFromPoint=2022(int x, int y) @@ -414,6 +425,7 @@ val STYLE_BRACEBAD=35 val STYLE_CONTROLCHAR=36 val STYLE_INDENTGUIDE=37 val STYLE_CALLTIP=38 +val STYLE_FOLDDISPLAYTEXT=39 val STYLE_LASTPREDEFINED=39 val STYLE_MAX=255 @@ -626,6 +638,8 @@ val INDIC_COMPOSITIONTHICK=14 val INDIC_COMPOSITIONTHIN=15 val INDIC_FULLBOX=16 val INDIC_TEXTFORE=17 +val INDIC_POINT=18 +val INDIC_POINTCHARACTER=19 val INDIC_IME=32 val INDIC_IME_MAX=35 val INDIC_MAX=35 @@ -1198,6 +1212,17 @@ get bool GetFoldExpanded=2230(int line,) # Switch a header line between expanded and contracted. fun void ToggleFold=2231(int line,) +# Switch a header line between expanded and contracted and show some text after the line. +fun void ToggleFoldShowText=2700(int line, string text) + +enu foldDisplayTextStyle=SC_FOLDDISPLAYTEXTSTYLE_ +val SC_FOLDDISPLAYTEXT_HIDDEN=0 +val SC_FOLDDISPLAYTEXT_STANDARD=1 +val SC_FOLDDISPLAYTEXT_BOXED=2 + +# Set the style of fold display text +set void FoldDisplayTextSetStyle=2701(int style,) + enu FoldAction=SC_FOLDACTION_ val SC_FOLDACTION_CONTRACT=0 val SC_FOLDACTION_EXPAND=1 @@ -1723,9 +1748,14 @@ fun int SearchPrev=2368(int searchFlags, string text) # Retrieves the number of lines completely visible. get int LinesOnScreen=2370(,) +enu PopUp=SC_POPUP_ +val SC_POPUP_NEVER=0 +val SC_POPUP_ALL=1 +val SC_POPUP_TEXT=2 + # Set whether a pop up menu is displayed automatically when the user presses -# the wrong mouse button. -fun void UsePopUp=2371(bool allowPopUp,) +# the wrong mouse button on certain areas. +fun void UsePopUp=2371(int popUpMode,) # Is the selection rectangular? The alternative is the more common stream selection. get bool SelectionIsRectangle=2372(,) @@ -2860,6 +2890,7 @@ val SCLEX_SREC=117 val SCLEX_IHEX=118 val SCLEX_TEHEX=119 val SCLEX_JSON=120 +val SCLEX_EDIFACT=121 # When a lexer specifies its language as SCLEX_AUTOMATIC it receives a # value assigned in sequence from SCLEX_AUTOMATIC+1. @@ -4749,6 +4780,16 @@ val SCE_JSON_COMPACTIRI=10 val SCE_JSON_KEYWORD=11 val SCE_JSON_LDKEYWORD=12 val SCE_JSON_ERROR=13 +lex EDIFACT=SCLEX_EDIFACT SCE_EDI_ +val SCE_EDI_DEFAULT=0 +val SCE_EDI_SEGMENTSTART=1 +val SCE_EDI_SEGMENTEND=2 +val SCE_EDI_SEP_ELEMENT=3 +val SCE_EDI_SEP_COMPOSITE=4 +val SCE_EDI_SEP_RELEASE=5 +val SCE_EDI_UNA=6 +val SCE_EDI_UNH=7 +val SCE_EDI_BADSEGMENT=8 # Events @@ -4783,6 +4824,7 @@ evt void HotSpotReleaseClick=2027(int modifiers, int position) evt void FocusIn=2028(void) evt void FocusOut=2029(void) evt void AutoCCompleted=2030(string text, int position, int ch, CompletionMethods listCompletionMethod) +evt void MarginRightClick=2031(int modifiers, int position, int margin) # There are no provisional APIs currently, but some arguments to SCI_SETTECHNOLOGY are provisional. @@ -4790,19 +4832,6 @@ cat Provisional cat Deprecated -# Deprecated in 2.21 -# The SC_CP_DBCS value can be used to indicate a DBCS mode for GTK+. -val SC_CP_DBCS=1 - -# Deprecated in 2.30 - -# In palette mode? -get bool GetUsePalette=2139(,) - -# In palette mode, Scintilla uses the environment's palette calls to display -# more colours. This may lead to ugly displays. -set void SetUsePalette=2039(bool usePalette,) - # Deprecated in 3.5.5 # Always interpret keyboard input as Unicode diff --git a/scintilla/lexers/LexCPP.cxx b/scintilla/lexers/LexCPP.cxx index 64387a085..ec040fbf6 100644 --- a/scintilla/lexers/LexCPP.cxx +++ b/scintilla/lexers/LexCPP.cxx @@ -692,7 +692,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i } } - StyleContext sc(startPos, length, initStyle, styler, static_cast(0xff)); + StyleContext sc(startPos, length, initStyle, styler); LinePPState preproc = vlls.ForLine(lineCurrent); bool definitionsChanged = false; diff --git a/scintilla/lexers/LexMatlab.cxx b/scintilla/lexers/LexMatlab.cxx index 6b4b2a92a..ca5e4d908 100644 --- a/scintilla/lexers/LexMatlab.cxx +++ b/scintilla/lexers/LexMatlab.cxx @@ -15,6 +15,9 @@ ** ** Changes by John Donoghue 2014/08/01 ** - fix allowed transpose ' after {} operator + ** + ** Changes by John Donoghue 2016/11/15 + ** - update matlab code folding **/ // Copyright 1998-2001 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. @@ -49,14 +52,28 @@ static bool IsOctaveCommentChar(int c) { return (c == '%' || c == '#') ; } -static bool IsMatlabComment(Accessor &styler, Sci_Position pos, Sci_Position len) { - return len > 0 && IsMatlabCommentChar(styler[pos]) ; +static inline int LowerCase(int c) { + if (c >= 'A' && c <= 'Z') + return 'a' + c - 'A'; + return c; } -static bool IsOctaveComment(Accessor &styler, Sci_Position pos, Sci_Position len) { - return len > 0 && IsOctaveCommentChar(styler[pos]) ; +static int CheckKeywordFoldPoint(char *str) { + if (strcmp ("if", str) == 0 || + strcmp ("for", str) == 0 || + strcmp ("switch", str) == 0 || + strcmp ("try", str) == 0 || + strcmp ("do", str) == 0 || + strcmp ("parfor", str) == 0 || + strcmp ("function", str) == 0) + return 1; + if (strncmp("end", str, 3) == 0 || + strcmp("until", str) == 0) + return -1; + return 0; } + static void ColouriseMatlabOctaveDoc( Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler, @@ -245,58 +262,82 @@ static void ColouriseOctaveDoc(Sci_PositionU startPos, Sci_Position length, int ColouriseMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveCommentChar, false); } -static void FoldMatlabOctaveDoc(Sci_PositionU startPos, Sci_Position length, int, +static void FoldMatlabOctaveDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler, - bool (*IsComment)(Accessor&, Sci_Position, Sci_Position)) { - - Sci_Position endPos = startPos + length; + bool (*IsComment)(int ch)) { - // Backtrack to previous line in case need to fix its fold status + Sci_PositionU endPos = startPos + length; + int visibleChars = 0; Sci_Position lineCurrent = styler.GetLine(startPos); - if (startPos > 0) { - if (lineCurrent > 0) { - lineCurrent--; - startPos = styler.LineStart(lineCurrent); - } - } - int spaceFlags = 0; - int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsComment); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) + levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; + int levelNext = levelCurrent; char chNext = styler[startPos]; - for (Sci_Position i = startPos; i < endPos; i++) { + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + char word[100]; + int wordlen = 0; + for (Sci_PositionU i = startPos; i < endPos; i++) { char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); - - if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) { - int lev = indentCurrent; - int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsComment); - if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { - // Only non whitespace lines can be headers - if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) { - lev |= SC_FOLDLEVELHEADERFLAG; - } else if (indentNext & SC_FOLDLEVELWHITEFLAG) { - // Line after is blank so check the next - maybe should continue further? - int spaceFlags2 = 0; - int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsComment); - if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) { - lev |= SC_FOLDLEVELHEADERFLAG; - } - } + style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + + // a line that starts with a comment + if (style == SCE_MATLAB_COMMENT && IsComment(ch) && visibleChars == 0) { + // start/end of block comment + if (chNext == '{') + levelNext ++; + if (chNext == '}') + levelNext --; + } + // keyword + if(style == SCE_MATLAB_KEYWORD) { + word[wordlen++] = static_cast(LowerCase(ch)); + if (wordlen == 100) { // prevent overflow + word[0] = '\0'; + wordlen = 1; + } + if (styleNext != SCE_MATLAB_KEYWORD) { + word[wordlen] = '\0'; + wordlen = 0; + + levelNext += CheckKeywordFoldPoint(word); + } + } + if (!IsASpace(ch)) + visibleChars++; + if (atEOL || (i == endPos-1)) { + int levelUse = levelCurrent; + int lev = levelUse | levelNext << 16; + if (visibleChars == 0) + lev |= SC_FOLDLEVELWHITEFLAG; + if (levelUse < levelNext) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); } - indentCurrent = indentNext; - styler.SetLevel(lineCurrent, lev); lineCurrent++; + levelCurrent = levelNext; + if (atEOL && (i == static_cast(styler.Length() - 1))) { + // There is an empty line at end of file so give it same level and empty + styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG); + } + visibleChars = 0; } } } static void FoldMatlabDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler) { - FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsMatlabComment); + FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsMatlabCommentChar); } static void FoldOctaveDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler) { - FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveComment); + FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveCommentChar); } static const char * const matlabWordListDesc[] = { diff --git a/scintilla/lexers/LexPerl.cxx b/scintilla/lexers/LexPerl.cxx index 3b10b47e6..b3c5d5899 100644 --- a/scintilla/lexers/LexPerl.cxx +++ b/scintilla/lexers/LexPerl.cxx @@ -755,7 +755,7 @@ void SCI_METHOD LexerPerl::Lex(Sci_PositionU startPos, Sci_Position length, int backPos++; } - StyleContext sc(startPos, endPos - startPos, initStyle, styler, static_cast(STYLE_MAX)); + StyleContext sc(startPos, endPos - startPos, initStyle, styler); for (; sc.More(); sc.Forward()) { diff --git a/scintilla/lexers/LexYAML.cxx b/scintilla/lexers/LexYAML.cxx index e420d9d79..9f28bb843 100644 --- a/scintilla/lexers/LexYAML.cxx +++ b/scintilla/lexers/LexYAML.cxx @@ -49,10 +49,25 @@ static unsigned int SpaceCount(char* lineBuffer) { return static_cast(headBuffer - lineBuffer); } -#define YAML_STATE_BITSIZE 16 +static bool KeywordAtChar(char* lineBuffer, char* startComment, const WordList &keywords) { + if (lineBuffer == NULL || startComment <= lineBuffer) + return false; + char* endValue = startComment - 1; + while (endValue >= lineBuffer && *endValue == ' ') + endValue--; + Sci_PositionU len = static_cast(endValue - lineBuffer) + 1; + char s[100]; + if (len > (sizeof(s) / sizeof(s[0]) - 1)) + return false; + strncpy(s, lineBuffer, len); + s[len] = '\0'; + return (keywords.InList(s)); +} + +#define YAML_STATE_BITSIZE 16 #define YAML_STATE_MASK (0xFFFF0000) #define YAML_STATE_DOCUMENT (1 << YAML_STATE_BITSIZE) -#define YAML_STATE_VALUE (2 << YAML_STATE_BITSIZE) +#define YAML_STATE_VALUE (2 << YAML_STATE_BITSIZE) #define YAML_STATE_COMMENT (3 << YAML_STATE_BITSIZE) #define YAML_STATE_TEXT_PARENT (4 << YAML_STATE_BITSIZE) #define YAML_STATE_TEXT (5 << YAML_STATE_BITSIZE) @@ -139,41 +154,44 @@ static void ColouriseYAMLLine( styler.ColourTo(endPos, SCE_YAML_COMMENT); return; } + Sci_PositionU startComment = i; + bInQuotes = false; + while (startComment < lengthLine) { // Comment must be space padded + if (lineBuffer[startComment] == '\'' || lineBuffer[startComment] == '\"') + bInQuotes = !bInQuotes; + if (lineBuffer[startComment] == '#' && isspacechar(lineBuffer[startComment - 1]) && !bInQuotes) + break; + startComment++; + } styler.SetLineState(currentLine, YAML_STATE_VALUE); if (lineBuffer[i] == '&' || lineBuffer[i] == '*') { - styler.ColourTo(endPos, SCE_YAML_REFERENCE); + styler.ColourTo(startLine + startComment - 1, SCE_YAML_REFERENCE); + if (startComment < lengthLine) + styler.ColourTo(endPos, SCE_YAML_COMMENT); return; } - if (keywords.InList(&lineBuffer[i])) { // Convertible value (true/false, etc.) - styler.ColourTo(endPos, SCE_YAML_KEYWORD); + if (KeywordAtChar(&lineBuffer[i], &lineBuffer[startComment], keywords)) { // Convertible value (true/false, etc.) + styler.ColourTo(startLine + startComment - 1, SCE_YAML_KEYWORD); + if (startComment < lengthLine) + styler.ColourTo(endPos, SCE_YAML_COMMENT); return; - } else { - Sci_PositionU startComment = i; - bInQuotes = false; - while (startComment < lengthLine) { // Comment must be space padded - if (lineBuffer[startComment] == '\'' || lineBuffer[startComment] == '\"') - bInQuotes = !bInQuotes; - if (lineBuffer[startComment] == '#' && isspacechar(lineBuffer[startComment - 1]) && !bInQuotes) - break; - startComment++; - } - Sci_PositionU i2 = i; - while ((i < startComment) && lineBuffer[i]) { - if (!(IsASCII(lineBuffer[i]) && isdigit(lineBuffer[i])) && lineBuffer[i] != '-' - && lineBuffer[i] != '.' && lineBuffer[i] != ',' && lineBuffer[i] != ' ') { - styler.ColourTo(startLine + startComment - 1, SCE_YAML_DEFAULT); - if (startComment < lengthLine) - styler.ColourTo(endPos, SCE_YAML_COMMENT); - return; - } - i++; - } - if (i > i2) { - styler.ColourTo(startLine + startComment - 1, SCE_YAML_NUMBER); + } + Sci_PositionU i2 = i; + while ((i < startComment) && lineBuffer[i]) { + if (!(IsASCII(lineBuffer[i]) && isdigit(lineBuffer[i])) && lineBuffer[i] != '-' + && lineBuffer[i] != '.' && lineBuffer[i] != ',' && lineBuffer[i] != ' ') { + styler.ColourTo(startLine + startComment - 1, SCE_YAML_DEFAULT); if (startComment < lengthLine) styler.ColourTo(endPos, SCE_YAML_COMMENT); return; } + i++; + } + if (i > i2) { + styler.ColourTo(startLine + startComment - 1, SCE_YAML_NUMBER); + if (startComment < lengthLine) + styler.ColourTo(endPos, SCE_YAML_COMMENT); + return; } break; // shouldn't get here, but just in case, the rest of the line is coloured the default } @@ -255,7 +273,7 @@ static void FoldYAMLDoc(Sci_PositionU startPos, Sci_Position length, int /*initS if (lineNext <= docLines) { // Information about next line is only available if not at end of document indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL); - } + } const int comment = foldComment && IsCommentLine(lineCurrent, styler); const int comment_start = (comment && !prevComment && (lineNext <= docLines) && IsCommentLine(lineNext, styler) && (lev > SC_FOLDLEVELBASE)); diff --git a/scintilla/lexlib/LexerModule.h b/scintilla/lexlib/LexerModule.h index 356ff27e9..a561cf151 100644 --- a/scintilla/lexlib/LexerModule.h +++ b/scintilla/lexlib/LexerModule.h @@ -67,7 +67,7 @@ inline int Maximum(int a, int b) { // Shut up annoying Visual C++ warnings: #ifdef _MSC_VER -#pragma warning(disable: 4244 4309 4456 4457) +#pragma warning(disable: 4244 4456 4457) #endif // Turn off shadow warnings for lexers as may be maintained by others diff --git a/scintilla/scintilla_changes.patch b/scintilla/scintilla_changes.patch index debd31cdd..ab142ceac 100644 --- a/scintilla/scintilla_changes.patch +++ b/scintilla/scintilla_changes.patch @@ -52,7 +52,7 @@ index 0871ca2..49dc278 100644 } @@ -3250,6 +3250,7 @@ void scintilla_release_resources(void) { static void *copy_(void *src) { return src; } - static void free_(void *doc) { } + static void free_(void *) { } +GEANY_API_SYMBOL GType scnotification_get_type(void) { @@ -62,7 +62,7 @@ diff --git scintilla/src/Catalogue.cxx scintilla/src/Catalogue.cxx index ed47aa8..e58f1ab 100644 --- scintilla/src/Catalogue.cxx +++ scintilla/src/Catalogue.cxx -@@ -77,121 +77,50 @@ int Scintilla_LinkLexers() { +@@ -77,122 +77,50 @@ int Scintilla_LinkLexers() { //++Autogenerated -- run scripts/LexGen.py to regenerate //**\(\tLINK_LEXER(\*);\n\) @@ -99,6 +99,7 @@ index ed47aa8..e58f1ab 100644 - LINK_LEXER(lmDMAP); - LINK_LEXER(lmDMIS); - LINK_LEXER(lmECL); +- LINK_LEXER(lmEDIFACT); - LINK_LEXER(lmEiffel); - LINK_LEXER(lmEiffelkw); LINK_LEXER(lmErlang); diff --git a/scintilla/src/CaseConvert.cxx b/scintilla/src/CaseConvert.cxx index 63a27222b..4fb755903 100644 --- a/scintilla/src/CaseConvert.cxx +++ b/scintilla/src/CaseConvert.cxx @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -630,6 +631,14 @@ size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixe return pCaseConv->CaseConvertString(converted, sizeConverted, mixed, lenMixed); } +std::string CaseConvertString(const std::string &s, enum CaseConversion conversion) { + std::string retMapped(s.length() * maxExpansionCaseConversion, 0); + size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(), + conversion); + retMapped.resize(lenMapped); + return retMapped; +} + #ifdef SCI_NAMESPACE } #endif diff --git a/scintilla/src/CaseConvert.h b/scintilla/src/CaseConvert.h index 60de22799..7a0100300 100644 --- a/scintilla/src/CaseConvert.h +++ b/scintilla/src/CaseConvert.h @@ -40,6 +40,9 @@ const int maxExpansionCaseConversion=3; // If there is not enough space then 0 is returned. size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed, enum CaseConversion conversion); +// Converts a mixed case string using a particular conversion. +std::string CaseConvertString(const std::string &s, enum CaseConversion conversion); + #ifdef SCI_NAMESPACE } #endif diff --git a/scintilla/src/CellBuffer.h b/scintilla/src/CellBuffer.h index 1c53d14e6..c1e973cff 100644 --- a/scintilla/src/CellBuffer.h +++ b/scintilla/src/CellBuffer.h @@ -47,22 +47,6 @@ public: int LineStart(int line) const { return starts.PositionFromPartition(line); } - - int MarkValue(int line); - int AddMark(int line, int marker); - void MergeMarkers(int pos); - void DeleteMark(int line, int markerNum, bool all); - void DeleteMarkFromHandle(int markerHandle); - int LineFromHandle(int markerHandle); - - void ClearLevels(); - int SetLevel(int line, int level); - int GetLevel(int line); - - int SetLineState(int line, int state); - int GetLineState(int line); - int GetMaxLineState(); - }; enum actionType { insertAction, removeAction, startAction, containerAction }; diff --git a/scintilla/src/ContractionState.cxx b/scintilla/src/ContractionState.cxx index 80f79de29..41627c173 100644 --- a/scintilla/src/ContractionState.cxx +++ b/scintilla/src/ContractionState.cxx @@ -6,6 +6,7 @@ // The License.txt file describes the conditions under which this software may be distributed. #include +#include #include #include @@ -16,13 +17,14 @@ #include "SplitVector.h" #include "Partitioning.h" #include "RunStyles.h" +#include "SparseVector.h" #include "ContractionState.h" #ifdef SCI_NAMESPACE using namespace Scintilla; #endif -ContractionState::ContractionState() : visible(0), expanded(0), heights(0), displayLines(0), linesInDocument(1) { +ContractionState::ContractionState() : visible(0), expanded(0), heights(0), foldDisplayTexts(0), displayLines(0), linesInDocument(1) { //InsertLine(0); } @@ -35,6 +37,7 @@ void ContractionState::EnsureData() { visible = new RunStyles(); expanded = new RunStyles(); heights = new RunStyles(); + foldDisplayTexts = new SparseVector(); displayLines = new Partitioning(4); InsertLines(0, linesInDocument); } @@ -47,6 +50,8 @@ void ContractionState::Clear() { expanded = 0; delete heights; heights = 0; + delete foldDisplayTexts; + foldDisplayTexts = 0; delete displayLines; displayLines = 0; linesInDocument = 1; @@ -108,6 +113,8 @@ void ContractionState::InsertLine(int lineDoc) { expanded->SetValueAt(lineDoc, 1); heights->InsertSpace(lineDoc, 1); heights->SetValueAt(lineDoc, 1); + foldDisplayTexts->InsertSpace(lineDoc, 1); + foldDisplayTexts->SetValueAt(lineDoc, NULL); int lineDisplay = DisplayFromDoc(lineDoc); displayLines->InsertPartition(lineDoc, lineDisplay); displayLines->InsertText(lineDoc, 1); @@ -132,6 +139,7 @@ void ContractionState::DeleteLine(int lineDoc) { visible->DeleteRange(lineDoc, 1); expanded->DeleteRange(lineDoc, 1); heights->DeleteRange(lineDoc, 1); + foldDisplayTexts->DeletePosition(lineDoc); } } @@ -184,6 +192,24 @@ bool ContractionState::HiddenLines() const { } } +const char *ContractionState::GetFoldDisplayText(int lineDoc) const { + Check(); + return foldDisplayTexts->ValueAt(lineDoc); +} + +bool ContractionState::SetFoldDisplayText(int lineDoc, const char *text) { + EnsureData(); + const char *foldText = foldDisplayTexts->ValueAt(lineDoc); + if (!foldText || 0 != strcmp(text, foldText)) { + foldDisplayTexts->SetValueAt(lineDoc, text); + Check(); + return true; + } else { + Check(); + return false; + } +} + bool ContractionState::GetExpanded(int lineDoc) const { if (OneToOne()) { return true; @@ -209,6 +235,10 @@ bool ContractionState::SetExpanded(int lineDoc, bool isExpanded) { } } +bool ContractionState::GetFoldDisplayTextShown(int lineDoc) const { + return !GetExpanded(lineDoc) && GetFoldDisplayText(lineDoc); +} + int ContractionState::ContractedNext(int lineDocStart) const { if (OneToOne()) { return -1; diff --git a/scintilla/src/ContractionState.h b/scintilla/src/ContractionState.h index 96cbf0763..622696939 100644 --- a/scintilla/src/ContractionState.h +++ b/scintilla/src/ContractionState.h @@ -12,6 +12,9 @@ namespace Scintilla { #endif +template +class SparseVector; + /** */ class ContractionState { @@ -19,6 +22,7 @@ class ContractionState { RunStyles *visible; RunStyles *expanded; RunStyles *heights; + SparseVector *foldDisplayTexts; Partitioning *displayLines; int linesInDocument; @@ -51,8 +55,12 @@ public: bool SetVisible(int lineDocStart, int lineDocEnd, bool isVisible); bool HiddenLines() const; + const char *GetFoldDisplayText(int lineDoc) const; + bool SetFoldDisplayText(int lineDoc, const char *text); + bool GetExpanded(int lineDoc) const; bool SetExpanded(int lineDoc, bool isExpanded); + bool GetFoldDisplayTextShown(int lineDoc) const; int ContractedNext(int lineDocStart) const; int GetHeight(int lineDoc) const; diff --git a/scintilla/src/Document.cxx b/scintilla/src/Document.cxx index 58f663376..c105bdda3 100644 --- a/scintilla/src/Document.cxx +++ b/scintilla/src/Document.cxx @@ -16,8 +16,16 @@ #include #include +#define NOEXCEPT + #ifndef NO_CXX11_REGEX #include +#if defined(__GLIBCXX__) +// If using the GNU implementation of then have 'noexcept' so can use +// when defining regex iterators to keep Clang analyze happy. +#undef NOEXCEPT +#define NOEXCEPT noexcept +#endif #endif #include "Platform.h" @@ -2570,7 +2578,7 @@ public: Position position; ByteIterator(const Document *doc_ = 0, Position position_ = 0) : doc(doc_), position(position_) { } - ByteIterator(const ByteIterator &other) { + ByteIterator(const ByteIterator &other) NOEXCEPT { doc = other.doc; position = other.position; } @@ -2745,7 +2753,7 @@ class UTF8Iterator : public std::iterator(rcTab.bottom - rcTab.top) / 2; - int xhead = static_cast(rcTab.right) - 1 - ydiff; - if (xhead <= rcTab.left) { - ydiff -= static_cast(rcTab.left) - xhead - 1; - xhead = static_cast(rcTab.left) - 1; - } +static void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid, const ViewStyle &vsDraw) { if ((rcTab.left + 2) < (rcTab.right - 1)) surface->MoveTo(static_cast(rcTab.left) + 2, ymid); else surface->MoveTo(static_cast(rcTab.right) - 1, ymid); surface->LineTo(static_cast(rcTab.right) - 1, ymid); - surface->LineTo(xhead, ymid - ydiff); - surface->MoveTo(static_cast(rcTab.right) - 1, ymid); - surface->LineTo(xhead, ymid + ydiff); + + // Draw the arrow head if needed + if (vsDraw.tabDrawMode == tdLongArrow) { + int ydiff = static_cast(rcTab.bottom - rcTab.top) / 2; + int xhead = static_cast(rcTab.right) - 1 - ydiff; + if (xhead <= rcTab.left) { + ydiff -= static_cast(rcTab.left) - xhead - 1; + xhead = static_cast(rcTab.left) - 1; + } + surface->LineTo(xhead, ymid - ydiff); + surface->MoveTo(static_cast(rcTab.right) - 1, ymid); + surface->LineTo(xhead, ymid + ydiff); + } } void EditView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) { @@ -596,19 +600,24 @@ void EditView::LayoutLine(const EditModel &model, int line, Surface *surface, co } } -Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs) { +Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, + const ViewStyle &vs, PointEnd pe) { Point pt; if (pos.Position() == INVALID_POSITION) return pt; - const int line = model.pdoc->LineFromPosition(pos.Position()); - const int lineVisible = model.cs.DisplayFromDoc(line); - //Platform::DebugPrintf("line=%d\n", line); - AutoLineLayout ll(llc, RetrieveLineLayout(line, model)); + int lineDoc = model.pdoc->LineFromPosition(pos.Position()); + int posLineStart = model.pdoc->LineStart(lineDoc); + if ((pe & peLineEnd) && (lineDoc > 0) && (pos.Position() == posLineStart)) { + // Want point at end of first line + lineDoc--; + posLineStart = model.pdoc->LineStart(lineDoc); + } + const int lineVisible = model.cs.DisplayFromDoc(lineDoc); + AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model)); if (surface && ll) { - const int posLineStart = model.pdoc->LineStart(line); - LayoutLine(model, line, surface, vs, ll, model.wrapWidth); + LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth); const int posInLine = pos.Position() - posLineStart; - pt = ll->PointFromPosition(posInLine, vs.lineHeight); + pt = ll->PointFromPosition(posInLine, vs.lineHeight, pe); pt.y += (lineVisible - topLine) * vs.lineHeight; pt.x += vs.textStart - model.xOffset; } @@ -616,6 +625,31 @@ Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, S return pt; } +Range EditView::RangeDisplayLine(Surface *surface, const EditModel &model, int lineVisible, const ViewStyle &vs) { + Range rangeSubLine = Range(0,0); + if (lineVisible < 0) { + return rangeSubLine; + } + const int lineDoc = model.cs.DocFromDisplay(lineVisible); + const int positionLineStart = model.pdoc->LineStart(lineDoc); + AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model)); + if (surface && ll) { + LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth); + const int lineStartSet = model.cs.DisplayFromDoc(lineDoc); + const int subLine = lineVisible - lineStartSet; + if (subLine < ll->lines) { + rangeSubLine = ll->SubLineRange(subLine); + if (subLine == ll->lines-1) { + rangeSubLine.end = model.pdoc->LineStart(lineDoc + 1) - + positionLineStart; + } + } + } + rangeSubLine.start += positionLineStart; + rangeSubLine.end += positionLineStart; + return rangeSubLine; +} + SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) { pt.x = pt.x - vs.textStart; int visibleLine = static_cast(floor(pt.y / vs.lineHeight)); @@ -845,7 +879,7 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle int alpha = SC_ALPHA_NOALPHA; if (!hideSelection) { int posAfterLineEnd = model.pdoc->LineStart(line + 1); - eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0; + eolInSelection = (lastSubLine == true) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0; alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha; } @@ -914,25 +948,15 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle } } - // Fill the remainder of the line rcSegment.left = rcSegment.right; if (rcSegment.left < rcLine.left) rcSegment.left = rcLine.left; rcSegment.right = rcLine.right; - if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) { - surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection)); - } else { - if (background.isSet) { - surface->FillRectangle(rcSegment, background); - } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) { - surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back); - } else { - surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back); - } - if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) { - SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha); - } + bool fillRemainder = !lastSubLine || model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_HIDDEN || !model.cs.GetFoldDisplayTextShown(line); + if (fillRemainder) { + // Fill the remainder of the line + FillLineRemainder(surface, model, vsDraw, ll, line, rcSegment, subLine); } bool drawWrapMarkEnd = false; @@ -963,14 +987,23 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle } static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw, - const LineLayout *ll, int xStart, PRectangle rcLine, int subLine, Indicator::DrawState drawState, int value) { + const LineLayout *ll, int xStart, PRectangle rcLine, int secondCharacter, int subLine, Indicator::DrawState drawState, int value) { const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)]; PRectangle rcIndic( ll->positions[startPos] + xStart - subLineStart, rcLine.top + vsDraw.maxAscent, ll->positions[endPos] + xStart - subLineStart, rcLine.top + vsDraw.maxAscent + 3); - vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine, drawState, value); + PRectangle rcFirstCharacter = rcIndic; + // Allow full descent space for character indicators + rcFirstCharacter.bottom = rcLine.top + vsDraw.maxAscent + vsDraw.maxDescent; + if (secondCharacter >= 0) { + rcFirstCharacter.right = ll->positions[secondCharacter] + xStart - subLineStart; + } else { + // Indicator continued from earlier line so make an empty box and don't draw + rcFirstCharacter.right = rcFirstCharacter.left; + } + vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine, rcFirstCharacter, drawState, value); } static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, @@ -993,8 +1026,9 @@ static void DrawIndicators(Surface *surface, const EditModel &model, const ViewS rangeRun.ContainsCharacter(hoverIndicatorPos); const int value = deco->rs.ValueAt(startPos); Indicator::DrawState drawState = hover ? Indicator::drawHover : Indicator::drawNormal; + const int posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1); DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart, - surface, vsDraw, ll, xStart, rcLine, subLine, drawState, value); + surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, drawState, value); startPos = endPos; if (!deco->rs.ValueAt(startPos)) { startPos = deco->rs.EndRun(startPos); @@ -1012,19 +1046,110 @@ static void DrawIndicators(Surface *surface, const EditModel &model, const ViewS if (rangeLine.ContainsCharacter(model.braces[0])) { int braceOffset = model.braces[0] - posLineStart; if (braceOffset < ll->numCharsInLine) { - DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine, Indicator::drawNormal, 1); + const int secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[0] + 1, 1) - posLineStart; + DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1); } } if (rangeLine.ContainsCharacter(model.braces[1])) { int braceOffset = model.braces[1] - posLineStart; if (braceOffset < ll->numCharsInLine) { - DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, subLine, Indicator::drawNormal, 1); + const int secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[1] + 1, 1) - posLineStart; + DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1); } } } } } +void EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, int xStart, PRectangle rcLine, int subLine, XYACCUMULATOR subLineStart, DrawPhase phase) { + const bool lastSubLine = subLine == (ll->lines - 1); + if (!lastSubLine) + return; + + if ((model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_HIDDEN) || !model.cs.GetFoldDisplayTextShown(line)) + return; + + PRectangle rcSegment = rcLine; + const char *foldDisplayText = model.cs.GetFoldDisplayText(line); + const int lengthFoldDisplayText = static_cast(strlen(foldDisplayText)); + FontAlias fontText = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].font; + const int widthFoldDisplayText = static_cast(surface->WidthText(fontText, foldDisplayText, lengthFoldDisplayText)); + + int eolInSelection = 0; + int alpha = SC_ALPHA_NOALPHA; + if (!hideSelection) { + int posAfterLineEnd = model.pdoc->LineStart(line + 1); + eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0; + alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha; + } + + const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; + XYPOSITION virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth; + rcSegment.left = xStart + static_cast(ll->positions[ll->numCharsInLine] - subLineStart) + spaceWidth + virtualSpace; + rcSegment.right = rcSegment.left + static_cast(widthFoldDisplayText); + + ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret); + FontAlias textFont = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].font; + ColourDesired textFore = vsDraw.styles[STYLE_FOLDDISPLAYTEXT].fore; + if (eolInSelection && (vsDraw.selColours.fore.isSet)) { + textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground; + } + ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, + false, STYLE_FOLDDISPLAYTEXT, -1); + + if (model.trackLineWidth) { + if (rcSegment.right + 1> lineWidthMaxSeen) { + // Fold display text border drawn on rcSegment.right with width 1 is the last visble object of the line + lineWidthMaxSeen = static_cast(rcSegment.right + 1); + } + } + + if ((phasesDraw != phasesOne) && (phase & drawBack)) { + surface->FillRectangle(rcSegment, textBack); + + // Fill Remainder of the line + PRectangle rcRemainder = rcSegment; + rcRemainder.left = rcRemainder.right + 1; + if (rcRemainder.left < rcLine.left) + rcRemainder.left = rcLine.left; + rcRemainder.right = rcLine.right; + FillLineRemainder(surface, model, vsDraw, ll, line, rcRemainder, subLine); + } + + if (phase & drawText) { + if (phasesDraw != phasesOne) { + surface->DrawTextTransparent(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, foldDisplayText, + lengthFoldDisplayText, textFore); + } else { + surface->DrawTextNoClip(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, foldDisplayText, + lengthFoldDisplayText, textFore, textBack); + } + } + + if (phase & drawIndicatorsFore) { + if (model.foldDisplayTextStyle == SC_FOLDDISPLAYTEXT_BOXED) { + surface->PenColour(textFore); + surface->MoveTo(static_cast(rcSegment.left), static_cast(rcSegment.top)); + surface->LineTo(static_cast(rcSegment.left), static_cast(rcSegment.bottom)); + surface->MoveTo(static_cast(rcSegment.right), static_cast(rcSegment.top)); + surface->LineTo(static_cast(rcSegment.right), static_cast(rcSegment.bottom)); + surface->MoveTo(static_cast(rcSegment.left), static_cast(rcSegment.top)); + surface->LineTo(static_cast(rcSegment.right), static_cast(rcSegment.top)); + surface->MoveTo(static_cast(rcSegment.left), static_cast(rcSegment.bottom - 1)); + surface->LineTo(static_cast(rcSegment.right), static_cast(rcSegment.bottom - 1)); + } + } + + if (phase & drawSelectionTranslucent) { + if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && alpha != SC_ALPHA_NOALPHA) { + SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha); + } + } +} + static bool AnnotationBoxedOrIndented(int annotationVisible) { return annotationVisible == ANNOTATION_BOXED || annotationVisible == ANNOTATION_INDENTED; } @@ -1537,7 +1662,7 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight, rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); if (customDrawTabArrow == NULL) - DrawTabArrow(surface, rcTab, static_cast(rcSegment.top + vsDraw.lineHeight / 2)); + DrawTabArrow(surface, rcTab, static_cast(rcSegment.top + vsDraw.lineHeight / 2), vsDraw); else customDrawTabArrow(surface, rcTab, static_cast(rcSegment.top + vsDraw.lineHeight / 2)); } @@ -1743,6 +1868,8 @@ void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyl xStart, subLine, subLineStart, background); } + DrawFoldDisplayText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, phase); + if (!hideSelection && (phase & drawSelectionTranslucent)) { DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart); } @@ -1878,7 +2005,8 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan ll->SetBracesHighlight(rangeLine, model.braces, static_cast(model.bracesMatchStyle), static_cast(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle); - if (leftTextOverlap && bufferedDraw) { + if (leftTextOverlap && (bufferedDraw || ((phasesDraw < phasesMultiple) && (*it & drawBack)))) { + // Clear the left margin PRectangle rcSpacer = rcLine; rcSpacer.right = rcSpacer.left; rcSpacer.left -= 1; @@ -1955,6 +2083,34 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan } } +void EditView::FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, PRectangle rcArea, int subLine) { + int eolInSelection = 0; + int alpha = SC_ALPHA_NOALPHA; + if (!hideSelection) { + int posAfterLineEnd = model.pdoc->LineStart(line + 1); + eolInSelection = (subLine == (ll->lines - 1)) ? model.sel.InSelectionForEOL(posAfterLineEnd) : 0; + alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha; + } + + ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret); + + if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) { + surface->FillRectangle(rcArea, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection)); + } else { + if (background.isSet) { + surface->FillRectangle(rcArea, background); + } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) { + surface->FillRectangle(rcArea, vsDraw.styles[ll->styles[ll->numCharsInLine]].back); + } else { + surface->FillRectangle(rcArea, vsDraw.styles[STYLE_DEFAULT].back); + } + if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) { + SimpleAlphaRectangle(surface, rcArea, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha); + } + } +} + // Space (3 space characters) between line numbers and text when printing. #define lineNumberPrintSpace " " diff --git a/scintilla/src/EditView.h b/scintilla/src/EditView.h index 79a88657a..83dd8bb1f 100644 --- a/scintilla/src/EditView.h +++ b/scintilla/src/EditView.h @@ -111,7 +111,9 @@ public: void LayoutLine(const EditModel &model, int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width = LineLayout::wrapWidthInfinite); - Point LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs); + Point LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, + const ViewStyle &vs, PointEnd pe); + Range RangeDisplayLine(Surface *surface, const EditModel &model, int lineVisible, const ViewStyle &vs); SelectionPosition SPositionFromLocation(Surface *surface, const EditModel &model, Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs); SelectionPosition SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs); @@ -122,6 +124,8 @@ public: void DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine, int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart, ColourOptional background); + void DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, int xStart, PRectangle rcLine, int subLine, XYACCUMULATOR subLineStart, DrawPhase phase); void DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase); void DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, int line, @@ -138,6 +142,8 @@ public: int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase); void PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea, PRectangle rcClient, const ViewStyle &vsDraw); + void FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, PRectangle rcArea, int subLine); long FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure, const EditModel &model, const ViewStyle &vs); }; diff --git a/scintilla/src/Editor.cxx b/scintilla/src/Editor.cxx index 9fcd01fbb..9c4e39490 100644 --- a/scintilla/src/Editor.cxx +++ b/scintilla/src/Editor.cxx @@ -363,14 +363,14 @@ SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const } } -Point Editor::LocationFromPosition(SelectionPosition pos) { +Point Editor::LocationFromPosition(SelectionPosition pos, PointEnd pe) { RefreshStyleData(); AutoSurface surface(this); - return view.LocationFromPosition(surface, *this, pos, topLine, vs); + return view.LocationFromPosition(surface, *this, pos, topLine, vs, pe); } -Point Editor::LocationFromPosition(int pos) { - return LocationFromPosition(SelectionPosition(pos)); +Point Editor::LocationFromPosition(int pos, PointEnd pe) { + return LocationFromPosition(SelectionPosition(pos), pe); } int Editor::XFromPosition(int pos) { @@ -2424,13 +2424,7 @@ void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctr } bool Editor::NotifyMarginClick(Point pt, int modifiers) { - int marginClicked = -1; - int x = vs.textStart - vs.fixedColumnWidth; - for (size_t margin = 0; margin < vs.ms.size(); margin++) { - if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width)) - marginClicked = static_cast(margin); - x += vs.ms[margin].width; - } + const int marginClicked = vs.MarginFromLocation(pt); if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) { int position = pdoc->LineStart(LineFromLocation(pt)); if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) { @@ -2471,6 +2465,22 @@ bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) { return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt)); } +bool Editor::NotifyMarginRightClick(Point pt, int modifiers) { + int marginRightClicked = vs.MarginFromLocation(pt); + if ((marginRightClicked >= 0) && vs.ms[marginRightClicked].sensitive) { + int position = pdoc->LineStart(LineFromLocation(pt)); + SCNotification scn = {}; + scn.nmhdr.code = SCN_MARGINRIGHTCLICK; + scn.modifiers = modifiers; + scn.position = position; + scn.margin = marginRightClicked; + NotifyParent(scn); + return true; + } else { + return false; + } +} + void Editor::NotifyNeedShown(int pos, int len) { SCNotification scn = {}; scn.nmhdr.code = SCN_NEEDSHOWN; @@ -3156,6 +3166,12 @@ void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) { } while (!cs.GetVisible(lineDoc)); } +Range Editor::RangeDisplayLine(int lineVisible) { + RefreshStyleData(); + AutoSurface surface(this); + return view.RangeDisplayLine(surface, *this, lineVisible, vs); +} + int Editor::StartEndDisplayLine(int pos, bool start) { RefreshStyleData(); AutoSurface surface(this); @@ -3932,7 +3948,7 @@ CaseFolder *Editor::CaseFolderForEncoding() { long Editor::FindText( uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD, ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX. - sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range. + sptr_t lParam) { ///< @c Sci_TextToFind structure: The text to search for in the given range. Sci_TextToFind *ft = reinterpret_cast(lParam); int lengthFound = istrlen(ft->lpstrText); @@ -4498,6 +4514,10 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifie } } else { // Single click if (inSelMargin) { + if (sel.IsRectangular() || (sel.Count() > 1)) { + InvalidateWholeSelection(); + sel.Clear(); + } sel.selType = Selection::selStream; if (!shift) { // Single click in margin: select whole line or only subline if word wrap is enabled @@ -4574,6 +4594,11 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifie ShowCaretAtCurrentPosition(); } +void Editor::RightButtonDownWithModifiers(Point pt, unsigned int, int modifiers) { + if (NotifyMarginRightClick(pt, modifiers)) + return; +} + void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) { return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt)); } @@ -5569,7 +5594,7 @@ void Editor::AddStyledText(char *buffer, int appendLength) { SetEmptySelection(sel.MainCaret() + lengthInserted); } -bool Editor::ValidMargin(uptr_t wParam) { +bool Editor::ValidMargin(uptr_t wParam) const { return wParam < vs.ms.size(); } @@ -6284,6 +6309,14 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { Redraw(); break; + case SCI_GETTABDRAWMODE: + return vs.tabDrawMode; + + case SCI_SETTABDRAWMODE: + vs.tabDrawMode = static_cast(wParam); + Redraw(); + break; + case SCI_GETWHITESPACESIZE: return vs.whitespaceSize; @@ -6704,15 +6737,6 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_GETIMEINTERACTION: return imeInteraction; -#ifdef INCLUDE_DEPRECATED_FEATURES - case SCI_SETUSEPALETTE: - InvalidateStyleRedraw(); - break; - - case SCI_GETUSEPALETTE: - return 0; -#endif - // Marker definition and setting case SCI_MARKERDEFINE: if (wParam <= MARKER_MAX) { @@ -7056,6 +7080,16 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { Redraw(); break; + case SCI_TOGGLEFOLDSHOWTEXT: + cs.SetFoldDisplayText(static_cast(wParam), CharPtrFromSPtr(lParam)); + FoldLine(static_cast(wParam), SC_FOLDACTION_TOGGLE); + break; + + case SCI_FOLDDISPLAYTEXTSETSTYLE: + foldDisplayTextStyle = static_cast(wParam); + Redraw(); + break; + case SCI_TOGGLEFOLD: FoldLine(static_cast(wParam), SC_FOLDACTION_TOGGLE); break; diff --git a/scintilla/src/Editor.h b/scintilla/src/Editor.h index 5fdcbffb9..052507381 100644 --- a/scintilla/src/Editor.h +++ b/scintilla/src/Editor.h @@ -285,8 +285,8 @@ protected: // ScintillaBase subclass needs access to much of Editor int LinesToScroll() const; int MaxScrollPos() const; SelectionPosition ClampPositionIntoDocument(SelectionPosition sp) const; - Point LocationFromPosition(SelectionPosition pos); - Point LocationFromPosition(int pos); + Point LocationFromPosition(SelectionPosition pos, PointEnd pe=peDefault); + Point LocationFromPosition(int pos, PointEnd pe=peDefault); int XFromPosition(int pos); int XFromPosition(SelectionPosition sp); SelectionPosition SPositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false, bool virtualSpace=true); @@ -439,6 +439,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt); bool NotifyMarginClick(Point pt, int modifiers); bool NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt); + bool NotifyMarginRightClick(Point pt, int modifiers); void NotifyNeedShown(int pos, int len); void NotifyDwelling(Point pt, bool state); void NotifyZoom(); @@ -465,6 +466,7 @@ protected: // ScintillaBase subclass needs access to much of Editor SelectionPosition PositionUpOrDown(SelectionPosition spStart, int direction, int lastX); void CursorUpOrDown(int direction, Selection::selTypes selt); void ParaUpOrDown(int direction, Selection::selTypes selt); + Range RangeDisplayLine(int lineVisible); int StartEndDisplayLine(int pos, bool start); int VCHomeDisplayPosition(int position); int VCHomeWrapPosition(int position); @@ -507,6 +509,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void DwellEnd(bool mouseMoved); void MouseLeave(); virtual void ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers); + virtual void RightButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers); virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt); void ButtonMoveWithModifiers(Point pt, int modifiers); void ButtonMove(Point pt); @@ -571,7 +574,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void AddStyledText(char *buffer, int appendLength); virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) = 0; - bool ValidMargin(uptr_t wParam); + bool ValidMargin(uptr_t wParam) const; void StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam); sptr_t StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam); diff --git a/scintilla/src/Indicator.cxx b/scintilla/src/Indicator.cxx index 4bc25a815..c23ae4e17 100644 --- a/scintilla/src/Indicator.cxx +++ b/scintilla/src/Indicator.cxx @@ -24,7 +24,7 @@ static PRectangle PixelGridAlign(const PRectangle &rc) { return PRectangle::FromInts(int(rc.left + 0.5), int(rc.top), int(rc.right + 0.5), int(rc.bottom)); } -void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, DrawState drawState, int value) const { +void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, DrawState drawState, int value) const { StyleAndColour sacDraw = sacNormal; if (Flags() & SC_INDICFLAG_VALUEFORE) { sacDraw.fore = value & SC_INDICVALUEMASK; @@ -170,6 +170,19 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r } else if (sacDraw.style == INDIC_COMPOSITIONTHIN) { PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom-1); surface->FillRectangle(rcComposition, sacDraw.fore); + } else if (sacDraw.style == INDIC_POINT || sacDraw.style == INDIC_POINTCHARACTER) { + if (rcCharacter.Width() >= 0.1) { + const int pixelHeight = static_cast(rc.Height() - 1.0f); // 1 pixel onto next line if multiphase + const XYPOSITION x = (sacDraw.style == INDIC_POINT) ? (rcCharacter.left) : ((rcCharacter.right + rcCharacter.left) / 2); + const int ix = static_cast(x + 0.5f); + const int iy = static_cast(rc.top + 1.0f); + Point pts[] = { + Point::FromInts(ix - pixelHeight, iy + pixelHeight), // Left + Point::FromInts(ix + pixelHeight, iy + pixelHeight), // Right + Point::FromInts(ix, iy) // Top + }; + surface->Polygon(pts, 3, sacDraw.fore, sacDraw.fore); + } } else { // Either INDIC_PLAIN or unknown surface->MoveTo(static_cast(rc.left), ymid); surface->LineTo(static_cast(rc.right), ymid); diff --git a/scintilla/src/Indicator.h b/scintilla/src/Indicator.h index c22ec71c6..9b887df9d 100644 --- a/scintilla/src/Indicator.h +++ b/scintilla/src/Indicator.h @@ -40,7 +40,7 @@ public: Indicator(int style_, ColourDesired fore_=ColourDesired(0,0,0), bool under_=false, int fillAlpha_=30, int outlineAlpha_=50) : sacNormal(style_, fore_), sacHover(style_, fore_), under(under_), fillAlpha(fillAlpha_), outlineAlpha(outlineAlpha_), attributes(0) { } - void Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, DrawState drawState, int value) const; + void Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, DrawState drawState, int value) const; bool IsDynamic() const { return !(sacNormal == sacHover); } diff --git a/scintilla/src/PositionCache.cxx b/scintilla/src/PositionCache.cxx index 997a4bfae..45731601a 100644 --- a/scintilla/src/PositionCache.cxx +++ b/scintilla/src/PositionCache.cxx @@ -217,7 +217,7 @@ int LineLayout::FindPositionFromX(XYPOSITION x, Range range, bool charPosition) return range.end; } -Point LineLayout::PointFromPosition(int posInLine, int lineHeight) const { +Point LineLayout::PointFromPosition(int posInLine, int lineHeight, PointEnd pe) const { Point pt; // In case of very long line put x at arbitrary large position if (posInLine > maxLineLength) { @@ -232,6 +232,12 @@ Point LineLayout::PointFromPosition(int posInLine, int lineHeight) const { pt.x = positions[posInLine] - positions[rangeSubLine.start]; if (rangeSubLine.start != 0) // Wrapped lines may be indented pt.x += wrapIndent; + if (pe & peSubLineEnd) // Return end of first subline not start of next + break; + } else if ((pe & peLineEnd) && (subLine == (lines-1))) { + pt.x = positions[numCharsInLine] - positions[rangeSubLine.start]; + if (rangeSubLine.start != 0) // Wrapped lines may be indented + pt.x += wrapIndent; } } else { break; diff --git a/scintilla/src/PositionCache.h b/scintilla/src/PositionCache.h index edc0a5ddb..5a829b76b 100644 --- a/scintilla/src/PositionCache.h +++ b/scintilla/src/PositionCache.h @@ -16,6 +16,15 @@ static inline bool IsEOLChar(char ch) { return (ch == '\r') || (ch == '\n'); } +// There are two points for some positions and this enumeration +// can choose between the end of the first line or subline +// and the start of the next line or subline. +enum PointEnd { + peDefault = 0x0, + peLineEnd = 0x1, + peSubLineEnd = 0x2 +}; + /** */ class LineLayout { @@ -28,6 +37,7 @@ private: bool inCache; public: enum { wrapWidthInfinite = 0x7ffffff }; + int maxLineLength; int numCharsInLine; int numCharsBeforeEOL; @@ -64,7 +74,7 @@ public: void RestoreBracesHighlight(Range rangeLine, const Position braces[], bool ignoreStyle); int FindBefore(XYPOSITION x, int lower, int upper) const; int FindPositionFromX(XYPOSITION x, Range range, bool charPosition) const; - Point PointFromPosition(int posInLine, int lineHeight) const; + Point PointFromPosition(int posInLine, int lineHeight, PointEnd pe) const; int EndLineStyle() const; }; diff --git a/scintilla/src/ScintillaBase.cxx b/scintilla/src/ScintillaBase.cxx index 92605d9b3..08b9fe829 100644 --- a/scintilla/src/ScintillaBase.cxx +++ b/scintilla/src/ScintillaBase.cxx @@ -64,7 +64,7 @@ using namespace Scintilla; #endif ScintillaBase::ScintillaBase() { - displayPopupMenu = true; + displayPopupMenu = SC_POPUP_ALL; listType = 0; maxListWidth = 0; multiAutoCMode = SC_MULTIAUTOC_ONCE; @@ -478,6 +478,11 @@ void ScintillaBase::CallTipClick() { NotifyParent(scn); } +bool ScintillaBase::ShouldDisplayPopup(Point ptInWindowCoordinates) const { + return (displayPopupMenu == SC_POPUP_ALL || + (displayPopupMenu == SC_POPUP_TEXT && !PointInSelMargin(ptInWindowCoordinates))); +} + void ScintillaBase::ContextMenu(Point pt) { if (displayPopupMenu) { bool writable = !WndProc(SCI_GETREADONLY, 0, 0); @@ -510,6 +515,11 @@ void ScintillaBase::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt)); } +void ScintillaBase::RightButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) { + CancelModes(); + Editor::RightButtonDownWithModifiers(pt, curTime, modifiers); +} + #ifdef SCI_LEXER #ifdef SCI_NAMESPACE @@ -970,7 +980,7 @@ sptr_t ScintillaBase::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lPara break; case SCI_USEPOPUP: - displayPopupMenu = wParam != 0; + displayPopupMenu = static_cast(wParam); break; #ifdef SCI_LEXER diff --git a/scintilla/src/ScintillaBase.h b/scintilla/src/ScintillaBase.h index 4bdf24ef2..e66403367 100644 --- a/scintilla/src/ScintillaBase.h +++ b/scintilla/src/ScintillaBase.h @@ -40,7 +40,7 @@ protected: enum { maxLenInputIME = 200 }; - bool displayPopupMenu; + int displayPopupMenu; Menu popup; AutoComplete ac; @@ -84,10 +84,12 @@ protected: virtual void CreateCallTipWindow(PRectangle rc) = 0; virtual void AddToPopUp(const char *label, int cmd=0, bool enabled=true) = 0; + bool ShouldDisplayPopup(Point ptInWindowCoordinates) const; void ContextMenu(Point pt); virtual void ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers); virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt); + virtual void RightButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers); void NotifyStyleToNeeded(int endStyleNeeded); void NotifyLexerChanged(Document *doc, void *userData); diff --git a/scintilla/src/SparseVector.h b/scintilla/src/SparseVector.h new file mode 100644 index 000000000..f96b36b8b --- /dev/null +++ b/scintilla/src/SparseVector.h @@ -0,0 +1,186 @@ +// Scintilla source code edit control +/** @file SparseVector.h + ** Hold data sparsely associated with elements in a range. + **/ +// Copyright 2016 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SPARSEVECTOR_H +#define SPARSEVECTOR_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +// SparseVector is similar to RunStyles but is more efficient for cases where values occur +// for one position instead of over a range of positions. +template +class SparseVector { +private: + Partitioning *starts; + SplitVector *values; + // Private so SparseVector objects can not be copied + SparseVector(const SparseVector &); + void ClearValue(int partition) { + values->SetValueAt(partition, T()); + } + void CommonSetValueAt(int position, T value) { + // Do the work of setting the value to allow for specialization of SetValueAt. + assert(position < Length()); + const int partition = starts->PartitionFromPosition(position); + const int startPartition = starts->PositionFromPartition(partition); + if (value == T()) { + // Setting the empty value is equivalent to deleting the position + if (position == 0) { + ClearValue(partition); + } else if (position == startPartition) { + // Currently an element at this position, so remove + ClearValue(partition); + starts->RemovePartition(partition); + values->Delete(partition); + } + // Else element remains empty + } else { + if (position == startPartition) { + // Already a value at this position, so replace + ClearValue(partition); + values->SetValueAt(partition, value); + } else { + // Insert a new element + starts->InsertPartition(partition + 1, position); + values->InsertValue(partition + 1, 1, value); + } + } + } +public: + SparseVector() { + starts = new Partitioning(8); + values = new SplitVector(); + values->InsertValue(0, 2, T()); + } + ~SparseVector() { + delete starts; + starts = NULL; + // starts dead here but not used by ClearValue. + for (int part = 0; part < values->Length(); part++) { + ClearValue(part); + } + delete values; + values = NULL; + } + int Length() const { + return starts->PositionFromPartition(starts->Partitions()); + } + int Elements() const { + return starts->Partitions(); + } + int PositionOfElement(int element) const { + return starts->PositionFromPartition(element); + } + T ValueAt(int position) const { + assert(position < Length()); + const int partition = starts->PartitionFromPosition(position); + const int startPartition = starts->PositionFromPartition(partition); + if (startPartition == position) { + return values->ValueAt(partition); + } else { + return T(); + } + } + void SetValueAt(int position, T value) { + CommonSetValueAt(position, value); + } + void InsertSpace(int position, int insertLength) { + assert(position <= Length()); // Only operation that works at end. + const int partition = starts->PartitionFromPosition(position); + const int startPartition = starts->PositionFromPartition(partition); + if (startPartition == position) { + T valueCurrent = values->ValueAt(partition); + // Inserting at start of run so make previous longer + if (partition == 0) { + // Inserting at start of document so ensure 0 + if (valueCurrent != T()) { + ClearValue(0); + starts->InsertPartition(1, 0); + values->InsertValue(1, 1, valueCurrent); + starts->InsertText(0, insertLength); + } else { + starts->InsertText(partition, insertLength); + } + } else { + if (valueCurrent != T()) { + starts->InsertText(partition - 1, insertLength); + } else { + // Insert at end of run so do not extend style + starts->InsertText(partition, insertLength); + } + } + } else { + starts->InsertText(partition, insertLength); + } + } + void DeletePosition(int position) { + assert(position < Length()); + int partition = starts->PartitionFromPosition(position); + const int startPartition = starts->PositionFromPartition(partition); + if (startPartition == position) { + if (partition == 0) { + ClearValue(0); + } else if (partition == starts->Partitions()) { + // This should not be possible + ClearValue(partition); + throw std::runtime_error("SparseVector: deleting end partition."); + } else { + ClearValue(partition); + starts->RemovePartition(partition); + values->Delete(partition); + // Its the previous partition now that gets smaller + partition--; + } + } + starts->InsertText(partition, -1); + } + void Check() const { + if (Length() < 0) { + throw std::runtime_error("SparseVector: Length can not be negative."); + } + if (starts->Partitions() < 1) { + throw std::runtime_error("SparseVector: Must always have 1 or more partitions."); + } + if (starts->Partitions() != values->Length() - 1) { + throw std::runtime_error("SparseVector: Partitions and values different lengths."); + } + // The final element can not be set + if (values->ValueAt(values->Length() - 1) != T()) { + throw std::runtime_error("SparseVector: Unused style at end changed."); + } + } +}; + +// The specialization for const char * makes copies and deletes them as needed. + +template<> +inline void SparseVector::ClearValue(int partition) { + const char *value = values->ValueAt(partition); + delete []value; + values->SetValueAt(partition, NULL); +} + +template<> +inline void SparseVector::SetValueAt(int position, const char *value) { + // Make a copy of the string + if (value) { + const size_t len = strlen(value); + char *valueCopy = new char[len + 1](); + std::copy(value, value + len, valueCopy); + CommonSetValueAt(position, valueCopy); + } else { + CommonSetValueAt(position, NULL); + } +} + +#ifdef SCI_NAMESPACE +} +#endif + +#endif diff --git a/scintilla/src/ViewStyle.cxx b/scintilla/src/ViewStyle.cxx index 58dbf167b..b694a62e3 100644 --- a/scintilla/src/ViewStyle.cxx +++ b/scintilla/src/ViewStyle.cxx @@ -153,6 +153,7 @@ ViewStyle::ViewStyle(const ViewStyle &source) { textStart = source.textStart; zoomLevel = source.zoomLevel; viewWhitespace = source.viewWhitespace; + tabDrawMode = source.tabDrawMode; whitespaceSize = source.whitespaceSize; viewIndentationGuides = source.viewIndentationGuides; viewEOL = source.viewEOL; @@ -293,6 +294,7 @@ void ViewStyle::Init(size_t stylesSize_) { textStart = marginInside ? fixedColumnWidth : leftMarginWidth; zoomLevel = 0; viewWhitespace = wsInvisible; + tabDrawMode = tdLongArrow; whitespaceSize = 1; viewIndentationGuides = ivNone; viewEOL = false; @@ -445,6 +447,17 @@ int ViewStyle::ExternalMarginWidth() const { return marginInside ? 0 : fixedColumnWidth; } +int ViewStyle::MarginFromLocation(Point pt) const { + int margin = -1; + int x = textStart - fixedColumnWidth; + for (size_t i = 0; i < ms.size(); i++) { + if ((pt.x >= x) && (pt.x < x + ms[i].width)) + margin = static_cast(i); + x += ms[i].width; + } + return margin; +} + bool ViewStyle::ValidStyle(size_t styleIndex) const { return styleIndex < styles.size(); } diff --git a/scintilla/src/ViewStyle.h b/scintilla/src/ViewStyle.h index 3b812693e..1a876f85e 100644 --- a/scintilla/src/ViewStyle.h +++ b/scintilla/src/ViewStyle.h @@ -55,6 +55,8 @@ enum IndentView {ivNone, ivReal, ivLookForward, ivLookBoth}; enum WhiteSpaceVisibility {wsInvisible=0, wsVisibleAlways=1, wsVisibleAfterIndent=2, wsVisibleOnlyInIndent=3}; +enum TabDrawMode {tdLongArrow=0, tdStrikeOut=1}; + typedef std::map FontMap; enum WrapMode { eWrapNone, eWrapWord, eWrapChar, eWrapWhitespace }; @@ -133,6 +135,7 @@ public: int textStart; ///< Starting x position of text within the view int zoomLevel; WhiteSpaceVisibility viewWhitespace; + TabDrawMode tabDrawMode; int whitespaceSize; IndentView viewIndentationGuides; bool viewEOL; @@ -184,6 +187,7 @@ public: void SetStyleFontName(int styleIndex, const char *name); bool ProtectionActive() const; int ExternalMarginWidth() const; + int MarginFromLocation(Point pt) const; bool ValidStyle(size_t styleIndex) const; void CalcLargestMarkerHeight(); ColourOptional Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const; diff --git a/scintilla/version.txt b/scintilla/version.txt index 5b0cffbc0..67bf40fe1 100644 --- a/scintilla/version.txt +++ b/scintilla/version.txt @@ -1 +1 @@ -370 +371 -- 2.11.4.GIT