// Scintilla source code edit control
/** @file Editor.h
 ** Defines the main editor class.
 **/
// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#ifndef EDITOR_H
#define EDITOR_H

#ifdef SCI_NAMESPACE
namespace Scintilla {
#endif

/**
 */
class Caret {
public:
	bool active;
	bool on;
	int period;

	Caret();
};

/**
 */
class Timer {
public:
	bool ticking;
	int ticksToWait;
	enum {tickSize = 100};
	TickerID tickerID;

	Timer();
};

/**
 */
class Idler {
public:
	bool state;
	IdlerID idlerID;

	Idler();
};

/**
 * When platform has a way to generate an event before painting,
 * accumulate needed styling range and other work items in 
 * WorkNeeded to avoid unnecessary work inside paint handler
 */
class WorkNeeded {
public:
	enum workItems {
		workNone=0,
		workStyle=1,
		workUpdateUI=2
	};
	bool active;
	enum workItems items;
	Position upTo;

	WorkNeeded() : active(false), items(workNone), upTo(0) {}
	void Reset() {
		active = false;
		items = workNone;
		upTo = 0;
	}
	void Need(workItems items_, Position pos) {
		if ((items_ & workStyle) && (upTo < pos))
			upTo = pos;
		items = static_cast<workItems>(items | items_);
	}
};

/**
 * Hold a piece of text selected for copying or dragging, along with encoding and selection format information.
 */
class SelectionText {
	std::string s;
public:
	bool rectangular;
	bool lineCopy;
	int codePage;
	int characterSet;
	SelectionText() : rectangular(false), lineCopy(false), codePage(0), characterSet(0) {}
	~SelectionText() {
	}
	void Clear() {
		s.clear();
		rectangular = false;
		lineCopy = false;
		codePage = 0;
		characterSet = 0;
	}
	void Copy(const std::string &s_, int codePage_, int characterSet_, bool rectangular_, bool lineCopy_) {
		s = s_;
		codePage = codePage_;
		characterSet = characterSet_;
		rectangular = rectangular_;
		lineCopy = lineCopy_;
		FixSelectionForClipboard();
	}
	void Copy(const SelectionText &other) {
		Copy(other.s, other.codePage, other.characterSet, other.rectangular, other.lineCopy);
	}
	const char *Data() const {
		return s.c_str();
	}
	size_t Length() const {
		return s.length();
	}
	size_t LengthWithTerminator() const {
		return s.length() + 1;
	}
	bool Empty() const {
		return s.empty();
	}
private:
	void FixSelectionForClipboard() {
		// To avoid truncating the contents of the clipboard when pasted where the 
		// clipboard contains NUL characters, replace NUL characters by spaces.
		std::replace(s.begin(), s.end(), '\0', ' ');
	}
};

/**
 */
class Editor : public DocWatcher {
	// Private so Editor objects can not be copied
	Editor(const Editor &);
	Editor &operator=(const Editor &);

protected:	// ScintillaBase subclass needs access to much of Editor

	/** On GTK+, Scintilla is a container widget holding two scroll bars
	 * whereas on Windows there is just one window with both scroll bars turned on. */
	Window wMain;	///< The Scintilla parent window
	Window wMargin;	///< May be separate when using a scroll view for wMain

	/** Style resources may be expensive to allocate so are cached between uses.
	 * When a style attribute is changed, this cache is flushed. */
	bool stylesValid;
	ViewStyle vs;
	int technology;
	Point sizeRGBAImage;
	float scaleRGBAImage;

	int printMagnification;
	int printColourMode;
	int printWrapState;
	int cursorMode;
	int controlCharSymbol;

	// Highlight current folding block
	HighlightDelimiter highlightDelimiter;

	bool hasFocus;
	bool hideSelection;
	bool inOverstrike;
	bool mouseDownCaptures;

	/** In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to
	 * the screen. This avoids flashing but is about 30% slower. */
	bool bufferedDraw;
	/** In twoPhaseDraw mode, drawing is performed in two phases, first the background
	* and then the foreground. This avoids chopping off characters that overlap the next run. */
	bool twoPhaseDraw;

