/***************************************************************************
                           plugin_katetabbarextension.cpp
                           -------------------
    begin                : 2004-04-20
    copyright            : (C) 2004 by Dominik Haumann
    email                : dhdev@gmx.de
 ***************************************************************************/

/***************************************************************************
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU 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.
 ***************************************************************************/


// BEGIN INCLUDES
#include "plugin_katetabbarextension.h"

#include <tdeaction.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <tdeglobalsettings.h>

#include <kdebug.h>
#include <tdetoolbar.h>
#include <tdeconfig.h>
#include <kiconloader.h>
#include <tdefiledialog.h>

#include <tqpushbutton.h>
#include <tqlayout.h>
#include <tqtooltip.h>
#include <tqgroupbox.h>
#include <tqcheckbox.h>
// END

class PluginView : public KXMLGUIClient
{
  friend class KatePluginTabBarExtension;

  public:
    Kate::MainWindow *win;
    KateTabBarExtension* tabbar;
};

extern "C"
{
	KDE_EXPORT void* init_libkatetabbarextensionplugin()
	{
		TDEGlobal::locale()->insertCatalogue("katetabbarextension");
		return new KatePluginFactory;
	}
}

TQObject* KatePluginFactory::createObject(TQObject *parent,
     const char *name, const char*, const TQStringList&)
{
  return new KatePluginTabBarExtension(parent, name);
}

TDEInstance* KatePluginFactory::s_instance = 0L;

// BEGIN KatePluginTabBarExtension
KatePluginTabBarExtension::KatePluginTabBarExtension(TQObject *parent, const char *name)
  : Kate::Plugin((Kate::Application*)parent, name),
    pConfig(new TDEConfig("katetabbarextensionpluginrc"))
{
  pConfig->setGroup("global");
}

KatePluginTabBarExtension::~KatePluginTabBarExtension()
{
  while (m_views.count() > 0)
  {
    removeView(m_views.at(0)->win);
  }

  delete pConfig;
}

void KatePluginTabBarExtension::addView(Kate::MainWindow *win)
{
  PluginView *view = new PluginView ();

  bool bHoriz              = pConfig->readBoolEntry("horizontal orientation", true);
  bool bSort               = pConfig->readBoolEntry("sort", true);
  bool bCloseOnMiddleClick = pConfig->readBoolEntry("closeOnMiddleClick", false);

  view->tabbar = new KateTabBarExtension(application()->documentManager(),
                     win, bHoriz, bSort, bCloseOnMiddleClick, 0, "tabs_hbox");

  new KWidgetAction(view->tabbar, "tab_bar_widget",
      TDEShortcut::null(), 0, 0, view->actionCollection(), "tabbar_widget");

  view->setInstance (new TDEInstance("kate"));
  view->setXMLFile("plugins/katetabbarextension/ui.rc");
  win->guiFactory()->addClient (view);
  view->win = win;

  m_views.append (view);

  TDEToolBar* toolbar = dynamic_cast<TDEToolBar*>
      (win->guiFactory()->container("tabbarExtensionToolBar", view));
  if (toolbar) {
    connect(toolbar, TQT_SIGNAL(orientationChanged(Qt::Orientation)),
        view->tabbar, TQT_SLOT(slotMoved(Qt::Orientation)));
  }
}

void KatePluginTabBarExtension::removeView(Kate::MainWindow *win)
{
  for (uint z=0; z < m_views.count(); z++) {
    if (m_views.at(z)->win == win)
    {
      PluginView *view = m_views.at(z);

      // the last tabbar, save options
      if (m_views.count() == 1)
      {
        pConfig->writeEntry("horizontal orientation",
            view->tabbar->orientation()==Qt::Horizontal?true:false);
        pConfig->writeEntry("sort", view->tabbar->sortByName());
        pConfig->writeEntry("closeOnMiddleClick", view->tabbar->closeOnMiddleClick());
        pConfig->sync();
      }

      m_views.remove (view);
      win->guiFactory()->removeClient (view);
      delete view->tabbar;
      delete view;
    }
  }
}

