/*******************************************************************
* kftabdlg.cpp
* 
* 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, see <http://www.gnu.org/licenses/>.
* 
******************************************************************/

#include "kftabdlg.h"

#include <QButtonGroup>
#include <QRadioButton>
#include <QLabel>
#include <QLayout>
#include <QCheckBox>
#include <QWhatsThis>

#include <QPushButton>
#include <QApplication>
#include <QDesktopWidget>

#include <kcalendarsystem.h>
#include <KDateComboBox>
#include <kglobal.h>
#include <kcombobox.h>
#include <kurlcombobox.h>
#include <kurlcompletion.h>
#include <klineedit.h>
#include <kmessagebox.h>
#include <KNumInput>
#include <kfiledialog.h>
#include <kregexpeditorinterface.h>
#include <kservicetypetrader.h>
#include <kstandarddirs.h>
#include <kdialog.h>
#include <kconfiggroup.h>
#include <KShell>

#include "kquery.h"
// Static utility functions
static void save_pattern(KComboBox *, const QString &, const QString &);

#define SPECIAL_TYPES 7

struct LessMimeType_ByComment
{
    bool operator()(const KMimeType::Ptr& lhs, const KMimeType::Ptr& rhs) const
    {
        return lhs->comment() < rhs->comment();
    }
};

