/*
   - 

  written 1998 by Alexander Budnik <budnik@linserv.jinr.ru>
  
  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., 675 Mass Ave, Cambridge, MA 02139, USA.
   
  */

#include <sys/types.h>
#include <sys/wait.h>

#include <stream.h>
#include <unistd.h>
#include <stdlib.h>

#include <qfileinfo.h>
#include <qdir.h>
#include <qcolor.h>
#include <qpainter.h>
#include <qdict.h>

#include <kconfig.h>
#include <kapp.h>
#include <kiconloader.h> 
#include <kglobal.h>
#include <kstddirs.h>
#include <kmessagebox.h>

#include "kconfobjs.h"
#include "options.h"
#include "kcmikbd.h"
#include <klocale.h>

#define I18n_NOOP(x) x

//=========================================================
//    data
//=========================================================
static const char* confMainGroup        = "International Keyboard";
static const char* confStartupGroup     = "StartUp";
static const char* confMapGroups[] = {
  "KeyboardMap",
  "ComposeMap1",
  "ComposeMap2",
  "ComposeMap3"
};
static const char* confStringBeep       = "Beep";
static const char* confStringSwitch     = "Switch";
static const char* confStringAltSwitch  = "AltSwitch";
static const char* confStringAutoMenu   = "WorldMenu";
static const char* confStringEmuCapsLock= "EmulateCapsLock";
static const char* confStringSaveClasses= "SaveClasses";
static const char* confStringInput      = "Input";
static const char* confStringMap        = "Map";
static const char* confStringLabel      = "Label";
static const char* confStringComment    = "Comment";
static const char* confStringLanguage   = "Name";
static const char* confStringLocale     = "Locale";
static const char* confStringCharset    = "Charset";
static const char* confStringAuthors    = "Authors";
static const char* confStringCaps       = "CapsSymbols";
static const char* confStringCapsColor  = "CapsLockColor";
static const char* confStringAltColor   = "AltColor";
static const char* confStringForColor   = "ForegroundColor";
static const char* confStringBakColor   = "BackgroundColor";
static const char* confStringFont       = "Font";
static const char* confStringCustFont   = "CustomizeFont";
static const char* confStringAutoStart  = "AutoStart";
static const char* confStringDocking    = "Docking";  
static const char* confStringHotList    = "HotList";  
static const char* confStringCodes      = "Codes";  
static const char* confStringAutoStartPlace  = "AutoStartPlace";
static const char* swConfigString[] = {
  "None",
  "Alt_R",
  "Control_R",
  "Alt_R+Shift_R",
  "Control_R+Alt_R",
  "Control_R+Shift_R",
  "Alt_L+Shift_L",
  "Control_L+Alt_L",
  "Control_L+Shift_L",
  "Shift_L+Shift_R"
};
static const char* swConfigAltString[] = {
  "None",
  "Alt_R",
  "Control_R",
  "Meta_R",
  "Alt_L",
  "Control_L",
  "Meta_L"
};
static const char* inpConfigString[] = {
  "Global", "Window", "Class"
};
// GUI labels
static const char* inpLabels[] = {
  I18n_NOOP("Global"), I18n_NOOP("Window"), I18n_NOOP("Class")
};
static const char* switchLabels[] = {
  I18n_NOOP("(None)"),
  I18n_NOOP("Right Alt"),
  I18n_NOOP("Right Control"),
  I18n_NOOP("Rights (Alt+Shift)") ,
  I18n_NOOP("Rights (Ctrl+Alt)")  ,
  I18n_NOOP("Rights (Ctrl+Shift)"),
  I18n_NOOP("Lefts  (Alt+Shift)"),
  I18n_NOOP("Lefts  (Ctrl+Alt)"),
  I18n_NOOP("Lefts  (Ctrl+Shift)"),
  I18n_NOOP("Both Shift's (Shift+Shift)")
};
static const char* altSwitchLabels[] = {
  I18n_NOOP("(None)"),
  I18n_NOOP("Right Alt"),
  I18n_NOOP("Right Control"),
  I18n_NOOP("Right Meta"),
  I18n_NOOP("Left  Alt"),
  I18n_NOOP("Left  Control"),
  I18n_NOOP("Left  Meta"),
};
static const char* autoStartPlaceLabels[] = {
  I18n_NOOP("Top Left"),
  I18n_NOOP("Top Right"),
  I18n_NOOP("Bottom Left"),
  I18n_NOOP("Bottom Right")
};