	int xOffset;		///< Horizontal scrolled amount in pixels
	int xCaretMargin;	///< Ensure this many pixels visible on both sides of caret
	bool horizontalScrollBarVisible;
	int scrollWidth;
	bool trackLineWidth;
	int lineWidthMaxSeen;
	bool verticalScrollBarVisible;
	bool endAtLastLine;
	int caretSticky;
	int marginOptions;
	bool multipleSelection;
	bool additionalSelectionTyping;
	int multiPasteMode;
	bool additionalCaretsBlink;
	bool additionalCaretsVisible;

	int virtualSpaceOptions;

	Surface *pixmapLine;
	Surface *pixmapSelMargin;
	Surface *pixmapSelPattern;
	Surface *pixmapSelPatternOffset1;
	Surface *pixmapIndentGuide;
	Surface *pixmapIndentGuideHighlight;

	LineLayoutCache llc;
	PositionCache posCache;

	KeyMap kmap;

	Caret caret;
	Timer timer;
	Timer autoScrollTimer;
	enum { autoScrollDelay = 200 };

	Idler idler;

	Point lastClick;
	unsigned int lastClickTime;
	int dwellDelay;
	int ticksToDwell;
	bool dwelling;
	enum { selChar, selWord, selSubLine, selWholeLine } selectionType;
	Point ptMouseLast;
	enum { ddNone, ddInitial, ddDragging } inDragDrop;
	bool dropWentOutside;
	SelectionPosition posDrag;
	SelectionPosition posDrop;
	int hotSpotClickPos;
	int lastXChosen;
	int lineAnchorPos;
	int originalAnchorPos;
	int wordSelectAnchorStartPos;
	int wordSelectAnchorEndPos;
	int wordSelectInitialCaretPos;
	int targetStart;
	int targetEnd;
	int searchFlags;
	int topLine;
	int posTopLine;
	int lengthForEncode;

	int needUpdateUI;
	Position braces[2];
	int bracesMatchStyle;
	int highlightGuideColumn;

	int theEdge;

	enum { notPainting, painting, paintAbandoned } paintState;
	PRectangle rcPaint;
	bool paintingAllText;
	bool willRedrawAll;
	WorkNeeded workNeeded;

	int modEventMask;

	SelectionText drag;
	Selection sel;
	bool primarySelection;

	int caretXPolicy;
	int caretXSlop;	///< Ensure this many pixels visible on both sides of caret

	int caretYPolicy;
	int caretYSlop;	///< Ensure this many lines visible on both sides of caret

	int visiblePolicy;
	int visibleSlop;

	int searchAnchor;

	bool recordingMacro;

	int foldFlags;
	int foldAutomatic;
	ContractionState cs;

	// Hotspot support
	int hsStart;
	int hsEnd;

	// Wrapping support
	enum { eWrapNone, eWrapWord, eWrapChar } wrapState;
	enum { wrapLineLarge = 0x7ffffff };
	int wrapWidth;
	int wrapStart;
	int wrapEnd;
	int wrapVisualFlags;
	int wrapVisualFlagsLocation;
	int wrapVisualStartIndent;
	int wrapIndentMode; // SC_WRAPINDENT_FIXED, _SAME, _INDENT

	bool convertPastes;

	int marginNumberPadding; // the right-side padding of the number margin
	int ctrlCharPadding; // the padding around control character text blobs
	int lastSegItalicsOffset; // the offset so as not to clip italic characters at EOLs

	Document *pdoc;

	Editor();
	virtual ~Editor();
	virtual void Initialise() = 0;
	virtual void Finalise();

	void InvalidateStyleData();
	void InvalidateStyleRedraw();
	void RefreshStyleData();
	void DropGraphics(bool freeObjects);
	void AllocateGraphics();

	// The top left visible point in main window coordinates. Will be 0,0 except for
	// scroll views where it will be equivalent to the current scroll position.
	virtual Point GetVisibleOriginInMain();
	Point DocumentPointFromView(Point ptView);  // Convert a point from view space to document
	int TopLineOfMain() const;   // Return the line at Main's y coordinate 0
	virtual PRectangle GetClientRectangle();
	PRectangle GetTextRectangle();

