/*
 * 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.
 */

#include "quickopenplugin.h"

#include <cassert>
#include <typeinfo>
#include <QtGui/QTreeView>
#include <QtGui/QHeaderView>
#include <QDialog>
#include <QKeyEvent>
#include <QApplication>
#include <QScrollBar>
#include <QCheckBox>
#include <QMetaObject>

#include <kbuttongroup.h>
#include <klocale.h>
#include <kpluginfactory.h>
#include <kpluginloader.h>
#include <kaboutdata.h>
#include <ktexteditor/document.h>
#include <ktexteditor/view.h>
#include <kparts/mainwindow.h>
#include <kactioncollection.h>
#include <kaction.h>
#include <kshortcut.h>
#include <kdebug.h>

#include <interfaces/ilanguage.h>
#include <interfaces/icore.h>
#include <interfaces/iuicontroller.h>
#include <interfaces/idocumentcontroller.h>
#include <interfaces/ilanguagecontroller.h>
#include <interfaces/iprojectcontroller.h>
#include <language/interfaces/ilanguagesupport.h>
#include <language/duchain/duchainutils.h>
#include <language/duchain/duchainlock.h>
#include <language/duchain/duchain.h>
#include <language/duchain/types/identifiedtype.h>
#include <language/duchain/indexedstring.h>
#include <language/duchain/types/functiontype.h>

#include "expandingtree/expandingdelegate.h"
#include "ui_quickopen.h"
#include "quickopenmodel.h"
#include "projectfilequickopen.h"
#include "projectitemquickopen.h"
#include "declarationlistquickopen.h"
#include "documentationquickopenprovider.h"
#include <language/duchain/functiondefinition.h>
#include <qmenu.h>
#include <qdesktopwidget.h>
#include <util/activetooltip.h>
#include <qboxlayout.h>
#include <language/util/navigationtooltip.h>
#include <interfaces/contextmenuextension.h>
#include <language/interfaces/codecontext.h>

using namespace KDevelop;

const bool noHtmlDestriptionInOutline = true;

class QuickOpenWidgetCreator {
  public:
    virtual ~QuickOpenWidgetCreator() {
    }
    virtual QuickOpenWidget* createWidget() = 0;
    virtual QString objectNameForLine() = 0;
    virtual void widgetShown() {
    }
};

class StandardQuickOpenWidgetCreator : public QuickOpenWidgetCreator {
  public:
    StandardQuickOpenWidgetCreator(const QStringList& items, const QStringList& scopes)
      : m_items(items)
      , m_scopes(scopes)
    {
    }

    virtual QString objectNameForLine() {
      return "Quickopen";
    }

    void setItems(const QStringList& scopes, const QStringList& items)
    {
      m_scopes = scopes;
      m_items = items;
    }

    virtual QuickOpenWidget* createWidget() {
      QStringList useItems = m_items;
      if(useItems.isEmpty())
        useItems = QuickOpenPlugin::self()->lastUsedItems;
      
      QStringList useScopes = m_scopes;
      if(useScopes.isEmpty())
        useScopes = QuickOpenPlugin::self()->lastUsedScopes;
      
      return new QuickOpenWidget( i18n("Quick Open"), QuickOpenPlugin::self()->m_model, QuickOpenPlugin::self()->lastUsedItems, useScopes, false, true );
    }
    
    QStringList m_items;
    QStringList m_scopes;
};

class QuickOpenDelegate : public ExpandingDelegate {
public:
  QuickOpenDelegate(ExpandingWidgetModel* model, QObject* parent = 0L) : ExpandingDelegate(model, parent) {
  }
  virtual QList<QTextLayout::FormatRange> createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const {
    QList<QVariant> highlighting = index.data(KTextEditor::CodeCompletionModel::CustomHighlight).toList();
    if(!highlighting.isEmpty())
      return highlightingFromVariantList(highlighting);
    return ExpandingDelegate::createHighlighting( index, option );
  }

};

class OutlineFilter : public DUChainUtils::DUChainItemFilter {
public:
  enum OutlineMode { Functions, FunctionsAndClasses };
  OutlineFilter(QList<DUChainItem>& _items, OutlineMode _mode = FunctionsAndClasses) : items(_items), mode(_mode) {
  }
  virtual bool accept(Declaration* decl) {
    if(decl->range().isEmpty())
      return false;
    bool collectable = mode == Functions ? decl->isFunctionDeclaration() : (decl->isFunctionDeclaration() || (decl->internalContext() && decl->internalContext()->type() == DUContext::Class));
    if (collectable) {
      DUChainItem item;
      item.m_item = IndexedDeclaration(decl);
      item.m_text = decl->toString();
      items << item;

      return true;
    } else
      return false;
  }
  virtual bool accept(DUContext* ctx) {
    if(ctx->type() == DUContext::Class || ctx->type() == DUContext::Namespace || ctx->type() == DUContext::Global || ctx->type() == DUContext::Other || ctx->type() == DUContext::Helper )
      return true;
    else
      return false;
  }
  QList<DUChainItem>& items;
  OutlineMode mode;
};

K_PLUGIN_FACTORY(KDevQuickOpenFactory, registerPlugin<QuickOpenPlugin>(); )
K_EXPORT_PLUGIN(KDevQuickOpenFactory(KAboutData("kdevquickopen","kdevquickopen", ki18n("Quick Open"), "0.1", ki18n("This plugin allows quick access to project files and language-items like classes/functions."), KAboutData::License_GPL)))

Declaration* cursorDeclaration() {
  IDocument* doc = ICore::self()->documentController()->activeDocument();
  if(!doc)
    return 0;

  KTextEditor::Document* textDoc = doc->textDocument();
  if(!textDoc)
    return 0;

  KTextEditor::View* view = textDoc->activeView();
  if(!view)
    return 0;

  KDevelop::DUChainReadLocker lock( DUChain::lock() );

  return DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor( doc->url(), SimpleCursor(view->cursorPosition()) ) );
}