KfindTabWidget::KfindTabWidget(QWidget *parent)
  : KTabWidget( parent ), regExpDialog(0)
{
    // This validator will be used for all numeric edit fields
    //KDigitValidator *digitV = new KDigitValidator(this);

    // ************ Page One ************

    pages[0] = new QWidget;
    pages[0]->setObjectName( QLatin1String( "page1" ) );

    nameBox = new KComboBox(pages[0]);
    nameBox->setEditable( true );
    nameBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);  // allow smaller than widest entry
    QLabel * namedL = new QLabel(i18nc("this is the label for the name textfield","&Named:"), pages[0]);
    namedL->setBuddy( nameBox );
    namedL->setObjectName( QLatin1String( "named" ) );
    namedL->setToolTip( i18n("You can use wildcard matching and \";\" for separating multiple names") );
    dirBox  = new KUrlComboBox(KUrlComboBox::Directories, pages[0]);
    dirBox->setEditable( true );
    dirBox->setCompletionObject(new KUrlCompletion(KUrlCompletion::DirCompletion));
    dirBox->setAutoDeleteCompletionObject(true);
    dirBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);  // allow smaller than widest entry
    QLabel * lookinL = new QLabel(i18n("Look &in:"), pages[0]);
    lookinL->setBuddy( dirBox );
    lookinL->setObjectName( QLatin1String( "lookin" ) );
    subdirsCb  = new QCheckBox(i18n("Include &subfolders"), pages[0]);
    caseSensCb  = new QCheckBox(i18n("Case s&ensitive search"), pages[0]);
    browseB    = new QPushButton(i18n("&Browse..."), pages[0]);
    useLocateCb = new QCheckBox(i18n("&Use files index"), pages[0]);
    hiddenFilesCb = new QCheckBox(i18n("Show &hidden files"), pages[0]);
    // Setup

    subdirsCb->setChecked(true);
    caseSensCb->setChecked(false);
    useLocateCb->setChecked(false);
    hiddenFilesCb->setChecked(false);
    if(KStandardDirs::findExe(QLatin1String("locate")).isEmpty())
        useLocateCb->setEnabled(false);

    nameBox->setDuplicatesEnabled(false);
    nameBox->setFocus();
    dirBox->setDuplicatesEnabled(false);

    nameBox->setInsertPolicy(QComboBox::InsertAtTop);
    dirBox->setInsertPolicy(QComboBox::InsertAtTop);

    const QString nameWhatsThis
      = i18n("<qt>Enter the filename you are looking for. <br />"
	     "Alternatives may be separated by a semicolon \";\".<br />"
	     "<br />"
	     "The filename may contain the following special characters:"
	     "<ul>"
	     "<li><b>?</b> matches any single character</li>"
	     "<li><b>*</b> matches zero or more of any characters</li>"
	     "<li><b>[...]</b> matches any of the characters between the braces</li>"
	     "</ul>"
	     "<br />"
	     "Example searches:"
	     "<ul>"
	     "<li><b>*.kwd;*.txt</b> finds all files ending with .kwd or .txt</li>"
	     "<li><b>go[dt]</b> finds god and got</li>"
	     "<li><b>Hel?o</b> finds all files that start with \"Hel\" and end with \"o\", "
	     "having one character in between</li>"
	     "<li><b>My Document.kwd</b> finds a file of exactly that name</li>"
	     "</ul></qt>");
    nameBox->setWhatsThis(nameWhatsThis);
    namedL->setWhatsThis(nameWhatsThis);
    const QString whatsfileindex
        = i18n("<qt>This lets you use the files' index created by the <i>slocate</i> "
               "package to speed-up the search; remember to update the index from time to time "
               "(using <i>updatedb</i>)."
               "</qt>");
    useLocateCb->setWhatsThis(whatsfileindex);

    // Layout

    QGridLayout *grid = new QGridLayout( pages[0] );
    grid->setMargin( KDialog::marginHint() );
    grid->setSpacing( KDialog::spacingHint() );
    QVBoxLayout *subgrid = new QVBoxLayout();
    grid->addWidget( namedL, 0, 0 );
    grid->addWidget( nameBox, 0, 1, 1, 3 );
    grid->addWidget( lookinL, 1, 0 );
    grid->addWidget( dirBox, 1, 1 );
    grid->addWidget( browseB, 1, 2);
    grid->setColumnStretch(1,1);
    grid->addLayout( subgrid, 2, 1, 1, 2 );
    
    QHBoxLayout * layoutOne = new QHBoxLayout();
    layoutOne->addWidget( subdirsCb );
    layoutOne->addWidget( hiddenFilesCb );
    
    QHBoxLayout * layoutTwo = new QHBoxLayout();
    layoutTwo->addWidget( caseSensCb);
    layoutTwo->addWidget( useLocateCb );
    
    subgrid->addLayout( layoutOne );
    subgrid->addLayout( layoutTwo );
    
    subgrid->addStretch(1);

    // Signals

    connect(browseB, &QPushButton::clicked, this, &KfindTabWidget::getDirectory);

    connect(nameBox, static_cast<void (KComboBox::*)()>(&KComboBox::returnPressed), this, &KfindTabWidget::startSearch);

    connect(dirBox, static_cast<void (KUrlComboBox::*)()>(&KUrlComboBox::returnPressed), this, &KfindTabWidget::startSearch);
    
    // ************ Page Two

    pages[1] = new QWidget;
    pages[1]->setObjectName( QLatin1String( "page2" ) );

    findCreated =  new QCheckBox(i18n("Find all files created or &modified:"), pages[1]);
    bg  = new QButtonGroup();
    rb[0] = new QRadioButton(i18n("&between"), pages[1] );
    rb[1] = new QRadioButton(pages[1]); // text set in updateDateLabels
    andL = new QLabel(i18n("and"), pages[1]);
    andL->setObjectName( QLatin1String( "and" ) );
    betweenType = new KComboBox( pages[1] );
    betweenType->setObjectName( QLatin1String( "comboBetweenType" ) );
    betweenType->addItems(QVector<QString>(5).toList()); // texts set in updateDateLabels
    betweenType->setCurrentIndex(1);
    updateDateLabels(1, 1);

    const QDate dt = QDate::currentDate().addYears(-1);

    fromDate = new KDateComboBox(pages[1]);
    fromDate->setObjectName( QLatin1String( "fromDate" ) );
    fromDate->setDate(dt);
    toDate = new KDateComboBox(pages[1] );
    toDate->setObjectName( QLatin1String( "toDate" ) );
    timeBox = new KIntSpinBox( pages[1] );
    timeBox->setRange( 1, 60 );
    timeBox->setSingleStep( 1 );
    timeBox->setObjectName( QLatin1String( "timeBox" ) );

    sizeBox =new KComboBox( pages[1] );
    sizeBox->setObjectName( QLatin1String( "sizeBox" ) );
    QLabel * sizeL   =new QLabel(i18n("File &size is:"), pages[1]);
    sizeL->setBuddy( sizeBox );
    sizeEdit=new KIntSpinBox(pages[1] );
    sizeEdit->setRange( 0, INT_MAX );
    sizeEdit->setSingleStep( 1 );
    sizeEdit->setObjectName( QLatin1String( "sizeEdit" ) );
    sizeEdit->setValue(1);
    sizeUnitBox =new KComboBox( pages[1] );
    sizeUnitBox->setObjectName( QLatin1String( "sizeUnitBox" ) );

    m_usernameBox = new KComboBox( pages[1] );
    m_usernameBox->setEditable( true );
    m_usernameBox->setObjectName( QLatin1String( "m_combo1" ));
    QLabel *usernameLabel= new QLabel(i18n("Files owned by &user:"),pages[1]);
    usernameLabel->setBuddy( m_usernameBox );
    m_groupBox = new KComboBox( pages[1] );
    m_groupBox->setEditable( true );
    m_groupBox->setObjectName( QLatin1String( "m_combo2" ) );
    QLabel *groupLabel= new QLabel(i18n("Owned by &group:"),pages[1]);
    groupLabel->setBuddy( m_groupBox );

    sizeBox ->addItem( i18nc("file size isn't considered in the search","(none)") );
    sizeBox ->addItem( i18n("At Least") );
    sizeBox ->addItem( i18n("At Most") );
    sizeBox ->addItem( i18n("Equal To") );

    sizeUnitBox ->addItem( i18np("Byte", "Bytes", 1) );
    sizeUnitBox ->addItem( i18n("KiB") );
    sizeUnitBox ->addItem( i18n("MiB") );
    sizeUnitBox ->addItem( i18n("GiB") );
    sizeUnitBox ->setCurrentIndex(1);

    int tmp = sizeEdit->fontMetrics().width(QLatin1String(" 000000000 "));
    sizeEdit->setMinimumSize(tmp, sizeEdit->sizeHint().height());

    m_usernameBox->setDuplicatesEnabled(false);
    m_groupBox->setDuplicatesEnabled(false);
    m_usernameBox->setInsertPolicy(QComboBox::InsertAtTop);
    m_groupBox->setInsertPolicy(QComboBox::InsertAtTop);


    // Setup
    rb[0]->setChecked(true);
    bg->addButton( rb[0] );
    bg->addButton( rb[1] );

    // Layout

    QGridLayout *grid1 = new QGridLayout( pages[1] );
    grid1->setMargin( KDialog::marginHint() );
    grid1->setSpacing( KDialog::spacingHint() );

    grid1->addWidget(findCreated, 0, 0, 1, 3 );
    grid1->addItem( new QSpacerItem(KDialog::spacingHint(), 0), 0, 0 );

    grid1->addWidget(rb[0], 1, 1 );
    grid1->addWidget(fromDate, 1, 2 );
    grid1->addWidget(andL, 1, 3, Qt::AlignHCenter );
    grid1->addWidget(toDate, 1, 4 );

    grid1->addWidget(rb[1], 2, 1 );
    grid1->addWidget(timeBox, 2, 2, 1, 2);
    grid1->addWidget(betweenType, 2, 4 );

    grid1->addWidget(sizeL,3,0,1,2);
    grid1->addWidget(sizeBox,3,2);
    grid1->addWidget(sizeEdit,3,3);
    grid1->addWidget(sizeUnitBox,3,4);

    grid1->addWidget(usernameLabel,4,0,1,2);
    grid1->addWidget(m_usernameBox,4,2);
    grid1->addWidget(groupLabel,4,3);
    grid1->addWidget(m_groupBox,4,4);

    for (int c=1; c<=4; c++)
       grid1->setColumnStretch(c,1);

    grid1->setRowStretch(6,1);

    // Connect
    connect(findCreated, &QCheckBox::toggled, this, &KfindTabWidget::fixLayout);
    connect(bg, static_cast<void (QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked), this, &KfindTabWidget::fixLayout);
    connect(sizeBox, static_cast<void (KComboBox::*)(int)>(&KComboBox::activated), this, &KfindTabWidget::slotSizeBoxChanged);
    connect(timeBox, static_cast<void (KIntSpinBox::*)(int)>(&KIntSpinBox::valueChanged), this, &KfindTabWidget::slotUpdateDateLabelsForNumber);
    connect(betweenType, static_cast<void (KComboBox::*)(int)>(&KComboBox::currentIndexChanged), this, &KfindTabWidget::slotUpdateDateLabelsForType);
    connect(sizeEdit, static_cast<void (KIntSpinBox::*)(int)>(&KIntSpinBox::valueChanged), this, &KfindTabWidget::slotUpdateByteComboBox);


    // ************ Page Three

    pages[2] = new QWidget;
    pages[2]->setObjectName( QLatin1String( "page3" ) );

    typeBox =new KComboBox( pages[2] );
    typeBox->setObjectName( QLatin1String( "typeBox" ) );
    typeBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);  // allow smaller than widest entry
    QLabel * typeL   =new QLabel( i18nc("label for the file type combobox","File &type:"), pages[2] );
    typeL->setBuddy( typeBox );
    textEdit=new KLineEdit(pages[2]);
    textEdit->setClearButtonShown(true);
    textEdit->setObjectName( QLatin1String( "textEdit" ) );
    QLabel * textL   =new QLabel(i18n("C&ontaining text:"), pages[2]);
    textL->setBuddy( textEdit );

    connect(textEdit, &KLineEdit::returnPressed, this, &KfindTabWidget::startSearch);

    const QString containingtext
      = i18n("<qt>If specified, only files that contain this text"
	      " are found. Note that not all file types from the list"
		  " above are supported. Please refer to the documentation"
		  " for a list of supported file types."
	      "</qt>");
    textEdit->setToolTip(containingtext);
    textL->setWhatsThis(containingtext);

    caseContextCb  =new QCheckBox(i18n("Case s&ensitive"), pages[2]);
    binaryContextCb  =new QCheckBox(i18n("Include &binary files"), pages[2]);
    regexpContentCb  =new QCheckBox(i18n("Regular e&xpression"), pages[2]);

    const QString binaryTooltip
      = i18n("<qt>This lets you search in any type of file, "
       "even those that usually do not contain text (for example "
	   "program files and images).</qt>");
    binaryContextCb->setToolTip(binaryTooltip);

    QPushButton* editRegExp = 0;
    if ( !KServiceTypeTrader::self()->query(QStringLiteral("KRegExpEditor/KRegExpEditor")).isEmpty() ) {
        // The editor is available, so lets use it.
        editRegExp = new QPushButton(i18n("&Edit..."), pages[2]);
        editRegExp->setObjectName( QLatin1String( "editRegExp" ) );
    }

    metainfokeyEdit=new KLineEdit(pages[2]);
    metainfoEdit=new KLineEdit(pages[2]);
    QLabel * textMetaInfo = new QLabel(i18nc("as in search for", "fo&r:"), pages[2]);
    textMetaInfo->setBuddy( metainfoEdit );
    QLabel * textMetaKey = new QLabel(i18n("Search &metainfo sections:"), pages[2]);
    textMetaKey->setBuddy( metainfokeyEdit );

    // Setup
    typeBox->addItem(i18n("All Files & Folders"));
    typeBox->addItem(i18n("Files"));
    typeBox->addItem(i18n("Folders"));
    typeBox->addItem(i18n("Symbolic Links"));
    typeBox->addItem(i18n("Special Files (Sockets, Device Files, ...)"));
    typeBox->addItem(i18n("Executable Files"));
    typeBox->addItem(i18n("SUID Executable Files"));
    typeBox->addItem(i18n("All Images"));
    typeBox->addItem(i18n("All Video"));
    typeBox->addItem(i18n("All Sounds"));

    initMimeTypes();
    initSpecialMimeTypes();

    for ( KMimeType::List::ConstIterator it = m_types.constBegin();
          it != m_types.constEnd(); ++it )
    {
      KMimeType::Ptr typ = *it;
// TODO: needs to move to thread (increases startup time to bizzare amount)
//       and replaced with a better concept 16x16 icons don't cut the cheese
//       typeBox->addItem(KIconLoader::global()->loadMimeTypeIcon( typ->iconName(), KIconLoader::Small ), typ->comment());
      typeBox->addItem(typ->comment());
    }

    if ( editRegExp ) {
      // The editor was available, so lets use it.
      connect(regexpContentCb, &QCheckBox::toggled, editRegExp, &QPushButton::setEnabled);
      editRegExp->setEnabled(false);
      connect(editRegExp, &QPushButton::clicked, this, &KfindTabWidget::slotEditRegExp);
    }
    else
        regexpContentCb->hide();

    // Layout
    tmp = sizeEdit->fontMetrics().width(QLatin1String(" 00000 "));
    sizeEdit->setMinimumSize(tmp, sizeEdit->sizeHint().height());

    QGridLayout *grid2 = new QGridLayout( pages[2] );
    grid2->setMargin( KDialog::marginHint() );
    grid2->setSpacing( KDialog::spacingHint() );
    grid2->addWidget( typeL, 0, 0 );
    grid2->addWidget( textL, 1, 0 );
    grid2->addWidget( typeBox, 0, 1, 1, 3 );
    grid2->addWidget( textEdit, 1, 1, 1, 3 );
    grid2->addWidget( regexpContentCb, 2, 2);
    grid2->addWidget( caseContextCb, 2, 1 );
    grid2->addWidget( binaryContextCb, 3, 1);

    grid2->addWidget( textMetaKey, 4, 0 );
    grid2->addWidget( metainfokeyEdit, 4, 1 );
    grid2->addWidget( textMetaInfo, 4, 2, Qt::AlignHCenter  );
    grid2->addWidget( metainfoEdit, 4, 3 );

    metainfokeyEdit->setText(QLatin1String("*"));

    if ( editRegExp ) {
      // The editor was available, so lets use it.
      grid2->addWidget( editRegExp, 2, 3 );
    }

    addTab( pages[0], i18n("Name/&Location") );
    addTab( pages[2], i18nc("tab name: search by contents","C&ontents") );
    addTab( pages[1], i18n("&Properties") );


    // Setup
    const QString whatsmetainfo
      = i18n("<qt>Search within files' specific comments/metainfo<br />"
	     "These are some examples:<br />"
	     "<ul>"
	     "<li><b>Audio files (mp3...)</b> Search in id3 tag for a title, an album</li>"
	     "<li><b>Images (png...)</b> Search images with a special resolution, comment...</li>"
	     "</ul>"
	     "</qt>");
    const QString whatsmetainfokey
      = i18n("<qt>If specified, search only in this field<br />"
	     "<ul>"
	     "<li><b>Audio files (mp3...)</b> This can be Title, Album...</li>"
	     "<li><b>Images (png...)</b> Search only in Resolution, Bitdepth...</li>"
	     "</ul>"
	     "</qt>");
    textMetaInfo->setWhatsThis(whatsmetainfo);
    metainfoEdit->setToolTip(whatsmetainfo);
    textMetaKey->setWhatsThis(whatsmetainfokey);
    metainfokeyEdit->setToolTip(whatsmetainfokey);


    fixLayout();
    loadHistory();
}