Kate::PluginConfigPage* KatePluginTabBarExtension::configPage(
    uint, TQWidget *w, const char* /*name*/)
{
  KateTabBarExtensionConfigPage* p = new KateTabBarExtensionConfigPage(this, w);
  initConfigPage( p );
  connect( p, TQT_SIGNAL(configPageApplyRequest(KateTabBarExtensionConfigPage*)),
      TQT_SLOT(applyConfig(KateTabBarExtensionConfigPage*)) );
  return (Kate::PluginConfigPage*)p;
}

void KatePluginTabBarExtension::initConfigPage(KateTabBarExtensionConfigPage *p)
{
  p->pSortAlpha->setChecked(m_views.at(0)->tabbar->sortByName());
  p->pCloseOnMiddleClick->setChecked(m_views.at(0)->tabbar->closeOnMiddleClick());
}

void KatePluginTabBarExtension::applyConfig(KateTabBarExtensionConfigPage *p)
{
  m_views.at(0)->tabbar->setSortByName(p->pSortAlpha->isChecked());
  m_views.at(0)->tabbar->setCloseOnMiddleClick(p->pCloseOnMiddleClick->isChecked());
  // sync m_config in destructor
}
// END KatePluginTabBarExtension

// BEGIN KateTabBarButton
KateTabBarButton::KateTabBarButton(Kate::ViewManager* pViewManager,
    Kate::Document *pDoc, TQWidget * parent, const char * name)
  : TQPushButton(parent, name),
    modified(false),
    myDocID(pDoc->documentNumber()),
    doc(pDoc),
    viewManager(pViewManager)
{
  setFlat(true);
  setToggleButton(true);
  setFocusPolicy(TQ_NoFocus);

  setText(pDoc->docName());

  connect(this, TQT_SIGNAL(toggled(bool)), TQT_SLOT(setOn(bool)));
}

void KateTabBarButton::setDirty(bool d)
{
  if (d) {
    setIconSet(SmallIconSet("cancel"));
  } else {
    if (modified) setIconSet(SmallIconSet("modified"));
    else setIconSet(TQIconSet());
  }
}

void KateTabBarButton::setText( const TQString& newText)
{
  TQToolTip::remove(this);

  if (newText.length() > 20) {
    // squeeze to length 17+3=20
    TQPushButton::setText(newText.left(9) + "..." + newText.right(8));
    TQToolTip::add(this, newText);
  } else {
    TQPushButton::setText(newText);
  }
}

void KateTabBarButton::mouseReleaseEvent(TQMouseEvent *e)
{
  // Only handle middle click events when no keyboard modifier is pressed
  if (e->button() == TQt::MidButton && !(e->state() & TQt::KeyButtonMask))
  {
    emit middleButtonPressed(this);
  }
  else
  {
    // Invoke parent handler for unwanted events
    TQPushButton::mouseReleaseEvent(e);
  }
}

TQString KateTabBarButton::fullName() const
{
  if (doc) {
    return doc->docName();
  } else {
    return TQString("");
  }
}

void KateTabBarButton::triggerModified()
{
  modified = !modified;
  if (modified) {
    TQColor c(255, 0, 0);
    setPaletteForegroundColor( c );
    setIconSet(SmallIconSet("modified"));
  } else {
    TQColor c(TDEGlobalSettings::textColor());
    setPaletteForegroundColor( c );
    setIconSet(TQIconSet());
  }
}

void KateTabBarButton::setOn(bool on)
{
  disconnect( TQT_SIGNAL(toggled(bool)));
//  kdDebug() << "setOn: " << (int)on << endl;
  if ((!on) && viewManager->activeView()->document()->documentNumber()
      == documentNumber()) {
//    kdDebug() << "setOn aborted " << endl;
    TQPushButton::setOn(true);
  } else {
    TQPushButton::setOn(on);

    if (on) emit myToggled(this);
  }
  connect(this, TQT_SIGNAL(toggled(bool)), TQT_SLOT(setOn(bool)));
}
// END KateTabBarButton

