
/*
 * This file is part of KDevelop
 *
 * Copyright 2007 David Nolden <david.nolden.kdevelop@art-master.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef CONTEXTBROWSERPLUGIN_H
#define CONTEXTBROWSERPLUGIN_H

#include <QtCore/QVariant>
#include <QSet>
#include <QMap>
#include <QList>
#include <ktexteditor/rangefeedback.h>
#include <interfaces/iplugin.h>
#include <language/duchain/duchainpointer.h>
#include <language/editor/simplecursor.h>
#include <language/duchain/declaration.h>
#include <KUrl>
#include <language/editor/persistentmovingrange.h>
#include <language/interfaces/iquickopen.h>
#include <QToolButton>
#include <QMenu>
#include <QHBoxLayout>
#include <language/editor/documentcursor.h>
#include <KTextEditor/Document>

namespace Sublime {
  class MainWindow;
}

namespace KDevelop {
  class IDocument;
  class ILanguage;
  class ParseJob;
  class DUContext;
  class TopDUContext;
  class DUChainBase;
  class AbstractNavigationWidget;
}

namespace KTextEditor {
  class View;
}

//TODO: move into kdevelop namespace
using namespace KDevelop;

class ContextBrowserViewFactory;
class ContextBrowserView;
class BrowseManager;

QWidget* masterWidget(QWidget* w);

struct ViewHighlights
{
  ViewHighlights() : keep(false) {
  }
  // Whether the same highlighting should be kept highlighted (usually during typing)
  bool keep;
  // The declaration that is highlighted for this view
  IndexedDeclaration declaration;
  // Highlighted ranges. Those may also be contained by different views.
  QList<PersistentMovingRange::Ptr> highlights;
};

class ContextBrowserPlugin : public KDevelop::IPlugin
{
    Q_OBJECT
  public:
    ContextBrowserPlugin(QObject *parent, const QVariantList & = QVariantList() );
    virtual ~ContextBrowserPlugin();

    virtual void unload();

    void registerToolView(ContextBrowserView* view);
    void unRegisterToolView(ContextBrowserView* view);
    
    virtual KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context*);

    virtual KXMLGUIClient* createGUIForMainWindow( Sublime::MainWindow* window );

    ///duchain must be locked
    ///@param force When this is true, the history-entry is added, no matter whether the context is "interesting" or not
    void updateHistory(KDevelop::DUContext* context, const KDevelop::SimpleCursor& cursorPosition,
                       bool force = false);

    void updateDeclarationListBox(KDevelop::DUContext* context);
    void setAllowBrowsing(bool allow);

  public Q_SLOTS:
    void previousContextShortcut();
    void nextContextShortcut();
    
    void startDelayedBrowsing(KTextEditor::View* view);
    void stopDelayedBrowsing();
    
    void previousUseShortcut();
    void nextUseShortcut();

    void declarationSelectedInUI(DeclarationPointer decl);

    void parseJobFinished(KDevelop::ParseJob* job);
    void textDocumentCreated( KDevelop::IDocument* document );
    void documentActivated( KDevelop::IDocument* );
    void viewDestroyed( QObject* obj );
    void cursorPositionChanged( KTextEditor::View* view, const KTextEditor::Cursor& newPosition );
    void viewCreated( KTextEditor::Document* , KTextEditor::View* );
    void updateViews();

    void textHintRequested(const KTextEditor::Cursor&, QString&);

    void hideTooTip();
    void findUses();
    
    void textInserted(KTextEditor::Document*, KTextEditor::Range);
    void selectionChanged(KTextEditor::View*);

  private slots:
    // history browsing
    void documentJumpPerformed( KDevelop::IDocument* newDocument,
                                const KTextEditor::Cursor& newCursor,
                                KDevelop::IDocument* previousDocument,
                                const KTextEditor::Cursor& previousCursor);

    void historyNext();
    void historyPrevious();
    void nextMenuAboutToShow();
    void previousMenuAboutToShow();
    void actionTriggered();

    void navigateLeft();
    void navigateRight();
    void navigateUp();
    void navigateDown();
    void navigateAccept();
    void navigateBack();

  private:
    QWidget* toolbarWidgetForMainWindow(Sublime::MainWindow* window);
    virtual void createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile,
                                            KActionCollection& actions);
    void switchUse(bool forward);
    void clearMouseHover();

    void addHighlight( KTextEditor::View* view, KDevelop::Declaration* decl );

    /** helper for updateBrowserView().
     *  Tries to find a 'specialLanguageObject' (eg macro) in @p view under cursor @c.
     *  If found returns true and sets @p pickedLanguage to the language this object belongs to */
    KDevelop::Declaration* findDeclaration(KTextEditor::View* view, const KDevelop::SimpleCursor&, bool mouseHighlight);
    void updateForView(KTextEditor::View* view);

    // history browsing
    bool isPreviousEntry(KDevelop::DUContext*, const KDevelop::SimpleCursor& cursor) const;
    QString actionTextFor(int historyIndex) const;
    void updateButtonState();
    void openDocument(int historyIndex);
    void fillHistoryPopup(QMenu* menu, const QList<int>& historyIndices);

    enum NavigationActionType {
      Accept,
      Back,
      Down,
      Up,
      Left,
      Right
    };
    void doNavigate(NavigationActionType action);

  private:
    
    // Returns the currently active and visible context browser view that belongs
    // to the same context (mainwindow and area) as the given widget
    ContextBrowserView* browserViewForWidget(QWidget* widget);
    
    void showToolTip(KTextEditor::View* view, KTextEditor::Cursor position);
    QTimer* m_updateTimer;
    
    //Contains the range, the old attribute, and the attribute it was replaced with
    QSet<KTextEditor::View*> m_updateViews;
    QMap<KTextEditor::View*, ViewHighlights > m_highlightedRanges;

    //Holds a list of all active context browser tool views
    QList<ContextBrowserView*> m_views;

    //Used to override the next declaration that will be highlighted
    IndexedDeclaration m_useDeclaration;
    IndexedDeclaration m_lastHighlightedDeclaration;

    KUrl m_mouseHoverDocument;
    SimpleCursor m_mouseHoverCursor;
    ContextBrowserViewFactory* m_viewFactory;
    QPointer<QWidget> m_currentToolTip;
    QPointer<QWidget> m_currentNavigationWidget;
    IndexedDeclaration m_currentToolTipDeclaration;
    QAction* m_findUses;
    
    QPointer<KTextEditor::Document> m_lastInsertionDocument;
    KTextEditor::Cursor m_lastInsertionPos;

    // outline toolbar
    QPointer<KDevelop::IQuickOpenLine> m_outlineLine;
    QPointer<QHBoxLayout> m_toolbarWidgetLayout;
    QPointer<QWidget> m_toolbarWidget;

    // history browsing
    struct HistoryEntry {
        //Duchain must be locked
        HistoryEntry(KDevelop::IndexedDUContext ctx = KDevelop::IndexedDUContext(), const KDevelop::SimpleCursor& cursorPosition = KDevelop::SimpleCursor());
        HistoryEntry(KDevelop::DocumentCursor pos);
        //Duchain must be locked
        void setCursorPosition(const KDevelop::SimpleCursor& cursorPosition);

        //Duchain does not need to be locked
        KDevelop::DocumentCursor computePosition() const;

        KDevelop::IndexedDUContext context;
        KDevelop::DocumentCursor absoluteCursorPosition;
        KDevelop::SimpleCursor relativeCursorPosition; //Cursor position relative to the start line of the context
        QString alternativeString;
    };

    QVector<HistoryEntry> m_history;
    QPointer<QToolButton> m_previousButton;
    QPointer<QToolButton> m_nextButton;
    QPointer<QMenu> m_previousMenu, m_nextMenu;
    QPointer<QToolButton> m_browseButton;
    QList<KDevelop::IndexedDeclaration> m_listDeclarations;
    KDevelop::IndexedString m_listUrl;
    BrowseManager* m_browseManager;
    //Used to not record jumps triggered by the context-browser as history entries
    QPointer<QWidget> m_focusBackWidget;
    int m_nextHistoryIndex;
};

#endif // CONTEXTBROWSERPLUGIN_H

// kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on