KfindTabWidget::~KfindTabWidget()
{
  delete pages[0];
  delete pages[1];
  delete pages[2];
  delete bg;
}

void KfindTabWidget::setURL( const QUrl & url )
{
  KConfigGroup conf(KSharedConfig::openConfig(), "History");
  m_url = url;
  QStringList sl = conf.readPathEntry("Directories", QStringList());
  dirBox->clear(); // make sure there is no old Stuff in there

  if(!sl.isEmpty()) {
    dirBox->addItems(sl);
    // If the _searchPath already exists in the list we do not
    // want to add it again
    int indx = sl.indexOf(m_url.toDisplayString());
    if(indx == -1) {
      dirBox->insertItem(0, m_url.toDisplayString()); // make it the first one
      dirBox->setCurrentIndex(0);
    }
    else
      dirBox->setCurrentIndex(indx);
  }
  else {
    QDir m_dir(QStringLiteral("/lib"));
    dirBox ->insertItem( 0, m_url.toDisplayString() );
    dirBox ->addItem( QStringLiteral("file:") + QDir::homePath() );
    dirBox ->addItem( QStringLiteral("file:/") );
    dirBox ->addItem( QStringLiteral("file:/usr") );
    if (m_dir.exists())
      dirBox ->addItem( QStringLiteral("file:/lib") );
    dirBox ->addItem( QStringLiteral("file:/home") );
    dirBox ->addItem( QStringLiteral("file:/etc") );
    dirBox ->addItem( QStringLiteral("file:/var") );
    dirBox ->addItem( QStringLiteral("file:/mnt") );
    dirBox->setCurrentIndex(0);
  }
}