///The first definition that belongs to a context that surrounds the current cursor
Declaration* cursorContextDeclaration() {
  IDocument* doc = ICore::self()->documentController()->activeDocument();
  if(!doc)
    return 0;

  KTextEditor::Document* textDoc = doc->textDocument();
  if(!textDoc)
    return 0;

  KTextEditor::View* view = textDoc->activeView();
  if(!view)
    return 0;

  KDevelop::DUChainReadLocker lock( DUChain::lock() );

  TopDUContext* ctx = DUChainUtils::standardContextForUrl(doc->url());
  if(!ctx)
    return 0;

  SimpleCursor cursor(view->cursorPosition());

  DUContext* subCtx = ctx->findContext(ctx->transformToLocalRevision(cursor));

  while(subCtx && !subCtx->owner())
    subCtx = subCtx->parentContext();

  Declaration* definition = 0;

  if(!subCtx || !subCtx->owner())
    definition = DUChainUtils::declarationInLine(cursor, ctx);
  else
    definition = subCtx->owner();

  if(!definition)
    return 0;

  return definition;
}

//Returns only the name, no template-parameters or scope
QString cursorItemText() {
  KDevelop::DUChainReadLocker lock( DUChain::lock() );

  Declaration* decl = cursorDeclaration();
  if(!decl)
    return QString();

  IDocument* doc = ICore::self()->documentController()->activeDocument();
  if(!doc)
    return QString();

  TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() );

  if( !context ) {
    kDebug() << "Got no standard context";
    return QString();
  }

  AbstractType::Ptr t = decl->abstractType();
  IdentifiedType* idType = dynamic_cast<IdentifiedType*>(t.unsafeData());
  if( idType && idType->declaration(context) )
    decl = idType->declaration(context);

  if(!decl->qualifiedIdentifier().isEmpty())
    return decl->qualifiedIdentifier().last().identifier().str();

  return QString();
}

QuickOpenLineEdit* QuickOpenPlugin::createQuickOpenLineWidget()
{
  return new QuickOpenLineEdit(new StandardQuickOpenWidgetCreator(QStringList(), QStringList()));
}

void QuickOpenWidget::showStandardButtons(bool show)
{
  if(show) {
    o.okButton->show();
    o.cancelButton->show();
  }else{
    o.okButton->hide();
    o.cancelButton->hide();
  }
}

QuickOpenWidget::QuickOpenWidget( QString title, QuickOpenModel* model, const QStringList& initialItems, const QStringList& initialScopes, bool listOnly, bool noSearchField ) : m_model(model), m_expandedTemporary(false) {
  m_filterTimer.setSingleShot(true);
  connect(&m_filterTimer, SIGNAL(timeout()), this, SLOT(applyFilter()));

  Q_UNUSED( title );
  o.setupUi( this );
  o.list->header()->hide();
  o.list->setRootIsDecorated( false );
  o.list->setVerticalScrollMode( QAbstractItemView::ScrollPerItem );
  
  connect(o.list->verticalScrollBar(), SIGNAL(valueChanged(int)), m_model, SLOT(placeExpandingWidgets()));

  o.searchLine->setFocus();

  o.list->setItemDelegate( new QuickOpenDelegate( m_model, o.list ) );

  if(!listOnly) {
    QStringList allTypes = m_model->allTypes();
    QStringList allScopes = m_model->allScopes();

    QMenu* itemsMenu = new QMenu;
    
    foreach( const QString &type, allTypes )
    {
      QAction* action = new QAction(type, itemsMenu);
      action->setCheckable(true);
      action->setChecked(initialItems.isEmpty() || initialItems.contains( type ));
      connect( action, SIGNAL(toggled(bool)), this, SLOT(updateProviders()), Qt::QueuedConnection );
      itemsMenu->addAction(action);
    }

    o.itemsButton->setMenu(itemsMenu);

    QMenu* scopesMenu = new QMenu;

    foreach( const QString &scope, allScopes )
    {
      QAction* action = new QAction(scope, scopesMenu);
      action->setCheckable(true);
      action->setChecked(initialScopes.isEmpty() || initialScopes.contains( scope ) );

      connect( action, SIGNAL(toggled(bool)), this, SLOT(updateProviders()), Qt::QueuedConnection );
      scopesMenu->addAction(action);
    }
    
    o.scopesButton->setMenu(scopesMenu);

  }else{
    o.list->setFocusPolicy(Qt::StrongFocus);
    o.scopesButton->hide();
    o.itemsButton->hide();
    o.label->hide();
    o.label_2->hide();
  }

  showSearchField(!noSearchField);
  
  o.okButton->hide();
  o.cancelButton->hide();
  
  o.searchLine->installEventFilter( this );
  o.list->installEventFilter( this );
  o.list->setFocusPolicy(Qt::NoFocus);
  o.scopesButton->setFocusPolicy(Qt::NoFocus);
  o.itemsButton->setFocusPolicy(Qt::NoFocus);

  connect( o.searchLine, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) );

  connect( o.list, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(doubleClicked(QModelIndex)) );

  connect(o.okButton, SIGNAL(clicked(bool)), this, SLOT(accept()));
  connect(o.okButton, SIGNAL(clicked(bool)), SIGNAL(ready()));
  connect(o.cancelButton, SIGNAL(clicked(bool)), SIGNAL(ready()));
  
  updateProviders();
  updateTimerInterval(true);
  
// no need to call this, it's done by updateProviders already
//   m_model->restart();
}

void QuickOpenWidget::updateTimerInterval(bool cheapFilterChange)
{
  const int MAX_ITEMS = 10000;
  if ( cheapFilterChange && m_model->rowCount(QModelIndex()) < MAX_ITEMS ) {
    // cheap change and there are currently just a few items,
    // so apply filter instantly
    m_filterTimer.setInterval(0);
  } else if ( m_model->unfilteredRowCount() < MAX_ITEMS ) {
    // not a cheap change, but there are generally
    // just a few items in the list: apply filter instantly
    m_filterTimer.setInterval(0);
  } else {
    // otherwise use a timer to prevent sluggishness while typing
    m_filterTimer.setInterval(300);
  }
}

void QuickOpenWidget::showEvent(QShowEvent* e)
{
    QWidget::showEvent(e);
    
    // The column width only has an effect _after_ the widget has been shown
    o.list->setColumnWidth( 0, 20 );
}

void QuickOpenWidget::setAlternativeSearchField(KLineEdit* alterantiveSearchField)
{
    o.searchLine = alterantiveSearchField;
    o.searchLine->installEventFilter( this );
    connect( o.searchLine, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) );
}


void QuickOpenWidget::showSearchField(bool b)
{
    if(b){
      o.searchLine->show();
      o.searchLabel->show();
    }else{
      o.searchLine->hide();
      o.searchLabel->hide();
    }
}