	int LinesOnScreen();
	int LinesToScroll();
	int MaxScrollPos();
	SelectionPosition ClampPositionIntoDocument(SelectionPosition sp) const;
	Point LocationFromPosition(SelectionPosition pos);
	Point LocationFromPosition(int pos);
	int XFromPosition(int pos);
	int XFromPosition(SelectionPosition sp);
	SelectionPosition SPositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false, bool virtualSpace=true);
	int PositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false);
	SelectionPosition SPositionFromLineX(int lineDoc, int x);
	int PositionFromLineX(int line, int x);
	int LineFromLocation(Point pt) const;
	void SetTopLine(int topLineNew);

	bool AbandonPaint();
	void RedrawRect(PRectangle rc);
	void Redraw();
	void RedrawSelMargin(int line=-1, bool allAfter=false);
	PRectangle RectangleFromRange(int start, int end);
	void InvalidateRange(int start, int end);

	bool UserVirtualSpace() const {
		return ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0);
	}
	int CurrentPosition() const;
	bool SelectionEmpty() const;
	SelectionPosition SelectionStart();
	SelectionPosition SelectionEnd();
	void SetRectangularRange();
	void ThinRectangularRange();
	void InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection=false);
	void SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_);
	void SetSelection(int currentPos_, int anchor_);
	void SetSelection(SelectionPosition currentPos_);
	void SetSelection(int currentPos_);
	void SetEmptySelection(SelectionPosition currentPos_);
	void SetEmptySelection(int currentPos_);
	bool RangeContainsProtected(int start, int end) const;
	bool SelectionContainsProtected();
	int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true) const;
	SelectionPosition MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd=true) const;
	int MovePositionTo(SelectionPosition newPos, Selection::selTypes sel=Selection::noSel, bool ensureVisible=true);
	int MovePositionTo(int newPos, Selection::selTypes sel=Selection::noSel, bool ensureVisible=true);
	SelectionPosition MovePositionSoVisible(SelectionPosition pos, int moveDir);
	SelectionPosition MovePositionSoVisible(int pos, int moveDir);
	Point PointMainCaret();
	void SetLastXChosen();

	void ScrollTo(int line, bool moveThumb=true);
	virtual void ScrollText(int linesToMove);
	void HorizontalScrollTo(int xPos);
	void VerticalCentreCaret();
	void MoveSelectedLines(int lineDelta);
	void MoveSelectedLinesUp();
	void MoveSelectedLinesDown();
	void MoveCaretInsideView(bool ensureVisible=true);
	int DisplayFromPosition(int pos);

	struct XYScrollPosition {
		int xOffset;
		int topLine;
		XYScrollPosition(int xOffset_, int topLine_) : xOffset(xOffset_), topLine(topLine_) {}
		bool operator==(const XYScrollPosition &other) const {
			return (xOffset == other.xOffset) && (topLine == other.topLine);
		}
	};
	enum XYScrollOptions {
		xysUseMargin=0x1,
		xysVertical=0x2,
		xysHorizontal=0x4,
		xysDefault=xysUseMargin|xysVertical|xysHorizontal};
	XYScrollPosition XYScrollToMakeVisible(const SelectionRange range, const XYScrollOptions options);
	void SetXYScroll(XYScrollPosition newXY);
	void EnsureCaretVisible(bool useMargin=true, bool vert=true, bool horiz=true);
	void ScrollRange(SelectionRange range);
	void ShowCaretAtCurrentPosition();
	void DropCaret();
	void InvalidateCaret();
	virtual void UpdateSystemCaret();

	void NeedWrapping(int docLineStart = 0, int docLineEnd = wrapLineLarge);
	bool WrapOneLine(Surface *surface, int lineToWrap);
	bool WrapLines(bool fullWrap, int priorityWrapLineStart);
	void LinesJoin();
	void LinesSplit(int pixelWidth);

	int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) const;
	void PaintSelMargin(Surface *surface, PRectangle &rc);
	LineLayout *RetrieveLineLayout(int lineNumber);
	void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll,
		int width=LineLayout::wrapWidthInfinite);
	ColourDesired SelectionBackground(ViewStyle &vsDraw, bool main) const;
	ColourDesired TextBackground(ViewStyle &vsDraw, bool overrideBackground, ColourDesired background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) const;
	void DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight);
	void DrawWrapMarker(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourDesired wrapColour);
	void DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
		int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
		bool overrideBackground, ColourDesired background,
		bool drawWrapMark, ColourDesired wrapColour);
	void DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
		int xStart, PRectangle rcLine, LineLayout *ll, int subLine);
	void DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
		PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under);
	void DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
        PRectangle rcLine, LineLayout *ll, int subLine);
	void DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
		PRectangle rcLine, LineLayout *ll, int subLine);
	void DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
		int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour);
	void DrawCarets(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
		PRectangle rcLine, LineLayout *ll, int subLine);
	void RefreshPixMaps(Surface *surfaceWindow);
	void Paint(Surface *surfaceWindow, PRectangle rcArea);
	long FormatRange(bool draw, Sci_RangeToFormat *pfr);
	int TextWidth(int style, const char *text);

	virtual void SetVerticalScrollPos() = 0;
	virtual void SetHorizontalScrollPos() = 0;
	virtual bool ModifyScrollBars(int nMax, int nPage) = 0;
	virtual void ReconfigureScrollBars();
	void SetScrollBars();
	void ChangeSize();

	void FilterSelections();
	int InsertSpace(int position, unsigned int spaces);
	void AddChar(char ch);
	virtual void AddCharUTF(char *s, unsigned int len, bool treatAsDBCS=false);
	void InsertPaste(SelectionPosition selStart, const char *text, int len);
	void ClearSelection(bool retainMultipleSelections=false);
	void ClearAll();
	void ClearDocumentStyle();
	void Cut();
	void PasteRectangular(SelectionPosition pos, const char *ptr, int len);
	virtual void Copy() = 0;
	virtual void CopyAllowLine();
	virtual bool CanPaste();
	virtual void Paste() = 0;
	void Clear();
	void SelectAll();
	void Undo();
	void Redo();
	void DelChar();
	void DelCharBack(bool allowLineStartDeletion);
	virtual void ClaimSelection() = 0;

	virtual void NotifyChange() = 0;
	virtual void NotifyFocus(bool focus);
	virtual void SetCtrlID(int identifier);
	virtual int GetCtrlID() { return ctrlID; }
	virtual void NotifyParent(SCNotification scn) = 0;
	virtual void NotifyStyleToNeeded(int endStyleNeeded);
	void NotifyChar(int ch);
	void NotifySavePoint(bool isSavePoint);
	void NotifyModifyAttempt();
	virtual void NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt);
	void NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt);
	void NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt);
	void NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt);
	bool NotifyUpdateUI();
	void NotifyPainted();
	void NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt);
	bool NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt);
	void NotifyNeedShown(int pos, int len);
	void NotifyDwelling(Point pt, bool state);
	void NotifyZoom();

	void NotifyModifyAttempt(Document *document, void *userData);
	void NotifySavePoint(Document *document, void *userData, bool atSavePoint);
	void CheckModificationForWrap(DocModification mh);
	void NotifyModified(Document *document, DocModification mh, void *userData);
	void NotifyDeleted(Document *document, void *userData);
	void NotifyStyleNeeded(Document *doc, void *userData, int endPos);
	void NotifyLexerChanged(Document *doc, void *userData);
	void NotifyErrorOccurred(Document *doc, void *userData, int status);
	void NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam);

	void ContainerNeedsUpdate(int flags);
	void PageMove(int direction, Selection::selTypes sel=Selection::noSel, bool stuttered = false);
	enum { cmSame, cmUpper, cmLower } caseMap;
	virtual std::string CaseMapString(const std::string &s, int caseMapping);
	void ChangeCaseOfSelection(int caseMapping);
	void LineTranspose();
	void Duplicate(bool forLine);
	virtual void CancelModes();
	void NewLine();
	void CursorUpOrDown(int direction, Selection::selTypes sel=Selection::noSel);
	void ParaUpOrDown(int direction, Selection::selTypes sel=Selection::noSel);
	int StartEndDisplayLine(int pos, bool start);
	virtual int KeyCommand(unsigned int iMessage);
	virtual int KeyDefault(int /* key */, int /*modifiers*/);
	int KeyDownWithModifiers(int key, int modifiers, bool *consumed);
	int KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed=0);

	void Indent(bool forwards);

	virtual CaseFolder *CaseFolderForEncoding();
	long FindText(uptr_t wParam, sptr_t lParam);
	void SearchAnchor();
	long SearchText(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
	long SearchInTarget(const char *text, int length);
	void GoToLine(int lineNo);

	virtual void CopyToClipboard(const SelectionText &selectedText) = 0;
	std::string RangeText(int start, int end) const;
	void CopySelectionRange(SelectionText *ss, bool allowLineCopy=false);
	void CopyRangeToClipboard(int start, int end);
	void CopyText(int length, const char *text);
	void SetDragPosition(SelectionPosition newPos);
	virtual void DisplayCursor(Window::Cursor c);
	virtual bool DragThreshold(Point ptStart, Point ptNow);
	virtual void StartDrag();
	void DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular);
	void DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular);
	/** PositionInSelection returns true if position in selection. */
	bool PositionInSelection(int pos);
	bool PointInSelection(Point pt);
	bool PointInSelMargin(Point pt);
	Window::Cursor GetMarginCursor(Point pt) const;
	void TrimAndSetSelection(int currentPos_, int anchor_);
	void LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine);
	void WordSelection(int pos);
	void DwellEnd(bool mouseMoved);
	void MouseLeave();
	virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt);
	void ButtonMove(Point pt);
	void ButtonUp(Point pt, unsigned int curTime, bool ctrl);

	void Tick();
	bool Idle();
	virtual void SetTicking(bool on) = 0;
	virtual bool SetIdle(bool) { return false; }
	virtual void SetMouseCapture(bool on) = 0;
	virtual bool HaveMouseCapture() = 0;
	void SetFocusState(bool focusState);

	int PositionAfterArea(PRectangle rcArea) const;
	void StyleToPositionInView(Position pos);
	virtual void IdleWork();
	virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo=0);

	virtual bool PaintContains(PRectangle rc);
	bool PaintContainsMargin();
	void CheckForChangeOutsidePaint(Range r);
	void SetBraceHighlight(Position pos0, Position pos1, int matchStyle);

	void SetAnnotationHeights(int start, int end);
	virtual void SetDocPointer(Document *document);

	void SetAnnotationVisible(int visible);

	int ExpandLine(int line);
	void SetFoldExpanded(int lineDoc, bool expanded);
	void FoldLine(int line, int action);
	void FoldExpand(int line, int action, int level);
	int ContractedFoldNext(int lineStart) const;
	void EnsureLineVisible(int lineDoc, bool enforcePolicy);
	void FoldChanged(int line, int levelNow, int levelPrev);
	void NeedShown(int pos, int len);
	void FoldAll(int action);

	int GetTag(char *tagValue, int tagNumber);
	int ReplaceTarget(bool replacePatterns, const char *text, int length=-1);

	bool PositionIsHotspot(int position) const;
	bool PointIsHotspot(Point pt);
	void SetHotSpotRange(Point *pt);
	void GetHotSpotRange(int &hsStart, int &hsEnd) const;

	int CodePage() const;
	virtual bool ValidCodePage(int /* codePage */) const { return true; }
	int WrapCount(int line);
	void AddStyledText(char *buffer, int appendLength);

	virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) = 0;
	void StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
	sptr_t StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);

	static const char *StringFromEOLMode(int eolMode);

	static sptr_t StringResult(sptr_t lParam, const char *val);