void KfindTabWidget::initMimeTypes()
{
    KMimeType::List sortedList;
    foreach ( const KMimeType::Ptr &type, KMimeType::allMimeTypes() )
    {
      if ( (!type->comment().isEmpty())
           && (!type->name().startsWith( QLatin1String("kdedevice/") ))
           && (!type->name().startsWith( QLatin1String("all/") )) )
        sortedList.append(type);
    }
    qSort( sortedList.begin(), sortedList.end(), LessMimeType_ByComment() );
    m_types += sortedList;
}

void KfindTabWidget::initSpecialMimeTypes()
{
    const KMimeType::List tmp = KMimeType::allMimeTypes();

    for ( KMimeType::List::ConstIterator it = tmp.constBegin(); it != tmp.constEnd(); ++it )
    {
	  const KMimeType* type = (*it).data();

      if(!type->comment().isEmpty()) {
        if(type->name().startsWith( QLatin1String("image/") ))
           m_ImageTypes.append(type->name());
        else if(type->name().startsWith( QLatin1String("video/") ))
          m_VideoTypes.append(type->name());
        else if(type->name().startsWith( QLatin1String("audio/") ))
          m_AudioTypes.append(type->name());
      }
    }
}

void KfindTabWidget::saveHistory()
{
  save_pattern(nameBox, QLatin1String("History"), QLatin1String("Patterns"));
  save_pattern(dirBox, QLatin1String("History"), QLatin1String("Directories"));
}