// BEGIN KateTabBarExtension
KateTabBarExtension::KateTabBarExtension(Kate::DocumentManager *pDocManager,
    Kate::MainWindow *win, bool bHorizOrientation, bool bSort,
    bool bCloseOnMiddleClick, TQWidget* parent, const char* name, WFlags f)
  : TQWidget(parent, name, f),
    pCurrentTab(0), m_win(win), m_docManager(pDocManager), m_sort(false)
{
  if (bHorizOrientation) {
    top = new TQBoxLayout(this, TQBoxLayout::LeftToRight);
    m_orientation = Qt::Horizontal;
  } else {
    top = new TQBoxLayout(this, TQBoxLayout::TopToBottom);
    m_orientation = Qt::Vertical;
  }

  // add all already existing documents to the tabbar
  for (uint i = 0; i < pDocManager->documents(); i++)
  {
    slotDocumentCreated (pDocManager->document(i));
  }

  connect(m_win->viewManager(), TQT_SIGNAL(viewChanged()), TQT_SLOT(slotViewChanged()));
  connect(pDocManager,
      TQT_SIGNAL(documentCreated(Kate::Document *)),
      TQT_SLOT(slotDocumentCreated(Kate::Document *)));
  connect(pDocManager,
      TQT_SIGNAL(documentDeleted(uint)),
      TQT_SLOT(slotDocumentDeleted(uint)));

  setSortByName(bSort);
  setCloseOnMiddleClick(bCloseOnMiddleClick);
}

void KateTabBarExtension::slotMoved(Qt::Orientation o)
{
  // the tabbar moved (top, right, bottom, left or fluently)
  switch (o) {
    case Qt::Vertical:
      top->setDirection(TQBoxLayout::TopToBottom);
      break;

    case Qt::Horizontal:
      top->setDirection(TQBoxLayout::LeftToRight);
      break;
  }

  m_orientation = o;
}

void KateTabBarExtension::setSortByName(bool sbn)
{
  if (m_sort != sbn) {
    m_sort = sbn;
    if (m_sort)
      updateSort();
  }
}

void KateTabBarExtension::updateSort()
{
//  kdDebug() << "updateSort called" << endl;

  if (sortByName()) {
    // remove all tabs from the tabbar
    KateTabBarButton* tab;
    for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
      top->remove(tab);
    }

    // now sort
    m_tabs.sort();

    // and finally add tabs again. FIXME: Is there a better way? :(
    for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
      top->addWidget(tab);
    }
  }
}

void KateTabBarExtension::slotDocumentCreated (Kate::Document *doc)
{
//  kdDebug() << "slotDocumentCreated" << endl;
  if (!doc) return;

  KateTabBarButton* tab = new KateTabBarButton(m_win->viewManager(), doc, this);
  connect(tab, TQT_SIGNAL(myToggled(KateTabBarButton*)),
      TQT_SLOT(slotActivateView(KateTabBarButton*)));
  connect(tab, TQT_SIGNAL(middleButtonPressed(KateTabBarButton*)),
          TQT_SLOT(slotRequestDocClose(KateTabBarButton*)));
  connect(doc, TQT_SIGNAL(nameChanged(Kate::Document *)),
      TQT_SLOT(slotNameChanged(Kate::Document *)));
  connect(doc, TQT_SIGNAL(modStateChanged(Kate::Document *)),
      TQT_SLOT(slotModChanged(Kate::Document *)));
  connect(doc,
      TQT_SIGNAL(modifiedOnDisc(Kate::Document *, bool, unsigned char)),
      TQT_SLOT(slotModifiedOnDisc(Kate::Document *, bool, unsigned char)));
  if(doc->isModified()) tab->triggerModified();
  tab->show();
  top->addWidget(tab);
  m_tabs.append(tab);

  updateSort();
}

void KateTabBarExtension::slotDocumentDeleted (uint documentNumber)
{
//  kdDebug() << "slotDocumentDeleted " << endl;
  KateTabBarButton* tab;
  for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
    if (tab->documentNumber() == documentNumber) {
      tab->disconnect();
      top->remove(tab);
      m_tabs.removeRef(tab);
      delete tab;
      tab = 0;
      break;
    }
  }
}

void KateTabBarExtension::slotActivateView(KateTabBarButton* newTab)
{
//  kdDebug() << "slotActiavateView" << endl;
  pCurrentTab = newTab;
  if (pCurrentTab) {
    KateTabBarButton* tab;
    for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
      if (tab->isOn() && tab != pCurrentTab)
        tab->setOn(false);
    }
    uint id = pCurrentTab->documentNumber();
    m_win->viewManager()->activateView( id );
  }
}

