From d77fe4c6b78f1b3d6f7650370d2f46eaec0e6f8d Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Sun, 10 Aug 2014 02:17:32 +0200 Subject: [PATCH] Update Scintilla to 3.5.0 pre-release --- scintilla/Makefile.am | 6 + scintilla/gtk/PlatGTK.cxx | 12 +- scintilla/gtk/ScintillaGTK.cxx | 284 +++- scintilla/include/SciLexer.h | 18 + scintilla/include/Scintilla.h | 15 +- scintilla/include/Scintilla.iface | 48 +- scintilla/lexers/LexHTML.cxx | 12 +- scintilla/lexers/LexMatlab.cxx | 5 +- scintilla/lexers/LexRuby.cxx | 36 +- scintilla/lexers/LexRust.cxx | 105 +- scintilla/lexlib/LexerModule.h | 7 +- scintilla/makefile.win32 | 3 + scintilla/scintilla_changes.patch | 3 +- scintilla/src/CellBuffer.cxx | 40 +- scintilla/src/CellBuffer.h | 12 + scintilla/src/ContractionState.cxx | 18 +- scintilla/src/ContractionState.h | 4 +- scintilla/src/Document.cxx | 59 + scintilla/src/Document.h | 8 +- scintilla/src/EditModel.cxx | 74 + scintilla/src/EditModel.h | 67 + scintilla/src/EditView.cxx | 2072 ++++++++++++++++++++++++++ scintilla/src/EditView.h | 163 +++ scintilla/src/Editor.cxx | 2795 +++++------------------------------- scintilla/src/Editor.h | 130 +- scintilla/src/LineMarker.cxx | 96 +- scintilla/src/MarginView.cxx | 455 ++++++ scintilla/src/MarginView.h | 41 + scintilla/src/PerLine.cxx | 70 + scintilla/src/PerLine.h | 17 + scintilla/src/PositionCache.cxx | 25 +- scintilla/src/PositionCache.h | 7 +- scintilla/src/RESearch.cxx | 17 +- scintilla/src/RESearch.h | 1 - scintilla/src/ScintillaBase.cxx | 5 +- scintilla/src/ScintillaBase.h | 4 +- scintilla/src/Selection.cxx | 11 +- scintilla/src/Selection.h | 1 + scintilla/src/ViewStyle.cxx | 14 + scintilla/src/ViewStyle.h | 4 + scintilla/version.txt | 2 +- 41 files changed, 4007 insertions(+), 2759 deletions(-) create mode 100644 scintilla/src/EditModel.cxx create mode 100644 scintilla/src/EditModel.h create mode 100644 scintilla/src/EditView.cxx create mode 100644 scintilla/src/EditView.h create mode 100644 scintilla/src/MarginView.cxx create mode 100644 scintilla/src/MarginView.h diff --git a/scintilla/Makefile.am b/scintilla/Makefile.am index aa156de21..3537504e4 100644 --- a/scintilla/Makefile.am +++ b/scintilla/Makefile.am @@ -97,6 +97,10 @@ src/Document.cxx \ src/Document.h \ src/Editor.cxx \ src/Editor.h \ +src/EditModel.cxx \ +src/EditModel.h \ +src/EditView.cxx \ +src/EditView.h \ src/ExternalLexer.cxx \ src/ExternalLexer.h \ src/FontQuality.h \ @@ -106,6 +110,8 @@ src/KeyMap.cxx \ src/KeyMap.h \ src/LineMarker.cxx \ src/LineMarker.h \ +src/MarginView.cxx \ +src/MarginView.h \ src/Partitioning.h \ src/PerLine.cxx \ src/PerLine.h \ diff --git a/scintilla/gtk/PlatGTK.cxx b/scintilla/gtk/PlatGTK.cxx index cd57607bc..2637e5d99 100644 --- a/scintilla/gtk/PlatGTK.cxx +++ b/scintilla/gtk/PlatGTK.cxx @@ -527,6 +527,7 @@ void SurfaceImpl::Release() { } bool SurfaceImpl::Initialised() { +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 8, 0) if (inited && context) { if (cairo_status(context) == CAIRO_STATUS_SUCCESS) { // Even when status is success, the target surface may have been @@ -543,6 +544,7 @@ bool SurfaceImpl::Initialised() { } return cairo_status(context) == CAIRO_STATUS_SUCCESS; } +#endif return inited; } @@ -1086,6 +1088,10 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, XYPOSITION } clusterStart = clusterEnd; } + while (i < lenPositions) { + // If something failed, fill in rest of the positions + positions[i++] = clusterStart; + } PLATFORM_ASSERT(i == lenPositions); } } @@ -1799,6 +1805,7 @@ void ListBoxX::Select(int n) { } int ListBoxX::GetSelection() { + int index = -1; GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *selection; @@ -1808,9 +1815,10 @@ int ListBoxX::GetSelection() { int *indices = gtk_tree_path_get_indices(path); // Don't free indices. if (indices) - return indices[0]; + index = indices[0]; + gtk_tree_path_free(path); } - return -1; + return index; } int ListBoxX::Find(const char *prefix) { diff --git a/scintilla/gtk/ScintillaGTK.cxx b/scintilla/gtk/ScintillaGTK.cxx index f74971b1b..9b0622a9b 100644 --- a/scintilla/gtk/ScintillaGTK.cxx +++ b/scintilla/gtk/ScintillaGTK.cxx @@ -57,9 +57,13 @@ #include "UniConversion.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 "UnicodeFromUTF8.h" #ifdef SCI_LEXER #include "ExternalLexer.h" @@ -186,13 +190,23 @@ 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); - virtual void SetTicking(bool on); + 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(); + virtual PRectangle GetClientRectangle() const; virtual void ScrollText(int linesToMove); virtual void SetVerticalScrollPos(); virtual void SetHorizontalScrollPos(); @@ -280,6 +294,9 @@ private: static void Commit(GtkIMContext *context, char *str, ScintillaGTK *sciThis); void PreeditChangedThis(); static void PreeditChanged(GtkIMContext *context, ScintillaGTK *sciThis); + + bool KoreanIME(); + static void StyleSetText(GtkWidget *widget, GtkStyle *previous, void*); static void RealizeText(GtkWidget *widget, void*); static void Destroy(GObject *object); @@ -300,7 +317,7 @@ private: 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(ScintillaGTK *sciThis); + static gboolean TimeOut(TimeThunk *tt); static gboolean IdleCallback(ScintillaGTK *sciThis); static gboolean StyleIdle(ScintillaGTK *sciThis); virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo); @@ -624,22 +641,37 @@ void ScintillaGTK::MainForAll(GtkContainer *container, gboolean include_internal } } +namespace { + +class PreEditString { +public: + gchar *str; + gint cursor_pos; + PangoAttrList *attrs; + + PreEditString(GtkIMContext *im_context) { + gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos); + } + ~PreEditString() { + g_free(str); + pango_attr_list_unref(attrs); + } +}; + +} + gint ScintillaGTK::FocusInThis(GtkWidget *widget) { try { SetFocusState(true); if (im_context != NULL) { - gchar *str = NULL; - gint cursor_pos; - - gtk_im_context_get_preedit_string(im_context, &str, NULL, &cursor_pos); + PreEditString pes(im_context); if (PWidget(wPreedit) != NULL) { - if (strlen(str) > 0) { + if (strlen(pes.str) > 0) { gtk_widget_show(PWidget(wPreedit)); } else { gtk_widget_hide(PWidget(wPreedit)); } } - g_free(str); gtk_im_context_focus_in(im_context); } @@ -829,11 +861,16 @@ void ScintillaGTK::Initialise() { caret.period = 0; } - SetTicking(true); + for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { + timers[tr].reason = tr; + timers[tr].scintilla = this; + } } void ScintillaGTK::Finalise() { - SetTicking(false); + for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { + FineTickerCancel(tr); + } ScintillaBase::Finalise(); } @@ -1024,17 +1061,27 @@ sptr_t ScintillaGTK::DefWndProc(unsigned int, uptr_t, sptr_t) { return 0; } -void ScintillaGTK::SetTicking(bool on) { - if (timer.ticking != on) { - timer.ticking = on; - if (timer.ticking) { - timer.tickerID = reinterpret_cast(g_timeout_add(timer.tickSize, - reinterpret_cast(TimeOut), this)); - } else { - g_source_remove(GPOINTER_TO_UINT(timer.tickerID)); - } +/** +* Report that this Editor subclass has a working implementation of FineTickerStart. +*/ +bool ScintillaGTK::FineTickerAvailable() { + return true; +} + +bool ScintillaGTK::FineTickerRunning(TickReason reason) { + return timers[reason].timer != 0; +} + +void ScintillaGTK::FineTickerStart(TickReason reason, int millis, int /* tolerance */) { + FineTickerCancel(reason); + timers[reason].timer = g_timeout_add(millis, reinterpret_cast(TimeOut), &timers[reason]); +} + +void ScintillaGTK::FineTickerCancel(TickReason reason) { + if (timers[reason].timer) { + g_source_remove(timers[reason].timer); + timers[reason].timer = 0; } - timer.ticksToWait = caret.period; } bool ScintillaGTK::SetIdle(bool on) { @@ -1121,8 +1168,9 @@ void ScintillaGTK::FullPaint() { wText.InvalidateAll(); } -PRectangle ScintillaGTK::GetClientRectangle() { - PRectangle rc = wMain.GetClientPosition(); +PRectangle ScintillaGTK::GetClientRectangle() const { + Window &win = const_cast(wMain); + PRectangle rc = win.GetClientPosition(); if (verticalScrollBarVisible) rc.right -= verticalScrollBarWidth; if (horizontalScrollBarVisible && !Wrapping()) @@ -2220,19 +2268,13 @@ gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) { gboolean ScintillaGTK::DrawPreeditThis(GtkWidget *widget, cairo_t *cr) { try { - gchar *str; - gint cursor_pos; - PangoAttrList *attrs; - - gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos); - PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), str); - pango_layout_set_attributes(layout, attrs); + PreEditString pes(im_context); + PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str); + pango_layout_set_attributes(layout, pes.attrs); cairo_move_to(cr, 0, 0); pango_cairo_show_layout(cr, layout); - g_free(str); - pango_attr_list_unref(attrs); g_object_unref(layout); } catch (...) { errorStatus = SC_STATUS_FAILURE; @@ -2248,20 +2290,14 @@ gboolean ScintillaGTK::DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK gboolean ScintillaGTK::ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose) { try { - gchar *str; - gint cursor_pos; - PangoAttrList *attrs; - - gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos); - PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), str); - pango_layout_set_attributes(layout, attrs); + PreEditString pes(im_context); + PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str); + pango_layout_set_attributes(layout, pes.attrs); cairo_t *context = gdk_cairo_create(reinterpret_cast(WindowFromWidget(widget))); cairo_move_to(context, 0, 0); pango_cairo_show_layout(context, layout); cairo_destroy(context); - g_free(str); - pango_attr_list_unref(attrs); g_object_unref(layout); } catch (...) { errorStatus = SC_STATUS_FAILURE; @@ -2275,9 +2311,43 @@ gboolean ScintillaGTK::ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, Sci #endif +bool ScintillaGTK::KoreanIME() { + // Warn : for KoreanIME use only. + if (pdoc->TentativeActive()) { + return true; + } + + bool koreanIME = false; + PreEditString utfval(im_context); + + // Only need to check if the first preedit char is Korean. + // The rest will be handled in TentativeActive() + // which can handle backspace and CJK commons and so forth. + + if (strlen(utfval.str) == 3) { // One hangul char has 3byte. + int unicode = UnicodeFromUTF8(reinterpret_cast(utfval.str)); + // Korean character ranges which are used for the first preedit chars. + // http://www.programminginkorean.com/programming/hangul-in-unicode/ + bool HangulJamo = (0x1100 <= unicode && unicode <= 0x11FF); + bool HangulCompatibleJamo = (0x3130 <= unicode && unicode <= 0x318F); + bool HangulJamoExtendedA = (0xA960 <= unicode && unicode <= 0xA97F); + bool HangulJamoExtendedB = (0xD7B0 <= unicode && unicode <= 0xD7FF); + bool HangulSyllable = (0xAC00 <= unicode && unicode <= 0xD7A3); + koreanIME = (HangulJamo | HangulCompatibleJamo | HangulSyllable + | HangulJamoExtendedA | HangulJamoExtendedB); + } + return koreanIME; +} + void ScintillaGTK::CommitThis(char *utfVal) { try { //~ fprintf(stderr, "Commit '%s'\n", utfVal); + if (pdoc->TentativeActive()) { + pdoc->TentativeUndo(); + } + + view.imeCaretBlockOverride = false; + if (IsUnicodeMode()) { AddCharUTF(utfVal, strlen(utfVal)); } else { @@ -2285,7 +2355,7 @@ void ScintillaGTK::CommitThis(char *utfVal) { if (*source) { Converter conv(source, "UTF-8", true); if (conv) { - char localeVal[4] = "\0\0\0"; + char localeVal[maxLenInputIME * 2]; char *pin = utfVal; size_t inLeft = strlen(utfVal); char *pout = localeVal; @@ -2293,15 +2363,14 @@ void ScintillaGTK::CommitThis(char *utfVal) { size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft); if (conversions != ((size_t)(-1))) { *pout = '\0'; - for (int i = 0; localeVal[i]; i++) { - AddChar(localeVal[i]); - } + AddCharUTF(localeVal, strlen(localeVal)); } else { fprintf(stderr, "Conversion failed '%s'\n", utfVal); } } } } + ShowCaretAtCurrentPosition(); } catch (...) { errorStatus = SC_STATUS_FAILURE; } @@ -2313,36 +2382,99 @@ void ScintillaGTK::Commit(GtkIMContext *, char *str, ScintillaGTK *sciThis) { void ScintillaGTK::PreeditChangedThis() { try { - gchar *str; - PangoAttrList *attrs; - gint cursor_pos; - gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos); - if (strlen(str) > 0) { - PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), str); - pango_layout_set_attributes(layout, attrs); - - gint w, h; - pango_layout_get_pixel_size(layout, &w, &h); - g_object_unref(layout); - - gint x, y; - gdk_window_get_origin(PWindow(wText), &x, &y); - - Point pt = PointMainCaret(); - if (pt.x < 0) - pt.x = 0; - if (pt.y < 0) - pt.y = 0; - - gtk_window_move(GTK_WINDOW(PWidget(wPreedit)), x + pt.x, y + pt.y); - gtk_window_resize(GTK_WINDOW(PWidget(wPreedit)), w, h); - gtk_widget_show(PWidget(wPreedit)); - gtk_widget_queue_draw_area(PWidget(wPreeditDraw), 0, 0, w, h); - } else { - gtk_widget_hide(PWidget(wPreedit)); + if (KoreanIME()) { + // Copy & paste by johnsonj. + // Great thanks to + // jiniya from http://www.jiniya.net/tt/494 for DBCS input with AddCharUTF(). + // BLUEnLIVE from http://zockr.tistory.com/1118 for UNDO and inOverstrike. + view.imeCaretBlockOverride = false; // If backspace. + + if (pdoc->TentativeActive()) { + pdoc->TentativeUndo(); + } else { + // No tentative undo means start of this composition so + // fill in any virtual spaces. + bool tmpOverstrike = inOverstrike; + inOverstrike = false; // Not allowed to be deleted twice. + AddCharUTF("", 0); + inOverstrike = tmpOverstrike; + } + + PreEditString utfval(im_context); + + if (strlen(utfval.str) > maxLenInputIME * 3) { + return; // Do not allow over 200 chars. + } + + char localeVal[maxLenInputIME * 2]; + char *hanval = (char *)""; + + if (IsUnicodeMode()) { + hanval = utfval.str; + } else { + const char *source = CharacterSetID(); + if (*source) { + Converter conv(source, "UTF-8", true); + if (conv) { + char *pin = utfval.str; + size_t inLeft = strlen(utfval.str); + char *pout = localeVal; + size_t outLeft = sizeof(localeVal); + size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft); + if (conversions != ((size_t)(-1))) { + *pout = '\0'; + hanval = localeVal; + } else { + fprintf(stderr, "Conversion failed '%s'\n", utfval.str); + } + } + } + } + + if (!pdoc->TentativeActive()) { + pdoc->TentativeStart(); + } + + bool tmpRecordingMacro = recordingMacro; + recordingMacro = false; + int hanlen = strlen(hanval); + AddCharUTF(hanval, hanlen); + recordingMacro = tmpRecordingMacro; + + // For block caret which means KoreanIME is in composition. + view.imeCaretBlockOverride = true; + for (size_t r = 0; r < sel.Count(); r++) { + int positionInsert = sel.Range(r).Start().Position(); + sel.Range(r).caret.SetPosition(positionInsert - hanlen); + sel.Range(r).anchor.SetPosition(positionInsert - hanlen); + } + } else { // Original code follows for other IMEs. + PreEditString pes(im_context); + if (strlen(pes.str) > 0) { + PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str); + pango_layout_set_attributes(layout, pes.attrs); + + gint w, h; + pango_layout_get_pixel_size(layout, &w, &h); + g_object_unref(layout); + + gint x, y; + gdk_window_get_origin(PWindow(wText), &x, &y); + + Point pt = PointMainCaret(); + if (pt.x < 0) + pt.x = 0; + if (pt.y < 0) + pt.y = 0; + + gtk_window_move(GTK_WINDOW(PWidget(wPreedit)), x + pt.x, y + pt.y); + gtk_window_resize(GTK_WINDOW(PWidget(wPreedit)), w, h); + gtk_widget_show(PWidget(wPreedit)); + gtk_widget_queue_draw_area(PWidget(wPreeditDraw), 0, 0, w, h); + } else { + gtk_widget_hide(PWidget(wPreedit)); + } } - g_free(str); - pango_attr_list_unref(attrs); } catch (...) { errorStatus = SC_STATUS_FAILURE; } @@ -2711,8 +2843,8 @@ void ScintillaGTK::DragDataGet(GtkWidget *widget, GdkDragContext *context, } } -int ScintillaGTK::TimeOut(ScintillaGTK *sciThis) { - sciThis->Tick(); +int ScintillaGTK::TimeOut(TimeThunk *tt) { + tt->scintilla->TickFor(tt->reason); return 1; } diff --git a/scintilla/include/SciLexer.h b/scintilla/include/SciLexer.h index 54cc8ba7e..b45ed9474 100644 --- a/scintilla/include/SciLexer.h +++ b/scintilla/include/SciLexer.h @@ -127,6 +127,7 @@ #define SCLEX_DMAP 112 #define SCLEX_AS 113 #define SCLEX_DMIS 114 +#define SCLEX_REGISTRY 115 #define SCLEX_AUTOMATIC 1000 #define SCE_P_DEFAULT 0 #define SCE_P_COMMENTLINE 1 @@ -906,6 +907,7 @@ #define SCE_KIX_KEYWORD 7 #define SCE_KIX_FUNCTIONS 8 #define SCE_KIX_OPERATOR 9 +#define SCE_KIX_COMMENTSTREAM 10 #define SCE_KIX_IDENTIFIER 31 #define SCE_GC_DEFAULT 0 #define SCE_GC_COMMENTLINE 1 @@ -1692,6 +1694,9 @@ #define SCE_RUST_LIFETIME 18 #define SCE_RUST_MACRO 19 #define SCE_RUST_LEXERROR 20 +#define SCE_RUST_BYTESTRING 21 +#define SCE_RUST_BYTESTRINGR 22 +#define SCE_RUST_BYTECHARACTER 23 #define SCE_DMAP_DEFAULT 0 #define SCE_DMAP_COMMENT 1 #define SCE_DMAP_NUMBER 2 @@ -1713,6 +1718,19 @@ #define SCE_DMIS_UNSUPPORTED_MAJOR 7 #define SCE_DMIS_UNSUPPORTED_MINOR 8 #define SCE_DMIS_LABEL 9 +#define SCE_REG_DEFAULT 0 +#define SCE_REG_COMMENT 1 +#define SCE_REG_VALUENAME 2 +#define SCE_REG_STRING 3 +#define SCE_REG_HEXDIGIT 4 +#define SCE_REG_VALUETYPE 5 +#define SCE_REG_ADDEDKEY 6 +#define SCE_REG_DELETEDKEY 7 +#define SCE_REG_ESCAPED 8 +#define SCE_REG_KEYPATH_GUID 9 +#define SCE_REG_STRING_GUID 10 +#define SCE_REG_PARAMETER 11 +#define SCE_REG_OPERATOR 12 /* --Autogenerated -- end of section automatically generated from Scintilla.iface */ #endif diff --git a/scintilla/include/Scintilla.h b/scintilla/include/Scintilla.h index 93992e28f..949d42267 100644 --- a/scintilla/include/Scintilla.h +++ b/scintilla/include/Scintilla.h @@ -18,9 +18,9 @@ extern "C" { #if defined(_WIN32) /* Return false on failure: */ int Scintilla_RegisterClasses(void *hInstance); -int Scintilla_ReleaseResources(); +int Scintilla_ReleaseResources(void); #endif -int Scintilla_LinkLexers(); +int Scintilla_LinkLexers(void); #ifdef __cplusplus } @@ -92,6 +92,9 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_SETBUFFEREDDRAW 2035 #define SCI_SETTABWIDTH 2036 #define SCI_GETTABWIDTH 2121 +#define SCI_CLEARTABSTOPS 2675 +#define SCI_ADDTABSTOP 2676 +#define SCI_GETNEXTTABSTOP 2677 #define SC_CP_UTF8 65001 #define SCI_SETCODEPAGE 2037 #define MARKER_MAX 31 @@ -517,6 +520,11 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_APPENDTEXT 2282 #define SCI_GETTWOPHASEDRAW 2283 #define SCI_SETTWOPHASEDRAW 2284 +#define SC_PHASES_ONE 0 +#define SC_PHASES_TWO 1 +#define SC_PHASES_MULTIPLE 2 +#define SCI_GETPHASESDRAW 2673 +#define SCI_SETPHASESDRAW 2674 #define SC_EFF_QUALITY_MASK 0xF #define SC_EFF_QUALITY_DEFAULT 0 #define SC_EFF_QUALITY_NON_ANTIALIASED 1 @@ -952,7 +960,8 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SC_MOD_CONTAINER 0x40000 #define SC_MOD_LEXERSTATE 0x80000 #define SC_MOD_INSERTCHECK 0x100000 -#define SC_MODEVENTMASKALL 0x1FFFFF +#define SC_MOD_CHANGETABSTOPS 0x200000 +#define SC_MODEVENTMASKALL 0x3FFFFF #define SC_UPDATE_CONTENT 0x1 #define SC_UPDATE_SELECTION 0x2 #define SC_UPDATE_V_SCROLL 0x4 diff --git a/scintilla/include/Scintilla.iface b/scintilla/include/Scintilla.iface index 3e5f51b9c..ec047f2d4 100644 --- a/scintilla/include/Scintilla.iface +++ b/scintilla/include/Scintilla.iface @@ -226,6 +226,15 @@ set void SetTabWidth=2036(int tabWidth,) # Retrieve the visible size of a tab. get int GetTabWidth=2121(,) +# Clear explicit tabstops on a line. +fun void ClearTabStops=2675(int line,) + +# Add an explicit tab stop for a line. +fun void AddTabStop=2676(int line, int x) + +# Find the next explicit tab stop position on a line after a position. +fun int GetNextTabStop=2677(int line, int x) + # The SC_CP_UTF8 value can be used to enter Unicode mode. # This is the same value as CP_UTF8 in Windows val SC_CP_UTF8=65001 @@ -1291,13 +1300,27 @@ get bool GetVScrollBar=2281(,) # Append a string to the end of the document without changing the selection. fun void AppendText=2282(int length, string text) -# Is drawing done in two phases with backgrounds drawn before faoregrounds? +# Is drawing done in two phases with backgrounds drawn before foregrounds? get bool GetTwoPhaseDraw=2283(,) # In twoPhaseDraw mode, drawing is performed in two phases, first the background # and then the foreground. This avoids chopping off characters that overlap the next run. set void SetTwoPhaseDraw=2284(bool twoPhase,) +enu FontQuality=SC_PHASES_ +val SC_PHASES_ONE=0 +val SC_PHASES_TWO=1 +val SC_PHASES_MULTIPLE=2 + +# How many phases is drawing done in? +get int GetPhasesDraw=2673(,) + +# In one phase draw, text is drawn in a series of rectangular blocks with no overlap. +# In two phase draw, text is drawn in a series of lines allowing runs to overlap horizontally. +# In multiple phase draw, each element is drawn over the whole drawing area, allowing text +# to overlap from one line to the next. +set void SetPhasesDraw=2674(int phases,) + # Control font anti-aliasing. enu FontQuality=SC_EFF_ @@ -2509,7 +2532,8 @@ val SC_MOD_CHANGEANNOTATION=0x20000 val SC_MOD_CONTAINER=0x40000 val SC_MOD_LEXERSTATE=0x80000 val SC_MOD_INSERTCHECK=0x100000 -val SC_MODEVENTMASKALL=0x1FFFFF +val SC_MOD_CHANGETABSTOPS=0x200000 +val SC_MODEVENTMASKALL=0x3FFFFF enu Update=SC_UPDATE_ val SC_UPDATE_CONTENT=0x1 @@ -2675,6 +2699,7 @@ val SCLEX_RUST=111 val SCLEX_DMAP=112 val SCLEX_AS=113 val SCLEX_DMIS=114 +val SCLEX_REGISTRY=115 # When a lexer specifies its language as SCLEX_AUTOMATIC it receives a # value assigned in sequence from SCLEX_AUTOMATIC+1. @@ -3567,6 +3592,7 @@ val SCE_KIX_MACRO=6 val SCE_KIX_KEYWORD=7 val SCE_KIX_FUNCTIONS=8 val SCE_KIX_OPERATOR=9 +val SCE_KIX_COMMENTSTREAM=10 val SCE_KIX_IDENTIFIER=31 # Lexical states for SCLEX_GUI4CLI lex Gui4Cli=SCLEX_GUI4CLI SCE_GC_ @@ -4443,6 +4469,9 @@ val SCE_RUST_IDENTIFIER=17 val SCE_RUST_LIFETIME=18 val SCE_RUST_MACRO=19 val SCE_RUST_LEXERROR=20 +val SCE_RUST_BYTESTRING=21 +val SCE_RUST_BYTESTRINGR=22 +val SCE_RUST_BYTECHARACTER=23 # Lexical states for SCLEX_DMAP lex DMAP=SCLEX_DMAP SCE_DMAP_ val SCE_DMAP_DEFAULT=0 @@ -4468,6 +4497,21 @@ val SCE_DMIS_MINORWORD=6 val SCE_DMIS_UNSUPPORTED_MAJOR=7 val SCE_DMIS_UNSUPPORTED_MINOR=8 val SCE_DMIS_LABEL=9 +# Lexical states for SCLEX_REGISTRY +lex REG=SCLEX_REGISTRY SCE_REG_ +val SCE_REG_DEFAULT=0 +val SCE_REG_COMMENT=1 +val SCE_REG_VALUENAME=2 +val SCE_REG_STRING=3 +val SCE_REG_HEXDIGIT=4 +val SCE_REG_VALUETYPE=5 +val SCE_REG_ADDEDKEY=6 +val SCE_REG_DELETEDKEY=7 +val SCE_REG_ESCAPED=8 +val SCE_REG_KEYPATH_GUID=9 +val SCE_REG_STRING_GUID=10 +val SCE_REG_PARAMETER=11 +val SCE_REG_OPERATOR=12 # Events diff --git a/scintilla/lexers/LexHTML.cxx b/scintilla/lexers/LexHTML.cxx index d6b0b31bc..02047930c 100644 --- a/scintilla/lexers/LexHTML.cxx +++ b/scintilla/lexers/LexHTML.cxx @@ -822,19 +822,27 @@ static void ColouriseHyperTextDoc(unsigned int startPos, int length, int initSty // handle start of Mako comment line if (isMako && ch == '#' && chNext == '#') { makoComment = 1; + state = SCE_HP_COMMENTLINE; } // handle end of Mako comment line else if (isMako && makoComment && (ch == '\r' || ch == '\n')) { makoComment = 0; - styler.ColourTo(i, SCE_HP_COMMENTLINE); - state = SCE_HP_DEFAULT; + styler.ColourTo(i, StateToPrint); + if (scriptLanguage == eScriptPython) { + state = SCE_HP_DEFAULT; + } else { + state = SCE_H_DEFAULT; + } } // Allow falling through to mako handling code if newline is going to end a block if (((ch == '\r' && chNext != '\n') || (ch == '\n')) && (!isMako || (0 != strcmp(makoBlockType, "%")))) { } + // Ignore everything in mako comment until the line ends + else if (isMako && makoComment) { + } // generic end of script processing else if ((inScriptType == eNonHtmlScript) && (ch == '<') && (chNext == '/')) { diff --git a/scintilla/lexers/LexMatlab.cxx b/scintilla/lexers/LexMatlab.cxx index a8ac03cc7..4dfd512c8 100644 --- a/scintilla/lexers/LexMatlab.cxx +++ b/scintilla/lexers/LexMatlab.cxx @@ -12,6 +12,9 @@ ** - added ... displayed as a comment ** - removed unused IsAWord functions ** - added some comments + ** + ** Changes by John Donoghue 2014/08/01 + ** - fix allowed transpose ' after {} operator **/ // Copyright 1998-2001 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. @@ -218,7 +221,7 @@ static void ColouriseMatlabOctaveDoc( } else if (isalpha(sc.ch)) { sc.SetState(SCE_MATLAB_KEYWORD); } else if (isoperator(static_cast(sc.ch)) || sc.ch == '@' || sc.ch == '\\') { - if (sc.ch == ')' || sc.ch == ']') { + if (sc.ch == ')' || sc.ch == ']' || sc.ch == '}') { transpose = true; } else { transpose = false; diff --git a/scintilla/lexers/LexRuby.cxx b/scintilla/lexers/LexRuby.cxx index f818d3d47..d4c3fad25 100644 --- a/scintilla/lexers/LexRuby.cxx +++ b/scintilla/lexers/LexRuby.cxx @@ -882,6 +882,31 @@ static void ColouriseRbDoc(unsigned int startPos, int length, int initStyle, preferRE = false; } else if (isSafeWordcharOrHigh(chNext)) { state = SCE_RB_SYMBOL; + } else if ((chNext == '@' || chNext == '$') && + isSafeWordcharOrHigh(chNext2)) { + // instance and global variable followed by an identifier + advance_char(i, ch, chNext, chNext2); + state = SCE_RB_SYMBOL; + } else if (((chNext == '@' && chNext2 == '@') || + (chNext == '$' && chNext2 == '-')) && + isSafeWordcharOrHigh(styler.SafeGetCharAt(i+3))) { + // class variables and special global variable "$-IDENTCHAR" + state = SCE_RB_SYMBOL; + // $-IDENTCHAR doesn't continue past the IDENTCHAR + if (chNext == '$') { + styler.ColourTo(i+3, SCE_RB_SYMBOL); + state = SCE_RB_DEFAULT; + } + i += 3; + ch = styler.SafeGetCharAt(i); + chNext = styler.SafeGetCharAt(i+1); + } else if (chNext == '$' && strchr("_~*$?!@/\\;,.=:<>\"&`'+", chNext2)) { + // single-character special global variables + i += 2; + ch = chNext2; + chNext = styler.SafeGetCharAt(i+1); + styler.ColourTo(i, SCE_RB_SYMBOL); + state = SCE_RB_DEFAULT; } else if (strchr("[*!~+-*/%=<>&^|", chNext)) { // Do the operator analysis in-line, looking ahead // Based on the table in pickaxe 2nd ed., page 339 @@ -1260,7 +1285,16 @@ static void ColouriseRbDoc(unsigned int startPos, int length, int initStyle, } else if (state == SCE_RB_CLASS_VAR || state == SCE_RB_INSTANCE_VAR || state == SCE_RB_SYMBOL) { - if (!isSafeWordcharOrHigh(ch)) { + if (state == SCE_RB_SYMBOL && + // FIDs suffices '?' and '!' + (((ch == '!' || ch == '?') && chNext != '=') || + // identifier suffix '=' + (ch == '=' && (chNext != '~' && chNext != '>' && + (chNext != '=' || chNext2 == '>'))))) { + styler.ColourTo(i, state); + state = SCE_RB_DEFAULT; + preferRE = false; + } else if (!isSafeWordcharOrHigh(ch)) { styler.ColourTo(i - 1, state); redo_char(i, ch, chNext, chNext2, state); // pass by ref preferRE = false; diff --git a/scintilla/lexers/LexRust.cxx b/scintilla/lexers/LexRust.cxx index 8a3020532..3b1201c7f 100644 --- a/scintilla/lexers/LexRust.cxx +++ b/scintilla/lexers/LexRust.cxx @@ -230,7 +230,9 @@ static void ScanIdentifier(Accessor& styler, int& pos, WordList *keywords) { } } -static void ScanDigits(Accessor& styler, int& pos, int base) { +/* Scans a sequence of digits, returning true if it found any. */ +static bool ScanDigits(Accessor& styler, int& pos, int base) { + int old_pos = pos; for (;;) { int c = styler.SafeGetCharAt(pos, '\0'); if (IsADigit(c, base) || c == '_') @@ -238,13 +240,17 @@ static void ScanDigits(Accessor& styler, int& pos, int base) { else break; } + return old_pos != pos; } +/* Scans an integer and floating point literals. */ static void ScanNumber(Accessor& styler, int& pos) { int base = 10; int c = styler.SafeGetCharAt(pos, '\0'); int n = styler.SafeGetCharAt(pos + 1, '\0'); bool error = false; + /* Scan the prefix, thus determining the base. + * 10 is default if there's no prefix. */ if (c == '0' && n == 'x') { pos += 2; base = 16; @@ -255,8 +261,11 @@ static void ScanNumber(Accessor& styler, int& pos) { pos += 2; base = 8; } - int old_pos = pos; - ScanDigits(styler, pos, base); + + /* Scan initial digits. The literal is malformed if there are none. */ + error |= !ScanDigits(styler, pos, base); + /* See if there's an integer suffix. We mimic the Rust's lexer + * and munch it even if there was an error above. */ c = styler.SafeGetCharAt(pos, '\0'); if (c == 'u' || c == 'i') { pos++; @@ -271,14 +280,22 @@ static void ScanNumber(Accessor& styler, int& pos) { } else if (c == '6' && n == '4') { pos += 2; } - } else { + /* See if it's a floating point literal. These literals have to be base 10. + */ + } else if (!error) { + /* If there's a period, it's a floating point literal unless it's + * followed by an identifier (meaning this is a method call, e.g. + * `1.foo()`) or another period, in which case it's a range (e.g. 1..2) + */ n = styler.SafeGetCharAt(pos + 1, '\0'); if (c == '.' && !(IsIdentifierStart(n) || n == '.')) { error |= base != 10; pos++; + /* It's ok to have no digits after the period. */ ScanDigits(styler, pos, 10); } + /* Look for the exponentiation. */ c = styler.SafeGetCharAt(pos, '\0'); if (c == 'e' || c == 'E') { error |= base != 10; @@ -286,13 +303,11 @@ static void ScanNumber(Accessor& styler, int& pos) { c = styler.SafeGetCharAt(pos, '\0'); if (c == '-' || c == '+') pos++; - int old_pos = pos; - ScanDigits(styler, pos, 10); - if (old_pos == pos) { - error = true; - } + /* It is invalid to have no digits in the exponent. */ + error |= !ScanDigits(styler, pos, 10); } + /* Scan the floating point suffix. */ c = styler.SafeGetCharAt(pos, '\0'); if (c == 'f') { error |= base != 10; @@ -308,9 +323,7 @@ static void ScanNumber(Accessor& styler, int& pos) { } } } - if (old_pos == pos) { - error = true; - } + if (error) styler.ColourTo(pos - 1, SCE_RUST_LEXERROR); else @@ -351,7 +364,7 @@ static bool IsValidCharacterEscape(int c) { } static bool IsValidStringEscape(int c) { - return IsValidCharacterEscape(c) || c == '\n'; + return IsValidCharacterEscape(c) || c == '\n' || c == '\r'; } static bool ScanNumericEscape(Accessor &styler, int& pos, int num_digits, bool stop_asap) { @@ -373,12 +386,12 @@ static bool ScanNumericEscape(Accessor &styler, int& pos, int num_digits, bool s /* This is overly permissive for character literals in order to accept UTF-8 encoded * character literals. */ -static void ScanCharacterLiteralOrLifetime(Accessor &styler, int& pos) { +static void ScanCharacterLiteralOrLifetime(Accessor &styler, int& pos, bool ascii_only) { pos++; int c = styler.SafeGetCharAt(pos, '\0'); int n = styler.SafeGetCharAt(pos + 1, '\0'); bool done = false; - bool valid_lifetime = IsIdentifierStart(c); + bool valid_lifetime = !ascii_only && IsIdentifierStart(c); bool valid_char = true; bool first = true; while (!done) { @@ -390,10 +403,10 @@ static void ScanCharacterLiteralOrLifetime(Accessor &styler, int& pos) { } else if (n == 'x') { pos += 2; valid_char = ScanNumericEscape(styler, pos, 2, false); - } else if (n == 'u') { + } else if (n == 'u' && !ascii_only) { pos += 2; valid_char = ScanNumericEscape(styler, pos, 4, false); - } else if (n == 'U') { + } else if (n == 'U' && !ascii_only) { pos += 2; valid_char = ScanNumericEscape(styler, pos, 8, false); } else { @@ -412,7 +425,10 @@ static void ScanCharacterLiteralOrLifetime(Accessor &styler, int& pos) { done = true; break; default: - if (!IsIdentifierContinue(c) && !first) { + if (ascii_only && !IsASCII((char)c)) { + done = true; + valid_char = false; + } else if (!IsIdentifierContinue(c) && !first) { done = true; } else { pos++; @@ -433,7 +449,7 @@ static void ScanCharacterLiteralOrLifetime(Accessor &styler, int& pos) { styler.ColourTo(pos - 1, SCE_RUST_LIFETIME); } else if (valid_char) { pos++; - styler.ColourTo(pos - 1, SCE_RUST_CHARACTER); + styler.ColourTo(pos - 1, ascii_only ? SCE_RUST_BYTECHARACTER : SCE_RUST_CHARACTER); } else { styler.ColourTo(pos - 1, SCE_RUST_LEXERROR); } @@ -542,7 +558,7 @@ static void ScanComments(Accessor &styler, int& pos, int max) { ResumeBlockComment(styler, pos, max, UnknownComment, 1); } -static void ResumeString(Accessor &styler, int& pos, int max) { +static void ResumeString(Accessor &styler, int& pos, int max, bool ascii_only) { int c = styler.SafeGetCharAt(pos, '\0'); bool error = false; while (c != '"' && !error) { @@ -559,10 +575,10 @@ static void ResumeString(Accessor &styler, int& pos, int max) { } else if (n == 'x') { pos += 2; error = !ScanNumericEscape(styler, pos, 2, true); - } else if (n == 'u') { + } else if (n == 'u' && !ascii_only) { pos += 2; error = !ScanNumericEscape(styler, pos, 4, true); - } else if (n == 'U') { + } else if (n == 'U' && !ascii_only) { pos += 2; error = !ScanNumericEscape(styler, pos, 8, true); } else { @@ -570,16 +586,19 @@ static void ResumeString(Accessor &styler, int& pos, int max) { error = true; } } else { - pos++; + if (ascii_only && !IsASCII((char)c)) + error = true; + else + pos++; } c = styler.SafeGetCharAt(pos, '\0'); } if (!error) pos++; - styler.ColourTo(pos - 1, SCE_RUST_STRING); + styler.ColourTo(pos - 1, ascii_only ? SCE_RUST_BYTESTRING : SCE_RUST_STRING); } -static void ResumeRawString(Accessor &styler, int& pos, int max, int num_hashes) { +static void ResumeRawString(Accessor &styler, int& pos, int max, int num_hashes, bool ascii_only) { for (;;) { if (pos == styler.LineEnd(styler.GetLine(pos))) styler.SetLineState(styler.GetLine(pos), num_hashes); @@ -594,19 +613,20 @@ static void ResumeRawString(Accessor &styler, int& pos, int max, int num_hashes) } if (trailing_num_hashes == num_hashes) { styler.SetLineState(styler.GetLine(pos), 0); - styler.ColourTo(pos - 1, SCE_RUST_STRINGR); break; } } else if (pos >= max) { - styler.ColourTo(pos - 1, SCE_RUST_STRINGR); break; - } else { + } else { + if (ascii_only && !IsASCII((char)c)) + break; pos++; } } + styler.ColourTo(pos - 1, ascii_only ? SCE_RUST_BYTESTRINGR : SCE_RUST_STRINGR); } -static void ScanRawString(Accessor &styler, int& pos, int max) { +static void ScanRawString(Accessor &styler, int& pos, int max, bool ascii_only) { pos++; int num_hashes = 0; while (styler.SafeGetCharAt(pos, '\0') == '#') { @@ -617,7 +637,7 @@ static void ScanRawString(Accessor &styler, int& pos, int max) { styler.ColourTo(pos - 1, SCE_RUST_LEXERROR); } else { pos++; - ResumeRawString(styler, pos, max, num_hashes); + ResumeRawString(styler, pos, max, num_hashes, ascii_only); } } @@ -635,9 +655,13 @@ void SCI_METHOD LexerRust::Lex(unsigned int startPos, int length, int initStyle, } else if (initStyle == SCE_RUST_COMMENTLINE || initStyle == SCE_RUST_COMMENTLINEDOC) { ResumeLineComment(styler, pos, max, initStyle == SCE_RUST_COMMENTLINEDOC ? DocComment : NotDocComment); } else if (initStyle == SCE_RUST_STRING) { - ResumeString(styler, pos, max); + ResumeString(styler, pos, max, false); + } else if (initStyle == SCE_RUST_BYTESTRING) { + ResumeString(styler, pos, max, true); } else if (initStyle == SCE_RUST_STRINGR) { - ResumeRawString(styler, pos, max, styler.GetLineState(styler.GetLine(pos) - 1)); + ResumeRawString(styler, pos, max, styler.GetLineState(styler.GetLine(pos) - 1), false); + } else if (initStyle == SCE_RUST_BYTESTRINGR) { + ResumeRawString(styler, pos, max, styler.GetLineState(styler.GetLine(pos) - 1), true); } while (pos < max) { @@ -645,7 +669,7 @@ void SCI_METHOD LexerRust::Lex(unsigned int startPos, int length, int initStyle, int n = styler.SafeGetCharAt(pos + 1, '\0'); int n2 = styler.SafeGetCharAt(pos + 2, '\0'); - if (pos == 0 && c == '#' && n == '!') { + if (pos == 0 && c == '#' && n == '!' && n2 != '[') { pos += 2; ResumeLineComment(styler, pos, max, NotDocComment); } else if (IsWhitespace(c)) { @@ -653,7 +677,16 @@ void SCI_METHOD LexerRust::Lex(unsigned int startPos, int length, int initStyle, } else if (c == '/' && (n == '/' || n == '*')) { ScanComments(styler, pos, max); } else if (c == 'r' && (n == '#' || n == '"')) { - ScanRawString(styler, pos, max); + ScanRawString(styler, pos, max, false); + } else if (c == 'b' && n == 'r' && (n2 == '#' || n2 == '"')) { + pos++; + ScanRawString(styler, pos, max, true); + } else if (c == 'b' && n == '"') { + pos += 2; + ResumeString(styler, pos, max, true); + } else if (c == 'b' && n == '\'') { + pos++; + ScanCharacterLiteralOrLifetime(styler, pos, true); } else if (IsIdentifierStart(c)) { ScanIdentifier(styler, pos, keywords); } else if (IsADigit(c)) { @@ -668,10 +701,10 @@ void SCI_METHOD LexerRust::Lex(unsigned int startPos, int length, int initStyle, pos++; styler.ColourTo(pos - 1, SCE_RUST_OPERATOR); } else if (c == '\'') { - ScanCharacterLiteralOrLifetime(styler, pos); + ScanCharacterLiteralOrLifetime(styler, pos, false); } else if (c == '"') { pos++; - ResumeString(styler, pos, max); + ResumeString(styler, pos, max, false); } else { pos++; styler.ColourTo(pos - 1, SCE_RUST_LEXERROR); diff --git a/scintilla/lexlib/LexerModule.h b/scintilla/lexlib/LexerModule.h index ee092e68f..5993c0fe9 100644 --- a/scintilla/lexlib/LexerModule.h +++ b/scintilla/lexlib/LexerModule.h @@ -67,7 +67,12 @@ inline int Maximum(int a, int b) { // Shut up annoying Visual C++ warnings: #ifdef _MSC_VER -#pragma warning(disable: 4244 4309) +#pragma warning(disable: 4244 4309 4456 4457) +#endif + +// Turn off shadow warnings for lexers as may be maintained by others +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wshadow" #endif #ifdef SCI_NAMESPACE diff --git a/scintilla/makefile.win32 b/scintilla/makefile.win32 index 6daac9b63..3d2bde993 100644 --- a/scintilla/makefile.win32 +++ b/scintilla/makefile.win32 @@ -130,10 +130,13 @@ SRCOBJS=\ Decoration.o \ Document.o \ Editor.o \ + EditModel.o \ + EditView.o \ ExternalLexer.o \ Indicator.o \ KeyMap.o \ LineMarker.o \ + MarginView.o \ PerLine.o \ PositionCache.o \ RESearch.o \ diff --git a/scintilla/scintilla_changes.patch b/scintilla/scintilla_changes.patch index e57ea095c..d68729810 100644 --- a/scintilla/scintilla_changes.patch +++ b/scintilla/scintilla_changes.patch @@ -31,7 +31,7 @@ diff --git b/scintilla/src/Catalogue.cxx a/scintilla/src/Catalogue.cxx index 41d5d54..70ce3bc 100644 --- scintilla/src/Catalogue.cxx +++ scintilla/src/Catalogue.cxx -@@ -76,115 +76,48 @@ int Scintilla_LinkLexers() { +@@ -76,116 +76,48 @@ int Scintilla_LinkLexers() { //++Autogenerated -- run scripts/LexGen.py to regenerate //**\(\tLINK_LEXER(\*);\n\) @@ -123,6 +123,7 @@ index 41d5d54..70ce3bc 100644 LINK_LEXER(lmPython); LINK_LEXER(lmR); - LINK_LEXER(lmREBOL); +- LINK_LEXER(lmRegistry); LINK_LEXER(lmRuby); LINK_LEXER(lmRust); - LINK_LEXER(lmScriptol); diff --git a/scintilla/src/CellBuffer.cxx b/scintilla/src/CellBuffer.cxx index 0c56c9e92..710e32403 100644 --- a/scintilla/src/CellBuffer.cxx +++ b/scintilla/src/CellBuffer.cxx @@ -144,6 +144,7 @@ UndoHistory::UndoHistory() { currentAction = 0; undoSequenceDepth = 0; savePoint = 0; + tentativePoint = -1; actions[currentAction].Create(startAction); } @@ -194,7 +195,7 @@ const char *UndoHistory::AppendAction(actionType at, int position, const char *d // Visual Studio 2013 Code Analysis wrongly believes actions can be NULL at its next reference __analysis_assume(actions); #endif - if (currentAction == savePoint) { + if ((currentAction == savePoint) || (currentAction == tentativePoint)) { currentAction++; } else if (!actions[currentAction].mayCoalesce) { // Not allowed to coalesce if this set @@ -282,6 +283,7 @@ void UndoHistory::DeleteUndoHistory() { currentAction = 0; actions[currentAction].Create(startAction); savePoint = 0; + tentativePoint = -1; } void UndoHistory::SetSavePoint() { @@ -292,6 +294,26 @@ bool UndoHistory::IsSavePoint() const { return savePoint == currentAction; } +void UndoHistory::TentativeStart() { + tentativePoint = currentAction; +} + +void UndoHistory::TentativeCommit() { + tentativePoint = -1; + // Truncate undo history + maxAction = currentAction; +} + +int UndoHistory::TentativeSteps() { + // Drop any trailing startAction + if (actions[currentAction].at == startAction && currentAction > 0) + currentAction--; + if (tentativePoint >= 0) + return currentAction - tentativePoint; + else + return -1; +} + bool UndoHistory::CanUndo() const { return (currentAction > 0) && (maxAction > 0); } @@ -505,6 +527,22 @@ bool CellBuffer::IsSavePoint() const { return uh.IsSavePoint(); } +void CellBuffer::TentativeStart() { + uh.TentativeStart(); +} + +void CellBuffer::TentativeCommit() { + uh.TentativeCommit(); +} + +int CellBuffer::TentativeSteps() { + return uh.TentativeSteps(); +} + +bool CellBuffer::TentativeActive() const { + return uh.TentativeActive(); +} + // Without undo void CellBuffer::InsertLine(int line, int position, bool lineStart) { diff --git a/scintilla/src/CellBuffer.h b/scintilla/src/CellBuffer.h index f07b45983..5e4fc7c8c 100644 --- a/scintilla/src/CellBuffer.h +++ b/scintilla/src/CellBuffer.h @@ -95,6 +95,7 @@ class UndoHistory { int currentAction; int undoSequenceDepth; int savePoint; + int tentativePoint; void EnsureUndoRoom(); @@ -117,6 +118,12 @@ public: void SetSavePoint(); bool IsSavePoint() const; + // Tentative actions are used for input composition so that it can be undone cleanly + void TentativeStart(); + void TentativeCommit(); + bool TentativeActive() const { return tentativePoint >= 0; } + int TentativeSteps(); + /// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is /// called that many times. Similarly for redo. bool CanUndo() const; @@ -193,6 +200,11 @@ public: void SetSavePoint(); bool IsSavePoint() const; + void TentativeStart(); + void TentativeCommit(); + bool TentativeActive() const; + int TentativeSteps(); + bool SetUndoCollection(bool collectUndo); bool IsCollectingUndo() const; void BeginUndoAction(); diff --git a/scintilla/src/ContractionState.cxx b/scintilla/src/ContractionState.cxx index a5ecfe113..7b7655420 100644 --- a/scintilla/src/ContractionState.cxx +++ b/scintilla/src/ContractionState.cxx @@ -150,8 +150,8 @@ bool ContractionState::GetVisible(int lineDoc) const { } } -bool ContractionState::SetVisible(int lineDocStart, int lineDocEnd, bool visible_) { - if (OneToOne() && visible_) { +bool ContractionState::SetVisible(int lineDocStart, int lineDocEnd, bool isVisible) { + if (OneToOne() && isVisible) { return false; } else { EnsureData(); @@ -159,9 +159,9 @@ bool ContractionState::SetVisible(int lineDocStart, int lineDocEnd, bool visible Check(); if ((lineDocStart <= lineDocEnd) && (lineDocStart >= 0) && (lineDocEnd < LinesInDoc())) { for (int line = lineDocStart; line <= lineDocEnd; line++) { - if (GetVisible(line) != visible_) { - int difference = visible_ ? heights->ValueAt(line) : -heights->ValueAt(line); - visible->SetValueAt(line, visible_ ? 1 : 0); + if (GetVisible(line) != isVisible) { + int difference = isVisible ? heights->ValueAt(line) : -heights->ValueAt(line); + visible->SetValueAt(line, isVisible ? 1 : 0); displayLines->InsertText(line, difference); delta += difference; } @@ -191,13 +191,13 @@ bool ContractionState::GetExpanded(int lineDoc) const { } } -bool ContractionState::SetExpanded(int lineDoc, bool expanded_) { - if (OneToOne() && expanded_) { +bool ContractionState::SetExpanded(int lineDoc, bool isExpanded) { + if (OneToOne() && isExpanded) { return false; } else { EnsureData(); - if (expanded_ != (expanded->ValueAt(lineDoc) == 1)) { - expanded->SetValueAt(lineDoc, expanded_ ? 1 : 0); + if (isExpanded != (expanded->ValueAt(lineDoc) == 1)) { + expanded->SetValueAt(lineDoc, isExpanded ? 1 : 0); Check(); return true; } else { diff --git a/scintilla/src/ContractionState.h b/scintilla/src/ContractionState.h index 1c9109c69..96cbf0763 100644 --- a/scintilla/src/ContractionState.h +++ b/scintilla/src/ContractionState.h @@ -48,11 +48,11 @@ public: void DeleteLines(int lineDoc, int lineCount); bool GetVisible(int lineDoc) const; - bool SetVisible(int lineDocStart, int lineDocEnd, bool visible); + bool SetVisible(int lineDocStart, int lineDocEnd, bool isVisible); bool HiddenLines() const; bool GetExpanded(int lineDoc) const; - bool SetExpanded(int lineDoc, bool expanded); + bool SetExpanded(int lineDoc, bool isExpanded); int ContractedNext(int lineDocStart) const; int GetHeight(int lineDoc) const; diff --git a/scintilla/src/Document.cxx b/scintilla/src/Document.cxx index 32d5f1896..4c1901679 100644 --- a/scintilla/src/Document.cxx +++ b/scintilla/src/Document.cxx @@ -209,6 +209,65 @@ void Document::SetSavePoint() { NotifySavePoint(true); } +void Document::TentativeUndo() { + CheckReadOnly(); + if (enteredModification == 0) { + enteredModification++; + if (!cb.IsReadOnly()) { + bool startSavePoint = cb.IsSavePoint(); + bool multiLine = false; + int steps = cb.TentativeSteps(); + //Platform::DebugPrintf("Steps=%d\n", steps); + for (int step = 0; step < steps; step++) { + const int prevLinesTotal = LinesTotal(); + const Action &action = cb.GetUndoStep(); + if (action.at == removeAction) { + NotifyModified(DocModification( + SC_MOD_BEFOREINSERT | SC_PERFORMED_UNDO, action)); + } else if (action.at == containerAction) { + DocModification dm(SC_MOD_CONTAINER | SC_PERFORMED_UNDO); + dm.token = action.position; + NotifyModified(dm); + } else { + NotifyModified(DocModification( + SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action)); + } + cb.PerformUndoStep(); + if (action.at != containerAction) { + ModifiedAt(action.position); + } + + int modFlags = SC_PERFORMED_UNDO; + // With undo, an insertion action becomes a deletion notification + if (action.at == removeAction) { + modFlags |= SC_MOD_INSERTTEXT; + } else if (action.at == insertAction) { + modFlags |= SC_MOD_DELETETEXT; + } + if (steps > 1) + modFlags |= SC_MULTISTEPUNDOREDO; + const int linesAdded = LinesTotal() - prevLinesTotal; + if (linesAdded != 0) + multiLine = true; + if (step == steps - 1) { + modFlags |= SC_LASTSTEPINUNDOREDO; + if (multiLine) + modFlags |= SC_MULTILINEUNDOREDO; + } + NotifyModified(DocModification(modFlags, action.position, action.lenData, + linesAdded, action.data)); + } + + bool endSavePoint = cb.IsSavePoint(); + if (startSavePoint != endSavePoint) + NotifySavePoint(endSavePoint); + + cb.TentativeCommit(); + } + enteredModification--; + } +} + int Document::GetMark(int line) { return static_cast(perLineData[ldMarkers])->MarkValue(line); } diff --git a/scintilla/src/Document.h b/scintilla/src/Document.h index a59f192a9..e84be14e4 100644 --- a/scintilla/src/Document.h +++ b/scintilla/src/Document.h @@ -303,6 +303,12 @@ public: void AddUndoAction(int token, bool mayCoalesce) { cb.AddUndoAction(token, mayCoalesce); } void SetSavePoint(); bool IsSavePoint() const { return cb.IsSavePoint(); } + + void TentativeStart() { cb.TentativeStart(); } + void TentativeCommit() { cb.TentativeCommit(); } + void TentativeUndo(); + bool TentativeActive() const { return cb.TentativeActive(); } + const char * SCI_METHOD BufferPointer() { return cb.BufferPointer(); } const char *RangePointer(int position, int rangeLength) { return cb.RangePointer(position, rangeLength); } int GapPosition() const { return cb.GapPosition(); } @@ -368,7 +374,7 @@ public: void SetDefaultCharClasses(bool includeWordClass); void SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass); - int GetCharsOfClass(CharClassify::cc charClass, unsigned char *buffer); + int GetCharsOfClass(CharClassify::cc characterClass, unsigned char *buffer); void SCI_METHOD StartStyling(int position, char mask); bool SCI_METHOD SetStyleFor(int length, char style); bool SCI_METHOD SetStyles(int length, const char *styles); diff --git a/scintilla/src/EditModel.cxx b/scintilla/src/EditModel.cxx new file mode 100644 index 000000000..815d22781 --- /dev/null +++ b/scintilla/src/EditModel.cxx @@ -0,0 +1,74 @@ +// Scintilla source code edit control +/** @file EditModel.cxx + ** Defines the editor state that must be visible to EditorView. + **/ +// Copyright 1998-2014 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "ILexer.h" +#include "Scintilla.h" + +#include "StringCopy.h" +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" +#include "ContractionState.h" +#include "CellBuffer.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "XPM.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "CharClassify.h" +#include "Decoration.h" +#include "CaseFolder.h" +#include "Document.h" +#include "UniConversion.h" +#include "Selection.h" +#include "PositionCache.h" +#include "EditModel.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +Caret::Caret() : + active(false), on(false), period(500) {} + +EditModel::EditModel() { + inOverstrike = false; + xOffset = 0; + trackLineWidth = false; + posDrag = SelectionPosition(invalidPosition); + braces[0] = invalidPosition; + braces[1] = invalidPosition; + bracesMatchStyle = STYLE_BRACEBAD; + highlightGuideColumn = 0; + primarySelection = true; + foldFlags = 0; + hotspot = Range(invalidPosition); + wrapWidth = LineLayout::wrapWidthInfinite; + pdoc = new Document(); + pdoc->AddRef(); +} + +EditModel::~EditModel() { + pdoc->Release(); + pdoc = 0; +} diff --git a/scintilla/src/EditModel.h b/scintilla/src/EditModel.h new file mode 100644 index 000000000..f7ca7497a --- /dev/null +++ b/scintilla/src/EditModel.h @@ -0,0 +1,67 @@ +// Scintilla source code edit control +/** @file EditModel.h + ** Defines the editor state that must be visible to EditorView. + **/ +// Copyright 1998-2014 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef EDITMODEL_H +#define EDITMODEL_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +/** +*/ +class Caret { +public: + bool active; + bool on; + int period; + + Caret(); +}; + +class EditModel { + // Private so EditModel objects can not be copied + EditModel(const EditModel &); + EditModel &operator=(const EditModel &); + +public: + bool inOverstrike; + int xOffset; ///< Horizontal scrolled amount in pixels + bool trackLineWidth; + + SpecialRepresentations reprs; + Caret caret; + SelectionPosition posDrag; + Position braces[2]; + int bracesMatchStyle; + int highlightGuideColumn; + Selection sel; + bool primarySelection; + + int foldFlags; + ContractionState cs; + // Hotspot support + Range hotspot; + + // Wrapping support + int wrapWidth; + + Document *pdoc; + + EditModel(); + virtual ~EditModel(); + virtual int TopLineOfMain() const = 0; + virtual Point GetVisibleOriginInMain() const = 0; + virtual int LinesOnScreen() const = 0; + virtual Range GetHotSpotRange() const = 0; +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif diff --git a/scintilla/src/EditView.cxx b/scintilla/src/EditView.cxx new file mode 100644 index 000000000..c46a0fbcf --- /dev/null +++ b/scintilla/src/EditView.cxx @@ -0,0 +1,2072 @@ +// Scintilla source code edit control +/** @file Editor.cxx + ** Defines the appearance of the main text area of the editor window. + **/ +// Copyright 1998-2014 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "ILexer.h" +#include "Scintilla.h" + +#include "StringCopy.h" +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" +#include "ContractionState.h" +#include "CellBuffer.h" +#include "PerLine.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 "UniConversion.h" +#include "Selection.h" +#include "PositionCache.h" +#include "EditModel.h" +#include "MarginView.h" +#include "EditView.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +static inline bool IsControlCharacter(int ch) { + // iscntrl returns true for lots of chars > 127 which are displayable + return ch >= 0 && ch < ' '; +} + +PrintParameters::PrintParameters() { + magnification = 0; + colourMode = SC_PRINT_NORMAL; + wrapState = eWrapWord; +} + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) { + if (st.multipleStyles) { + for (size_t iStyle = 0; iStyle(styles[endSegment + 1]) == style)) + endSegment++; + FontAlias fontText = vs.styles[style + styleOffset].font; + width += static_cast(surface->WidthText(fontText, text + start, + static_cast(endSegment - start + 1))); + start = endSegment + 1; + } + return width; +} + +int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) { + int widthMax = 0; + size_t start = 0; + while (start < st.length) { + size_t lenLine = st.LineLength(start); + int widthSubLine; + if (st.multipleStyles) { + widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine); + } else { + FontAlias fontText = vs.styles[styleOffset + st.style].font; + widthSubLine = static_cast(surface->WidthText(fontText, + st.text + start, static_cast(lenLine))); + } + if (widthSubLine > widthMax) + widthMax = widthSubLine; + start += lenLine + 1; + } + return widthMax; +} + +void DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase, + const char *s, int len, DrawPhase phase) { + FontAlias fontText = style.font; + if (phase & drawBack) { + if (phase & drawText) { + // Drawing both + surface->DrawTextNoClip(rc, fontText, ybase, s, len, + style.fore, style.back); + } else { + surface->FillRectangle(rc, style.back); + } + } else if (phase & drawText) { + surface->DrawTextTransparent(rc, fontText, ybase, s, len, style.fore); + } +} + +void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText, + const StyledText &st, size_t start, size_t length, DrawPhase phase) { + + if (st.multipleStyles) { + int x = static_cast(rcText.left); + size_t i = 0; + while (i < length) { + size_t end = i; + size_t style = st.styles[i + start]; + while (end < length - 1 && st.styles[start + end + 1] == style) + end++; + style += styleOffset; + FontAlias fontText = vs.styles[style].font; + const int width = static_cast(surface->WidthText(fontText, + st.text + start + i, static_cast(end - i + 1))); + PRectangle rcSegment = rcText; + rcSegment.left = static_cast(x); + rcSegment.right = static_cast(x + width + 1); + DrawTextNoClipPhase(surface, rcSegment, vs.styles[style], + rcText.top + vs.maxAscent, st.text + start + i, + static_cast(end - i + 1), phase); + x += width; + i = end + 1; + } + } else { + const size_t style = st.style + styleOffset; + DrawTextNoClipPhase(surface, rcText, vs.styles[style], + rcText.top + vs.maxAscent, st.text + start, + static_cast(length), phase); + } +} + +#ifdef SCI_NAMESPACE +} +#endif + +const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues + +EditView::EditView() { + ldTabstops = NULL; + hideSelection = false; + drawOverstrikeCaret = true; + bufferedDraw = true; + phasesDraw = phasesTwo; + lineWidthMaxSeen = 0; + additionalCaretsBlink = true; + additionalCaretsVisible = true; + imeCaretBlockOverride = false; + pixmapLine = 0; + pixmapIndentGuide = 0; + pixmapIndentGuideHighlight = 0; + llc.SetLevel(LineLayoutCache::llcCaret); + posCache.SetSize(0x400); +} + +EditView::~EditView() { + delete ldTabstops; + ldTabstops = NULL; +} + +bool EditView::SetTwoPhaseDraw(bool twoPhaseDraw) { + const PhasesDraw phasesDrawNew = twoPhaseDraw ? phasesTwo : phasesOne; + const bool redraw = phasesDraw != phasesDrawNew; + phasesDraw = phasesDrawNew; + return redraw; +} + +bool EditView::SetPhasesDraw(int phases) { + const PhasesDraw phasesDrawNew = static_cast(phases); + const bool redraw = phasesDraw != phasesDrawNew; + phasesDraw = phasesDrawNew; + return redraw; +} + +bool EditView::LinesOverlap() const { + return phasesDraw == phasesMultiple; +} + +void EditView::ClearAllTabstops() { + delete ldTabstops; + ldTabstops = 0; +} + +int EditView::NextTabstopPos(int line, int x, int tabWidth) const { + int next = GetNextTabstop(line, x); + if (next > 0) + return next; + return ((((x + 2) / tabWidth) + 1) * tabWidth); +} + +bool EditView::ClearTabstops(int line) { + LineTabstops *lt = static_cast(ldTabstops); + return lt && lt->ClearTabstops(line); +} + +bool EditView::AddTabstop(int line, int x) { + if (!ldTabstops) { + ldTabstops = new LineTabstops(); + } + LineTabstops *lt = static_cast(ldTabstops); + return lt && lt->AddTabstop(line, x); +} + +int EditView::GetNextTabstop(int line, int x) const { + LineTabstops *lt = static_cast(ldTabstops); + if (lt) { + return lt->GetNextTabstop(line, x); + } else { + return 0; + } +} + +void EditView::LinesAddedOrRemoved(int lineOfPos, int linesAdded) { + if (ldTabstops) { + if (linesAdded > 0) { + for (int line = lineOfPos; line < lineOfPos + linesAdded; line++) { + ldTabstops->InsertLine(line); + } + } else { + for (int line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) { + ldTabstops->RemoveLine(line); + } + } + } +} + +void EditView::DropGraphics(bool freeObjects) { + if (freeObjects) { + delete pixmapLine; + pixmapLine = 0; + delete pixmapIndentGuide; + pixmapIndentGuide = 0; + delete pixmapIndentGuideHighlight; + pixmapIndentGuideHighlight = 0; + } else { + if (pixmapLine) + pixmapLine->Release(); + if (pixmapIndentGuide) + pixmapIndentGuide->Release(); + if (pixmapIndentGuideHighlight) + pixmapIndentGuideHighlight->Release(); + } +} + +void EditView::AllocateGraphics(const ViewStyle &vsDraw) { + if (!pixmapLine) + pixmapLine = Surface::Allocate(vsDraw.technology); + if (!pixmapIndentGuide) + pixmapIndentGuide = Surface::Allocate(vsDraw.technology); + if (!pixmapIndentGuideHighlight) + pixmapIndentGuideHighlight = Surface::Allocate(vsDraw.technology); +} + +const char *ControlCharacterString(unsigned char ch) { + const char *reps[] = { + "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", + "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", + "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" + }; + if (ch < ELEMENTS(reps)) { + return reps[ch]; + } else { + return "BAD"; + } +} + +void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) { + 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; + } + 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); +} + +void EditView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) { + if (!pixmapIndentGuide->Initialised()) { + // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line + pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid); + pixmapIndentGuideHighlight->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid); + PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vsDraw.lineHeight); + pixmapIndentGuide->FillRectangle(rcIG, vsDraw.styles[STYLE_INDENTGUIDE].back); + pixmapIndentGuide->PenColour(vsDraw.styles[STYLE_INDENTGUIDE].fore); + pixmapIndentGuideHighlight->FillRectangle(rcIG, vsDraw.styles[STYLE_BRACELIGHT].back); + pixmapIndentGuideHighlight->PenColour(vsDraw.styles[STYLE_BRACELIGHT].fore); + for (int stripe = 1; stripe < vsDraw.lineHeight + 1; stripe += 2) { + PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1); + pixmapIndentGuide->FillRectangle(rcPixel, vsDraw.styles[STYLE_INDENTGUIDE].fore); + pixmapIndentGuideHighlight->FillRectangle(rcPixel, vsDraw.styles[STYLE_BRACELIGHT].fore); + } + } +} + +LineLayout *EditView::RetrieveLineLayout(int lineNumber, const EditModel &model) { + int posLineStart = model.pdoc->LineStart(lineNumber); + int posLineEnd = model.pdoc->LineStart(lineNumber + 1); + PLATFORM_ASSERT(posLineEnd >= posLineStart); + int lineCaret = model.pdoc->LineFromPosition(model.sel.MainCaret()); + return llc.Retrieve(lineNumber, lineCaret, + posLineEnd - posLineStart, model.pdoc->GetStyleClock(), + model.LinesOnScreen() + 1, model.pdoc->LinesTotal()); +} + +/** +* Fill in the LineLayout data for the given line. +* Copy the given @a line and its styles from the document into local arrays. +* Also determine the x position at which each character starts. +*/ +void EditView::LayoutLine(const EditModel &model, int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) { + if (!ll) + return; + + PLATFORM_ASSERT(line < model.pdoc->LinesTotal()); + PLATFORM_ASSERT(ll->chars != NULL); + int posLineStart = model.pdoc->LineStart(line); + int posLineEnd = model.pdoc->LineStart(line + 1); + // If the line is very long, limit the treatment to a length that should fit in the viewport + if (posLineEnd >(posLineStart + ll->maxLineLength)) { + posLineEnd = posLineStart + ll->maxLineLength; + } + if (ll->validity == LineLayout::llCheckTextAndStyle) { + int lineLength = posLineEnd - posLineStart; + if (!vstyle.viewEOL) { + lineLength = model.pdoc->LineEnd(line) - posLineStart; + } + if (lineLength == ll->numCharsInLine) { + // See if chars, styles, indicators, are all the same + bool allSame = true; + // Check base line layout + char styleByte = 0; + int numCharsInLine = 0; + while (numCharsInLine < lineLength) { + int charInDoc = numCharsInLine + posLineStart; + char chDoc = model.pdoc->CharAt(charInDoc); + styleByte = model.pdoc->StyleAt(charInDoc); + allSame = allSame && + (ll->styles[numCharsInLine] == static_cast(styleByte)); + if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed) + allSame = allSame && + (ll->chars[numCharsInLine] == chDoc); + else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower) + allSame = allSame && + (ll->chars[numCharsInLine] == static_cast(tolower(chDoc))); + else // Style::caseUpper + allSame = allSame && + (ll->chars[numCharsInLine] == static_cast(toupper(chDoc))); + numCharsInLine++; + } + allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled + if (allSame) { + ll->validity = LineLayout::llPositions; + } else { + ll->validity = LineLayout::llInvalid; + } + } else { + ll->validity = LineLayout::llInvalid; + } + } + if (ll->validity == LineLayout::llInvalid) { + ll->widthLine = LineLayout::wrapWidthInfinite; + ll->lines = 1; + if (vstyle.edgeState == EDGE_BACKGROUND) { + ll->edgeColumn = model.pdoc->FindColumn(line, vstyle.theEdge); + if (ll->edgeColumn >= posLineStart) { + ll->edgeColumn -= posLineStart; + } + } else { + ll->edgeColumn = -1; + } + + // Fill base line layout + const int lineLength = posLineEnd - posLineStart; + model.pdoc->GetCharRange(ll->chars, posLineStart, lineLength); + model.pdoc->GetStyleRange(ll->styles, posLineStart, lineLength); + int numCharsBeforeEOL = model.pdoc->LineEnd(line) - posLineStart; + const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL; + for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) { + const unsigned char styleByte = ll->styles[styleInLine]; + ll->styles[styleInLine] = styleByte; + } + const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0; + if (vstyle.someStylesForceCase) { + for (int charInLine = 0; charInLinechars[charInLine]; + if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper) + ll->chars[charInLine] = static_cast(toupper(chDoc)); + else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower) + ll->chars[charInLine] = static_cast(tolower(chDoc)); + } + } + ll->xHighlightGuide = 0; + // Extra element at the end of the line to hold end x position and act as + ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character + ll->styles[numCharsInLine] = styleByteLast; // For eolFilled + + // Layout the line, determining the position of each character, + // with an extra element at the end for the end of the line. + ll->positions[0] = 0; + bool lastSegItalics = false; + + BreakFinder bfLayout(ll, NULL, Range(0, numCharsInLine), posLineStart, 0, false, model.pdoc, &model.reprs); + while (bfLayout.More()) { + + const TextSegment ts = bfLayout.Next(); + + std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f); + if (vstyle.styles[ll->styles[ts.start]].visible) { + if (ts.representation) { + XYPOSITION representationWidth = vstyle.controlCharWidth; + if (ll->chars[ts.start] == '\t') { + // Tab is a special case of representation, taking a variable amount of space + const int x = static_cast(ll->positions[ts.start]); + const int tabWidth = static_cast(vstyle.tabWidth); + representationWidth = static_cast(NextTabstopPos(line, x, tabWidth) - ll->positions[ts.start]); + } else { + if (representationWidth <= 0.0) { + XYPOSITION positionsRepr[256]; // Should expand when needed + posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(), + static_cast(ts.representation->stringRep.length()), positionsRepr, model.pdoc); + representationWidth = positionsRepr[ts.representation->stringRep.length() - 1] + vstyle.ctrlCharPadding; + } + } + for (int ii = 0; ii < ts.length; ii++) + ll->positions[ts.start + 1 + ii] = representationWidth; + } else { + if ((ts.length == 1) && (' ' == ll->chars[ts.start])) { + // Over half the segments are single characters and of these about half are space characters. + ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth; + } else { + posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start, + ts.length, ll->positions + ts.start + 1, model.pdoc); + } + } + lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic); + } + + for (int posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) { + ll->positions[posToIncrease] += ll->positions[ts.start]; + } + } + + // Small hack to make lines that end with italics not cut off the edge of the last character + if (lastSegItalics) { + ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset; + } + ll->numCharsInLine = numCharsInLine; + ll->numCharsBeforeEOL = numCharsBeforeEOL; + ll->validity = LineLayout::llPositions; + } + // Hard to cope when too narrow, so just assume there is space + if (width < 20) { + width = 20; + } + if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) { + ll->widthLine = width; + if (width == LineLayout::wrapWidthInfinite) { + ll->lines = 1; + } else if (width > ll->positions[ll->numCharsInLine]) { + // Simple common case where line does not need wrapping. + ll->lines = 1; + } else { + if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) { + width -= static_cast(vstyle.aveCharWidth); // take into account the space for end wrap mark + } + XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line + if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) { + wrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth; + } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) { + wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth; + } + ll->wrapIndent = wrapAddIndent; + if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED) + for (int i = 0; i < ll->numCharsInLine; i++) { + if (!IsSpaceOrTab(ll->chars[i])) { + ll->wrapIndent += ll->positions[i]; // Add line indent + break; + } + } + // Check for text width minimum + if (ll->wrapIndent > width - static_cast(vstyle.aveCharWidth) * 15) + ll->wrapIndent = wrapAddIndent; + // Check for wrapIndent minimum + if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth)) + ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual + ll->lines = 0; + // Calculate line start positions based upon width. + int lastGoodBreak = 0; + int lastLineStart = 0; + XYACCUMULATOR startOffset = 0; + int p = 0; + while (p < ll->numCharsInLine) { + if ((ll->positions[p + 1] - startOffset) >= width) { + if (lastGoodBreak == lastLineStart) { + // Try moving to start of last character + if (p > 0) { + lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1) + - posLineStart; + } + if (lastGoodBreak == lastLineStart) { + // Ensure at least one character on line. + lastGoodBreak = model.pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1) + - posLineStart; + } + } + lastLineStart = lastGoodBreak; + ll->lines++; + ll->SetLineStart(ll->lines, lastGoodBreak); + startOffset = ll->positions[lastGoodBreak]; + // take into account the space for start wrap mark and indent + startOffset -= ll->wrapIndent; + p = lastGoodBreak + 1; + continue; + } + if (p > 0) { + if (vstyle.wrapState == eWrapChar) { + lastGoodBreak = model.pdoc->MovePositionOutsideChar(p + posLineStart, -1) + - posLineStart; + p = model.pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart; + continue; + } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) { + lastGoodBreak = p; + } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) { + lastGoodBreak = p; + } + } + p++; + } + ll->lines++; + } + ll->validity = LineLayout::llLines; + } +} + +Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, int topLine, const ViewStyle &vs) { + 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)); + if (surface && ll) { + const int posLineStart = model.pdoc->LineStart(line); + LayoutLine(model, line, surface, vs, ll, model.wrapWidth); + const int posInLine = pos.Position() - posLineStart; + pt = ll->PointFromPosition(posInLine, vs.lineHeight); + pt.y += (lineVisible - topLine) * vs.lineHeight; + pt.x += vs.textStart - model.xOffset; + } + pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth; + return pt; +} + +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)); + if (!canReturnInvalid && (visibleLine < 0)) + visibleLine = 0; + const int lineDoc = model.cs.DocFromDisplay(visibleLine); + if (canReturnInvalid && (lineDoc < 0)) + return SelectionPosition(INVALID_POSITION); + if (lineDoc >= model.pdoc->LinesTotal()) + return SelectionPosition(canReturnInvalid ? INVALID_POSITION : model.pdoc->Length()); + const int posLineStart = 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 = visibleLine - lineStartSet; + if (subLine < ll->lines) { + const Range rangeSubLine = ll->SubLineRange(subLine); + const XYPOSITION subLineStart = ll->positions[rangeSubLine.start]; + if (subLine > 0) // Wrapped + pt.x -= ll->wrapIndent; + const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition); + if (positionInLine < rangeSubLine.end) { + return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1)); + } + if (virtualSpace) { + const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth; + const int spaceOffset = static_cast( + (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth); + return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset); + } else if (canReturnInvalid) { + if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) { + return SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1)); + } + } else { + return SelectionPosition(rangeSubLine.end + posLineStart); + } + } + if (!canReturnInvalid) + return SelectionPosition(ll->numCharsInLine + posLineStart); + } + return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart); +} + +/** +* Find the document position corresponding to an x coordinate on a particular document line. +* Ensure is between whole characters when document is in multi-byte or UTF-8 mode. +* This method is used for rectangular selections and does not work on wrapped lines. +*/ +SelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, int lineDoc, int x, const ViewStyle &vs) { + AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model)); + if (surface && ll) { + const int posLineStart = model.pdoc->LineStart(lineDoc); + LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth); + const Range rangeSubLine = ll->SubLineRange(0); + const XYPOSITION subLineStart = ll->positions[rangeSubLine.start]; + const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false); + if (positionInLine < rangeSubLine.end) { + return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1)); + } + const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth; + const int spaceOffset = static_cast( + (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth); + return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset); + } + return SelectionPosition(0); +} + +int EditView::DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs) { + int lineDoc = model.pdoc->LineFromPosition(pos); + int lineDisplay = model.cs.DisplayFromDoc(lineDoc); + AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model)); + if (surface && ll) { + LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth); + unsigned int posLineStart = model.pdoc->LineStart(lineDoc); + int posInLine = pos - posLineStart; + lineDisplay--; // To make up for first increment ahead. + for (int subLine = 0; subLine < ll->lines; subLine++) { + if (posInLine >= ll->LineStart(subLine)) { + lineDisplay++; + } + } + } + return lineDisplay; +} + +int EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs) { + int line = model.pdoc->LineFromPosition(pos); + AutoLineLayout ll(llc, RetrieveLineLayout(line, model)); + int posRet = INVALID_POSITION; + if (surface && ll) { + unsigned int posLineStart = model.pdoc->LineStart(line); + LayoutLine(model, line, surface, vs, ll, model.wrapWidth); + int posInLine = pos - posLineStart; + if (posInLine <= ll->maxLineLength) { + for (int subLine = 0; subLine < ll->lines; subLine++) { + if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) { + if (start) { + posRet = ll->LineStart(subLine) + posLineStart; + } else { + if (subLine == ll->lines - 1) + posRet = ll->LineStart(subLine + 1) + posLineStart; + else + posRet = ll->LineStart(subLine + 1) + posLineStart - 1; + } + } + } + } + } + return posRet; +} + +static ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main, bool primarySelection) { + return main ? + (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) : + vsDraw.selAdditionalBackground; +} + +static ColourDesired TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i) { + if (inSelection == 1) { + if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) { + return SelectionBackground(vsDraw, true, model.primarySelection); + } + } else if (inSelection == 2) { + if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) { + return SelectionBackground(vsDraw, false, model.primarySelection); + } + } else { + if ((vsDraw.edgeState == EDGE_BACKGROUND) && + (i >= ll->edgeColumn) && + (i < ll->numCharsBeforeEOL)) + return vsDraw.edgecolour; + if (inHotspot && vsDraw.hotspotColours.back.isSet) + return vsDraw.hotspotColours.back; + } + if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) { + return background; + } else { + return vsDraw.styles[styleMain].back; + } +} + +void EditView::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) { + Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0); + PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast(rcSegment.top), start + 2, static_cast(rcSegment.bottom)); + surface->Copy(rcCopyArea, from, + highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide); +} + +static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) { + if (alpha != SC_ALPHA_NOALPHA) { + surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0); + } +} + +static void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment, + const char *s, ColourDesired textBack, ColourDesired textFore, bool fillBackground) { + if (fillBackground) { + surface->FillRectangle(rcSegment, textBack); + } + FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font; + int normalCharHeight = static_cast(surface->Ascent(ctrlCharsFont) - + surface->InternalLeading(ctrlCharsFont)); + PRectangle rcCChar = rcSegment; + rcCChar.left = rcCChar.left + 1; + rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight; + rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1; + PRectangle rcCentral = rcCChar; + rcCentral.top++; + rcCentral.bottom--; + surface->FillRectangle(rcCentral, textFore); + PRectangle rcChar = rcCChar; + rcChar.left++; + rcChar.right--; + surface->DrawTextClipped(rcChar, ctrlCharsFont, + rcSegment.top + vsDraw.maxAscent, s, static_cast(s ? strlen(s) : 0), + textBack, textFore); +} + +void EditView::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) { + + const int posLineStart = model.pdoc->LineStart(line); + PRectangle rcSegment = rcLine; + + const bool lastSubLine = subLine == (ll->lines - 1); + XYPOSITION virtualSpace = 0; + if (lastSubLine) { + const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; + virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth; + } + XYPOSITION xEol = static_cast(ll->positions[lineEnd] - subLineStart); + + // Fill the virtual space and show selections within it + if (virtualSpace) { + rcSegment.left = xEol + xStart; + rcSegment.right = xEol + xStart + virtualSpace; + surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back); + if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) { + SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)))); + for (size_t r = 0; rEndLineStyle()].spaceWidth; + rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - + static_cast(subLineStart)+portion.start.VirtualSpace() * spaceWidth; + rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - + static_cast(subLineStart)+portion.end.VirtualSpace() * spaceWidth; + rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left; + rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right; + surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection)); + } + } + } + } + } + + 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; + } + + // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on + XYPOSITION blobsWidth = 0; + if (lastSubLine) { + for (int eolPos = ll->numCharsBeforeEOL; eolPosnumCharsInLine; eolPos++) { + rcSegment.left = xStart + ll->positions[eolPos] - static_cast(subLineStart)+virtualSpace; + rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast(subLineStart)+virtualSpace; + blobsWidth += rcSegment.Width(); + char hexits[4]; + const char *ctrlChar; + unsigned char chEOL = ll->chars[eolPos]; + int styleMain = ll->styles[eolPos]; + ColourDesired textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos); + if (UTF8IsAscii(chEOL)) { + ctrlChar = ControlCharacterString(chEOL); + } else { + const Representation *repr = model.reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos); + if (repr) { + ctrlChar = repr->stringRep.c_str(); + eolPos = ll->numCharsInLine; + } else { + sprintf(hexits, "x%2X", chEOL); + ctrlChar = hexits; + } + } + ColourDesired textFore = vsDraw.styles[styleMain].fore; + if (eolInSelection && vsDraw.selColours.fore.isSet) { + textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground; + } + if (eolInSelection && vsDraw.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1)) { + if (alpha == SC_ALPHA_NOALPHA) { + surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection)); + } else { + surface->FillRectangle(rcSegment, textBack); + } + } else { + surface->FillRectangle(rcSegment, textBack); + } + DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, phasesDraw == phasesOne); + 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); + } + } + } + + // Draw the eol-is-selected rectangle + rcSegment.left = xEol + xStart + virtualSpace + blobsWidth; + rcSegment.right = rcSegment.left + vsDraw.aveCharWidth; + + if (eolInSelection && 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 (line < model.pdoc->LinesTotal() - 1) { + surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back); + } 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.selColours.back.isSet && (line < model.pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) { + SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1, model.primarySelection), alpha); + } + } + + // 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 drawWrapMarkEnd = false; + + if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) { + if (subLine + 1 < ll->lines) { + drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0; + } + } + + if (drawWrapMarkEnd) { + PRectangle rcPlace = rcSegment; + + if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) { + rcPlace.left = xEol + xStart + virtualSpace; + rcPlace.right = rcPlace.left + vsDraw.aveCharWidth; + } else { + // rcLine is clipped to text area + rcPlace.right = rcLine.right; + rcPlace.left = rcPlace.right - vsDraw.aveCharWidth; + } + DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour()); + } +} + +static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw, + const LineLayout *ll, int xStart, PRectangle rcLine, int subLine) { + 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); +} + +static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, int xStart, PRectangle rcLine, int subLine, int lineEnd, bool under) { + // Draw decorators + const int posLineStart = model.pdoc->LineStart(line); + const int lineStart = ll->LineStart(subLine); + const int posLineEnd = posLineStart + lineEnd; + + for (Decoration *deco = model.pdoc->decorations.root; deco; deco = deco->next) { + if (under == vsDraw.indicators[deco->indicator].under) { + int startPos = posLineStart + lineStart; + if (!deco->rs.ValueAt(startPos)) { + startPos = deco->rs.EndRun(startPos); + } + while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) { + int endPos = deco->rs.EndRun(startPos); + if (endPos > posLineEnd) + endPos = posLineEnd; + DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart, + surface, vsDraw, ll, xStart, rcLine, subLine); + startPos = endPos; + if (!deco->rs.ValueAt(startPos)) { + startPos = deco->rs.EndRun(startPos); + } + } + } + } + + // Use indicators to highlight matching braces + if ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) || + (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))) { + int braceIndicator = (model.bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator; + if (under == vsDraw.indicators[braceIndicator].under) { + Range rangeLine(posLineStart + lineStart, posLineEnd); + 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); + } + } + 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); + } + } + } + } +} + +void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) { + int indent = static_cast(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth); + PRectangle rcSegment = rcLine; + int annotationLine = subLine - ll->lines; + const StyledText stAnnotation = model.pdoc->AnnotationStyledText(line); + if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) { + if (phase & drawBack) { + surface->FillRectangle(rcSegment, vsDraw.styles[0].back); + } + rcSegment.left = static_cast(xStart); + if (model.trackLineWidth || (vsDraw.annotationVisible == ANNOTATION_BOXED)) { + // Only care about calculating width if tracking or need to draw box + int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation); + if (vsDraw.annotationVisible == ANNOTATION_BOXED) { + widthAnnotation += static_cast(vsDraw.spaceWidth * 2); // Margins + } + if (widthAnnotation > lineWidthMaxSeen) + lineWidthMaxSeen = widthAnnotation; + if (vsDraw.annotationVisible == ANNOTATION_BOXED) { + rcSegment.left = static_cast(xStart + indent); + rcSegment.right = rcSegment.left + widthAnnotation; + } + } + const int annotationLines = model.pdoc->AnnotationLines(line); + size_t start = 0; + size_t lengthAnnotation = stAnnotation.LineLength(start); + int lineInAnnotation = 0; + while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) { + start += lengthAnnotation + 1; + lengthAnnotation = stAnnotation.LineLength(start); + lineInAnnotation++; + } + PRectangle rcText = rcSegment; + if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) { + surface->FillRectangle(rcText, + vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back); + rcText.left += vsDraw.spaceWidth; + } + DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, + stAnnotation, start, lengthAnnotation, phase); + if ((phase & drawBack) && (vsDraw.annotationVisible == ANNOTATION_BOXED)) { + surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore); + 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)); + if (subLine == ll->lines) { + surface->MoveTo(static_cast(rcSegment.left), static_cast(rcSegment.top)); + surface->LineTo(static_cast(rcSegment.right), static_cast(rcSegment.top)); + } + if (subLine == ll->lines + annotationLines - 1) { + surface->MoveTo(static_cast(rcSegment.left), static_cast(rcSegment.bottom - 1)); + surface->LineTo(static_cast(rcSegment.right), static_cast(rcSegment.bottom - 1)); + } + } + } +} + +static void DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) { + + int lineStart = ll->LineStart(subLine); + int posBefore = posCaret; + int posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1); + int numCharsToDraw = posAfter - posCaret; + + // Work out where the starting and ending offsets are. We need to + // see if the previous character shares horizontal space, such as a + // glyph / combining character. If so we'll need to draw that too. + int offsetFirstChar = offset; + int offsetLastChar = offset + (posAfter - posCaret); + while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) { + if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) { + // The char does not share horizontal space + break; + } + // Char shares horizontal space, update the numChars to draw + // Update posBefore to point to the prev char + posBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1); + numCharsToDraw = posAfter - posBefore; + offsetFirstChar = offset - (posCaret - posBefore); + } + + // See if the next character shares horizontal space, if so we'll + // need to draw that too. + if (offsetFirstChar < 0) + offsetFirstChar = 0; + numCharsToDraw = offsetLastChar - offsetFirstChar; + while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) { + // Update posAfter to point to the 2nd next char, this is where + // the next character ends, and 2nd next begins. We'll need + // to compare these two + posBefore = posAfter; + posAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1); + offsetLastChar = offset + (posAfter - posCaret); + if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) { + // The char does not share horizontal space + break; + } + // Char shares horizontal space, update the numChars to draw + numCharsToDraw = offsetLastChar - offsetFirstChar; + } + + // We now know what to draw, update the caret drawing rectangle + rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart; + rcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart; + + // Adjust caret position to take into account any word wrapping symbols. + if ((ll->wrapIndent != 0) && (lineStart != 0)) { + XYPOSITION wordWrapCharWidth = ll->wrapIndent; + rcCaret.left += wordWrapCharWidth; + rcCaret.right += wordWrapCharWidth; + } + + // This character is where the caret block is, we override the colours + // (inversed) for drawing the caret here. + int styleMain = ll->styles[offsetFirstChar]; + FontAlias fontText = vsDraw.styles[styleMain].font; + surface->DrawTextClipped(rcCaret, fontText, + rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar, + numCharsToDraw, vsDraw.styles[styleMain].back, + caretColour); +} + +void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int lineDoc, int xStart, PRectangle rcLine, int subLine) const { + // When drag is active it is the only caret drawn + bool drawDrag = model.posDrag.IsValid(); + if (hideSelection && !drawDrag) + return; + const int posLineStart = model.pdoc->LineStart(lineDoc); + // For each selection draw + for (size_t r = 0; (rEndLineStyle()].spaceWidth; + const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth; + if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) { + XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)]; + if (ll->wrapIndent != 0) { + int lineStart = ll->LineStart(subLine); + if (lineStart != 0) // Wrapped + xposCaret += ll->wrapIndent; + } + bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret); + bool caretVisibleState = additionalCaretsVisible || mainCaret; + if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) && + ((model.posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) { + bool caretAtEOF = false; + bool caretAtEOL = false; + bool drawBlockCaret = false; + XYPOSITION widthOverstrikeCaret; + XYPOSITION caretWidthOffset = 0; + PRectangle rcCaret = rcLine; + + if (posCaret.Position() == model.pdoc->Length()) { // At end of document + caretAtEOF = true; + widthOverstrikeCaret = vsDraw.aveCharWidth; + } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line + caretAtEOL = true; + widthOverstrikeCaret = vsDraw.aveCharWidth; + } else { + const int widthChar = model.pdoc->LenChar(posCaret.Position()); + widthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset]; + } + if (widthOverstrikeCaret < 3) // Make sure its visible + widthOverstrikeCaret = 3; + + if (xposCaret > 0) + caretWidthOffset = 0.51f; // Move back so overlaps both character cells. + xposCaret += xStart; + if (model.posDrag.IsValid()) { + /* Dragging text, use a line caret */ + rcCaret.left = static_cast(RoundXYPosition(xposCaret - caretWidthOffset)); + rcCaret.right = rcCaret.left + vsDraw.caretWidth; + } else if (model.inOverstrike && drawOverstrikeCaret) { + /* Overstrike (insert mode), use a modified bar caret */ + rcCaret.top = rcCaret.bottom - 2; + rcCaret.left = xposCaret + 1; + rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1; + } else if ((vsDraw.caretStyle == CARETSTYLE_BLOCK) || imeCaretBlockOverride) { + /* Block caret */ + rcCaret.left = xposCaret; + if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) { + drawBlockCaret = true; + rcCaret.right = xposCaret + widthOverstrikeCaret; + } else { + rcCaret.right = xposCaret + vsDraw.aveCharWidth; + } + } else { + /* Line caret */ + rcCaret.left = static_cast(RoundXYPosition(xposCaret - caretWidthOffset)); + rcCaret.right = rcCaret.left + vsDraw.caretWidth; + } + ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour; + if (drawBlockCaret) { + DrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour); + } else { + surface->FillRectangle(rcCaret, caretColour); + } + } + } + if (drawDrag) + break; + } +} + +static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, + int xStart, PRectangle rcLine, ColourOptional background) { + // default bgnd here.. + surface->FillRectangle(rcLine, background.isSet ? background : + vsDraw.styles[STYLE_DEFAULT].back); + + if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) { + + // draw continuation rect + PRectangle rcPlace = rcLine; + + rcPlace.left = static_cast(xStart); + rcPlace.right = rcPlace.left + ll->wrapIndent; + + if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT) + rcPlace.left = rcPlace.right - vsDraw.aveCharWidth; + else + rcPlace.right = rcPlace.left + vsDraw.aveCharWidth; + + DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour()); + } +} + +void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + PRectangle rcLine, Range lineRange, int posLineStart, int xStart, + int subLine, ColourOptional background) const { + + const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn(); + bool inIndentation = subLine == 0; // Do not handle indentation except on first subline. + const XYACCUMULATOR subLineStart = ll->positions[lineRange.start]; + // Does not take margin into account but not significant + const int xStartVisible = static_cast(subLineStart)-xStart; + + BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs); + + const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet; + + // Background drawing loop + while (bfBack.More()) { + + const TextSegment ts = bfBack.Next(); + const int i = ts.end() - 1; + const int iDoc = i + posLineStart; + + PRectangle rcSegment = rcLine; + rcSegment.left = ll->positions[ts.start] + xStart - static_cast(subLineStart); + rcSegment.right = ll->positions[ts.end()] + xStart - static_cast(subLineStart); + // Only try to draw if really visible - enhances performance by not calling environment to + // draw strings that are completely past the right side of the window. + if (rcSegment.Intersects(rcLine)) { + // Clip to line rectangle, since may have a huge position which will not work with some platforms + if (rcSegment.left < rcLine.left) + rcSegment.left = rcLine.left; + if (rcSegment.right > rcLine.right) + rcSegment.right = rcLine.right; + + const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc); + const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc); + ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, + inHotspot, ll->styles[i], i); + if (ts.representation) { + if (ll->chars[i] == '\t') { + // Tab display + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) + textBack = vsDraw.whitespaceColours.back; + } else { + // Blob display + inIndentation = false; + } + surface->FillRectangle(rcSegment, textBack); + } else { + // Normal text display + surface->FillRectangle(rcSegment, textBack); + if (vsDraw.viewWhitespace != wsInvisible || + (inIndentation && vsDraw.viewIndentationGuides == ivReal)) { + for (int cpos = 0; cpos <= i - ts.start; cpos++) { + if (ll->chars[cpos + ts.start] == ' ') { + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { + PRectangle rcSpace( + ll->positions[cpos + ts.start] + xStart - static_cast(subLineStart), + rcSegment.top, + ll->positions[cpos + ts.start + 1] + xStart - static_cast(subLineStart), + rcSegment.bottom); + surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back); + } + } else { + inIndentation = false; + } + } + } + } + } else if (rcSegment.left > rcLine.right) { + break; + } + } +} + +static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine, + Range lineRange, int xStart) { + if (vsDraw.edgeState == EDGE_LINE) { + PRectangle rcSegment = rcLine; + int edgeX = static_cast(vsDraw.theEdge * vsDraw.spaceWidth); + rcSegment.left = static_cast(edgeX + xStart); + if ((ll->wrapIndent != 0) && (lineRange.start != 0)) + rcSegment.left -= ll->wrapIndent; + rcSegment.right = rcSegment.left + 1; + surface->FillRectangle(rcSegment, vsDraw.edgecolour); + } +} + +// Draw underline mark as part of background if not transparent +static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, + int line, PRectangle rcLine) { + int marks = model.pdoc->GetMark(line); + for (int markBit = 0; (markBit < 32) && marks; markBit++) { + if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) && + (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) { + PRectangle rcUnderline = rcLine; + rcUnderline.top = rcUnderline.bottom - 2; + surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back); + } + marks >>= 1; + } +} +static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, PRectangle rcLine, int subLine, Range lineRange, int xStart) { + if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) { + const int posLineStart = model.pdoc->LineStart(line); + const XYACCUMULATOR subLineStart = ll->positions[lineRange.start]; + // For each selection draw + int virtualSpaces = 0; + if (subLine == (ll->lines - 1)) { + virtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)); + } + SelectionPosition posStart(posLineStart + lineRange.start); + SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces); + SelectionSegment virtualSpaceRange(posStart, posEnd); + for (size_t r = 0; r < model.sel.Count(); r++) { + int alpha = (r == model.sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha; + if (alpha != SC_ALPHA_NOALPHA) { + SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange); + if (!portion.Empty()) { + const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; + PRectangle rcSegment = rcLine; + rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - + static_cast(subLineStart)+portion.start.VirtualSpace() * spaceWidth; + rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - + static_cast(subLineStart)+portion.end.VirtualSpace() * spaceWidth; + if ((ll->wrapIndent != 0) && (lineRange.start != 0)) { + if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1)) + rcSegment.left -= static_cast(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here + } + rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left; + rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right; + if (rcSegment.right > rcLine.left) + SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha); + } + } + } + } +} + +// Draw any translucent whole line states +static void DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, PRectangle rcLine) { + if ((model.caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) { + SimpleAlphaRectangle(surface, rcLine, vsDraw.caretLineBackground, vsDraw.caretLineAlpha); + } + int marks = model.pdoc->GetMark(line); + for (int markBit = 0; (markBit < 32) && marks; markBit++) { + if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) { + SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha); + } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) { + PRectangle rcUnderline = rcLine; + rcUnderline.top = rcUnderline.bottom - 2; + SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha); + } + marks >>= 1; + } + if (vsDraw.maskInLine) { + int marksMasked = model.pdoc->GetMark(line) & vsDraw.maskInLine; + if (marksMasked) { + for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) { + if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) { + SimpleAlphaRectangle(surface, rcLine, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha); + } + marksMasked >>= 1; + } + } + } +} + +void EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int lineVisible, PRectangle rcLine, Range lineRange, int posLineStart, int xStart, + int subLine, ColourOptional background) { + + const bool selBackDrawn = vsDraw.SelectionBackgroundDrawn(); + const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background.isSet; + bool inIndentation = subLine == 0; // Do not handle indentation except on first subline. + + const XYACCUMULATOR subLineStart = ll->positions[lineRange.start]; + const XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth; + + // Does not take margin into account but not significant + const int xStartVisible = static_cast(subLineStart)-xStart; + + // Foreground drawing loop + BreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible, + (((phasesDraw == phasesOne) && selBackDrawn) || vsDraw.selColours.fore.isSet), model.pdoc, &model.reprs); + + while (bfFore.More()) { + + const TextSegment ts = bfFore.Next(); + const int i = ts.end() - 1; + const int iDoc = i + posLineStart; + + PRectangle rcSegment = rcLine; + rcSegment.left = ll->positions[ts.start] + xStart - static_cast(subLineStart); + rcSegment.right = ll->positions[ts.end()] + xStart - static_cast(subLineStart); + // Only try to draw if really visible - enhances performance by not calling environment to + // draw strings that are completely past the right side of the window. + if (rcSegment.Intersects(rcLine)) { + int styleMain = ll->styles[i]; + ColourDesired textFore = vsDraw.styles[styleMain].fore; + FontAlias textFont = vsDraw.styles[styleMain].font; + //hotspot foreground + const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc); + if (inHotspot) { + if (vsDraw.hotspotColours.fore.isSet) + textFore = vsDraw.hotspotColours.fore; + } + const int inSelection = hideSelection ? 0 : model.sel.CharacterInSelection(iDoc); + if (inSelection && (vsDraw.selColours.fore.isSet)) { + textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground; + } + ColourDesired textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i); + if (ts.representation) { + if (ll->chars[i] == '\t') { + // Tab display + if (phasesDraw == phasesOne) { + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) + textBack = vsDraw.whitespaceColours.back; + surface->FillRectangle(rcSegment, textBack); + } + if (inIndentation && vsDraw.viewIndentationGuides == ivReal) { + for (int indentCount = static_cast((ll->positions[i] + epsilon) / indentWidth); + indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth; + indentCount++) { + if (indentCount > 0) { + int xIndent = static_cast(indentCount * indentWidth); + DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment, + (ll->xHighlightGuide == xIndent)); + } + } + } + if (vsDraw.viewWhitespace != wsInvisible) { + if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { + if (vsDraw.whitespaceColours.fore.isSet) + textFore = vsDraw.whitespaceColours.fore; + surface->PenColour(textFore); + PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4, + rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); + DrawTabArrow(surface, rcTab, static_cast(rcSegment.top + vsDraw.lineHeight / 2)); + } + } + } else { + inIndentation = false; + if (vsDraw.controlCharSymbol >= 32) { + // Using one font for all control characters so it can be controlled independently to ensure + // the box goes around the characters tightly. Seems to be no way to work out what height + // is taken by an individual character - internal leading gives varying results. + FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font; + char cc[2] = { static_cast(vsDraw.controlCharSymbol), '\0' }; + surface->DrawTextNoClip(rcSegment, ctrlCharsFont, + rcSegment.top + vsDraw.maxAscent, + cc, 1, textBack, textFore); + } else { + DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(), + textBack, textFore, phasesDraw == phasesOne); + } + } + } else { + // Normal text display + if (vsDraw.styles[styleMain].visible) { + if (phasesDraw != phasesOne) { + surface->DrawTextTransparent(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start, + i - ts.start + 1, textFore); + } else { + surface->DrawTextNoClip(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start, + i - ts.start + 1, textFore, textBack); + } + } + if (vsDraw.viewWhitespace != wsInvisible || + (inIndentation && vsDraw.viewIndentationGuides != ivNone)) { + for (int cpos = 0; cpos <= i - ts.start; cpos++) { + if (ll->chars[cpos + ts.start] == ' ') { + if (vsDraw.viewWhitespace != wsInvisible) { + if (vsDraw.whitespaceColours.fore.isSet) + textFore = vsDraw.whitespaceColours.fore; + if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { + XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2; + if ((phasesDraw == phasesOne) && drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { + textBack = vsDraw.whitespaceColours.back; + PRectangle rcSpace( + ll->positions[cpos + ts.start] + xStart - static_cast(subLineStart), + rcSegment.top, + ll->positions[cpos + ts.start + 1] + xStart - static_cast(subLineStart), + rcSegment.bottom); + surface->FillRectangle(rcSpace, textBack); + } + PRectangle rcDot(xmid + xStart - static_cast(subLineStart), + rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f); + rcDot.right = rcDot.left + vsDraw.whitespaceSize; + rcDot.bottom = rcDot.top + vsDraw.whitespaceSize; + surface->FillRectangle(rcDot, textFore); + } + } + if (inIndentation && vsDraw.viewIndentationGuides == ivReal) { + for (int indentCount = static_cast((ll->positions[cpos + ts.start] + epsilon) / indentWidth); + indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth; + indentCount++) { + if (indentCount > 0) { + int xIndent = static_cast(indentCount * indentWidth); + DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment, + (ll->xHighlightGuide == xIndent)); + } + } + } + } else { + inIndentation = false; + } + } + } + } + if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) { + PRectangle rcUL = rcSegment; + rcUL.top = rcUL.top + vsDraw.maxAscent + 1; + rcUL.bottom = rcUL.top + 1; + if (vsDraw.hotspotColours.fore.isSet) + surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore); + else + surface->FillRectangle(rcUL, textFore); + } else if (vsDraw.styles[styleMain].underline) { + PRectangle rcUL = rcSegment; + rcUL.top = rcUL.top + vsDraw.maxAscent + 1; + rcUL.bottom = rcUL.top + 1; + surface->FillRectangle(rcUL, textFore); + } + } else if (rcSegment.left > rcLine.right) { + break; + } + } +} + +void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, int lineVisible, PRectangle rcLine, int xStart, int subLine) { + if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth) + && (subLine == 0)) { + const int posLineStart = model.pdoc->LineStart(line); + int indentSpace = model.pdoc->GetLineIndentation(line); + int xStartText = static_cast(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]); + + // Find the most recent line with some text + + int lineLastWithText = line; + while (lineLastWithText > Platform::Maximum(line - 20, 0) && model.pdoc->IsWhiteLine(lineLastWithText)) { + lineLastWithText--; + } + if (lineLastWithText < line) { + xStartText = 100000; // Don't limit to visible indentation on empty line + // This line is empty, so use indentation of last line with text + int indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText); + int isFoldHeader = model.pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG; + if (isFoldHeader) { + // Level is one more level than parent + indentLastWithText += model.pdoc->IndentSize(); + } + if (vsDraw.viewIndentationGuides == ivLookForward) { + // In viLookForward mode, previous line only used if it is a fold header + if (isFoldHeader) { + indentSpace = Platform::Maximum(indentSpace, indentLastWithText); + } + } else { // viLookBoth + indentSpace = Platform::Maximum(indentSpace, indentLastWithText); + } + } + + int lineNextWithText = line; + while (lineNextWithText < Platform::Minimum(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) { + lineNextWithText++; + } + if (lineNextWithText > line) { + xStartText = 100000; // Don't limit to visible indentation on empty line + // This line is empty, so use indentation of first next line with text + indentSpace = Platform::Maximum(indentSpace, + model.pdoc->GetLineIndentation(lineNextWithText)); + } + + for (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) { + int xIndent = static_cast(indentPos * vsDraw.spaceWidth); + if (xIndent < xStartText) { + DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcLine, + (ll->xHighlightGuide == xIndent)); + } + } + } +} + +void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) { + + if (subLine >= ll->lines) { + DrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase); + return; // No further drawing + } + + // See if something overrides the line background color. + const ColourOptional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret); + + const int posLineStart = model.pdoc->LineStart(line); + + const Range lineRange = ll->SubLineRange(subLine); + const XYACCUMULATOR subLineStart = ll->positions[lineRange.start]; + + if ((ll->wrapIndent != 0) && (subLine > 0)) { + if (phase & drawBack) { + DrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background); + } + xStart += static_cast(ll->wrapIndent); + } + + if ((phasesDraw != phasesOne) && (phase & drawBack)) { + DrawBackground(surface, model, vsDraw, ll, rcLine, lineRange, posLineStart, xStart, + subLine, background); + DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end, + xStart, subLine, subLineStart, background); + } + + if (phase & drawIndicatorsBack) { + DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, true); + DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart); + DrawMarkUnderline(surface, model, vsDraw, line, rcLine); + } + + if (phase & drawText) { + DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart, + subLine, background); + } + + if (phase & drawIndentationGuides) { + DrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, lineVisible, rcLine, xStart, subLine); + } + + if (phase & drawIndicatorsFore) { + DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRange.end, false); + } + + // End of the drawing of the current line + if (phasesDraw == phasesOne) { + DrawEOL(surface, model, vsDraw, ll, rcLine, line, lineRange.end, + xStart, subLine, subLineStart, background); + } + + if (!hideSelection && (phase & drawSelectionTranslucent)) { + DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart); + } + + if (phase & drawLineTranslucent) { + DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine); + } +} + +static void DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, int line, PRectangle rcLine) { + bool expanded = model.cs.GetExpanded(line); + const int level = model.pdoc->GetLevel(line); + const int levelNext = model.pdoc->GetLevel(line + 1); + if ((level & SC_FOLDLEVELHEADERFLAG) && + ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) { + // Paint the line above the fold + if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED)) + || + (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) { + PRectangle rcFoldLine = rcLine; + rcFoldLine.bottom = rcFoldLine.top + 1; + surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore); + } + // Paint the line below the fold + if ((expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED)) + || + (!expanded && (model.foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) { + PRectangle rcFoldLine = rcLine; + rcFoldLine.top = rcFoldLine.bottom - 1; + surface->FillRectangle(rcFoldLine, vsDraw.styles[STYLE_DEFAULT].fore); + } + } +} + +void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea, + PRectangle rcClient, const ViewStyle &vsDraw) { + // Allow text at start of line to overlap 1 pixel into the margin as this displays + // serifs and italic stems for aliased text. + const int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0; + + // Do the painting + if (rcArea.right > vsDraw.textStart - leftTextOverlap) { + + Surface *surface = surfaceWindow; + if (bufferedDraw) { + surface = pixmapLine; + PLATFORM_ASSERT(pixmapLine->Initialised()); + } + surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage); + surface->SetDBCSMode(model.pdoc->dbcsCodePage); + + const Point ptOrigin = model.GetVisibleOriginInMain(); + + const int screenLinePaintFirst = static_cast(rcArea.top) / vsDraw.lineHeight; + const int xStart = vsDraw.textStart - model.xOffset + static_cast(ptOrigin.x); + + SelectionPosition posCaret = model.sel.RangeMain().caret; + if (model.posDrag.IsValid()) + posCaret = model.posDrag; + const int lineCaret = model.pdoc->LineFromPosition(posCaret.Position()); + + PRectangle rcTextArea = rcClient; + if (vsDraw.marginInside) { + rcTextArea.left += vsDraw.textStart; + rcTextArea.right -= vsDraw.rightMarginWidth; + } else { + rcTextArea = rcArea; + } + + // Remove selection margin from drawing area so text will not be drawn + // on it in unbuffered mode. + if (!bufferedDraw && vsDraw.marginInside) { + PRectangle rcClipText = rcTextArea; + rcClipText.left -= leftTextOverlap; + surfaceWindow->SetClip(rcClipText); + } + + // Loop on visible lines + //double durLayout = 0.0; + //double durPaint = 0.0; + //double durCopy = 0.0; + //ElapsedTime etWhole; + + const bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACELIGHT)) || + (vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == STYLE_BRACEBAD))); + + int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times + AutoLineLayout ll(llc, 0); + std::vector phases; + if ((phasesDraw == phasesMultiple) && !bufferedDraw) { + for (DrawPhase phase = drawBack; phase <= drawCarets; phase = static_cast(phase * 2)) { + phases.push_back(phase); + } + } else { + phases.push_back(drawAll); + } + for (std::vector::iterator it = phases.begin(); it != phases.end(); ++it) { + int ypos = 0; + if (!bufferedDraw) + ypos += screenLinePaintFirst * vsDraw.lineHeight; + int yposScreen = screenLinePaintFirst * vsDraw.lineHeight; + int visibleLine = model.TopLineOfMain() + screenLinePaintFirst; + while (visibleLine < model.cs.LinesDisplayed() && yposScreen < rcArea.bottom) { + + const int lineDoc = model.cs.DocFromDisplay(visibleLine); + // Only visible lines should be handled by the code within the loop + PLATFORM_ASSERT(model.cs.GetVisible(lineDoc)); + const int lineStartSet = model.cs.DisplayFromDoc(lineDoc); + const int subLine = visibleLine - lineStartSet; + + // Copy this line and its styles from the document into local arrays + // and determine the x position at which each character starts. + //ElapsedTime et; + if (lineDoc != lineDocPrevious) { + ll.Set(0); + ll.Set(RetrieveLineLayout(lineDoc, model)); + LayoutLine(model, lineDoc, surface, vsDraw, ll, model.wrapWidth); + lineDocPrevious = lineDoc; + } + //durLayout += et.Duration(true); + + if (ll) { + ll->containsCaret = !hideSelection && (lineDoc == lineCaret); + ll->hotspot = model.GetHotSpotRange(); + + PRectangle rcLine = rcTextArea; + rcLine.top = static_cast(ypos); + rcLine.bottom = static_cast(ypos + vsDraw.lineHeight); + + Range rangeLine(model.pdoc->LineStart(lineDoc), model.pdoc->LineStart(lineDoc + 1)); + + // Highlight the current braces if any + ll->SetBracesHighlight(rangeLine, model.braces, static_cast(model.bracesMatchStyle), + static_cast(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle); + + if (leftTextOverlap && bufferedDraw) { + PRectangle rcSpacer = rcLine; + rcSpacer.right = rcSpacer.left; + rcSpacer.left -= 1; + surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back); + } + + DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, *it); + //durPaint += et.Duration(true); + + // Restore the previous styles for the brace highlights in case layout is in cache. + ll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle); + + if (*it & drawFoldLines) { + DrawFoldLines(surface, model, vsDraw, lineDoc, rcLine); + } + + if (*it & drawCarets) { + DrawCarets(surface, model, vsDraw, ll, lineDoc, xStart, rcLine, subLine); + } + + if (bufferedDraw) { + Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0); + PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen, + static_cast(rcClient.right - vsDraw.rightMarginWidth), + yposScreen + vsDraw.lineHeight); + surfaceWindow->Copy(rcCopyArea, from, *pixmapLine); + } + + lineWidthMaxSeen = Platform::Maximum( + lineWidthMaxSeen, static_cast(ll->positions[ll->numCharsInLine])); + //durCopy += et.Duration(true); + } + + if (!bufferedDraw) { + ypos += vsDraw.lineHeight; + } + + yposScreen += vsDraw.lineHeight; + visibleLine++; + } + } + ll.Set(0); + //if (durPaint < 0.00000001) + // durPaint = 0.00000001; + + // Right column limit indicator + PRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea; + rcBeyondEOF.left = static_cast(vsDraw.textStart); + rcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0); + rcBeyondEOF.top = static_cast((model.cs.LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight); + if (rcBeyondEOF.top < rcBeyondEOF.bottom) { + surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.styles[STYLE_DEFAULT].back); + if (vsDraw.edgeState == EDGE_LINE) { + int edgeX = static_cast(vsDraw.theEdge * vsDraw.spaceWidth); + rcBeyondEOF.left = static_cast(edgeX + xStart); + rcBeyondEOF.right = rcBeyondEOF.left + 1; + surfaceWindow->FillRectangle(rcBeyondEOF, vsDraw.edgecolour); + } + } + //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset); + + //Platform::DebugPrintf( + //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n", + //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration()); + } +} + +// Space (3 space characters) between line numbers and text when printing. +#define lineNumberPrintSpace " " + +ColourDesired InvertedLight(ColourDesired orig) { + unsigned int r = orig.GetRed(); + unsigned int g = orig.GetGreen(); + unsigned int b = orig.GetBlue(); + unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye + unsigned int il = 0xff - l; + if (l == 0) + return ColourDesired(0xff, 0xff, 0xff); + r = r * il / l; + g = g * il / l; + b = b * il / l; + return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff)); +} + +long EditView::FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure, + const EditModel &model, const ViewStyle &vs) { + // Can't use measurements cached for screen + posCache.Clear(); + + ViewStyle vsPrint(vs); + vsPrint.technology = SC_TECHNOLOGY_DEFAULT; + + // Modify the view style for printing as do not normally want any of the transient features to be printed + // Printing supports only the line number margin. + int lineNumberIndex = -1; + for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) { + if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) { + lineNumberIndex = margin; + } else { + vsPrint.ms[margin].width = 0; + } + } + vsPrint.fixedColumnWidth = 0; + vsPrint.zoomLevel = printParameters.magnification; + // Don't show indentation guides + // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT + vsPrint.viewIndentationGuides = ivNone; + // Don't show the selection when printing + vsPrint.selColours.back.isSet = false; + vsPrint.selColours.fore.isSet = false; + vsPrint.selAlpha = SC_ALPHA_NOALPHA; + vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA; + vsPrint.whitespaceColours.back.isSet = false; + vsPrint.whitespaceColours.fore.isSet = false; + vsPrint.showCaretLineBackground = false; + vsPrint.alwaysShowCaretLineBackground = false; + // Don't highlight matching braces using indicators + vsPrint.braceHighlightIndicatorSet = false; + vsPrint.braceBadLightIndicatorSet = false; + + // Set colours for printing according to users settings + for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) { + if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) { + vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore); + vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back); + } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) { + vsPrint.styles[sty].fore = ColourDesired(0, 0, 0); + vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff); + } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) { + vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff); + } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) { + if (sty <= STYLE_DEFAULT) { + vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff); + } + } + } + // White background for the line numbers + vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff); + + // Printing uses different margins, so reset screen margins + vsPrint.leftMarginWidth = 0; + vsPrint.rightMarginWidth = 0; + + vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); + // Determining width must happen after fonts have been realised in Refresh + int lineNumberWidth = 0; + if (lineNumberIndex >= 0) { + lineNumberWidth = static_cast(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font, + "99999" lineNumberPrintSpace, 5 + static_cast(strlen(lineNumberPrintSpace)))); + vsPrint.ms[lineNumberIndex].width = lineNumberWidth; + vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth + } + + int linePrintStart = model.pdoc->LineFromPosition(pfr->chrg.cpMin); + int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1; + if (linePrintLast < linePrintStart) + linePrintLast = linePrintStart; + int linePrintMax = model.pdoc->LineFromPosition(pfr->chrg.cpMax); + if (linePrintLast > linePrintMax) + linePrintLast = linePrintMax; + //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n", + // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight, + // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font)); + int endPosPrint = model.pdoc->Length(); + if (linePrintLast < model.pdoc->LinesTotal()) + endPosPrint = model.pdoc->LineStart(linePrintLast + 1); + + // Ensure we are styled to where we are formatting. + model.pdoc->EnsureStyledTo(endPosPrint); + + int xStart = vsPrint.fixedColumnWidth + pfr->rc.left; + int ypos = pfr->rc.top; + + int lineDoc = linePrintStart; + + int nPrintPos = pfr->chrg.cpMin; + int visibleLine = 0; + int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth; + if (printParameters.wrapState == eWrapNone) + widthPrint = LineLayout::wrapWidthInfinite; + + while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) { + + // When printing, the hdc and hdcTarget may be the same, so + // changing the state of surfaceMeasure may change the underlying + // state of surface. Therefore, any cached state is discarded before + // using each surface. + surfaceMeasure->FlushCachedState(); + + // Copy this line and its styles from the document into local arrays + // and determine the x position at which each character starts. + LineLayout ll(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1); + LayoutLine(model, lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint); + + ll.containsCaret = false; + + PRectangle rcLine = PRectangle::FromInts( + pfr->rc.left, + ypos, + pfr->rc.right - 1, + ypos + vsPrint.lineHeight); + + // When document line is wrapped over multiple display lines, find where + // to start printing from to ensure a particular position is on the first + // line of the page. + if (visibleLine == 0) { + int startWithinLine = nPrintPos - model.pdoc->LineStart(lineDoc); + for (int iwl = 0; iwl < ll.lines - 1; iwl++) { + if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) { + visibleLine = -iwl; + } + } + + if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) { + visibleLine = -(ll.lines - 1); + } + } + + if (draw && lineNumberWidth && + (ypos + vsPrint.lineHeight <= pfr->rc.bottom) && + (visibleLine >= 0)) { + char number[100]; + sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1); + PRectangle rcNumber = rcLine; + rcNumber.right = rcNumber.left + lineNumberWidth; + // Right justify + rcNumber.left = rcNumber.right - surfaceMeasure->WidthText( + vsPrint.styles[STYLE_LINENUMBER].font, number, static_cast(strlen(number))); + surface->FlushCachedState(); + surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font, + static_cast(ypos + vsPrint.maxAscent), number, static_cast(strlen(number)), + vsPrint.styles[STYLE_LINENUMBER].fore, + vsPrint.styles[STYLE_LINENUMBER].back); + } + + // Draw the line + surface->FlushCachedState(); + + for (int iwl = 0; iwl < ll.lines; iwl++) { + if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) { + if (visibleLine >= 0) { + if (draw) { + rcLine.top = static_cast(ypos); + rcLine.bottom = static_cast(ypos + vsPrint.lineHeight); + DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, drawAll); + } + ypos += vsPrint.lineHeight; + } + visibleLine++; + if (iwl == ll.lines - 1) + nPrintPos = model.pdoc->LineStart(lineDoc + 1); + else + nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl); + } + } + + ++lineDoc; + } + + // Clear cache so measurements are not used for screen + posCache.Clear(); + + return nPrintPos; +} diff --git a/scintilla/src/EditView.h b/scintilla/src/EditView.h new file mode 100644 index 000000000..18451104f --- /dev/null +++ b/scintilla/src/EditView.h @@ -0,0 +1,163 @@ +// Scintilla source code edit control +/** @file EditView.h + ** Defines the appearance of the main text area of the editor window. + **/ +// Copyright 1998-2014 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef EDITVIEW_H +#define EDITVIEW_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +struct PrintParameters { + int magnification; + int colourMode; + WrapMode wrapState; + PrintParameters(); +}; + +/** +* The view may be drawn in separate phases. +*/ +enum DrawPhase { + drawBack = 0x1, + drawIndicatorsBack = 0x2, + drawText = 0x4, + drawIndentationGuides = 0x8, + drawIndicatorsFore = 0x10, + drawSelectionTranslucent = 0x20, + drawLineTranslucent = 0x40, + drawFoldLines = 0x80, + drawCarets = 0x100, + drawAll = 0x1FF, +}; + +bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st); +int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st); +void DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase, + const char *s, int len, DrawPhase phase); +void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText, + const StyledText &st, size_t start, size_t length, DrawPhase phase); + +/** +* EditView draws the main text area. +*/ +class EditView { +public: + PrintParameters printParameters; + PerLine *ldTabstops; + + bool hideSelection; + bool drawOverstrikeCaret; + + /** In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to + * the screen. This avoids flashing but is about 30% slower. */ + bool bufferedDraw; + /** In phasesTwo mode, drawing is performed in two phases, first the background + * and then the foreground. This avoids chopping off characters that overlap the next run. + * In multiPhaseDraw mode, drawing is performed in multiple phases with each phase drawing + * one feature over the whole drawing area, instead of within one line. This allows text to + * overlap from one line to the next. */ + enum PhasesDraw { phasesOne, phasesTwo, phasesMultiple }; + PhasesDraw phasesDraw; + + int lineWidthMaxSeen; + + bool additionalCaretsBlink; + bool additionalCaretsVisible; + + bool imeCaretBlockOverride; + + Surface *pixmapLine; + Surface *pixmapIndentGuide; + Surface *pixmapIndentGuideHighlight; + + LineLayoutCache llc; + PositionCache posCache; + + EditView(); + virtual ~EditView(); + + bool SetTwoPhaseDraw(bool twoPhaseDraw); + bool SetPhasesDraw(int phases); + bool LinesOverlap() const; + + void ClearAllTabstops(); + int NextTabstopPos(int line, int x, int tabWidth) const; + bool ClearTabstops(int line); + bool AddTabstop(int line, int x); + int GetNextTabstop(int line, int x) const; + void LinesAddedOrRemoved(int lineOfPos, int linesAdded); + + void DropGraphics(bool freeObjects); + void AllocateGraphics(const ViewStyle &vsDraw); + void RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw); + + LineLayout *RetrieveLineLayout(int lineNumber, const EditModel &model); + 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); + 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); + int DisplayFromPosition(Surface *surface, const EditModel &model, int pos, const ViewStyle &vs); + int StartEndDisplayLine(Surface *surface, const EditModel &model, int pos, bool start, const ViewStyle &vs); + + void DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight); + 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 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, + int xStart, PRectangle rcLine, int subLine) const; + void DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine, + Range lineRange, int posLineStart, int xStart, + int subLine, ColourOptional background) const; + void DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, int lineVisible, + PRectangle rcLine, Range lineRange, int posLineStart, int xStart, + int subLine, ColourOptional background); + void DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, + int line, int lineVisible, PRectangle rcLine, int xStart, int subLine); + void DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, int line, + int lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase); + void PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea, PRectangle rcClient, + const ViewStyle &vsDraw); + long FormatRange(bool draw, Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure, + const EditModel &model, const ViewStyle &vs); +}; + +/** +* Convenience class to ensure LineLayout objects are always disposed. +*/ +class AutoLineLayout { + LineLayoutCache &llc; + LineLayout *ll; + AutoLineLayout &operator=(const AutoLineLayout &); +public: + AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {} + ~AutoLineLayout() { + llc.Dispose(ll); + ll = 0; + } + LineLayout *operator->() const { + return ll; + } + operator LineLayout *() const { + return ll; + } + void Set(LineLayout *ll_) { + llc.Dispose(ll); + ll = ll_; + } +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif diff --git a/scintilla/src/Editor.cxx b/scintilla/src/Editor.cxx index db0aecf43..1c1e38d65 100644 --- a/scintilla/src/Editor.cxx +++ b/scintilla/src/Editor.cxx @@ -29,6 +29,7 @@ #include "RunStyles.h" #include "ContractionState.h" #include "CellBuffer.h" +#include "PerLine.h" #include "KeyMap.h" #include "Indicator.h" #include "XPM.h" @@ -42,6 +43,9 @@ #include "UniConversion.h" #include "Selection.h" #include "PositionCache.h" +#include "EditModel.h" +#include "MarginView.h" +#include "EditView.h" #include "Editor.h" #ifdef SCI_NAMESPACE @@ -79,21 +83,13 @@ static bool IsLastStep(const DocModification &mh) { && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0; } -Caret::Caret() : - active(false), on(false), period(500) {} - Timer::Timer() : ticking(false), ticksToWait(0), tickerID(0) {} Idler::Idler() : state(false), idlerID(0) {} -static inline bool IsControlCharacter(int ch) { - // iscntrl returns true for lots of chars > 127 which are displayable - return ch >= 0 && ch < ' '; -} - -static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) { +static inline bool IsAllSpacesOrTabs(const char *s, unsigned int len) { for (unsigned int i = 0; i < len; i++) { // This is safe because IsSpaceOrTab() will return false for null terminators if (!IsSpaceOrTab(s[i])) @@ -102,12 +98,6 @@ static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) { return true; } -PrintParameters::PrintParameters() { - magnification = 0; - colourMode = SC_PRINT_NORMAL; - wrapState = eWrapWord; -} - Editor::Editor() { ctrlID = 0; @@ -118,15 +108,9 @@ Editor::Editor() { cursorMode = SC_CURSORNORMAL; hasFocus = false; - hideSelection = false; - inOverstrike = false; - drawOverstrikeCaret = true; errorStatus = 0; mouseDownCaptures = true; - bufferedDraw = true; - twoPhaseDraw = true; - lastClickTime = 0; dwellDelay = SC_TIME_FOREVER; ticksToDwell = SC_TIME_FOREVER; @@ -135,7 +119,6 @@ Editor::Editor() { ptMouseLast.y = 0; inDragDrop = ddNone; dropWentOutside = false; - posDrag = SelectionPosition(invalidPosition); posDrop = SelectionPosition(invalidPosition); hotSpotClickPos = INVALID_POSITION; selectionType = selChar; @@ -147,8 +130,6 @@ Editor::Editor() { wordSelectAnchorEndPos = 0; wordSelectInitialCaretPos = -1; - primarySelection = true; - caretXPolicy = CARET_SLOP | CARET_EVEN; caretXSlop = 50; @@ -160,12 +141,9 @@ Editor::Editor() { searchAnchor = 0; - xOffset = 0; xCaretMargin = 50; horizontalScrollBarVisible = true; scrollWidth = 2000; - trackLineWidth = false; - lineWidthMaxSeen = 0; verticalScrollBarVisible = true; endAtLastLine = true; caretSticky = SC_CARETSTICKY_OFF; @@ -174,17 +152,8 @@ Editor::Editor() { multipleSelection = false; additionalSelectionTyping = false; multiPasteMode = SC_MULTIPASTE_ONCE; - additionalCaretsBlink = true; - additionalCaretsVisible = true; virtualSpaceOptions = SCVS_NONE; - pixmapLine = 0; - pixmapSelMargin = 0; - pixmapSelPattern = 0; - pixmapSelPatternOffset1 = 0; - pixmapIndentGuide = 0; - pixmapIndentGuideHighlight = 0; - targetStart = 0; targetEnd = 0; searchFlags = 0; @@ -196,10 +165,6 @@ Editor::Editor() { needUpdateUI = 0; ContainerNeedsUpdate(SC_UPDATE_CONTENT); - braces[0] = invalidPosition; - braces[1] = invalidPosition; - bracesMatchStyle = STYLE_BRACEBAD; - highlightGuideColumn = 0; paintState = notPainting; paintAbandonedByStyling = false; @@ -208,30 +173,18 @@ Editor::Editor() { modEventMask = SC_MODEVENTMASKALL; - pdoc = new Document(); - pdoc->AddRef(); pdoc->AddWatcher(this, 0); recordingMacro = false; - foldFlags = 0; foldAutomatic = 0; - wrapWidth = LineLayout::wrapWidthInfinite; - convertPastes = true; - hotspot = Range(invalidPosition); - - llc.SetLevel(LineLayoutCache::llcCaret); - posCache.SetSize(0x400); - SetRepresentations(); } Editor::~Editor() { pdoc->RemoveWatcher(this, 0); - pdoc->Release(); - pdoc = 0; DropGraphics(true); } @@ -284,48 +237,13 @@ void Editor::SetRepresentations() { } void Editor::DropGraphics(bool freeObjects) { - if (freeObjects) { - delete pixmapLine; - pixmapLine = 0; - delete pixmapSelMargin; - pixmapSelMargin = 0; - delete pixmapSelPattern; - pixmapSelPattern = 0; - delete pixmapSelPatternOffset1; - pixmapSelPatternOffset1 = 0; - delete pixmapIndentGuide; - pixmapIndentGuide = 0; - delete pixmapIndentGuideHighlight; - pixmapIndentGuideHighlight = 0; - } else { - if (pixmapLine) - pixmapLine->Release(); - if (pixmapSelMargin) - pixmapSelMargin->Release(); - if (pixmapSelPattern) - pixmapSelPattern->Release(); - if (pixmapSelPatternOffset1) - pixmapSelPatternOffset1->Release(); - if (pixmapIndentGuide) - pixmapIndentGuide->Release(); - if (pixmapIndentGuideHighlight) - pixmapIndentGuideHighlight->Release(); - } + marginView.DropGraphics(freeObjects); + view.DropGraphics(freeObjects); } void Editor::AllocateGraphics() { - if (!pixmapLine) - pixmapLine = Surface::Allocate(technology); - if (!pixmapSelMargin) - pixmapSelMargin = Surface::Allocate(technology); - if (!pixmapSelPattern) - pixmapSelPattern = Surface::Allocate(technology); - if (!pixmapSelPatternOffset1) - pixmapSelPatternOffset1 = Surface::Allocate(technology); - if (!pixmapIndentGuide) - pixmapIndentGuide = Surface::Allocate(technology); - if (!pixmapIndentGuideHighlight) - pixmapIndentGuideHighlight = Surface::Allocate(technology); + marginView.AllocateGraphics(vs); + view.AllocateGraphics(vs); } void Editor::InvalidateStyleData() { @@ -333,8 +251,8 @@ void Editor::InvalidateStyleData() { vs.technology = technology; DropGraphics(false); AllocateGraphics(); - llc.Invalidate(LineLayout::llInvalid); - posCache.Clear(); + view.llc.Invalidate(LineLayout::llInvalid); + view.posCache.Clear(); } void Editor::InvalidateStyleRedraw() { @@ -355,11 +273,11 @@ void Editor::RefreshStyleData() { } } -Point Editor::GetVisibleOriginInMain() { +Point Editor::GetVisibleOriginInMain() const { return Point(0,0); } -Point Editor::DocumentPointFromView(Point ptView) { +Point Editor::DocumentPointFromView(Point ptView) const { Point ptDocument = ptView; if (wMargin.GetID()) { Point ptOrigin = GetVisibleOriginInMain(); @@ -379,29 +297,30 @@ int Editor::TopLineOfMain() const { return topLine; } -PRectangle Editor::GetClientRectangle() { - return wMain.GetClientPosition(); +PRectangle Editor::GetClientRectangle() const { + Window &win = const_cast(wMain); + return win.GetClientPosition(); } PRectangle Editor::GetClientDrawingRectangle() { return GetClientRectangle(); } -PRectangle Editor::GetTextRectangle() { +PRectangle Editor::GetTextRectangle() const { PRectangle rc = GetClientRectangle(); rc.left += vs.textStart; rc.right -= vs.rightMarginWidth; return rc; } -int Editor::LinesOnScreen() { +int Editor::LinesOnScreen() const { PRectangle rcClient = GetClientRectangle(); int htClient = static_cast(rcClient.bottom - rcClient.top); //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1); return htClient / vs.lineHeight; } -int Editor::LinesToScroll() { +int Editor::LinesToScroll() const { int retVal = LinesOnScreen() - 1; if (retVal < 1) return 1; @@ -409,7 +328,7 @@ int Editor::LinesToScroll() { return retVal; } -int Editor::MaxScrollPos() { +int Editor::MaxScrollPos() const { //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n", //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1); int retVal = cs.LinesDisplayed(); @@ -425,45 +344,6 @@ int Editor::MaxScrollPos() { } } -const char *ControlCharacterString(unsigned char ch) { - const char *reps[] = { - "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", - "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", - "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", - "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" - }; - if (ch < ELEMENTS(reps)) { - return reps[ch]; - } else { - return "BAD"; - } -} - -/** - * Convenience class to ensure LineLayout objects are always disposed. - */ -class AutoLineLayout { - LineLayoutCache &llc; - LineLayout *ll; - AutoLineLayout &operator=(const AutoLineLayout &); -public: - AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {} - ~AutoLineLayout() { - llc.Dispose(ll); - ll = 0; - } - LineLayout *operator->() const { - return ll; - } - operator LineLayout *() const { - return ll; - } - void Set(LineLayout *ll_) { - llc.Dispose(ll); - ll = ll_; - } -}; - SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const { if (sp.Position() < 0) { return SelectionPosition(0); @@ -478,25 +358,9 @@ SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const } Point Editor::LocationFromPosition(SelectionPosition pos) { - Point pt; RefreshStyleData(); - if (pos.Position() == INVALID_POSITION) - return pt; - const int line = pdoc->LineFromPosition(pos.Position()); - const int lineVisible = cs.DisplayFromDoc(line); - //Platform::DebugPrintf("line=%d\n", line); AutoSurface surface(this); - AutoLineLayout ll(llc, RetrieveLineLayout(line)); - if (surface && ll) { - const int posLineStart = pdoc->LineStart(line); - LayoutLine(line, surface, vs, ll, wrapWidth); - const int posInLine = pos.Position() - posLineStart; - pt = ll->PointFromPosition(posInLine, vs.lineHeight); - pt.y += (lineVisible - topLine) * vs.lineHeight; - pt.x += vs.textStart - xOffset; - } - pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth; - return pt; + return view.LocationFromPosition(surface, *this, pos, topLine, vs); } Point Editor::LocationFromPosition(int pos) { @@ -513,20 +377,10 @@ int Editor::XFromPosition(SelectionPosition sp) { return static_cast(pt.x) - vs.textStart + xOffset; } -int Editor::LineFromLocation(Point pt) const { - return cs.DocFromDisplay(static_cast(pt.y) / vs.lineHeight + topLine); -} - -void Editor::SetTopLine(int topLineNew) { - if ((topLine != topLineNew) && (topLineNew >= 0)) { - topLine = topLineNew; - ContainerNeedsUpdate(SC_UPDATE_V_SCROLL); - } - posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine)); -} - SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) { RefreshStyleData(); + AutoSurface surface(this); + if (canReturnInvalid) { PRectangle rcClient = GetTextRectangle(); // May be in scroll view coordinates so translate back to main view @@ -540,48 +394,7 @@ SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, return SelectionPosition(INVALID_POSITION); } pt = DocumentPointFromView(pt); - pt.x = pt.x - vs.textStart; - int visibleLine = static_cast(floor(pt.y / vs.lineHeight)); - if (!canReturnInvalid && (visibleLine < 0)) - visibleLine = 0; - const int lineDoc = cs.DocFromDisplay(visibleLine); - if (canReturnInvalid && (lineDoc < 0)) - return SelectionPosition(INVALID_POSITION); - if (lineDoc >= pdoc->LinesTotal()) - return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length()); - const int posLineStart = pdoc->LineStart(lineDoc); - AutoSurface surface(this); - AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc)); - if (surface && ll) { - LayoutLine(lineDoc, surface, vs, ll, wrapWidth); - const int lineStartSet = cs.DisplayFromDoc(lineDoc); - const int subLine = visibleLine - lineStartSet; - if (subLine < ll->lines) { - const Range rangeSubLine = ll->SubLineRange(subLine); - const XYPOSITION subLineStart = ll->positions[rangeSubLine.start]; - if (subLine > 0) // Wrapped - pt.x -= ll->wrapIndent; - const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition); - if (positionInLine < rangeSubLine.end) { - return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1)); - } - if (virtualSpace) { - const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth; - const int spaceOffset = static_cast( - (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth); - return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset); - } else if (canReturnInvalid) { - if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) { - return SelectionPosition(pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1)); - } - } else { - return SelectionPosition(rangeSubLine.end + posLineStart); - } - } - if (!canReturnInvalid) - return SelectionPosition(ll->numCharsInLine + posLineStart); - } - return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart); + return view.SPositionFromLocation(surface, *this, pt, canReturnInvalid, charPosition, virtualSpace, vs); } int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) { @@ -589,38 +402,35 @@ int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosit } /** - * Find the document position corresponding to an x coordinate on a particular document line. - * Ensure is between whole characters when document is in multi-byte or UTF-8 mode. - * This method is used for rectangular selections and does not work on wrapped lines. - */ +* Find the document position corresponding to an x coordinate on a particular document line. +* Ensure is between whole characters when document is in multi-byte or UTF-8 mode. +* This method is used for rectangular selections and does not work on wrapped lines. +*/ SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) { RefreshStyleData(); if (lineDoc >= pdoc->LinesTotal()) return SelectionPosition(pdoc->Length()); //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine); AutoSurface surface(this); - AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc)); - if (surface && ll) { - const int posLineStart = pdoc->LineStart(lineDoc); - LayoutLine(lineDoc, surface, vs, ll, wrapWidth); - const Range rangeSubLine = ll->SubLineRange(0); - const XYPOSITION subLineStart = ll->positions[rangeSubLine.start]; - const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false); - if (positionInLine < rangeSubLine.end) { - return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1)); - } - const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth; - const int spaceOffset = static_cast( - (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth); - return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset); - } - return SelectionPosition(0); + return view.SPositionFromLineX(surface, *this, lineDoc, x, vs); } int Editor::PositionFromLineX(int lineDoc, int x) { return SPositionFromLineX(lineDoc, x).Position(); } +int Editor::LineFromLocation(Point pt) const { + return cs.DocFromDisplay(static_cast(pt.y) / vs.lineHeight + topLine); +} + +void Editor::SetTopLine(int topLineNew) { + if ((topLine != topLineNew) && (topLineNew >= 0)) { + topLine = topLineNew; + ContainerNeedsUpdate(SC_UPDATE_V_SCROLL); + } + posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine)); +} + /** * If painting then abandon the painting because a wider redraw is needed. * @return true if calling code should stop drawing. @@ -675,7 +485,7 @@ void Editor::RedrawSelMargin(int line, bool allAfter) { PRectangle rcSelMargin = GetClientRectangle(); rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth; if (line != -1) { - PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line))); + PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0); // Inflate line rectangle if there are image markers with height larger than line height if (vs.largestMarkerHeight > vs.lineHeight) { @@ -705,25 +515,25 @@ void Editor::RedrawSelMargin(int line, bool allAfter) { } } -PRectangle Editor::RectangleFromRange(Range r) { +PRectangle Editor::RectangleFromRange(Range r, int overlap) { const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First())); const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last())); const PRectangle rcClientDrawing = GetClientDrawingRectangle(); PRectangle rc; const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0; rc.left = static_cast(vs.textStart - leftTextOverlap); - rc.top = static_cast((minLine - TopLineOfMain()) * vs.lineHeight); + rc.top = static_cast((minLine - TopLineOfMain()) * vs.lineHeight - overlap); if (rc.top < rcClientDrawing.top) rc.top = rcClientDrawing.top; // Extend to right of prepared area if any to prevent artifacts from caret line highlight rc.right = rcClientDrawing.right; - rc.bottom = static_cast((maxLine - TopLineOfMain() + 1) * vs.lineHeight); + rc.bottom = static_cast((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap); return rc; } void Editor::InvalidateRange(int start, int end) { - RedrawRect(RectangleFromRange(Range(start, end))); + RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0)); } int Editor::CurrentPosition() const { @@ -819,7 +629,7 @@ void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition ancho SetRectangularRange(); ClaimSelection(); - if (highlightDelimiter.NeedsDrawing(currentLine)) { + if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) { RedrawSelMargin(); } QueueIdleWork(WorkNeeded::workUpdateUI); @@ -846,7 +656,7 @@ void Editor::SetSelection(SelectionPosition currentPos_) { } ClaimSelection(); - if (highlightDelimiter.NeedsDrawing(currentLine)) { + if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) { RedrawSelMargin(); } QueueIdleWork(WorkNeeded::workUpdateUI); @@ -867,7 +677,7 @@ void Editor::SetEmptySelection(SelectionPosition currentPos_) { SetRectangularRange(); ClaimSelection(); - if (highlightDelimiter.NeedsDrawing(currentLine)) { + if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) { RedrawSelMargin(); } QueueIdleWork(WorkNeeded::workUpdateUI); @@ -977,7 +787,7 @@ int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, b } } - if (highlightDelimiter.NeedsDrawing(currentLine)) { + if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) { RedrawSelMargin(); } return 0; @@ -1174,22 +984,8 @@ void Editor::MoveCaretInsideView(bool ensureVisible) { } int Editor::DisplayFromPosition(int pos) { - int lineDoc = pdoc->LineFromPosition(pos); - int lineDisplay = cs.DisplayFromDoc(lineDoc); AutoSurface surface(this); - AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc)); - if (surface && ll) { - LayoutLine(lineDoc, surface, vs, ll, wrapWidth); - unsigned int posLineStart = pdoc->LineStart(lineDoc); - int posInLine = pos - posLineStart; - lineDisplay--; // To make up for first increment ahead. - for (int subLine = 0; subLine < ll->lines; subLine++) { - if (posInLine >= ll->LineStart(subLine)) { - lineDisplay++; - } - } - } - return lineDisplay; + return view.DisplayFromPosition(surface, *this, pos, vs); } /** @@ -1451,7 +1247,7 @@ Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &ran newXY.xOffset = static_cast(pt.x + xOffset - rcClient.left) - 2; } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) { newXY.xOffset = static_cast(pt.x + xOffset - rcClient.right) + 2; - if (vs.caretStyle == CARETSTYLE_BLOCK) { + if ((vs.caretStyle == CARETSTYLE_BLOCK) || view.imeCaretBlockOverride) { // Ensure we can see a good portion of the block caret newXY.xOffset += static_cast(vs.aveCharWidth); } @@ -1516,16 +1312,26 @@ void Editor::ShowCaretAtCurrentPosition() { if (hasFocus) { caret.active = true; caret.on = true; - SetTicking(true); + if (FineTickerAvailable()) { + FineTickerCancel(tickCaret); + if (caret.period > 0) + FineTickerStart(tickCaret, caret.period, caret.period/10); + } else { + SetTicking(true); + } } else { caret.active = false; caret.on = false; + if (FineTickerAvailable()) { + FineTickerCancel(tickCaret); + } } InvalidateCaret(); } void Editor::DropCaret() { caret.active = false; + FineTickerCancel(tickCaret); InvalidateCaret(); } @@ -1533,6 +1339,11 @@ void Editor::CaretSetPeriod(int period) { if (caret.period != period) { caret.period = period; caret.on = true; + if (FineTickerAvailable()) { + FineTickerCancel(tickCaret); + if ((caret.active) && (caret.period > 0)) + FineTickerStart(tickCaret, caret.period, caret.period/10); + } InvalidateCaret(); } } @@ -1558,7 +1369,7 @@ bool Editor::Wrapping() const { void Editor::NeedWrapping(int docLineStart, int docLineEnd) { //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd); if (wrapPending.AddRange(docLineStart, docLineEnd)) { - llc.Invalidate(LineLayout::llPositions); + view.llc.Invalidate(LineLayout::llPositions); } // Wrap lines during idle. if (Wrapping() && wrapPending.NeedsWrap()) { @@ -1567,10 +1378,10 @@ void Editor::NeedWrapping(int docLineStart, int docLineEnd) { } bool Editor::WrapOneLine(Surface *surface, int lineToWrap) { - AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap)); + AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this)); int linesWrapped = 1; if (ll) { - LayoutLine(lineToWrap, surface, vs, ll, wrapWidth); + view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth); linesWrapped = ll->lines; } return cs.SetHeight(lineToWrap, linesWrapped + @@ -1644,1785 +1455,152 @@ bool Editor::WrapLines(enum wrapScope ws) { if (surface) { //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd); - while (lineToWrap < lineToWrapEnd) { - if (WrapOneLine(surface, lineToWrap)) { - wrapOccurred = true; - } - wrapPending.Wrapped(lineToWrap); - lineToWrap++; - } - - goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1); - } - } - - // If wrapping is done, bring it to resting position - if (wrapPending.start >= lineEndNeedWrap) { - wrapPending.Reset(); - } - } - - if (wrapOccurred) { - SetScrollBars(); - SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos())); - SetVerticalScrollPos(); - } - - return wrapOccurred; -} - -void Editor::LinesJoin() { - if (!RangeContainsProtected(targetStart, targetEnd)) { - UndoGroup ug(pdoc); - bool prevNonWS = true; - for (int pos = targetStart; pos < targetEnd; pos++) { - if (pdoc->IsPositionInLineEnd(pos)) { - targetEnd -= pdoc->LenChar(pos); - pdoc->DelChar(pos); - if (prevNonWS) { - // Ensure at least one space separating previous lines - const int lengthInserted = pdoc->InsertString(pos, " ", 1); - targetEnd += lengthInserted; - } - } else { - prevNonWS = pdoc->CharAt(pos) != ' '; - } - } - } -} - -const char *Editor::StringFromEOLMode(int eolMode) { - if (eolMode == SC_EOL_CRLF) { - return "\r\n"; - } else if (eolMode == SC_EOL_CR) { - return "\r"; - } else { - return "\n"; - } -} - -void Editor::LinesSplit(int pixelWidth) { - if (!RangeContainsProtected(targetStart, targetEnd)) { - if (pixelWidth == 0) { - PRectangle rcText = GetTextRectangle(); - pixelWidth = static_cast(rcText.Width()); - } - int lineStart = pdoc->LineFromPosition(targetStart); - int lineEnd = pdoc->LineFromPosition(targetEnd); - const char *eol = StringFromEOLMode(pdoc->eolMode); - UndoGroup ug(pdoc); - for (int line = lineStart; line <= lineEnd; line++) { - AutoSurface surface(this); - AutoLineLayout ll(llc, RetrieveLineLayout(line)); - if (surface && ll) { - unsigned int posLineStart = pdoc->LineStart(line); - LayoutLine(line, surface, vs, ll, pixelWidth); - int lengthInsertedTotal = 0; - for (int subLine = 1; subLine < ll->lines; subLine++) { - const int lengthInserted = pdoc->InsertString( - static_cast(posLineStart + lengthInsertedTotal + - ll->LineStart(subLine)), - eol, istrlen(eol)); - targetEnd += lengthInserted; - lengthInsertedTotal += lengthInserted; - } - } - lineEnd = pdoc->LineFromPosition(targetEnd); - } - } -} - -int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) const { - if (vs.markers[markerCheck].markType == SC_MARK_EMPTY) - return markerDefault; - return markerCheck; -} - -bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) { - if (st.multipleStyles) { - for (size_t iStyle=0; iStyle(styles[endSegment+1]) == style)) - endSegment++; - FontAlias fontText = vs.styles[style + styleOffset].font; - width += static_cast(surface->WidthText(fontText, text + start, - static_cast(endSegment - start + 1))); - start = endSegment + 1; - } - return width; -} - -static int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) { - int widthMax = 0; - size_t start = 0; - while (start < st.length) { - size_t lenLine = st.LineLength(start); - int widthSubLine; - if (st.multipleStyles) { - widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine); - } else { - FontAlias fontText = vs.styles[styleOffset + st.style].font; - widthSubLine = static_cast(surface->WidthText(fontText, - st.text + start, static_cast(lenLine))); - } - if (widthSubLine > widthMax) - widthMax = widthSubLine; - start += lenLine + 1; - } - return widthMax; -} - -static void DrawTextInStyle(Surface *surface, PRectangle rcText, const Style &style, XYPOSITION ybase, const char *s, size_t length) { - FontAlias fontText = style.font; - surface->DrawTextNoClip(rcText, fontText, ybase, s, static_cast(length), - style.fore, style.back); -} - -static void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText, - const StyledText &st, size_t start, size_t length) { - - if (st.multipleStyles) { - int x = static_cast(rcText.left); - size_t i = 0; - while (i < length) { - size_t end = i; - size_t style = st.styles[i + start]; - while (end < length-1 && st.styles[start+end+1] == style) - end++; - style += styleOffset; - FontAlias fontText = vs.styles[style].font; - const int width = static_cast(surface->WidthText(fontText, - st.text + start + i, static_cast(end - i + 1))); - PRectangle rcSegment = rcText; - rcSegment.left = static_cast(x); - rcSegment.right = static_cast(x + width + 1); - DrawTextInStyle(surface, rcSegment, vs.styles[style], rcText.top + vs.maxAscent, - st.text + start + i, end - i + 1); - x += width; - i = end + 1; - } - } else { - const size_t style = st.style + styleOffset; - DrawTextInStyle(surface, rcText, vs.styles[style], rcText.top + vs.maxAscent, - st.text + start, length); - } -} - -void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) { - if (vs.fixedColumnWidth == 0) - return; - - AllocateGraphics(); - RefreshStyleData(); - RefreshPixMaps(surfWindow); - - // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished - // at this point. The Initialised call checks for this case and sets the status - // to be bad which avoids crashes in following calls. - if (!surfWindow->Initialised()) { - return; - } - - PRectangle rcMargin = GetClientRectangle(); - Point ptOrigin = GetVisibleOriginInMain(); - rcMargin.Move(0, -ptOrigin.y); - rcMargin.left = 0; - rcMargin.right = static_cast(vs.fixedColumnWidth); - - if (!rc.Intersects(rcMargin)) - return; - - Surface *surface; - if (bufferedDraw) { - surface = pixmapSelMargin; - } else { - surface = surfWindow; - } - - // Clip vertically to paint area to avoid drawing line numbers - if (rcMargin.bottom > rc.bottom) - rcMargin.bottom = rc.bottom; - if (rcMargin.top < rc.top) - rcMargin.top = rc.top; - - PRectangle rcSelMargin = rcMargin; - rcSelMargin.right = rcMargin.left; - if (rcSelMargin.bottom < rc.bottom) - rcSelMargin.bottom = rc.bottom; - - for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) { - if (vs.ms[margin].width > 0) { - - rcSelMargin.left = rcSelMargin.right; - rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width; - - if (vs.ms[margin].style != SC_MARGIN_NUMBER) { - if (vs.ms[margin].mask & SC_MASK_FOLDERS) { - // Required because of special way brush is created for selection margin - // Ensure patterns line up when scrolling with separate margin view - // by choosing correctly aligned variant. - bool invertPhase = static_cast(ptOrigin.y) & 1; - surface->FillRectangle(rcSelMargin, - invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1); - } else { - ColourDesired colour; - switch (vs.ms[margin].style) { - case SC_MARGIN_BACK: - colour = vs.styles[STYLE_DEFAULT].back; - break; - case SC_MARGIN_FORE: - colour = vs.styles[STYLE_DEFAULT].fore; - break; - default: - colour = vs.styles[STYLE_LINENUMBER].back; - break; - } - surface->FillRectangle(rcSelMargin, colour); - } - } else { - surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back); - } - - const int lineStartPaint = static_cast(rcMargin.top + ptOrigin.y) / vs.lineHeight; - int visibleLine = TopLineOfMain() + lineStartPaint; - int yposScreen = lineStartPaint * vs.lineHeight - static_cast(ptOrigin.y); - // Work out whether the top line is whitespace located after a - // lessening of fold level which implies a 'fold tail' but which should not - // be displayed until the last of a sequence of whitespace. - bool needWhiteClosure = false; - if (vs.ms[margin].mask & SC_MASK_FOLDERS) { - int level = pdoc->GetLevel(cs.DocFromDisplay(visibleLine)); - if (level & SC_FOLDLEVELWHITEFLAG) { - int lineBack = cs.DocFromDisplay(visibleLine); - int levelPrev = level; - while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) { - lineBack--; - levelPrev = pdoc->GetLevel(lineBack); - } - if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) { - if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK)) - needWhiteClosure = true; - } - } - if (highlightDelimiter.isEnabled) { - int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1; - pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine); - } - } - - // Old code does not know about new markers needed to distinguish all cases - const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID, - SC_MARKNUM_FOLDEROPEN); - const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND, - SC_MARKNUM_FOLDER); - - while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rc.bottom) { - - PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed()); - const int lineDoc = cs.DocFromDisplay(visibleLine); - PLATFORM_ASSERT(cs.GetVisible(lineDoc)); - const bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc); - const bool lastSubLine = visibleLine == cs.DisplayLastFromDoc(lineDoc); - - int marks = pdoc->GetMark(lineDoc); - if (!firstSubLine) - marks = 0; - - bool headWithTail = false; - - if (vs.ms[margin].mask & SC_MASK_FOLDERS) { - // Decide which fold indicator should be displayed - const int level = pdoc->GetLevel(lineDoc); - const int levelNext = pdoc->GetLevel(lineDoc + 1); - const int levelNum = level & SC_FOLDLEVELNUMBERMASK; - const int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK; - if (level & SC_FOLDLEVELHEADERFLAG) { - if (firstSubLine) { - if (levelNum < levelNextNum) { - if (cs.GetExpanded(lineDoc)) { - if (levelNum == SC_FOLDLEVELBASE) - marks |= 1 << SC_MARKNUM_FOLDEROPEN; - else - marks |= 1 << folderOpenMid; - } else { - if (levelNum == SC_FOLDLEVELBASE) - marks |= 1 << SC_MARKNUM_FOLDER; - else - marks |= 1 << folderEnd; - } - } else if (levelNum > SC_FOLDLEVELBASE) { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - } - } else { - if (levelNum < levelNextNum) { - if (cs.GetExpanded(lineDoc)) { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - } else if (levelNum > SC_FOLDLEVELBASE) { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - } - } else if (levelNum > SC_FOLDLEVELBASE) { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - } - } - needWhiteClosure = false; - const int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1)); - const int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine); - const int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK; - if (!cs.GetExpanded(lineDoc)) { - if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) && - (levelNum > secondFollowupLineLevelNum)) - needWhiteClosure = true; - - if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine)) - headWithTail = true; - } - } else if (level & SC_FOLDLEVELWHITEFLAG) { - if (needWhiteClosure) { - if (levelNext & SC_FOLDLEVELWHITEFLAG) { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - } else if (levelNextNum > SC_FOLDLEVELBASE) { - marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; - needWhiteClosure = false; - } else { - marks |= 1 << SC_MARKNUM_FOLDERTAIL; - needWhiteClosure = false; - } - } else if (levelNum > SC_FOLDLEVELBASE) { - if (levelNextNum < levelNum) { - if (levelNextNum > SC_FOLDLEVELBASE) { - marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; - } else { - marks |= 1 << SC_MARKNUM_FOLDERTAIL; - } - } else { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - } - } - } else if (levelNum > SC_FOLDLEVELBASE) { - if (levelNextNum < levelNum) { - needWhiteClosure = false; - if (levelNext & SC_FOLDLEVELWHITEFLAG) { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - needWhiteClosure = true; - } else if (lastSubLine) { - if (levelNextNum > SC_FOLDLEVELBASE) { - marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; - } else { - marks |= 1 << SC_MARKNUM_FOLDERTAIL; - } - } else { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - } - } else { - marks |= 1 << SC_MARKNUM_FOLDERSUB; - } - } - } - - marks &= vs.ms[margin].mask; - - PRectangle rcMarker = rcSelMargin; - rcMarker.top = static_cast(yposScreen); - rcMarker.bottom = static_cast(yposScreen + vs.lineHeight); - if (vs.ms[margin].style == SC_MARGIN_NUMBER) { - if (firstSubLine) { - char number[100] = ""; - if (lineDoc >= 0) - sprintf(number, "%d", lineDoc + 1); - if (foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) { - if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) { - int lev = pdoc->GetLevel(lineDoc); - sprintf(number, "%c%c %03X %03X", - (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_', - (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_', - lev & SC_FOLDLEVELNUMBERMASK, - lev >> 16 - ); - } else { - int state = pdoc->GetLineState(lineDoc); - sprintf(number, "%0X", state); - } - } - PRectangle rcNumber = rcMarker; - // Right justify - XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number)); - XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding; - rcNumber.left = xpos; - DrawTextInStyle(surface, rcNumber, vs.styles[STYLE_LINENUMBER], - rcNumber.top + vs.maxAscent, number, strlen(number)); - } else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) { - PRectangle rcWrapMarker = rcMarker; - rcWrapMarker.right -= 3; - rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth; - DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore); - } - } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) { - if (firstSubLine) { - const StyledText stMargin = pdoc->MarginStyledText(lineDoc); - if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) { - surface->FillRectangle(rcMarker, - vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back); - if (vs.ms[margin].style == SC_MARGIN_RTEXT) { - int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin); - rcMarker.left = rcMarker.right - width - 3; - } - DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, - stMargin, 0, stMargin.length); - } - } - } - - if (marks) { - for (int markBit = 0; (markBit < 32) && marks; markBit++) { - if (marks & 1) { - LineMarker::typeOfFold tFold = LineMarker::undefined; - if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) { - if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) { - tFold = LineMarker::body; - } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) { - if (firstSubLine) { - tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head; - } else { - if (cs.GetExpanded(lineDoc) || headWithTail) { - tFold = LineMarker::body; - } else { - tFold = LineMarker::undefined; - } - } - } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) { - tFold = LineMarker::tail; - } - } - vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style); - } - marks >>= 1; - } - } - - visibleLine++; - yposScreen += vs.lineHeight; - } - } - } - - PRectangle rcBlankMargin = rcMargin; - rcBlankMargin.left = rcSelMargin.right; - surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back); - - if (bufferedDraw) { - surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin); - } -} - -void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) { - 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; - } - 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); -} - -LineLayout *Editor::RetrieveLineLayout(int lineNumber) { - int posLineStart = pdoc->LineStart(lineNumber); - int posLineEnd = pdoc->LineStart(lineNumber + 1); - PLATFORM_ASSERT(posLineEnd >= posLineStart); - int lineCaret = pdoc->LineFromPosition(sel.MainCaret()); - return llc.Retrieve(lineNumber, lineCaret, - posLineEnd - posLineStart, pdoc->GetStyleClock(), - LinesOnScreen() + 1, pdoc->LinesTotal()); -} - -/** - * Fill in the LineLayout data for the given line. - * Copy the given @a line and its styles from the document into local arrays. - * Also determine the x position at which each character starts. - */ -void Editor::LayoutLine(int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) { - if (!ll) - return; - - PLATFORM_ASSERT(line < pdoc->LinesTotal()); - PLATFORM_ASSERT(ll->chars != NULL); - int posLineStart = pdoc->LineStart(line); - int posLineEnd = pdoc->LineStart(line + 1); - // If the line is very long, limit the treatment to a length that should fit in the viewport - if (posLineEnd > (posLineStart + ll->maxLineLength)) { - posLineEnd = posLineStart + ll->maxLineLength; - } - if (ll->validity == LineLayout::llCheckTextAndStyle) { - int lineLength = posLineEnd - posLineStart; - if (!vstyle.viewEOL) { - lineLength = pdoc->LineEnd(line) - posLineStart; - } - if (lineLength == ll->numCharsInLine) { - // See if chars, styles, indicators, are all the same - bool allSame = true; - // Check base line layout - char styleByte = 0; - int numCharsInLine = 0; - while (numCharsInLine < lineLength) { - int charInDoc = numCharsInLine + posLineStart; - char chDoc = pdoc->CharAt(charInDoc); - styleByte = pdoc->StyleAt(charInDoc); - allSame = allSame && - (ll->styles[numCharsInLine] == static_cast(styleByte)); - if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed) - allSame = allSame && - (ll->chars[numCharsInLine] == chDoc); - else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower) - allSame = allSame && - (ll->chars[numCharsInLine] == static_cast(tolower(chDoc))); - else // Style::caseUpper - allSame = allSame && - (ll->chars[numCharsInLine] == static_cast(toupper(chDoc))); - numCharsInLine++; - } - allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled - if (allSame) { - ll->validity = LineLayout::llPositions; - } else { - ll->validity = LineLayout::llInvalid; - } - } else { - ll->validity = LineLayout::llInvalid; - } - } - if (ll->validity == LineLayout::llInvalid) { - ll->widthLine = LineLayout::wrapWidthInfinite; - ll->lines = 1; - if (vstyle.edgeState == EDGE_BACKGROUND) { - ll->edgeColumn = pdoc->FindColumn(line, vstyle.theEdge); - if (ll->edgeColumn >= posLineStart) { - ll->edgeColumn -= posLineStart; - } - } else { - ll->edgeColumn = -1; - } - - // Fill base line layout - const int lineLength = posLineEnd - posLineStart; - pdoc->GetCharRange(ll->chars, posLineStart, lineLength); - pdoc->GetStyleRange(ll->styles, posLineStart, lineLength); - int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart; - const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL; - for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) { - const unsigned char styleByte = ll->styles[styleInLine]; - ll->styles[styleInLine] = styleByte; - } - const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength-1] : 0; - if (vstyle.someStylesForceCase) { - for (int charInLine = 0; charInLinechars[charInLine]; - if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper) - ll->chars[charInLine] = static_cast(toupper(chDoc)); - else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower) - ll->chars[charInLine] = static_cast(tolower(chDoc)); - } - } - ll->xHighlightGuide = 0; - // Extra element at the end of the line to hold end x position and act as - ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character - ll->styles[numCharsInLine] = styleByteLast; // For eolFilled - - // Layout the line, determining the position of each character, - // with an extra element at the end for the end of the line. - ll->positions[0] = 0; - bool lastSegItalics = false; - - BreakFinder bfLayout(ll, NULL, 0, numCharsInLine, posLineStart, 0, false, pdoc, &reprs); - while (bfLayout.More()) { - - const TextSegment ts = bfLayout.Next(); - - std::fill(&ll->positions[ts.start+1], &ll->positions[ts.end()+1], 0.0f); - if (vstyle.styles[ll->styles[ts.start]].visible) { - if (ts.representation) { - XYPOSITION representationWidth = vstyle.controlCharWidth; - if (ll->chars[ts.start] == '\t') { - // Tab is a special case of representation, taking a variable amount of space - representationWidth = - ((static_cast((ll->positions[ts.start] + 2) / vstyle.tabWidth) + 1) * vstyle.tabWidth) - ll->positions[ts.start]; - } else { - if (representationWidth <= 0.0) { - XYPOSITION positionsRepr[256]; // Should expand when needed - posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(), - static_cast(ts.representation->stringRep.length()), positionsRepr, pdoc); - representationWidth = positionsRepr[ts.representation->stringRep.length()-1] + vstyle.ctrlCharPadding; - } - } - for (int ii=0; ii < ts.length; ii++) - ll->positions[ts.start + 1 + ii] = representationWidth; - } else { - if ((ts.length == 1) && (' ' == ll->chars[ts.start])) { - // Over half the segments are single characters and of these about half are space characters. - ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth; - } else { - posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start, - ts.length, ll->positions + ts.start + 1, pdoc); - } - } - lastSegItalics = (!ts.representation) && ((ll->chars[ts.end()-1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic); - } - - for (int posToIncrease = ts.start+1; posToIncrease <= ts.end(); posToIncrease++) { - ll->positions[posToIncrease] += ll->positions[ts.start]; - } - } - - // Small hack to make lines that end with italics not cut off the edge of the last character - if (lastSegItalics) { - ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset; - } - ll->numCharsInLine = numCharsInLine; - ll->numCharsBeforeEOL = numCharsBeforeEOL; - ll->validity = LineLayout::llPositions; - } - // Hard to cope when too narrow, so just assume there is space - if (width < 20) { - width = 20; - } - if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) { - ll->widthLine = width; - if (width == LineLayout::wrapWidthInfinite) { - ll->lines = 1; - } else if (width > ll->positions[ll->numCharsInLine]) { - // Simple common case where line does not need wrapping. - ll->lines = 1; - } else { - if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) { - width -= static_cast(vstyle.aveCharWidth); // take into account the space for end wrap mark - } - XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line - if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) { - wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth; - } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) { - wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth; - } - ll->wrapIndent = wrapAddIndent; - if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED) - for (int i = 0; i < ll->numCharsInLine; i++) { - if (!IsSpaceOrTab(ll->chars[i])) { - ll->wrapIndent += ll->positions[i]; // Add line indent - break; - } - } - // Check for text width minimum - if (ll->wrapIndent > width - static_cast(vstyle.aveCharWidth) * 15) - ll->wrapIndent = wrapAddIndent; - // Check for wrapIndent minimum - if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth)) - ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual - ll->lines = 0; - // Calculate line start positions based upon width. - int lastGoodBreak = 0; - int lastLineStart = 0; - XYACCUMULATOR startOffset = 0; - int p = 0; - while (p < ll->numCharsInLine) { - if ((ll->positions[p + 1] - startOffset) >= width) { - if (lastGoodBreak == lastLineStart) { - // Try moving to start of last character - if (p > 0) { - lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1) - - posLineStart; - } - if (lastGoodBreak == lastLineStart) { - // Ensure at least one character on line. - lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1) - - posLineStart; - } - } - lastLineStart = lastGoodBreak; - ll->lines++; - ll->SetLineStart(ll->lines, lastGoodBreak); - startOffset = ll->positions[lastGoodBreak]; - // take into account the space for start wrap mark and indent - startOffset -= ll->wrapIndent; - p = lastGoodBreak + 1; - continue; - } - if (p > 0) { - if (vstyle.wrapState == eWrapChar) { - lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1) - - posLineStart; - p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart; - continue; - } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) { - lastGoodBreak = p; - } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) { - lastGoodBreak = p; - } - } - p++; - } - ll->lines++; - } - ll->validity = LineLayout::llLines; - } -} - -ColourDesired Editor::SelectionBackground(const ViewStyle &vsDraw, bool main) const { - return main ? - (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) : - vsDraw.selAdditionalBackground; -} - -ColourDesired Editor::TextBackground(const ViewStyle &vsDraw, - ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) const { - if (inSelection == 1) { - if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) { - return SelectionBackground(vsDraw, true); - } - } else if (inSelection == 2) { - if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) { - return SelectionBackground(vsDraw, false); - } - } else { - if ((vsDraw.edgeState == EDGE_BACKGROUND) && - (i >= ll->edgeColumn) && - (i < ll->numCharsBeforeEOL)) - return vsDraw.edgecolour; - if (inHotspot && vsDraw.hotspotColours.back.isSet) - return vsDraw.hotspotColours.back; - } - if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) { - return background; - } else { - return vsDraw.styles[styleMain].back; - } -} - -void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) { - Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0); - PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast(rcSegment.top), start + 2, static_cast(rcSegment.bottom)); - surface->Copy(rcCopyArea, from, - highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide); -} - -void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace, - bool isEndMarker, ColourDesired wrapColour) { - surface->PenColour(wrapColour); - - enum { xa = 1 }; // gap before start - int w = static_cast(rcPlace.right - rcPlace.left) - xa - 1; - - bool xStraight = isEndMarker; // x-mirrored symbol for start marker - - int x0 = static_cast(xStraight ? rcPlace.left : rcPlace.right - 1); - int y0 = static_cast(rcPlace.top); - - int dy = static_cast(rcPlace.bottom - rcPlace.top) / 5; - int y = static_cast(rcPlace.bottom - rcPlace.top) / 2 + dy; - - struct Relative { - Surface *surface; - int xBase; - int xDir; - int yBase; - int yDir; - void MoveTo(int xRelative, int yRelative) { - surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative); - } - void LineTo(int xRelative, int yRelative) { - surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative); - } - }; - Relative rel = {surface, x0, xStraight ? 1 : -1, y0, 1}; - - // arrow head - rel.MoveTo(xa, y); - rel.LineTo(xa + 2*w / 3, y - dy); - rel.MoveTo(xa, y); - rel.LineTo(xa + 2*w / 3, y + dy); - - // arrow body - rel.MoveTo(xa, y); - rel.LineTo(xa + w, y); - rel.LineTo(xa + w, y - 2 * dy); - rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not... - y - 2 * dy); -} - -static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) { - if (alpha != SC_ALPHA_NOALPHA) { - surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0); - } -} - -void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment, - const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) { - if (!twoPhaseDraw) { - surface->FillRectangle(rcSegment, textBack); - } - FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font; - int normalCharHeight = static_cast(surface->Ascent(ctrlCharsFont) - - surface->InternalLeading(ctrlCharsFont)); - PRectangle rcCChar = rcSegment; - rcCChar.left = rcCChar.left + 1; - rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight; - rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1; - PRectangle rcCentral = rcCChar; - rcCentral.top++; - rcCentral.bottom--; - surface->FillRectangle(rcCentral, textFore); - PRectangle rcChar = rcCChar; - rcChar.left++; - rcChar.right--; - surface->DrawTextClipped(rcChar, ctrlCharsFont, - rcSegment.top + vsDraw.maxAscent, s, istrlen(s), - textBack, textFore); -} - -void Editor::DrawEOL(Surface *surface, const ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll, - int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart, - ColourOptional background) { - - const int posLineStart = pdoc->LineStart(line); - PRectangle rcSegment = rcLine; - - const bool lastSubLine = subLine == (ll->lines - 1); - XYPOSITION virtualSpace = 0; - if (lastSubLine) { - const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; - virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth; - } - XYPOSITION xEol = static_cast(ll->positions[lineEnd] - subLineStart); - - // Fill the virtual space and show selections within it - if (virtualSpace) { - rcSegment.left = xEol + xStart; - rcSegment.right = xEol + xStart + virtualSpace; - surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back); - if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) { - SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line)))); - for (size_t r=0; rEndLineStyle()].spaceWidth; - rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - - static_cast(subLineStart) + portion.start.VirtualSpace() * spaceWidth; - rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - - static_cast(subLineStart) + portion.end.VirtualSpace() * spaceWidth; - rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left; - rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right; - surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main())); - } - } - } - } - } - - int eolInSelection = 0; - int alpha = SC_ALPHA_NOALPHA; - if (!hideSelection) { - int posAfterLineEnd = pdoc->LineStart(line + 1); - eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0; - alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha; - } - - // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on - XYPOSITION blobsWidth = 0; - if (lastSubLine) { - for (int eolPos=ll->numCharsBeforeEOL; eolPosnumCharsInLine; eolPos++) { - rcSegment.left = xStart + ll->positions[eolPos] - static_cast(subLineStart) + virtualSpace; - rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast(subLineStart) + virtualSpace; - blobsWidth += rcSegment.Width(); - char hexits[4]; - const char *ctrlChar; - unsigned char chEOL = ll->chars[eolPos]; - int styleMain = ll->styles[eolPos]; - ColourDesired textBack = TextBackground(vsDraw, background, eolInSelection, false, styleMain, eolPos, ll); - if (UTF8IsAscii(chEOL)) { - ctrlChar = ControlCharacterString(chEOL); - } else { - const Representation *repr = reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos); - if (repr) { - ctrlChar = repr->stringRep.c_str(); - eolPos = ll->numCharsInLine; - } else { - sprintf(hexits, "x%2X", chEOL); - ctrlChar = hexits; - } - } - ColourDesired textFore = vsDraw.styles[styleMain].fore; - if (eolInSelection && vsDraw.selColours.fore.isSet) { - textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground; - } - if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1)) { - if (alpha == SC_ALPHA_NOALPHA) { - surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1)); - } else { - surface->FillRectangle(rcSegment, textBack); - } - } else { - surface->FillRectangle(rcSegment, textBack); - } - DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw); - if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) { - SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha); - } - } - } - - // Draw the eol-is-selected rectangle - rcSegment.left = xEol + xStart + virtualSpace + blobsWidth; - rcSegment.right = rcSegment.left + vsDraw.aveCharWidth; - - if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) { - surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1)); - } else { - if (background.isSet) { - surface->FillRectangle(rcSegment, background); - } else if (line < pdoc->LinesTotal() - 1) { - surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back); - } 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.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) { - SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha); - } - } - - // 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 < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) { - surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1)); - } 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 < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) { - SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha); - } - } - - bool drawWrapMarkEnd = false; - - if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) { - if (subLine + 1 < ll->lines) { - drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0; - } - } - - if (drawWrapMarkEnd) { - PRectangle rcPlace = rcSegment; - - if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) { - rcPlace.left = xEol + xStart + virtualSpace; - rcPlace.right = rcPlace.left + vsDraw.aveCharWidth; - } else { - // rcLine is clipped to text area - rcPlace.right = rcLine.right; - rcPlace.left = rcPlace.right - vsDraw.aveCharWidth; - } - DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour()); - } -} - -void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw, - int xStart, PRectangle rcLine, LineLayout *ll, int subLine) { - 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); -} - -void Editor::DrawIndicators(Surface *surface, const ViewStyle &vsDraw, int line, int xStart, - PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) { - // Draw decorators - const int posLineStart = pdoc->LineStart(line); - const int lineStart = ll->LineStart(subLine); - const int posLineEnd = posLineStart + lineEnd; - - for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) { - if (under == vsDraw.indicators[deco->indicator].under) { - int startPos = posLineStart + lineStart; - if (!deco->rs.ValueAt(startPos)) { - startPos = deco->rs.EndRun(startPos); - } - while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) { - int endPos = deco->rs.EndRun(startPos); - if (endPos > posLineEnd) - endPos = posLineEnd; - DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart, - surface, vsDraw, xStart, rcLine, ll, subLine); - startPos = endPos; - if (!deco->rs.ValueAt(startPos)) { - startPos = deco->rs.EndRun(startPos); - } - } - } - } - - // Use indicators to highlight matching braces - if ((vsDraw.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) || - (vsDraw.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) { - int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator; - if (under == vsDraw.indicators[braceIndicator].under) { - Range rangeLine(posLineStart + lineStart, posLineEnd); - if (rangeLine.ContainsCharacter(braces[0])) { - int braceOffset = braces[0] - posLineStart; - if (braceOffset < ll->numCharsInLine) { - DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine); - } - } - if (rangeLine.ContainsCharacter(braces[1])) { - int braceOffset = braces[1] - posLineStart; - if (braceOffset < ll->numCharsInLine) { - DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine); - } - } - } - } -} - -void Editor::DrawAnnotation(Surface *surface, const ViewStyle &vsDraw, int line, int xStart, - PRectangle rcLine, LineLayout *ll, int subLine) { - int indent = static_cast(pdoc->GetLineIndentation(line) * vsDraw.spaceWidth); - PRectangle rcSegment = rcLine; - int annotationLine = subLine - ll->lines; - const StyledText stAnnotation = pdoc->AnnotationStyledText(line); - if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) { - surface->FillRectangle(rcSegment, vsDraw.styles[0].back); - rcSegment.left = static_cast(xStart); - if (trackLineWidth || (vsDraw.annotationVisible == ANNOTATION_BOXED)) { - // Only care about calculating width if tracking or need to draw box - int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation); - if (vsDraw.annotationVisible == ANNOTATION_BOXED) { - widthAnnotation += static_cast(vsDraw.spaceWidth * 2); // Margins - } - if (widthAnnotation > lineWidthMaxSeen) - lineWidthMaxSeen = widthAnnotation; - if (vsDraw.annotationVisible == ANNOTATION_BOXED) { - rcSegment.left = static_cast(xStart + indent); - rcSegment.right = rcSegment.left + widthAnnotation; - } - } - const int annotationLines = pdoc->AnnotationLines(line); - size_t start = 0; - size_t lengthAnnotation = stAnnotation.LineLength(start); - int lineInAnnotation = 0; - while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) { - start += lengthAnnotation + 1; - lengthAnnotation = stAnnotation.LineLength(start); - lineInAnnotation++; - } - PRectangle rcText = rcSegment; - if (vsDraw.annotationVisible == ANNOTATION_BOXED) { - surface->FillRectangle(rcText, - vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back); - rcText.left += vsDraw.spaceWidth; - } - DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, - stAnnotation, start, lengthAnnotation); - if (vsDraw.annotationVisible == ANNOTATION_BOXED) { - surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore); - 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)); - if (subLine == ll->lines) { - surface->MoveTo(static_cast(rcSegment.left), static_cast(rcSegment.top)); - surface->LineTo(static_cast(rcSegment.right), static_cast(rcSegment.top)); - } - if (subLine == ll->lines+annotationLines-1) { - surface->MoveTo(static_cast(rcSegment.left), static_cast(rcSegment.bottom - 1)); - surface->LineTo(static_cast(rcSegment.right), static_cast(rcSegment.bottom - 1)); - } - } - } -} - -void Editor::DrawLine(Surface *surface, const ViewStyle &vsDraw, int line, int lineVisible, int xStart, - PRectangle rcLine, LineLayout *ll, int subLine) { - - if (subLine >= ll->lines) { - DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine); - return; // No further drawing - } - - PRectangle rcSegment = rcLine; - - // Using one font for all control characters so it can be controlled independently to ensure - // the box goes around the characters tightly. Seems to be no way to work out what height - // is taken by an individual character - internal leading gives varying results. - FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font; - - // See if something overrides the line background color. - const ColourOptional background = vsDraw.Background(pdoc->GetMark(line), caret.active, ll->containsCaret); - - const bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) && - (!background.isSet) && (vsDraw.whitespaceColours.back.isSet); - - bool inIndentation = subLine == 0; // Do not handle indentation except on first subline. - const XYPOSITION indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth; - const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues - - const int posLineStart = pdoc->LineStart(line); - - const int startseg = ll->LineStart(subLine); - const XYACCUMULATOR subLineStart = ll->positions[startseg]; - int lineStart = 0; - int lineEnd = 0; - if (subLine < ll->lines) { - lineStart = ll->LineStart(subLine); - lineEnd = ll->LineStart(subLine + 1); - if (subLine == ll->lines - 1) { - lineEnd = ll->numCharsBeforeEOL; - } - } - - if (ll->wrapIndent != 0) { - - bool continuedWrapLine = false; - if (subLine < ll->lines) { - continuedWrapLine = ll->LineStart(subLine) != 0; - } - - if (continuedWrapLine) { - // draw continuation rect - PRectangle rcPlace = rcSegment; - - rcPlace.left = ll->positions[startseg] + xStart - static_cast(subLineStart); - rcPlace.right = rcPlace.left + ll->wrapIndent; - - // default bgnd here.. - surface->FillRectangle(rcSegment, background.isSet ? background : - vsDraw.styles[STYLE_DEFAULT].back); - - // main line style would be below but this would be inconsistent with end markers - // also would possibly not be the style at wrap point - //int styleMain = ll->styles[lineStart]; - //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back); - - if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) { - - if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT) - rcPlace.left = rcPlace.right - vsDraw.aveCharWidth; - else - rcPlace.right = rcPlace.left + vsDraw.aveCharWidth; - - DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour()); - } - - xStart += static_cast(ll->wrapIndent); - } - } - - const bool selBackDrawn = vsDraw.selColours.back.isSet && - ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)); - - // Does not take margin into account but not significant - const int xStartVisible = static_cast(subLineStart) - xStart; - - if (twoPhaseDraw) { - BreakFinder bfBack(ll, &sel, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc, &reprs); - - // Background drawing loop - while (bfBack.More()) { - - const TextSegment ts = bfBack.Next(); - const int i = ts.end() - 1; - const int iDoc = i + posLineStart; - - rcSegment.left = ll->positions[ts.start] + xStart - static_cast(subLineStart); - rcSegment.right = ll->positions[ts.end()] + xStart - static_cast(subLineStart); - // Only try to draw if really visible - enhances performance by not calling environment to - // draw strings that are completely past the right side of the window. - if (rcSegment.Intersects(rcLine)) { - // Clip to line rectangle, since may have a huge position which will not work with some platforms - if (rcSegment.left < rcLine.left) - rcSegment.left = rcLine.left; - if (rcSegment.right > rcLine.right) - rcSegment.right = rcLine.right; - - const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc); - const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc); - ColourDesired textBack = TextBackground(vsDraw, background, inSelection, - inHotspot, ll->styles[i], i, ll); - if (ts.representation) { - if (ll->chars[i] == '\t') { - // Tab display - if (drawWhitespaceBackground && - (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) - textBack = vsDraw.whitespaceColours.back; - } else { - // Blob display - inIndentation = false; - } - surface->FillRectangle(rcSegment, textBack); - } else { - // Normal text display - surface->FillRectangle(rcSegment, textBack); - if (vsDraw.viewWhitespace != wsInvisible || - (inIndentation && vsDraw.viewIndentationGuides == ivReal)) { - for (int cpos = 0; cpos <= i - ts.start; cpos++) { - if (ll->chars[cpos + ts.start] == ' ') { - if (drawWhitespaceBackground && - (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { - PRectangle rcSpace( - ll->positions[cpos + ts.start] + xStart - static_cast(subLineStart), - rcSegment.top, - ll->positions[cpos + ts.start + 1] + xStart - static_cast(subLineStart), - rcSegment.bottom); - surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back); - } - } else { - inIndentation = false; - } - } - } - } - } else if (rcSegment.left > rcLine.right) { - break; - } - } - - DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd, - xStart, subLine, subLineStart, background); - } - - DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true); - - if (vsDraw.edgeState == EDGE_LINE) { - int edgeX = static_cast(vsDraw.theEdge * vsDraw.spaceWidth); - rcSegment.left = static_cast(edgeX + xStart); - if ((ll->wrapIndent != 0) && (lineStart != 0)) - rcSegment.left -= ll->wrapIndent; - rcSegment.right = rcSegment.left + 1; - surface->FillRectangle(rcSegment, vsDraw.edgecolour); - } - - // Draw underline mark as part of background if not transparent - int marks = pdoc->GetMark(line); - int markBit; - for (markBit = 0; (markBit < 32) && marks; markBit++) { - if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) && - (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) { - PRectangle rcUnderline = rcLine; - rcUnderline.top = rcUnderline.bottom - 2; - surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back); - } - marks >>= 1; - } - - inIndentation = subLine == 0; // Do not handle indentation except on first subline. - // Foreground drawing loop - BreakFinder bfFore(ll, &sel, lineStart, lineEnd, posLineStart, xStartVisible, - ((!twoPhaseDraw && selBackDrawn) || vsDraw.selColours.fore.isSet), pdoc, &reprs); - - while (bfFore.More()) { - - const TextSegment ts = bfFore.Next(); - const int i = ts.end() - 1; - const int iDoc = i + posLineStart; - - rcSegment.left = ll->positions[ts.start] + xStart - static_cast(subLineStart); - rcSegment.right = ll->positions[ts.end()] + xStart - static_cast(subLineStart); - // Only try to draw if really visible - enhances performance by not calling environment to - // draw strings that are completely past the right side of the window. - if (rcSegment.Intersects(rcLine)) { - int styleMain = ll->styles[i]; - ColourDesired textFore = vsDraw.styles[styleMain].fore; - FontAlias textFont = vsDraw.styles[styleMain].font; - //hotspot foreground - const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc); - if (inHotspot) { - if (vsDraw.hotspotColours.fore.isSet) - textFore = vsDraw.hotspotColours.fore; - } - const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc); - if (inSelection && (vsDraw.selColours.fore.isSet)) { - textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground; - } - ColourDesired textBack = TextBackground(vsDraw, background, inSelection, inHotspot, styleMain, i, ll); - if (ts.representation) { - if (ll->chars[i] == '\t') { - // Tab display - if (!twoPhaseDraw) { - if (drawWhitespaceBackground && - (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) - textBack = vsDraw.whitespaceColours.back; - surface->FillRectangle(rcSegment, textBack); - } - if (inIndentation && vsDraw.viewIndentationGuides == ivReal) { - for (int indentCount = static_cast((ll->positions[i] + epsilon) / indentWidth); - indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth; - indentCount++) { - if (indentCount > 0) { - int xIndent = static_cast(indentCount * indentWidth); - DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment, - (ll->xHighlightGuide == xIndent)); - } - } - } - if (vsDraw.viewWhitespace != wsInvisible) { - if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { - if (vsDraw.whitespaceColours.fore.isSet) - textFore = vsDraw.whitespaceColours.fore; - surface->PenColour(textFore); - PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4, - rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); - DrawTabArrow(surface, rcTab, static_cast(rcSegment.top + vsDraw.lineHeight / 2)); - } - } - } else { - inIndentation = false; - if (vsDraw.controlCharSymbol >= 32) { - char cc[2] = { static_cast(vsDraw.controlCharSymbol), '\0' }; - surface->DrawTextNoClip(rcSegment, ctrlCharsFont, - rcSegment.top + vsDraw.maxAscent, - cc, 1, textBack, textFore); - } else { - DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(), textBack, textFore, twoPhaseDraw); - } - } - } else { - // Normal text display - if (vsDraw.styles[styleMain].visible) { - if (twoPhaseDraw) { - surface->DrawTextTransparent(rcSegment, textFont, - rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start, - i - ts.start + 1, textFore); - } else { - surface->DrawTextNoClip(rcSegment, textFont, - rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start, - i - ts.start + 1, textFore, textBack); - } - } - if (vsDraw.viewWhitespace != wsInvisible || - (inIndentation && vsDraw.viewIndentationGuides != ivNone)) { - for (int cpos = 0; cpos <= i - ts.start; cpos++) { - if (ll->chars[cpos + ts.start] == ' ') { - if (vsDraw.viewWhitespace != wsInvisible) { - if (vsDraw.whitespaceColours.fore.isSet) - textFore = vsDraw.whitespaceColours.fore; - if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { - XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2; - if (!twoPhaseDraw && drawWhitespaceBackground && - (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { - textBack = vsDraw.whitespaceColours.back; - PRectangle rcSpace( - ll->positions[cpos + ts.start] + xStart - static_cast(subLineStart), - rcSegment.top, - ll->positions[cpos + ts.start + 1] + xStart - static_cast(subLineStart), - rcSegment.bottom); - surface->FillRectangle(rcSpace, textBack); - } - PRectangle rcDot(xmid + xStart - static_cast(subLineStart), - rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f); - rcDot.right = rcDot.left + vsDraw.whitespaceSize; - rcDot.bottom = rcDot.top + vsDraw.whitespaceSize; - surface->FillRectangle(rcDot, textFore); - } - } - if (inIndentation && vsDraw.viewIndentationGuides == ivReal) { - for (int indentCount = static_cast((ll->positions[cpos + ts.start] + epsilon) / indentWidth); - indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth; - indentCount++) { - if (indentCount > 0) { - int xIndent = static_cast(indentCount * indentWidth); - DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment, - (ll->xHighlightGuide == xIndent)); - } - } - } - } else { - inIndentation = false; - } - } - } - } - if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) { - PRectangle rcUL = rcSegment; - rcUL.top = rcUL.top + vsDraw.maxAscent + 1; - rcUL.bottom = rcUL.top + 1; - if (vsDraw.hotspotColours.fore.isSet) - surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore); - else - surface->FillRectangle(rcUL, textFore); - } else if (vsDraw.styles[styleMain].underline) { - PRectangle rcUL = rcSegment; - rcUL.top = rcUL.top + vsDraw.maxAscent + 1; - rcUL.bottom = rcUL.top + 1; - surface->FillRectangle(rcUL, textFore); - } - } else if (rcSegment.left > rcLine.right) { - break; - } - } - if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth) - && (subLine == 0)) { - int indentSpace = pdoc->GetLineIndentation(line); - int xStartText = static_cast(ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart]); - - // Find the most recent line with some text - - int lineLastWithText = line; - while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) { - lineLastWithText--; - } - if (lineLastWithText < line) { - xStartText = 100000; // Don't limit to visible indentation on empty line - // This line is empty, so use indentation of last line with text - int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText); - int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG; - if (isFoldHeader) { - // Level is one more level than parent - indentLastWithText += pdoc->IndentSize(); - } - if (vsDraw.viewIndentationGuides == ivLookForward) { - // In viLookForward mode, previous line only used if it is a fold header - if (isFoldHeader) { - indentSpace = Platform::Maximum(indentSpace, indentLastWithText); + while (lineToWrap < lineToWrapEnd) { + if (WrapOneLine(surface, lineToWrap)) { + wrapOccurred = true; + } + wrapPending.Wrapped(lineToWrap); + lineToWrap++; } - } else { // viLookBoth - indentSpace = Platform::Maximum(indentSpace, indentLastWithText); + + goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1); } } - int lineNextWithText = line; - while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) { - lineNextWithText++; - } - if (lineNextWithText > line) { - xStartText = 100000; // Don't limit to visible indentation on empty line - // This line is empty, so use indentation of first next line with text - indentSpace = Platform::Maximum(indentSpace, - pdoc->GetLineIndentation(lineNextWithText)); + // If wrapping is done, bring it to resting position + if (wrapPending.start >= lineEndNeedWrap) { + wrapPending.Reset(); } + } - for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) { - int xIndent = static_cast(indentPos * vsDraw.spaceWidth); - if (xIndent < xStartText) { - DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment, - (ll->xHighlightGuide == xIndent)); - } - } + if (wrapOccurred) { + SetScrollBars(); + SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos())); + SetVerticalScrollPos(); } - DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false); + return wrapOccurred; +} - // End of the drawing of the current line - if (!twoPhaseDraw) { - DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd, - xStart, subLine, subLineStart, background); - } - if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) { - // For each selection draw - int virtualSpaces = 0; - if (subLine == (ll->lines - 1)) { - virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line)); - } - SelectionPosition posStart(posLineStart + lineStart); - SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces); - SelectionSegment virtualSpaceRange(posStart, posEnd); - for (size_t r=0; rEndLineStyle()].spaceWidth; - rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - - static_cast(subLineStart) + portion.start.VirtualSpace() * spaceWidth; - rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - - static_cast(subLineStart) + portion.end.VirtualSpace() * spaceWidth; - if ((ll->wrapIndent != 0) && (lineStart != 0)) { - if ((portion.start.Position() - posLineStart) == lineStart && sel.Range(r).ContainsCharacter(portion.start.Position() - 1)) - rcSegment.left -= static_cast(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here - } - rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left; - rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right; - if (rcSegment.right > rcLine.left) - SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha); +void Editor::LinesJoin() { + if (!RangeContainsProtected(targetStart, targetEnd)) { + UndoGroup ug(pdoc); + bool prevNonWS = true; + for (int pos = targetStart; pos < targetEnd; pos++) { + if (pdoc->IsPositionInLineEnd(pos)) { + targetEnd -= pdoc->LenChar(pos); + pdoc->DelChar(pos); + if (prevNonWS) { + // Ensure at least one space separating previous lines + const int lengthInserted = pdoc->InsertString(pos, " ", 1); + targetEnd += lengthInserted; } + } else { + prevNonWS = pdoc->CharAt(pos) != ' '; } } } +} - // Draw any translucent whole line states - rcSegment = rcLine; - if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) { - SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground, vsDraw.caretLineAlpha); +const char *Editor::StringFromEOLMode(int eolMode) { + if (eolMode == SC_EOL_CRLF) { + return "\r\n"; + } else if (eolMode == SC_EOL_CR) { + return "\r"; + } else { + return "\n"; } - marks = pdoc->GetMark(line); - for (markBit = 0; (markBit < 32) && marks; markBit++) { - if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) { - SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha); - } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) { - PRectangle rcUnderline = rcSegment; - rcUnderline.top = rcUnderline.bottom - 2; - SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha); +} + +void Editor::LinesSplit(int pixelWidth) { + if (!RangeContainsProtected(targetStart, targetEnd)) { + if (pixelWidth == 0) { + PRectangle rcText = GetTextRectangle(); + pixelWidth = static_cast(rcText.Width()); } - marks >>= 1; - } - if (vsDraw.maskInLine) { - int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine; - if (marksMasked) { - for (markBit = 0; (markBit < 32) && marksMasked; markBit++) { - if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) { - SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha); + int lineStart = pdoc->LineFromPosition(targetStart); + int lineEnd = pdoc->LineFromPosition(targetEnd); + const char *eol = StringFromEOLMode(pdoc->eolMode); + UndoGroup ug(pdoc); + for (int line = lineStart; line <= lineEnd; line++) { + AutoSurface surface(this); + AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this)); + if (surface && ll) { + unsigned int posLineStart = pdoc->LineStart(line); + view.LayoutLine(*this, line, surface, vs, ll, pixelWidth); + int lengthInsertedTotal = 0; + for (int subLine = 1; subLine < ll->lines; subLine++) { + const int lengthInserted = pdoc->InsertString( + static_cast(posLineStart + lengthInsertedTotal + + ll->LineStart(subLine)), + eol, istrlen(eol)); + targetEnd += lengthInserted; + lengthInsertedTotal += lengthInserted; } - marksMasked >>= 1; } + lineEnd = pdoc->LineFromPosition(targetEnd); } } } -void Editor::DrawBlockCaret(Surface *surface, const ViewStyle &vsDraw, LineLayout *ll, int subLine, - int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) const { +void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) { + if (vs.fixedColumnWidth == 0) + return; - int lineStart = ll->LineStart(subLine); - int posBefore = posCaret; - int posAfter = MovePositionOutsideChar(posCaret + 1, 1); - int numCharsToDraw = posAfter - posCaret; + AllocateGraphics(); + RefreshStyleData(); + RefreshPixMaps(surfWindow); - // Work out where the starting and ending offsets are. We need to - // see if the previous character shares horizontal space, such as a - // glyph / combining character. If so we'll need to draw that too. - int offsetFirstChar = offset; - int offsetLastChar = offset + (posAfter - posCaret); - while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) { - if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) { - // The char does not share horizontal space - break; - } - // Char shares horizontal space, update the numChars to draw - // Update posBefore to point to the prev char - posBefore = MovePositionOutsideChar(posBefore - 1, -1); - numCharsToDraw = posAfter - posBefore; - offsetFirstChar = offset - (posCaret - posBefore); - } - - // See if the next character shares horizontal space, if so we'll - // need to draw that too. - if (offsetFirstChar < 0) - offsetFirstChar = 0; - numCharsToDraw = offsetLastChar - offsetFirstChar; - while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) { - // Update posAfter to point to the 2nd next char, this is where - // the next character ends, and 2nd next begins. We'll need - // to compare these two - posBefore = posAfter; - posAfter = MovePositionOutsideChar(posAfter + 1, 1); - offsetLastChar = offset + (posAfter - posCaret); - if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) { - // The char does not share horizontal space - break; - } - // Char shares horizontal space, update the numChars to draw - numCharsToDraw = offsetLastChar - offsetFirstChar; + // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished + // at this point. The Initialised call checks for this case and sets the status + // to be bad which avoids crashes in following calls. + if (!surfWindow->Initialised()) { + return; } - // We now know what to draw, update the caret drawing rectangle - rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart; - rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart; + PRectangle rcMargin = GetClientRectangle(); + Point ptOrigin = GetVisibleOriginInMain(); + rcMargin.Move(0, -ptOrigin.y); + rcMargin.left = 0; + rcMargin.right = static_cast(vs.fixedColumnWidth); + + if (!rc.Intersects(rcMargin)) + return; - // Adjust caret position to take into account any word wrapping symbols. - if ((ll->wrapIndent != 0) && (lineStart != 0)) { - XYPOSITION wordWrapCharWidth = ll->wrapIndent; - rcCaret.left += wordWrapCharWidth; - rcCaret.right += wordWrapCharWidth; + Surface *surface; + if (view.bufferedDraw) { + surface = marginView.pixmapSelMargin; + } else { + surface = surfWindow; } - // This character is where the caret block is, we override the colours - // (inversed) for drawing the caret here. - int styleMain = ll->styles[offsetFirstChar]; - FontAlias fontText = vsDraw.styles[styleMain].font; - surface->DrawTextClipped(rcCaret, fontText, - rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar, - numCharsToDraw, vsDraw.styles[styleMain].back, - caretColour); -} + // Clip vertically to paint area to avoid drawing line numbers + if (rcMargin.bottom > rc.bottom) + rcMargin.bottom = rc.bottom; + if (rcMargin.top < rc.top) + rcMargin.top = rc.top; -void Editor::RefreshPixMaps(Surface *surfaceWindow) { - if (!pixmapSelPattern->Initialised()) { - const int patternSize = 8; - pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID()); - pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID()); - // This complex procedure is to reproduce the checkerboard dithered pattern used by windows - // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half - // way between the chrome colour and the chrome highlight colour making a nice transition - // between the window chrome and the content area. And it works in low colour depths. - PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize); - - // Initialize default colours based on the chrome colour scheme. Typically the highlight is white. - ColourDesired colourFMFill = vs.selbar; - ColourDesired colourFMStripes = vs.selbarlight; - - if (!(vs.selbarlight == ColourDesired(0xff, 0xff, 0xff))) { - // User has chosen an unusual chrome colour scheme so just use the highlight edge colour. - // (Typically, the highlight colour is white.) - colourFMFill = vs.selbarlight; - } - - if (vs.foldmarginColour.isSet) { - // override default fold margin colour - colourFMFill = vs.foldmarginColour; - } - if (vs.foldmarginHighlightColour.isSet) { - // override default fold margin highlight colour - colourFMStripes = vs.foldmarginHighlightColour; - } - - pixmapSelPattern->FillRectangle(rcPattern, colourFMFill); - pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes); - for (int y = 0; y < patternSize; y++) { - for (int x = y % 2; x < patternSize; x+=2) { - PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1); - pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes); - pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill); - } - } - } + marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs); - if (!pixmapIndentGuide->Initialised()) { - // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line - pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID()); - pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID()); - PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vs.lineHeight); - pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back); - pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore); - pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back); - pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore); - for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) { - PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1); - pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore); - pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore); - } + if (view.bufferedDraw) { + surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin); } +} + +void Editor::RefreshPixMaps(Surface *surfaceWindow) { + view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs); + marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs); + if (view.bufferedDraw) { + PRectangle rcClient = GetClientRectangle(); + if (!view.pixmapLine->Initialised()) { - if (bufferedDraw) { - if (!pixmapLine->Initialised()) { - PRectangle rcClient = GetClientRectangle(); - pixmapLine->InitPixMap(static_cast(rcClient.Width()), vs.lineHeight, + view.pixmapLine->InitPixMap(static_cast(rcClient.Width()), vs.lineHeight, surfaceWindow, wMain.GetID()); - pixmapSelMargin->InitPixMap(vs.fixedColumnWidth, - static_cast(rcClient.Height()), surfaceWindow, wMain.GetID()); } - } -} - -void Editor::DrawCarets(Surface *surface, const ViewStyle &vsDraw, int lineDoc, int xStart, - PRectangle rcLine, LineLayout *ll, int subLine) { - // When drag is active it is the only caret drawn - bool drawDrag = posDrag.IsValid(); - if (hideSelection && !drawDrag) - return; - const int posLineStart = pdoc->LineStart(lineDoc); - // For each selection draw - for (size_t r=0; (rEndLineStyle()].spaceWidth; - const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth; - if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) { - XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)]; - if (ll->wrapIndent != 0) { - int lineStart = ll->LineStart(subLine); - if (lineStart != 0) // Wrapped - xposCaret += ll->wrapIndent; - } - bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret); - bool caretVisibleState = additionalCaretsVisible || mainCaret; - if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) && - ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) { - bool caretAtEOF = false; - bool caretAtEOL = false; - bool drawBlockCaret = false; - XYPOSITION widthOverstrikeCaret; - XYPOSITION caretWidthOffset = 0; - PRectangle rcCaret = rcLine; - - if (posCaret.Position() == pdoc->Length()) { // At end of document - caretAtEOF = true; - widthOverstrikeCaret = vsDraw.aveCharWidth; - } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line - caretAtEOL = true; - widthOverstrikeCaret = vsDraw.aveCharWidth; - } else { - widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset]; - } - if (widthOverstrikeCaret < 3) // Make sure its visible - widthOverstrikeCaret = 3; - - if (xposCaret > 0) - caretWidthOffset = 0.51f; // Move back so overlaps both character cells. - xposCaret += xStart; - if (posDrag.IsValid()) { - /* Dragging text, use a line caret */ - rcCaret.left = static_cast(RoundXYPosition(xposCaret - caretWidthOffset)); - rcCaret.right = rcCaret.left + vsDraw.caretWidth; - } else if (inOverstrike && drawOverstrikeCaret) { - /* Overstrike (insert mode), use a modified bar caret */ - rcCaret.top = rcCaret.bottom - 2; - rcCaret.left = xposCaret + 1; - rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1; - } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) { - /* Block caret */ - rcCaret.left = xposCaret; - if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) { - drawBlockCaret = true; - rcCaret.right = xposCaret + widthOverstrikeCaret; - } else { - rcCaret.right = xposCaret + vsDraw.aveCharWidth; - } - } else { - /* Line caret */ - rcCaret.left = static_cast(RoundXYPosition(xposCaret - caretWidthOffset)); - rcCaret.right = rcCaret.left + vsDraw.caretWidth; - } - ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour; - if (drawBlockCaret) { - DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour); - } else { - surface->FillRectangle(rcCaret, caretColour); - } - } + if (!marginView.pixmapSelMargin->Initialised()) { + marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth, + static_cast(rcClient.Height()), surfaceWindow, wMain.GetID()); } - if (drawDrag) - break; } } @@ -3441,18 +1619,9 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { StyleToPositionInView(PositionAfterArea(rcArea)); PRectangle rcClient = GetClientRectangle(); - Point ptOrigin = GetVisibleOriginInMain(); //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n", // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); - int screenLinePaintFirst = static_cast(rcArea.top) / vs.lineHeight; - - int xStart = vs.textStart - xOffset + static_cast(ptOrigin.x); - int ypos = 0; - if (!bufferedDraw) - ypos += screenLinePaintFirst * vs.lineHeight; - int yposScreen = screenLinePaintFirst * vs.lineHeight; - if (NotifyUpdateUI()) { RefreshStyleData(); RefreshPixMaps(surfaceWindow); @@ -3467,9 +1636,9 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { } RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change } - PLATFORM_ASSERT(pixmapSelPattern->Initialised()); + PLATFORM_ASSERT(marginView.pixmapSelPattern->Initialised()); - if (!bufferedDraw) + if (!view.bufferedDraw) surfaceWindow->SetClip(rcArea); if (paintState != paintAbandoned) { @@ -3503,195 +1672,19 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { } return; } - //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset); - - // Allow text at start of line to overlap 1 pixel into the margin as this displays - // serifs and italic stems for aliased text. - const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0; - - // Do the painting - if (rcArea.right > vs.textStart - leftTextOverlap) { - - Surface *surface = surfaceWindow; - if (bufferedDraw) { - surface = pixmapLine; - PLATFORM_ASSERT(pixmapLine->Initialised()); - } - surface->SetUnicodeMode(IsUnicodeMode()); - surface->SetDBCSMode(CodePage()); - - int visibleLine = TopLineOfMain() + screenLinePaintFirst; - - SelectionPosition posCaret = sel.RangeMain().caret; - if (posDrag.IsValid()) - posCaret = posDrag; - int lineCaret = pdoc->LineFromPosition(posCaret.Position()); - - PRectangle rcTextArea = rcClient; - if (vs.marginInside) { - rcTextArea.left += vs.textStart; - rcTextArea.right -= vs.rightMarginWidth; - } else { - rcTextArea = rcArea; - } - - // Remove selection margin from drawing area so text will not be drawn - // on it in unbuffered mode. - if (!bufferedDraw && vs.marginInside) { - PRectangle rcClipText = rcTextArea; - rcClipText.left -= leftTextOverlap; - surfaceWindow->SetClip(rcClipText); - } - - // Loop on visible lines - //double durLayout = 0.0; - //double durPaint = 0.0; - //double durCopy = 0.0; - //ElapsedTime etWhole; - int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times - AutoLineLayout ll(llc, 0); - while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) { - - int lineDoc = cs.DocFromDisplay(visibleLine); - // Only visible lines should be handled by the code within the loop - PLATFORM_ASSERT(cs.GetVisible(lineDoc)); - int lineStartSet = cs.DisplayFromDoc(lineDoc); - int subLine = visibleLine - lineStartSet; - - // Copy this line and its styles from the document into local arrays - // and determine the x position at which each character starts. - //ElapsedTime et; - if (lineDoc != lineDocPrevious) { - ll.Set(0); - ll.Set(RetrieveLineLayout(lineDoc)); - LayoutLine(lineDoc, surface, vs, ll, wrapWidth); - lineDocPrevious = lineDoc; - } - //durLayout += et.Duration(true); - - if (ll) { - ll->containsCaret = lineDoc == lineCaret; - if (hideSelection) { - ll->containsCaret = false; - } - - ll->hotspot = GetHotSpotRange(); - - PRectangle rcLine = rcTextArea; - rcLine.top = static_cast(ypos); - rcLine.bottom = static_cast(ypos + vs.lineHeight); - - bool bracesIgnoreStyle = false; - if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) || - (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) { - bracesIgnoreStyle = true; - } - Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1)); - // Highlight the current braces if any - ll->SetBracesHighlight(rangeLine, braces, static_cast(bracesMatchStyle), - static_cast(highlightGuideColumn * vs.spaceWidth), bracesIgnoreStyle); - - if (leftTextOverlap && bufferedDraw) { - PRectangle rcSpacer = rcLine; - rcSpacer.right = rcSpacer.left; - rcSpacer.left -= 1; - surface->FillRectangle(rcSpacer, vs.styles[STYLE_DEFAULT].back); - } - - // Draw the line - DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine); - //durPaint += et.Duration(true); - - // Restore the previous styles for the brace highlights in case layout is in cache. - ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle); - - bool expanded = cs.GetExpanded(lineDoc); - const int level = pdoc->GetLevel(lineDoc); - const int levelNext = pdoc->GetLevel(lineDoc + 1); - if ((level & SC_FOLDLEVELHEADERFLAG) && - ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) { - // Paint the line above the fold - if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED)) - || - (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) { - PRectangle rcFoldLine = rcLine; - rcFoldLine.bottom = rcFoldLine.top + 1; - surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore); - } - // Paint the line below the fold - if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED)) - || - (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) { - PRectangle rcFoldLine = rcLine; - rcFoldLine.top = rcFoldLine.bottom - 1; - surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore); - } - } - - DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine); - - if (bufferedDraw) { - Point from = Point::FromInts(vs.textStart-leftTextOverlap, 0); - PRectangle rcCopyArea = PRectangle::FromInts(vs.textStart - leftTextOverlap, yposScreen, - static_cast(rcClient.right - vs.rightMarginWidth), - yposScreen + vs.lineHeight); - surfaceWindow->Copy(rcCopyArea, from, *pixmapLine); - } - - lineWidthMaxSeen = Platform::Maximum( - lineWidthMaxSeen, static_cast(ll->positions[ll->numCharsInLine])); - //durCopy += et.Duration(true); - } - if (!bufferedDraw) { - ypos += vs.lineHeight; - } + view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs); - yposScreen += vs.lineHeight; - visibleLine++; - - //gdk_flush(); - } - ll.Set(0); - //if (durPaint < 0.00000001) - // durPaint = 0.00000001; - - // Right column limit indicator - PRectangle rcBeyondEOF = (vs.marginInside) ? rcClient : rcArea; - rcBeyondEOF.left = static_cast(vs.textStart); - rcBeyondEOF.right = rcBeyondEOF.right - ((vs.marginInside) ? vs.rightMarginWidth : 0); - rcBeyondEOF.top = static_cast((cs.LinesDisplayed() - TopLineOfMain()) * vs.lineHeight); - if (rcBeyondEOF.top < rcBeyondEOF.bottom) { - surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back); - if (vs.edgeState == EDGE_LINE) { - int edgeX = static_cast(vs.theEdge * vs.spaceWidth); - rcBeyondEOF.left = static_cast(edgeX + xStart); - rcBeyondEOF.right = rcBeyondEOF.left + 1; - surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour); + if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) { + if (FineTickerAvailable()) { + scrollWidth = view.lineWidthMaxSeen; + if (!FineTickerRunning(tickWiden)) { + FineTickerStart(tickWiden, 50, 5); } } - //Platform::DebugPrintf( - //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n", - //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration()); - NotifyPainted(); } -} - -// Space (3 space characters) between line numbers and text when printing. -#define lineNumberPrintSpace " " -ColourDesired InvertedLight(ColourDesired orig) { - unsigned int r = orig.GetRed(); - unsigned int g = orig.GetGreen(); - unsigned int b = orig.GetBlue(); - unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye - unsigned int il = 0xff - l; - if (l == 0) - return ColourDesired(0xff, 0xff, 0xff); - r = r * il / l; - g = g * il / l; - b = b * il / l; - return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff)); + NotifyPainted(); } // This is mostly copied from the Paint method but with some things omitted @@ -3708,184 +1701,7 @@ long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) { if (!surfaceMeasure) { return 0; } - - // Can't use measurements cached for screen - posCache.Clear(); - - ViewStyle vsPrint(vs); - vsPrint.technology = SC_TECHNOLOGY_DEFAULT; - - // Modify the view style for printing as do not normally want any of the transient features to be printed - // Printing supports only the line number margin. - int lineNumberIndex = -1; - for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) { - if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) { - lineNumberIndex = margin; - } else { - vsPrint.ms[margin].width = 0; - } - } - vsPrint.fixedColumnWidth = 0; - vsPrint.zoomLevel = printParameters.magnification; - // Don't show indentation guides - // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT - vsPrint.viewIndentationGuides = ivNone; - // Don't show the selection when printing - vsPrint.selColours.back.isSet = false; - vsPrint.selColours.fore.isSet = false; - vsPrint.selAlpha = SC_ALPHA_NOALPHA; - vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA; - vsPrint.whitespaceColours.back.isSet = false; - vsPrint.whitespaceColours.fore.isSet = false; - vsPrint.showCaretLineBackground = false; - vsPrint.alwaysShowCaretLineBackground = false; - // Don't highlight matching braces using indicators - vsPrint.braceHighlightIndicatorSet = false; - vsPrint.braceBadLightIndicatorSet = false; - - // Set colours for printing according to users settings - for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) { - if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) { - vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore); - vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back); - } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) { - vsPrint.styles[sty].fore = ColourDesired(0, 0, 0); - vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff); - } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) { - vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff); - } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) { - if (sty <= STYLE_DEFAULT) { - vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff); - } - } - } - // White background for the line numbers - vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff); - - // Printing uses different margins, so reset screen margins - vsPrint.leftMarginWidth = 0; - vsPrint.rightMarginWidth = 0; - - vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars); - // Determining width must happen after fonts have been realised in Refresh - int lineNumberWidth = 0; - if (lineNumberIndex >= 0) { - lineNumberWidth = static_cast(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font, - "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace))); - vsPrint.ms[lineNumberIndex].width = lineNumberWidth; - vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars); // Recalculate fixedColumnWidth - } - - int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin); - int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1; - if (linePrintLast < linePrintStart) - linePrintLast = linePrintStart; - int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax); - if (linePrintLast > linePrintMax) - linePrintLast = linePrintMax; - //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n", - // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight, - // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font)); - int endPosPrint = pdoc->Length(); - if (linePrintLast < pdoc->LinesTotal()) - endPosPrint = pdoc->LineStart(linePrintLast + 1); - - // Ensure we are styled to where we are formatting. - pdoc->EnsureStyledTo(endPosPrint); - - int xStart = vsPrint.fixedColumnWidth + pfr->rc.left; - int ypos = pfr->rc.top; - - int lineDoc = linePrintStart; - - int nPrintPos = pfr->chrg.cpMin; - int visibleLine = 0; - int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth; - if (printParameters.wrapState == eWrapNone) - widthPrint = LineLayout::wrapWidthInfinite; - - while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) { - - // When printing, the hdc and hdcTarget may be the same, so - // changing the state of surfaceMeasure may change the underlying - // state of surface. Therefore, any cached state is discarded before - // using each surface. - surfaceMeasure->FlushCachedState(); - - // Copy this line and its styles from the document into local arrays - // and determine the x position at which each character starts. - LineLayout ll(pdoc->LineStart(lineDoc+1)-pdoc->LineStart(lineDoc)+1); - LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint); - - ll.containsCaret = false; - - PRectangle rcLine = PRectangle::FromInts( - pfr->rc.left, - ypos, - pfr->rc.right - 1, - ypos + vsPrint.lineHeight); - - // When document line is wrapped over multiple display lines, find where - // to start printing from to ensure a particular position is on the first - // line of the page. - if (visibleLine == 0) { - int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc); - for (int iwl = 0; iwl < ll.lines - 1; iwl++) { - if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) { - visibleLine = -iwl; - } - } - - if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) { - visibleLine = -(ll.lines - 1); - } - } - - if (draw && lineNumberWidth && - (ypos + vsPrint.lineHeight <= pfr->rc.bottom) && - (visibleLine >= 0)) { - char number[100]; - sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1); - PRectangle rcNumber = rcLine; - rcNumber.right = rcNumber.left + lineNumberWidth; - // Right justify - rcNumber.left = rcNumber.right - surfaceMeasure->WidthText( - vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number)); - surface->FlushCachedState(); - surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font, - static_cast(ypos + vsPrint.maxAscent), number, istrlen(number), - vsPrint.styles[STYLE_LINENUMBER].fore, - vsPrint.styles[STYLE_LINENUMBER].back); - } - - // Draw the line - surface->FlushCachedState(); - - for (int iwl = 0; iwl < ll.lines; iwl++) { - if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) { - if (visibleLine >= 0) { - if (draw) { - rcLine.top = static_cast(ypos); - rcLine.bottom = static_cast(ypos + vsPrint.lineHeight); - DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl); - } - ypos += vsPrint.lineHeight; - } - visibleLine++; - if (iwl == ll.lines - 1) - nPrintPos = pdoc->LineStart(lineDoc + 1); - else - nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl); - } - } - - ++lineDoc; - } - - // Clear cache so measurements are not used for screen - posCache.Clear(); - - return nPrintPos; + return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs); } int Editor::TextWidth(int style, const char *text) { @@ -3968,7 +1784,7 @@ static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) { } // AddCharUTF inserts an array of bytes which may or may not be in UTF-8. -void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) { +void Editor::AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS) { FilterSelections(); { UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike); @@ -4172,6 +1988,9 @@ void Editor::ClearAll() { pdoc->MarginClearAll(); } } + + view.ClearAllTabstops(); + sel.Clear(); SetTopLine(0); SetVerticalScrollPos(); @@ -4192,6 +2011,7 @@ void Editor::ClearDocumentStyle() { pdoc->StartStyling(0, '\377'); pdoc->SetStyleFor(pdoc->Length(), 0); cs.ShowAll(); + SetAnnotationHeights(0, pdoc->LinesTotal()); pdoc->ClearLevels(); } @@ -4321,6 +2141,7 @@ void Editor::DelChar() { } void Editor::DelCharBack(bool allowLineStartDeletion) { + RefreshStyleData(); if (!sel.IsRectangular()) FilterSelections(); if (sel.IsRectangular()) @@ -4591,7 +2412,7 @@ void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) { void Editor::CheckModificationForWrap(DocModification mh) { if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) { - llc.Invalidate(LineLayout::llCheckTextAndStyle); + view.llc.Invalidate(LineLayout::llCheckTextAndStyle); int lineDoc = pdoc->LineFromPosition(mh.position); int lines = Platform::Maximum(0, mh.linesAdded); if (Wrapping()) { @@ -4640,6 +2461,9 @@ void Editor::NotifyModified(Document *, DocModification mh, void *) { Redraw(); } } + if (mh.modificationType & SC_MOD_CHANGETABSTOPS) { + Redraw(); + } if (mh.modificationType & SC_MOD_LEXERSTATE) { if (paintState == painting) { CheckForChangeOutsidePaint( @@ -4661,7 +2485,7 @@ void Editor::NotifyModified(Document *, DocModification mh, void *) { } } if (mh.modificationType & SC_MOD_CHANGESTYLE) { - llc.Invalidate(LineLayout::llCheckTextAndStyle); + view.llc.Invalidate(LineLayout::llCheckTextAndStyle); } } else { // Move selection and brace highlights @@ -4703,6 +2527,7 @@ void Editor::NotifyModified(Document *, DocModification mh, void *) { } else { cs.DeleteLines(lineOfPos, -mh.linesAdded); } + view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded); } if (mh.modificationType & SC_MOD_CHANGEANNOTATION) { int lineDoc = pdoc->LineFromPosition(mh.position); @@ -4742,7 +2567,7 @@ void Editor::NotifyModified(Document *, DocModification mh, void *) { if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) { if (mh.modificationType & SC_MOD_CHANGEFOLD) { // Fold changes can affect the drawing of following lines so redraw whole margin - RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true); + RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true); } else { RedrawSelMargin(mh.line); } @@ -5195,29 +3020,8 @@ void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) { int Editor::StartEndDisplayLine(int pos, bool start) { RefreshStyleData(); - int line = pdoc->LineFromPosition(pos); AutoSurface surface(this); - AutoLineLayout ll(llc, RetrieveLineLayout(line)); - int posRet = INVALID_POSITION; - if (surface && ll) { - unsigned int posLineStart = pdoc->LineStart(line); - LayoutLine(line, surface, vs, ll, wrapWidth); - int posInLine = pos - posLineStart; - if (posInLine <= ll->maxLineLength) { - for (int subLine = 0; subLine < ll->lines; subLine++) { - if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) { - if (start) { - posRet = ll->LineStart(subLine) + posLineStart; - } else { - if (subLine == ll->lines - 1) - posRet = ll->LineStart(subLine + 1) + posLineStart; - else - posRet = ll->LineStart(subLine + 1) + posLineStart - 1; - } - } - } - } - } + int posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs); if (posRet == INVALID_POSITION) { return pos; } else { @@ -5581,6 +3385,7 @@ int Editor::KeyCommand(unsigned int iMessage) { break; case SCI_DELWORDRIGHT: { UndoGroup ug(pdoc); + InvalidateSelection(sel.RangeMain(), true); sel.RangeMain().caret = SelectionPosition( InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace())); sel.RangeMain().anchor = sel.RangeMain().caret; @@ -5590,6 +3395,7 @@ int Editor::KeyCommand(unsigned int iMessage) { break; case SCI_DELWORDRIGHTEND: { UndoGroup ug(pdoc); + InvalidateSelection(sel.RangeMain(), true); sel.RangeMain().caret = SelectionPosition( InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace())); int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1); @@ -6038,7 +3844,13 @@ void Editor::SetDragPosition(SelectionPosition newPos) { } if (!(posDrag == newPos)) { caret.on = true; - SetTicking(true); + if (FineTickerAvailable()) { + FineTickerCancel(tickCaret); + if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0)) + FineTickerStart(tickCaret, caret.period, caret.period/10); + } else { + SetTicking(true); + } InvalidateCaret(); posDrag = newPos; InvalidateCaret(); @@ -6169,7 +3981,7 @@ bool Editor::PointInSelection(Point pt) { return false; } -bool Editor::PointInSelMargin(Point pt) { +bool Editor::PointInSelMargin(Point pt) const { // Really means: "Point in a margin" if (vs.fixedColumnWidth > 0) { // There is a margin PRectangle rcSelMargin = GetClientRectangle(); @@ -6262,6 +4074,12 @@ void Editor::DwellEnd(bool mouseMoved) { dwelling = false; NotifyDwelling(ptMouseLast, dwelling); } + if (FineTickerAvailable()) { + FineTickerCancel(tickDwell); + if (mouseMoved && (dwellDelay < SC_TIME_FOREVER)) { + //FineTickerStart(tickDwell, dwellDelay, dwellDelay/10); + } + } } void Editor::MouseLeave() { @@ -6309,6 +4127,9 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifie if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) { //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime); SetMouseCapture(true); + if (FineTickerAvailable()) { + FineTickerStart(tickScroll, 100, 10); + } if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord)) SetEmptySelection(newPos.Position()); bool doubleClick = false; @@ -6405,6 +4226,9 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifie SetDragPosition(SelectionPosition(invalidPosition)); SetMouseCapture(true); + if (FineTickerAvailable()) { + FineTickerStart(tickScroll, 100, 10); + } } else { if (PointIsHotspot(pt)) { NotifyHotSpotClicked(newCharPos.Position(), modifiers); @@ -6417,6 +4241,9 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifie inDragDrop = ddNone; } SetMouseCapture(true); + if (FineTickerAvailable()) { + FineTickerStart(tickScroll, 100, 10); + } if (inDragDrop != ddInitial) { SetDragPosition(SelectionPosition(invalidPosition)); if (!shift) { @@ -6510,6 +4337,9 @@ void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) { if (inDragDrop == ddInitial) { if (DragThreshold(ptMouseLast, pt)) { SetMouseCapture(false); + if (FineTickerAvailable()) { + FineTickerCancel(tickScroll); + } SetDragPosition(movePos); CopySelectionRange(&drag); StartDrag(); @@ -6518,6 +4348,12 @@ void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) { } ptMouseLast = pt; + PRectangle rcClient = GetClientRectangle(); + Point ptOrigin = GetVisibleOriginInMain(); + rcClient.Move(0, -ptOrigin.y); + if (FineTickerAvailable() && (dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) { + FineTickerStart(tickDwell, dwellDelay, dwellDelay/10); + } //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y); if (HaveMouseCapture()) { @@ -6569,9 +4405,6 @@ void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) { } // Autoscroll - PRectangle rcClient = GetClientRectangle(); - Point ptOrigin = GetVisibleOriginInMain(); - rcClient.Move(0, -ptOrigin.y); int lineMove = DisplayFromPosition(movePos.Position()); if (pt.y > rcClient.bottom) { ScrollTo(lineMove - LinesOnScreen() + 1); @@ -6643,6 +4476,9 @@ void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) { } ptMouseLast = pt; SetMouseCapture(false); + if (FineTickerAvailable()) { + FineTickerCancel(tickScroll); + } NotifyIndicatorClick(false, newPos.Position(), 0); if (inDragDrop == ddDragging) { SelectionPosition selStart = SelectionStart(); @@ -6719,8 +4555,8 @@ void Editor::Tick() { } } } - if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) { - scrollWidth = lineWidthMaxSeen; + if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) { + scrollWidth = view.lineWidthMaxSeen; SetScrollBars(); } if ((dwellDelay < SC_TIME_FOREVER) && @@ -6759,6 +4595,66 @@ bool Editor::Idle() { return !idleDone; } +void Editor::SetTicking(bool) { + // SetTicking is deprecated. In the past it was pure virtual and was overridden in each + // derived platform class but fine grained timers should now be implemented. + // Either way, execution should not arrive here so assert failure. + assert(false); +} + +void Editor::TickFor(TickReason reason) { + switch (reason) { + case tickCaret: + caret.on = !caret.on; + if (caret.active) { + InvalidateCaret(); + } + break; + case tickScroll: + // Auto scroll + ButtonMove(ptMouseLast); + break; + case tickWiden: + SetScrollBars(); + FineTickerCancel(tickWiden); + break; + case tickDwell: + if ((!HaveMouseCapture()) && + (ptMouseLast.y >= 0)) { + dwelling = true; + NotifyDwelling(ptMouseLast, dwelling); + } + FineTickerCancel(tickDwell); + break; + default: + // tickPlatform handled by subclass + break; + } +} + +bool Editor::FineTickerAvailable() { + return false; +} + +// FineTickerStart is be overridden by subclasses that support fine ticking so +// this method should never be called. +bool Editor::FineTickerRunning(TickReason) { + assert(false); + return false; +} + +// FineTickerStart is be overridden by subclasses that support fine ticking so +// this method should never be called. +void Editor::FineTickerStart(TickReason, int, int) { + assert(false); +} + +// FineTickerCancel is be overridden by subclasses that support fine ticking so +// this method should never be called. +void Editor::FineTickerCancel(TickReason) { + assert(false); +} + void Editor::SetFocusState(bool focusState) { hasFocus = focusState; NotifyFocus(hasFocus); @@ -6838,7 +4734,7 @@ void Editor::CheckForChangeOutsidePaint(Range r) { if (!r.Valid()) return; - PRectangle rcRange = RectangleFromRange(r); + PRectangle rcRange = RectangleFromRange(r, 0); PRectangle rcText = GetTextRectangle(); if (rcRange.top < rcText.top) { rcRange.top = rcText.top; @@ -6880,9 +4776,9 @@ void Editor::SetAnnotationHeights(int start, int end) { int linesWrapped = 1; if (Wrapping()) { AutoSurface surface(this); - AutoLineLayout ll(llc, RetrieveLineLayout(line)); + AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this)); if (surface && ll) { - LayoutLine(line, surface, vs, ll, wrapWidth); + view.LayoutLine(*this, line, surface, vs, ll, wrapWidth); linesWrapped = ll->lines; } } @@ -6922,9 +4818,11 @@ void Editor::SetDocPointer(Document *document) { cs.Clear(); cs.InsertLines(0, pdoc->LinesTotal() - 1); SetAnnotationHeights(0, pdoc->LinesTotal()); - llc.Deallocate(); + view.llc.Deallocate(); NeedWrapping(); + view.ClearAllTabstops(); + pdoc->AddWatcher(this, 0); SetScrollBars(); Redraw(); @@ -7235,10 +5133,10 @@ int Editor::CodePage() const { int Editor::WrapCount(int line) { AutoSurface surface(this); - AutoLineLayout ll(llc, RetrieveLineLayout(line)); + AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this)); if (surface && ll) { - LayoutLine(line, surface, vs, ll, wrapWidth); + view.LayoutLine(*this, line, surface, vs, ll, wrapWidth); return ll->lines; } else { return 1; @@ -7704,7 +5602,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { } case SCI_HIDESELECTION: - hideSelection = wParam != 0; + view.hideSelection = wParam != 0; Redraw(); break; @@ -7892,25 +5790,25 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { break; case SCI_SETPRINTMAGNIFICATION: - printParameters.magnification = static_cast(wParam); + view.printParameters.magnification = static_cast(wParam); break; case SCI_GETPRINTMAGNIFICATION: - return printParameters.magnification; + return view.printParameters.magnification; case SCI_SETPRINTCOLOURMODE: - printParameters.colourMode = static_cast(wParam); + view.printParameters.colourMode = static_cast(wParam); break; case SCI_GETPRINTCOLOURMODE: - return printParameters.colourMode; + return view.printParameters.colourMode; case SCI_SETPRINTWRAPMODE: - printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone; + view.printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone; break; case SCI_GETPRINTWRAPMODE: - return printParameters.wrapState; + return view.printParameters.wrapState; case SCI_GETSTYLEAT: if (static_cast(wParam) >= pdoc->Length()) @@ -8052,18 +5950,26 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { break; case SCI_SETBUFFEREDDRAW: - bufferedDraw = wParam != 0; + view.bufferedDraw = wParam != 0; break; case SCI_GETBUFFEREDDRAW: - return bufferedDraw; + return view.bufferedDraw; case SCI_GETTWOPHASEDRAW: - return twoPhaseDraw; + return view.phasesDraw == EditView::phasesTwo; case SCI_SETTWOPHASEDRAW: - twoPhaseDraw = wParam != 0; - InvalidateStyleRedraw(); + if (view.SetTwoPhaseDraw(wParam != 0)) + InvalidateStyleRedraw(); + break; + + case SCI_GETPHASESDRAW: + return view.phasesDraw; + + case SCI_SETPHASESDRAW: + if (view.SetPhasesDraw(static_cast(wParam))) + InvalidateStyleRedraw(); break; case SCI_SETFONTQUALITY: @@ -8087,6 +5993,23 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_GETTABWIDTH: return pdoc->tabInChars; + case SCI_CLEARTABSTOPS: + if (view.ClearTabstops(static_cast(wParam))) { + DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast(wParam)); + NotifyModified(pdoc, mh, NULL); + } + break; + + case SCI_ADDTABSTOP: + if (view.AddTabstop(static_cast(wParam), static_cast(lParam))) { + DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast(wParam)); + NotifyModified(pdoc, mh, NULL); + } + break; + + case SCI_GETNEXTTABSTOP: + return view.GetNextTabstop(static_cast(wParam), static_cast(lParam)); + case SCI_SETINDENT: pdoc->indentInChars = static_cast(wParam); if (pdoc->indentInChars != 0) @@ -8197,23 +6120,23 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { return vs.wrapIndentMode; case SCI_SETLAYOUTCACHE: - llc.SetLevel(static_cast(wParam)); + view.llc.SetLevel(static_cast(wParam)); break; case SCI_GETLAYOUTCACHE: - return llc.GetLevel(); + return view.llc.GetLevel(); case SCI_SETPOSITIONCACHE: - posCache.SetSize(wParam); + view.posCache.SetSize(wParam); break; case SCI_GETPOSITIONCACHE: - return posCache.GetSize(); + return view.posCache.GetSize(); case SCI_SETSCROLLWIDTH: PLATFORM_ASSERT(wParam > 0); if ((wParam > 0) && (wParam != static_cast(scrollWidth))) { - lineWidthMaxSeen = 0; + view.lineWidthMaxSeen = 0; scrollWidth = static_cast(wParam); SetScrollBars(); } @@ -8374,7 +6297,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { RedrawSelMargin(); break; case SCI_MARKERENABLEHIGHLIGHT: - highlightDelimiter.isEnabled = wParam == 1; + marginView.highlightDelimiter.isEnabled = wParam == 1; RedrawSelMargin(); break; case SCI_MARKERSETBACK: @@ -9441,20 +7364,20 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { return multiPasteMode; case SCI_SETADDITIONALCARETSBLINK: - additionalCaretsBlink = wParam != 0; + view.additionalCaretsBlink = wParam != 0; InvalidateCaret(); break; case SCI_GETADDITIONALCARETSBLINK: - return additionalCaretsBlink; + return view.additionalCaretsBlink; case SCI_SETADDITIONALCARETSVISIBLE: - additionalCaretsVisible = wParam != 0; + view.additionalCaretsVisible = wParam != 0; InvalidateCaret(); break; case SCI_GETADDITIONALCARETSVISIBLE: - return additionalCaretsVisible; + return view.additionalCaretsVisible; case SCI_GETSELECTIONS: return sel.Count(); diff --git a/scintilla/src/Editor.h b/scintilla/src/Editor.h index c7d44c16d..7e505bb09 100644 --- a/scintilla/src/Editor.h +++ b/scintilla/src/Editor.h @@ -14,17 +14,6 @@ namespace Scintilla { /** */ -class Caret { -public: - bool active; - bool on; - int period; - - Caret(); -}; - -/** - */ class Timer { public: bool ticking; @@ -160,16 +149,9 @@ struct WrapPending { } }; -struct PrintParameters { - int magnification; - int colourMode; - WrapMode wrapState; - PrintParameters(); -}; - /** */ -class Editor : public DocWatcher { +class Editor : public EditModel, public DocWatcher { // Private so Editor objects can not be copied Editor(const Editor &); Editor &operator=(const Editor &); @@ -189,32 +171,17 @@ protected: // ScintillaBase subclass needs access to much of Editor Point sizeRGBAImage; float scaleRGBAImage; - PrintParameters printParameters; + MarginView marginView; + EditView view; int cursorMode; - // Highlight current folding block - HighlightDelimiter highlightDelimiter; - bool hasFocus; - bool hideSelection; - bool inOverstrike; - bool drawOverstrikeCaret; bool mouseDownCaptures; - /** In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to - * the screen. This avoids flashing but is about 30% slower. */ - bool bufferedDraw; - /** In twoPhaseDraw mode, drawing is performed in two phases, first the background - * and then the foreground. This avoids chopping off characters that overlap the next run. */ - bool twoPhaseDraw; - - int xOffset; ///< Horizontal scrolled amount in pixels int xCaretMargin; ///< Ensure this many pixels visible on both sides of caret bool horizontalScrollBarVisible; int scrollWidth; - bool trackLineWidth; - int lineWidthMaxSeen; bool verticalScrollBarVisible; bool endAtLastLine; int caretSticky; @@ -223,25 +190,11 @@ protected: // ScintillaBase subclass needs access to much of Editor bool multipleSelection; bool additionalSelectionTyping; int multiPasteMode; - bool additionalCaretsBlink; - bool additionalCaretsVisible; int virtualSpaceOptions; - Surface *pixmapLine; - Surface *pixmapSelMargin; - Surface *pixmapSelPattern; - Surface *pixmapSelPatternOffset1; - Surface *pixmapIndentGuide; - Surface *pixmapIndentGuideHighlight; - - LineLayoutCache llc; - PositionCache posCache; - SpecialRepresentations reprs; - KeyMap kmap; - Caret caret; Timer timer; Timer autoScrollTimer; enum { autoScrollDelay = 200 }; @@ -257,7 +210,6 @@ protected: // ScintillaBase subclass needs access to much of Editor Point ptMouseLast; enum { ddNone, ddInitial, ddDragging } inDragDrop; bool dropWentOutside; - SelectionPosition posDrag; SelectionPosition posDrop; int hotSpotClickPos; int lastXChosen; @@ -274,9 +226,6 @@ protected: // ScintillaBase subclass needs access to much of Editor int lengthForEncode; int needUpdateUI; - Position braces[2]; - int bracesMatchStyle; - int highlightGuideColumn; enum { notPainting, painting, paintAbandoned } paintState; bool paintAbandonedByStyling; @@ -288,8 +237,6 @@ protected: // ScintillaBase subclass needs access to much of Editor int modEventMask; SelectionText drag; - Selection sel; - bool primarySelection; int caretXPolicy; int caretXSlop; ///< Ensure this many pixels visible on both sides of caret @@ -304,21 +251,13 @@ protected: // ScintillaBase subclass needs access to much of Editor bool recordingMacro; - int foldFlags; int foldAutomatic; - ContractionState cs; - - // Hotspot support - Range hotspot; // Wrapping support - int wrapWidth; WrapPending wrapPending; bool convertPastes; - Document *pdoc; - Editor(); virtual ~Editor(); virtual void Initialise() = 0; @@ -333,23 +272,23 @@ protected: // ScintillaBase subclass needs access to much of Editor // The top left visible point in main window coordinates. Will be 0,0 except for // scroll views where it will be equivalent to the current scroll position. - virtual Point GetVisibleOriginInMain(); - Point DocumentPointFromView(Point ptView); // Convert a point from view space to document + virtual Point GetVisibleOriginInMain() const; + Point DocumentPointFromView(Point ptView) const; // Convert a point from view space to document int TopLineOfMain() const; // Return the line at Main's y coordinate 0 - virtual PRectangle GetClientRectangle(); + virtual PRectangle GetClientRectangle() const; virtual PRectangle GetClientDrawingRectangle(); - PRectangle GetTextRectangle(); + PRectangle GetTextRectangle() const; - int LinesOnScreen(); - int LinesToScroll(); - int MaxScrollPos(); + virtual int LinesOnScreen() const; + int LinesToScroll() const; + int MaxScrollPos() const; SelectionPosition ClampPositionIntoDocument(SelectionPosition sp) const; Point LocationFromPosition(SelectionPosition pos); Point LocationFromPosition(int pos); int XFromPosition(int pos); int XFromPosition(SelectionPosition sp); SelectionPosition SPositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false, bool virtualSpace=true); - int PositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false); + int PositionFromLocation(Point pt, bool canReturnInvalid = false, bool charPosition = false); SelectionPosition SPositionFromLineX(int lineDoc, int x); int PositionFromLineX(int line, int x); int LineFromLocation(Point pt) const; @@ -360,7 +299,7 @@ protected: // ScintillaBase subclass needs access to much of Editor virtual void DiscardOverdraw(); virtual void Redraw(); void RedrawSelMargin(int line=-1, bool allAfter=false); - PRectangle RectangleFromRange(Range r); + PRectangle RectangleFromRange(Range r, int overlap); void InvalidateRange(int start, int end); bool UserVirtualSpace() const { @@ -383,8 +322,8 @@ protected: // ScintillaBase subclass needs access to much of Editor bool SelectionContainsProtected(); int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true) const; SelectionPosition MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd=true) const; - int MovePositionTo(SelectionPosition newPos, Selection::selTypes sel=Selection::noSel, bool ensureVisible=true); - int MovePositionTo(int newPos, Selection::selTypes sel=Selection::noSel, bool ensureVisible=true); + int MovePositionTo(SelectionPosition newPos, Selection::selTypes selt=Selection::noSel, bool ensureVisible=true); + int MovePositionTo(int newPos, Selection::selTypes selt=Selection::noSel, bool ensureVisible=true); SelectionPosition MovePositionSoVisible(SelectionPosition pos, int moveDir); SelectionPosition MovePositionSoVisible(int pos, int moveDir); Point PointMainCaret(); @@ -431,30 +370,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void LinesJoin(); void LinesSplit(int pixelWidth); - int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) const; void PaintSelMargin(Surface *surface, PRectangle &rc); - LineLayout *RetrieveLineLayout(int lineNumber); - void LayoutLine(int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, - int width=LineLayout::wrapWidthInfinite); - ColourDesired SelectionBackground(const ViewStyle &vsDraw, bool main) const; - ColourDesired TextBackground(const ViewStyle &vsDraw, ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) const; - void DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight); - static void DrawWrapMarker(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourDesired wrapColour); - void DrawEOL(Surface *surface, const ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll, - int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart, - ColourOptional background); - static void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw, - int xStart, PRectangle rcLine, LineLayout *ll, int subLine); - void DrawIndicators(Surface *surface, const ViewStyle &vsDraw, int line, int xStart, - PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under); - void DrawAnnotation(Surface *surface, const ViewStyle &vsDraw, int line, int xStart, - PRectangle rcLine, LineLayout *ll, int subLine); - void DrawLine(Surface *surface, const ViewStyle &vsDraw, int line, int lineVisible, int xStart, - PRectangle rcLine, LineLayout *ll, int subLine); - void DrawBlockCaret(Surface *surface, const ViewStyle &vsDraw, LineLayout *ll, int subLine, - int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) const; - void DrawCarets(Surface *surface, const ViewStyle &vsDraw, int line, int xStart, - PRectangle rcLine, LineLayout *ll, int subLine); void RefreshPixMaps(Surface *surfaceWindow); void Paint(Surface *surfaceWindow, PRectangle rcArea); long FormatRange(bool draw, Sci_RangeToFormat *pfr); @@ -470,7 +386,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void FilterSelections(); int InsertSpace(int position, unsigned int spaces); void AddChar(char ch); - virtual void AddCharUTF(char *s, unsigned int len, bool treatAsDBCS=false); + virtual void AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS=false); void InsertPaste(const char *text, int len); enum PasteShape { pasteStream=0, pasteRectangular = 1, pasteLine = 2 }; void InsertPasteShape(const char *text, int len, PasteShape shape); @@ -530,7 +446,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam); void ContainerNeedsUpdate(int flags); - void PageMove(int direction, Selection::selTypes sel=Selection::noSel, bool stuttered = false); + void PageMove(int direction, Selection::selTypes selt=Selection::noSel, bool stuttered = false); enum { cmSame, cmUpper, cmLower }; virtual std::string CaseMapString(const std::string &s, int caseMapping); void ChangeCaseOfSelection(int caseMapping); @@ -538,8 +454,8 @@ protected: // ScintillaBase subclass needs access to much of Editor void Duplicate(bool forLine); virtual void CancelModes(); void NewLine(); - void CursorUpOrDown(int direction, Selection::selTypes sel=Selection::noSel); - void ParaUpOrDown(int direction, Selection::selTypes sel=Selection::noSel); + void CursorUpOrDown(int direction, Selection::selTypes selt=Selection::noSel); + void ParaUpOrDown(int direction, Selection::selTypes selt=Selection::noSel); int StartEndDisplayLine(int pos, bool start); virtual int KeyCommand(unsigned int iMessage); virtual int KeyDefault(int /* key */, int /*modifiers*/); @@ -569,7 +485,7 @@ protected: // ScintillaBase subclass needs access to much of Editor /** PositionInSelection returns true if position in selection. */ bool PositionInSelection(int pos); bool PointInSelection(Point pt); - bool PointInSelMargin(Point pt); + bool PointInSelMargin(Point pt) const; Window::Cursor GetMarginCursor(Point pt) const; void TrimAndSetSelection(int currentPos_, int anchor_); void LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine); @@ -584,7 +500,13 @@ protected: // ScintillaBase subclass needs access to much of Editor void Tick(); bool Idle(); - virtual void SetTicking(bool on) = 0; + virtual void SetTicking(bool on); + enum TickReason { tickCaret, tickScroll, tickWiden, tickDwell, tickPlatform }; + virtual void TickFor(TickReason reason); + 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) { return false; } virtual void SetMouseCapture(bool on) = 0; virtual bool HaveMouseCapture() = 0; diff --git a/scintilla/src/LineMarker.cxx b/scintilla/src/LineMarker.cxx index 8ae815a65..eb36f1bbc 100644 --- a/scintilla/src/LineMarker.cxx +++ b/scintilla/src/LineMarker.cxx @@ -72,23 +72,23 @@ static void DrawMinus(Surface *surface, int centreX, int centreY, int armSize, C } void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, typeOfFold tFold, int marginStyle) const { - ColourDesired head = back; - ColourDesired body = back; - ColourDesired tail = back; + ColourDesired colourHead = back; + ColourDesired colourBody = back; + ColourDesired colourTail = back; switch (tFold) { case LineMarker::head : case LineMarker::headWithTail : - head = backSelected; - tail = backSelected; + colourHead = backSelected; + colourTail = backSelected; break; case LineMarker::body : - head = backSelected; - body = backSelected; + colourHead = backSelected; + colourBody = backSelected; break; case LineMarker::tail : - body = backSelected; - tail = backSelected; + colourBody = backSelected; + colourTail = backSelected; break; default : // LineMarker::undefined @@ -192,69 +192,69 @@ void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharac // An invisible marker so don't draw anything } else if (markType == SC_MARK_VLINE) { - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, static_cast(rcWhole.bottom)); } else if (markType == SC_MARK_LCORNER) { - surface->PenColour(tail); + surface->PenColour(colourTail); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, centreY); surface->LineTo(static_cast(rc.right) - 1, centreY); } else if (markType == SC_MARK_TCORNER) { - surface->PenColour(tail); + surface->PenColour(colourTail); surface->MoveTo(centreX, centreY); surface->LineTo(static_cast(rc.right) - 1, centreY); - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, centreY + 1); - surface->PenColour(head); + surface->PenColour(colourHead); surface->LineTo(centreX, static_cast(rcWhole.bottom)); } else if (markType == SC_MARK_LCORNERCURVE) { - surface->PenColour(tail); + surface->PenColour(colourTail); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, centreY-3); surface->LineTo(centreX+3, centreY); surface->LineTo(static_cast(rc.right) - 1, centreY); } else if (markType == SC_MARK_TCORNERCURVE) { - surface->PenColour(tail); + surface->PenColour(colourTail); surface->MoveTo(centreX, centreY-3); surface->LineTo(centreX+3, centreY); surface->LineTo(static_cast(rc.right) - 1, centreY); - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, centreY-2); - surface->PenColour(head); + surface->PenColour(colourHead); surface->LineTo(centreX, static_cast(rcWhole.bottom)); } else if (markType == SC_MARK_BOXPLUS) { - DrawBox(surface, centreX, centreY, blobSize, fore, head); - DrawPlus(surface, centreX, centreY, blobSize, tail); + DrawBox(surface, centreX, centreY, blobSize, fore, colourHead); + DrawPlus(surface, centreX, centreY, blobSize, colourTail); } else if (markType == SC_MARK_BOXPLUSCONNECTED) { if (tFold == LineMarker::headWithTail) - surface->PenColour(tail); + surface->PenColour(colourTail); else - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, centreY + blobSize); surface->LineTo(centreX, static_cast(rcWhole.bottom)); - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, centreY - blobSize); - DrawBox(surface, centreX, centreY, blobSize, fore, head); - DrawPlus(surface, centreX, centreY, blobSize, tail); + DrawBox(surface, centreX, centreY, blobSize, fore, colourHead); + DrawPlus(surface, centreX, centreY, blobSize, colourTail); if (tFold == LineMarker::body) { - surface->PenColour(tail); + surface->PenColour(colourTail); surface->MoveTo(centreX + 1, centreY + blobSize); surface->LineTo(centreX + blobSize + 1, centreY + blobSize); @@ -265,27 +265,27 @@ void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharac surface->LineTo(centreX + blobSize + 1, centreY - blobSize); } } else if (markType == SC_MARK_BOXMINUS) { - DrawBox(surface, centreX, centreY, blobSize, fore, head); - DrawMinus(surface, centreX, centreY, blobSize, tail); + DrawBox(surface, centreX, centreY, blobSize, fore, colourHead); + DrawMinus(surface, centreX, centreY, blobSize, colourTail); - surface->PenColour(head); + surface->PenColour(colourHead); surface->MoveTo(centreX, centreY + blobSize); surface->LineTo(centreX, static_cast(rcWhole.bottom)); } else if (markType == SC_MARK_BOXMINUSCONNECTED) { - DrawBox(surface, centreX, centreY, blobSize, fore, head); - DrawMinus(surface, centreX, centreY, blobSize, tail); + DrawBox(surface, centreX, centreY, blobSize, fore, colourHead); + DrawMinus(surface, centreX, centreY, blobSize, colourTail); - surface->PenColour(head); + surface->PenColour(colourHead); surface->MoveTo(centreX, centreY + blobSize); surface->LineTo(centreX, static_cast(rcWhole.bottom)); - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, centreY - blobSize); if (tFold == LineMarker::body) { - surface->PenColour(tail); + surface->PenColour(colourTail); surface->MoveTo(centreX + 1, centreY + blobSize); surface->LineTo(centreX + blobSize + 1, centreY + blobSize); @@ -296,43 +296,43 @@ void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharac surface->LineTo(centreX + blobSize + 1, centreY - blobSize); } } else if (markType == SC_MARK_CIRCLEPLUS) { - DrawCircle(surface, centreX, centreY, blobSize, fore, head); - DrawPlus(surface, centreX, centreY, blobSize, tail); + DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead); + DrawPlus(surface, centreX, centreY, blobSize, colourTail); } else if (markType == SC_MARK_CIRCLEPLUSCONNECTED) { if (tFold == LineMarker::headWithTail) - surface->PenColour(tail); + surface->PenColour(colourTail); else - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, centreY + blobSize); surface->LineTo(centreX, static_cast(rcWhole.bottom)); - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, centreY - blobSize); - DrawCircle(surface, centreX, centreY, blobSize, fore, head); - DrawPlus(surface, centreX, centreY, blobSize, tail); + DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead); + DrawPlus(surface, centreX, centreY, blobSize, colourTail); } else if (markType == SC_MARK_CIRCLEMINUS) { - surface->PenColour(head); + surface->PenColour(colourHead); surface->MoveTo(centreX, centreY + blobSize); surface->LineTo(centreX, static_cast(rcWhole.bottom)); - DrawCircle(surface, centreX, centreY, blobSize, fore, head); - DrawMinus(surface, centreX, centreY, blobSize, tail); + DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead); + DrawMinus(surface, centreX, centreY, blobSize, colourTail); } else if (markType == SC_MARK_CIRCLEMINUSCONNECTED) { - surface->PenColour(head); + surface->PenColour(colourHead); surface->MoveTo(centreX, centreY + blobSize); surface->LineTo(centreX, static_cast(rcWhole.bottom)); - surface->PenColour(body); + surface->PenColour(colourBody); surface->MoveTo(centreX, static_cast(rcWhole.top)); surface->LineTo(centreX, centreY - blobSize); - DrawCircle(surface, centreX, centreY, blobSize, fore, head); - DrawMinus(surface, centreX, centreY, blobSize, tail); + DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead); + DrawMinus(surface, centreX, centreY, blobSize, colourTail); } else if (markType >= SC_MARK_CHARACTER) { char character[1]; diff --git a/scintilla/src/MarginView.cxx b/scintilla/src/MarginView.cxx new file mode 100644 index 000000000..9297eb4a8 --- /dev/null +++ b/scintilla/src/MarginView.cxx @@ -0,0 +1,455 @@ +// Scintilla source code edit control +/** @file MarginView.cxx + ** Defines the appearance of the editor margin. + **/ +// Copyright 1998-2014 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "ILexer.h" +#include "Scintilla.h" + +#include "StringCopy.h" +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" +#include "ContractionState.h" +#include "CellBuffer.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "XPM.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "CharClassify.h" +#include "Decoration.h" +#include "CaseFolder.h" +#include "Document.h" +#include "UniConversion.h" +#include "Selection.h" +#include "PositionCache.h" +#include "EditModel.h" +#include "MarginView.h" +#include "EditView.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +void DrawWrapMarker(Surface *surface, PRectangle rcPlace, + bool isEndMarker, ColourDesired wrapColour) { + surface->PenColour(wrapColour); + + enum { xa = 1 }; // gap before start + int w = static_cast(rcPlace.right - rcPlace.left) - xa - 1; + + bool xStraight = isEndMarker; // x-mirrored symbol for start marker + + int x0 = static_cast(xStraight ? rcPlace.left : rcPlace.right - 1); + int y0 = static_cast(rcPlace.top); + + int dy = static_cast(rcPlace.bottom - rcPlace.top) / 5; + int y = static_cast(rcPlace.bottom - rcPlace.top) / 2 + dy; + + struct Relative { + Surface *surface; + int xBase; + int xDir; + int yBase; + int yDir; + void MoveTo(int xRelative, int yRelative) { + surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative); + } + void LineTo(int xRelative, int yRelative) { + surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative); + } + }; + Relative rel = { surface, x0, xStraight ? 1 : -1, y0, 1 }; + + // arrow head + rel.MoveTo(xa, y); + rel.LineTo(xa + 2 * w / 3, y - dy); + rel.MoveTo(xa, y); + rel.LineTo(xa + 2 * w / 3, y + dy); + + // arrow body + rel.MoveTo(xa, y); + rel.LineTo(xa + w, y); + rel.LineTo(xa + w, y - 2 * dy); + rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not... + y - 2 * dy); +} + +MarginView::MarginView() { + pixmapSelMargin = 0; + pixmapSelPattern = 0; + pixmapSelPatternOffset1 = 0; +} + +void MarginView::DropGraphics(bool freeObjects) { + if (freeObjects) { + delete pixmapSelMargin; + pixmapSelMargin = 0; + delete pixmapSelPattern; + pixmapSelPattern = 0; + delete pixmapSelPatternOffset1; + pixmapSelPatternOffset1 = 0; + } else { + if (pixmapSelMargin) + pixmapSelMargin->Release(); + if (pixmapSelPattern) + pixmapSelPattern->Release(); + if (pixmapSelPatternOffset1) + pixmapSelPatternOffset1->Release(); + } +} + +void MarginView::AllocateGraphics(const ViewStyle &vsDraw) { + if (!pixmapSelMargin) + pixmapSelMargin = Surface::Allocate(vsDraw.technology); + if (!pixmapSelPattern) + pixmapSelPattern = Surface::Allocate(vsDraw.technology); + if (!pixmapSelPatternOffset1) + pixmapSelPatternOffset1 = Surface::Allocate(vsDraw.technology); +} + +void MarginView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) { + if (!pixmapSelPattern->Initialised()) { + const int patternSize = 8; + pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wid); + pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wid); + // This complex procedure is to reproduce the checkerboard dithered pattern used by windows + // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half + // way between the chrome colour and the chrome highlight colour making a nice transition + // between the window chrome and the content area. And it works in low colour depths. + PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize); + + // Initialize default colours based on the chrome colour scheme. Typically the highlight is white. + ColourDesired colourFMFill = vsDraw.selbar; + ColourDesired colourFMStripes = vsDraw.selbarlight; + + if (!(vsDraw.selbarlight == ColourDesired(0xff, 0xff, 0xff))) { + // User has chosen an unusual chrome colour scheme so just use the highlight edge colour. + // (Typically, the highlight colour is white.) + colourFMFill = vsDraw.selbarlight; + } + + if (vsDraw.foldmarginColour.isSet) { + // override default fold margin colour + colourFMFill = vsDraw.foldmarginColour; + } + if (vsDraw.foldmarginHighlightColour.isSet) { + // override default fold margin highlight colour + colourFMStripes = vsDraw.foldmarginHighlightColour; + } + + pixmapSelPattern->FillRectangle(rcPattern, colourFMFill); + pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes); + for (int y = 0; y < patternSize; y++) { + for (int x = y % 2; x < patternSize; x += 2) { + PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1); + pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes); + pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill); + } + } + } +} + +static int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault, const ViewStyle &vs) { + if (vs.markers[markerCheck].markType == SC_MARK_EMPTY) + return markerDefault; + return markerCheck; +} + +void MarginView::PaintMargin(Surface *surface, int topLine, PRectangle rc, PRectangle rcMargin, + const EditModel &model, const ViewStyle &vs) { + + PRectangle rcSelMargin = rcMargin; + rcSelMargin.right = rcMargin.left; + if (rcSelMargin.bottom < rc.bottom) + rcSelMargin.bottom = rc.bottom; + + Point ptOrigin = model.GetVisibleOriginInMain(); + FontAlias fontLineNumber = vs.styles[STYLE_LINENUMBER].font; + for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) { + if (vs.ms[margin].width > 0) { + + rcSelMargin.left = rcSelMargin.right; + rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width; + + if (vs.ms[margin].style != SC_MARGIN_NUMBER) { + if (vs.ms[margin].mask & SC_MASK_FOLDERS) { + // Required because of special way brush is created for selection margin + // Ensure patterns line up when scrolling with separate margin view + // by choosing correctly aligned variant. + bool invertPhase = static_cast(ptOrigin.y) & 1; + surface->FillRectangle(rcSelMargin, + invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1); + } else { + ColourDesired colour; + switch (vs.ms[margin].style) { + case SC_MARGIN_BACK: + colour = vs.styles[STYLE_DEFAULT].back; + break; + case SC_MARGIN_FORE: + colour = vs.styles[STYLE_DEFAULT].fore; + break; + default: + colour = vs.styles[STYLE_LINENUMBER].back; + break; + } + surface->FillRectangle(rcSelMargin, colour); + } + } else { + surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back); + } + + const int lineStartPaint = static_cast(rcMargin.top + ptOrigin.y) / vs.lineHeight; + int visibleLine = model.TopLineOfMain() + lineStartPaint; + int yposScreen = lineStartPaint * vs.lineHeight - static_cast(ptOrigin.y); + // Work out whether the top line is whitespace located after a + // lessening of fold level which implies a 'fold tail' but which should not + // be displayed until the last of a sequence of whitespace. + bool needWhiteClosure = false; + if (vs.ms[margin].mask & SC_MASK_FOLDERS) { + int level = model.pdoc->GetLevel(model.cs.DocFromDisplay(visibleLine)); + if (level & SC_FOLDLEVELWHITEFLAG) { + int lineBack = model.cs.DocFromDisplay(visibleLine); + int levelPrev = level; + while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) { + lineBack--; + levelPrev = model.pdoc->GetLevel(lineBack); + } + if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) { + if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK)) + needWhiteClosure = true; + } + } + if (highlightDelimiter.isEnabled) { + int lastLine = model.cs.DocFromDisplay(topLine + model.LinesOnScreen()) + 1; + model.pdoc->GetHighlightDelimiters(highlightDelimiter, model.pdoc->LineFromPosition(model.sel.MainCaret()), lastLine); + } + } + + // Old code does not know about new markers needed to distinguish all cases + const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID, + SC_MARKNUM_FOLDEROPEN, vs); + const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND, + SC_MARKNUM_FOLDER, vs); + + while ((visibleLine < model.cs.LinesDisplayed()) && yposScreen < rc.bottom) { + + PLATFORM_ASSERT(visibleLine < model.cs.LinesDisplayed()); + const int lineDoc = model.cs.DocFromDisplay(visibleLine); + PLATFORM_ASSERT(model.cs.GetVisible(lineDoc)); + const bool firstSubLine = visibleLine == model.cs.DisplayFromDoc(lineDoc); + const bool lastSubLine = visibleLine == model.cs.DisplayLastFromDoc(lineDoc); + + int marks = model.pdoc->GetMark(lineDoc); + if (!firstSubLine) + marks = 0; + + bool headWithTail = false; + + if (vs.ms[margin].mask & SC_MASK_FOLDERS) { + // Decide which fold indicator should be displayed + const int level = model.pdoc->GetLevel(lineDoc); + const int levelNext = model.pdoc->GetLevel(lineDoc + 1); + const int levelNum = level & SC_FOLDLEVELNUMBERMASK; + const int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK; + if (level & SC_FOLDLEVELHEADERFLAG) { + if (firstSubLine) { + if (levelNum < levelNextNum) { + if (model.cs.GetExpanded(lineDoc)) { + if (levelNum == SC_FOLDLEVELBASE) + marks |= 1 << SC_MARKNUM_FOLDEROPEN; + else + marks |= 1 << folderOpenMid; + } else { + if (levelNum == SC_FOLDLEVELBASE) + marks |= 1 << SC_MARKNUM_FOLDER; + else + marks |= 1 << folderEnd; + } + } else if (levelNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + } else { + if (levelNum < levelNextNum) { + if (model.cs.GetExpanded(lineDoc)) { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } else if (levelNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + } else if (levelNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + } + needWhiteClosure = false; + const int firstFollowupLine = model.cs.DocFromDisplay(model.cs.DisplayFromDoc(lineDoc + 1)); + const int firstFollowupLineLevel = model.pdoc->GetLevel(firstFollowupLine); + const int secondFollowupLineLevelNum = model.pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK; + if (!model.cs.GetExpanded(lineDoc)) { + if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) && + (levelNum > secondFollowupLineLevelNum)) + needWhiteClosure = true; + + if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine)) + headWithTail = true; + } + } else if (level & SC_FOLDLEVELWHITEFLAG) { + if (needWhiteClosure) { + if (levelNext & SC_FOLDLEVELWHITEFLAG) { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } else if (levelNextNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; + needWhiteClosure = false; + } else { + marks |= 1 << SC_MARKNUM_FOLDERTAIL; + needWhiteClosure = false; + } + } else if (levelNum > SC_FOLDLEVELBASE) { + if (levelNextNum < levelNum) { + if (levelNextNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; + } else { + marks |= 1 << SC_MARKNUM_FOLDERTAIL; + } + } else { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + } + } else if (levelNum > SC_FOLDLEVELBASE) { + if (levelNextNum < levelNum) { + needWhiteClosure = false; + if (levelNext & SC_FOLDLEVELWHITEFLAG) { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + needWhiteClosure = true; + } else if (lastSubLine) { + if (levelNextNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; + } else { + marks |= 1 << SC_MARKNUM_FOLDERTAIL; + } + } else { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + } else { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + } + } + + marks &= vs.ms[margin].mask; + + PRectangle rcMarker = rcSelMargin; + rcMarker.top = static_cast(yposScreen); + rcMarker.bottom = static_cast(yposScreen + vs.lineHeight); + if (vs.ms[margin].style == SC_MARGIN_NUMBER) { + if (firstSubLine) { + char number[100] = ""; + if (lineDoc >= 0) + sprintf(number, "%d", lineDoc + 1); + if (model.foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) { + if (model.foldFlags & SC_FOLDFLAG_LEVELNUMBERS) { + int lev = model.pdoc->GetLevel(lineDoc); + sprintf(number, "%c%c %03X %03X", + (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_', + (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_', + lev & SC_FOLDLEVELNUMBERMASK, + lev >> 16 + ); + } else { + int state = model.pdoc->GetLineState(lineDoc); + sprintf(number, "%0X", state); + } + } + PRectangle rcNumber = rcMarker; + // Right justify + XYPOSITION width = surface->WidthText(fontLineNumber, number, static_cast(strlen(number))); + XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding; + rcNumber.left = xpos; + DrawTextNoClipPhase(surface, rcNumber, vs.styles[STYLE_LINENUMBER], + rcNumber.top + vs.maxAscent, number, static_cast(strlen(number)), drawAll); + } else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) { + PRectangle rcWrapMarker = rcMarker; + rcWrapMarker.right -= 3; + rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth; + DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore); + } + } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) { + if (firstSubLine) { + const StyledText stMargin = model.pdoc->MarginStyledText(lineDoc); + if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) { + surface->FillRectangle(rcMarker, + vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back); + if (vs.ms[margin].style == SC_MARGIN_RTEXT) { + int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin); + rcMarker.left = rcMarker.right - width - 3; + } + DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, + stMargin, 0, stMargin.length, drawAll); + } + } + } + + if (marks) { + for (int markBit = 0; (markBit < 32) && marks; markBit++) { + if (marks & 1) { + LineMarker::typeOfFold tFold = LineMarker::undefined; + if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) { + if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) { + tFold = LineMarker::body; + } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) { + if (firstSubLine) { + tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head; + } else { + if (model.cs.GetExpanded(lineDoc) || headWithTail) { + tFold = LineMarker::body; + } else { + tFold = LineMarker::undefined; + } + } + } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) { + tFold = LineMarker::tail; + } + } + vs.markers[markBit].Draw(surface, rcMarker, fontLineNumber, tFold, vs.ms[margin].style); + } + marks >>= 1; + } + } + + visibleLine++; + yposScreen += vs.lineHeight; + } + } + } + + PRectangle rcBlankMargin = rcMargin; + rcBlankMargin.left = rcSelMargin.right; + surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back); +} + +#ifdef SCI_NAMESPACE +} +#endif + diff --git a/scintilla/src/MarginView.h b/scintilla/src/MarginView.h new file mode 100644 index 000000000..465095f29 --- /dev/null +++ b/scintilla/src/MarginView.h @@ -0,0 +1,41 @@ +// Scintilla source code edit control +/** @file MarginView.h + ** Defines the appearance of the editor margin. + **/ +// Copyright 1998-2014 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef MARGINVIEW_H +#define MARGINVIEW_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +void DrawWrapMarker(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourDesired wrapColour); + +/** +* MarginView draws the margins. +*/ +class MarginView { +public: + Surface *pixmapSelMargin; + Surface *pixmapSelPattern; + Surface *pixmapSelPatternOffset1; + // Highlight current folding block + HighlightDelimiter highlightDelimiter; + + MarginView(); + + void DropGraphics(bool freeObjects); + void AllocateGraphics(const ViewStyle &vsDraw); + void RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw); + void PaintMargin(Surface *surface, int topLine, PRectangle rc, PRectangle rcMargin, + const EditModel &model, const ViewStyle &vs); +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif diff --git a/scintilla/src/PerLine.cxx b/scintilla/src/PerLine.cxx index 8b0dbc44b..8fd96cbed 100644 --- a/scintilla/src/PerLine.cxx +++ b/scintilla/src/PerLine.cxx @@ -7,6 +7,7 @@ #include +#include #include #include "Platform.h" @@ -484,3 +485,72 @@ int LineAnnotation::Lines(int line) const { else return 0; } + +LineTabstops::~LineTabstops() { + Init(); +} + +void LineTabstops::Init() { + for (int line = 0; line < tabstops.Length(); line++) { + delete tabstops[line]; + } + tabstops.DeleteAll(); +} + +void LineTabstops::InsertLine(int line) { + if (tabstops.Length()) { + tabstops.EnsureLength(line); + tabstops.Insert(line, 0); + } +} + +void LineTabstops::RemoveLine(int line) { + if (tabstops.Length() > line) { + delete tabstops[line]; + tabstops.Delete(line); + } +} + +bool LineTabstops::ClearTabstops(int line) { + if (line < tabstops.Length()) { + TabstopList *tl = tabstops[line]; + if (tl) { + tl->clear(); + return true; + } + } + return false; +} + +bool LineTabstops::AddTabstop(int line, int x) { + tabstops.EnsureLength(line + 1); + if (!tabstops[line]) { + tabstops[line] = new TabstopList(); + } + + TabstopList *tl = tabstops[line]; + if (tl) { + // tabstop positions are kept in order - insert in the right place + std::vector::iterator it = std::lower_bound(tl->begin(), tl->end(), x); + // don't insert duplicates + if (it == tl->end() || *it != x) { + tl->insert(it, x); + return true; + } + } + return false; +} + +int LineTabstops::GetNextTabstop(int line, int x) const { + if (line < tabstops.Length()) { + TabstopList *tl = tabstops[line]; + if (tl) { + for (size_t i = 0; i < tl->size(); i++) { + if ((*tl)[i] > x) { + return (*tl)[i]; + } + } + } + } + return 0; +} diff --git a/scintilla/src/PerLine.h b/scintilla/src/PerLine.h index 70d0023e4..4bf1c88fd 100644 --- a/scintilla/src/PerLine.h +++ b/scintilla/src/PerLine.h @@ -112,6 +112,23 @@ public: int Lines(int line) const; }; +typedef std::vector TabstopList; + +class LineTabstops : public PerLine { + SplitVector tabstops; +public: + LineTabstops() { + } + virtual ~LineTabstops(); + virtual void Init(); + virtual void InsertLine(int line); + virtual void RemoveLine(int line); + + bool ClearTabstops(int line); + bool AddTabstop(int line, int x); + int GetNextTabstop(int line, int x) const; +}; + #ifdef SCI_NAMESPACE } #endif diff --git a/scintilla/src/PositionCache.cxx b/scintilla/src/PositionCache.cxx index 9e55c1a82..53767e032 100644 --- a/scintilla/src/PositionCache.cxx +++ b/scintilla/src/PositionCache.cxx @@ -438,13 +438,12 @@ void BreakFinder::Insert(int val) { } } -BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, int lineStart_, int lineEnd_, int posLineStart_, +BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, int posLineStart_, int xStart, bool breakForSelection, const Document *pdoc_, const SpecialRepresentations *preprs_) : ll(ll_), - lineStart(lineStart_), - lineEnd(lineEnd_), + lineRange(lineRange_), posLineStart(posLineStart_), - nextBreak(lineStart_), + nextBreak(lineRange_.start), saeCurrentPos(0), saeNext(0), subBreak(-1), @@ -455,15 +454,15 @@ BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, int lineS // Search for first visible break // First find the first visible character if (xStart > 0.0f) - nextBreak = ll->FindBefore(static_cast(xStart), lineStart, lineEnd); + nextBreak = ll->FindBefore(static_cast(xStart), lineRange.start, lineRange.end); // Now back to a style break - while ((nextBreak > lineStart) && (ll->styles[nextBreak] == ll->styles[nextBreak - 1])) { + while ((nextBreak > lineRange.start) && (ll->styles[nextBreak] == ll->styles[nextBreak - 1])) { nextBreak--; } if (breakForSelection) { SelectionPosition posStart(posLineStart); - SelectionPosition posEnd(posLineStart + lineEnd); + SelectionPosition posEnd(posLineStart + lineRange.end); SelectionSegment segmentLine(posStart, posEnd); for (size_t r=0; rCount(); r++) { SelectionSegment portion = psel->Range(r).Intersect(segmentLine); @@ -477,7 +476,7 @@ BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, int lineS } Insert(ll->edgeColumn); - Insert(lineEnd); + Insert(lineRange.end); saeNext = (!selAndEdge.empty()) ? selAndEdge[0] : -1; } @@ -487,19 +486,19 @@ BreakFinder::~BreakFinder() { TextSegment BreakFinder::Next() { if (subBreak == -1) { int prev = nextBreak; - while (nextBreak < lineEnd) { + while (nextBreak < lineRange.end) { int charWidth = 1; if (encodingFamily == efUnicode) - charWidth = UTF8DrawBytes(reinterpret_cast(ll->chars) + nextBreak, lineEnd - nextBreak); + charWidth = UTF8DrawBytes(reinterpret_cast(ll->chars) + nextBreak, lineRange.end - nextBreak); else if (encodingFamily == efDBCS) charWidth = pdoc->IsDBCSLeadByte(ll->chars[nextBreak]) ? 2 : 1; const Representation *repr = preprs->RepresentationFromCharacter(ll->chars + nextBreak, charWidth); if (((nextBreak > 0) && (ll->styles[nextBreak] != ll->styles[nextBreak - 1])) || repr || (nextBreak == saeNext)) { - while ((nextBreak >= saeNext) && (saeNext < lineEnd)) { + while ((nextBreak >= saeNext) && (saeNext < lineRange.end)) { saeCurrentPos++; - saeNext = (saeCurrentPos < selAndEdge.size()) ? selAndEdge[saeCurrentPos] : lineEnd; + saeNext = (saeCurrentPos < selAndEdge.size()) ? selAndEdge[saeCurrentPos] : lineRange.end; } if ((nextBreak > prev) || repr) { // Have a segment to report @@ -540,7 +539,7 @@ TextSegment BreakFinder::Next() { } bool BreakFinder::More() const { - return (subBreak >= 0) || (nextBreak < lineEnd); + return (subBreak >= 0) || (nextBreak < lineRange.end); } PositionCacheEntry::PositionCacheEntry() : diff --git a/scintilla/src/PositionCache.h b/scintilla/src/PositionCache.h index 05005e9ac..9d9821f8f 100644 --- a/scintilla/src/PositionCache.h +++ b/scintilla/src/PositionCache.h @@ -104,7 +104,7 @@ class PositionCacheEntry { public: PositionCacheEntry(); ~PositionCacheEntry(); - void Set(unsigned int styleNumber_, const char *s_, unsigned int len_, XYPOSITION *positions_, unsigned int clock); + void Set(unsigned int styleNumber_, const char *s_, unsigned int len_, XYPOSITION *positions_, unsigned int clock_); void Clear(); bool Retrieve(unsigned int styleNumber_, const char *s_, unsigned int len_, XYPOSITION *positions_) const; static unsigned int Hash(unsigned int styleNumber_, const char *s, unsigned int len); @@ -148,8 +148,7 @@ struct TextSegment { // Class to break a line of text into shorter runs at sensible places. class BreakFinder { const LineLayout *ll; - int lineStart; - int lineEnd; + Range lineRange; int posLineStart; int nextBreak; std::vector selAndEdge; @@ -168,7 +167,7 @@ public: enum { lengthStartSubdivision = 300 }; // Try to make each subdivided run lengthEachSubdivision or shorter. enum { lengthEachSubdivision = 100 }; - BreakFinder(const LineLayout *ll_, const Selection *psel, int lineStart_, int lineEnd_, int posLineStart_, + BreakFinder(const LineLayout *ll_, const Selection *psel, Range rangeLine_, int posLineStart_, int xStart, bool breakForSelection, const Document *pdoc_, const SpecialRepresentations *preprs_); ~BreakFinder(); TextSegment Next(); diff --git a/scintilla/src/RESearch.cxx b/scintilla/src/RESearch.cxx index 74db01355..138469f0a 100644 --- a/scintilla/src/RESearch.cxx +++ b/scintilla/src/RESearch.cxx @@ -203,6 +203,7 @@ #include #include +#include #include "CharClassify.h" #include "RESearch.h" @@ -251,22 +252,18 @@ const char bitarr[] = { 1, 2, 4, 8, 16, 32, 64, '\200' }; RESearch::RESearch(CharClassify *charClassTable) { failure = 0; charClass = charClassTable; - Init(); + sta = NOP; /* status of lastpat */ + bol = 0; + std::fill(bittab, bittab + BITBLK, 0); + std::fill(tagstk, tagstk + MAXTAG, 0); + std::fill(nfa, nfa + MAXNFA, 0); + Clear(); } RESearch::~RESearch() { Clear(); } -void RESearch::Init() { - sta = NOP; /* status of lastpat */ - bol = 0; - for (int i = 0; i < MAXTAG; i++) - pat[i].clear(); - for (int j = 0; j < BITBLK; j++) - bittab[j] = 0; -} - void RESearch::Clear() { for (int i = 0; i < MAXTAG; i++) { pat[i].clear(); diff --git a/scintilla/src/RESearch.h b/scintilla/src/RESearch.h index 48533a41c..38875a3f4 100644 --- a/scintilla/src/RESearch.h +++ b/scintilla/src/RESearch.h @@ -46,7 +46,6 @@ public: std::string pat[MAXTAG]; private: - void Init(); void Clear(); void ChSet(unsigned char c); void ChSetWithCase(unsigned char c, bool caseSensitive); diff --git a/scintilla/src/ScintillaBase.cxx b/scintilla/src/ScintillaBase.cxx index d42dfc7ff..ee7818ef2 100644 --- a/scintilla/src/ScintillaBase.cxx +++ b/scintilla/src/ScintillaBase.cxx @@ -50,6 +50,9 @@ #include "Document.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" @@ -73,7 +76,7 @@ void ScintillaBase::Finalise() { popup.Destroy(); } -void ScintillaBase::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) { +void ScintillaBase::AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS) { bool isFillUp = ac.Active() && ac.IsFillUpChar(*s); if (!isFillUp) { Editor::AddCharUTF(s, len, treatAsDBCS); diff --git a/scintilla/src/ScintillaBase.h b/scintilla/src/ScintillaBase.h index 8440ebecc..668abed3c 100644 --- a/scintilla/src/ScintillaBase.h +++ b/scintilla/src/ScintillaBase.h @@ -38,6 +38,8 @@ protected: idcmdSelectAll=16 }; + enum { maxLenInputIME = 200 }; + bool displayPopupMenu; Menu popup; AutoComplete ac; @@ -60,7 +62,7 @@ protected: virtual void Initialise() = 0; virtual void Finalise(); - virtual void AddCharUTF(char *s, unsigned int len, bool treatAsDBCS=false); + virtual void AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS=false); void Command(int cmdId); virtual void CancelModes(); virtual int KeyCommand(unsigned int iMessage); diff --git a/scintilla/src/Selection.cxx b/scintilla/src/Selection.cxx index b46dca890..52ed5774e 100644 --- a/scintilla/src/Selection.cxx +++ b/scintilla/src/Selection.cxx @@ -81,6 +81,11 @@ int SelectionRange::Length() const { } } +void SelectionRange::MoveForInsertDelete(bool insertion, int startChange, int length) { + caret.MoveForInsertDelete(insertion, startChange, length); + anchor.MoveForInsertDelete(insertion, startChange, length); +} + bool SelectionRange::Contains(int pos) const { if (anchor > caret) return (pos >= caret.Position()) && (pos <= anchor.Position()); @@ -283,9 +288,11 @@ int Selection::Length() const { void Selection::MovePositions(bool insertion, int startChange, int length) { for (size_t i=0; i lineHeight) + lineOverlap = lineHeight; someStylesProtected = false; someStylesForceCase = false; @@ -470,6 +476,14 @@ ColourOptional ViewStyle::Background(int marksOfLine, bool caretActive, bool lin return background; } +bool ViewStyle::SelectionBackgroundDrawn() const { + return selColours.back.isSet && + ((selAlpha == SC_ALPHA_NOALPHA) || (selAdditionalAlpha == SC_ALPHA_NOALPHA)); +} + +bool ViewStyle::WhitespaceBackgroundDrawn() const { + return (viewWhitespace != wsInvisible) && (whitespaceColours.back.isSet); +} ColourDesired ViewStyle::WrapColour() const { if (whitespaceColours.fore.isSet) diff --git a/scintilla/src/ViewStyle.h b/scintilla/src/ViewStyle.h index 60bd9688e..4a4ffcdf0 100644 --- a/scintilla/src/ViewStyle.h +++ b/scintilla/src/ViewStyle.h @@ -85,6 +85,7 @@ public: Indicator indicators[INDIC_MAX + 1]; int technology; int lineHeight; + int lineOverlap; unsigned int maxAscent; unsigned int maxDescent; XYPOSITION aveCharWidth; @@ -170,7 +171,10 @@ public: bool ValidStyle(size_t styleIndex) const; void CalcLargestMarkerHeight(); ColourOptional Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const; + bool SelectionBackgroundDrawn() const; + bool WhitespaceBackgroundDrawn() const; ColourDesired WrapColour() const; + bool SetWrapState(int wrapState_); bool SetWrapVisualFlags(int wrapVisualFlags_); bool SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_); diff --git a/scintilla/version.txt b/scintilla/version.txt index 4772052f5..0fecf6533 100644 --- a/scintilla/version.txt +++ b/scintilla/version.txt @@ -1 +1 @@ -344 +350 -- 2.11.4.GIT