void KfindTabWidget::loadHistory()
{
  // Load pattern history
  KConfigGroup conf(KSharedConfig::openConfig(), QLatin1String("History"));
  QStringList sl = conf.readEntry(QLatin1String("Patterns"), QStringList());
  if(!sl.isEmpty())
    nameBox->addItems(sl);
  else
    nameBox->addItem(QLatin1String("*"));

  sl = conf.readPathEntry(QLatin1String("Directories"), QStringList());
  if(!sl.isEmpty()) {
    dirBox->addItems(sl);
    // If the _searchPath already exists in the list we do not
    // want to add it again
    int indx = sl.indexOf(m_url.toDisplayString());
    if(indx == -1) {
      dirBox->insertItem(0, m_url.toDisplayString()); // make it the first one
      dirBox->setCurrentIndex(0);
    }
    else
      dirBox->setCurrentIndex(indx);
  }
  else {
    QDir m_dir(QStringLiteral("/lib"));
    dirBox ->insertItem( 0, m_url.toDisplayString() );
    dirBox ->addItem( QStringLiteral("file:") + QDir::homePath() );
    dirBox ->addItem( QStringLiteral("file:/") );
    dirBox ->addItem( QStringLiteral("file:/usr") );
    if (m_dir.exists())
      dirBox ->addItem( QStringLiteral("file:/lib") );
    dirBox ->addItem( QStringLiteral("file:/home") );
    dirBox ->addItem( QStringLiteral("file:/etc") );
    dirBox ->addItem( QStringLiteral("file:/var") );
    dirBox ->addItem( QStringLiteral("file:/mnt") );
    dirBox ->setCurrentIndex(0);
  }
}