void QuickOpenWidget::prepareShow()
{
  o.list->setModel( 0 );
  o.list->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
  m_model->setTreeView( o.list );
  o.list->setModel( m_model );
  
  m_filterTimer.stop();
  m_filter = QString();
  
  if (!m_preselectedText.isEmpty())
  {
    o.searchLine->setText(m_preselectedText);
    o.searchLine->selectAll();
  }
  
  m_model->restart(false);
  
  connect( o.list->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
           SLOT(callRowSelected()) );
  connect( o.list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
           SLOT(callRowSelected()) );
}

void QuickOpenWidgetDialog::run() {
  m_widget->prepareShow();
  m_dialog->show();
}

QuickOpenWidget::~QuickOpenWidget() {
  m_model->setTreeView( 0 );
}


QuickOpenWidgetDialog::QuickOpenWidgetDialog(QString title, QuickOpenModel* model, const QStringList& initialItems, const QStringList& initialScopes, bool listOnly, bool noSearchField)
{
  m_widget = new QuickOpenWidget(title, model, initialItems, initialScopes, listOnly, noSearchField);
  // the QMenu might close on esc and we want to close the whole dialog then
  connect( m_widget, SIGNAL(aboutToHide()), this, SLOT(deleteLater()) );
  
  //KDialog always sets the focus on the "OK" button, so we use QDialog
  m_dialog = new QDialog( ICore::self()->uiController()->activeMainWindow() );
  m_dialog->resize(QSize(800, 400));

  m_dialog->setWindowTitle(title);
  QVBoxLayout* layout = new QVBoxLayout(m_dialog);
  layout->addWidget(m_widget);
  m_widget->showStandardButtons(true);
  connect(m_widget, SIGNAL(ready()), m_dialog, SLOT(close()));
  connect( m_dialog, SIGNAL(accepted()), m_widget, SLOT(accept()) );
}


QuickOpenWidgetDialog::~QuickOpenWidgetDialog()
{
  delete m_dialog;
}

void QuickOpenWidget::setPreselectedText(const QString& text)
{
  m_preselectedText = text;
}

void QuickOpenWidget::updateProviders() {
  if(QAction* action = qobject_cast<QAction*>(sender())) {
    QMenu* menu = qobject_cast<QMenu*>(action->parentWidget());
    if(menu) {
      menu->show();
      menu->setActiveAction(action);
    }
  }
  
  QStringList checkedItems;
  
  if(o.itemsButton->menu()) {
  
    foreach( QObject* obj, o.itemsButton->menu()->children() ) {
      QAction* box = qobject_cast<QAction*>( obj );
      if( box ) {
        if( box->isChecked() )
          checkedItems << box->text().remove('&');
      }
    }
    o.itemsButton->setText(checkedItems.join(", "));
  }

  QStringList checkedScopes;
  
  if(o.scopesButton->menu()) {
    
    foreach( QObject* obj, o.scopesButton->menu()->children() ) {
      QAction* box = qobject_cast<QAction*>( obj );
      if( box ) {
        if( box->isChecked() )
          checkedScopes << box->text().remove('&');
      }
    }
    
    o.scopesButton->setText(checkedScopes.join(", "));
  }

  emit itemsChanged( checkedItems );
  emit scopesChanged( checkedScopes );
  m_model->enableProviders( checkedItems, checkedScopes );
}

void QuickOpenWidget::textChanged( const QString& str )
{
  // "cheap" when something was just appended to the current filter
  updateTimerInterval(str.startsWith(m_filter));
  m_filter = str;
  m_filterTimer.start();
}

void QuickOpenWidget::applyFilter()
{
  m_model->textChanged( m_filter );

  QModelIndex currentIndex = m_model->index(0, 0, QModelIndex());
  o.list->selectionModel()->setCurrentIndex( currentIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current );

  callRowSelected();
}

void QuickOpenWidget::callRowSelected() {
  QModelIndex currentIndex = o.list->selectionModel()->currentIndex();
  if( currentIndex.isValid() )
    m_model->rowSelected( currentIndex );
  else
    kDebug() << "current index is not valid";
}

void QuickOpenWidget::accept() {
  QString filterText = o.searchLine->text();
  m_model->execute( o.list->currentIndex(), filterText );
}

void QuickOpenWidget::doubleClicked ( const QModelIndex & index ) {
  // crash guard: https://bugs.kde.org/show_bug.cgi?id=297178
  o.list->setCurrentIndex(index);
  QMetaObject::invokeMethod(this, "accept", Qt::QueuedConnection);
  QMetaObject::invokeMethod(this, "ready", Qt::QueuedConnection);
}