void KateTabBarExtension::slotModChanged (Kate::Document *doc)
{
//  kdDebug() << "slotModChanged" << endl;

  if (!doc) return;

  KateTabBarButton* tab;
  for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
    if (tab->documentNumber() == doc->documentNumber()) {
      // found
      tab->triggerModified();
      break;
    }
  }
}

void KateTabBarExtension::slotModifiedOnDisc (
    Kate::Document *doc, bool b, unsigned char /*reason*/)
{
//  kdDebug() << "slotModifiedOnDisc: " << (int)b << endl;

  // find corresponding tab
  KateTabBarButton* tab;
  for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
    if (tab->documentNumber() == doc->documentNumber()) {
      tab->setDirty(b);
    }
  }
}

void KateTabBarExtension::slotNameChanged (Kate::Document *doc)
{
  if (!doc) return;
//  kdDebug() << "slotNameChanged " << doc->docName() << endl;

  KateTabBarButton* tab;
  for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
    if (tab->documentNumber() == doc->documentNumber()) {
      tab->setText(doc->docName());
      break;
    }
  }

  updateSort();
}

void KateTabBarExtension::slotViewChanged ()
{
//  kdDebug() << "slotVieChanged()" << endl;
  Kate::View *view = m_win->viewManager()->activeView();
  if (!view) return;

  KateTabBarButton* tab;
  for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
    if (tab->documentNumber()
        == ((Kate::Document *)view->getDoc())->documentNumber()) {
      pCurrentTab = tab;
      for (tab = m_tabs.first(); tab; tab = m_tabs.next() ) {
        if (tab->isOn()) tab->setOn(false);
      }
      if (!pCurrentTab->isOn()) pCurrentTab->setOn(true);

      break;
    }
  }
}

void KateTabBarExtension::slotRequestDocClose(KateTabBarButton *tab)
{
  if (closeOnMiddleClick() && tab)
  {
    m_docManager->closeDocument(tab->document());
  }
}
// END KateTabBarExtension

// BEGIN KateTabBarExtensionConfigPage
KateTabBarExtensionConfigPage::KateTabBarExtensionConfigPage(
    TQObject* /*parent*/ /*= 0L*/, TQWidget *parentWidget /*= 0L*/)
  : Kate::PluginConfigPage( parentWidget )
{
  TQVBoxLayout* top = new TQVBoxLayout(this, 0,
      KDialogBase::spacingHint());

  TQGroupBox* gb = new TQGroupBox(1, Qt::Horizontal, i18n("Behavior options"),
                                  this, "tab_bar_extension_config_page_layout" );
  gb->setInsideSpacing(KDialogBase::spacingHint());
  pSortAlpha = new TQCheckBox(i18n("Sort files alphabetically"), gb);
  pCloseOnMiddleClick = new TQCheckBox(i18n("Close document on mouse middle click"), gb);

  top->add(gb);
  top->addStretch(1);
//  throw signal changed
  connect(pSortAlpha, TQT_SIGNAL(toggled(bool)), this, TQT_SIGNAL(changed()));
  connect(pCloseOnMiddleClick, TQT_SIGNAL(toggled(bool)), this, TQT_SIGNAL(changed()));
}
// END KateTabBarExtensionConfigPage


// BEGIN MyPtrList implementaion
int MyPtrList::compareItems ( TQPtrCollection::Item item1_,
    TQPtrCollection::Item item2_ )
{
  KateTabBarButton* item1 = reinterpret_cast<KateTabBarButton*>(item1_);
  KateTabBarButton* item2 = reinterpret_cast<KateTabBarButton*>(item2_);

  if (item1->fullName().lower() < item2->fullName().lower()) {
//    kdDebug() << item1->fullName().lower() << " < "
//              << item2->fullName().lower() << endl;
    return -1;
  } else {
    if (item1->fullName().lower() > item2->fullName().lower()) {
//      kdDebug() << item1->fullName().lower() << " > "
//                << item2->fullName().lower() << endl;
      return 1;
    } else {
//      kdDebug() << item1->fullName().lower() << " == "
//                << item2->fullName().lower() << endl;
      return 0;
    }
  }
}
// END MyPtrList implementaion

#include "plugin_katetabbarextension.moc"