void KfindTabWidget::slotEditRegExp()
{
  if ( ! regExpDialog )
    regExpDialog = KServiceTypeTrader::createInstanceFromQuery<KDialog>( QLatin1String("KRegExpEditor/KRegExpEditor"), QString(), this );

  KRegExpEditorInterface *iface = qobject_cast<KRegExpEditorInterface *>( regExpDialog );
  if ( !iface )
       return;

  iface->setRegExp( textEdit->text() );
  bool ok = regExpDialog->exec();
  if ( ok )
    textEdit->setText( iface->regExp() );
}

void KfindTabWidget::setFocus()
{
  nameBox->setFocus();
  nameBox->lineEdit()->selectAll();
}

void KfindTabWidget::slotSizeBoxChanged(int index)
{
  sizeEdit->setEnabled((bool)(index != 0));
  sizeUnitBox->setEnabled((bool)(index != 0));
}

void KfindTabWidget::setDefaults()
{
    const QDate dt = QDate::currentDate().addYears(-1);

    fromDate ->setDate(dt);
    toDate ->setDate(QDate::currentDate());

    timeBox->setValue(1);
    betweenType->setCurrentIndex(1);

    typeBox ->setCurrentIndex(0);
    sizeBox ->setCurrentIndex(0);
    sizeUnitBox ->setCurrentIndex(1);
    sizeEdit->setValue(1);
}

/*
  Checks if dates are correct and popups a error box
  if they are not.
*/
bool KfindTabWidget::isDateValid()
{
  // All files
  if ( !findCreated->isChecked() ) return true;

  if (rb[1]->isChecked())
  {
    if (timeBox->value() > 0 ) return true;

    KMessageBox::sorry(this, i18n("Unable to search within a period which is less than a minute."));
    return false;
  }

  // If we can not parse either of the dates or
  // "from" date is bigger than "to" date return false.
  const QDate from = fromDate->date();
  const QDate to = toDate->date();

  QString str;
  if (!from.isValid() || !to.isValid()) {
      str = i18n("The date is not valid.");
  } else if (from > to) {
      str = i18n("Invalid date range.");
  } else if (from > QDate::currentDate()) {
      str = i18n("Unable to search dates in the future.");
  }

  if (!str.isEmpty()) {
    KMessageBox::sorry(0, str);
    return false;
  }
  return true;
}