bool QuickOpenWidget::eventFilter ( QObject * watched, QEvent * event )
{
  QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event);
  
  if( event->type() == QEvent::KeyRelease ) {
    if(keyEvent->key() == Qt::Key_Alt) {
      if((m_expandedTemporary && m_altDownTime.msecsTo( QTime::currentTime() ) > 300) || (!m_expandedTemporary && m_altDownTime.msecsTo( QTime::currentTime() ) < 300 && m_hadNoCommandSinceAlt)) {
        //Unexpand the item
        QModelIndex row = o.list->selectionModel()->currentIndex();
        if( row.isValid() ) {
          row = row.sibling( row.row(), 0 );
          if(m_model->isExpanded( row ))
            m_model->setExpanded( row, false );
        }      
      }
      m_expandedTemporary = false;
    }
  }
  
  if( event->type() == QEvent::KeyPress  ) {
    m_hadNoCommandSinceAlt = false;
    if(keyEvent->key() == Qt::Key_Alt) {
      m_hadNoCommandSinceAlt = true;
      //Expand
      QModelIndex row = o.list->selectionModel()->currentIndex();
      if( row.isValid() ) {
        row = row.sibling( row.row(), 0 );
        m_altDownTime = QTime::currentTime();
        if(!m_model->isExpanded( row )) {
          m_expandedTemporary = true;
          m_model->setExpanded( row, true );
        }
      }      
    }

    switch( keyEvent->key() ) {
      case Qt::Key_Tab:
        if ( keyEvent->modifiers() == Qt::NoModifier ) {
          // Tab should work just like Down
          QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier));
          QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Down, Qt::NoModifier));
          return true;
        }
        break;
      case Qt::Key_Backtab:
         if ( keyEvent->modifiers() == Qt::ShiftModifier ) {
          // Shift + Tab should work just like Up
          QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier));
          QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Up, Qt::NoModifier));
          return true;
        }
        break;
      case Qt::Key_Down:
      case Qt::Key_Up:
      {
        if( keyEvent->modifiers() == Qt::AltModifier ) {
          QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex());
          if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface =
              dynamic_cast<KDevelop::QuickOpenEmbeddedWidgetInterface*>( w ) ){
            if( keyEvent->key() == Qt::Key_Down )
              interface->down();
            else
              interface->up();
            return true;
          }
          return false;
        }
      }
      case Qt::Key_PageUp:
      case Qt::Key_PageDown:
        if(watched == o.list )
          return false;
        QApplication::sendEvent( o.list, event );
      //callRowSelected();
        return true;

      case Qt::Key_Left: {
        //Expand/unexpand
        if( keyEvent->modifiers() == Qt::AltModifier ) {
          //Eventually Send action to the widget
          QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex());
          if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface =
              dynamic_cast<KDevelop::QuickOpenEmbeddedWidgetInterface*>( w ) ){
            interface->previous();
            return true;
          }
        } else {
          QModelIndex row = o.list->selectionModel()->currentIndex();
          if( row.isValid() ) {
            row = row.sibling( row.row(), 0 );

            if( m_model->isExpanded( row ) ) {
              m_model->setExpanded( row, false );
              return true;
            }
          }
        }
        return false;
      }
      case Qt::Key_Right: {
        //Expand/unexpand
        if( keyEvent->modifiers() == Qt::AltModifier ) {
          //Eventually Send action to the widget
          QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex());
          if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface =
              dynamic_cast<KDevelop::QuickOpenEmbeddedWidgetInterface*>( w ) ){
            interface->next();
            return true;
          }
        } else {
          QModelIndex row = o.list->selectionModel()->currentIndex();
          if( row.isValid() ) {
            row = row.sibling( row.row(), 0 );

            if( !m_model->isExpanded( row ) ) {
              m_model->setExpanded( row, true );
              return true;
            }
          }
        }
        return false;
      }
      case Qt::Key_Return:
      case Qt::Key_Enter: {
        if (m_filterTimer.isActive()) {
          m_filterTimer.stop();
          applyFilter();
        }
        if( keyEvent->modifiers() == Qt::AltModifier ) {
          //Eventually Send action to the widget
          QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex());
          if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface =
              dynamic_cast<KDevelop::QuickOpenEmbeddedWidgetInterface*>( w ) ){
            interface->accept();
            return true;
          }
        } else {
          QString filterText = o.searchLine->text();
          
          //Safety: Track whether this object is deleted. When execute() is called, a dialog may be opened,
          //which kills the quickopen widget.
          QPointer<QObject> stillExists(this);
          
          if( m_model->execute( o.list->currentIndex(), filterText ) ) {
            
            if(!stillExists)
              return true;
            
            if(!(keyEvent->modifiers() & Qt::ShiftModifier))
              emit ready();
          } else {
            //Maybe the filter-text was changed:
            if( filterText != o.searchLine->text() ) {
              o.searchLine->setText( filterText );
            }
          }
        }
        return true;
      }
    }
  }

  return false;
}


QuickOpenLineEdit* QuickOpenPlugin::quickOpenLine(QString name)
{
  QList< QuickOpenLineEdit* > lines = ICore::self()->uiController()->activeMainWindow()->findChildren<QuickOpenLineEdit*>(name);
  foreach(QuickOpenLineEdit* line, lines) {
    if(line->isVisible()) {
      return line;
    }
  }
  
  return 0;
}

static QuickOpenPlugin* staticQuickOpenPlugin = 0;

QuickOpenPlugin* QuickOpenPlugin::self()
{
  return staticQuickOpenPlugin;
}

void QuickOpenPlugin::createActionsForMainWindow(Sublime::MainWindow* /*window*/, QString& xmlFile, KActionCollection& actions)
{
    xmlFile ="kdevquickopen.rc";

    KAction* quickOpen = actions.addAction("quick_open");
    quickOpen->setText( i18n("&Quick Open") );
    quickOpen->setIcon( KIcon("quickopen") );
    quickOpen->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_Q );
    connect(quickOpen, SIGNAL(triggered(bool)), this, SLOT(quickOpen()));

    KAction* quickOpenFile = actions.addAction("quick_open_file");
    quickOpenFile->setText( i18n("Quick Open &File") );
    quickOpenFile->setIcon( KIcon("quickopen-file") );
    quickOpenFile->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_O );
    connect(quickOpenFile, SIGNAL(triggered(bool)), this, SLOT(quickOpenFile()));

    KAction* quickOpenClass = actions.addAction("quick_open_class");
    quickOpenClass->setText( i18n("Quick Open &Class") );
    quickOpenClass->setIcon( KIcon("quickopen-class") );
    quickOpenClass->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_C );
    connect(quickOpenClass, SIGNAL(triggered(bool)), this, SLOT(quickOpenClass()));

    KAction* quickOpenFunction = actions.addAction("quick_open_function");
    quickOpenFunction->setText( i18n("Quick Open &Function") );
    quickOpenFunction->setIcon( KIcon("quickopen-function") );
    quickOpenFunction->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_M );
    connect(quickOpenFunction, SIGNAL(triggered(bool)), this, SLOT(quickOpenFunction()));

    KAction* quickOpenAlreadyOpen = actions.addAction("quick_open_already_open");
    quickOpenAlreadyOpen->setText( i18n("Quick Open &Already Open File") );
    quickOpenAlreadyOpen->setIcon( KIcon("quickopen-file") );
    connect(quickOpenAlreadyOpen, SIGNAL(triggered(bool)), this, SLOT(quickOpenOpenFile()));

    KAction* quickOpenDocumentation = actions.addAction("quick_open_documentation");
    quickOpenDocumentation->setText( i18n("Quick Open &Documentation") );
    quickOpenDocumentation->setIcon( KIcon("quickopen-documentation") );
    quickOpenDocumentation->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_D );
    connect(quickOpenDocumentation, SIGNAL(triggered(bool)), this, SLOT(quickOpenDocumentation()));

    m_quickOpenDeclaration = actions.addAction("quick_open_jump_declaration");
    m_quickOpenDeclaration->setText( i18n("Jump to Declaration") );
    m_quickOpenDeclaration->setIcon( KIcon("go-jump-declaration" ) );
    m_quickOpenDeclaration->setShortcut( Qt::CTRL | Qt::Key_Period );
    connect(m_quickOpenDeclaration, SIGNAL(triggered(bool)), this, SLOT(quickOpenDeclaration()), Qt::QueuedConnection);

    m_quickOpenDefinition = actions.addAction("quick_open_jump_definition");
    m_quickOpenDefinition->setText( i18n("Jump to Definition") );
    m_quickOpenDefinition->setIcon( KIcon("go-jump-definition" ) );
    m_quickOpenDefinition->setShortcut( Qt::CTRL | Qt::Key_Comma );
    connect(m_quickOpenDefinition, SIGNAL(triggered(bool)), this, SLOT(quickOpenDefinition()), Qt::QueuedConnection);

    KAction* quickOpenLine = actions.addAction("quick_open_line");
    quickOpenLine->setText( i18n("Embedded Quick Open") );
