/* This file is part of the KDE libraries
   Copyright (C) 2002-2007 Hamish Rodda <rodda@kde.org>
   Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
   Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
   Copyright (C) 2002 Christoph Cullmann <cullmann@kde.org>
   Copyright (C) 2007 Mirko Stocker <me@misto.ch>

   Based on:
     KWriteView : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#ifndef _KATE_VIEW_INTERNAL_
#define _KATE_VIEW_INTERNAL_

#include <ktexteditor/attribute.h>
#include <ktexteditor/rangefeedback.h>

#include "katesmartcursor.h"
#include "katelinelayout.h"
#include "katetextline.h"
#include "katedocument.h"

#include <QtCore/QPoint>
#include <QtCore/QTimer>
#include <QtGui/QDrag>
#include <QtGui/QWidget>
#include <QtCore/QSet>
#include <QtCore/QPointer>

class KateView;
class KateIconBorder;
class KateScrollBar;
class KateSmartRange;
class KateTextLayout;

class QScrollBar;

class KateViewInternal : public QWidget, private KTextEditor::SmartRangeWatcher
{
    Q_OBJECT

    friend class KateView;
    friend class KateIconBorder;
    friend class KateScrollBar;
    friend class CalculatingCursor;
    friend class BoundedCursor;
    friend class WrappingCursor;

  public:
    enum Bias
    {
        left  = -1,
        none  =  0,
        right =  1
    };

  public:
    KateViewInternal ( KateView *view, KateDocument *doc );
    ~KateViewInternal ();

  //BEGIN EDIT STUFF
  public:
    void editStart ();
    void editEnd (int editTagLineStart, int editTagLineEnd, bool tagFrom);

    void editSetCursor (const KTextEditor::Cursor &cursor);

  private:
    uint editSessionNumber;
    bool editIsRunning;
    KTextEditor::Cursor editOldCursor;
  //END

  //BEGIN TAG & CLEAR & UPDATE STUFF
  public:
    bool tagLine (const KTextEditor::Cursor& virtualCursor);

    bool tagLines (int start, int end, bool realLines = false);
    // cursors not const references as they are manipulated within
    bool tagLines (KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors = false);

    bool tagRange(const KTextEditor::Range& range, bool realCursors);

    void tagAll ();

    void relayoutRange(const KTextEditor::Range& range, bool realCursors = true);

    void updateDirty();

    void clear ();

  Q_SIGNALS:
    // Trigger this signal whenever you want to call updateView() and may not be in the same thread.
    void requestViewUpdate(bool changed);
  //END

  private Q_SLOTS:
    // Updates the view and requests a redraw.
    void updateView (bool changed = false, int viewLinesScrolled = 0);

  private:
    // Actually performs the updating, but doesn't call update().
    void doUpdateView(bool changed = false, int viewLinesScrolled = 0);
    void makeVisible (const KTextEditor::Cursor& c, int endCol, bool force = false, bool center = false, bool calledExternally = false);

  public:
    // Start Position is a virtual cursor
    inline const KTextEditor::Cursor& startPos() const { return m_startPos; }
    inline int startLine () const { return m_startPos.line(); }
    inline int startX () const { return m_startX; }

    KTextEditor::Cursor endPos () const;
    int endLine () const;

    KateTextLayout yToKateTextLayout(int y) const;

    void prepareForDynWrapChange();
    void dynWrapChanged();

    KateView *view () { return m_view; }

  public Q_SLOTS:
    void slotIncFontSizes();
    void slotDecFontSizes();

  private Q_SLOTS:
    void scrollLines(int line); // connected to the sliderMoved of the m_lineScroll
    void scrollViewLines(int offset);
    void scrollAction(int action);
    void scrollNextPage();
    void scrollPrevPage();
    void scrollPrevLine();
    void scrollNextLine();
    void scrollColumns (int x); // connected to the valueChanged of the m_columnScroll
    void viewSelectionChanged ();

  public:
    void doReturn();
    void doDelete();
    void doBackspace();
    void doTranspose();
    void doDeleteWordLeft();
    void doDeleteWordRight();

    void cursorLeft(bool sel=false);
    void cursorRight(bool sel=false);
    void wordLeft(bool sel=false);
    void wordRight(bool sel=false);
    void home(bool sel=false);
    void end(bool sel=false);
    void cursorUp(bool sel=false);
    void cursorDown(bool sel=false);
    void cursorToMatchingBracket(bool sel=false);
    void scrollUp();
    void scrollDown();
    void topOfView(bool sel=false);
    void bottomOfView(bool sel=false);
    void pageUp(bool sel=false);
    void pageDown(bool sel=false);
    void top(bool sel=false);
    void bottom(bool sel=false);
    void top_home(bool sel=false);
    void bottom_end(bool sel=false);

    KTextEditor::Cursor getCursor() const;
    QPoint cursorToCoordinate(const KTextEditor::Cursor& cursor, bool realCursor = true, bool includeBorder = true) const;
    QPoint cursorCoordinates(bool includeBorder = true) const;

  // EVENT HANDLING STUFF - IMPORTANT
  private:
    void fixDropEvent(QDropEvent *event);
  protected:
    virtual void hideEvent(QHideEvent* e);
    virtual void paintEvent(QPaintEvent *e);
    virtual bool eventFilter( QObject *obj, QEvent *e );
    virtual void keyPressEvent( QKeyEvent* );
    virtual void keyReleaseEvent( QKeyEvent* );
    virtual void resizeEvent( QResizeEvent* );
    virtual void mousePressEvent(       QMouseEvent* );
    virtual void mouseDoubleClickEvent( QMouseEvent* );
    virtual void mouseReleaseEvent(     QMouseEvent* );
    virtual void mouseMoveEvent(        QMouseEvent* );
    virtual void leaveEvent( QEvent* );
    virtual void dragEnterEvent( QDragEnterEvent* );
    virtual void dragMoveEvent( QDragMoveEvent* );
    virtual void dropEvent( QDropEvent* );
    virtual void showEvent ( QShowEvent *);
    virtual void wheelEvent(QWheelEvent* e);
    virtual void focusInEvent (QFocusEvent *);
    virtual void focusOutEvent (QFocusEvent *);
    virtual void inputMethodEvent(QInputMethodEvent* e);

    void contextMenuEvent ( QContextMenuEvent * e );

  private Q_SLOTS:
    void tripleClickTimeout();

  Q_SIGNALS:
    // emitted when KateViewInternal is not handling its own URI drops
    void dropEventPass(QDropEvent*);

  private Q_SLOTS:
    void slotRegionVisibilityChangedAt(unsigned int);
    void slotRegionBeginEndAddedRemoved(unsigned int);
    void slotCodeFoldingChanged();

  private:
    void moveChar( Bias bias, bool sel );
    void moveEdge( Bias bias, bool sel );
    KTextEditor::Cursor maxStartPos(bool changed = false);
    void scrollPos(KTextEditor::Cursor& c, bool force = false, bool calledExternally = false);
    void scrollLines( int lines, bool sel );

    int linesDisplayed() const;

    int lineToY(int viewLine) const;

    void updateSelection( const KTextEditor::Cursor&, bool keepSel );
    void updateCursor( const KTextEditor::Cursor& newCursor, bool force = false, bool center = false, bool calledExternally = false );
    void updateBracketMarks();

    void paintCursor();

    void placeCursor( const QPoint& p, bool keepSelection = false, bool updateSelection = true );
    bool isTargetSelected( const QPoint& p );

    void doDrag();

    inline KateView* view() const { return m_view; }
    KateRenderer* renderer() const;

    KateView *m_view;
    KateDocument* m_doc;
    class KateIconBorder *m_leftBorder;

    int m_mouseX;
    int m_mouseY;
    int m_scrollX;
    int m_scrollY;

    Qt::CursorShape m_mouseCursor;

    KateSmartCursor m_cursor;
    KTextEditor::Cursor m_mouse;
    KTextEditor::Cursor m_displayCursor;
    int m_cursorX;

    bool m_possibleTripleClick;

    // Bracket mark and corresponding decorative ranges
    KateSmartRange *m_bm, *m_bmStart, *m_bmEnd;
    bool m_bmHighlighted;
    void updateBracketMarkAttributes();

    enum DragState { diNone, diPending, diDragging };

    struct _dragInfo {
      DragState    state;
      QPoint       start;
      QDrag*   dragObject;
    } m_dragInfo;

    uint m_iconBorderHeight;

    //
    // line scrollbar + first visible (virtual) line in the current view
    //
    KateScrollBar *m_lineScroll;
    QWidget* m_dummy;

    // These are now cursors to account for word-wrap.
    // Start Position is a virtual cursor
    KateSmartCursor m_startPos;

    // This is set to false on resize or scroll (other than that called by makeVisible),
    // so that makeVisible is again called when a key is pressed and the cursor is in the same spot
    bool m_madeVisible;
    bool m_shiftKeyPressed;

    // How many lines to should be kept visible above/below the cursor when possible
    void setAutoCenterLines(int viewLines, bool updateView = true);
    int m_autoCenterLines;
    int m_minLinesVisible;

    //
    // column scrollbar + x position
    //
    QScrollBar *m_columnScroll;
    int m_startX;

    // has selection changed while your mouse or shift key is pressed
    bool m_selChangedByUser;
    KTextEditor::Cursor m_selectAnchor;

    enum SelectionMode { Default=0, Mouse, Word, Line }; ///< for drag selection.
    uint m_selectionMode;
    // when drag selecting after double/triple click, keep the initial selected
    // word/line independent of direction.
    // They get set in the event of a double click, and is used with mouse move + leftbutton
    KTextEditor::Range m_selectionCached;

    // maximal length of textlines visible from given startLine
    int maxLen(int startLine);

    // are we allowed to scroll columns?
    bool columnScrollingPossible ();

    // returns the maximum X value / col value a cursor can take for a specific line range
    int lineMaxCursorX(const KateTextLayout& line);
    int lineMaxCol(const KateTextLayout& line);

    class KateLayoutCache* cache() const;
    KateLayoutCache* m_layoutCache;

    // convenience methods
    KateTextLayout currentLayout() const;
    KateTextLayout previousLayout() const;
    KateTextLayout nextLayout() const;

    // find the cursor offset by (offset) view lines from a cursor.
    // when keepX is true, the column position will be calculated based on the x
    // position of the specified cursor.
    KTextEditor::Cursor viewLineOffset(const KTextEditor::Cursor& virtualCursor, int offset, bool keepX = false);

    KTextEditor::Cursor toRealCursor(const KTextEditor::Cursor& virtualCursor) const;
    KTextEditor::Cursor toVirtualCursor(const KTextEditor::Cursor& realCursor) const;

    // These variable holds the most recent maximum real & visible column number
    bool m_preserveMaxX;
    int m_currentMaxX;

    bool m_usePlainLines; // accept non-highlighted lines if this is set

    inline KateTextLine::Ptr textLine( int realLine ) const
    {
      if (m_usePlainLines)
        return m_doc->plainKateTextLine(realLine);
      else
        return m_doc->kateTextLine(realLine);
    }

    bool m_updatingView;
    int m_wrapChangeViewLine;
    KTextEditor::Cursor m_cachedMaxStartPos;

  private Q_SLOTS:
    void doDragScroll();
    void startDragScroll();
    void stopDragScroll();

  private:
    // Timers
    QTimer m_dragScrollTimer;
    QTimer m_scrollTimer;
    QTimer m_cursorTimer;
    QTimer m_textHintTimer;

    static const int s_scrollTime = 30;
    static const int s_scrollMargin = 16;

  private Q_SLOTS:
    void scrollTimeout ();
    void cursorTimeout ();
    void textHintTimeout ();

  //TextHint
 public:
   void enableTextHints(int timeout);
   void disableTextHints();

 private:
   bool m_textHintEnabled;
   int m_textHintTimeout;
   int m_textHintMouseX;
   int m_textHintMouseY;

  /**
   * IM input stuff
   */
  public:
    virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;

  private:
    KTextEditor::SmartRange* m_imPreedit;

  // Arbitrary highlighting
  public:
    void addHighlightRange(KTextEditor::SmartRange* range);
    void removeHighlightRange(KTextEditor::SmartRange* range);

  private:
    // Dynamic highlighting
    struct DynamicRangeHL {
      DynamicRangeHL(KateSmartRange* top);
      ~DynamicRangeHL();

      KateSmartRange* top;
      bool isView;
      KateSmartRange* caretOver;
      KateSmartRange* mouseOver;
      QHash<KateSmartRange*, QPointer<class KateDynamicAnimation> > caretAnimations;
      QHash<KateSmartRange*, QPointer<KateDynamicAnimation> > mouseAnimations;
    };

    void mouseMoved();
    void cursorMoved();
    void dynamicMoved(bool mouse);
    void startDynamic(DynamicRangeHL* hl, KateSmartRange* range, KTextEditor::Attribute::ActivationType type);
    void endDynamic(DynamicRangeHL* hl, KateSmartRange* range, KTextEditor::Attribute::ActivationType type);

  private:
    // Overrides for watched highlighting ranges
    void rangePositionChanged(KTextEditor::SmartRange* range);
    void rangeDeleted(KTextEditor::SmartRange* range);
    void childRangeInserted(KTextEditor::SmartRange* range, KTextEditor::SmartRange* child);
    void childRangeRemoved(KTextEditor::SmartRange* range, KTextEditor::SmartRange* child);
    void rangeAttributeChanged(KTextEditor::SmartRange* range, KTextEditor::Attribute::Ptr currentAttribute, KTextEditor::Attribute::Ptr previousAttribute);

  public Q_SLOTS:
    void dynamicHighlightAdded(KateSmartRange* range);
    void dynamicHighlightRemoved(KateSmartRange* range);
    void rangeDeleted(KateSmartRange* range);

    void updateRange(KateSmartRange* range);

  private:
    QHash<KateSmartRange*, DynamicRangeHL*> m_dynamicHighlights;
    bool m_smartDirty;

    void removeWatcher(KTextEditor::SmartRange* range, KTextEditor::SmartRangeWatcher* watcher);
    void addWatcher(KTextEditor::SmartRange* range, KTextEditor::SmartRangeWatcher* watcher);
    int m_watcherCount1, m_watcherCount3;
};

#endif

// kate: space-indent on; indent-width 2; replace-tabs on;