void KfindTabWidget::setQuery(KQuery *query)
{
  KIO::filesize_t size;
  KIO::filesize_t sizeunit;
  bool itemAlreadyContained(false);
  // only start if we have valid dates
  if (!isDateValid()) return;

  const QString trimmedDirBoxText = dirBox->currentText().trimmed();
  const QString tildeExpandedPath = KShell::tildeExpand(trimmedDirBoxText);

  query->setPath(QUrl::fromUserInput(tildeExpandedPath, m_url.toLocalFile(), QUrl::AssumeLocalFile));

  for (int idx=0; idx<dirBox->count(); idx++)
     if (dirBox->itemText(idx)==dirBox->currentText())
        itemAlreadyContained=true;

  if (!itemAlreadyContained)
     dirBox->addItem(dirBox->currentText().trimmed(),0);

  QString regex = nameBox->currentText().isEmpty() ? QLatin1String("*") : nameBox->currentText();
  query->setRegExp(regex, caseSensCb->isChecked());
  itemAlreadyContained=false;
  for (int idx=0; idx<nameBox->count(); idx++)
     if (nameBox->itemText(idx)==nameBox->currentText())
        itemAlreadyContained=true;

  if (!itemAlreadyContained)
     nameBox->addItem(nameBox->currentText(),0);

  query->setRecursive(subdirsCb->isChecked());

  switch (sizeUnitBox->currentIndex())
  {
     case 0:
         sizeunit = 1; //one byte
			break;
     case 2:
         sizeunit = 1048576; //1Mi
			break;
     case 3:
         sizeunit = 1073741824; //1Gi
			break;
     case 1: // fall to default case
     default:
         sizeunit=1024; //1Ki
            break;
  }
  size = sizeEdit->value() * sizeunit;

// TODO: troeder: do we need this check since it is very unlikely
// to exceed ULLONG_MAX with INT_MAX * 1024^3.
// Or is there an arch where this can happen?
#if 0
  if (size < 0)  // overflow
  {
	if (KMessageBox::warningYesNo(this, i18n("Size is too big. Set maximum size value?"), i18n("Error"),i18n("Set"),i18n("Do Not Set"))
           == KMessageBox::Yes)
		{
         sizeEdit->setValue(INT_MAX);
		 sizeUnitBox->setCurrentIndex(0);
		 size = INT_MAX;
		}
     else
        return;
  }
#endif

  // set range mode and size value
  query->setSizeRange(sizeBox->currentIndex(),size,0);

  // dates
  QDateTime epoch;
  epoch.setTime_t(0);

  // Add date predicate
  if (findCreated->isChecked()) { // Modified
    if (rb[0]->isChecked()) { // Between dates
      const QDate &from = fromDate->date();
      const QDate &to = toDate->date();

      // do not generate negative numbers .. find doesn't handle that
      time_t time1 = epoch.secsTo(QDateTime(from));
      time_t time2 = epoch.secsTo(QDateTime(to.addDays(1))) - 1; // Include the last day

      query->setTimeRange(time1, time2);
    }
    else
    {
       time_t cur = time(NULL);
       time_t minutes = cur;

       switch (betweenType->currentIndex())
       {
          case 0: // minutes
                 minutes = timeBox->value();
 	              break;
          case 1: // hours
                 minutes = 60 * timeBox->value();
 	              break;
          case 2: // days
                 minutes = 60 * 24 * timeBox->value();
 	              break;
          case 3: // months
                 minutes = 60 * 24 * (time_t)(timeBox->value() * 30.41667);
 	              break;
          case 4: // years
                 minutes = 12 * 60 * 24 * (time_t)(timeBox->value() * 30.41667);
 	              break;
       }

       query->setTimeRange(cur - minutes * 60, 0);
    }
  }
  else
    query->setTimeRange(0, 0);

  query->setUsername( m_usernameBox->currentText() );
  query->setGroupname( m_groupBox->currentText() );

  query->setFileType(typeBox->currentIndex());

  int id = typeBox->currentIndex()-10;

  if ((id >= -3) && (id < (int) m_types.count()))
  {
    switch(id)
    {
      case -3:
        query->setMimeType( m_ImageTypes );
        break;
      case -2:
        query->setMimeType( m_VideoTypes );
        break;
      case -1:
        query->setMimeType( m_AudioTypes );
        break;
      default:
        query->setMimeType( QStringList() += m_types[id]->name() );
     }
  }
  else
  {
     query->setMimeType( QStringList() );
  }

  //Metainfo
  query->setMetaInfo(metainfoEdit->text(), metainfokeyEdit->text());

  //Use locate to speed-up search ?
  query->setUseFileIndex(useLocateCb->isChecked());

  query->setShowHiddenFiles(hiddenFilesCb->isChecked());
  
  query->setContext(textEdit->text(), caseContextCb->isChecked(),
  	binaryContextCb->isChecked(), regexpContentCb->isChecked());
}

void KfindTabWidget::getDirectory()
{
  QString result =
      KFileDialog::getExistingDirectory(QUrl::fromUserInput(dirBox->currentText().trimmed()), this );

  if (!result.isEmpty())
  {
    for (int i = 0; i < dirBox->count(); i++)
      if (result == dirBox->itemText(i)) {
	dirBox->setCurrentIndex(i);
	return;
      }
    dirBox->insertItem(0, result);
    dirBox->setCurrentIndex(0);
  }
}

void KfindTabWidget::beginSearch()
{
///  dirlister->openUrl(KUrl(dirBox->currentText().trimmed()));

  saveHistory();

  for (uint i = 0; i < sizeof(pages) / sizeof(pages[0]); ++i) {
      pages[i]->setEnabled(false);
  }
}

void KfindTabWidget::endSearch()
{
    for (uint i = 0; i < sizeof(pages) / sizeof(pages[0]); ++i) {
        pages[i]->setEnabled(true);
    }
}