public:
	// Public so the COM thunks can access it.
	bool IsUnicodeMode() const;
	// Public so scintilla_send_message can use it.
	virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
	// Public so scintilla_set_id can use it.
	int ctrlID;
	// Public so COM methods for drag and drop can set it.
	int errorStatus;
	friend class AutoSurface;
	friend class SelectionLineIterator;
};

/**
 * A smart pointer class to ensure Surfaces are set up and deleted correctly.
 */
class AutoSurface {
private:
	Surface *surf;
public:
	AutoSurface(Editor *ed, int technology = -1) : surf(0) {
		if (ed->wMain.GetID()) {
			surf = Surface::Allocate(technology != -1 ? technology : ed->technology);
			if (surf) {
				surf->Init(ed->wMain.GetID());
				surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage());
				surf->SetDBCSMode(ed->CodePage());
			}
		}
	}
	AutoSurface(SurfaceID sid, Editor *ed, int technology = -1) : surf(0) {
		if (ed->wMain.GetID()) {
			surf = Surface::Allocate(technology != -1 ? technology : ed->technology);
			if (surf) {
				surf->Init(sid, ed->wMain.GetID());
				surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage());
				surf->SetDBCSMode(ed->CodePage());
			}
		}
	}
	~AutoSurface() {
		delete surf;
	}
	Surface *operator->() const {
		return surf;
	}
	operator Surface *() const {
		return surf;
	}
};

#ifdef SCI_NAMESPACE
}
#endif

#endif
