Branch: refs/heads/master Author: Colomban Wendling ban@herbesfolles.org Committer: Colomban Wendling ban@herbesfolles.org Date: Sun, 10 Aug 2014 00:23:21 UTC Commit: a8a6ef7d131e49e1cc5ed843ebec3167fd7b018b https://github.com/geany/geany/commit/a8a6ef7d131e49e1cc5ed843ebec3167fd7b01...
Log Message: ----------- Merge branch 'scintilla-update-350-pre'
Modified Paths: -------------- data/filetypes.rust scintilla/Makefile.am scintilla/gtk/PlatGTK.cxx scintilla/gtk/ScintillaGTK.cxx scintilla/include/SciLexer.h scintilla/include/Scintilla.h scintilla/include/Scintilla.iface scintilla/lexers/LexHTML.cxx scintilla/lexers/LexMatlab.cxx scintilla/lexers/LexRuby.cxx scintilla/lexers/LexRust.cxx scintilla/lexlib/LexerModule.h scintilla/makefile.win32 scintilla/scintilla_changes.patch scintilla/src/CellBuffer.cxx scintilla/src/CellBuffer.h scintilla/src/ContractionState.cxx scintilla/src/ContractionState.h scintilla/src/Document.cxx scintilla/src/Document.h scintilla/src/EditModel.cxx scintilla/src/EditModel.h scintilla/src/EditView.cxx scintilla/src/EditView.h scintilla/src/Editor.cxx scintilla/src/Editor.h scintilla/src/LineMarker.cxx scintilla/src/MarginView.cxx scintilla/src/MarginView.h scintilla/src/PerLine.cxx scintilla/src/PerLine.h scintilla/src/PositionCache.cxx scintilla/src/PositionCache.h scintilla/src/RESearch.cxx scintilla/src/RESearch.h scintilla/src/ScintillaBase.cxx scintilla/src/ScintillaBase.h scintilla/src/Selection.cxx scintilla/src/Selection.h scintilla/src/ViewStyle.cxx scintilla/src/ViewStyle.h scintilla/version.txt src/highlighting.c src/highlightingmappings.h
Modified: data/filetypes.rust 3 lines changed, 3 insertions(+), 0 deletions(-) =================================================================== @@ -14,6 +14,9 @@ word4=type string=string_1 stringraw=string_2 character=character +bytestring=string_1 +bytestringraw=string_2 +bytecharacter=character operator=operator identifier=identifier_1 lifetime=parameter
Modified: scintilla/Makefile.am 6 lines changed, 6 insertions(+), 0 deletions(-) =================================================================== @@ -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 \
Modified: scintilla/gtk/PlatGTK.cxx 12 lines changed, 10 insertions(+), 2 deletions(-) =================================================================== @@ -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) {
Modified: scintilla/gtk/ScintillaGTK.cxx 284 lines changed, 208 insertions(+), 76 deletions(-) =================================================================== @@ -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 @@ class ScintillaGTK : public ScintillaBase { 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 @@ class ScintillaGTK : public ScintillaBase { 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 @@ class ScintillaGTK : public ScintillaBase { 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<TickReason>(tr + 1)) { + timers[tr].reason = tr; + timers[tr].scintilla = this; + } }
void ScintillaGTK::Finalise() { - SetTicking(false); + for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(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<TickerID>(g_timeout_add(timer.tickSize, - reinterpret_cast<GSourceFunc>(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<GSourceFunc>(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<Window &>(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<GdkDrawable *>(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<unsigned char *>(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; }
Modified: scintilla/include/SciLexer.h 18 lines changed, 18 insertions(+), 0 deletions(-) =================================================================== @@ -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
Modified: scintilla/include/Scintilla.h 15 lines changed, 12 insertions(+), 3 deletions(-) =================================================================== @@ -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
Modified: scintilla/include/Scintilla.iface 48 lines changed, 46 insertions(+), 2 deletions(-) =================================================================== @@ -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
Modified: scintilla/lexers/LexHTML.cxx 12 lines changed, 10 insertions(+), 2 deletions(-) =================================================================== @@ -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 == '/')) {
Modified: scintilla/lexers/LexMatlab.cxx 5 lines changed, 4 insertions(+), 1 deletions(-) =================================================================== @@ -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 neilh@scintilla.org // 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<char>(sc.ch)) || sc.ch == '@' || sc.ch == '\') { - if (sc.ch == ')' || sc.ch == ']') { + if (sc.ch == ')' || sc.ch == ']' || sc.ch == '}') { transpose = true; } else { transpose = false;
Modified: scintilla/lexers/LexRuby.cxx 36 lines changed, 35 insertions(+), 1 deletions(-) =================================================================== @@ -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;
Modified: scintilla/lexers/LexRust.cxx 105 lines changed, 69 insertions(+), 36 deletions(-) =================================================================== @@ -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);
Modified: scintilla/lexlib/LexerModule.h 7 lines changed, 6 insertions(+), 1 deletions(-) =================================================================== @@ -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
Modified: scintilla/makefile.win32 3 lines changed, 3 insertions(+), 0 deletions(-) =================================================================== @@ -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 \
Modified: scintilla/scintilla_changes.patch 3 lines changed, 2 insertions(+), 1 deletions(-) =================================================================== @@ -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);
Modified: scintilla/src/CellBuffer.cxx 40 lines changed, 39 insertions(+), 1 deletions(-) =================================================================== @@ -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) {
Modified: scintilla/src/CellBuffer.h 12 lines changed, 12 insertions(+), 0 deletions(-) =================================================================== @@ -95,6 +95,7 @@ class UndoHistory { int currentAction; int undoSequenceDepth; int savePoint; + int tentativePoint;
void EnsureUndoRoom();
@@ -117,6 +118,12 @@ class UndoHistory { 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 @@ class CellBuffer { void SetSavePoint(); bool IsSavePoint() const;
+ void TentativeStart(); + void TentativeCommit(); + bool TentativeActive() const; + int TentativeSteps(); + bool SetUndoCollection(bool collectUndo); bool IsCollectingUndo() const; void BeginUndoAction();
Modified: scintilla/src/ContractionState.cxx 18 lines changed, 9 insertions(+), 9 deletions(-) =================================================================== @@ -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 {
Modified: scintilla/src/ContractionState.h 4 lines changed, 2 insertions(+), 2 deletions(-) =================================================================== @@ -48,11 +48,11 @@ class ContractionState { 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;
Modified: scintilla/src/Document.cxx 59 lines changed, 59 insertions(+), 0 deletions(-) =================================================================== @@ -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<LineMarkers *>(perLineData[ldMarkers])->MarkValue(line); }
Modified: scintilla/src/Document.h 8 lines changed, 7 insertions(+), 1 deletions(-) =================================================================== @@ -303,6 +303,12 @@ class Document : PerLine, public IDocumentWithLineEnd, public ILoader { 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 @@ class Document : PerLine, public IDocumentWithLineEnd, public ILoader {
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);
Modified: scintilla/src/EditModel.cxx 74 lines changed, 74 insertions(+), 0 deletions(-) =================================================================== @@ -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 neilh@scintilla.org +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <assert.h> +#include <ctype.h> + +#include <string> +#include <vector> +#include <map> +#include <algorithm> +#include <memory> + +#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; +}
Modified: scintilla/src/EditModel.h 67 lines changed, 67 insertions(+), 0 deletions(-) =================================================================== @@ -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 neilh@scintilla.org +// 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
Modified: scintilla/src/EditView.cxx 2072 lines changed, 2072 insertions(+), 0 deletions(-) =================================================================== @@ -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 neilh@scintilla.org +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <assert.h> +#include <ctype.h> + +#include <string> +#include <vector> +#include <map> +#include <algorithm> +#include <memory> + +#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<st.length; iStyle++) { + if (!vs.ValidStyle(styleOffset + st.styles[iStyle])) + return false; + } + } else { + if (!vs.ValidStyle(styleOffset + st.style)) + return false; + } + return true; +} + +static int WidthStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, + const char *text, const unsigned char *styles, size_t len) { + int width = 0; + size_t start = 0; + while (start < len) { + size_t style = styles[start]; + size_t endSegment = start; + while ((endSegment + 1 < len) && (static_cast<size_t>(styles[endSegment + 1]) == style)) + endSegment++; + FontAlias fontText = vs.styles[style + styleOffset].font; + width += static_cast<int>(surface->WidthText(fontText, text + start, + static_cast<int>(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<int>(surface->WidthText(fontText, + st.text + start, static_cast<int>(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<int>(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<int>(surface->WidthText(fontText, + st.text + start + i, static_cast<int>(end - i + 1))); + PRectangle rcSegment = rcText; + rcSegment.left = static_cast<XYPOSITION>(x); + rcSegment.right = static_cast<XYPOSITION>(x + width + 1); + DrawTextNoClipPhase(surface, rcSegment, vs.styles[style], + rcText.top + vs.maxAscent, st.text + start + i, + static_cast<int>(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<int>(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<PhasesDraw>(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<LineTabstops *>(ldTabstops); + return lt && lt->ClearTabstops(line); +} + +bool EditView::AddTabstop(int line, int x) { + if (!ldTabstops) { + ldTabstops = new LineTabstops(); + } + LineTabstops *lt = static_cast<LineTabstops *>(ldTabstops); + return lt && lt->AddTabstop(line, x); +} + +int EditView::GetNextTabstop(int line, int x) const { + LineTabstops *lt = static_cast<LineTabstops *>(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<int>(rcTab.bottom - rcTab.top) / 2; + int xhead = static_cast<int>(rcTab.right) - 1 - ydiff; + if (xhead <= rcTab.left) { + ydiff -= static_cast<int>(rcTab.left) - xhead - 1; + xhead = static_cast<int>(rcTab.left) - 1; + } + if ((rcTab.left + 2) < (rcTab.right - 1)) + surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid); + else + surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid); + surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid); + surface->LineTo(xhead, ymid - ydiff); + surface->MoveTo(static_cast<int>(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<unsigned char>(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<char>(tolower(chDoc))); + else // Style::caseUpper + allSame = allSame && + (ll->chars[numCharsInLine] == static_cast<char>(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; charInLine<lineLength; charInLine++) { + char chDoc = ll->chars[charInLine]; + if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper) + ll->chars[charInLine] = static_cast<char>(toupper(chDoc)); + else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower) + ll->chars[charInLine] = static_cast<char>(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<int>(ll->positions[ts.start]); + const int tabWidth = static_cast<int>(vstyle.tabWidth); + representationWidth = static_cast<XYPOSITION>(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<unsigned int>(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<int>(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<int>(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<int>(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<int>( + (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<int>( + (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<int>(rcSegment.top), start + 2, static_cast<int>(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<int>(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<int>(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<XYPOSITION>(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; 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; + rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - + static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth; + rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - + static_cast<XYPOSITION>(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; eolPos<ll->numCharsInLine; eolPos++) { + rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart)+virtualSpace; + rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(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<int>(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<XYPOSITION>(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<int>(vsDraw.spaceWidth * 2); // Margins + } + if (widthAnnotation > lineWidthMaxSeen) + lineWidthMaxSeen = widthAnnotation; + if (vsDraw.annotationVisible == ANNOTATION_BOXED) { + rcSegment.left = static_cast<XYPOSITION>(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<int>(rcSegment.left), static_cast<int>(rcSegment.top)); + surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom)); + surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top)); + surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom)); + if (subLine == ll->lines) { + surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top)); + surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top)); + } + if (subLine == ll->lines + annotationLines - 1) { + surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1)); + surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(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; (r<model.sel.Count()) || drawDrag; r++) { + const bool mainCaret = r == model.@@ Diff output truncated at 100000 characters. @@
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).