/*
  Disables/enables all edit fields depending on their
  respective check buttons.
*/
void KfindTabWidget::fixLayout()
{
  int i;
  // If "All files" is checked - disable all edits
  // and second radio group on page two

  if(! findCreated->isChecked())  {
    fromDate->setEnabled(false);
    toDate->setEnabled(false);
    andL->setEnabled(false);
    timeBox->setEnabled(false);
    for(i=0; i<2; ++i)
      rb[i]->setEnabled(false);
    betweenType->setEnabled(false);
  }
  else {
    for(i=0; i<2; ++i)
      rb[i]->setEnabled(true);

    fromDate->setEnabled(rb[0]->isChecked());
    toDate->setEnabled(rb[0]->isChecked());
    andL->setEnabled(rb[0]->isEnabled());
    timeBox->setEnabled(rb[1]->isChecked());
    betweenType->setEnabled(rb[1]->isChecked());
  }

  // Size box on page three
  sizeEdit->setEnabled(sizeBox->currentIndex() != 0);
  sizeUnitBox->setEnabled(sizeBox->currentIndex() != 0);
}

bool KfindTabWidget::isSearchRecursive()
{
  return subdirsCb->isChecked();
}

void KfindTabWidget::slotUpdateDateLabelsForNumber(int value)
{
  updateDateLabels(betweenType->currentIndex(), value);
}

void KfindTabWidget::slotUpdateDateLabelsForType(int index)
{
  updateDateLabels(index, timeBox->value());
}

void KfindTabWidget::updateDateLabels(int type, int value)
{
  QString typeKey(type == 0 ? QLatin1Char('i') : type == 1 ? QLatin1Char('h') : type == 2 ? QLatin1Char('d') : type == 3 ? QLatin1Char('m') : QLatin1Char('y'));
  rb[1]->setText(ki18ncp("during the previous minute(s)/hour(s)/...; "
                         "dynamic context 'type': 'i' minutes, 'h' hours, 'd' days, 'm' months, 'y' years",
                         "&during the previous", "&during the previous").subs(value).inContext(QLatin1String("type"), typeKey).toString());
  betweenType->setItemText(0, i18ncp("use date ranges to search files by modified time", "minute", "minutes", value));
  betweenType->setItemText(1, i18ncp("use date ranges to search files by modified time", "hour", "hours", value));
  betweenType->setItemText(2, i18ncp("use date ranges to search files by modified time", "day", "days", value));
  betweenType->setItemText(3, i18ncp("use date ranges to search files by modified time", "month", "months", value));
  betweenType->setItemText(4, i18ncp("use date ranges to search files by modified time", "year", "years", value));
}

void KfindTabWidget::slotUpdateByteComboBox(int value)
{
  sizeUnitBox->setItemText(0, i18np("Byte", "Bytes", value) );
}


/**
   Digit validator. Allows only digits to be typed.
**/
KDigitValidator::KDigitValidator( QWidget * parent )
  : QValidator( parent )
{
  r = new QRegExp(QStringLiteral("^[0-9]*$"));
}

KDigitValidator::~KDigitValidator()
{
  delete r;
}

QValidator::State KDigitValidator::validate( QString & input, int & ) const
{
  if (r->indexIn(input) < 0) {
    // Beep on user if he enters non-digit
    QApplication::beep();
    return QValidator::Invalid;
  }
  else
    return QValidator::Acceptable;
}

//*******************************************************
//             Static utility functions
//*******************************************************
static void save_pattern(KComboBox *obj,
			 const QString & group, const QString & entry)
{
  // QComboBox allows insertion of items more than specified by
  // maxCount() (QT bug?). This API call will truncate list if needed.
  obj->setMaxCount(15);

  // make sure the current item is saved first so it will be the
  // default when started next time
  QStringList sl;
  QString cur = obj->itemText(obj->currentIndex());
  sl.append(cur);
  for (int i = 0; i < obj->count(); i++) {
    if( cur != obj->itemText(i) ) {
      sl.append(obj->itemText(i));
    }
  }

  KConfigGroup conf(KSharedConfig::openConfig(), group);
  conf.writePathEntry(entry, sl);
}

QSize KfindTabWidget::sizeHint() const
{
  // #44662: avoid a huge default size when the comboboxes have very large items
  // Like in minicli, we changed the combobox size policy so that they can resize down,
  // and then we simply provide a reasonable size hint for the whole window, depending
  // on the screen width.
  QSize sz = KTabWidget::sizeHint();
  KfindTabWidget* me = const_cast<KfindTabWidget*>( this );
  const int screenWidth = qApp->desktop()->screenGeometry(me).width();
  if ( sz.width() > screenWidth / 2 )
    sz.setWidth( screenWidth / 2 );
  return sz;
}