//     quickOpenLine->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_E );
//     connect(quickOpenLine, SIGNAL(triggered(bool)), this, SLOT(quickOpenLine(bool)));
    quickOpenLine->setDefaultWidget(createQuickOpenLineWidget());

    KAction* quickOpenNextFunction = actions.addAction("quick_open_next_function");
    quickOpenNextFunction->setText( i18n("Next Function") );
    quickOpenNextFunction->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_PageDown );
    connect(quickOpenNextFunction, SIGNAL(triggered(bool)), this, SLOT(nextFunction()));

    KAction* quickOpenPrevFunction = actions.addAction("quick_open_prev_function");
    quickOpenPrevFunction->setText( i18n("Previous Function") );
    quickOpenPrevFunction->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_PageUp );
    connect(quickOpenPrevFunction, SIGNAL(triggered(bool)), this, SLOT(previousFunction()));

    KAction* quickOpenNavigateFunctions = actions.addAction("quick_open_outline");
    quickOpenNavigateFunctions->setText( i18n("Outline") );
    quickOpenNavigateFunctions->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_N );
    connect(quickOpenNavigateFunctions, SIGNAL(triggered(bool)), this, SLOT(quickOpenNavigateFunctions()));   
}

QuickOpenPlugin::QuickOpenPlugin(QObject *parent,
                                 const QVariantList&)
    : KDevelop::IPlugin(KDevQuickOpenFactory::componentData(), parent)
{
    staticQuickOpenPlugin = this;
    KDEV_USE_EXTENSION_INTERFACE( KDevelop::IQuickOpen )
    m_model = new QuickOpenModel( 0 );

    KConfigGroup quickopengrp = KGlobal::config()->group("QuickOpen");
    lastUsedScopes = quickopengrp.readEntry("SelectedScopes", QStringList() << i18n("Project") << i18n("Includes") << i18n("Includers") << i18n("Currently Open") );
    lastUsedItems = quickopengrp.readEntry("SelectedItems", QStringList() );

    {
      m_openFilesData = new OpenFilesDataProvider();
      QStringList scopes, items;
      scopes << i18n("Currently Open");
      items << i18n("Files");
      m_model->registerProvider( scopes, items, m_openFilesData );
    }
    {
      m_projectFileData = new ProjectFileDataProvider();
      QStringList scopes, items;
      scopes << i18n("Project");
      items << i18n("Files");
      m_model->registerProvider( scopes, items, m_projectFileData );
    }
    {
      m_projectItemData = new ProjectItemDataProvider(this);
      QStringList scopes, items;
      scopes << i18n("Project");
      items << ProjectItemDataProvider::supportedItemTypes();
      m_model->registerProvider( scopes, items, m_projectItemData );
    }
    {
      m_documentationItemData = new DocumentationQuickOpenProvider;
      QStringList scopes, items;
      scopes << i18n("Includes");
      items << i18n("Documentation");
      m_model->registerProvider( scopes, items, m_documentationItemData );
    }
}

QuickOpenPlugin::~QuickOpenPlugin()
{
  freeModel();

  delete m_model;
  delete m_projectFileData;
  delete m_projectItemData;
  delete m_openFilesData;
  delete m_documentationItemData;
}

void QuickOpenPlugin::unload()
{
}

ContextMenuExtension QuickOpenPlugin::contextMenuExtension(Context* context)
{
  KDevelop::ContextMenuExtension menuExt = KDevelop::IPlugin::contextMenuExtension( context );

  KDevelop::DeclarationContext *codeContext = dynamic_cast<KDevelop::DeclarationContext*>(context);

  if (!codeContext)
      return menuExt;

  DUChainReadLocker readLock;
  Declaration* decl(codeContext->declaration().data());

  if (decl) {
    const bool isDef = FunctionDefinition::definition(decl);
    if (codeContext->use().isValid() || !isDef) {
      menuExt.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, m_quickOpenDeclaration);
    }

    if(isDef) {
      menuExt.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, m_quickOpenDefinition);
    }
  }

  return menuExt;
}

void QuickOpenPlugin::showQuickOpen(const QStringList& items)
{
  if(!freeModel())
    return;

  QStringList initialItems = items;

  QStringList useScopes = lastUsedScopes;

  if (!useScopes.contains(i18n("Currently Open")))
    useScopes << i18n("Currently Open");

  showQuickOpenWidget(initialItems, useScopes, false);
}

void QuickOpenPlugin::showQuickOpen( ModelTypes modes )
{
  if(!freeModel())
    return;

  QStringList initialItems;
  if( modes & Files || modes & OpenFiles )
    initialItems << i18n("Files");

  if( modes & Functions )
    initialItems << i18n("Functions");

  if( modes & Classes )
    initialItems << i18n("Classes");
  
  QStringList useScopes;
  if ( modes != OpenFiles )
    useScopes = lastUsedScopes;
  
  if((modes & OpenFiles) && !useScopes.contains(i18n("Currently Open")))
    useScopes << i18n("Currently Open");

  bool preselectText = (!(modes & Files) || modes == QuickOpenPlugin::All);
  showQuickOpenWidget(initialItems, useScopes, preselectText);
}

