//
// ksyntaxmle.h
//
// written by Michael Riedel <Michael.Riedel@gmx.de>
//

#ifndef SYNTAX_MLINED_H
#define SYNTAX_MLINED_H

#include <qmlined.h>
#include <qstring.h>
#include <qlist.h>

#include "Action.h"
#include "Colorizer.h"
#include "EditorSettings.h"

#define NONE      0
#define BACKWARD  1
#define FORWARD   2

class KEdSrch;
class KEdReplace;
class KEdGotoLine;
class StateCache;
class KSyntaxMultiLineEdit;



///////////////////////////////////////////////////////////////////
class Marker
{
public:
	Marker(KSyntaxMultiLineEdit* edit)
	{ 
		Edit = edit; 
		unset(); 
	}
	
	bool isSet() const
	{ return AnchorX != DragX || AnchorY != DragY; }
	void unset()
	{ setMarkBase(0, 0); }
		
	void setMarkBase(int posy, int posx);
	void extendMark(int posy, int posx);
	
	int anchorX() const { return AnchorX; }
	int anchorY() const { return AnchorY; }
	int dragX() const { return DragX; }
	int dragY() const { return DragY; }
	
	bool getMark(int& x1, int& y1, int& x2, int& y2) const
	{
	    if(AnchorY < DragY)
	    {
	        y1 = AnchorY;
	        y2 = DragY;
	        x1 = AnchorX;
	        x2 = DragX;
	    }
	    else if(AnchorY > DragY)
	    {
	        y1 = DragY;
	        y2 = AnchorY;
	        x1 = DragX;
	        x2 = AnchorX;
	    }
	    else // AnchorY == DragY
	    {
	        y1 = y2 = AnchorY;
	        x1 = QMIN(AnchorX, DragX);
	        x2 = QMAX(AnchorX, DragX);
	    }
	    return isSet();
	}
	
	
protected:
	int AnchorX, 
		AnchorY;
	int DragX, 
		DragY;
	KSyntaxMultiLineEdit* Edit;
};
///////////////////////////////////////////////////////////////////




	// constants for loadFile() method:
#define EDITOR_OPEN_REPLACE 0	// replace actually loaded file
#define EDITOR_OPEN_INSERT  1	// insert file content at actual cursor position

/**
	KSyntaxMultiLineEdit is a widget that is able to show a text file
	with colored sections. It has the following features:
		* simple to use as a replacement for the QMultiLineEdit
		* configurable syntax highlighting
		* multiple undo and redo function

	Be aware that the only functions that produce entries in the
	undo list are (besides the internal input functions) the UFXXX()
	methods; use these in order to perform an undoable action (e.g.
	use UFPaste() instead of paste()!
*/
class KSyntaxMultiLineEdit : public QMultiLineEdit
{
	Q_OBJECT
	friend class Marker;

public:
		/** 
			This creates a new KSyntaxMultiLineEdit.
			
			@param mode: mode is a string that contains the name of the mode (rule) file 
		 				 to be loaded and used to do the syntax coloring. this name 
		 				 includes the path and extension of the rule file.
			@param parent is passed on to the constructor of QMultiLineEdit and specifies the 
						 parent widget of the newly created one.
			@param name is passed on to the constructor of QMultiLineEdit and specifies the
						 name of the newly created widget.
		*/
    KSyntaxMultiLineEdit(const QString& mode, QWidget *parent=0, const char *name=0);
    	/**
    		Destructor for KSyntaxMultiLineEdit.
    	*/
    virtual ~KSyntaxMultiLineEdit();

        /** Returns the number of lines visible (even partially) in the actual window. */
	int  numVisibleLines() const;
	
		/** 
			Sets a new region of marked text.
			@param startx is the starting col of the new marked region
			@param starty is the starting row of the new marked region
			@param endx is the ending col of the new marked region
			@param endy is the ending row of the new marked region
		*/
	void setMark(int startx, int starty, int endx, int endy);
	void setCursorPosition(int row, int col, bool mark = false);
	virtual void insertAt(const char* s, int line, int col);

		/** 
			Returns a reference to the current settings of the editor widget.
			For more information on the settings see section @ref EditorSettings.
		*/
	EditorSettings& settings();
	
		/**
			Changes the settings of the KSyntaxMultiLineEdit to es. Does not call
			updateFromSettings() to make changes visible.
			For more information on the settings see section @ref EditorSettings.
			@param es settings to apply to the editor widget.
		*/
	void setSettings(const EditorSettings& es);
	
		/**
			Sets the syntax coloring mode to mode. updateFromSettings() is
			called to show current text using the new mode.
			@param mode mode file string; same format as mode parameter for 
			constructor.
		*/
	void setNewMode(const QString& mode);
	
		/**
			Must be called in order to make any changes in the settings of the
			editor widget become visible. Also needs to be called after changing
			the current syntax coloring mode.
		*/
	void updateFromSettings();  
	
