aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gtk/ScintillaGTK.cxx1003
-rw-r--r--include/Accessor.h76
-rw-r--r--include/KeyWords.h8
-rw-r--r--include/Platform.h394
-rw-r--r--include/PropSet.h180
-rw-r--r--include/SciLexer.h172
-rw-r--r--include/Scintilla.h415
-rw-r--r--include/WinDefs.h218
-rw-r--r--src/Accessor.cxx112
-rw-r--r--src/AutoComplete.cxx104
-rw-r--r--src/AutoComplete.h43
-rw-r--r--src/CallTip.cxx168
-rw-r--r--src/CallTip.h46
-rw-r--r--src/CellBuffer.cxx950
-rw-r--r--src/CellBuffer.h197
-rw-r--r--src/ContractionState.cxx203
-rw-r--r--src/ContractionState.h50
-rw-r--r--src/Document.cxx734
-rw-r--r--src/Document.h222
19 files changed, 5295 insertions, 0 deletions
diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx
new file mode 100644
index 000000000..266492fc3
--- /dev/null
+++ b/gtk/ScintillaGTK.cxx
@@ -0,0 +1,1003 @@
+// Scintilla source code edit control
+// ScintillaGTK.cxx - GTK+ specific subclass of ScintillaBase
+// Copyright 1998-2000 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 <ctype.h>
+#include <time.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#ifdef SCI_LEXER
+#include "SciLexer.h"
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#endif
+#include "ContractionState.h"
+#include "SVector.h"
+#include "CellBuffer.h"
+#include "CallTip.h"
+#include "KeyMap.h"
+#include "Indicator.h"
+#include "LineMarker.h"
+#include "Style.h"
+#include "AutoComplete.h"
+#include "ViewStyle.h"
+#include "Document.h"
+#include "Editor.h"
+#include "ScintillaBase.h"
+
+#include "gtk/gtksignal.h"
+
+class ScintillaGTK : public ScintillaBase {
+ _ScintillaObject *sci;
+ Window scrollbarv;
+ Window scrollbarh;
+ GtkObject *adjustmentv;
+ GtkObject *adjustmenth;
+ char *pasteBuffer;
+ bool pasteBufferIsRectangular;
+ GdkEventButton evbtn;
+ bool capturedMouse;
+ bool dragWasDropped;
+
+ static GdkAtom clipboard_atom;
+
+public:
+ ScintillaGTK(_ScintillaObject *sci_);
+ virtual ~ScintillaGTK();
+
+private:
+ virtual void Initialise();
+ virtual void Finalise();
+ virtual void StartDrag();
+public: // Public for scintilla_send_message
+ virtual LRESULT WndProc(UINT iMessage,WPARAM wParam,LPARAM lParam);
+private:
+ virtual LRESULT DefWndProc(UINT iMessage,WPARAM wParam,LPARAM lParam);
+ virtual void SetTicking(bool on);
+ virtual void SetMouseCapture(bool on);
+ virtual bool HaveMouseCapture();
+ void FullPaint();
+ void SyncPaint(PRectangle rc);
+ virtual void ScrollText(int linesToMove);
+ virtual void SetVerticalScrollPos();
+ virtual void SetHorizontalScrollPos();
+ virtual bool ModifyScrollBars(int nMax, int nPage);
+ virtual void NotifyChange();
+ virtual void NotifyFocus(bool focus);
+ virtual void NotifyParent(SCNotification scn);
+ void NotifyKey(int key, int modifiers);
+ virtual int KeyDefault(int key, int modifiers);
+ virtual void Copy();
+ virtual void Paste();
+ virtual void CreateCallTipWindow(PRectangle rc);
+ virtual void AddToPopUp(const char *label, int cmd=0, bool enabled=true);
+ virtual void ClaimSelection();
+ void ReceivedSelection(GtkSelectionData *selection_data);
+ void ReceivedDrop(GtkSelectionData *selection_data);
+ void GetSelection(GtkSelectionData *selection_data, guint info, char *text, bool isRectangular);
+ void Resize(int width, int height);
+
+ // Callback functions
+ static gint FocusIn(GtkWidget *widget, GdkEventFocus *event, ScintillaGTK *sciThis);
+ static gint FocusOut(GtkWidget *widget, GdkEventFocus *event, ScintillaGTK *sciThis);
+ static gint Expose(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis);
+ static void ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis);
+ static void ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis);
+ static gint MoveResize(GtkWidget *widget, GtkAllocation *allocation, ScintillaGTK *sciThis);
+ static gint Press(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis);
+ static gint MouseRelease(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis);
+ static gint Motion(GtkWidget *widget, GdkEventMotion *event, ScintillaGTK *sciThis);
+ static gint KeyPress(GtkWidget *widget, GdkEventKey *event, ScintillaGTK *sciThis);
+ static gint KeyRelease(GtkWidget *widget, GdkEventKey *event, ScintillaGTK *sciThis);
+ static gint DestroyWindow(GtkWidget *widget, ScintillaGTK *sciThis);
+ static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data,
+ guint time, ScintillaGTK *sciThis);
+ static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data,
+ guint info, guint time, ScintillaGTK *sciThis);
+ static void DragBegin(GtkWidget *widget, GdkDragContext *context,
+ ScintillaGTK *sciThis);
+ static gboolean DragMotion(GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, guint time, ScintillaGTK *sciThis);
+ static void DragLeave(GtkWidget *widget, GdkDragContext *context,
+ guint time, ScintillaGTK *sciThis);
+ static void DragEnd(GtkWidget *widget, GdkDragContext *context,
+ ScintillaGTK *sciThis);
+ static gboolean Drop(GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, guint time, ScintillaGTK *sciThis);
+ static void DragDataReceived(GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, GtkSelectionData *selection_data, guint info, guint time,
+ ScintillaGTK *sciThis);
+ static void DragDataGet(GtkWidget *widget, GdkDragContext *context,
+ GtkSelectionData *selection_data, guint info, guint time, ScintillaGTK *sciThis);
+ static gint TimeOut(ScintillaGTK *sciThis);
+ static void PopUpCB(ScintillaGTK *sciThis, guint action, GtkWidget *widget);
+ static gint ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ct);
+};
+
+enum {
+ COMMAND_SIGNAL,
+ NOTIFY_SIGNAL,
+ LAST_SIGNAL
+};
+
+static gint scintilla_signals[LAST_SIGNAL] = { 0 };
+
+GdkAtom ScintillaGTK::clipboard_atom = GDK_NONE;
+
+enum {
+ TARGET_STRING,
+ TARGET_TEXT,
+ TARGET_COMPOUND_TEXT
+};
+
+ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) :
+ adjustmentv(0), adjustmenth(0),
+ pasteBuffer(0), pasteBufferIsRectangular(false),
+ capturedMouse(false), dragWasDropped(false) {
+ sci = sci_;
+ wMain = GTK_WIDGET(sci);
+
+ Initialise();
+}
+
+ScintillaGTK::~ScintillaGTK() {
+}
+
+gint ScintillaGTK::FocusIn(GtkWidget *widget, GdkEventFocus *event, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("ScintillaGTK::focus in %x\n", sciThis);
+ GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
+ sciThis->NotifyFocus(true);
+ sciThis->ShowCaretAtCurrentPosition();
+ return FALSE;
+}
+
+gint ScintillaGTK::FocusOut(GtkWidget *widget, GdkEventFocus *event, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("ScintillaGTK::focus out %x\n", sciThis);
+ GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
+ sciThis->NotifyFocus(false);
+ sciThis->DropCaret();
+ return FALSE;
+}
+
+void ScintillaGTK::Initialise() {
+ pasteBuffer = 0;
+ pasteBufferIsRectangular = false;
+
+ GTK_WIDGET_SET_FLAGS(wMain.GetID(), GTK_CAN_FOCUS);
+ GTK_WIDGET_SET_FLAGS(GTK_WIDGET(wMain.GetID()), GTK_SENSITIVE);
+ gtk_signal_connect(GTK_OBJECT(wMain.GetID()), "size_allocate",
+ GTK_SIGNAL_FUNC(MoveResize), this);
+ gtk_widget_set_events (wMain.GetID(),
+ GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK
+ | GDK_FOCUS_CHANGE_MASK);
+ // Using "after" connect to avoid main window using cursor keys
+ // to move focus.
+ //gtk_signal_connect(GTK_OBJECT(wMain), "key_press_event",
+ // GtkSignalFunc(key_event), this);
+ gtk_signal_connect_after(GTK_OBJECT(wMain.GetID()), "key_press_event",
+ GtkSignalFunc(KeyPress), this);
+
+ gtk_signal_connect(GTK_OBJECT(wMain.GetID()), "key_release_event",
+ GtkSignalFunc(KeyRelease), this);
+ gtk_signal_connect(GTK_OBJECT(wMain.GetID()), "focus_in_event",
+ GtkSignalFunc(FocusIn), this);
+ gtk_signal_connect(GTK_OBJECT(wMain.GetID()), "focus_out_event",
+ GtkSignalFunc(FocusOut), this);
+ gtk_signal_connect(GTK_OBJECT(wMain.GetID()), "destroy",
+ GtkSignalFunc(DestroyWindow), this);
+
+ wDraw = gtk_drawing_area_new();
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()), "expose_event",
+ GtkSignalFunc(Expose), this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()), "motion_notify_event",
+ GtkSignalFunc(Motion), this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()), "button_press_event",
+ GtkSignalFunc(Press), this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()), "button_release_event",
+ GtkSignalFunc(MouseRelease), this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()), "selection_received",
+ GtkSignalFunc(SelectionReceived), this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()), "selection_get",
+ GtkSignalFunc(SelectionGet), this);
+
+ gtk_widget_set_events(wDraw.GetID(),
+ GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK
+ );
+
+ gtk_drawing_area_size(GTK_DRAWING_AREA(wDraw.GetID()), 400, 400);
+ gtk_fixed_put(GTK_FIXED(sci), wDraw.GetID(), 0, 0);
+
+ adjustmentv = gtk_adjustment_new(0.0, 0.0, 201.0, 1.0, 20.0, 20.0);
+ scrollbarv = gtk_vscrollbar_new(GTK_ADJUSTMENT(adjustmentv));
+ GTK_WIDGET_UNSET_FLAGS(scrollbarv.GetID(), GTK_CAN_FOCUS);
+ gtk_signal_connect(GTK_OBJECT(adjustmentv), "value_changed",
+ GTK_SIGNAL_FUNC(ScrollSignal), this);
+ gtk_fixed_put(GTK_FIXED(sci), scrollbarv.GetID(), 0, 0);
+
+ adjustmenth = gtk_adjustment_new(0.0, 0.0, 101.0, 1.0, 20.0, 20.0);
+ scrollbarh = gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustmenth));
+ GTK_WIDGET_UNSET_FLAGS(scrollbarh.GetID(), GTK_CAN_FOCUS);
+ gtk_signal_connect(GTK_OBJECT(adjustmenth), "value_changed",
+ GTK_SIGNAL_FUNC(ScrollHSignal), this);
+ gtk_fixed_put(GTK_FIXED(sci), scrollbarh.GetID(), 0, 0);
+
+ gtk_widget_grab_focus(wMain.GetID());
+
+ static const GtkTargetEntry targets[] = {
+ { "STRING", 0, TARGET_STRING },
+ { "TEXT", 0, TARGET_TEXT },
+ { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
+ };
+ static const gint n_targets = sizeof(targets) / sizeof(targets[0]);
+
+ gtk_selection_add_targets(GTK_WIDGET(wDraw.GetID()), GDK_SELECTION_PRIMARY,
+ targets, n_targets);
+
+ if (!clipboard_atom)
+ clipboard_atom = gdk_atom_intern("CLIPBOARD", FALSE);
+
+ gtk_selection_add_targets(GTK_WIDGET(wDraw.GetID()), clipboard_atom,
+ targets, n_targets);
+
+ gtk_drag_dest_set(GTK_WIDGET(wDraw.GetID()),
+ GTK_DEST_DEFAULT_ALL, targets, n_targets,
+ static_cast<GdkDragAction>(GDK_ACTION_COPY|GDK_ACTION_MOVE));
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()),
+ "drag_data_received",
+ GTK_SIGNAL_FUNC(DragDataReceived),
+ this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()),
+ "drag_motion",
+ GTK_SIGNAL_FUNC(DragMotion),
+ this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()),
+ "drag_leave",
+ GTK_SIGNAL_FUNC(DragLeave),
+ this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()),
+ "drag_end",
+ GTK_SIGNAL_FUNC(DragEnd),
+ this);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()),
+ "drag_drop",
+ GTK_SIGNAL_FUNC(Drop),
+ this);
+ // gtk_drag_source_set not used as it stops dragging over text to select it
+ //gtk_drag_source_set(GTK_WIDGET(wDraw.GetID()),
+ // static_cast<GdkModifierType>(GDK_BUTTON1_MASK|GDK_BUTTON3_MASK),
+ // targets, n_targets,
+ // GDK_ACTION_COPY);
+ gtk_signal_connect(GTK_OBJECT(wDraw.GetID()),
+ "drag_data_get",
+ GTK_SIGNAL_FUNC(DragDataGet),
+ this);
+
+ SetTicking(true);
+}
+
+void ScintillaGTK::Finalise() {
+ SetTicking(false);
+ ScintillaBase::Finalise();
+}
+
+void ScintillaGTK::StartDrag() {
+ dragWasDropped = false;
+ static const GtkTargetEntry targets[] = {
+ { "STRING", 0, TARGET_STRING },
+ { "TEXT", 0, TARGET_TEXT },
+ { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
+ };
+ static const gint n_targets = sizeof(targets) / sizeof(targets[0]);
+ GtkTargetList *tl = gtk_target_list_new(targets, n_targets);
+ gtk_drag_begin(GTK_WIDGET(wDraw.GetID()),
+ tl,
+ static_cast<GdkDragAction>(GDK_ACTION_COPY|GDK_ACTION_MOVE),
+ evbtn.button,
+ reinterpret_cast<GdkEvent *>(&evbtn));
+}
+
+LRESULT ScintillaGTK::WndProc(UINT iMessage,WPARAM wParam,LPARAM lParam) {
+ switch (iMessage) {
+
+ case SCI_GRABFOCUS:
+ gtk_widget_grab_focus(wMain.GetID());
+ break;
+
+ default:
+ return ScintillaBase::WndProc(iMessage,wParam,lParam);
+ }
+ return 0l;
+}
+
+LRESULT ScintillaGTK::DefWndProc(UINT, WPARAM, LPARAM) {
+ return 0;
+}
+
+void ScintillaGTK::SetTicking(bool on) {
+ if (timer.ticking != on) {
+ timer.ticking = on;
+ if (timer.ticking) {
+ timer.tickerID = gtk_timeout_add(timer.tickSize, TimeOut, this);
+ } else {
+ gtk_timeout_remove(timer.tickerID);
+ }
+ }
+ timer.ticksToWait = caret.period;
+}
+
+void ScintillaGTK::SetMouseCapture(bool on) {
+ if (on) {
+ gtk_grab_add(GTK_WIDGET(wDraw.GetID()));
+ } else {
+ gtk_grab_remove(GTK_WIDGET(wDraw.GetID()));
+ }
+ capturedMouse = on;
+}
+
+bool ScintillaGTK::HaveMouseCapture() {
+ return capturedMouse;
+}
+
+// Redraw all of text area. This paint will not be abandoned.
+void ScintillaGTK::FullPaint() {
+ paintState = painting;
+ rcPaint = GetTextRectangle();
+ paintingAllText = true;
+ Surface sw;
+ sw.Init((wDraw.GetID())->window);
+ Paint(&sw, rcPaint);
+ sw.Release();
+ paintState = notPainting;
+}
+
+// Synchronously paint a rectangle of the window.
+void ScintillaGTK::SyncPaint(PRectangle rc) {
+ paintState = painting;
+ rcPaint = rc;
+ PRectangle rcText = GetTextRectangle();
+ paintingAllText = rcPaint.Contains(rcText);
+ Surface sw;
+ sw.Init((wDraw.GetID())->window);
+ Paint(&sw, rc);
+ sw.Release();
+ if (paintState == paintAbandoned) {
+ // Painting area was insufficient to cover new styling or brace highlight positions
+ FullPaint();
+ }
+ paintState = notPainting;
+}
+
+void ScintillaGTK::ScrollText(int linesToMove) {
+ //Platform::DebugPrintf("ScintillaGTK::ScrollText %d\n", linesToMove);
+ PRectangle rc = GetClientRectangle();
+ int diff = vs.lineHeight * -linesToMove;
+ WindowID wi = wDraw.GetID();
+ GdkGC *gc = gdk_gc_new(wi->window);
+
+ // Redraw exposed bit : scrolling upwards
+ if (diff > 0) {
+ gdk_draw_pixmap(wi->window,
+ gc, wi->window,
+ 0, diff,
+ 0, 0,
+ rc.Width(), rc.Height() - diff);
+ // RedrawRect(PRectangle(0,rc.Height() - diff -
+ // vs.lineHeight, rc.Width(), rc.Height()));
+ gdk_gc_unref(gc);
+ SyncPaint(PRectangle(0,rc.Height() - diff -
+ vs.lineHeight, rc.Width(), rc.Height()));
+
+ // Redraw exposed bit : scrolling downwards
+ } else {
+ gdk_draw_pixmap(wi->window,
+ gc, wi->window,
+ 0, 0,
+ 0, -diff,
+ rc.Width(), rc.Height() - diff);
+ gdk_gc_unref(gc);
+ // RedrawRect(PRectangle(0,0,rc.Width(),-diff + vs.lineHeight));
+ SyncPaint(PRectangle(0,0,rc.Width(),-diff + vs.lineHeight));
+ }
+}
+
+
+void ScintillaGTK::SetVerticalScrollPos() {
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv), topLine);
+}
+
+void ScintillaGTK::SetHorizontalScrollPos() {
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth), xOffset / 2);
+}
+
+bool ScintillaGTK::ModifyScrollBars(int nMax, int nPage) {
+ bool modified = false;
+ int pageScroll = LinesToScroll();
+
+ if (GTK_ADJUSTMENT(adjustmentv)->upper != (nMax+1) ||
+ GTK_ADJUSTMENT(adjustmentv)->page_size != nPage ||
+ GTK_ADJUSTMENT(adjustmentv)->page_increment != pageScroll) {
+ GTK_ADJUSTMENT(adjustmentv)->upper = nMax + 1;
+ GTK_ADJUSTMENT(adjustmentv)->page_size = nPage;
+ GTK_ADJUSTMENT(adjustmentv)->page_increment = pageScroll;
+ gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv));
+ modified = true;
+ }
+
+ if (GTK_ADJUSTMENT(adjustmenth)->upper != 2000 ||
+ GTK_ADJUSTMENT(adjustmenth)->page_size != 200) {
+ GTK_ADJUSTMENT(adjustmenth)->upper = 2000;
+ GTK_ADJUSTMENT(adjustmenth)->page_size = 200;
+ gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth));
+ modified = true;
+ }
+ return modified;
+}
+
+void ScintillaGTK::NotifyChange() {
+ gtk_signal_emit(GTK_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL],
+ MAKELONG(ctrlID, EN_CHANGE), wMain.GetID());
+}
+
+void ScintillaGTK::NotifyFocus(bool focus) {
+ gtk_signal_emit(GTK_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL],
+ MAKELONG(ctrlID, focus ? EN_SETFOCUS : EN_KILLFOCUS), wMain.GetID());
+}
+
+void ScintillaGTK::NotifyParent(SCNotification scn) {
+ scn.nmhdr.hwndFrom = wMain.GetID();
+ scn.nmhdr.idFrom = ctrlID;
+ gtk_signal_emit(GTK_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL],
+ ctrlID, &scn);
+}
+
+void ScintillaGTK::NotifyKey(int key, int modifiers) {
+ SCNotification scn;
+ scn.nmhdr.code = SCN_KEY;
+ scn.ch = key;
+ scn.modifiers = modifiers;
+
+ NotifyParent(scn);
+}
+
+int ScintillaGTK::KeyDefault(int key, int modifiers) {
+ if (!(modifiers & SCI_CTRL) && !(modifiers & SCI_ALT) && (key < 128)) {
+ AddChar(key);
+ } else {
+ // Pass up to container in case it is an accelerator
+ NotifyKey(key, modifiers);
+ }
+ //Platform::DebugPrintf("SK-key: %d %x %x\n",event->keyval, event->state, GTK_WIDGET_FLAGS(widget));
+ return 1;
+}
+
+void ScintillaGTK::Copy() {
+ if (currentPos != anchor) {
+ delete []pasteBuffer;
+ pasteBuffer = CopySelectionRange();
+ pasteBufferIsRectangular = selType == selRectangle;
+ gtk_selection_owner_set(GTK_WIDGET(wDraw.GetID()),
+ clipboard_atom,
+ GDK_CURRENT_TIME);
+ }
+}
+
+void ScintillaGTK::Paste() {
+ gtk_selection_convert(GTK_WIDGET(wDraw.GetID()),
+ clipboard_atom,
+ gdk_atom_intern("STRING", FALSE), GDK_CURRENT_TIME);
+}
+
+void ScintillaGTK::CreateCallTipWindow(PRectangle rc) {
+ ct.wCallTip = gtk_window_new(GTK_WINDOW_POPUP);
+ ct.wDraw = gtk_drawing_area_new();
+ gtk_container_add(GTK_CONTAINER(ct.wCallTip.GetID()), ct.wDraw.GetID());
+ gtk_signal_connect(GTK_OBJECT(ct.wDraw.GetID()), "expose_event",
+ GtkSignalFunc(ScintillaGTK::ExposeCT), &ct);
+ gtk_widget_set_events(ct.wDraw.GetID(), GDK_EXPOSURE_MASK);
+ gtk_drawing_area_size(GTK_DRAWING_AREA(ct.wDraw.GetID()),
+ rc.Width(), rc.Height());
+ ct.wDraw.Show();
+}
+
+void ScintillaGTK::AddToPopUp(const char *label, int cmd, bool enabled) {
+ char fulllabel[200];
+ strcpy(fulllabel, "/");
+ strcat(fulllabel, label);
+ GtkItemFactoryEntry itemEntry = {
+ fulllabel, NULL,
+ GTK_SIGNAL_FUNC(ScintillaGTK::PopUpCB), cmd,
+ const_cast<gchar *>(label[0] ? "<Item>" : "<Separator>")
+ };
+ gtk_item_factory_create_item(GTK_ITEM_FACTORY(popup.GetID()),
+ &itemEntry, this, 1);
+ if (cmd) {
+ GtkWidget *item = gtk_item_factory_get_widget_by_action(
+ popup.GetID(), cmd);
+ if (item)
+ gtk_widget_set_sensitive(item, enabled);
+ }
+}
+
+void ScintillaGTK::ClaimSelection() {
+ // X Windows has a 'primary selection' as well as the clipboard.
+ // Whenever the user selects some text, we become the primary selection
+ if (currentPos != anchor) {
+ gtk_selection_owner_set(GTK_WIDGET(wDraw.GetID()),
+ GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
+ } else if (gdk_selection_owner_get(GDK_SELECTION_PRIMARY) ==
+ GTK_WIDGET(wDraw.GetID())->window) {
+ gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
+ }
+}
+
+void ScintillaGTK::ReceivedSelection(GtkSelectionData *selection_data) {
+ if (selection_data->type == GDK_TARGET_STRING) {
+//Platform::DebugPrintf("Received String Selection %x %d\n", selection_data->selection, selection_data->length);
+ if (((selection_data->selection == clipboard_atom)||
+ (selection_data->selection == GDK_SELECTION_PRIMARY)) &&
+ (selection_data->length > 0)) {
+ //if (selection_data->length > 0) {
+ char *ptr = reinterpret_cast<char *>(selection_data->data);
+ unsigned int len = selection_data->length;
+ for (unsigned int i=0; i<selection_data->length; i++) {
+ if ((len == selection_data->length) && (0 == ptr[i]))
+ len = i;
+ }
+ pdoc->BeginUndoAction();
+ int selStart = SelectionStart();
+ ClearSelection();
+ // Check for "\n\0" ending to string indicating that selection is rectangular
+ bool isRectangular = ((selection_data->length > 1) &&
+ (ptr[selection_data->length-1] == 0 && ptr[selection_data->length-2] == '\n'));
+ if (isRectangular) {
+ PasteRectangular(selStart, ptr, len);
+ } else {
+ pdoc->InsertString(currentPos, ptr, len);
+ SetEmptySelection(currentPos + len);
+ }
+ pdoc->EndUndoAction();
+ }
+ }
+ Redraw();
+}
+
+void ScintillaGTK::ReceivedDrop(GtkSelectionData *selection_data) {
+ dragWasDropped = true;
+ if (selection_data->type == GDK_TARGET_STRING) {
+ if (selection_data->length > 0) {
+ char *ptr = reinterpret_cast<char *>(selection_data->data);
+ // 3rd argument is false because the deletion of the moved data is handle by GetSelection
+ bool isRectangular = ((selection_data->length > 1) &&
+ (ptr[selection_data->length-1] == 0 && ptr[selection_data->length-2] == '\n'));
+ DropAt(posDrop, ptr, false, isRectangular);
+ }
+ }
+ Redraw();
+}
+
+void ScintillaGTK::GetSelection(GtkSelectionData *selection_data, guint info, char *text, bool isRectangular) {
+//Platform::DebugPrintf("GetSelection %d\n", info);
+ char *selBuffer = text;
+ if (selection_data->selection == GDK_SELECTION_PRIMARY) {
+//Platform::DebugPrintf("GetSelection PRIMARY\n");
+ selBuffer = CopySelectionRange();
+ }
+
+ if (info == TARGET_STRING) {
+ int len = strlen(selBuffer);
+ // Here is a somewhat evil kludge.
+ // As I can not work out how to store data on the clipboard in multiple formats
+ // and need some way to mark the clipping as being stream or rectangular,
+ // the terminating \0 is included in the length for rectangular clippings.
+ // All other tested aplications behave benignly by ignoring the \0.
+ if (isRectangular)
+ len++;
+//Platform::DebugPrintf("GetSelection STRING %d %s %d\n", selection_data->type,
+//isRectangular ? "rect" : "stream", len);
+ gtk_selection_data_set(selection_data, GDK_SELECTION_TYPE_STRING,
+ 8, reinterpret_cast<unsigned char *>(selBuffer),
+ len);
+ } else if ((info == TARGET_TEXT) || (info == TARGET_COMPOUND_TEXT)) {
+//Platform::DebugPrintf("GetSelection TEXT\n");
+//if (info == TARGET_COMPOUND_TEXT)
+//Platform::DebugPrintf("GetSelection COMPOUND\n");
+ guchar *text;
+ GdkAtom encoding;
+ gint format;
+ gint new_length;
+
+ gdk_string_to_compound_text(reinterpret_cast<char *>(selBuffer),
+ &encoding, &format, &text, &new_length);
+ gtk_selection_data_set(selection_data, encoding, format, text, new_length);
+ gdk_free_compound_text(text);
+ }
+
+//Platform::DebugPrintf("GetSelection FREE\n");
+ if (selection_data->selection == GDK_SELECTION_PRIMARY) {
+ delete []selBuffer;
+ }
+//Platform::DebugPrintf("GetSelection END\n");
+}
+
+void ScintillaGTK::Resize(int width, int height) {
+ //Platform::DebugPrintf("Resize %d %d\n", width, height);
+ DropGraphics();
+ GtkAllocation alloc;
+
+ // Not always needed, but some themes can have different sizes of scrollbars
+ int scrollBarWidth = GTK_WIDGET(scrollbarv.GetID())->requisition.width;
+ int scrollBarHeight = GTK_WIDGET(scrollbarh.GetID())->requisition.height;
+
+ // These allocations should never produce negative sizes as they would wrap around to huge
+ // unsigned numbers inside GTK+ causing warnings.
+
+ alloc.x = 0;
+ alloc.y = 0;
+ alloc.width = Platform::Maximum(1, width - scrollBarWidth) + 1;
+ alloc.height = Platform::Maximum(1, height - scrollBarHeight) + 1;
+ gtk_widget_size_allocate(GTK_WIDGET(wDraw.GetID()), &alloc);
+
+ alloc.x = 0;
+ alloc.y = height - scrollBarHeight + 1;
+ alloc.width = Platform::Maximum(1, width - scrollBarWidth) + 1;
+ alloc.height = scrollBarHeight;
+ gtk_widget_size_allocate(GTK_WIDGET(scrollbarh.GetID()), &alloc);
+
+ alloc.x = width - scrollBarWidth + 1;
+ alloc.y = 0;
+ alloc.width = scrollBarWidth;
+ alloc.height = Platform::Maximum(1, height - scrollBarHeight) + 1;
+ gtk_widget_size_allocate(GTK_WIDGET(scrollbarv.GetID()), &alloc);
+
+ SetScrollBars();
+}
+
+gint ScintillaGTK::MoveResize(GtkWidget *, GtkAllocation *allocation, ScintillaGTK *sciThis) {
+ // Platform::DebugPrintf("sci move resize %d %d\n", allocation->width, allocation->height);
+ sciThis->Resize(allocation->width, allocation->height);
+ return TRUE;
+}
+
+gint ScintillaGTK::Press(GtkWidget *, GdkEventButton *event, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("Press %x time=%d state = %x button = %x\n",sciThis,event->time, event->state, event->button);
+ sciThis->evbtn = *event;
+ Point pt;
+ pt.x = int(event->x);
+ pt.y = int(event->y);
+ gtk_widget_grab_focus(sciThis->wMain.GetID());
+ if (event->button == 1) {
+ //sciThis->ButtonDown(pt, event->time,
+ // event->state & GDK_SHIFT_MASK,
+ // event->state & GDK_CONTROL_MASK,
+ // event->state & GDK_MOD1_MASK);
+ // Instead of sending literal modifiers use control instead of alt
+ // Ths s because all the window managers seem to grab alt + click for moving
+ sciThis->ButtonDown(pt, event->time,
+ event->state & GDK_SHIFT_MASK,
+ event->state & GDK_CONTROL_MASK,
+ event->state & GDK_CONTROL_MASK);
+ } else if (event->button == 2) {
+ // Grab the primary selection
+ gtk_selection_convert(GTK_WIDGET(sciThis->wDraw.GetID()),
+ GDK_SELECTION_PRIMARY,
+ gdk_atom_intern("STRING", FALSE), event->time);
+ } else if (event->button == 3) {
+ // PopUp menu
+ // Convert to screen
+ int ox = 0;
+ int oy = 0;
+ gdk_window_get_origin(sciThis->wDraw.GetID()->window, &ox, &oy);
+ sciThis->ContextMenu(Point(pt.x + ox, pt.y + oy));
+ }
+ return TRUE;
+}
+
+gint ScintillaGTK::MouseRelease(GtkWidget *, GdkEventButton *event, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state);
+ if (event->button == 1) {
+ Point pt;
+ pt.x = int(event->x);
+ pt.y = int(event->y);
+ //Platform::DebugPrintf("Up %x %x %d %d %d\n",
+ // sciThis,event->window,event->time, pt.x, pt.y);
+ if (event->window != sciThis->wDraw.GetID()->window)
+ // If mouse released on scroll bar then the position is relative to the
+ // scrollbar, not the drawing window so just repeat the most recent point.
+ pt = sciThis->ptMouseLast;
+ sciThis->ButtonUp(pt, event->time, event->state & 4);
+ }
+ return TRUE;
+}
+
+gint ScintillaGTK::Motion(GtkWidget *, GdkEventMotion *event, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("Move %x %d\n",sciThis,event->time);
+ int x = 0;
+ int y = 0;
+ GdkModifierType state;
+ if (event->is_hint) {
+ gdk_window_get_pointer(event->window, &x, &y, &state);
+ } else {
+ x = static_cast<int>(event->x);
+ y = static_cast<int>(event->y);
+ state = static_cast<GdkModifierType>(event->state);
+ }
+ //Platform::DebugPrintf("Move %x %x %d %c %d %d\n",
+ // sciThis,event->window,event->time,event->is_hint? 'h' :'.', x, y);
+ if (state & GDK_BUTTON1_MASK) {
+ Point pt;
+ pt.x = x;
+ pt.y = y;
+ sciThis->ButtonMove(pt);
+ }
+ return TRUE;
+}
+
+// Map the keypad keys to their equivalent functions
+static int KeyTranslate(int keyIn) {
+ switch (keyIn) {
+ case GDK_ISO_Left_Tab: return GDK_Tab;
+ case GDK_KP_Down: return GDK_Down;
+ case GDK_KP_Up: return GDK_Up;
+ case GDK_KP_Left: return GDK_Left;
+ case GDK_KP_Right: return GDK_Right;
+ case GDK_KP_Home: return GDK_Home;
+ case GDK_KP_End: return GDK_End;
+ case GDK_KP_Page_Up: return GDK_Page_Up;
+ case GDK_KP_Page_Down: return GDK_Page_Down;
+ case GDK_KP_Delete: return GDK_Delete;
+ case GDK_KP_Insert: return GDK_Insert;
+ case GDK_KP_Enter: return GDK_Return;
+ default: return keyIn;
+ }
+}
+
+gint ScintillaGTK::KeyPress(GtkWidget *, GdkEventKey *event, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("SC-key: %d %x %x\n",event->keyval, event->state, GTK_WIDGET_FLAGS(widget));
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ int key = event->keyval;
+ if (ctrl && (key < 128))
+ key = toupper(key);
+ else if (key >= GDK_KP_Multiply && key <= GDK_KP_9)
+ key &= 0x7F;
+ else
+ key = KeyTranslate(key);
+
+ sciThis->KeyDown(key, shift, ctrl, alt);
+ //Platform::DebugPrintf("SK-key: %d %x %x\n",event->keyval, event->state, GTK_WIDGET_FLAGS(widget));
+ return 1;
+}
+
+gint ScintillaGTK::KeyRelease(GtkWidget *, GdkEventKey *event, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string);
+ return TRUE;
+}
+
+gint ScintillaGTK::DestroyWindow(GtkWidget *, ScintillaGTK *sciThis) {
+//Platform::DebugPrintf("Destroying window %x %x\n", sciThis, widget);
+ sciThis->Finalise();
+ delete sciThis;
+ return TRUE;
+}
+
+gint ScintillaGTK::Expose(GtkWidget *, GdkEventExpose *ose, ScintillaGTK *sciThis) {
+ if (sciThis->firstExpose) {
+ sciThis->wDraw.SetCursor(Window::cursorText);
+ sciThis->firstExpose = false;
+ }
+
+ sciThis->paintState = painting;
+
+ sciThis->rcPaint.left = ose->area.x;
+ sciThis->rcPaint.top = ose->area.y;
+ sciThis->rcPaint.right = ose->area.x + ose->area.width;
+ sciThis->rcPaint.bottom = ose->area.y + ose->area.height;
+
+ PRectangle rcText = sciThis->GetTextRectangle();
+ sciThis->paintingAllText = sciThis->rcPaint.Contains(rcText);
+ Surface surfaceWindow;
+ surfaceWindow.Init((sciThis->wDraw.GetID())->window);
+ sciThis->Paint(&surfaceWindow, sciThis->rcPaint);
+ surfaceWindow.Release();
+ if (sciThis->paintState == paintAbandoned) {
+ // Painting area was insufficient to cover new styling or brace highlight positions
+ sciThis->FullPaint();
+ }
+ sciThis->paintState = notPainting;
+
+ return TRUE;
+}
+
+void ScintillaGTK::ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("Scrolly %g %x\n",adj->value,p);
+ sciThis->ScrollTo((int)adj->value);
+}
+
+void ScintillaGTK::ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("Scrollyh %g %x\n",adj->value,p);
+ sciThis->HorizontalScrollTo((int)adj->value * 2);
+}
+
+void ScintillaGTK::SelectionReceived(GtkWidget *,
+ GtkSelectionData *selection_data, guint, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("Selection received\n");
+ sciThis->ReceivedSelection(selection_data);
+}
+
+void ScintillaGTK::SelectionGet(GtkWidget *,
+ GtkSelectionData *selection_data, guint info, guint, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("Selection get\n");
+ sciThis->GetSelection(selection_data, info, sciThis->pasteBuffer, sciThis->pasteBufferIsRectangular);
+}
+
+void ScintillaGTK::DragBegin(GtkWidget *, GdkDragContext *,
+ ScintillaGTK *) {
+ //Platform::DebugPrintf("DragBegin\n");
+}
+
+gboolean ScintillaGTK::DragMotion(GtkWidget *, GdkDragContext *context,
+ gint x, gint y, guint dragtime, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("DragMotion %d %d %x %x %x\n", x, y,
+ // context->actions, context->suggested_action, sciThis);
+ Point npt(x, y);
+ sciThis->inDragDrop = true;
+ sciThis->SetDragPosition(sciThis->PositionFromLocation(npt));
+ gdk_drag_status(context, context->suggested_action, dragtime);
+ return TRUE;
+}
+
+void ScintillaGTK::DragLeave(GtkWidget *, GdkDragContext *context,
+ guint, ScintillaGTK *sciThis) {
+ sciThis->SetDragPosition(invalidPosition);
+ //Platform::DebugPrintf("DragLeave %x\n", sciThis);
+}
+
+void ScintillaGTK::DragEnd(GtkWidget *, GdkDragContext *context,
+ ScintillaGTK *sciThis) {
+ // If drag did not result in drop here or elsewhere
+ if (!sciThis->dragWasDropped)
+ sciThis->SetEmptySelection(sciThis->posDrag);
+ sciThis->SetDragPosition(invalidPosition);
+ //Platform::DebugPrintf("DragEnd %x %d\n", sciThis, sciThis->dragWasDropped);
+}
+
+gboolean ScintillaGTK::Drop(GtkWidget *, GdkDragContext *context,
+ gint, gint, guint, ScintillaGTK *sciThis) {
+ //Platform::DebugPrintf("Drop %x\n", sciThis);
+ sciThis->SetDragPosition(invalidPosition);
+ return TRUE;
+}
+
+void ScintillaGTK::DragDataReceived(GtkWidget *, GdkDragContext *context,
+ gint, gint, GtkSelectionData *selection_data, guint info, guint,
+ ScintillaGTK *sciThis) {
+ sciThis->ReceivedDrop(selection_data);
+ sciThis->SetDragPosition(invalidPosition);
+}
+
+void ScintillaGTK::DragDataGet(GtkWidget *, GdkDragContext *context,
+ GtkSelectionData *selection_data, guint info, guint, ScintillaGTK *sciThis) {
+ sciThis->dragWasDropped = true;
+ if (sciThis->currentPos != sciThis->anchor) {
+ sciThis->GetSelection(selection_data, info, sciThis->dragChars, sciThis->dragIsRectangle);
+ }
+ if (context->action == GDK_ACTION_MOVE) {
+ int selStart = sciThis->SelectionStart();
+ int selEnd = sciThis->SelectionEnd();
+ if (sciThis->posDrop > selStart) {
+ if (sciThis->posDrop > selEnd)
+ sciThis->posDrop = sciThis->posDrop - (selEnd-selStart);
+ else
+ sciThis->posDrop = selStart;
+ sciThis->posDrop = sciThis->pdoc->ClampPositionIntoDocument(sciThis->posDrop);
+ }
+ sciThis->ClearSelection();
+ }
+ sciThis->SetDragPosition(invalidPosition);
+}
+
+int ScintillaGTK::TimeOut(ScintillaGTK *sciThis) {
+ sciThis->Tick();
+ return 1;
+}
+
+void ScintillaGTK::PopUpCB(ScintillaGTK *sciThis, guint action, GtkWidget *) {
+ if (action) {
+ sciThis->Command(action);
+ }
+}
+
+gint ScintillaGTK::ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ctip) {
+ Surface surfaceWindow;
+ //surfaceWindow.Init((ct->wCallTip.GetID())->window);
+ surfaceWindow.Init(widget->window);
+ ctip->PaintCT(&surfaceWindow);
+ surfaceWindow.Release();
+ return TRUE;
+}
+
+long scintilla_send_message(ScintillaObject *sci, int iMessage, int wParam, int lParam) {
+ ScintillaGTK *psci = reinterpret_cast<ScintillaGTK *>(sci->pscin);
+ return psci->WndProc(iMessage, wParam, lParam);
+}
+
+static void scintilla_class_init (ScintillaClass *klass);
+static void scintilla_init (ScintillaObject *sci);
+
+guint scintilla_get_type() {
+ static guint scintilla_type = 0;
+
+ if (!scintilla_type) {
+ GtkTypeInfo scintilla_info = {
+ "Scintilla",
+ sizeof (ScintillaObject),
+ sizeof (ScintillaClass),
+ (GtkClassInitFunc) scintilla_class_init,
+ (GtkObjectInitFunc) scintilla_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ scintilla_type = gtk_type_unique(gtk_fixed_get_type(), &scintilla_info);
+ }
+
+ return scintilla_type;
+}
+
+static void scintilla_class_init(ScintillaClass *klass) {
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) klass;
+
+ scintilla_signals[COMMAND_SIGNAL] = gtk_signal_new(
+ "command",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET(ScintillaClass, command),
+ gtk_marshal_NONE__INT_POINTER,
+ GTK_TYPE_NONE,
+ 2, GTK_TYPE_INT, GTK_TYPE_POINTER);
+
+ scintilla_signals[NOTIFY_SIGNAL] = gtk_signal_new(
+ "notify",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET(ScintillaClass, notify),
+ gtk_marshal_NONE__INT_POINTER,
+ GTK_TYPE_NONE,
+ 2, GTK_TYPE_INT, GTK_TYPE_POINTER);
+
+ gtk_object_class_add_signals(object_class,
+ reinterpret_cast<unsigned int *>(scintilla_signals), LAST_SIGNAL);
+
+ klass->command = NULL;
+ klass->notify = NULL;
+}
+
+static void scintilla_init(ScintillaObject *sci) {
+ GTK_WIDGET_SET_FLAGS(sci, GTK_CAN_FOCUS);
+ sci->pscin = new ScintillaGTK(sci);
+}
+
+GtkWidget* scintilla_new() {
+ return GTK_WIDGET(gtk_type_new(scintilla_get_type()));
+}
+
+void scintilla_set_id(ScintillaObject *sci,int id) {
+ ScintillaGTK *psci = reinterpret_cast<ScintillaGTK *>(sci->pscin);
+ psci->ctrlID = id;
+}
diff --git a/include/Accessor.h b/include/Accessor.h
new file mode 100644
index 000000000..1bba4af55
--- /dev/null
+++ b/include/Accessor.h
@@ -0,0 +1,76 @@
+// SciTE - Scintilla based Text Editor
+// Accessor.h - rapid easy access to contents of a Scintilla
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+class Accessor {
+protected:
+ // bufferSize is a trade off between time taken to copy the characters and SendMessage overhead
+ // slopSize positions the buffer before the desired position in case there is some backtracking
+ enum {bufferSize=4000, slopSize=bufferSize/8};
+ char buf[bufferSize+1];
+ WindowID id;
+ PropSet &props;
+ int startPos;
+ int endPos;
+ int lenDoc;
+ int offset; // Optional but including an offset makes GCC generate better code
+ void Fill(int position);
+public:
+ Accessor(WindowID id_, PropSet &props_, int offset_=0) :
+ id(id_), props(props_), startPos(0x7FFFFFFF), endPos(0),
+ lenDoc(-1), offset(offset_) {
+ }
+ char operator[](int position) {
+ position += offset;
+ if (position < startPos || position >= endPos) {
+ Fill(position);
+ }
+ return buf[position - startPos];
+ }
+ char SafeGetCharAt(int position, char chDefault=' ') {
+ // Safe version of operator[], returning a defined value for invalid position
+ position += offset;
+ if (position < startPos || position >= endPos) {
+ Fill(position);
+ if (position < startPos || position >= endPos) {
+ // Position is outside range of document
+ return chDefault;
+ }
+ }
+ return buf[position - startPos];
+ }
+ char StyleAt(int position);
+ int GetLine(int position);
+ int LineStart(int line);
+ int LevelAt(int line);
+ int Length();
+ void Flush() {
+ startPos = 0x7FFFFFFF;
+ lenDoc = -1;
+ }
+ int GetLineState(int line);
+ int SetLineState(int line, int state);
+ PropSet &GetPropSet() { return props; }
+};
+
+class StylingContext : public Accessor {
+ char styleBuf[bufferSize];
+ int validLen;
+ char chFlags;
+ char chWhile;
+ unsigned int startSeg;
+public:
+ StylingContext(WindowID id_, PropSet &props_, int offset_=0) :
+ Accessor(id_,props_,offset_), validLen(0), chFlags(0) {}
+ void StartAt(unsigned int start, char chMask=31);
+ void SetFlags(char chFlags_, char chWhile_) {chFlags = chFlags_; chWhile = chWhile_; };
+ void ColourSegment(unsigned int start, unsigned int end, int chAttr);
+ unsigned int GetStartSegment() { return startSeg; }
+ void StartSegment(unsigned int pos);
+ void ColourTo(unsigned int pos, int chAttr);
+ int GetLine(int position);
+ void SetLevel(int line, int level);
+ void Flush();
+};
+
diff --git a/include/KeyWords.h b/include/KeyWords.h
new file mode 100644
index 000000000..2cc03b788
--- /dev/null
+++ b/include/KeyWords.h
@@ -0,0 +1,8 @@
+// SciTE - Scintilla based Text Editor
+// KeyWords.h - colourise for particular languages
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+void ColouriseDoc(int codePage, int startPos, int lengthDoc, int initStyle,
+ int language, WordList *keywordlists[], StylingContext &styler);
+
diff --git a/include/Platform.h b/include/Platform.h
new file mode 100644
index 000000000..b077e2e34
--- /dev/null
+++ b/include/Platform.h
@@ -0,0 +1,394 @@
+// Scintilla source code edit control
+// Platform.h - interface to platform facilities
+// Also includes some basic utilities
+// Implemented in PlatGTK.cxx for GTK+/Linux, PlatWin.cxx for Windows, and PlatWX.cxx for wxWindows
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+// PLAT_GTK = GTK+ on Linux, PLAT_WIN = Win32 API on Win32 OS
+// PLAT_WX is wxWindows on any supported platform
+// Could also have PLAT_GTKWIN = GTK+ on Win32 OS in future
+
+#define PLAT_GTK 0
+#define PLAT_WIN 0
+#define PLAT_WX 0
+
+#if defined(__WX__)
+#undef PLAT_WX
+#define PLAT_WX 1
+
+#elif defined(GTK)
+#undef PLAT_GTK
+#define PLAT_GTK 1
+
+#else
+#undef PLAT_WIN
+#define PLAT_WIN 1
+
+#endif
+
+
+// Include the main header for each platform
+
+#if PLAT_GTK
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#endif
+
+#if PLAT_WIN
+#define _WIN32_WINNT 0x0400 // Otherwise some required stuff gets ifdef'd out
+// Vassili Bourdo: shut up annoying Visual C++ warnings:
+#ifdef _MSC_VER
+#pragma warning(disable: 4800 4244 4309)
+#endif
+#include <windows.h>
+#include <richedit.h>
+#endif
+
+#if PLAT_WX
+#include <wx/wx.h>
+#endif
+
+// Underlying the implementation of the platform classes are platform specific types.
+// Sometimes these need to be passed around by client code so they are defined here
+
+#if PLAT_GTK
+typedef GdkColor ColourID;
+typedef GdkFont* FontID;
+typedef GdkDrawable* SurfaceID;
+typedef GtkWidget* WindowID;
+typedef GtkItemFactory* MenuID;
+#endif
+
+#if PLAT_WIN
+typedef COLORREF ColourID;
+typedef HFONT FontID;
+typedef HDC SurfaceID;
+typedef HWND WindowID;
+typedef HMENU MenuID;
+#endif
+
+#if PLAT_WX
+typedef wxColour ColourID;
+typedef wxFont* FontID;
+typedef wxDC* SurfaceID;
+typedef wxWindow* WindowID;
+typedef wxMenu* MenuID;
+#endif
+
+#if PLAT_GTK || PLAT_WX
+#define SHIFT_PRESSED 1
+#define LEFT_CTRL_PRESSED 2
+#define LEFT_ALT_PRESSED 4
+#endif
+
+// Point is exactly the same as the Win32 POINT and GTK+ GdkPoint so can be used interchangeably
+
+class Point {
+public:
+ int x;
+ int y;
+
+ Point(int x_=0, int y_=0) : x(x_), y(y_) {
+ }
+
+ // Other automatically defined methods (assignment, copy constructor, destructor) are fine
+
+ static Point FromLong(long lpoint);
+};
+
+// PRectangle is exactly the same as the Win32 RECT so can be used interchangeably
+// PRectangles contain their top and left sides, but not their right and bottom sides
+class PRectangle {
+public:
+ int left;
+ int top;
+ int right;
+ int bottom;
+
+ PRectangle(int left_=0, int top_=0, int right_=0, int bottom_ = 0) :
+ left(left_), top(top_), right(right_), bottom(bottom_) {
+ }
+
+ // Other automatically defined methods (assignment, copy constructor, destructor) are fine
+
+ bool Contains(Point pt) {
+ return (pt.x >= left) && (pt.x <= right) &&
+ (pt.y >= top) && (pt.y <= bottom);
+ }
+ bool Contains(PRectangle rc) {
+ return (rc.left >= left) && (rc.right <= right) &&
+ (rc.top >= top) && (rc.bottom <= bottom);
+ }
+ bool Intersects(PRectangle other) {
+ return (right >= other.left) && (left <= other.right) &&
+ (bottom >= other.top) && (top <= other.bottom);
+ }
+ int Width() { return right - left; }
+ int Height() { return bottom - top; }
+};
+
+#if PLAT_WX
+wxRect wxRectFromPRectangle(PRectangle prc);
+PRectangle PRectangleFromwxRect(wxRect rc);
+#endif
+
+class Colour {
+ ColourID co;
+public:
+ Colour(long lcol=0);
+ Colour(unsigned int red, unsigned int green, unsigned int blue);
+ bool operator==(const Colour &other) const;
+ long AsLong() const;
+ unsigned int GetRed();
+ unsigned int GetGreen();
+ unsigned int GetBlue();
+
+ friend class Surface;
+ friend class Palette;
+};
+
+// Colour pairs hold a desired colour and the colour that the graphics engine
+// allocates to approximate the desired colour.
+// To make palette management more automatic, ColourPairs could register at
+// construction time with a palette management object.
+struct ColourPair {
+ Colour desired;
+ Colour allocated;
+
+ ColourPair(Colour desired_=Colour(0,0,0)) {
+ desired = desired_;
+ allocated = desired;
+ }
+};
+
+class Window; // Forward declaration for Palette
+
+class Palette {
+ int used;
+ enum {numEntries = 100};
+ ColourPair entries[numEntries];
+#if PLAT_GTK
+ GdkColor *allocatedPalette;
+ int allocatedLen;
+#elif PLAT_WIN
+ HPALETTE hpal;
+#elif PLAT_WX
+ // wxPalette* pal; // **** Is this needed?
+#endif
+public:
+ bool allowRealization;
+
+ Palette();
+ ~Palette();
+
+ void Release();
+
+ // This method either adds a colour to the list of wanted colours (want==true)
+ // or retrieves the allocated colour back to the ColourPair.
+ // This is one method to make it easier to keep the code for wanting and retrieving in sync.
+ void WantFind(ColourPair &cp, bool want);
+
+ void Allocate(Window &w);
+
+ friend class Surface;
+};
+
+class Font {
+ FontID id;
+#if PLAT_WX
+ int ascent;
+#endif
+ // Private so Font objects can not be copied
+ Font(const Font &) {}
+ Font &operator=(const Font &) { id=0; return *this; }
+public:
+ Font();
+ ~Font();
+
+ void Create(const char *faceName, int size, bool bold=false, bool italic=false);
+ void Release();
+
+ FontID GetID() { return id; }
+ // Alias another font - caller guarantees not to Release
+ void SetID(FontID id_) { id = id_; }
+ friend class Surface;
+};
+
+// A surface abstracts a place to draw
+class Surface {
+private:
+#if PLAT_GTK
+ GdkDrawable *drawable;
+ GdkGC *gc;
+ GdkPixmap *ppixmap;
+ int x;
+ int y;
+ bool inited;
+ bool createdGC;
+#elif PLAT_WIN
+ HDC hdc;
+ bool hdcOwned;
+ HPEN pen;
+ HPEN penOld;
+ HBRUSH brush;
+ HBRUSH brushOld;
+ HFONT font;
+ HFONT fontOld;
+ HBITMAP bitmap;
+ HBITMAP bitmapOld;
+ HPALETTE paletteOld;
+#elif PLAT_WX
+ wxDC* hdc;
+ bool hdcOwned;
+ wxBitmap* bitmap;
+ int x;
+ int y;
+#endif
+
+ // Private so Surface objects can not be copied
+ Surface(const Surface &) {}
+ Surface &operator=(const Surface &) { return *this; }
+#if PLAT_WIN || PLAT_WX
+ void BrushColor(Colour back);
+ void SetFont(Font &font_);
+#endif
+public:
+ Surface();
+ ~Surface();
+
+ void Init();
+ void Init(SurfaceID hdc_);
+ void InitPixMap(int width, int height, Surface *surface_);
+
+ void Release();
+ bool Initialised();
+ void PenColour(Colour fore);
+ int LogPixelsY();
+ void MoveTo(int x_, int y_);
+ void LineTo(int x_, int y_);
+ void Polygon(Point *pts, int npts, Colour fore, Colour back);
+ void RectangleDraw(PRectangle rc, Colour fore, Colour back);
+ void FillRectangle(PRectangle rc, Colour back);
+ void FillRectangle(PRectangle rc, Surface &surfacePattern);
+ void RoundedRectangle(PRectangle rc, Colour fore, Colour back);
+ void Ellipse(PRectangle rc, Colour fore, Colour back);
+ void Copy(PRectangle rc, Point from, Surface &surfaceSource);
+
+ void DrawText(PRectangle rc, Font &font_, int ybase, const char *s, int len, Colour fore, Colour back);
+ void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, Colour fore, Colour back);
+ void MeasureWidths(Font &font_, const char *s, int len, int *positions);
+ int WidthText(Font &font_, const char *s, int len);
+ int WidthChar(Font &font_, char ch);
+ int Ascent(Font &font_);
+ int Descent(Font &font_);
+ int InternalLeading(Font &font_);
+ int ExternalLeading(Font &font_);
+ int Height(Font &font_);
+ int AverageCharWidth(Font &font_);
+
+ int SetPalette(Palette *pal, bool inBackGround);
+ void SetClip(PRectangle rc);
+};
+
+// Class to hide the details of window manipulation
+// Does not own the window which will normally have a longer life than this object
+class Window {
+ friend class ListBox;
+protected:
+ WindowID id;
+public:
+ Window() : id(0) {}
+ virtual ~Window();
+ Window &operator=(WindowID id_) {
+ id = id_;
+ return *this;
+ }
+ WindowID GetID() { return id; }
+ bool Created() { return id != 0; }
+ void Destroy();
+ bool HasFocus();
+ PRectangle GetPosition();
+ void SetPosition(PRectangle rc);
+ void SetPositionRelative(PRectangle rc, Window relativeTo);
+ PRectangle GetClientPosition();
+ void Show(bool show=true);
+ void InvalidateAll();
+ void InvalidateRectangle(PRectangle rc);
+ void SetFont(Font &font);
+ enum Cursor { cursorText, cursorArrow, cursorUp, cursorWait, cursorHoriz, cursorVert, cursorReverseArrow };
+ void SetCursor(Cursor curs);
+ void SetTitle(const char *s);
+#if PLAT_WIN
+ LRESULT SendMessage(UINT msg, WPARAM wParam=0, LPARAM lParam=0);
+ int GetDlgCtrlID();
+ HINSTANCE GetInstance();
+#endif
+};
+
+class ListBox : public Window {
+#if PLAT_GTK
+ WindowID list;
+ WindowID scroller;
+ int current;
+#endif
+public:
+ ListBox();
+ virtual ~ListBox();
+ ListBox &operator=(WindowID id_) {
+ id = id_;
+ return *this;
+ }
+ void Create(Window &parent, int ctrlID);
+ void Clear();
+ void Append(char *s);
+ int Length();
+ void Select(int n);
+ int GetSelection();
+ int Find(const char *prefix);
+ void GetValue(int n, char *value, int len);
+ void Sort();
+};
+
+class Menu {
+ MenuID id;
+public:
+ Menu();
+ MenuID GetID() { return id; }
+ void CreatePopUp();
+ void Destroy();
+ void Show(Point pt, Window &w);
+};
+
+// Platform class used to retrieve system wide parameters such as double click speed
+// and chrome colour. Not a creatable object, more of a module with several functions.
+class Platform {
+ // Private so Platform objects can not be copied
+ Platform(const Platform &) {}
+ Platform &operator=(const Platform &) { return *this; }
+public:
+ // Should be private because no new Platforms are ever created
+ // but gcc warns about this
+ Platform() {}
+ ~Platform() {}
+ static Colour Chrome();
+ static Colour ChromeHighlight();
+ static const char *DefaultFont();
+ static int DefaultFontSize();
+ static unsigned int DoubleClickTime();
+ static void DebugDisplay(const char *s);
+ static bool IsKeyDown(int key);
+ static long SendScintilla(
+ WindowID w, unsigned int msg, unsigned long wParam=0, long lParam=0);
+
+ // These are utility functions not really tied to a platform
+ static int Minimum(int a, int b);
+ static int Maximum(int a, int b);
+ static void DebugPrintf(const char *format, ...);
+ static int Clamp(int val, int minVal, int maxVal);
+};
+
+#endif
diff --git a/include/PropSet.h b/include/PropSet.h
new file mode 100644
index 000000000..31da01f95
--- /dev/null
+++ b/include/PropSet.h
@@ -0,0 +1,180 @@
+// SciTE - Scintilla based Text Editor
+// PropSet.h - a java style properties file module
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef PROPSET_H
+#define PROPSET_H
+
+bool EqualCaseInsensitive(const char *a, const char *b);
+
+// Define another string class.
+// While it would be 'better' to use std::string, that doubles the executable size.
+
+inline char *StringDup(const char *s) {
+ if (!s)
+ return 0;
+ char *sNew = new char[strlen(s) + 1];
+ if (sNew)
+ strcpy(sNew, s);
+ return sNew;
+}
+
+class SString {
+ char *s;
+public:
+ SString() {
+ s = 0;
+ }
+ SString(const SString &source) {
+ s = StringDup(source.s);
+ }
+ SString(const char *s_) {
+ s = StringDup(s_);
+ }
+ SString(int i) {
+ char number[100];
+ sprintf(number, "%0d", i);
+ //itoa(i, number, 10);
+ s = StringDup(number);
+ }
+ ~SString() {
+ delete []s;
+ s = 0;
+ }
+ SString &operator=(const SString &source) {
+ if (this != &source) {
+ delete []s;
+ s = StringDup(source.s);
+ }
+ return *this;
+ }
+ bool operator==(const SString &other) const {
+ if ((s == 0) && (other.s == 0))
+ return true;
+ if ((s == 0) || (other.s == 0))
+ return false;
+ return strcmp(s, other.s) == 0;
+ }
+ bool operator==(const char *sother) const {
+ if ((s == 0) && (sother == 0))
+ return true;
+ if ((s == 0) || (sother == 0))
+ return false;
+ return strcmp(s, sother) == 0;
+ }
+ const char *c_str() const {
+ if (s)
+ return s;
+ else
+ return "";
+ }
+ int length() const {
+ if (s)
+ return strlen(s);
+ else
+ return 0;
+ }
+ char operator[](int i) {
+ if (s)
+ return s[i];
+ else
+ return '\0';
+ }
+ SString &operator +=(const char *sother) {
+ int len = length();
+ int lenOther = strlen(sother);
+ char *sNew = new char[len + lenOther + 1];
+ if (sNew) {
+ if (s)
+ memcpy(sNew, s, len);
+ memcpy(sNew + len, sother, lenOther);
+ sNew[len + lenOther] = '\0';
+ delete []s;
+ s = sNew;
+ }
+ return *this;
+ }
+ int value() {
+ if (s)
+ return atoi(s);
+ else
+ return 0;
+ }
+};
+
+class PropSet {
+private:
+ char **vals;
+ int size;
+ int used;
+public:
+ PropSet *superPS;
+ PropSet();
+ ~PropSet();
+ void EnsureCanAddEntry();
+ void Set(const char *key, const char *val);
+ void Set(char *keyval);
+ SString Get(const char *key);
+ int GetInt(const char *key, int defaultValue=0);
+ SString GetWild(const char *keybase, const char *filename);
+ SString GetNewExpand(const char *keybase, const char *filename);
+ void Clear();
+ void ReadFromMemory(const char *data, int len);
+ void Read(const char *filename);
+};
+
+// This is a fixed length list of strings suitable for display in combo boxes
+// as a memory of user entries
+template<int sz>
+class EntryMemory {
+ SString entries[sz];
+public:
+ void Insert(SString s) {
+ for (int i=0;i<sz;i++) {
+ if (entries[i] == s) {
+ for (int j=i;j>0;j--) {
+ entries[j] = entries[j-1];
+ }
+ entries[0] = s;
+ return;
+ }
+ }
+ for (int k=sz-1;k>0;k--) {
+ entries[k] = entries[k-1];
+ }
+ entries[0] = s;
+ }
+ int Length() const {
+ int len = 0;
+ for (int i=0;i<sz;i++)
+ if (entries[i].length())
+ len++;
+ return len;
+ }
+ SString At(int n) const {
+ return entries[n];
+ }
+};
+
+class WordList {
+public:
+ // Each word contains at least one character - a empty word acts as sentinal at the end.
+ char **words;
+ char *list;
+ int len;
+ bool onlyLineEnds; // Delimited by any white space or only line ends
+ int starts[256];
+ WordList(bool onlyLineEnds_ = false) :
+ words(0), list(0), len(0), onlyLineEnds(onlyLineEnds_) {}
+ ~WordList() { Clear(); }
+ operator bool() { return list; }
+ const char *operator[](int ind) { return words[ind]; }
+ void Clear();
+ void Set(const char *s);
+ char *Allocate(int size);
+ void SetFromAllocated();
+ bool InList(const char *s);
+};
+
+#endif
diff --git a/include/SciLexer.h b/include/SciLexer.h
new file mode 100644
index 000000000..f3f97b7ae
--- /dev/null
+++ b/include/SciLexer.h
@@ -0,0 +1,172 @@
+// Scintilla source code edit control
+// SciLexer - interface to the added lexer functions in the SciLexer version of the edit control
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef SCILEXER_H
+#define SCILEXER_H
+
+// SciLexer features - not in standard Scintilla
+
+#define SCLEX_CONTAINER 0
+#define SCLEX_NULL 1
+#define SCLEX_PYTHON 2
+#define SCLEX_CPP 3
+#define SCLEX_HTML 4
+#define SCLEX_XML 5
+#define SCLEX_PERL 6
+#define SCLEX_SQL 7
+#define SCLEX_VB 8
+#define SCLEX_PROPERTIES 9
+#define SCLEX_ERRORLIST 10
+#define SCLEX_MAKEFILE 11
+#define SCLEX_BATCH 12
+
+// Lexical states for SCLEX_PYTHON
+#define SCE_P_DEFAULT 0
+#define SCE_P_COMMENTLINE 1
+#define SCE_P_NUMBER 2
+#define SCE_P_STRING 3
+#define SCE_P_CHARACTER 4
+#define SCE_P_WORD 5
+#define SCE_P_TRIPLE 6
+#define SCE_P_TRIPLEDOUBLE 7
+#define SCE_P_CLASSNAME 8
+#define SCE_P_DEFNAME 9
+#define SCE_P_OPERATOR 10
+#define SCE_P_IDENTIFIER 11
+#define SCE_P_COMMENTBLOCK 12
+#define SCE_P_STRINGEOL 13
+
+// Lexical states for SCLEX_CPP, SCLEX_VB
+#define SCE_C_DEFAULT 0
+#define SCE_C_COMMENT 1
+#define SCE_C_COMMENTLINE 2
+#define SCE_C_COMMENTDOC 3
+#define SCE_C_NUMBER 4
+#define SCE_C_WORD 5
+#define SCE_C_STRING 6
+#define SCE_C_CHARACTER 7
+#define SCE_C_PUNTUATION 8
+#define SCE_C_PREPROCESSOR 9
+#define SCE_C_OPERATOR 10
+#define SCE_C_IDENTIFIER 11
+#define SCE_C_STRINGEOL 12
+
+// Lexical states for SCLEX_HTML, SCLEX_xML
+#define SCE_H_DEFAULT 0
+#define SCE_H_TAG 1
+#define SCE_H_TAGUNKNOWN 2
+#define SCE_H_ATTRIBUTE 3
+#define SCE_H_ATTRIBUTEUNKNOWN 4
+#define SCE_H_NUMBER 5
+#define SCE_H_DOUBLESTRING 6
+#define SCE_H_SINGLESTRING 7
+#define SCE_H_OTHER 8
+#define SCE_H_COMMENT 9
+#define SCE_H_ENTITY 10
+// XML and ASP
+#define SCE_H_TAGEND 11
+#define SCE_H_XMLSTART 12
+#define SCE_H_XMLEND 13
+#define SCE_H_SCRIPT 14
+#define SCE_H_ASP 15
+#define SCE_H_ASPAT 16
+// Embedded Javascript
+#define SCE_HJ_START 40
+#define SCE_HJ_DEFAULT 41
+#define SCE_HJ_COMMENT 42
+#define SCE_HJ_COMMENTLINE 43
+#define SCE_HJ_COMMENTDOC 44
+#define SCE_HJ_NUMBER 45
+#define SCE_HJ_WORD 46
+#define SCE_HJ_KEYWORD 47
+#define SCE_HJ_DOUBLESTRING 48
+#define SCE_HJ_SINGLESTRING 49
+#define SCE_HJ_SYMBOLS 50
+#define SCE_HJ_STRINGEOL 51
+// ASP Javascript
+#define SCE_HJA_START 55
+#define SCE_HJA_DEFAULT 56
+#define SCE_HJA_COMMENT 57
+#define SCE_HJA_COMMENTLINE 58
+#define SCE_HJA_COMMENTDOC 59
+#define SCE_HJA_NUMBER 60
+#define SCE_HJA_WORD 61
+#define SCE_HJA_KEYWORD 62
+#define SCE_HJA_DOUBLESTRING 63
+#define SCE_HJA_SINGLESTRING 64
+#define SCE_HJA_SYMBOLS 65
+#define SCE_HJA_STRINGEOL 66
+// Embedded VBScript
+#define SCE_HB_START 70
+#define SCE_HB_DEFAULT 71
+#define SCE_HB_COMMENTLINE 72
+#define SCE_HB_NUMBER 73
+#define SCE_HB_WORD 74
+#define SCE_HB_STRING 75
+#define SCE_HB_IDENTIFIER 76
+#define SCE_HB_STRINGEOL 77
+// ASP VBScript
+#define SCE_HBA_START 80
+#define SCE_HBA_DEFAULT 81
+#define SCE_HBA_COMMENTLINE 82
+#define SCE_HBA_NUMBER 83
+#define SCE_HBA_WORD 84
+#define SCE_HBA_STRING 85
+#define SCE_HBA_IDENTIFIER 86
+#define SCE_HBA_STRINGEOL 87
+// Embedded Python
+#define SCE_HP_START 90
+#define SCE_HP_DEFAULT 91
+#define SCE_HP_COMMENTLINE 92
+#define SCE_HP_NUMBER 93
+#define SCE_HP_STRING 94
+#define SCE_HP_CHARACTER 95
+#define SCE_HP_WORD 96
+#define SCE_HP_TRIPLE 97
+#define SCE_HP_TRIPLEDOUBLE 98
+#define SCE_HP_CLASSNAME 99
+#define SCE_HP_DEFNAME 100
+#define SCE_HP_OPERATOR 101
+#define SCE_HP_IDENTIFIER 102
+// ASP Python
+#define SCE_HPA_START 105
+#define SCE_HPA_DEFAULT 106
+#define SCE_HPA_COMMENTLINE 107
+#define SCE_HPA_NUMBER 108
+#define SCE_HPA_STRING 109
+#define SCE_HPA_CHARACTER 110
+#define SCE_HPA_WORD 111
+#define SCE_HPA_TRIPLE 112
+#define SCE_HPA_TRIPLEDOUBLE 113
+#define SCE_HPA_CLASSNAME 114
+#define SCE_HPA_DEFNAME 115
+#define SCE_HPA_OPERATOR 116
+#define SCE_HPA_IDENTIFIER 117
+
+// Lexical states for SCLEX_PERL
+#define SCE_PL_DEFAULT 0
+#define SCE_PL_HERE 1
+#define SCE_PL_COMMENTLINE 2
+#define SCE_PL_POD 3
+#define SCE_PL_NUMBER 4
+#define SCE_PL_WORD 5
+#define SCE_PL_STRING 6
+#define SCE_PL_CHARACTER 7
+#define SCE_PL_PUNCTUATION 8
+#define SCE_PL_PREPROCESSOR 9
+#define SCE_PL_OPERATOR 10
+#define SCE_PL_IDENTIFIER 11
+#define SCE_PL_SCALAR 12
+#define SCE_PL_ARRAY 13
+#define SCE_PL_HASH 14
+#define SCE_PL_SYMBOLTABLE 15
+#define SCE_PL_REF 16
+#define SCE_PL_REGEX 17
+#define SCE_PL_REGSUBST 18
+#define SCE_PL_LONGQUOTE 19
+#define SCE_PL_BACKTICKS 20
+#define SCE_PL_DATASECTION 21
+
+#endif
diff --git a/include/Scintilla.h b/include/Scintilla.h
new file mode 100644
index 000000000..35b5ad2e2
--- /dev/null
+++ b/include/Scintilla.h
@@ -0,0 +1,415 @@
+// Scintilla source code edit control
+// Scintilla.h - interface to the edit control
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef SCINTILLA_H
+#define SCINTILLA_H
+
+// Compile-time configuration options
+#define MACRO_SUPPORT 1 // Comment out to remove macro hooks
+
+#if PLAT_GTK
+#include <gdk/gdk.h>
+#include <gtk/gtkvbox.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SCINTILLA(obj) GTK_CHECK_CAST (obj, scintilla_get_type (), ScintillaObject)
+#define SCINTILLA_CLASS(klass) GTK_CHECK_CLASS_CAS T (klass, scintilla_get_type (), ScintillaClass)
+#define IS_SCINTILLA(obj) GTK_CHECK_TYPE (obj, scintilla_get_type ())
+
+ typedef struct _ScintillaObject ScintillaObject;
+ typedef struct _ScintillaClass ScintillaClass;
+
+ struct _ScintillaObject
+ {
+ GtkFixed vbox;
+ void *pscin;
+ };
+
+ struct _ScintillaClass
+ {
+ GtkFixedClass parent_class;
+
+ void (* command) (ScintillaObject *ttt);
+ void (* notify) (ScintillaObject *ttt);
+ };
+
+ guint scintilla_get_type (void);
+ GtkWidget* scintilla_new (void);
+ void scintilla_set_id (ScintillaObject *sci,int id);
+ long scintilla_send_message (ScintillaObject *sci,int iMessage,int wParam,int lParam);
+
+#include "WinDefs.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#if PLAT_WX
+#include "WinDefs.h"
+#endif
+
+// Both GTK and Windows
+
+#define INVALID_POSITION -1
+
+// Define start of Scintilla messages to be greater than all edit (EM_*) messages
+// as many EM_ messages can be used.
+#define SCI_START 2000
+#define SCI_OPTIONAL_START 3000
+#define SCI_LEXER_START 4000
+
+#define SCI_ADDTEXT SCI_START + 1
+#define SCI_ADDSTYLEDTEXT SCI_START + 2
+#define SCI_INSERTTEXT SCI_START + 3
+#define SCI_CLEARALL SCI_START + 4
+#define SCI_GETLENGTH SCI_START + 6
+#define SCI_GETCHARAT SCI_START + 7
+#define SCI_GETCURRENTPOS SCI_START + 8
+#define SCI_GETANCHOR SCI_START + 9
+#define SCI_GETSTYLEAT SCI_START + 10
+
+#define SCI_REDO SCI_START + 11
+#define SCI_SETUNDOCOLLECTION SCI_START + 12
+#define SCI_SELECTALL SCI_START + 13
+#define SCI_SETSAVEPOINT SCI_START + 14
+#define SCI_GETSTYLEDTEXT SCI_START + 15
+#define SCI_CANREDO SCI_START + 16
+#define SCI_MARKERLINEFROMHANDLE SCI_START + 17
+#define SCI_MARKERDELETEHANDLE SCI_START + 18
+
+#define SC_UNDOCOLLECT_NONE 0
+#define SC_UNDOCOLLECT_AUTOSTART 1
+
+#define SCI_GETVIEWWS SCI_START + 20
+#define SCI_SETVIEWWS SCI_START + 21
+#define SCI_CHANGEPOSITION SCI_START + 22
+#define SCI_GOTOLINE SCI_START + 24
+#define SCI_GOTOPOS SCI_START + 25
+#define SCI_SETANCHOR SCI_START + 26
+#define SCI_GETCURLINE SCI_START + 27
+#define SCI_GETENDSTYLED SCI_START + 28
+#define SCI_CONVERTEOLS SCI_START + 29
+
+#define SCI_GETEOLMODE SCI_START + 30
+#define SCI_SETEOLMODE SCI_START + 31
+
+#define SC_EOL_CRLF 0
+#define SC_EOL_CR 1
+#define SC_EOL_LF 2
+
+#define SCI_STARTSTYLING SCI_START + 32
+#define SCI_SETSTYLING SCI_START + 33
+
+#define SCI_SETBUFFEREDDRAW SCI_START + 35
+#define SCI_SETTABWIDTH SCI_START + 36
+#define SCI_SETCODEPAGE SCI_START + 37
+#define SCI_SETUSEPALETTE SCI_START + 39
+
+#define MARKER_MAX 31
+
+#define SC_MARK_CIRCLE 0
+#define SC_MARK_ROUNDRECT 1
+#define SC_MARK_ARROW 2
+#define SC_MARK_SMALLRECT 3
+#define SC_MARK_SHORTARROW 4
+#define SC_MARK_EMPTY 5
+#define SC_MARK_ARROWDOWN 6
+#define SC_MARK_MINUS 7
+#define SC_MARK_PLUS 8
+
+#define SCI_MARKERDEFINE SCI_START + 40
+#define SCI_MARKERSETFORE SCI_START + 41
+#define SCI_MARKERSETBACK SCI_START + 42
+#define SCI_MARKERADD SCI_START + 43
+#define SCI_MARKERDELETE SCI_START + 44
+#define SCI_MARKERDELETEALL SCI_START + 45
+#define SCI_MARKERGET SCI_START + 46
+#define SCI_MARKERNEXT SCI_START + 47
+#define SCI_MARKERPREVIOUS SCI_START + 48
+
+#define SC_MARKNUM_FOLDER 30
+#define SC_MARKNUM_FOLDEROPEN 31
+
+#define SC_MASK_FOLDERS ((1<<SC_MARKNUM_FOLDER) | (1<<SC_MARKNUM_FOLDEROPEN))
+
+#define SC_MARGIN_SYMBOL 0
+#define SC_MARGIN_NUMBER 1
+
+#define SCI_SETMARGINTYPEN SCI_START + 240
+#define SCI_GETMARGINTYPEN SCI_START + 241
+#define SCI_SETMARGINWIDTHN SCI_START + 242
+#define SCI_GETMARGINWIDTHN SCI_START + 243
+#define SCI_SETMARGINMASKN SCI_START + 244
+#define SCI_GETMARGINMASKN SCI_START + 245
+#define SCI_SETMARGINSENSITIVEN SCI_START + 246
+#define SCI_GETMARGINSENSITIVEN SCI_START + 247
+
+#define STYLE_DEFAULT 32
+#define STYLE_LINENUMBER 33
+#define STYLE_BRACELIGHT 34
+#define STYLE_BRACEBAD 35
+#define STYLE_CONTROLCHAR 36
+#define STYLE_MAX 127
+
+#define SCI_STYLECLEARALL SCI_START + 50
+#define SCI_STYLESETFORE SCI_START + 51
+#define SCI_STYLESETBACK SCI_START + 52
+#define SCI_STYLESETBOLD SCI_START + 53
+#define SCI_STYLESETITALIC SCI_START + 54
+#define SCI_STYLESETSIZE SCI_START + 55
+#define SCI_STYLESETFONT SCI_START + 56
+#define SCI_STYLESETEOLFILLED SCI_START + 57
+#define SCI_STYLERESETDEFAULT SCI_START + 58
+
+#define SCI_SETSELFORE SCI_START + 67
+#define SCI_SETSELBACK SCI_START + 68
+#define SCI_SETCARETFORE SCI_START + 69
+
+#define SCI_ASSIGNCMDKEY SCI_START + 70
+#define SCI_CLEARCMDKEY SCI_START + 71
+#define SCI_CLEARALLCMDKEYS SCI_START + 72
+
+#define SCI_SETSTYLINGEX SCI_START + 73
+
+#define SCI_GETCARETPERIOD SCI_START + 75
+#define SCI_SETCARETPERIOD SCI_START + 76
+#define SCI_SETWORDCHARS SCI_START + 77
+
+#define SCI_BEGINUNDOACTION SCI_START + 78
+#define SCI_ENDUNDOACTION SCI_START + 79
+
+#define INDIC_MAX 7
+
+#define INDIC_PLAIN 0
+#define INDIC_SQUIGGLE 1
+#define INDIC_TT 2
+
+#define INDIC0_MASK 32
+#define INDIC1_MASK 64
+#define INDIC2_MASK 128
+#define INDICS_MASK (INDIC0_MASK | INDIC1_MASK | INDIC2_MASK)
+
+#define SCI_INDICSETSTYLE SCI_START + 80
+#define SCI_INDICGETSTYLE SCI_START + 81
+#define SCI_INDICSETFORE SCI_START + 82
+#define SCI_INDICGETFORE SCI_START + 83
+
+#define SCI_SETSTYLEBITS SCI_START + 90
+#define SCI_GETSTYLEBITS SCI_START + 91
+#define SCI_SETLINESTATE SCI_START + 92
+#define SCI_GETLINESTATE SCI_START + 93
+#define SCI_GETMAXLINESTATE SCI_START + 94
+
+#define SCI_AUTOCSHOW SCI_START + 100
+#define SCI_AUTOCCANCEL SCI_START + 101
+#define SCI_AUTOCACTIVE SCI_START + 102
+#define SCI_AUTOCPOSSTART SCI_START + 103
+#define SCI_AUTOCCOMPLETE SCI_START + 104
+#define SCI_AUTOCSTOPS SCI_START + 105
+
+#define SCI_CALLTIPSHOW SCI_START + 200
+#define SCI_CALLTIPCANCEL SCI_START + 201
+#define SCI_CALLTIPACTIVE SCI_START + 202
+#define SCI_CALLTIPPOSSTART SCI_START + 203
+#define SCI_CALLTIPSETHLT SCI_START + 204
+#define SCI_CALLTIPSETBACK SCI_START + 205
+
+#define SC_FOLDLEVELBASE 0x400
+#define SC_FOLDLEVELWHITEFLAG 0x1000
+#define SC_FOLDLEVELHEADERFLAG 0x2000
+#define SC_FOLDLEVELNUMBERMASK 0x0FFF
+
+#define SCI_VISIBLEFROMDOCLINE SCI_START + 220
+#define SCI_DOCLINEFROMVISIBLE SCI_START + 221
+#define SCI_SETFOLDLEVEL SCI_START + 222
+#define SCI_GETFOLDLEVEL SCI_START + 223
+#define SCI_GETLASTCHILD SCI_START + 224
+#define SCI_GETFOLDPARENT SCI_START + 225
+#define SCI_SHOWLINES SCI_START + 226
+#define SCI_HIDELINES SCI_START + 227
+#define SCI_GETLINEVISIBLE SCI_START + 228
+#define SCI_SETFOLDEXPANDED SCI_START + 229
+#define SCI_GETFOLDEXPANDED SCI_START + 230
+#define SCI_TOGGLEFOLD SCI_START + 231
+#define SCI_ENSUREVISIBLE SCI_START + 232
+#define SCI_SETFOLDFLAGS SCI_START + 233
+
+// Key messages
+#define SCI_LINEDOWN SCI_START + 300
+#define SCI_LINEDOWNEXTEND SCI_START + 301
+#define SCI_LINEUP SCI_START + 302
+#define SCI_LINEUPEXTEND SCI_START + 303
+#define SCI_CHARLEFT SCI_START + 304
+#define SCI_CHARLEFTEXTEND SCI_START + 305
+#define SCI_CHARRIGHT SCI_START + 306
+#define SCI_CHARRIGHTEXTEND SCI_START + 307
+#define SCI_WORDLEFT SCI_START + 308
+#define SCI_WORDLEFTEXTEND SCI_START + 309
+#define SCI_WORDRIGHT SCI_START + 310
+#define SCI_WORDRIGHTEXTEND SCI_START + 311
+#define SCI_HOME SCI_START + 312
+#define SCI_HOMEEXTEND SCI_START + 313
+#define SCI_LINEEND SCI_START + 314
+#define SCI_LINEENDEXTEND SCI_START + 315
+#define SCI_DOCUMENTSTART SCI_START + 316
+#define SCI_DOCUMENTSTARTEXTEND SCI_START + 317
+#define SCI_DOCUMENTEND SCI_START + 318
+#define SCI_DOCUMENTENDEXTEND SCI_START + 319
+#define SCI_PAGEUP SCI_START + 320
+#define SCI_PAGEUPEXTEND SCI_START + 321
+#define SCI_PAGEDOWN SCI_START + 322
+#define SCI_PAGEDOWNEXTEND SCI_START + 323
+#define SCI_EDITTOGGLEOVERTYPE SCI_START + 324
+#define SCI_CANCEL SCI_START + 325
+#define SCI_DELETEBACK SCI_START + 326
+#define SCI_TAB SCI_START + 327
+#define SCI_BACKTAB SCI_START + 328
+#define SCI_NEWLINE SCI_START + 329
+#define SCI_FORMFEED SCI_START + 330
+#define SCI_VCHOME SCI_START + 331
+#define SCI_VCHOMEEXTEND SCI_START + 332
+#define SCI_ZOOMIN SCI_START + 333
+#define SCI_ZOOMOUT SCI_START + 334
+#define SCI_DELWORDLEFT SCI_START + 335
+#define SCI_DELWORDRIGHT SCI_START + 336
+
+#define SCI_LINELENGTH SCI_START + 350
+#define SCI_BRACEHIGHLIGHT SCI_START + 351
+#define SCI_BRACEBADLIGHT SCI_START + 352
+#define SCI_BRACEMATCH SCI_START + 353
+#define SCI_GETVIEWEOL SCI_START + 355
+#define SCI_SETVIEWEOL SCI_START + 356
+#define SCI_GETDOCPOINTER SCI_START + 357
+#define SCI_SETDOCPOINTER SCI_START + 358
+#define SCI_SETMODEVENTMASK SCI_START + 359
+
+#define EDGE_NONE 0
+#define EDGE_LINE 1
+#define EDGE_BACKGROUND 2
+
+#define SCI_GETEDGECOLUMN SCI_START + 360
+#define SCI_SETEDGECOLUMN SCI_START + 361
+#define SCI_GETEDGEMODE SCI_START + 362
+#define SCI_SETEDGEMODE SCI_START + 363
+#define SCI_GETEDGECOLOUR SCI_START + 364
+#define SCI_SETEDGECOLOUR SCI_START + 365
+
+#define SCI_SEARCHANCHOR SCI_START + 366
+#define SCI_SEARCHNEXT SCI_START + 367
+#define SCI_SEARCHPREV SCI_START + 368
+
+#define CARET_SLOP 0x01 // Show caret within N lines of edge when it's scrolled to view
+#define CARET_CENTER 0x02 // Center caret on screen when it's scrolled to view
+#define CARET_STRICT 0x04 // OR this with CARET_CENTER to reposition even when visible, or
+ // OR this with CARET_SLOP to reposition whenever outside slop border
+
+#define SCI_SETCARETPOLICY SCI_START + 369
+
+// GTK+ Specific
+#define SCI_GRABFOCUS SCI_START + 400
+
+// Optional module for macro recording
+#ifdef MACRO_SUPPORT
+typedef void (tMacroRecorder)(UINT iMessage, WPARAM wParam, LPARAM lParam,
+ void *userData);
+#define SCI_STARTRECORD SCI_OPTIONAL_START + 1
+#define SCI_STOPRECORD SCI_OPTIONAL_START + 2
+#endif
+
+#define SCI_SETLEXER SCI_LEXER_START + 1
+#define SCI_GETLEXER SCI_LEXER_START + 2
+#define SCI_COLOURISE SCI_LEXER_START + 3
+#define SCI_SETPROPERTY SCI_LEXER_START + 4
+#define SCI_SETKEYWORDS SCI_LEXER_START + 5
+
+// Notifications
+
+// Type of modification and the action which caused the modification
+// These are defined as a bit mask to make it easy to specify which notifications are wanted.
+// One bit is set from each of SC_MOD_* and SC_PERFORMED_*.
+#define SC_MOD_INSERTTEXT 0x1
+#define SC_MOD_DELETETEXT 0x2
+#define SC_MOD_CHANGESTYLE 0x4
+#define SC_MOD_CHANGEFOLD 0x8
+#define SC_PERFORMED_USER 0x10
+#define SC_PERFORMED_UNDO 0x20
+#define SC_PERFORMED_REDO 0x40
+#define SC_LASTSTEPINUNDOREDO 0x100
+
+#define SC_MODEVENTMASKALL 0x377
+
+struct SCNotification {
+ NMHDR nmhdr;
+ int position; // SCN_STYLENEEDED, SCN_MODIFIED
+ int ch; // SCN_CHARADDED, SCN_KEY
+ int modifiers; // SCN_KEY
+ int modificationType; // SCN_MODIFIED
+ const char *text; // SCN_MODIFIED
+ int length; // SCN_MODIFIED
+ int linesAdded; // SCN_MODIFIED
+#ifdef MACRO_SUPPORT
+ int message; // SCN_MACRORECORD
+ int wParam; // SCN_MACRORECORD
+ int lParam; // SCN_MACRORECORD
+#endif
+ int line; // SCN_MODIFIED
+ int foldLevelNow; // SCN_MODIFIED
+ int foldLevelPrev; // SCN_MODIFIED
+ int margin; // SCN_MARGINCLICK
+};
+
+#define SCN_STYLENEEDED 2000
+#define SCN_CHARADDED 2001
+#define SCN_SAVEPOINTREACHED 2002
+#define SCN_SAVEPOINTLEFT 2003
+#define SCN_MODIFYATTEMPTRO 2004
+// GTK+ Specific to work around focus and accelerator problems:
+#define SCN_KEY 2005
+#define SCN_DOUBLECLICK 2006
+#define SCN_UPDATEUI 2007
+// The old name for SCN_UPDATEUI:
+#define SCN_CHECKBRACE 2007
+#define SCN_MODIFIED 2008
+// Optional module for macro recording
+#ifdef MACRO_SUPPORT
+#define SCN_MACRORECORD 2009
+#endif
+#define SCN_MARGINCLICK 2010
+#define SCN_NEEDSHOWN 2011
+
+#ifdef STATIC_BUILD
+void Scintilla_RegisterClasses(HINSTANCE hInstance);
+#endif
+
+// Deprecation section listing all API features that are deprecated and will
+// will be removed completely in a future version.
+// To enable these features define INCLUDE_DEPRECATED_FEATURES
+
+#ifdef INCLUDE_DEPRECATED_FEATURES
+
+// Default style settings. These are deprecated and will be removed in a future version.
+#define SCI_SETFORE SCI_START + 60
+#define SCI_SETBACK SCI_START + 61
+#define SCI_SETBOLD SCI_START + 62
+#define SCI_SETITALIC SCI_START + 63
+#define SCI_SETSIZE SCI_START + 64
+#define SCI_SETFONT SCI_START + 65
+
+#define SCI_APPENDUNDOSTARTACTION SCI_START + 74
+
+#define SC_UNDOCOLLECT_MANUALSTART 2
+
+// Deprecated in release 1.22
+#define SCI_SETMARGINWIDTH SCI_START + 34
+#define SCI_SETLINENUMBERWIDTH SCI_START + 38
+
+#endif
+
+#endif
diff --git a/include/WinDefs.h b/include/WinDefs.h
new file mode 100644
index 000000000..0b125e731
--- /dev/null
+++ b/include/WinDefs.h
@@ -0,0 +1,218 @@
+// Scintilla source code edit control
+// WinDefs.h - the subset of definitions from Windows needed by Scintilla for GTK+
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef WINDEFS_H
+#define WINDEFS_H
+
+#define WORD short
+#define WPARAM unsigned long
+#define LPARAM long
+#define LRESULT long
+#define DWORD long
+
+#define UINT unsigned int
+#define LPSTR char *
+#define LONG long
+
+/* RTF control */
+#define EM_CANPASTE (1074)
+#define EM_CANUNDO (198)
+#define EM_CHARFROMPOS (215)
+#define EM_DISPLAYBAND (1075)
+#define EM_EMPTYUNDOBUFFER (205)
+#define EM_EXGETSEL (1076)
+#define EM_EXLIMITTEXT (1077)
+#define EM_EXLINEFROMCHAR (1078)
+#define EM_EXSETSEL (1079)
+#define EM_FINDTEXT (1080)
+#define EM_FINDTEXTEX (1103)
+#define EM_FINDWORDBREAK (1100)
+#define EM_FMTLINES (200)
+#define EM_FORMATRANGE (1081)
+#define EM_GETCHARFORMAT (1082)
+#define EM_GETEVENTMASK (1083)
+#define EM_GETFIRSTVISIBLELINE (206)
+#define EM_GETHANDLE (189)
+#define EM_GETLIMITTEXT (213)
+#define EM_GETLINE (196)
+#define EM_GETLINECOUNT (186)
+#define EM_GETMARGINS (212)
+#define EM_GETMODIFY (184)
+#define EM_GETIMECOLOR (1129)
+#define EM_GETIMEOPTIONS (1131)
+#define EM_GETOPTIONS (1102)
+#define EM_GETOLEINTERFACE (1084)
+#define EM_GETPARAFORMAT (1085)
+#define EM_GETPASSWORDCHAR (210)
+#define EM_GETPUNCTUATION (1125)
+#define EM_GETRECT (178)
+#define EM_GETSEL (176)
+#define EM_GETSELTEXT (1086)
+#define EM_GETTEXTRANGE (1099)
+#define EM_GETTHUMB (190)
+#define EM_GETWORDBREAKPROC (209)
+#define EM_GETWORDBREAKPROCEX (1104)
+#define EM_GETWORDWRAPMODE (1127)
+#define EM_HIDESELECTION (1087)
+#define EM_LIMITTEXT (197)
+#define EM_LINEFROMCHAR (201)
+#define EM_LINEINDEX (187)
+#define EM_LINELENGTH (193)
+#define EM_LINESCROLL (182)
+#define EM_PASTESPECIAL (1088)
+#define EM_POSFROMCHAR (214)
+#define EM_REPLACESEL (194)
+#define EM_REQUESTRESIZE (1089)
+#define EM_SCROLL (181)
+#define EM_SCROLLCARET (183)
+#define EM_SELECTIONTYPE (1090)
+#define EM_SETBKGNDCOLOR (1091)
+#define EM_SETCHARFORMAT (1092)
+#define EM_SETEVENTMASK (1093)
+#define EM_SETHANDLE (188)
+#define EM_SETIMECOLOR (1128)
+#define EM_SETIMEOPTIONS (1130)
+#define EM_SETLIMITTEXT (197)
+#define EM_SETMARGINS (211)
+#define EM_SETMODIFY (185)
+#define EM_SETOLECALLBACK (1094)
+#define EM_SETOPTIONS (1101)
+#define EM_SETPARAFORMAT (1095)
+#define EM_SETPASSWORDCHAR (204)
+#define EM_SETPUNCTUATION (1124)
+#define EM_SETREADONLY (207)
+#define EM_SETRECT (179)
+#define EM_SETRECTNP (180)
+#define EM_SETSEL (177)
+#define EM_SETTABSTOPS (203)
+#define EM_SETTARGETDEVICE (1096)
+#define EM_SETWORDBREAKPROC (208)
+#define EM_SETWORDBREAKPROCEX (1105)
+#define EM_SETWORDWRAPMODE (1126)
+#define EM_STREAMIN (1097)
+#define EM_STREAMOUT (1098)
+#define EM_UNDO (199)
+
+#define WM_NULL (0)
+#define WM_CLEAR (771)
+#define WM_COMMAND (273)
+#define WM_COPY (769)
+#define WM_CUT (768)
+#define WM_GETTEXT (13)
+#define WM_GETTEXTLENGTH (14)
+#define WM_NOTIFY (78)
+#define WM_PASTE (770)
+#define WM_SETTEXT (12)
+#define WM_UNDO (772)
+
+#define EN_CHANGE (768)
+#define EN_KILLFOCUS (512)
+#define EN_SETFOCUS (256)
+
+#define EC_LEFTMARGIN 1
+#define EC_RIGHTMARGIN 2
+#define EC_USEFONTINFO 0xffff
+
+#if PLAT_GTK
+#define VK_DOWN GDK_Down
+#define VK_UP GDK_Up
+#define VK_LEFT GDK_Left
+#define VK_RIGHT GDK_Right
+#define VK_HOME GDK_Home
+#define VK_END GDK_End
+#define VK_PRIOR GDK_Page_Up
+#define VK_NEXT GDK_Page_Down
+#define VK_DELETE GDK_Delete
+#define VK_INSERT GDK_Insert
+#define VK_ESCAPE GDK_Escape
+#define VK_BACK GDK_BackSpace
+#define VK_TAB GDK_Tab
+#define VK_RETURN GDK_Return
+#define VK_ADD GDK_KP_Add
+#define VK_SUBTRACT GDK_KP_Subtract
+#endif
+
+#if PLAT_WX
+#define VK_DOWN WXK_DOWN
+#define VK_UP WXK_UP
+#define VK_LEFT WXK_LEFT
+#define VK_RIGHT WXK_RIGHT
+#define VK_HOME WXK_HOME
+#define VK_END WXK_END
+#define VK_PRIOR WXK_PRIOR
+#define VK_NEXT WXK_NEXT
+#define VK_DELETE WXK_DELETE
+#define VK_INSERT WXK_INSERT
+#define VK_ESCAPE WXK_ESCAPE
+#define VK_BACK WXK_BACK
+#define VK_TAB WXK_TAB
+#define VK_RETURN WXK_RETURN
+#define VK_ADD WXK_ADD
+#define VK_SUBTRACT WXK_SUBTRACT
+
+// Are these needed any more
+#define LPSTR char *
+#define LONG long
+#define LPDWORD (long *)
+#endif
+
+/* SELCHANGE structure */
+#define SEL_EMPTY (0)
+#define SEL_TEXT (1)
+#define SEL_OBJECT (2)
+#define SEL_MULTICHAR (4)
+#define SEL_MULTIOBJECT (8)
+
+/* FINDREPLACE structure */
+#define FR_MATCHCASE (0x4)
+#define FR_WHOLEWORD (0x2)
+#define FR_DOWN (0x1)
+
+#define SHIFT_PRESSED 1
+#define LEFT_CTRL_PRESSED 2
+#define LEFT_ALT_PRESSED 4
+
+struct RECT {
+ LONG left;
+ LONG top;
+ LONG right;
+ LONG bottom;
+};
+
+struct CHARRANGE {
+ LONG cpMin;
+ LONG cpMax;
+};
+
+struct TEXTRANGE {
+ CHARRANGE chrg;
+ LPSTR lpstrText;
+};
+
+struct FINDTEXTEX {
+ CHARRANGE chrg;
+ LPSTR lpstrText;
+ CHARRANGE chrgText;
+};
+
+struct NMHDR {
+ WindowID hwndFrom;
+ UINT idFrom;
+ UINT code;
+};
+
+struct FORMATRANGE {
+ SurfaceID hdc;
+ SurfaceID hdcTarget;
+ RECT rc;
+ RECT rcPage;
+ CHARRANGE chrg;
+};
+
+#define MAKELONG(a, b) ((a) | ((b) << 16))
+#define LOWORD(x) (x & 0xffff)
+#define HIWORD(x) (x >> 16)
+
+#endif
diff --git a/src/Accessor.cxx b/src/Accessor.cxx
new file mode 100644
index 000000000..57b7e4dc4
--- /dev/null
+++ b/src/Accessor.cxx
@@ -0,0 +1,112 @@
+// SciTE - Scintilla based Text Editor
+// Accessor.cxx - rapid easy access to contents of a Scintilla
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "Scintilla.h"
+
+void Accessor::Fill(int position) {
+ if (lenDoc == -1)
+ lenDoc = Platform::SendScintilla(id, WM_GETTEXTLENGTH, 0, 0);
+ startPos = position - slopSize;
+ if (startPos + bufferSize > lenDoc)
+ startPos = lenDoc - bufferSize;
+ if (startPos < 0)
+ startPos = 0;
+ endPos = startPos + bufferSize;
+ if (endPos > lenDoc)
+ endPos = lenDoc;
+
+ TEXTRANGE tr = {{startPos, endPos}, buf};
+ Platform::SendScintilla(id, EM_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&tr));
+}
+
+char Accessor::StyleAt(int position) {
+ return static_cast<char>(Platform::SendScintilla(
+ id, SCI_GETSTYLEAT, position, 0));
+}
+
+int Accessor::GetLine(int position) {
+ return Platform::SendScintilla(id, EM_LINEFROMCHAR, position, 0);
+}
+
+int Accessor::LineStart(int line) {
+ return Platform::SendScintilla(id, EM_LINEINDEX, line, 0);
+}
+
+int Accessor::LevelAt(int line) {
+ return Platform::SendScintilla(id, SCI_GETFOLDLEVEL, line, 0);
+}
+
+int Accessor::Length() {
+ if (lenDoc == -1)
+ lenDoc = Platform::SendScintilla(id, WM_GETTEXTLENGTH, 0, 0);
+ return lenDoc;
+}
+
+int Accessor::GetLineState(int line) {
+ return Platform::SendScintilla(id, SCI_GETLINESTATE, line);
+}
+
+int Accessor::SetLineState(int line, int state) {
+ return Platform::SendScintilla(id, SCI_SETLINESTATE, line, state);
+}
+
+void StylingContext::StartAt(unsigned int start, char chMask) {
+ Platform::SendScintilla(id, SCI_STARTSTYLING, start, chMask);
+}
+
+void StylingContext::ColourSegment(unsigned int start, unsigned int end, int chAttr) {
+ // Only perform styling if non empty range
+ if (end != start - 1) {
+ if (end < start) {
+ Platform::DebugPrintf("Bad colour positions %d - %d\n", start, end);
+ }
+
+ if (validLen + (end - start + 1) >= bufferSize)
+ Flush();
+ if (validLen + (end - start + 1) >= bufferSize) {
+ // Too big for buffer so send directly
+ Platform::SendScintilla(id, SCI_SETSTYLING, end - start + 1, chAttr);
+ } else {
+ if (chAttr != chWhile)
+ chFlags = 0;
+ chAttr |= chFlags;
+ for (unsigned int i = start; i <= end; i++) {
+ styleBuf[validLen++] = chAttr;
+ }
+ }
+ }
+}
+
+void StylingContext::StartSegment(unsigned int pos) {
+ startSeg = pos;
+}
+
+void StylingContext::ColourTo(unsigned int pos, int chAttr) {
+ ColourSegment(startSeg, pos, chAttr);
+ startSeg = pos+1;
+}
+
+int StylingContext::GetLine(int position) {
+ return Platform::SendScintilla(id, EM_LINEFROMCHAR, position, 0);
+}
+
+void StylingContext::SetLevel(int line, int level) {
+ Platform::SendScintilla(id, SCI_SETFOLDLEVEL, line, level);
+}
+
+void StylingContext::Flush() {
+ if (validLen > 0) {
+ Platform::SendScintilla(id, SCI_SETSTYLINGEX, validLen,
+ reinterpret_cast<LPARAM>(styleBuf));
+ validLen = 0;
+ }
+}
diff --git a/src/AutoComplete.cxx b/src/AutoComplete.cxx
new file mode 100644
index 000000000..c3ec29c3c
--- /dev/null
+++ b/src/AutoComplete.cxx
@@ -0,0 +1,104 @@
+// Scintilla source code edit control
+// AutoComplete.cxx - defines the auto completion list box
+// Copyright 1998-2000 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 "Platform.h"
+
+#include "AutoComplete.h"
+
+AutoComplete::AutoComplete() {
+ lb = 0;
+ active = false;
+ posStart = 0;
+ strcpy(stopChars, "");
+}
+
+AutoComplete::~AutoComplete() {
+ lb.Destroy();
+}
+
+bool AutoComplete::Active() {
+ return active;
+}
+
+void AutoComplete::Start(Window &parent, int ctrlID, int position, int startLen_) {
+ if (!lb.Created()) {
+ lb.Create(parent, ctrlID);
+ }
+ lb.Clear();
+ active = true;
+ startLen = startLen_;
+ posStart = position;
+}
+
+void AutoComplete::SetStopChars(const char *stopChars_) {
+ strncpy(stopChars, stopChars_, sizeof(stopChars));
+ stopChars[sizeof(stopChars) - 1] = '\0';
+}
+
+bool AutoComplete::IsStopChar(char ch) {
+ return ch && strchr(stopChars, ch);
+}
+
+int AutoComplete::SetList(const char *list) {
+ int maxStrLen = 12;
+ lb.Clear();
+ char *words = new char[strlen(list) + 1];
+ if (words) {
+ strcpy(words, list);
+ char *startword = words;
+ int i = 0;
+ for (; words && words[i]; i++) {
+ if (words[i] == ' ') {
+ words[i] = '\0';
+ lb.Append(startword);
+ maxStrLen = Platform::Maximum(maxStrLen, strlen(startword));
+ startword = words + i + 1;
+ }
+ }
+ if (startword) {
+ lb.Append(startword);
+ maxStrLen = Platform::Maximum(maxStrLen, strlen(startword));
+ }
+ delete []words;
+ }
+ lb.Sort();
+ return maxStrLen;
+}
+
+void AutoComplete::Show() {
+ lb.Show();
+ lb.Select(0);
+}
+
+void AutoComplete::Cancel() {
+ if (lb.Created()) {
+ lb.Destroy();
+ lb = 0;
+ active = false;
+ }
+}
+
+
+void AutoComplete::Move(int delta) {
+ int count = lb.Length();
+ int current = lb.GetSelection();
+ current += delta;
+ if (current >= count)
+ current = count - 1;
+ if (current < 0)
+ current = 0;
+ lb.Select(current);
+}
+
+void AutoComplete::Select(const char *word) {
+ int pos = lb.Find(word);
+ //Platform::DebugPrintf("Autocompleting at <%s> %d\n", wordCurrent, pos);
+ if (pos != -1)
+ lb.Select(pos);
+}
+
diff --git a/src/AutoComplete.h b/src/AutoComplete.h
new file mode 100644
index 000000000..10216027b
--- /dev/null
+++ b/src/AutoComplete.h
@@ -0,0 +1,43 @@
+// Scintilla source code edit control
+// AutoComplete.h - defines the auto completion list box
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef AUTOCOMPLETE_H
+#define AUTOCOMPLETE_H
+
+class AutoComplete {
+ bool active;
+ char stopChars[256];
+public:
+ ListBox lb;
+ int posStart;
+ int startLen;
+
+ AutoComplete();
+ ~AutoComplete();
+
+ // Is the auto completion list displayed?
+ bool Active();
+
+ // Display the auto completion list positioned to be near a character position
+ void Start(Window &parent, int ctrlID, int position, int startLen_);
+
+ // The stop chars are characters which, when typed, cause the auto completion list to disappear
+ void SetStopChars(const char *stopChars_);
+ bool IsStopChar(char ch);
+
+ // The list string contains a sequence of words separated by spaces
+ int SetList(const char *list);
+
+ void Show();
+ void Cancel();
+
+ // Move the current list element by delta, scrolling appropriately
+ void Move(int delta);
+
+ // Select a list element that starts with word as the current element
+ void Select(const char *word);
+};
+
+#endif
diff --git a/src/CallTip.cxx b/src/CallTip.cxx
new file mode 100644
index 000000000..ad6740208
--- /dev/null
+++ b/src/CallTip.cxx
@@ -0,0 +1,168 @@
+// Scintilla source code edit control
+// CallTip.cxx - code for displaying call tips
+// Copyright 1998-2000 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 "Platform.h"
+
+#include "CallTip.h"
+
+CallTip::CallTip() {
+ wCallTip = 0;
+ inCallTipMode = false;
+ posStartCallTip = 0;
+ val = 0;
+ startHighlight = 0;
+ endHighlight = 0;
+
+ colourBG.desired = Colour(0xff, 0xff, 0xff);
+ colourUnSel.desired = Colour(0x80, 0x80, 0x80);
+ colourSel.desired = Colour(0, 0, 0x80);
+ colourShade.desired = Colour(0, 0, 0);
+ colourLight.desired = Colour(0xc0, 0xc0, 0xc0);
+}
+
+CallTip::~CallTip() {
+ wCallTip.Destroy();
+ delete []val;
+ val = 0;
+}
+
+void CallTip::RefreshColourPalette(Palette &pal, bool want) {
+ pal.WantFind(colourBG, want);
+ pal.WantFind(colourUnSel, want);
+ pal.WantFind(colourSel, want);
+ pal.WantFind(colourShade, want);
+ pal.WantFind(colourLight, want);
+}
+
+void CallTip::PaintCT(Surface *surfaceWindow) {
+ if (!val)
+ return;
+ PRectangle rcClientPos = wCallTip.GetClientPosition();
+ PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
+ rcClientPos.bottom - rcClientPos.top);
+ PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
+
+ surfaceWindow->FillRectangle(rcClient, colourBG.allocated);
+ // To make a nice small call tip window, it is only sized to fit most normal characters without accents
+ int lineHeight = surfaceWindow->Height(font);
+ int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font);
+
+ // For each line...
+ // Draw the definition in three parts: before highlight, highlighted, after highlight
+ int ytext = rcClient.top + ascent + 1;
+ char *chunkVal = val;
+ bool moreChunks = true;
+ while (moreChunks) {
+ char *chunkEnd = strchr(chunkVal, '\n');
+ if (chunkEnd == NULL) {
+ chunkEnd = chunkVal + strlen(chunkVal);
+ moreChunks = false;
+ }
+ int chunkOffset = chunkVal - val;
+ int chunkLength = chunkEnd - chunkVal;
+ int chunkEndOffset = chunkOffset + chunkLength;
+ int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset);
+ thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset);
+ thisStartHighlight -= chunkOffset;
+ int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset);
+ thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset);
+ thisEndHighlight -= chunkOffset;
+ int x = 5;
+ int xEnd = x + surfaceWindow->WidthText(font, chunkVal, thisStartHighlight);
+ rcClient.left = x;
+ rcClient.top = ytext - ascent - 1;
+ rcClient.right = xEnd;
+ surfaceWindow->DrawText(rcClient, font, ytext,
+ chunkVal, thisStartHighlight,
+ colourUnSel.allocated, colourBG.allocated);
+ x = xEnd;
+
+ xEnd = x + surfaceWindow->WidthText(font, chunkVal + thisStartHighlight,
+ thisEndHighlight - thisStartHighlight);
+ rcClient.top = ytext;
+ rcClient.left = x;
+ rcClient.right = xEnd;
+ surfaceWindow->DrawText(rcClient, font, ytext,
+ chunkVal + thisStartHighlight, thisEndHighlight - thisStartHighlight,
+ colourSel.allocated, colourBG.allocated);
+ x = xEnd;
+
+ xEnd = x + surfaceWindow->WidthText(font, chunkVal + thisEndHighlight,
+ chunkLength - thisEndHighlight);
+ rcClient.left = x;
+ rcClient.right = xEnd;
+ surfaceWindow->DrawText(rcClient, font, ytext,
+ chunkVal + thisEndHighlight, chunkLength - thisEndHighlight,
+ colourUnSel.allocated, colourBG.allocated);
+ chunkVal = chunkEnd + 1;
+ ytext += lineHeight;
+ }
+ // Draw a raised border around the edges of the window
+ surfaceWindow->MoveTo(0, rcClientSize.bottom - 1);
+ surfaceWindow->PenColour(colourShade.allocated);
+ surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1);
+ surfaceWindow->LineTo(rcClientSize.right - 1, 0);
+ surfaceWindow->PenColour(colourLight.allocated);
+ surfaceWindow->LineTo(0, 0);
+ surfaceWindow->LineTo(0, rcClientSize.bottom - 1);
+}
+
+PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn,
+ const char *faceName, int size) {
+ Surface surfaceMeasure;
+ surfaceMeasure.Init();
+ int deviceHeight = (size * surfaceMeasure.LogPixelsY()) / 72;
+ font.Create(faceName, deviceHeight);
+ if (val)
+ delete []val;
+ val = new char[strlen(defn) + 1];
+ if (!val)
+ return PRectangle();
+ strcpy(val, defn);
+ startHighlight = 0;
+ endHighlight = 0;
+ inCallTipMode = true;
+ posStartCallTip = pos;
+ // Look for multiple lines in the text
+ // Only support \n here - simply means container must avoid \r!
+ int width = 0;
+ int numLines = 1;
+ const char *newline;
+ const char *look = val;
+ while ((newline = strchr(look, '\n')) != NULL) {
+ int thisWidth = surfaceMeasure.WidthText(font, look, newline - look);
+ width = Platform::Maximum(width, thisWidth);
+ look = newline + 1;
+ numLines++;
+ }
+ int lastWidth = surfaceMeasure.WidthText(font, look, strlen(look));
+ width = Platform::Maximum(width, lastWidth) + 10;
+ int lineHeight = surfaceMeasure.Height(font);
+ // Extra line for border and an empty line at top and bottom
+ int height = lineHeight * numLines - surfaceMeasure.InternalLeading(font) + 2 + 2;
+ return PRectangle(pt.x -5, pt.y + lineHeight + 1, pt.x + width - 5, pt.y + lineHeight + 1 + height);
+}
+
+
+void CallTip::CallTipCancel() {
+ inCallTipMode = false;
+ if (wCallTip.Created()) {
+ wCallTip.Destroy();
+ }
+}
+
+void CallTip::SetHighlight(int start, int end) {
+ // Avoid flashing by checking something has really changed
+ if ((start != startHighlight) || (end != endHighlight)) {
+ startHighlight = start;
+ endHighlight = end;
+ if (wCallTip.Created()) {
+ wCallTip.InvalidateAll();
+ }
+ }
+}
diff --git a/src/CallTip.h b/src/CallTip.h
new file mode 100644
index 000000000..cd5b093c8
--- /dev/null
+++ b/src/CallTip.h
@@ -0,0 +1,46 @@
+// Scintilla source code edit control
+// CallTip.h - interface to the call tip control
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef CALLTIP_H
+#define CALLTIP_H
+
+const char callClassName[] = "CallTip";
+
+class CallTip {
+ int startHighlight;
+ int endHighlight;
+ char *val;
+ Font font;
+public:
+ Window wCallTip;
+ Window wDraw;
+ bool inCallTipMode;
+ int posStartCallTip;
+ ColourPair colourBG;
+ ColourPair colourUnSel;
+ ColourPair colourSel;
+ ColourPair colourShade;
+ ColourPair colourLight;
+
+ CallTip();
+ ~CallTip();
+
+ // Claim or accept palette entries for the colours required to paint a calltip
+ void RefreshColourPalette(Palette &pal, bool want);
+
+ void PaintCT(Surface *surfaceWindow);
+
+ // Setup the calltip and return a rectangle of the area required
+ PRectangle CallTipStart(int pos, Point pt, const char *defn,
+ const char *faceName, int size);
+
+ void CallTipCancel();
+
+ // Set a range of characters to be displayed in a highlight style.
+ // Commonly used to highlight the current parameter.
+ void SetHighlight(int start, int end);
+};
+
+#endif
diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx
new file mode 100644
index 000000000..777688511
--- /dev/null
+++ b/src/CellBuffer.cxx
@@ -0,0 +1,950 @@
+// Scintilla source code edit control
+// CellBuffer.cxx - manages a buffer of cells
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "SVector.h"
+#include "CellBuffer.h"
+
+MarkerHandleSet::MarkerHandleSet() {
+ root = 0;
+}
+
+MarkerHandleSet::~MarkerHandleSet() {
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ MarkerHandleNumber *mhnToFree = mhn;
+ mhn = mhn->next;
+ delete mhnToFree;
+ }
+ root = 0;
+}
+
+int MarkerHandleSet::Length() {
+ int c = 0;
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ c++;
+ mhn = mhn->next;
+ }
+ return c;
+}
+
+int MarkerHandleSet::NumberFromHandle(int handle) {
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ if (mhn->handle == handle) {
+ return mhn->number;
+ }
+ mhn = mhn->next;
+ }
+ return - 1;
+}
+
+int MarkerHandleSet::MarkValue() {
+ unsigned int m = 0;
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ m |= (1 << mhn->number);
+ mhn = mhn->next;
+ }
+ return m;
+}
+
+bool MarkerHandleSet::Contains(int handle) {
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ if (mhn->handle == handle) {
+ return true;
+ }
+ mhn = mhn->next;
+ }
+ return false;
+}
+
+bool MarkerHandleSet::InsertHandle(int handle, int markerNum) {
+ MarkerHandleNumber *mhn = new MarkerHandleNumber;
+ if (!mhn)
+ return false;
+ mhn->handle = handle;
+ mhn->number = markerNum;
+ mhn->next = root;
+ root = mhn;
+ return true;
+}
+
+void MarkerHandleSet::RemoveHandle(int handle) {
+ MarkerHandleNumber **pmhn = &root;
+ while (*pmhn) {
+ MarkerHandleNumber *mhn = *pmhn;
+ if (mhn->handle == handle) {
+ *pmhn = mhn->next;
+ delete mhn;
+ return;
+ }
+ pmhn = &((*pmhn)->next);
+ }
+}
+
+void MarkerHandleSet::RemoveNumber(int markerNum) {
+ MarkerHandleNumber **pmhn = &root;
+ while (*pmhn) {
+ MarkerHandleNumber *mhn = *pmhn;
+ if (mhn->number == markerNum) {
+ *pmhn = mhn->next;
+ delete mhn;
+ return;
+ }
+ pmhn = &((*pmhn)->next);
+ }
+}
+
+void MarkerHandleSet::CombineWith(MarkerHandleSet *other) {
+ MarkerHandleNumber **pmhn = &root;
+ while (*pmhn) {
+ pmhn = &((*pmhn)->next);
+ }
+ *pmhn = other->root;
+ other->root = 0;
+}
+
+LineVector::LineVector() {
+ linesData = 0;
+ lines = 0;
+ levels = 0;
+ Init();
+}
+
+LineVector::~LineVector() {
+ for (int line = 0; line < lines; line++) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ }
+ delete []linesData;
+ linesData = 0;
+ delete []levels;
+ levels = 0;
+}
+
+void LineVector::Init() {
+ for (int line = 0; line < lines; line++) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ }
+ delete []linesData;
+ linesData = new LineData[static_cast<int>(growSize)];
+ size = growSize;
+ lines = 1;
+ delete []levels;
+ levels = 0;
+ sizeLevels = 0;
+}
+
+void LineVector::Expand(int sizeNew) {
+ LineData *linesDataNew = new LineData[sizeNew];
+ if (linesDataNew) {
+ for (int i = 0; i < size; i++)
+ linesDataNew[i] = linesData[i];
+ // Do not delete handleSets here as they are transferred to new linesData
+ delete []linesData;
+ linesData = linesDataNew;
+ size = sizeNew;
+ } else {
+ Platform::DebugPrintf("No memory available\n");
+ // TODO: Blow up
+ }
+}
+
+void LineVector::ExpandLevels(int sizeNew) {
+ if (sizeNew == -1)
+ sizeNew = size;
+ int *levelsNew = new int[sizeNew];
+ if (levelsNew) {
+ int i = 0;
+ for (; i < sizeLevels; i++)
+ levelsNew[i] = levels[i];
+ for (; i < sizeNew; i++)
+ levelsNew[i] = SC_FOLDLEVELBASE;
+ delete []levels;
+ levels = levelsNew;
+ sizeLevels = sizeNew;
+ } else {
+ Platform::DebugPrintf("No memory available\n");
+ // TODO: Blow up
+ }
+}
+
+void LineVector::InsertValue(int pos, int value) {
+ //Platform::DebugPrintf("InsertValue[%d] = %d\n", pos, value);
+ if ((lines + 2) >= size) {
+ Expand(size + growSize);
+ if (levels) {
+ ExpandLevels(size + growSize);
+ }
+ }
+ lines++;
+ for (int i = lines + 1; i > pos; i--) {
+ linesData[i] = linesData[i - 1];
+ }
+ linesData[pos].startPosition = value;
+ linesData[pos].handleSet = 0;
+}
+
+void LineVector::SetValue(int pos, int value) {
+ //Platform::DebugPrintf("SetValue[%d] = %d\n", pos, value);
+ if ((pos + 2) >= size) {
+ //Platform::DebugPrintf("Resize %d %d\n", size,pos);
+ Expand(pos + growSize);
+ //Platform::DebugPrintf("end Resize %d %d\n", size,pos);
+ lines = pos;
+ if (levels) {
+ ExpandLevels(pos + growSize);
+ }
+ }
+ linesData[pos].startPosition = value;
+}
+
+void LineVector::Remove(int pos) {
+ //Platform::DebugPrintf("Remove %d\n", pos);
+ // Retain the markers from the deleted line by oring them into the previous line
+ if (pos > 0) {
+ MergeMarkers(pos - 1);
+ }
+ for (int i = pos; i < lines; i++) {
+ linesData[i] = linesData[i + 1];
+ }
+ lines--;
+}
+
+int LineVector::LineFromPosition(int pos) {
+ //Platform::DebugPrintf("LineFromPostion %d lines=%d end = %d\n", pos, lines, linesData[lines].startPosition);
+ if (lines == 0)
+ return 0;
+ //Platform::DebugPrintf("LineFromPosition %d\n", pos);
+ if (pos >= linesData[lines].startPosition)
+ return lines - 1;
+ int lower = 0;
+ int upper = lines;
+ int middle = 0;
+ do {
+ middle = (upper + lower + 1) / 2; // Round high
+ if (pos < linesData[middle].startPosition) {
+ upper = middle - 1;
+ } else {
+ lower = middle;
+ }
+ } while (lower < upper);
+ //Platform::DebugPrintf("LineFromPostion %d %d %d\n", pos, lower, linesData[lower].startPosition, linesData[lower > 1 ? lower - 1 : 0].startPosition);
+ return lower;
+}
+
+int LineVector::AddMark(int line, int markerNum) {
+ handleCurrent++;
+ if (!linesData[line].handleSet) {
+ // Need new structure to hold marker handle
+ linesData[line].handleSet = new MarkerHandleSet;
+ if (!linesData[line].handleSet)
+ return - 1;
+ }
+ linesData[line].handleSet->InsertHandle(handleCurrent, markerNum);
+
+ return handleCurrent;
+}
+
+void LineVector::MergeMarkers(int pos) {
+ if (linesData[pos].handleSet || linesData[pos + 1].handleSet) {
+ if (linesData[pos].handleSet && linesData[pos + 1].handleSet) {
+ linesData[pos].handleSet->CombineWith(linesData[pos].handleSet);
+ linesData[pos].handleSet = 0;
+ }
+ }
+}
+
+void LineVector::DeleteMark(int line, int markerNum) {
+ if (linesData[line].handleSet) {
+ if (markerNum == -1) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ } else {
+ linesData[line].handleSet->RemoveNumber(markerNum);
+ if (linesData[line].handleSet->Length() == 0) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ }
+ }
+ }
+}
+
+void LineVector::DeleteMarkFromHandle(int markerHandle) {
+ int line = LineFromHandle(markerHandle);
+ if (line >= 0) {
+ linesData[line].handleSet->RemoveHandle(markerHandle);
+ if (linesData[line].handleSet->Length() == 0) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ }
+ }
+}
+
+int LineVector::LineFromHandle(int markerHandle) {
+ for (int line = 0; line < lines; line++) {
+ if (linesData[line].handleSet) {
+ if (linesData[line].handleSet->Contains(markerHandle)) {
+ return line;
+ }
+ }
+ }
+ return - 1;
+}
+
+Action::Action() {
+ at = startAction;
+ position = 0;
+ data = 0;
+ lenData = 0;
+}
+
+Action::~Action() {
+ Destroy();
+}
+
+void Action::Create(actionType at_, int position_, char *data_, int lenData_) {
+ delete []data;
+ position = position_;
+ at = at_;
+ data = data_;
+ lenData = lenData_;
+}
+
+void Action::Destroy() {
+ delete []data;
+ data = 0;
+}
+
+void Action::Grab(Action *source) {
+ delete []data;
+
+ position = source->position;
+ at = source->at;
+ data = source->data;
+ lenData = source->lenData;
+
+ // Ownership of source data transferred to this
+ source->position = 0;
+ source->at = startAction;
+ source->data = 0;
+ source->lenData = 0;
+}
+
+CellBuffer::CellBuffer(int initialLength) {
+ body = new char[initialLength];
+ size = initialLength;
+ length = 0;
+ part1len = 0;
+ gaplen = initialLength;
+ part2body = body + gaplen;
+ readOnly = false;
+
+ lenActions = 100;
+ actions = new Action[lenActions];
+ maxAction = 0;
+ currentAction = 0;
+ collectingUndo = undoCollectAutoStart;
+ undoSequenceDepth = 0;
+ savePoint = 0;
+
+ actions[currentAction].Create(startAction);
+}
+
+CellBuffer::~CellBuffer() {
+ delete []body;
+ body = 0;
+ delete []actions;
+ actions = 0;
+}
+
+void CellBuffer::GapTo(int position) {
+ if (position == part1len)
+ return;
+ if (position < part1len) {
+ int diff = part1len - position;
+ //Platform::DebugPrintf("Move gap backwards to %d diff = %d part1len=%d length=%d \n", position,diff, part1len, length);
+ for (int i = 0; i < diff; i++)
+ body[part1len + gaplen - i - 1] = body[part1len - i - 1];
+ } else { // position > part1len
+ int diff = position - part1len;
+ //Platform::DebugPrintf("Move gap forwards to %d diff =%d\n", position,diff);
+ for (int i = 0; i < diff; i++)
+ body[part1len + i] = body[part1len + gaplen + i];
+ }
+ part1len = position;
+ part2body = body + gaplen;
+}
+
+void CellBuffer::RoomFor(int insertionLength) {
+ //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength);
+ if (gaplen <= insertionLength) {
+ //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength);
+ GapTo(length);
+ int newSize = size + insertionLength + 4000;
+ //Platform::DebugPrintf("moved gap %d\n", newSize);
+ char *newBody = new char[newSize];
+ memcpy(newBody, body, size);
+ delete []body;
+ body = newBody;
+ gaplen += newSize - size;
+ part2body = body + gaplen;
+ size = newSize;
+ //Platform::DebugPrintf("end need room %d %d - size=%d length=%d\n", gaplen, insertionLength,size,length);
+ }
+}
+
+// To make it easier to write code that uses ByteAt, a position outside the range of the buffer
+// can be retrieved. All characters outside the range have the value '\0'.
+char CellBuffer::ByteAt(int position) {
+ if (position < part1len) {
+ if (position < 0) {
+ return '\0';
+ } else {
+ return body[position];
+ }
+ } else {
+ if (position >= length) {
+ return '\0';
+ } else {
+ return part2body[position];
+ }
+ }
+}
+
+void CellBuffer::SetByteAt(int position, char ch) {
+
+ if (position < 0) {
+ //Platform::DebugPrintf("Bad position %d\n",position);
+ return;
+ }
+ if (position >= length + 11) {
+ Platform::DebugPrintf("Very Bad position %d of %d\n", position, length);
+ //exit(2);
+ return;
+ }
+ if (position >= length) {
+ //Platform::DebugPrintf("Bad position %d of %d\n",position,length);
+ return;
+ }
+
+ if (position < part1len) {
+ body[position] = ch;
+ } else {
+ part2body[position] = ch;
+ }
+}
+
+char CellBuffer::CharAt(int position) {
+ return ByteAt(position*2);
+}
+
+void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) {
+ if (lengthRetrieve < 0)
+ return;
+ if (position < 0)
+ return;
+ int bytePos = position * 2;
+ if ((bytePos + lengthRetrieve * 2) > length) {
+ Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n",bytePos,
+ lengthRetrieve, length);
+ return;
+ }
+ GapTo(0); // Move the buffer so its easy to subscript into it
+ char *pb = part2body + bytePos;
+ while (lengthRetrieve--) {
+ *buffer++ = *pb;
+ pb +=2;
+ }
+}
+
+char CellBuffer::StyleAt(int position) {
+ return ByteAt(position*2 + 1);
+}
+
+const char *CellBuffer::InsertString(int position, char *s, int insertLength) {
+ char *data = 0;
+ // InsertString and DeleteChars are the bottleneck though which all changes occur
+ if (!readOnly) {
+ if (collectingUndo) {
+ // Save into the undo/redo stack, but only the characters - not the formatting
+ // This takes up about half load time
+ data = new char[insertLength / 2];
+ for (int i = 0; i < insertLength / 2; i++) {
+ data[i] = s[i * 2];
+ }
+ AppendAction(insertAction, position, data, insertLength / 2);
+ }
+
+ BasicInsertString(position, s, insertLength);
+ }
+ return data;
+}
+
+void CellBuffer::InsertCharStyle(int position, char ch, char style) {
+ char s[2];
+ s[0] = ch;
+ s[1] = style;
+ InsertString(position*2, s, 2);
+}
+
+bool CellBuffer::SetStyleAt(int position, char style, char mask) {
+ char curVal = ByteAt(position*2 + 1);
+ if ((curVal & mask) != style) {
+ SetByteAt(position*2 + 1, (curVal & ~mask) | style);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool CellBuffer::SetStyleFor(int position, int lengthStyle, char style, char mask) {
+ int bytePos = position * 2 + 1;
+ bool changed = false;
+ while (lengthStyle--) {
+ char curVal = ByteAt(bytePos);
+ if ((curVal & mask) != style) {
+ SetByteAt(bytePos, (curVal & ~mask) | style);
+ changed = true;
+ }
+ bytePos += 2;
+ }
+ return changed;
+}
+
+void CellBuffer::EnsureUndoRoom() {
+ //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, length, currentAction);
+ if (currentAction >= 2) {
+ // Have to test that there is room for 2 more actions in the array
+ // as two actions may be created by this function
+ if (currentAction >= (lenActions - 2)) {
+ // Run out of undo nodes so extend the array
+ int lenActionsNew = lenActions * 2;
+ Action *actionsNew = new Action[lenActionsNew];
+ if (!actionsNew)
+ return;
+ for (int act = 0; act <= currentAction; act++)
+ actionsNew[act].Grab(&actions[act]);
+ delete []actions;
+ lenActions = lenActionsNew;
+ actions = actionsNew;
+ }
+ }
+}
+
+void CellBuffer::AppendAction(actionType at, int position, char *data, int lengthData) {
+ EnsureUndoRoom();
+ //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
+ if (currentAction >= 2) {
+ // See if current action can be coalesced into previous action
+ // Will work if both are inserts or deletes and position is same or two different
+ if ((at != actions[currentAction - 1].at) || (abs(position - actions[currentAction - 1].position) > 2)) {
+ currentAction++;
+ } else if (currentAction == savePoint) {
+ currentAction++;
+ }
+ } else {
+ currentAction++;
+ }
+ actions[currentAction].Create(at, position, data, lengthData);
+ if ((collectingUndo == undoCollectAutoStart) && (0 == undoSequenceDepth)) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ }
+ maxAction = currentAction;
+}
+
+const char *CellBuffer::DeleteChars(int position, int deleteLength) {
+ // InsertString and DeleteChars are the bottleneck though which all changes occur
+ char *data = 0;
+ if (!readOnly) {
+ if (collectingUndo) {
+ // Save into the undo/redo stack, but only the characters - not the formatting
+ data = new char[deleteLength / 2];
+ for (int i = 0; i < deleteLength / 2; i++) {
+ data[i] = ByteAt(position + i * 2);
+ }
+ AppendAction(removeAction, position, data, deleteLength / 2);
+ }
+
+ BasicDeleteChars(position, deleteLength);
+ }
+ return data;
+}
+
+int CellBuffer::ByteLength() {
+ return length;
+}
+
+int CellBuffer::Length() {
+ return ByteLength() / 2;
+}
+
+int CellBuffer::Lines() {
+ //Platform::DebugPrintf("Lines = %d\n", lv.lines);
+ return lv.lines;
+}
+
+int CellBuffer::LineStart(int line) {
+ if (line < 0)
+ return 0;
+ else if (line > lv.lines)
+ return length;
+ else
+ return lv.linesData[line].startPosition;
+}
+
+bool CellBuffer::IsReadOnly() {
+ return readOnly;
+}
+
+void CellBuffer::SetReadOnly(bool set) {
+ readOnly = set;
+}
+
+void CellBuffer::SetSavePoint() {
+ savePoint = currentAction;
+}
+
+bool CellBuffer::IsSavePoint() {
+ return savePoint == currentAction;
+}
+
+int CellBuffer::AddMark(int line, int markerNum) {
+ if ((line >= 0) && (line < lv.lines)) {
+ return lv.AddMark(line, markerNum);
+ }
+ return - 1;
+}
+
+void CellBuffer::DeleteMark(int line, int markerNum) {
+ if ((line >= 0) && (line < lv.lines)) {
+ lv.DeleteMark(line, markerNum);
+ }
+}
+
+void CellBuffer::DeleteMarkFromHandle(int markerHandle) {
+ lv.DeleteMarkFromHandle(markerHandle);
+}
+
+int CellBuffer::GetMark(int line) {
+ if ((line >= 0) && (line < lv.lines) && (lv.linesData[line].handleSet))
+ return lv.linesData[line].handleSet->MarkValue();
+ return 0;
+}
+
+void CellBuffer::DeleteAllMarks(int markerNum) {
+ for (int line = 0; line < lv.lines; line++) {
+ lv.DeleteMark(line, markerNum);
+ }
+}
+
+int CellBuffer::LineFromHandle(int markerHandle) {
+ return lv.LineFromHandle(markerHandle);
+}
+
+// Without undo
+
+void CellBuffer::BasicInsertString(int position, char *s, int insertLength) {
+ //Platform::DebugPrintf("Inserting at %d for %d\n", position, insertLength);
+ if (insertLength == 0)
+ return;
+ RoomFor(insertLength);
+ GapTo(position);
+
+ memcpy(body + part1len, s, insertLength);
+ length += insertLength;
+ part1len += insertLength;
+ gaplen -= insertLength;
+ part2body = body + gaplen;
+
+ int lineInsert = lv.LineFromPosition(position / 2) + 1;
+ // Point all the lines after the insertion point further along in the buffer
+ for (int lineAfter = lineInsert; lineAfter <= lv.lines; lineAfter++) {
+ lv.linesData[lineAfter].startPosition += insertLength / 2;
+ }
+ char chPrev = ' ';
+ if ((position - 2) >= 0)
+ chPrev = ByteAt(position - 2);
+ char chAfter = ' ';
+ if ((position + insertLength) < length)
+ chAfter = ByteAt(position + insertLength);
+ if (chPrev == '\r' && chAfter == '\n') {
+ //Platform::DebugPrintf("Splitting a crlf pair at %d\n", lineInsert);
+ // Splitting up a crlf pair at position
+ lv.InsertValue(lineInsert, position / 2);
+ lineInsert++;
+ }
+ char ch = ' ';
+ for (int i = 0; i < insertLength; i += 2) {
+ ch = s[i];
+ if (ch == '\r') {
+ //Platform::DebugPrintf("Inserting cr at %d\n", lineInsert);
+ lv.InsertValue(lineInsert, (position + i) / 2 + 1);
+ lineInsert++;
+ } else if (ch == '\n') {
+ if (chPrev == '\r') {
+ //Platform::DebugPrintf("Patching cr before lf at %d\n", lineInsert-1);
+ // Patch up what was end of line
+ lv.SetValue(lineInsert - 1, (position + i) / 2 + 1);
+ } else {
+ //Platform::DebugPrintf("Inserting lf at %d\n", lineInsert);
+ lv.InsertValue(lineInsert, (position + i) / 2 + 1);
+ lineInsert++;
+ }
+ }
+ chPrev = ch;
+ }
+ // Joining two lines where last insertion is cr and following text starts with lf
+ if (chAfter == '\n') {
+ if (ch == '\r') {
+ //Platform::DebugPrintf("Joining cr before lf at %d\n", lineInsert-1);
+ // End of line already in buffer so drop the newly created one
+ lv.Remove(lineInsert - 1);
+ }
+ }
+}
+
+void CellBuffer::BasicDeleteChars(int position, int deleteLength) {
+ //Platform::DebugPrintf("Deleting at %d for %d\n", position, deleteLength);
+ if (deleteLength == 0)
+ return;
+
+ if ((position == 0) && (deleteLength == length)) {
+ // If whole buffer is being deleted, faster to reinitialise lines data
+ // than to delete each line.
+ //printf("Whole buffer being deleted\n");
+ lv.Init();
+ } else {
+ // Have to fix up line positions before doing deletion as looking at text in buffer
+ // to work out which lines have been removed
+
+ int lineRemove = lv.LineFromPosition(position / 2) + 1;
+ // Point all the lines after the insertion point further along in the buffer
+ for (int lineAfter = lineRemove; lineAfter <= lv.lines; lineAfter++) {
+ lv.linesData[lineAfter].startPosition -= deleteLength / 2;
+ }
+ char chPrev = ' ';
+ if (position >= 2)
+ chPrev = ByteAt(position - 2);
+ char chBefore = chPrev;
+ char chNext = ' ';
+ if (position < length)
+ chNext = ByteAt(position);
+ bool ignoreNL = false;
+ if (chPrev == '\r' && chNext == '\n') {
+ //Platform::DebugPrintf("Deleting lf after cr, move line end to cr at %d\n", lineRemove);
+ // Move back one
+ lv.SetValue(lineRemove, position / 2);
+ lineRemove++;
+ ignoreNL = true; // First \n is not real deletion
+ }
+
+ char ch = chNext;
+ for (int i = 0; i < deleteLength; i += 2) {
+ chNext = ' ';
+ if ((position + i + 2) < length)
+ chNext = ByteAt(position + i + 2);
+ //Platform::DebugPrintf("Deleting %d %x\n", i, ch);
+ if (ch == '\r') {
+ if (chNext != '\n') {
+ //Platform::DebugPrintf("Removing cr end of line\n");
+ lv.Remove(lineRemove);
+ }
+ } else if ((ch == '\n') && !ignoreNL) {
+ //Platform::DebugPrintf("Removing lf end of line\n");
+ lv.Remove(lineRemove);
+ ignoreNL = false; // Further \n are not real deletions
+ }
+
+ ch = chNext;
+ }
+ // May have to fix up end if last deletion causes cr to be next to lf
+ // or removes one of a crlf pair
+ char chAfter = ' ';
+ if ((position + deleteLength) < length)
+ chAfter = ByteAt(position + deleteLength);
+ if (chBefore == '\r' && chAfter == '\n') {
+ //d.printf("Joining cr before lf at %d\n", lineRemove);
+ // Using lineRemove-1 as cr ended line before start of deletion
+ lv.Remove(lineRemove - 1);
+ lv.SetValue(lineRemove - 1, position / 2 + 1);
+ }
+ }
+ GapTo(position);
+ length -= deleteLength;
+ gaplen += deleteLength;
+ part2body = body + gaplen;
+}
+
+undoCollectionType CellBuffer::SetUndoCollection(undoCollectionType collectUndo) {
+ collectingUndo = collectUndo;
+ undoSequenceDepth = 0;
+ return collectingUndo;
+}
+
+bool CellBuffer::IsCollectingUndo() {
+ return collectingUndo;
+}
+
+void CellBuffer::AppendUndoStartAction() {
+ EnsureUndoRoom();
+ // Finish any currently active undo sequence
+ undoSequenceDepth = 0;
+ if (actions[currentAction].at != startAction) {
+ undoSequenceDepth++;
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ maxAction = currentAction;
+ }
+}
+
+void CellBuffer::BeginUndoAction() {
+ EnsureUndoRoom();
+ if (undoSequenceDepth == 0) {
+ if (actions[currentAction].at != startAction) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ maxAction = currentAction;
+ }
+ }
+ undoSequenceDepth++;
+}
+
+void CellBuffer::EndUndoAction() {
+ EnsureUndoRoom();
+ undoSequenceDepth--;
+ if (0 == undoSequenceDepth) {
+ if (actions[currentAction].at != startAction) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ maxAction = currentAction;
+ }
+ }
+}
+
+void CellBuffer::DeleteUndoHistory() {
+ for (int i = 1; i < maxAction; i++)
+ actions[i].Destroy();
+ maxAction = 0;
+ currentAction = 0;
+ savePoint = 0;
+}
+
+bool CellBuffer::CanUndo() {
+ return (!readOnly) && ((currentAction > 0) && (maxAction > 0));
+}
+
+int CellBuffer::StartUndo() {
+ // Drop any trailing startAction
+ if (actions[currentAction].at == startAction && currentAction > 0)
+ currentAction--;
+
+ // Count the steps in this action
+ int act = currentAction;
+ while (actions[act].at != startAction && act > 0) {
+ act--;
+ }
+ return currentAction - act;
+}
+
+const Action &CellBuffer::UndoStep() {
+ const Action &actionStep = actions[currentAction];
+ if (actionStep.at == insertAction) {
+ BasicDeleteChars(actionStep.position, actionStep.lenData*2);
+ } else if (actionStep.at == removeAction) {
+ char *styledData = new char[actionStep.lenData * 2];
+ for (int i = 0; i < actionStep.lenData; i++) {
+ styledData[i*2] = actionStep.data[i];
+ styledData[i*2+1] = 0;
+ }
+ BasicInsertString(actionStep.position, styledData, actionStep.lenData*2);
+ delete []styledData;
+ }
+ currentAction--;
+ return actionStep;
+}
+
+bool CellBuffer::CanRedo() {
+ return (!readOnly) && (maxAction > currentAction);
+}
+
+int CellBuffer::StartRedo() {
+ // Drop any leading startAction
+ if (actions[currentAction].at == startAction && currentAction < maxAction)
+ currentAction++;
+
+ // Count the steps in this action
+ int act = currentAction;
+ while (actions[act].at != startAction && act < maxAction) {
+ act++;
+ }
+ return act - currentAction;
+}
+
+const Action &CellBuffer::RedoStep() {
+ const Action &actionStep = actions[currentAction];
+ if (actionStep.at == insertAction) {
+ char *styledData = new char[actionStep.lenData * 2];
+ for (int i = 0; i < actionStep.lenData; i++) {
+ styledData[i*2] = actionStep.data[i];
+ styledData[i*2+1] = 0;
+ }
+ BasicInsertString(actionStep.position, styledData, actionStep.lenData*2);
+ delete []styledData;
+ } else if (actionStep.at == removeAction) {
+ BasicDeleteChars(actionStep.position, actionStep.lenData*2);
+ }
+ currentAction++;
+ return actionStep;
+}
+
+int CellBuffer::SetLineState(int line, int state) {
+ int stateOld = lineStates[line];
+ lineStates[line] = state;
+ return stateOld;
+}
+
+int CellBuffer::GetLineState(int line) {
+ return lineStates[line];
+}
+
+int CellBuffer::GetMaxLineState() {
+ return lineStates.Length();
+}
+
+int CellBuffer::SetLevel(int line, int level) {
+ int prev = 0;
+ if ((line >= 0) && (line < lv.lines)) {
+ if (!lv.levels) {
+ lv.ExpandLevels();
+ }
+ prev = lv.levels[line];
+ if (lv.levels[line] != level) {
+ lv.levels[line] = level;
+ }
+ }
+ return prev;
+}
+
+int CellBuffer::GetLevel(int line) {
+ if (lv.levels && (line >= 0) && (line < lv.lines)) {
+ return lv.levels[line];
+ } else {
+ return SC_FOLDLEVELBASE;
+ }
+}
+
diff --git a/src/CellBuffer.h b/src/CellBuffer.h
new file mode 100644
index 000000000..5fbe2ea8a
--- /dev/null
+++ b/src/CellBuffer.h
@@ -0,0 +1,197 @@
+// Scintilla source code edit control
+// CellBuffer.h - manages the text of the document
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef CELLBUFFER_H
+#define CELLBUFFER_H
+
+// This holds the marker identifier and the marker type to display.
+// MarkerHandleNumbers are members of lists.
+struct MarkerHandleNumber {
+ int handle;
+ int number;
+ MarkerHandleNumber *next;
+};
+
+// A marker handle set contains any number of MarkerHandleNumbers
+class MarkerHandleSet {
+ MarkerHandleNumber *root;
+public:
+ MarkerHandleSet();
+ ~MarkerHandleSet();
+ int Length();
+ int NumberFromHandle(int handle);
+ int MarkValue(); // Bit set of marker numbers
+ bool Contains(int handle);
+ bool InsertHandle(int handle, int markerNum);
+ void RemoveHandle(int handle);
+ void RemoveNumber(int markerNum);
+ void CombineWith(MarkerHandleSet *other);
+};
+
+// Each line stores the starting position of the first character of the line in the cell buffer
+// and potentially a marker handle set. Often a line will not have any attached markers.
+struct LineData {
+ int startPosition;
+ MarkerHandleSet *handleSet;
+ LineData() : startPosition(0), handleSet(0) {
+ }
+};
+
+// The line vector contains information about each of the lines in a cell buffer.
+class LineVector {
+public:
+ enum { growSize = 4000 };
+ int lines;
+ LineData *linesData;
+ int size;
+ int *levels;
+ int sizeLevels;
+
+ // Handles are allocated sequentially and should never have to be reused as 32 bit ints are very big.
+ int handleCurrent;
+
+ LineVector();
+ ~LineVector();
+ void Init();
+
+ void Expand(int sizeNew);
+ void ExpandLevels(int sizeNew=-1);
+ void InsertValue(int pos, int value);
+ void SetValue(int pos, int value);
+ void Remove(int pos);
+ int LineFromPosition(int pos);
+
+ int AddMark(int line, int marker);
+ void MergeMarkers(int pos);
+ void DeleteMark(int line, int markerNum);
+ void DeleteMarkFromHandle(int markerHandle);
+ int LineFromHandle(int markerHandle);
+};
+
+// Actions are used to store all the information required to perform one undo/redo step.
+enum actionType { insertAction, removeAction, startAction };
+
+class Action {
+public:
+ actionType at;
+ int position;
+ char *data;
+ int lenData;
+
+ Action();
+ ~Action();
+ void Create(actionType at_, int position_=0, char *data_=0, int lenData_=0);
+ void Destroy();
+ void Grab(Action *source);
+};
+
+enum undoCollectionType { undoCollectNone, undoCollectAutoStart, undoCollectManualStart };
+
+// Holder for an expandable array of characters that supports undo and line markers
+// Based on article "Data Structures in a Bit-Mapped Text Editor"
+// by Wilfred J. Hansen, Byte January 1987, page 183
+class CellBuffer {
+private:
+ char *body;
+ int size;
+ int length;
+ int part1len;
+ int gaplen;
+ char *part2body;
+ bool readOnly;
+
+ Action *actions;
+ int lenActions;
+ int maxAction;
+ int currentAction;
+ undoCollectionType collectingUndo;
+ int undoSequenceDepth;
+ int savePoint;
+
+ LineVector lv;
+
+ SVector<int, 4000> lineStates;
+
+ void GapTo(int position);
+ void RoomFor(int insertionLength);
+
+ void EnsureUndoRoom();
+ void AppendAction(actionType at, int position, char *data, int length);
+
+ inline char ByteAt(int position);
+ void SetByteAt(int position, char ch);
+
+public:
+
+ CellBuffer(int initialLength = 4000);
+ ~CellBuffer();
+
+ // Retrieving positions outside the range of the buffer works and returns 0
+ char CharAt(int position);
+ void GetCharRange(char *buffer, int position, int lengthRetrieve);
+ char StyleAt(int position);
+
+ int ByteLength();
+ int Length();
+ int Lines();
+ int LineStart(int line);
+ int LineFromPosition(int pos) { return lv.LineFromPosition(pos); }
+ const char *InsertString(int position, char *s, int insertLength);
+ void InsertCharStyle(int position, char ch, char style);
+
+ // Setting styles for positions outside the range of the buffer is safe and has no effect.
+ // True is returned if the style of a character changed.
+ bool SetStyleAt(int position, char style, char mask=(char)0xff);
+ bool SetStyleFor(int position, int length, char style, char mask);
+
+ const char *DeleteChars(int position, int deleteLength);
+
+ bool IsReadOnly();
+ void SetReadOnly(bool set);
+
+ // The save point is a marker in the undo stack where the container has stated that
+ // the buffer was saved. Undo and redo can move over the save point.
+ void SetSavePoint();
+ bool IsSavePoint();
+
+ // Line marker functions
+ int AddMark(int line, int markerNum);
+ void DeleteMark(int line, int markerNum);
+ void DeleteMarkFromHandle(int markerHandle);
+ int GetMark(int line);
+ void DeleteAllMarks(int markerNum);
+ int LineFromHandle(int markerHandle);
+
+ // Without undo
+ void BasicInsertString(int position, char *s, int insertLength);
+ void BasicDeleteChars(int position, int deleteLength);
+
+ undoCollectionType SetUndoCollection(undoCollectionType collectUndo);
+ bool IsCollectingUndo();
+ void AppendUndoStartAction();
+ void BeginUndoAction();
+ void EndUndoAction();
+ void DeleteUndoHistory();
+
+ // To perform an undo, StartUndo is called to retreive the number of steps, then UndoStep is
+ // called that many times. Similarly for redo.
+ bool CanUndo();
+ int StartUndo();
+ const Action &UndoStep();
+ bool CanRedo();
+ int StartRedo();
+ const Action &RedoStep();
+
+ int SetLineState(int line, int state);
+ int GetLineState(int line);
+ int GetMaxLineState();
+
+ int SetLevel(int line, int level);
+ int GetLevel(int line);
+};
+
+#define CELL_SIZE 2
+
+#endif
diff --git a/src/ContractionState.cxx b/src/ContractionState.cxx
new file mode 100644
index 000000000..6f41461eb
--- /dev/null
+++ b/src/ContractionState.cxx
@@ -0,0 +1,203 @@
+// Scintilla source code edit control
+// ContractionState.cxx - manages visibility of lines for folding
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include "Platform.h"
+
+#include "ContractionState.h"
+
+OneLine::OneLine() {
+ displayLine = 0;
+ docLine = 0;
+ visible = true;
+ expanded = true;
+}
+
+ContractionState::ContractionState() {
+ lines = 0;
+ size = 0;
+ linesInDoc = 1;
+ linesInDisplay = 1;
+ valid = false;
+}
+
+ContractionState::~ContractionState() {
+ Clear();
+}
+
+void ContractionState::MakeValid() const {
+ if (!valid) {
+ // Could be cleverer by keeping the index of the last still valid entry
+ // rather than invalidating all.
+ int linePrev = -1;
+ int lineDisplay = 0;
+ for (int line=0; line<linesInDoc; line++) {
+ lines[line].displayLine = lineDisplay;
+ if (lines[line].visible) {
+ lines[lineDisplay].docLine = line;
+ lineDisplay++;
+ }
+ }
+ valid = true;
+ }
+}
+
+void ContractionState::Clear() {
+ delete []lines;
+ lines = 0;
+ size = 0;
+ linesInDoc = 1;
+ linesInDisplay = 1;
+}
+
+int ContractionState::LinesInDoc() const {
+ return linesInDoc;
+}
+
+int ContractionState::LinesDisplayed() const {
+ return linesInDisplay;
+}
+
+int ContractionState::DisplayFromDoc(int lineDoc) const {
+ if (size == 0) {
+ return lineDoc;
+ }
+ MakeValid();
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ return lines[lineDoc].displayLine;
+ }
+ return -1;
+}
+
+int ContractionState::DocFromDisplay(int lineDisplay) const {
+ if (lineDisplay <= 0)
+ return 0;
+ if (lineDisplay >= linesInDisplay)
+ return linesInDoc-1;
+ if (size == 0)
+ return lineDisplay;
+ MakeValid();
+ return lines[lineDisplay].docLine;
+}
+
+void ContractionState::Grow(int sizeNew) {
+ OneLine *linesNew = new OneLine[sizeNew];
+ if (linesNew) {
+ int i = 0;
+ for (; i < size; i++) {
+ linesNew[i] = lines[i];
+ }
+ for (; i < sizeNew; i++) {
+ linesNew[i].displayLine = i;
+ }
+ delete []lines;
+ lines = linesNew;
+ size = sizeNew;
+ valid = false;
+ } else {
+ Platform::DebugPrintf("No memory available\n");
+ // TODO: Blow up
+ }
+}
+
+void ContractionState::InsertLines(int lineDoc, int lineCount) {
+ if (size == 0) {
+ linesInDoc += lineCount;
+ linesInDisplay += lineCount;
+ return;
+ }
+ //Platform::DebugPrintf("InsertLine[%d] = %d\n", lineDoc);
+ if ((linesInDoc + 2) >= size) {
+ Grow(size + growSize);
+ }
+ linesInDoc += lineCount;
+ linesInDisplay += lineCount;
+ for (int i = linesInDoc + 1; i >= lineDoc + lineCount; i--) {
+ lines[i].visible = lines[i - lineCount].visible;
+ lines[i].expanded = lines[i - lineCount].expanded;
+ }
+ for (int d=0;d<lineCount;d++) {
+ lines[lineDoc+d].visible = true; // Should inherit visibility from context ?
+ lines[lineDoc+d].expanded = true;
+ }
+ valid = false;
+}
+
+void ContractionState::DeleteLines(int lineDoc, int lineCount) {
+ if (size == 0) {
+ linesInDoc -= lineCount;
+ linesInDisplay -= lineCount;
+ return;
+ }
+ int delta = 0;
+ for (int d=0;d<lineCount;d++)
+ if (lines[lineDoc+d].visible)
+ delta--;
+ for (int i = lineDoc; i < linesInDoc-lineCount; i++) {
+ lines[i].visible = lines[i + lineCount].visible;
+ lines[i].expanded = lines[i + lineCount].expanded;
+ }
+ linesInDoc -= lineCount;
+ linesInDisplay += delta;
+ valid = false;
+}
+
+bool ContractionState::GetVisible(int lineDoc) const {
+ if (size == 0)
+ return true;
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ return lines[lineDoc].visible;
+ } else {
+ return false;
+ }
+}
+
+bool ContractionState::SetVisible(int lineDocStart, int lineDocEnd, bool visible) {
+ if (size == 0) {
+ Grow(lineDocEnd + growSize);
+ }
+ // TODO: modify docLine members to mirror displayLine
+ int delta = 0;
+ // Change lineDocs
+ if ((lineDocStart <= lineDocEnd) && (lineDocStart >= 0) && (lineDocEnd < linesInDoc)) {
+ for (int line=lineDocStart; line <= lineDocEnd; line++) {
+ if (lines[line].visible != visible) {
+ delta += visible ? 1 : -1;
+ lines[line].visible = visible;
+ }
+ lines[line].displayLine += delta;
+ }
+ if (delta != 0) {
+ for (int line=lineDocEnd+1; line <= linesInDoc; line++) {
+ lines[line].displayLine += delta;
+ }
+ }
+ }
+ linesInDisplay += delta;
+ valid = false;
+ return delta != 0;
+}
+
+bool ContractionState::GetExpanded(int lineDoc) const {
+ if (size == 0)
+ return true;
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ return lines[lineDoc].expanded;
+ } else {
+ return false;
+ }
+}
+
+bool ContractionState::SetExpanded(int lineDoc, bool expanded) {
+ if (size == 0) {
+ Grow(lineDoc + growSize);
+ }
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ if (lines[lineDoc].expanded != expanded) {
+ lines[lineDoc].expanded = expanded;
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/ContractionState.h b/src/ContractionState.h
new file mode 100644
index 000000000..9e17a7693
--- /dev/null
+++ b/src/ContractionState.h
@@ -0,0 +1,50 @@
+// Scintilla source code edit control
+// ContractionState.h - manages visibility of lines for folding
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef CONTRACTIONSTATE_H
+#define CONTRACTIONSTATE_H
+
+class OneLine {
+public:
+ int displayLine; // position within set of visible lines
+ int docLine; // inverse of displayLine
+ bool visible;
+ bool expanded;
+
+ OneLine();
+ virtual ~OneLine() {}
+};
+
+class ContractionState {
+ void Grow(int sizeNew);
+ enum { growSize = 4000 };
+ int linesInDoc;
+ int linesInDisplay;
+ mutable OneLine *lines;
+ int size;
+ mutable bool valid;
+ void MakeValid() const;
+public:
+ ContractionState();
+ virtual ~ContractionState();
+
+ void Clear();
+
+ int LinesInDoc() const;
+ int LinesDisplayed() const;
+ int DisplayFromDoc(int lineDoc) const;
+ int DocFromDisplay(int lineDisplay) const;
+
+ void InsertLines(int lineDoc, int lineCount);
+ void DeleteLines(int lineDoc, int lineCount);
+
+ bool GetVisible(int lineDoc) const;
+ bool SetVisible(int lineDocStart, int lineDocEnd, bool visible);
+
+ bool GetExpanded(int lineDoc) const;
+ bool SetExpanded(int lineDoc, bool expanded);
+};
+
+#endif
diff --git a/src/Document.cxx b/src/Document.cxx
new file mode 100644
index 000000000..7d832241f
--- /dev/null
+++ b/src/Document.cxx
@@ -0,0 +1,734 @@
+// Scintilla source code edit control
+// Document.cxx - text document that handles notifications, DBCS, styling, words and end of line
+// Copyright 1998-2000 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 <ctype.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "SVector.h"
+#include "CellBuffer.h"
+#include "Document.h"
+
+Document::Document() {
+ refCount = 0;
+#ifdef unix
+ eolMode = SC_EOL_LF;
+#else
+ eolMode = SC_EOL_CRLF;
+#endif
+ dbcsCodePage = 0;
+ stylingBits = 5;
+ stylingBitsMask = 0x1F;
+ stylingPos = 0;
+ stylingMask = 0;
+ for (int ch = 0; ch < 256; ch++) {
+ wordchars[ch] = isalnum(ch) || ch == '_';
+ }
+ endStyled = 0;
+ enteredCount = 0;
+ tabInChars = 8;
+ watchers = 0;
+ lenWatchers = 0;
+}
+
+Document::~Document() {
+ for (int i = 0; i < lenWatchers; i++) {
+ watchers[i].watcher->NotifyDeleted(this, watchers[i].userData);
+ }
+ delete []watchers;
+ watchers = 0;
+ lenWatchers = 0;
+}
+
+// Increase reference count and return its previous value.
+int Document::AddRef() {
+ return refCount++;
+}
+
+// Decrease reference count and return its provius value.
+// Delete the document if reference count reaches zero.
+int Document::Release() {
+ int curRefCount = --refCount;
+ if (curRefCount == 0)
+ delete this;
+ return curRefCount;
+}
+
+void Document::SetSavePoint() {
+ cb.SetSavePoint();
+ NotifySavePoint(true);
+}
+
+int Document::LineStart(int line) {
+ return cb.LineStart(line);
+}
+
+int Document::LineFromPosition(int pos) {
+ return cb.LineFromPosition(pos);
+}
+
+int Document::LineEndPosition(int position) {
+ int line = LineFromPosition(position);
+ if (line == LinesTotal() - 1) {
+ position = LineStart(line + 1);
+ } else {
+ position = LineStart(line + 1) - 1;
+ // When line terminator is CR+LF, may need to go back one more
+ if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) {
+ position--;
+ }
+ }
+ return position;
+}
+
+int Document::VCHomePosition(int position) {
+ int line = LineFromPosition(position);
+ int startPosition = LineStart(line);
+ int endLine = LineStart(line + 1) - 1;
+ int startText = startPosition;
+ while (startText < endLine && (cb.CharAt(startText) == ' ' || cb.CharAt(startText) == '\t' ) )
+ startText++;
+ if (position == startText)
+ return startPosition;
+ else
+ return startText;
+}
+
+int Document::SetLevel(int line, int level) {
+ int prev = cb.SetLevel(line, level);
+ if (prev != level) {
+ DocModification mh(SC_MOD_CHANGEFOLD, LineStart(line), 0, 0, 0);
+ mh.line = line;
+ mh.foldLevelNow = level;
+ mh.foldLevelPrev = prev;
+ NotifyModified(mh);
+ }
+ return prev;
+}
+
+static bool IsSubordinate(int levelStart, int levelTry) {
+ if (levelTry & SC_FOLDLEVELWHITEFLAG)
+ return true;
+ else
+ return (levelStart & SC_FOLDLEVELNUMBERMASK) < (levelTry & SC_FOLDLEVELNUMBERMASK);
+}
+
+int Document::GetLastChild(int lineParent, int level) {
+ if (level == -1)
+ level = GetLevel(lineParent) & SC_FOLDLEVELNUMBERMASK;
+ int maxLine = LinesTotal();
+ int lineMaxSubord = lineParent;
+ while ((lineMaxSubord < maxLine-1) && IsSubordinate(level, GetLevel(lineMaxSubord+1))) {
+ lineMaxSubord++;
+ }
+ if (lineMaxSubord > lineParent) {
+ if (level > (GetLevel(lineMaxSubord+1) & SC_FOLDLEVELNUMBERMASK)) {
+ // Have chewed up some whitespace that belongs to a parent so seek back
+ if ((lineMaxSubord > lineParent) && (GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG)) {
+ lineMaxSubord--;
+ }
+ }
+ }
+ return lineMaxSubord;
+}
+
+int Document::GetFoldParent(int line) {
+ int level = GetLevel(line);
+ int lineLook = line-1;
+ while ((lineLook > 0) && (
+ (!(GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG)) ||
+ ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) >= level))
+ ) {
+ lineLook--;
+ }
+ if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) &&
+ ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) < level)) {
+ return lineLook;
+ } else {
+ return -1;
+ }
+}
+
+int Document::ClampPositionIntoDocument(int pos) {
+ return Platform::Clamp(pos, 0, Length());
+}
+
+bool Document::IsCrLf(int pos) {
+ if (pos < 0)
+ return false;
+ if (pos >= (Length() - 1))
+ return false;
+ return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n');
+}
+
+bool Document::IsDBCS(int pos) {
+#if PLAT_WIN
+ if (dbcsCodePage) {
+ // Anchor DBCS calculations at start of line because start of line can
+ // not be a DBCS trail byte.
+ int startLine = pos;
+ while (startLine > 0 && cb.CharAt(startLine) != '\r' && cb.CharAt(startLine) != '\n')
+ startLine--;
+ while (startLine <= pos) {
+ if (IsDBCSLeadByteEx(dbcsCodePage, cb.CharAt(startLine))) {
+ startLine++;
+ if (startLine >= pos)
+ return true;
+ }
+ startLine++;
+ }
+ }
+ return false;
+#else
+ return false;
+#endif
+}
+
+// Normalise a position so that it is not halfway through a two byte character.
+// This can occur in two situations -
+// When lines are terminated with \r\n pairs which should be treated as one character.
+// When displaying DBCS text such as Japanese.
+// If moving, move the position in the indicated direction.
+int Document::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
+ //Platform::DebugPrintf("NoCRLF %d %d\n", pos, moveDir);
+ // If out of range, just return value - should be fixed up after
+ if (pos < 0)
+ return pos;
+ if (pos > Length())
+ return pos;
+
+ // Position 0 and Length() can not be between any two characters
+ if (pos == 0)
+ return pos;
+ if (pos == Length())
+ return pos;
+
+ // assert pos > 0 && pos < Length()
+ if (checkLineEnd && IsCrLf(pos - 1)) {
+ if (moveDir > 0)
+ return pos + 1;
+ else
+ return pos - 1;
+ }
+
+ // Not between CR and LF
+
+#if PLAT_WIN
+ if (dbcsCodePage) {
+ // Anchor DBCS calculations at start of line because start of line can
+ // not be a DBCS trail byte.
+ int startLine = pos;
+ while (startLine > 0 && cb.CharAt(startLine) != '\r' && cb.CharAt(startLine) != '\n')
+ startLine--;
+ bool atLeadByte = false;
+ while (startLine < pos) {
+ if (atLeadByte)
+ atLeadByte = false;
+ else if (IsDBCSLeadByteEx(dbcsCodePage, cb.CharAt(startLine)))
+ atLeadByte = true;
+ else
+ atLeadByte = false;
+ startLine++;
+ //Platform::DebugPrintf("DBCS %s\n", atlead ? "D" : "-");
+ }
+
+ if (atLeadByte) {
+ // Position is between a lead byte and a trail byte
+ if (moveDir > 0)
+ return pos + 1;
+ else
+ return pos - 1;
+ }
+ }
+#endif
+
+ return pos;
+}
+
+void Document::ModifiedAt(int pos) {
+ if (endStyled > pos)
+ endStyled = pos;
+}
+
+// Document only modified by gateways DeleteChars, InsertStyledString, Undo, Redo, and SetStyleAt.
+// SetStyleAt does not change the persistent state of a document
+
+// Unlike Undo, Redo, and InsertStyledString, the pos argument is a cell number not a char number
+void Document::DeleteChars(int pos, int len) {
+ if (enteredCount == 0) {
+ enteredCount++;
+ if (cb.IsReadOnly())
+ NotifyModifyAttempt();
+ if (!cb.IsReadOnly()) {
+ int prevLinesTotal = LinesTotal();
+ bool startSavePoint = cb.IsSavePoint();
+ const char *text = cb.DeleteChars(pos*2, len * 2);
+ if (startSavePoint && cb.IsCollectingUndo())
+ NotifySavePoint(!startSavePoint);
+ ModifiedAt(pos);
+ int modFlags = SC_MOD_DELETETEXT | SC_PERFORMED_USER;
+ DocModification mh(modFlags, pos, len, LinesTotal() - prevLinesTotal, text);
+ NotifyModified(mh);
+ }
+ enteredCount--;
+ }
+}
+
+void Document::InsertStyledString(int position, char *s, int insertLength) {
+ if (enteredCount == 0) {
+ enteredCount++;
+ if (cb.IsReadOnly())
+ NotifyModifyAttempt();
+ if (!cb.IsReadOnly()) {
+ int prevLinesTotal = LinesTotal();
+ bool startSavePoint = cb.IsSavePoint();
+ const char *text = cb.InsertString(position, s, insertLength);
+ if (startSavePoint && cb.IsCollectingUndo())
+ NotifySavePoint(!startSavePoint);
+ ModifiedAt(position / 2);
+
+ int modFlags = SC_MOD_INSERTTEXT | SC_PERFORMED_USER;
+ DocModification mh(modFlags, position / 2, insertLength / 2, LinesTotal() - prevLinesTotal, text);
+ NotifyModified(mh);
+ }
+ enteredCount--;
+ }
+}
+
+int Document::Undo() {
+ int newPos = 0;
+ if (enteredCount == 0) {
+ enteredCount++;
+ bool startSavePoint = cb.IsSavePoint();
+ int steps = cb.StartUndo();
+ for (int step=0; step<steps; step++) {
+ int prevLinesTotal = LinesTotal();
+ const Action &action = cb.UndoStep();
+ int cellPosition = action.position / 2;
+ ModifiedAt(cellPosition);
+ newPos = cellPosition;
+
+ int modFlags = SC_PERFORMED_UNDO;
+ // With undo, an insertion action becomes a deletion notification
+ if (action.at == removeAction) {
+ newPos += action.lenData;
+ modFlags |= SC_MOD_INSERTTEXT;
+ } else {
+ modFlags |= SC_MOD_DELETETEXT;
+ }
+ if (step == steps-1)
+ modFlags |= SC_LASTSTEPINUNDOREDO;
+ NotifyModified(DocModification(modFlags, cellPosition, action.lenData,
+ LinesTotal() - prevLinesTotal, action.data));
+ }
+
+ bool endSavePoint = cb.IsSavePoint();
+ if (startSavePoint != endSavePoint)
+ NotifySavePoint(endSavePoint);
+ enteredCount--;
+ }
+ return newPos;
+}
+
+int Document::Redo() {
+ int newPos = 0;
+ if (enteredCount == 0) {
+ enteredCount++;
+ bool startSavePoint = cb.IsSavePoint();
+ int steps = cb.StartRedo();
+ for (int step=0; step<steps; step++) {
+ int prevLinesTotal = LinesTotal();
+ const Action &action = cb.RedoStep();
+ int cellPosition = action.position / 2;
+ ModifiedAt(cellPosition);
+ newPos = cellPosition;
+
+ int modFlags = SC_PERFORMED_REDO;
+ if (action.at == insertAction) {
+ newPos += action.lenData;
+ modFlags |= SC_MOD_INSERTTEXT;
+ } else {
+ modFlags |= SC_MOD_DELETETEXT;
+ }
+ if (step == steps-1)
+ modFlags |= SC_LASTSTEPINUNDOREDO;
+ NotifyModified(DocModification(modFlags, cellPosition, action.lenData,
+ LinesTotal() - prevLinesTotal, action.data));
+ }
+
+ bool endSavePoint = cb.IsSavePoint();
+ if (startSavePoint != endSavePoint)
+ NotifySavePoint(endSavePoint);
+ enteredCount--;
+ }
+ return newPos;
+}
+
+void Document::InsertChar(int pos, char ch) {
+ char chs[2];
+ chs[0] = ch;
+ chs[1] = 0;
+ InsertStyledString(pos*2, chs, 2);
+}
+
+// Insert a null terminated string
+void Document::InsertString(int position, const char *s) {
+ InsertString(position, s, strlen(s));
+}
+
+// Insert a string with a length
+void Document::InsertString(int position, const char *s, int insertLength) {
+ char *sWithStyle = new char[insertLength * 2];
+ if (sWithStyle) {
+ for (int i = 0; i < insertLength; i++) {
+ sWithStyle[i*2] = s[i];
+ sWithStyle[i*2 + 1] = 0;
+ }
+ InsertStyledString(position*2, sWithStyle, insertLength*2);
+ delete []sWithStyle;
+ }
+}
+
+void Document::DelChar(int pos) {
+ if (IsCrLf(pos)) {
+ DeleteChars(pos, 2);
+ } else if (IsDBCS(pos)) {
+ DeleteChars(pos, 2);
+ } else if (pos < Length()) {
+ DeleteChars(pos, 1);
+ }
+}
+
+int Document::DelCharBack(int pos) {
+ if (pos <= 0) {
+ return pos;
+ } else if (IsCrLf(pos - 2)) {
+ DeleteChars(pos - 2, 2);
+ return pos - 2;
+ } else if (IsDBCS(pos - 1)) {
+ DeleteChars(pos - 2, 2);
+ return pos - 2;
+ } else {
+ DeleteChars(pos - 1, 1);
+ return pos - 1;
+ }
+}
+
+void Document::Indent(bool forwards, int lineBottom, int lineTop) {
+ if (forwards) {
+ // Indent by a tab
+ for (int line = lineBottom; line >= lineTop; line--) {
+ InsertChar(LineStart(line), '\t');
+ }
+ } else {
+ // Dedent - suck white space off the front of the line to dedent by equivalent of a tab
+ for (int line = lineBottom; line >= lineTop; line--) {
+ int ispc = 0;
+ while (ispc < tabInChars && cb.CharAt(LineStart(line) + ispc) == ' ')
+ ispc++;
+ int posStartLine = LineStart(line);
+ if (ispc == tabInChars) {
+ DeleteChars(posStartLine, ispc);
+ } else if (cb.CharAt(posStartLine + ispc) == '\t') {
+ DeleteChars(posStartLine, ispc + 1);
+ } else { // Hit a non-white
+ DeleteChars(posStartLine, ispc);
+ }
+ }
+ }
+}
+
+void Document::ConvertLineEnds(int eolModeSet) {
+ BeginUndoAction();
+ for (int pos = 0; pos < Length(); pos++) {
+ if (cb.CharAt(pos) == '\r') {
+ if (cb.CharAt(pos+1) == '\n') {
+ if (eolModeSet != SC_EOL_CRLF) {
+ DeleteChars(pos, 2);
+ if (eolModeSet == SC_EOL_CR)
+ InsertString(pos, "\r", 1);
+ else
+ InsertString(pos, "\n", 1);
+ } else {
+ pos++;
+ }
+ } else {
+ if (eolModeSet != SC_EOL_CR) {
+ DeleteChars(pos, 1);
+ if (eolModeSet == SC_EOL_CRLF) {
+ InsertString(pos, "\r\n", 2);
+ pos++;
+ } else {
+ InsertString(pos, "\n", 1);
+ }
+ }
+ }
+ } else if (cb.CharAt(pos) == '\n') {
+ if (eolModeSet != SC_EOL_LF) {
+ DeleteChars(pos, 1);
+ if (eolModeSet == SC_EOL_CRLF) {
+ InsertString(pos, "\r\n", 2);
+ pos++;
+ } else {
+ InsertString(pos, "\r", 1);
+ }
+ }
+ }
+ }
+ EndUndoAction();
+}
+
+bool Document::IsWordChar(unsigned char ch) {
+ return wordchars[ch];
+}
+
+int Document::ExtendWordSelect(int pos, int delta) {
+ if (delta < 0) {
+ while (pos > 0 && IsWordChar(cb.CharAt(pos - 1)))
+ pos--;
+ } else {
+ while (pos < (Length()) && IsWordChar(cb.CharAt(pos)))
+ pos++;
+ }
+ return pos;
+}
+
+int Document::NextWordStart(int pos, int delta) {
+ if (delta < 0) {
+ while (pos > 0 && (cb.CharAt(pos - 1) == ' ' || cb.CharAt(pos - 1) == '\t'))
+ pos--;
+ if (isspace(cb.CharAt(pos - 1))) { // Back up to previous line
+ while (pos > 0 && isspace(cb.CharAt(pos - 1)))
+ pos--;
+ } else {
+ bool startAtWordChar = IsWordChar(cb.CharAt(pos - 1));
+ while (pos > 0 && !isspace(cb.CharAt(pos - 1)) && (startAtWordChar == IsWordChar(cb.CharAt(pos - 1))))
+ pos--;
+ }
+ } else {
+ bool startAtWordChar = IsWordChar(cb.CharAt(pos));
+ while (pos < (Length()) && isspace(cb.CharAt(pos)))
+ pos++;
+ while (pos < (Length()) && !isspace(cb.CharAt(pos)) && (startAtWordChar == IsWordChar(cb.CharAt(pos))))
+ pos++;
+ while (pos < (Length()) && (cb.CharAt(pos) == ' ' || cb.CharAt(pos) == '\t'))
+ pos++;
+ }
+ return pos;
+}
+
+bool Document::IsWordAt(int start, int end) {
+ int lengthDoc = Length();
+ if (start > 0) {
+ char ch = CharAt(start - 1);
+ if (IsWordChar(ch))
+ return false;
+ }
+ if (end < lengthDoc - 1) {
+ char ch = CharAt(end);
+ if (IsWordChar(ch))
+ return false;
+ }
+ return true;
+}
+
+// Find text in document, supporting both forward and backward
+// searches (just pass minPos > maxPos to do a backward search)
+// Has not been tested with backwards DBCS searches yet.
+long Document::FindText(int minPos, int maxPos, const char *s, bool caseSensitive, bool word) {
+ bool forward = minPos <= maxPos;
+ int increment = forward ? 1 : -1;
+
+ // Range endpoints should not be inside DBCS characters, but just in case, move them.
+ int startPos = MovePositionOutsideChar(minPos, increment, false);
+ int endPos = MovePositionOutsideChar(maxPos, increment, false);
+
+ // Compute actual search ranges needed
+ int lengthFind = strlen(s);
+ int endSearch = 0;
+ if (startPos <= endPos) {
+ endSearch = endPos - lengthFind + 1;
+ } else {
+ endSearch = endPos;
+ }
+ //Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind);
+ char firstChar = s[0];
+ if (!caseSensitive)
+ firstChar = toupper(firstChar);
+ int pos = startPos;
+ while (forward ? (pos < endSearch) : (pos >= endSearch)) {
+ char ch = CharAt(pos);
+ if (caseSensitive) {
+ if (ch == firstChar) {
+ bool found = true;
+ for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) {
+ ch = CharAt(pos + posMatch);
+ if (ch != s[posMatch])
+ found = false;
+ }
+ if (found) {
+ if ((!word) || IsWordAt(pos, pos + lengthFind))
+ return pos;
+ }
+ }
+ } else {
+ if (toupper(ch) == firstChar) {
+ bool found = true;
+ for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) {
+ ch = CharAt(pos + posMatch);
+ if (toupper(ch) != toupper(s[posMatch]))
+ found = false;
+ }
+ if (found) {
+ if ((!word) || IsWordAt(pos, pos + lengthFind))
+ return pos;
+ }
+ }
+ }
+ pos += increment;
+ if (dbcsCodePage) {
+ // Ensure trying to match from start of character
+ pos = MovePositionOutsideChar(pos, increment, false);
+ }
+ }
+ //Platform::DebugPrintf("Not found\n");
+ return - 1;
+}
+
+int Document::LinesTotal() {
+ return cb.Lines();
+}
+
+void Document::SetWordChars(unsigned char *chars) {
+ int ch;
+ for (ch = 0; ch < 256; ch++) {
+ wordchars[ch] = false;
+ }
+ if (chars) {
+ while (*chars) {
+ wordchars[*chars] = true;
+ chars++;
+ }
+ } else {
+ for (ch = 0; ch < 256; ch++) {
+ wordchars[ch] = isalnum(ch) || ch == '_';
+ }
+ }
+}
+
+void Document::SetStylingBits(int bits) {
+ stylingBits = bits;
+ stylingBitsMask = 0;
+ for (int bit=0; bit<stylingBits; bit++) {
+ stylingBitsMask <<= 1;
+ stylingBitsMask |= 1;
+ }
+}
+
+void Document::StartStyling(int position, char mask) {
+ stylingPos = position;
+ stylingMask = mask;
+}
+
+void Document::SetStyleFor(int length, char style) {
+ if (enteredCount == 0) {
+ enteredCount++;
+ int prevEndStyled = endStyled;
+ if (cb.SetStyleFor(stylingPos, length, style, stylingMask)) {
+ DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
+ prevEndStyled, length);
+ NotifyModified(mh);
+ }
+ stylingPos += length;
+ endStyled = stylingPos;
+ enteredCount--;
+ }
+}
+
+void Document::SetStyles(int length, char *styles) {
+ if (enteredCount == 0) {
+ enteredCount++;
+ int prevEndStyled = endStyled;
+ bool didChange = false;
+ for (int iPos = 0; iPos < length; iPos++, stylingPos++) {
+ if (cb.SetStyleAt(stylingPos, styles[iPos], stylingMask)) {
+ didChange = true;
+ }
+ }
+ endStyled = stylingPos;
+ if (didChange) {
+ DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
+ prevEndStyled, endStyled - prevEndStyled);
+ NotifyModified(mh);
+ }
+ enteredCount--;
+ }
+}
+
+bool Document::AddWatcher(DocWatcher *watcher, void *userData) {
+ for (int i = 0; i < lenWatchers; i++) {
+ if ((watchers[i].watcher == watcher) &&
+ (watchers[i].userData == userData))
+ return false;
+ }
+ WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers + 1];
+ if (!pwNew)
+ return false;
+ for (int j = 0; j < lenWatchers; j++)
+ pwNew[j] = watchers[j];
+ pwNew[lenWatchers].watcher = watcher;
+ pwNew[lenWatchers].userData = userData;
+ delete []watchers;
+ watchers = pwNew;
+ lenWatchers++;
+ return true;
+}
+
+bool Document::RemoveWatcher(DocWatcher *watcher, void *userData) {
+ for (int i = 0; i < lenWatchers; i++) {
+ if ((watchers[i].watcher == watcher) &&
+ (watchers[i].userData == userData)) {
+ if (lenWatchers == 1) {
+ delete []watchers;
+ watchers = 0;
+ lenWatchers = 0;
+ } else {
+ WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers];
+ if (!pwNew)
+ return false;
+ for (int j = 0; j < lenWatchers - 1; j++) {
+ pwNew[j] = (j < i) ? watchers[j] : watchers[j + 1];
+ }
+ delete []watchers;
+ watchers = pwNew;
+ lenWatchers--;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void Document::NotifyModifyAttempt() {
+ for (int i = 0; i < lenWatchers; i++) {
+ watchers[i].watcher->NotifyModifyAttempt(this, watchers[i].userData);
+ }
+}
+
+void Document::NotifySavePoint(bool atSavePoint) {
+ for (int i = 0; i < lenWatchers; i++) {
+ watchers[i].watcher->NotifySavePoint(this, watchers[i].userData, atSavePoint);
+ }
+}
+
+void Document::NotifyModified(DocModification mh) {
+ for (int i = 0; i < lenWatchers; i++) {
+ watchers[i].watcher->NotifyModified(this, mh, watchers[i].userData);
+ }
+}
diff --git a/src/Document.h b/src/Document.h
new file mode 100644
index 000000000..fba611c7f
--- /dev/null
+++ b/src/Document.h
@@ -0,0 +1,222 @@
+// Scintilla source code edit control
+// Document.h - text document that handles notifications, DBCS, styling, words and end of line
+// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef DOCUMENT_H
+#define DOCUMENT_H
+
+// A Position is a position within a document between two characters or at the beginning or end.
+// Sometimes used as a character index where it identifies the character after the position.
+typedef int Position;
+const Position invalidPosition = -1;
+
+// The range class represents a range of text in a document.
+// The two values are not sorted as one end may be more significant than the other
+// as is the case for the selection where the end position is the position of the caret.
+// If either position is invalidPosition then the range is invalid and most operations will fail.
+class Range {
+public:
+ Position start;
+ Position end;
+
+ Range(Position pos=0) :
+ start(pos), end(pos) {
+ };
+ Range(Position start_, Position end_) :
+ start(start_), end(end_) {
+ };
+
+ bool Valid() const {
+ return (start != invalidPosition) && (end != invalidPosition);
+ }
+
+ bool Contains(Position pos) const {
+ if (start < end) {
+ return (pos >= start && pos <= end);
+ } else {
+ return (pos <= start && pos >= end);
+ }
+ }
+
+ bool Contains(Range other) const {
+ return Contains(other.start) && Contains(other.end);
+ }
+
+ bool Overlaps(Range other) const {
+ return
+ Contains(other.start) ||
+ Contains(other.end) ||
+ other.Contains(start) ||
+ other.Contains(end);
+ }
+};
+
+class DocWatcher;
+class DocModification;
+
+class Document {
+
+public:
+ // Used to pair watcher pointer with user data
+ class WatcherWithUserData {
+ public:
+ DocWatcher *watcher;
+ void *userData;
+ WatcherWithUserData() {
+ watcher = 0;
+ userData = 0;
+ }
+ };
+
+private:
+ int refCount;
+ CellBuffer cb;
+ bool wordchars[256];
+ int stylingPos;
+ int stylingMask;
+ int endStyled;
+ int enteredCount;
+
+ WatcherWithUserData *watchers;
+ int lenWatchers;
+
+public:
+ int stylingBits;
+ int stylingBitsMask;
+
+ int eolMode;
+ int dbcsCodePage;
+ int tabInChars;
+
+ Document();
+ virtual ~Document();
+
+ int AddRef();
+ int Release();
+
+ int LineFromPosition(int pos);
+ int ClampPositionIntoDocument(int pos);
+ bool IsCrLf(int pos);
+ int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true);
+
+ // Gateways to modifying document
+ void DeleteChars(int pos, int len);
+ void InsertStyledString(int position, char *s, int insertLength);
+ int Undo();
+ int Redo();
+ bool CanUndo() { return cb.CanUndo(); }
+ bool CanRedo() { return cb.CanRedo(); }
+ void DeleteUndoHistory() { cb.DeleteUndoHistory(); }
+ undoCollectionType SetUndoCollection(undoCollectionType collectUndo) {
+ return cb.SetUndoCollection(collectUndo);
+ }
+ void AppendUndoStartAction() { cb.AppendUndoStartAction(); }
+ void BeginUndoAction() { cb.BeginUndoAction(); }
+ void EndUndoAction() { cb.EndUndoAction(); }
+ void SetSavePoint();
+ bool IsSavePoint() { return cb.IsSavePoint(); }
+ void Indent(bool forwards, int lineBottom, int lineTop);
+ void ConvertLineEnds(int eolModeSet);
+ void SetReadOnly(bool set) { cb.SetReadOnly(set); }
+
+ void InsertChar(int pos, char ch);
+ void InsertString(int position, const char *s);
+ void InsertString(int position, const char *s, int insertLength);
+ void DelChar(int pos);
+ int DelCharBack(int pos);
+
+ char CharAt(int position) { return cb.CharAt(position); }
+ void GetCharRange(char *buffer, int position, int lengthRetrieve) {
+ cb.GetCharRange(buffer, position, lengthRetrieve);
+ }
+ char StyleAt(int position) { return cb.StyleAt(position); }
+ int GetMark(int line) { return cb.GetMark(line); }
+ int AddMark(int line, int markerNum) { return cb.AddMark(line, markerNum); }
+ void DeleteMark(int line, int markerNum) { cb.DeleteMark(line, markerNum); }
+ void DeleteMarkFromHandle(int markerHandle) { cb.DeleteMarkFromHandle(markerHandle); }
+ void DeleteAllMarks(int markerNum) { cb.DeleteAllMarks(markerNum); }
+ int LineFromHandle(int markerHandle) { return cb.LineFromHandle(markerHandle); }
+ int LineStart(int line);
+ int LineEndPosition(int position);
+ int VCHomePosition(int position);
+
+ int SetLevel(int line, int level);
+ int GetLevel(int line) { return cb.GetLevel(line); }
+ int GetLastChild(int lineParent, int level=-1);
+ int GetFoldParent(int line);
+
+ void Indent(bool forwards);
+ int ExtendWordSelect(int pos, int delta);
+ int NextWordStart(int pos, int delta);
+ int Length() { return cb.Length(); }
+ long FindText(int minPos, int maxPos, const char *s, bool caseSensitive, bool word);
+ long FindText(WORD iMessage,WPARAM wParam,LPARAM lParam);
+ int LinesTotal();
+
+ void SetWordChars(unsigned char *chars);
+ void SetStylingBits(int bits);
+ void StartStyling(int position, char mask);
+ void SetStyleFor(int length, char style);
+ void SetStyles(int length, char *styles);
+ int GetEndStyled() { return endStyled; }
+
+ int SetLineState(int line, int state) { return cb.SetLineState(line, state); }
+ int GetLineState(int line) { return cb.GetLineState(line); }
+ int GetMaxLineState() { return cb.GetMaxLineState(); }
+
+ bool AddWatcher(DocWatcher *watcher, void *userData);
+ bool RemoveWatcher(DocWatcher *watcher, void *userData);
+ const WatcherWithUserData *GetWatchers() const { return watchers; }
+ int GetLenWatchers() const { return lenWatchers; }
+
+private:
+ bool IsDBCS(int pos);
+ bool IsWordChar(unsigned char ch);
+ bool IsWordAt(int start, int end);
+ void ModifiedAt(int pos);
+
+ void NotifyModifyAttempt();
+ void NotifySavePoint(bool atSavePoint);
+ void NotifyModified(DocModification mh);
+};
+
+// To optimise processing of document modifications by DocWatchers, a hint is passed indicating the
+// scope of the change.
+// If the DocWatcher is a document view then this can be used to optimise screen updating.
+class DocModification {
+public:
+ int modificationType;
+ int position;
+ int length;
+ int linesAdded; // Negative if lines deleted
+ const char *text; // Only valid for changes to text, not for changes to style
+ int line;
+ int foldLevelNow;
+ int foldLevelPrev;
+
+ DocModification(int modificationType_, int position_=0, int length_=0,
+ int linesAdded_=0, const char *text_=0) :
+ modificationType(modificationType_),
+ position(position_),
+ length(length_),
+ linesAdded(linesAdded_),
+ text(text_),
+ line(0),
+ foldLevelNow(0),
+ foldLevelPrev(0) {}
+};
+
+// A class that wants to receive notifications from a Document must be derived from DocWatcher
+// and implement the notification methods. It can then be added to the watcher list with AddWatcher.
+class DocWatcher {
+public:
+ virtual ~DocWatcher() {}
+
+ virtual void NotifyModifyAttempt(Document *doc, void *userData) = 0;
+ virtual void NotifySavePoint(Document *doc, void *userData, bool atSavePoint) = 0;
+ virtual void NotifyModified(Document *doc, DocModification mh, void *userData) = 0;
+ virtual void NotifyDeleted(Document *doc, void *userData) = 0;
+};
+
+#endif