void QuickOpenPlugin::showQuickOpenWidget(const QStringList& items, const QStringList& scopes, bool preselectText)
{
  QuickOpenWidgetDialog* dialog = new QuickOpenWidgetDialog( i18n("Quick Open"), m_model, items, scopes );
  m_currentWidgetHandler = dialog;
  if (preselectText)
  {
    KDevelop::IDocument *currentDoc = core()->documentController()->activeDocument();
    if (currentDoc && currentDoc->isTextDocument())
    {
      QString preselected = currentDoc->textSelection().isEmpty() ? currentDoc->textWord() : currentDoc->textDocument()->text(currentDoc->textSelection());
      dialog->widget()->setPreselectedText(preselected);
    }
  }

  connect( dialog->widget(), SIGNAL(scopesChanged(QStringList)), this, SLOT(storeScopes(QStringList)) );
  //Not connecting itemsChanged to storeItems, as showQuickOpen doesn't use lastUsedItems and so shouldn't store item changes
  //connect( dialog->widget(), SIGNAL(itemsChanged(QStringList)), this, SLOT(storeItems(QStringList)) );
  dialog->widget()->o.itemsButton->setEnabled(false);

  if(quickOpenLine()) {
    quickOpenLine()->showWithWidget(dialog->widget());
    dialog->deleteLater();
  }else{
    dialog->run();
  }
}

void QuickOpenPlugin::storeScopes( const QStringList& scopes )
{
  lastUsedScopes = scopes;
  KConfigGroup grp = KGlobal::config()->group("QuickOpen");
  grp.writeEntry( "SelectedScopes", scopes );
}

void QuickOpenPlugin::storeItems( const QStringList& items )
{
  lastUsedItems = items;
  KConfigGroup grp = KGlobal::config()->group("QuickOpen");
  grp.writeEntry( "SelectedItems", items );
}

void QuickOpenPlugin::quickOpen()
{
  if(quickOpenLine()) //Same as clicking on Quick Open
    quickOpenLine()->setFocus();
  else
    showQuickOpen( All );
}

void QuickOpenPlugin::quickOpenFile()
{
  showQuickOpen( (ModelTypes)(Files | OpenFiles) );
}

void QuickOpenPlugin::quickOpenFunction()
{
  showQuickOpen( Functions );
}

void QuickOpenPlugin::quickOpenClass()
{
  showQuickOpen( Classes );
}

void QuickOpenPlugin::quickOpenOpenFile()
{
  showQuickOpen( OpenFiles );
}

void QuickOpenPlugin::quickOpenDocumentation()
{
  showQuickOpenWidget(QStringList(i18n("Documentation")), QStringList(i18n("Includes")), true);
}

QSet<KDevelop::IndexedString> QuickOpenPlugin::fileSet() const {
  return m_model->fileSet();
}

void QuickOpenPlugin::registerProvider( const QStringList& scope, const QStringList& type, KDevelop::QuickOpenDataProviderBase* provider )
{
  m_model->registerProvider( scope, type, provider );
}

bool QuickOpenPlugin::removeProvider( KDevelop::QuickOpenDataProviderBase* provider )
{
  m_model->removeProvider( provider );
  return true;
}

void QuickOpenPlugin::quickOpenDeclaration()
{
  if(jumpToSpecialObject())
    return;

  KDevelop::DUChainReadLocker lock( DUChain::lock() );
  Declaration* decl = cursorDeclaration();

  if(!decl) {
    kDebug() << "Found no declaration for cursor, cannot jump";
    return;
  }
  decl->activateSpecialization();

  IndexedString u = decl->url();
  SimpleCursor c = decl->rangeInCurrentRevision().start;

  if(u.str().isEmpty()) {
    kDebug() << "Got empty url for declaration" << decl->toString();
    return;
  }

  lock.unlock();
  core()->documentController()->openDocument(KUrl(u.str()), c.textCursor());
}

///Returns all languages for that url that have a language support, and prints warnings for other ones.
QList<KDevelop::ILanguage*> languagesWithSupportForUrl(KUrl url) {
  QList<KDevelop::ILanguage*> languages = ICore::self()->languageController()->languagesForUrl(url);
  QList<KDevelop::ILanguage*> ret;
  foreach( KDevelop::ILanguage* language, languages) {
    if(language->languageSupport()) {
      ret << language;
    }else{
      kDebug() << "got no language-support for language" << language->name();
    }
  }
  return ret;
}

QWidget* QuickOpenPlugin::specialObjectNavigationWidget() const
{
  if( !ICore::self()->documentController()->activeDocument() || !ICore::self()->documentController()->activeDocument()->textDocument() || !ICore::self()->documentController()->activeDocument()->textDocument()->activeView() )
    return 0;

  KUrl url = ICore::self()->documentController()->activeDocument()->url();

  foreach( KDevelop::ILanguage* language, languagesWithSupportForUrl(url) ) {
    QWidget* w = language->languageSupport()->specialLanguageObjectNavigationWidget(url, SimpleCursor(ICore::self()->documentController()->activeDocument()->textDocument()->activeView()->cursorPosition()) );
    if(w)
      return w;
  }

  return 0;
}

QPair<KUrl, SimpleCursor> QuickOpenPlugin::specialObjectJumpPosition() const {
  if( !ICore::self()->documentController()->activeDocument() || !ICore::self()->documentController()->activeDocument()->textDocument() || !ICore::self()->documentController()->activeDocument()->textDocument()->activeView() )
    return qMakePair(KUrl(), SimpleCursor());

  KUrl url = ICore::self()->documentController()->activeDocument()->url();

  foreach( KDevelop::ILanguage* language, languagesWithSupportForUrl(url) ) {
    QPair<KUrl, SimpleCursor> pos = language->languageSupport()->specialLanguageObjectJumpCursor(url, SimpleCursor(ICore::self()->documentController()->activeDocument()->textDocument()->activeView()->cursorPosition()) );
    if(pos.second.isValid()) {
      return pos;
    }
  }

  return qMakePair(KUrl(), SimpleCursor::invalid());
}

bool QuickOpenPlugin::jumpToSpecialObject()
{
  QPair<KUrl, SimpleCursor> pos = specialObjectJumpPosition();
  if(pos.second.isValid()) {
    if(pos.first.isEmpty()) {
      kDebug() << "Got empty url for special language object";
      return false;
    }

    ICore::self()->documentController()->openDocument(pos.first, pos.second.textCursor());
    return true;
  }
  return false;
}

void QuickOpenPlugin::quickOpenDefinition()
{
  if(jumpToSpecialObject())
    return;

  KDevelop::DUChainReadLocker lock( DUChain::lock() );
  Declaration* decl = cursorDeclaration();

  if(!decl) {
    kDebug() << "Found no declaration for cursor, cannot jump";
    return;
  }

  IndexedString u = decl->url();
  SimpleCursor c = decl->rangeInCurrentRevision().start;
  if(FunctionDefinition* def = FunctionDefinition::definition(decl)) {
    def->activateSpecialization();
    u = def->url();
    c = def->rangeInCurrentRevision().start;
  }else{
    kDebug() << "Found no definition for declaration";
    decl->activateSpecialization();
  }

  if(u.str().isEmpty()) {
    kDebug() << "Got empty url for declaration" << decl->toString();
    return;
  }

  lock.unlock();
  core()->documentController()->openDocument(KUrl(u.str()), c.textCursor());
}