		/** Returns the current state of the editor's dirty flag: */
	inline bool dirty();

    bool loadFile(const QString& filename, int mode = EDITOR_OPEN_REPLACE);
    bool saveTo(const QString& filename);
    
    void debugOutUL() { UndoRedoList.debugOut(); }
    Colorizer* getColorizer() { return colorizer; }

    
public slots:
	void Search();
	void GotoLine();
	void Replace();
	void clear();
	void setText(const char*);

		//////////////////////////////////////////////
    	// Methods that can be assigned to a keystroke
	void UFStartOfText()  {_UFStartOfText(false); }		// Cursor movement
	void UFEndOfText()	  {_UFEndOfText(false); }
	void UFStartOfLine()  {_UFStartOfLine(false); }
	void UFEndOfLine()    {_UFEndOfLine(false); }
	void UFWordLeft()     {_UFWordLeft(false); }
	void UFWordRight()    {_UFWordRight(false); }
	void UFCursorLeft()   {_UFCursorLeft(false); }
	void UFCursorRight()  {_UFCursorRight(false); }
	void UFCursorUp()     {_UFCursorUp(false); }
	void UFCursorDown()   {_UFCursorDown(false); }
	void UFPageDown()     {_UFPageDown(false); }
	void UFPageUp()       {_UFPageUp(false); }

	void UFStartOfTextM() {_UFStartOfText(true); }		// CursorMovement with marking
	void UFEndOfTextM()   {_UFEndOfText(true); }
	void UFStartOfLineM() {_UFStartOfLine(true); }
	void UFEndOfLineM()   {_UFEndOfLine(true); }
	void UFWordLeftM()    {_UFWordLeft(true); }
	void UFWordRightM()   {_UFWordRight(true); }
	void UFCursorLeftM()  {_UFCursorLeft(true); }
	void UFCursorRightM() {_UFCursorRight(true); }
	void UFCursorUpM()    {_UFCursorUp(true); }
	void UFCursorDownM()  {_UFCursorDown(true); }
	void UFPageDownM()    {_UFPageDown(true); }
	void UFPageUpM()      {_UFPageUp(true); }

	void UFNewLine();									// Editing actions
	void UFKillLine();
	void UFPaste();
	void UFCopy();
	void UFCut();
	void UFDelete();
	void UFBackspace();
	void UFUndo();
	void UFRedo();
	void UFInsertToggle();


signals:
	void cursorPosChanged();
	void textChanged();
	void insModeChanged();
	void dirtyChanged();


protected:
    virtual void paintCell(QPainter*, int row, int col);
    virtual void keyPressEvent(QKeyEvent*);
    virtual void mouseDoubleClickEvent(QMouseEvent*);
    virtual void mousePressEvent(QMouseEvent*);
    virtual void mouseMoveEvent(QMouseEvent*);
    virtual void mouseReleaseEvent(QMouseEvent*);
    virtual void timerEvent(QTimerEvent *);

    virtual void wordLeft(bool mark, bool wrap = true);
    virtual void wordRight(bool mark, bool wrap = true);
    virtual void pageUp(bool mark);
    virtual void pageDown(bool mark);

    inline int   tabStopDist(const QFontMetrics &fm);
    int          textWidthWithTabs(const QFontMetrics &fm,const char *s,int nChars);
    int          xPosToCursorPos(const char *s, const QFontMetrics &fm,
			    				 int xPos, int width);
    int          doSearch(QString s_pattern, bool case_sensitive, 
						  bool wildcard, bool forward, int line, int col);
    int          repeatSearch();
	int          doReplace(QString s_pattern, bool case_sensitive, 
	   		    		   bool wildcard, bool forward, int line, int col, bool replace_all);


		/**
			sets or clears the dirty flag; also emits
	        the dirtyChanged() signal to make it possible for 
	        external objects to update any visual representation
	        of this flag.
	    */    
	inline void  setDirty(bool d = true);
	
	void         makeCursorVisibleImmediatly();
	void         smle_markWord(int posx, int posy);
	void         handleReadOnlyKey(QKeyEvent* e);
	int          lineLength(int row) const;
	void         drawText(QPainter* p, int x, int y, int w, int h, int tf, const char* str, int len = -1);
	void 		 setFontInfo(QPainter& p, int state);
	void 		 updateAllVisibleRowsFrom(int row);
	void 		 updateAllNewStateVisibleRowsFrom(int from, int to);
    void 		 inputChar(char c);
	void 		 inputTab();
	void 		 initColorHandler(const char* rule_file);
	void         initStateCache();
	void         deleteStateCache();

//*	void 		 updateCellWidth();
	virtual void cursorLeft(bool mark, bool wrap = true);
	virtual void cursorRight(bool mark, bool wrap = true);
	virtual void cursorUp(bool mark);
	virtual void cursorDown(bool mark);
	