const QColor mapNormalColor = Qt::black;
const QColor mapUserColor   = Qt::darkBlue;
const QColor mapNoFileColor = Qt::darkRed;

//=========================================================
//   special widget
//=========================================================
class KiKbdCodesObject: public KConfigComboObject {
protected:
  KiKbdConfig* owner;
public:
  KiKbdCodesObject(KiKbdConfig* owner)
    :KConfigComboObject(confStringCodes, *((QString*)&owner->Codes()),
			QStrList(), 0),
     owner(owner) 
     {
      list = owner->availableMaps("codes");
     }
  virtual QWidget* createWidget(QWidget* parent=0L, const char* name=0L)
    {
      list = owner->availableMaps("codes");
      if(labels) delete labels;
      labels = new QStrList();
      for(unsigned i=0; i<list.count(); i++)
	labels->append(owner->aMap(list.at(i))->Label());
      // preppend x default
      list.insert(0, "");
      labels->insert(0, i18n("X default codes"));
      num = list.count();
      return KConfigComboObject::createWidget(parent, name);
    }
};

//=========================================================
//   config class
//=========================================================
Options::Options(bool readOnly)
  :KObjectConfig(UserFromSystemRc, 0L, readOnly)
{
  setVersion(1);
  *this << setGroup(confMainGroup)
	<< new KConfigBoolObject(confStringHotList, hotList)
	<< new KConfigBoolObject(confStringBeep, keyboardBeep)
	<< new KConfigBoolObject(confStringAutoMenu, autoMenu)
	<< new KConfigBoolObject(confStringEmuCapsLock, emuCapsLock)
	<< new KConfigBoolObject(confStringCustFont, custFont)
	<< new KConfigBoolObject(confStringSaveClasses, saveClasses)
	<< new KConfigNumberedKeysObject(confStringMap, 0, 9, maps)
	<< new KConfigComboObject(confStringSwitch, switchComb, swConfigString,
				  sizeof(swConfigString)
				  /sizeof(*swConfigString), switchLabels)
	<< new KConfigComboObject(confStringAltSwitch, altSwitchComb,
				  swConfigAltString, sizeof(swConfigAltString)
				  /sizeof(*swConfigAltString), altSwitchLabels)
	<< new KConfigComboObject(confStringInput, input, inpConfigString,
				  sizeof(inpConfigString)
				  /sizeof(*inpConfigString), inpLabels,
				  KConfigComboObject::ButtonGroup)
	<< new KConfigColorObject(confStringCapsColor, capsColor)
	<< new KConfigColorObject(confStringAltColor, altColor)
	<< new KConfigColorObject(confStringForColor, forColor)
	<< new KConfigColorObject(confStringBakColor, bakColor)
	<< new KConfigFontObject(confStringFont,  font)
	<< new KiKbdCodesObject(this)
	<< setGroup(confStartupGroup)
	<< new KConfigBoolObject(confStringAutoStart, autoStart)
	<< new KConfigBoolObject(confStringDocking, docking)
	<< new KConfigComboObject(confStringAutoStartPlace, autoStartPlace,
				  autoStartPlaceLabels,
				  sizeof(autoStartPlaceLabels)
				  /sizeof(*autoStartPlaceLabels));
  connect(this, SIGNAL(newUserRcFile()), SLOT(newUserRc())); 
  connect(this, SIGNAL(wrongVersion(int)) , SLOT(wrongVersion(int))); 
  //CT connect(this, SIGNAL(newerVersion(float)) , SLOT(newerVersion(float))); 
  maps.setAutoDelete(TRUE);
  allMaps.setAutoDelete(TRUE);
}
void Options::loadConfig()
{
  setDefaults();
  KObjectConfig::loadConfig();
}
void ask(const char* msg)
{
  if(!Options::isConfigProgram()) 
  {
    if(KMessageBox::questionYesNo(0, i18n("%1\nDo you want to configure now?")
			  .arg(msg)), "International Keyboard")
      Options::startConfigProgram();
  } 
  else KMessageBox::warning(0,msg, "International Keyboard");
}
void Options::newUserRc()
{
  ask(i18n("You're using \"International Keyboard\" for the first time."
	      "\nI installed a number of default settings."));
}
//CT 17Jan1999 - rewritten
int doneCheck = FALSE;
void Options::wrongVersion(int how)
{
  if(!doneCheck) {
    QString tmp = "Your present configuration file uses a" ;
    if ( how < 0 )
      tmp = tmp + "n older ";
    else 
      tmp =tmp + " newer ";
    tmp = tmp +  "format than needed.\nSome settings may be incorrect.";
    ask(i18n(tmp));
    doneCheck = TRUE;
  }
}
KiKbdMapConfig* Options::aMap(const char* name)
{
  unsigned i;for(i=0; i<allMaps.count(); i++)
    if(allMaps.at(i)->name == name) return allMaps.at(i);
  allMaps.append(new KiKbdMapConfig(name));
  return allMaps.current();
}
bool Options::hasAltKeys()
{
  unsigned i;for(i=0; i<maps.count(); i++)
    if(aMap(maps.at(i))->HasAltKeys()) return TRUE;
  return FALSE;
}
bool Options::hasCompose()
{
  unsigned i;for(i=0; i<maps.count(); i++)
    if(aMap(maps.at(i))->HasCompose()) return TRUE;
  return FALSE;
}
void Options::setDefaults()
{
  //CT setting autoStart to false by default
  autoStart = FALSE;
  keyboardBeep = hotList     = /*CT autoStart = */docking     = TRUE;
  autoMenu     = emuCapsLock = custFont  = saveClasses = FALSE;
  switchComb     = "Control_R+Shift_R";
  altSwitchComb  = "Alt_R";
  codes          = "";
  autoStartPlace = input = 0;
  capsColor = QColor(128, 0, 128);
  bakColor  = QColor(0, 128, 128);
  altColor  = QColor(255, 255, 0);
  forColor  = black;
  font      = QFont("Helvetica");
  maps.clear(); maps.append("en");
  markDataChanged();
}
bool Options::oneKeySwitch() const
{
  return (!switchComb.contains('+')) && (switchComb != "None");
}
QStrList Options::availableMaps(const char* subdir)
{
  static QDict<QStrList> *dict = 0L;

  QStrList* list;
  if(dict) {
    if((list = dict->find(subdir))) return *list;
  } else {
    dict = new QDict<QStrList>;
    dict->setAutoDelete(TRUE);
  }
  list = new QStrList();
  dict->insert(subdir, list);

  QStringList entry( KGlobal::dirs()->findAllResources( "appdata", QString(subdir)+"/*.kimap" ));
  unsigned j;for(j=0; j<entry.count(); j++)
  {
    QString name = *entry.at(j);
    name.truncate(name.find("."));
    if(list->find(name) == -1) list->inSort(name);
  }
  return *list;
}
bool Options::readAutoStart()
{
  KObjectConfig config(AppRc);
  bool autoStart = FALSE;
  config << config.setGroup(confStartupGroup)
	 << new KConfigBoolObject(confStringAutoStart, autoStart);
  config.loadConfig();
  return autoStart;
}
void Options::startConfigProgram(int opt)
{
  int pid = 0;

  switch(opt) {
  case Config_Normal: 
    QApplication::flushX();
    pid = fork();
    if (!pid) {
      execlp("kcmikbd", "kcmikbd", 0L);
      ::exit(1);
    } else {
      waitpid(pid, NULL, 0);
    }
    break;
  case Config_StartKikbd: execlp("kcmikbd", "kcmikbd", "-startkikbd", 0L);
  }
}
bool Options::isConfigProgram()
{
  return QString(kapp->argv()[0]).find("kikbd") == -1;
}