bool QuickOpenPlugin::freeModel()
{
  if(m_currentWidgetHandler)
    delete m_currentWidgetHandler;
  m_currentWidgetHandler = 0;

  return true;
}

void QuickOpenPlugin::nextFunction()
{
  jumpToNearestFunction(NextFunction);
}

void QuickOpenPlugin::previousFunction()
{
  jumpToNearestFunction(PreviousFunction);
}

void QuickOpenPlugin::jumpToNearestFunction(QuickOpenPlugin::FunctionJumpDirection direction)
{
  IDocument* doc = ICore::self()->documentController()->activeDocument();
  if(!doc) {
    kDebug() << "No active document";
    return;
  }

  KDevelop::DUChainReadLocker lock( DUChain::lock() );

  TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() );

  if( !context ) {
    kDebug() << "Got no standard context";
    return;
  }

  QList<DUChainItem> items;
  OutlineFilter filter(items, OutlineFilter::Functions);
  DUChainUtils::collectItems( context, filter );

  CursorInRevision cursor = context->transformToLocalRevision(SimpleCursor(doc->cursorPosition()));
  if (!cursor.isValid())
    return;

  Declaration *nearestDeclBefore = 0;
  int distanceBefore = INT_MIN;
  Declaration *nearestDeclAfter = 0;
  int distanceAfter = INT_MAX;

  for (int i = 0; i < items.count(); ++i) {
    Declaration *decl = items[i].m_item.data();

    int distance = decl->range().start.line - cursor.line;
    if (distance < 0 && distance >= distanceBefore) {
      distanceBefore = distance;
      nearestDeclBefore = decl;
    } else if (distance > 0 && distance <= distanceAfter) {
      distanceAfter = distance;
      nearestDeclAfter = decl;
    }
  }

  CursorInRevision c = CursorInRevision::invalid();
  if (direction == QuickOpenPlugin::NextFunction && nearestDeclAfter)
    c = nearestDeclAfter->range().start;
  else if (direction == QuickOpenPlugin::PreviousFunction && nearestDeclBefore)
    c = nearestDeclBefore->range().start;

  KTextEditor::Cursor textCursor = KTextEditor::Cursor::invalid();
  if (c.isValid())
    textCursor = context->transformFromLocalRevision(c).textCursor();

  lock.unlock();
  if (textCursor.isValid())
    core()->documentController()->openDocument(doc->url(), textCursor);
  else
    kDebug() << "No declaration to jump to";
}


struct CreateOutlineDialog {
  CreateOutlineDialog() : dialog(0), cursorDecl(0), model(0) {
  }
  
  void start() {
    if(!QuickOpenPlugin::self()->freeModel())
        return;

      IDocument* doc = ICore::self()->documentController()->activeDocument();
      if(!doc) {
        kDebug() << "No active document";
        return;
      }

      KDevelop::DUChainReadLocker lock( DUChain::lock() );

      TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() );

      if( !context ) {
        kDebug() << "Got no standard context";
        return;
      }

      model = new QuickOpenModel(0);

      OutlineFilter filter(items);

      DUChainUtils::collectItems( context, filter );

      if(noHtmlDestriptionInOutline) {
        for(int a = 0; a < items.size(); ++a)
          items[a].m_noHtmlDestription = true;
      }

      cursorDecl = cursorContextDeclaration();

      model->registerProvider( QStringList(), QStringList(), new DeclarationListDataProvider(QuickOpenPlugin::self(), items, true) );

      dialog = new QuickOpenWidgetDialog( i18n("Outline"), model, QStringList(), QStringList(), true );
      
      model->setParent(dialog->widget());    
  }
  void finish() {
    //Select the declaration that contains the cursor
    if(cursorDecl && dialog) {
      int num = 0;
      foreach(const DUChainItem& item, items) {
        if(item.m_item.data() == cursorDecl) {
          dialog->widget()->o.list->setCurrentIndex( model->index(num,0,QModelIndex()) );
          dialog->widget()->o.list->scrollTo( model->index(num,0,QModelIndex()), QAbstractItemView::PositionAtCenter );
        }
        ++num;
      }
    }
  }
    QPointer<QuickOpenWidgetDialog> dialog;
    Declaration* cursorDecl;
    QList<DUChainItem> items;
    QuickOpenModel* model;
};

class OutlineQuickopenWidgetCreator : public QuickOpenWidgetCreator {
  public:
    OutlineQuickopenWidgetCreator(QStringList /*scopes*/, QStringList /*items*/) : m_creator(0) {
    }
    
    ~OutlineQuickopenWidgetCreator() {
      delete m_creator;
    }
    
    virtual QuickOpenWidget* createWidget() {
      delete m_creator;
      m_creator = new CreateOutlineDialog;
      m_creator->start();
      
      if(!m_creator->dialog)
        return 0;
      
      m_creator->dialog->deleteLater();
      return m_creator->dialog->widget();
    }
    
    virtual void widgetShown() {
      if(m_creator) {
        m_creator->finish();
        delete m_creator;
        m_creator = 0;
      }
    }
    
    virtual QString objectNameForLine() {
      return "Outline";
    }
    
    CreateOutlineDialog* m_creator;
};

void QuickOpenPlugin::quickOpenNavigateFunctions()
{
  CreateOutlineDialog create;
  create.start();
  
  if(!create.dialog)
    return;
  
  m_currentWidgetHandler = create.dialog;
  
  QuickOpenLineEdit* line = quickOpenLine("Outline");
  if(!line)
    line  = quickOpenLine();
  
  if(line) {
    line->showWithWidget(create.dialog->widget());
    create.dialog->deleteLater();
  }else
    create.dialog->run();
  
  create.finish();
}

QuickOpenLineEdit::QuickOpenLineEdit(QuickOpenWidgetCreator* creator) : m_widget(0), m_forceUpdate(false), m_widgetCreator(creator) {
    setMinimumWidth(200);
    setMaximumWidth(400);

    deactivate();
    setDefaultText(i18n("Quick Open..."));
    setToolTip(i18n("Search for files, classes, functions and more,"
                    " allowing you to quickly navigate in your source code."));
    setObjectName(m_widgetCreator->objectNameForLine());
    setFocusPolicy(Qt::ClickFocus);
}