	void 		 extendMark(int posy, int posx, bool c = false);
	bool 		 getMarkedRegion(int* ay, int* ax, int* dy, int* dx);
	void 		 updateMarkedLines(int oldMarkedLine, int newMarkedLine);
	bool 	     hasMarkedText() { return mark.isSet(); }
    QString      markedText() const;
    
    	// Text changing virtual methods:
    	/**
    	    This function creates a new line of text; when the actual
    	    cursor position is at the end of a line, a new line is
    	    inserted after the current line; when the cursor is in the
    	    middle of a line of text, this line is splitted and the
    	    part to the right of the cursor is moved to the new line.
    	    The flag doAutoIndent indicates, wether auto indentation
    	    shoud be performed (when activated via @ref EditorSettings)
    	    or not. 
    	*/
    virtual void newLine(bool doAutoIndent = true);
        
        /**
            Deletes the contents of the actual line from the actual
            cursor position to the end of the line.
        */
    virtual void killLine();
    
        /**
            Inserts character c into the text at the current cursor
            position and moves the cursor one character to the right.
        */
	virtual void insertChar(char c);
	
	    /**
	        Deletes the char left to the cursor and moves cursor on 
	        char to the left.
	    */
	virtual void backspace();
	
	    /**
	        Deletes the char rightmost to the cursor; the cursor 
	        position remains unchanged.
	    */
	virtual void del();

		// Cursor movement methods with marking flag as param:
	void _UFStartOfText(bool mark);
	void _UFEndOfText(bool mark);
	void _UFStartOfLine(bool mark);
	void _UFEndOfLine(bool mark);
	void _UFWordLeft(bool mark);
	void _UFWordRight(bool mark);
	void _UFCursorLeft(bool mark);
	void _UFCursorRight(bool mark);
	void _UFCursorUp(bool mark);
	void _UFCursorDown(bool mark);
	void _UFPageDown(bool mark);
	void _UFPageUp(bool mark);

	
	UndoList       UndoRedoList; // List with undo/redo data
	EditorSettings edSettings;	 // settings: autoindent, tabsize, font...
	Colorizer*     colorizer;	 // handles colors for Keywords etc.
	StateCache*    stateCache;	 // Cache for coloring
	Marker		   mark;		 // contains information about position of mark in text
	QString		   Mode;	 	 // Name of rule file without path and extention
	bool		   Dirty;	 	 // true, when text is changed since last save or load or construction


protected slots:
	void cut();					 // these are protected. use UFXXX() instead
	void copyText();
	void paste();
	
	    /** 
	    	Connected to the textChanged() signal to set the dirty flag 
	        according to any changes made in the text
		*/
	void textChangedSlot();

        // for use with search and replace dlgs:
	void searchSlot();
	void searchDoneSlot();
	void replaceSlot();
	void replaceAllSlot();
	void replaceDoneSlot();
	void replaceSearchSlot();


private:
	int  blinkTimerId;
	bool smle_cursorOn;
	bool actuallyMarking;        // true, if user is actually marking text with mouse
	int  oldMark;                // internal var for marking with mouse
	int  scrollingTimer;         // timer for scrolling when marking with mouse
	int  charsDrawn;			 // internal drawing variable
	int  drawXPos;				 // internal drawing variable


		// search/replace shit:
	int         last_search;     //-> to search dlg
	int         last_replace;    //-> to search dlg
	bool        can_replace;
	int         replace_all_line;
	int         replace_all_col;
	
	QString     pattern;		  // needed?

	KEdReplace*  replace_dialog; // pointer to replace dialog
	KEdSrch*     srchdialog;     // pointer to find dialog
	KEdGotoLine* gotodialog;     // pointer to goto dialog


		// Friend classes:
	friend class Action;
	friend class BackspaceAction;
	friend class DeleteAction;
	friend class CharInputAction;
	friend class ClipboardAction;
	friend class CursorMovementAction;
	friend class CursorPlacementAction;
	friend class NewlineAction;

	int 		 cursor2View(int x, int y);
	int 		 view2Cursor(int ix, int y);
};



inline int KSyntaxMultiLineEdit::numVisibleLines() const
{
	if(cellHeight())
		return height() / cellHeight();
	else
		return 1;
}

inline void KSyntaxMultiLineEdit::setSettings(const EditorSettings& es)
{
	edSettings = es;
}

inline EditorSettings&  KSyntaxMultiLineEdit::settings()
{
	return edSettings;
}

inline void KSyntaxMultiLineEdit::setDirty(bool d = true) 
{ 
	Dirty = d; 
    emit dirtyChanged();
}
   
inline bool KSyntaxMultiLineEdit::dirty()
{
	return Dirty;
}

#endif // SYNTAX_MLINED_H