QuickOpenLineEdit::~QuickOpenLineEdit() {
    delete m_widget;
    delete m_widgetCreator;
}

bool QuickOpenLineEdit::insideThis(QObject* object) {
    while (object)
    {
        kDebug() << object;
        if (object == this || object == m_widget)
        {
            return true;
        }
        object = object->parent();
    }
    return false;
}

void QuickOpenLineEdit::widgetDestroyed(QObject* obj)
{
  Q_UNUSED(obj);
  deactivate();
}

void QuickOpenLineEdit::showWithWidget(QuickOpenWidget* widget)
{
  connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
  kDebug() << "storing widget" << widget;
  deactivate();
  if(m_widget) {
    kDebug() << "deleting" << m_widget;
    delete m_widget;
  }
  m_widget = widget;
  m_forceUpdate = true;
  setFocus();
}

void QuickOpenLineEdit::focusInEvent(QFocusEvent* ev) {
    QLineEdit::focusInEvent(ev);
//       delete m_widget;
    kDebug() << "got focus";
    kDebug() << "old widget" << m_widget << "force update:" << m_forceUpdate;
    if (m_widget && !m_forceUpdate)
        return;

    if (!m_forceUpdate && !QuickOpenPlugin::self()->freeModel()) {
        deactivate();
        return;
    }
    
    m_forceUpdate = false;
    
    if(!m_widget)
    {
      m_widget = m_widgetCreator->createWidget();
      if(!m_widget) {
        deactivate();
        return;
      }
    }

    activate();

    m_widget->showStandardButtons(false);
    m_widget->showSearchField(false);
    
    m_widget->setParent(0, Qt::ToolTip);
    m_widget->setFocusPolicy(Qt::NoFocus);
    m_widget->setAlternativeSearchField(this);
    
    QuickOpenPlugin::self()->m_currentWidgetHandler = m_widget;
    connect(m_widget, SIGNAL(ready()), SLOT(deactivate()));

    connect( m_widget, SIGNAL(scopesChanged(QStringList)), QuickOpenPlugin::self(), SLOT(storeScopes(QStringList)) );
    connect( m_widget, SIGNAL(itemsChanged(QStringList)), QuickOpenPlugin::self(), SLOT(storeItems(QStringList)) );
    Q_ASSERT(m_widget->o.searchLine == this);
    m_widget->prepareShow();
    QRect widgetGeometry = QRect(mapToGlobal(QPoint(0, height())), mapToGlobal(QPoint(width(), height() + 400)));
    widgetGeometry.setWidth(700); ///@todo Waste less space
    QRect screenGeom = QApplication::desktop()->screenGeometry(this);
    if (widgetGeometry.right() > screenGeom.right()) {
      widgetGeometry.moveRight(screenGeom.right());
    }
    if (widgetGeometry.bottom() > screenGeom.bottom()) {
      widgetGeometry.moveBottom(mapToGlobal(QPoint(0, 0)).y());
    }
    m_widget->setGeometry(widgetGeometry);
    m_widget->show();
    
    m_widgetCreator->widgetShown();
}

void QuickOpenLineEdit::hideEvent(QHideEvent* ev)
{
    QWidget::hideEvent(ev);
    if(m_widget)
      QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection);
//       deactivate();
}

bool QuickOpenLineEdit::eventFilter(QObject* obj, QEvent* e) {
    if (!m_widget)
        return false;

    switch (e->type()) {
    case QEvent::KeyPress:
    case QEvent::ShortcutOverride:
      if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
          deactivate();
          e->accept();
          return true;
      }
      break;
     case QEvent::WindowActivate:
    case QEvent::WindowDeactivate:
        kDebug() << "closing because of window activation";
        deactivate();
        break;
    // handle bug 260657 - "Outline menu doesn't follow main window on its move"
    case QEvent::Move: {
            QWidget* widget = qobject_cast<QWidget*>(obj);
            Q_ASSERT(widget);
            // close the outline menu in case a parent widget moved
            if (widget->isAncestorOf(this)) {
              kDebug() << "closing because of parent widget move";
              deactivate();
            }
            break;
        }
    case QEvent::FocusIn:
        if (dynamic_cast<QWidget*>(obj)) {
            QFocusEvent* focusEvent = dynamic_cast<QFocusEvent*>(e);
            Q_ASSERT(focusEvent);
            //Eat the focus event, keep the focus
            kDebug() << "focus change" << "inside this: " << insideThis(obj) << "this" << this << "obj" << obj;
            if(obj == this)
              return false;
            
            kDebug() << "reason" << focusEvent->reason();
            if (focusEvent->reason() != Qt::MouseFocusReason && focusEvent->reason() != Qt::ActiveWindowFocusReason) {
                QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection);
                return false;
            }
            if (!insideThis(obj))
                deactivate();
        }
        break;
    default:
        break;
    }
    return false;
}
void QuickOpenLineEdit::activate() {
    kDebug() << "activating";
    setText("");
    setStyleSheet("");
    qApp->installEventFilter(this);
}
void QuickOpenLineEdit::deactivate() {
    kDebug() << "deactivating";
    
    clear();

    if(m_widget || hasFocus())
      QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection);
    
   if (m_widget)
        m_widget->deleteLater();
    
    m_widget = 0;
    qApp->removeEventFilter(this);
    
}

void QuickOpenLineEdit::checkFocus()
{
    kDebug() << "checking focus" << m_widget;
    if(m_widget) {
      if(isVisible() && !isHidden())
        setFocus();
      else
        deactivate();
    }else{
       if (ICore::self()->documentController()->activeDocument())
           ICore::self()->documentController()->activateDocument(ICore::self()->documentController()->activeDocument());
       
       //Make sure the focus is somewehre else, even if there is no active document
       setEnabled(false);
       setEnabled(true);
    }
}

IQuickOpenLine* QuickOpenPlugin::createQuickOpenLine(const QStringList& scopes, const QStringList& type, IQuickOpen::QuickOpenType kind)
{
  if(kind == Outline)
    return new QuickOpenLineEdit(new OutlineQuickopenWidgetCreator(scopes, type));
  else
    return new QuickOpenLineEdit(new StandardQuickOpenWidgetCreator(scopes, type));
}

#include "quickopenplugin.moc"

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