/*
  Copyright (c) 2000 Caldera Systems

  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 "kxfreeconfig.h"
#include "pciinfo.h"

#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>

#include <qfile.h>

#include <kdebug.h>
#include <kstandarddirs.h>
#include <kmessagebox.h>
#include <klocale.h>


#define DEFAULT_MODE "640x480"

const char *KXFreeConfig::mNodeNameVideoRam = "X-KXCONFIG-VideoRam";
const char *KXFreeConfig::mNodeNamePixelClock = "X-KXCONFIG-MaxClock";


KXFreeConfig::KXFreeConfig( const QString &customXF86Config )
  : mBackupWritten( false )
{
    if ( !customXF86Config.isEmpty() &&
         KStandardDirs::exists( customXF86Config ) )
        m_configFile = customXF86Config;
    else
    {
        QStringList searchPaths;
        searchPaths << "/etc/X11/XF86Config-4";
        searchPaths << "/etc/X11/XF86Config";
        searchPaths << "/etc/XF86Config";
        searchPaths << "/usr/X11R6/etc/X11/XF86Config-4";
        searchPaths << "/usr/X11R6/etc/X11/XF86Config";
        searchPaths << "/usr/X11R6/lib/X11/XF86Config-4";
        searchPaths << "/usr/X11R6/lib/X11/XF86Config";

        QStringList::ConstIterator it = searchPaths.begin();
        QStringList::ConstIterator end = searchPaths.end();
        for (; it != end; ++it )
            if ( KStandardDirs::exists( *it ) )
            {
                m_configFile = *it;
                break;
            }

    }

    kdDebug() << "KXFreeConfig(): config file '" << m_configFile << "'" << endl;
}

KXFreeConfig::~KXFreeConfig()
{
}

bool KXFreeConfig::loadConfig( const QString &configFile )
{
    kdDebug() << "KXFreeConfig::loadConfig(): '" << configFile << "'" << endl;

    if (!KXData::recreate()) return false;

    QString file;
    if ( configFile.isEmpty() ) {
        file = m_configFile;
    } else {
        file = configFile;
        m_configFile = QString::null;
    }

    QFile f( file );
    if ( !f.open( IO_ReadOnly ) )
        return true;

    Parser *parser = new Parser;

    QStringList flatNodes;
    flatNodes << "dri";

    m_config = parser->parse( &f, flatNodes );

    delete parser;

    if ( isXFree3XFile() )
        return false;

    //dumpNode(m_config);

    // iterate over main sections
    Node::List sections = m_config->children();
    Node::List::ConstIterator it = sections.begin();
    Node::List::ConstIterator end = sections.end();
    for (; it != end; ++it )
        if ( (*it)->name().lower() == "section" )
        {
            // ### add more

            QString section = (*it)->value().stringValue().lower();

            if ( section == "inputdevice" )
                parseInputDeviceSection( *it );

            if ( section == "serverflags" )
                parseServerFlagsSection( *it );

            if ( section == "monitor" )
                parseMonitorSection( *it );

            if ( section == "device" )
                parseDeviceSection( *it );

            if ( section == "screen" )
                parseScreenSection( *it );

            if ( section == "modes" )
                parseModesSection( *it );
        }

    return true;
}

QString KXFreeConfig::configFile() const
{
    return m_configFile;
}

/**
 * Parses a input device section. Based on the identifier key mouse or
 * keyboard configuration is parsed and applied to the global data settings
 * (see KXData::global() ) .
 */
void KXFreeConfig::parseInputDeviceSection( Node::Ptr section )
{
    kdDebug() << "found input device section!" << endl;

    // find for a child node with the name "identifier", case insensitive
    Node::Ptr identifierNode = section->children().namedItem( "identifier", false );

    if ( !identifierNode )
    {
        kdDebug() << "incomplete input device section :( missing identifier" << endl;
        return;
    }

    QString identifier = identifierNode->value().stringValue().lower(); //convert to lower case on the fly
    
//    kdDebug() << "---id: " << identifier << endl;

    if ( identifier == "mouse" || identifier == "mouse0" || identifier == "mouse[0]" ) // ### hack
    {
        kdDebug() << "found mouse section :-)" << endl;

        // find child nodes which look like "Option Foo"
        Node::Ptr protocolNode = findOptionNode( section, "protocol" );
        Node::Ptr deviceNode = findOptionNode( section, "device" );
        Node::Ptr emulate3rdNode = findOptionNode( section, "emulate3buttons" );
        Node::Ptr zAxisNode = findOptionNode( section, "zaxismapping" );

        // ### fixme: multiple mice
        // parseBoolOptionValue tries to interpret the given node as boolean option and checks
        // for all the possible combination how a boolean value can be represented in the config
        // file. the result we directly store in the global configuration.
        KXData::global()->currentPointers()[ 0 ].setEmulate3rd( parseBoolOptionValue( emulate3rdNode ) );

        if ( protocolNode )
        {
            QString mouseProtocol = protocolNode->values()[ 1 ].stringValue().lower();
            QString device;

            if ( deviceNode )
                device = deviceNode->values()[ 1 ].stringValue();

            kdDebug() << "found protocol node!" << endl;
            kdDebug() << "protocol appears to be " << mouseProtocol << endl;
            kdDebug() << "device appears to be " << device << endl;

            // find a pointer model that fits the specified model in the configuration
            // file and use it in the global configuration.
            // ### fixme: multiple pointers
            KXPointerModelList lst = KXData::global()->pointerModels();
            QValueListConstIterator<KXPointerModel> it = lst.begin();
            QValueListConstIterator<KXPointerModel> end = lst.end();
            for (; it != end; ++it ) {
                if ( (*it).xfreeName().lower() == mouseProtocol.lower() )
                {
                    KXPointerModel model = *it;

                    if ( zAxisNode && model.wheels() == 0 ) continue;

                    if ( !device.isEmpty() )
                        model.setDevice( device );

                    KXData::global()->currentPointers()[ 0 ].setPointerModel( model );

                    break;
                }
            }
        }

        KXData::global()->currentPointers()[ 0 ].setNode( section );
    }

    if ( identifier == "keyboard" || identifier == "keyboard0" || identifier == "keyboard[0]" ) // ### hack
    {
        kdDebug() << "found keyboard section" << endl;

        Node::Ptr modelNode = findOptionNode( section, "xkbmodel" );
        Node::Ptr layoutNode = findOptionNode( section, "xkblayout" );

        if ( modelNode )
        {
            QString keyboardModel = modelNode->values()[ 1 ].stringValue();

            kdDebug() << "found keyboard model " << keyboardModel << endl;

            // find a keyboard model that fits the specified model in the
            // config file and set it as model for the current keyboard
            // in the global configuration
            // ### fixme: multiple keyboards
            KXKeyboardModelList models = KXData::global()->keyboardModels();
            QValueList<KXKeyboardModel>::ConstIterator it = models.begin();
            QValueList<KXKeyboardModel>::ConstIterator end = models.end();
            for (; it != end; ++it )
                if ( (*it).id() == keyboardModel )
                {
                    KXData::global()->currentKeyboards()[ 0 ].setModel( *it );
                    break;
                }
        }

        if ( layoutNode )
        {
            QString keyboardLayout = layoutNode->values()[ 1 ].stringValue();

            kdDebug() << "found layout " << keyboardLayout << endl;

            // find a keyboard layout that fits the specified layout in the
            // config file and set it as layout for the current keyboard
            // in the global configuration
            // ### fixme: multiple keyboards
            KXKeyboardLayoutList layouts = KXData::global()->keyboardLayouts();
            QValueList<KXKeyboardLayout>::ConstIterator it = layouts.begin();
            QValueList<KXKeyboardLayout>::ConstIterator end = layouts.end();
            for (; it != end; ++it )
                if ( (*it).id() == keyboardLayout )
                {
                    // ### hack
                    KXData::global()->currentKeyboards()[ 0 ].setLayout( *it );
                    break;
                }
        }
        
        KXData::global()->currentKeyboards()[ 0 ].setNode( section );
    }
}

void KXFreeConfig::parseServerFlagsSection( Node::Ptr section )
{
    Node::Ptr dontZapNode = findOptionNode( section, "dontzap" );

    KXData::global()->generalServerData().setZapAllowed( !parseBoolOptionValue( dontZapNode ) );
}

void KXFreeConfig::parseMonitorSection( Node::Ptr section )
{
    Node::List children = section->children();

    Node::Ptr identifierNode = children.namedItem( "identifier", false );
    Node::Ptr vendorNode = children.namedItem( "vendorname", false );
    Node::Ptr modelNode = children.namedItem( "modelname", false );
    Node::Ptr horizSyncNode = children.namedItem( "horizsync", false );
    Node::Ptr vertRefreshNode = children.namedItem( "vertrefresh", false );

    QString vendor, model;

    if ( !identifierNode )
    {
        kdDebug() << "Warning! Monitor section without Identifier." << endl;
        return;
    }

    if ( vendorNode )
    {
        vendor = vendorNode->values()[ 0 ].stringValue();
        // ### HACK!!!!
        if ( vendor == "Typical Monitors" )
            vendor = i18n( "Typical Monitors" );
        kdDebug() << vendor << endl;
    }
    else
    {
        vendor = "[" + identifierNode->value().stringValue() + "]";
    }

    if ( modelNode )
    {
        model = modelNode->values()[ 0 ].stringValue();
        kdDebug() << model << endl;
    }
    else
    {
        model = "[" + identifierNode->value().stringValue() + "]";
    }


    float hmin = 0;
    float hmax = 0;
    float vmin = 0;
    float vmax = 0;

    if ( horizSyncNode )
    {
        kdDebug() << horizSyncNode->values()[ 0 ].stringValue() << endl;
        parseRangeValue( horizSyncNode, hmin, hmax );
    }

    if ( vertRefreshNode )
    {
        kdDebug() << vertRefreshNode->values()[ 0 ].stringValue() << endl;
        parseRangeValue( vertRefreshNode, vmin, vmax );
    }

    KXModeDataList modes;

    Node::List::ConstIterator it = children.begin();
    Node::List::ConstIterator end = children.end();
    for (; it != end; ++it )
        if ((*it)->name().lower() == "modeline")
        {
            KXModeData mode = parseModeLine( *it );
            if ( mode.isValid() ) {
                modes << mode;
            }
        }
        else if ((*it)->name().lower() == "mode")
        {
            // TODO: parsing of multi-line Mode entry
            kdDebug() << "multi-line Mode entry" << endl;

            QString name = (*it)->value().stringValue();
            float dotClock = 0;
            QString flags;

            Node::List modeChildren = (*it)->children();

            Node::Ptr dotClockNode = modeChildren.namedItem( "dotclock", false);
            Node::Ptr hTimingsNode = modeChildren.namedItem( "htimings", false);
            Node::Ptr vTimingsNode = modeChildren.namedItem( "vtimings", false);
            Node::Ptr flagsNode = modeChildren.namedItem( "flags", false);

            if (dotClockNode)
                dotClock = dotClockNode->value().floatValue();
            else
                kdDebug() << " -- dotClock entry not found" << endl;

            KXModeTiming hTimings;
            if (hTimingsNode)
            {
                NodeValue::List values = hTimingsNode->values();

                hTimings.resolution = values[ 0 ].intValue();
                hTimings.syncBegin = values[ 1 ].intValue();
                hTimings.syncEnd = values[ 2 ].intValue();
                hTimings.total = values[ 3 ].intValue();
            }

            KXModeTiming vTimings;
            if (vTimingsNode)
            {
                NodeValue::List values = vTimingsNode->values();

                vTimings.resolution = values[ 0 ].intValue();
                vTimings.syncBegin = values[ 1 ].intValue();
                vTimings.syncEnd = values[ 2 ].intValue();
                vTimings.total = values[ 3 ].intValue();
            }

            KXModeData mode( name, dotClock, hTimings, vTimings, flags );
            mode.setNode( (*it) );

            modes << mode;
        }

    int size = 0; // Nothing known about screen size

    KXMonitorData monitor( vendor, model, size, hmin, hmax, vmin, vmax, modes );
    monitor.setNode( section );

    KXData::global()->currentMonitors()[ 0 ] = monitor;

    KXData::global()->currentModes() += modes;

    // If current monitor isn't in the database, add it.
    bool found = false;
    KXMonitorDataMap monitors = KXData::global()->monitors();
    KXMonitorDataMap::ConstIterator vendorIt = monitors.begin();
    KXMonitorDataMap::ConstIterator vendorEnd = monitors.end();
    for (; vendorIt != vendorEnd; ++vendorIt )
    {
        if ( vendorIt.key() == monitor.vendor() )
        {
            KXMonitorDataList models = *vendorIt;
            KXMonitorDataList::ConstIterator modelIt = models.begin();
            KXMonitorDataList::ConstIterator modelEnd = models.end();
            for (; modelIt != modelEnd; ++modelIt )
            {
                if ( (*modelIt) == monitor )
                {
                    found = true;
                    break;
                }
            }
            break;
        }
    }
    if ( !found ) {
        kdDebug() << "KXFreeConfig::parseMonitor(): Adding monitor to database" << endl;
        kdDebug() << "  Vendor: " << monitor.vendor() << endl;
        
        KXData::global()->monitors()[ monitor.vendor() ].append( monitor );
    }

#if 0
    // Print out the modes that were found in the monitor section
    KXModeDataList::ConstIterator it2 = modes.begin();
    KXModeDataList::ConstIterator end2 = modes.end();
    for(; it2 != end2; ++it2 )
    {
        kdDebug() << "-- " << (*it2).modeLine() << endl;
    }

    // Print out all modes
    KXModeDataList allModes = KXData::global()->currentModes();
    KXModeDataList::ConstIterator it3 = allModes.begin();
    KXModeDataList::ConstIterator end3 = allModes.end();
    for(; it3 != end3; ++it3 )
    {
        kdDebug() << "== " << (*it3).modeLine() << endl;
    }
#endif
}

KXModeData KXFreeConfig::parseModeLine( Node::Ptr node )
{
    QString name;
    float dotClock = 0;
    QString flags;

    NodeValue::List values = node->values();

    if (values.count() < 10)
    {
        kdDebug() << "Illegal Mode Line" << endl;
        return KXModeData();
    }
    name = values[0].stringValue();

    dotClock = values[1].floatValue();

    KXModeTiming hTimings, vTimings;

    hTimings.resolution = values[ 2 ].intValue();
    hTimings.syncBegin = values[ 3 ].intValue();
    hTimings.syncEnd = values[ 4 ].intValue();
    hTimings.total = values[ 5 ].intValue();

    vTimings.resolution = values[ 6 ].intValue();
    vTimings.syncBegin = values[ 7 ].intValue();
    vTimings.syncEnd = values[ 8 ].intValue();
    vTimings.total = values[ 9 ].intValue();

    if (values.count() > 10) { // ### h/vskew stored in flags
        for(uint i=10;i<values.count();++i)
            flags += values[i].stringValue();
    }

    KXModeData mode( name, dotClock, hTimings, vTimings, flags );
    mode.setNode( node );

    return mode;
}

void KXFreeConfig::parseDeviceSection( Node::Ptr section )
{
    Node::List children = section->children();

    Node::Ptr identifierNode = children.namedItem( "identifier", false );
    Node::Ptr driverNode = children.namedItem( "driver", false );
    Node::Ptr vendorNode = children.namedItem( "vendorname", false );
    Node::Ptr boardNode = children.namedItem( "boardname", false );
    Node::Ptr busIDNode = children.namedItem( "busid", false );
    Node::Ptr ramNode = findOptionNode( section, "X-KXCONFIG-VideoRam" );
    Node::Ptr clockNode = findOptionNode( section, "X-KXCONFIG-MaxClock" );

    // these two are mandatory!
    if ( !identifierNode || !driverNode )
        return;

    QString driver, vendor, board;

    driver = driverNode->values()[ 0 ].stringValue();

    if ( driver.isEmpty() )
        return;

    if ( vendorNode )
        vendor = vendorNode->values()[ 0 ].stringValue();

    if ( boardNode )
        board = boardNode->values()[ 0 ].stringValue();

    int ram = 0;
    if ( ramNode ) {
        ram = ramNode->values()[ 1 ].intValue();
    }

    int maxClock = 0;
    if ( clockNode ) {
        maxClock = clockNode->values()[ 1].intValue();
    }

    KXVideoCardData currentCard( vendor, board, "", 0, 0, "", driver, ram, maxClock );

    uint pciVendorID = 0;
    uint pciDeviceID = 0;

    // do we have detailed pci information? if yes then let's use that
    // to find the correct video card
    if ( busIDNode && busIDNode->value().stringValue().startsWith( "PCI:" ) )
    {
        QString pciBus, pciSlot, pciFunc;

        QStringList splittedPCIString = QStringList::split( ':', busIDNode->value().stringValue() );
        assert( splittedPCIString.count() == 4 );
        pciBus = splittedPCIString[ 1 ];
        pciSlot = splittedPCIString[ 2 ];
        pciFunc = splittedPCIString[ 3 ];

        PCIInfo::lookupVendorAndDeviceID( pciBus + ':' + pciSlot + ':' + pciFunc, pciVendorID, pciDeviceID );
    }

    // ### fixme: multiple cards

    bool foundCard = false;
    KXVideoCardDataList vcardList;

    if ( pciVendorID != 0 && pciVendorID != 0 )
    {
        qDebug( "trying to find the vcard by using the pci info..." );

        vcardList = KXData::global()->videoCards()[ KXData::lookupVendor( pciVendorID ) ];

        if ( vcardList.count() == 0 )
        {
            kdDebug() << "unknown vendor?!" << endl;
            return;
        }

        KXVideoCardDataList::ConstIterator it = vcardList.begin();
        KXVideoCardDataList::ConstIterator end = vcardList.end();
        for (; it != end ; ++it )
        {
            if ( (*it).pciVendor() == pciVendorID &&
                 (*it).pciDevice() == pciDeviceID )
            {
                KXData::global()->currentVideoCards()[ 0 ] = *it;
                foundCard = true;
                break;
            }
        }
    }
    else if ( !vendor.isEmpty() )
    {

        vcardList = KXData::global()->videoCards()[ vendor ];

        if ( vcardList.count() > 0 ) {
            kdDebug() << board << " " << vendor << endl;

            KXVideoCardDataList::ConstIterator it = vcardList.begin();
            KXVideoCardDataList::ConstIterator end = vcardList.end();
            for (; it != end ; ++it ) {
                if ( (*it).model() == board &&
                     (*it).vendor() == vendor ) {
                    KXData::global()->currentVideoCards()[ 0 ] = *it;
                    foundCard = true;
                    break;
                }
            }
        } else
            kdDebug() << "unknown vendor " << vendor << endl;

    }

    if ( !foundCard ) {
        // try hard to find the generic item
        vcardList = KXData::global()->videoCards()[ "Generic" ];
        KXVideoCardDataList::ConstIterator it = vcardList.begin();
        KXVideoCardDataList::ConstIterator end = vcardList.end(); 
        for (; it != end; ++it ) if ( (*it).model() == driver ) {
            KXData::global()->currentVideoCards()[ 0 ] = *it; 
            foundCard = true; 
            break; 
        } 
    }

    if ( !foundCard ) {
        // Add card to database, if it does not yet exist.
        vcardList = KXData::global()->videoCards()[ vendor ];
        KXVideoCardDataList::ConstIterator it;
        for( it = vcardList.begin(); it != vcardList.end(); ++it ) 
            if ( (*it).model() == currentCard.model() )
               break;

        if ( it == vcardList.end() ) 
        KXData::global()->videoCards()[ vendor ].append( currentCard );
        KXData::global()->currentVideoCards()[ 0 ] = currentCard;
    }

    KXData::global()->currentVideoCards()[ 0 ].setNode( section );
}

void KXFreeConfig::parseScreenSection( Node::Ptr section )
{
    kdDebug() << "found screen section" << endl;

    Node::List children = section->children();

    Node::Ptr defaultDepthNode = children.namedItem( "defaultdepth", false );

    if ( !defaultDepthNode )
        return;

    QString defaultDepthString;
    defaultDepthString = defaultDepthNode->values()[ 0 ].stringValue();

    KXData::global()->currentScreens()[ 0 ].setDefaultDepth(defaultDepthString.toInt());

    KXDisplayDataList displays;

    // Go through all children of Screen section to find the display subsections
    Node::List::ConstIterator it = children.begin();
    Node::List::ConstIterator end = children.end();
    for (; it != end; ++it )
    {
        if ( (*it)->name().lower() == "subsection" &&
             (*it)->value().stringValue().lower() == "display" )
        {
            Node::List displayChildren = (*it)->children();

            // Parse display subsection
            Node::Ptr depthNode = displayChildren.namedItem( "depth", false );
            Node::Ptr modesNode = displayChildren.namedItem( "modes", false );
            Node::Ptr virtualResolutionNode = displayChildren.namedItem( "virtual", false );

            int depth = 8;
            if (depthNode) depth = depthNode->value().intValue();

            // copy Modes values into QStringList for KXDisplayData
            QStringList modes;
            if (modesNode)
            {
                NodeValue::List modesValues = modesNode->values();
                NodeValue::List::ConstIterator it2 = modesValues.begin();
                NodeValue::List::ConstIterator end2 = modesValues.end();
                for (; it2 != end2; ++it2 )
                {
                    modes << (*it2).stringValue();
                }
            }
            // default value, if no mode string was found
            if (modes.count() == 0) modes << "640x480";

            QSize virtualResolution( 0, 0 );
            if (virtualResolutionNode && virtualResolutionNode->values().count() == 2)
            {
                virtualResolution = QSize( virtualResolutionNode->values()[ 0 ].intValue(),
                                           virtualResolutionNode->values()[ 1 ].intValue() );
            }

            kdDebug() << "parseDisplay: " << depth << ", " << modes.join(" & ") << ", ("
                      << virtualResolution.width() << "," << virtualResolution.height()
                      << ")" << endl;

            displays << KXDisplayData( depth, modes, virtualResolution );
        }
    }

    KXData::global()->currentScreens()[ 0 ].setDisplays( displays );
}

void KXFreeConfig::parseModesSection( Node::Ptr section )
{
    KXModeDataList modes;

    Node::List children = section->children();
    Node::List::ConstIterator it = children.begin();
    for ( ; it != children.end(); ++it )
        if ( ( *it )->name().lower() == "modeline" ) {
            KXModeData mode = parseModeLine( *it );
            if ( mode.isValid() ) {
                modes << mode;
            }
        }

    KXData::global()->currentModes() += modes;
}

void KXFreeConfig::saveKeyboardConfig()
{
    // ### fixme: multiple keyboards
    KXKeyboardModel model = KXData::global()->currentKeyboards()[ 0 ].model();
    KXKeyboardLayout layout = KXData::global()->currentKeyboards()[ 0 ].layout();

    /**
     * Locate the keyboard section and modify/set the appropriate XkbModel and
     * XkbLayout configuration entries
     */

    Node::Ptr section = KXData::global()->currentKeyboards()[ 0 ].node();
    if ( !section ) {
        section = sectionByChildNode( m_config,
                                      "InputDevice",
                                      "Identifier",
                                      "Keyboard", true /* createIfMissing */ );
    }

    assert( section.operator->() );

    Node::Ptr modelNode = findOptionNode( section, "XkbModel", true );
    Node::Ptr layoutNode = findOptionNode( section, "XkbLayout", true );

    Node::Ptr driverNode = section->children().namedItem( "driver", false );
    if ( !driverNode )
    {
        Node::Ptr n = new Node;
        n->setName( "Driver" );
        n->setLead( "\t" );
        n->setValue( NodeValue( QString::fromLatin1( "keyboard" ) , true, " " ) );
        section->appendChild( n );
    }

    assert( modelNode );
    assert( layoutNode );

    // set keyboard model and layout. it's done by taking the current specified
    // values, removing all specified values expect for the first (which is the
    // string "XkbModel" or "XkbLayout", append the configured model/layout
    // value (<< operator) and store the values in the option node for
    // model/layout

    // set XKbModel entry

    // setup the model value with the given keyboard model string ( model.id() ),
    // an empty spacing (after the value) and specify that the string should
    // be quoted. So that the result looks like this:
    // Option XkbModel "BlahModel"
    // ^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^
    // |       |        |
    // |       |        + This is the entry we add after removing all other
    // |       |          Entries from the node's values() list (except the
    // |       |          first) .
    // |       |
    // |       + This is the first entry of the node's values() list, being a
    // |         NodeValue object with "XkbModel" as string value,
    // |         having a spacing of " " (one space, could however be anything
    // |         else specified in the config file) and being unquoted
    // |
    // + This is the node's name (see name() method)
    setOptionValue( modelNode, model.id() );

    // set XkbLayout entry
    setOptionValue( layoutNode, layout.id() );
}

void KXFreeConfig::savePointerConfig()
{
    KXPointerData data = KXData::global()->currentPointers()[ 0 ];

    Node::Ptr section = data.node();
    if ( !section ) {
        section = sectionByChildNode( m_config,
                                      "InputDevice",
                                      "Identifier",
                                      "Mouse", true /* createIfMissing */ );
    }

    Node::Ptr protocolNode = findOptionNode( section, "Protocol", true );
    Node::Ptr deviceNode = findOptionNode( section, "Device", true );

    Node::Ptr driverNode = section->children().namedItem( "driver", false );
    if ( !driverNode )
    {
        // create a new driver node looking like this (when being saved):
        // \tab Driver "mouse"
        Node::Ptr n = new Node;
        n->setLead( "\t" );
        n->setName( "Driver" );
        n->setValue( NodeValue( QString::fromLatin1( "mouse" ), true, " " ) );
        section->appendChild( n );
    }

    // locate the Emulate3Buttons option node and
    // (second argument) create it only if emulation is enabled
    // (which means we want the Emulate3Buttons option)
    Node::Ptr emulate3rdNode = findOptionNode( section, "Emulate3Buttons", data.emulate3rd() );

    // if emulation is disabled and an option node was found, then delete it
    if ( !data.emulate3rd() && emulate3rdNode )
        section->removeChild( emulate3rdNode );

    assert( protocolNode );
    assert( deviceNode );

    // See saveKeyboardConfig() what the following two blocks do in detail.

    // set Protocol entry
    kdDebug() << "saving protocol " << data.pointerModel().xfreeName() << endl;
    setOptionValue( protocolNode, data.pointerModel().xfreeName() );

    // set Device entry

    if ( data.port().isEmpty() ) // don't write out empty Device "" lines
    {
        setOptionValue( deviceNode, "/dev/mouse" );
    } else {
        setOptionValue( deviceNode, data.port() );
    }

    bool createZAxisMappingIfMissing = data.pointerModel().wheels() > 0;

    Node::Ptr zAxisMappingNode = findOptionNode( section, "ZAxisMapping", createZAxisMappingIfMissing );

    if ( !createZAxisMappingIfMissing )
        section->removeChild( zAxisMappingNode );
    else
    {
        setOptionValue( zAxisMappingNode, QString::fromLatin1( "4 5" ) );
    }
}

void KXFreeConfig::saveServerFlagsConfig()
{
    // locate the node corresponding to the ServerFlags section (and create it if
    // not existing, yet)
    Node::Ptr section = sectionByName( m_config, "ServerFlags", true /* createIfMissing */ );

    assert( section );

    // locate the DontZap option node in the ServerFlags section and
    // (second argument) create it only if zapping is disabled
    // (which means we want the DontZap option)
    Node::Ptr zapNode = findOptionNode( section, "DontZap", !KXData::global()->generalServerData().zapAllowed() );

    // if zapping is allowed and there exists a DontZap node then remove it
    if ( KXData::global()->generalServerData().zapAllowed() && zapNode )
        section->removeChild( zapNode );
}

void KXFreeConfig::saveScreenConfig( const QString &graphicsDeviceName )
{
    // locate the node corresponding to the first Screen section (and create it if
    // not existing, yet).
    Node::Ptr section = sectionByName( m_config, "Screen", true /* createIfMissing */ );

    assert( section );

    Node::Ptr deviceNode = findNode( section, "device" );
    deviceNode->setValue( NodeValue( graphicsDeviceName ) );

    bool depthFound = false;
    int defaultDepth = KXData::global()->currentScreens()[ 0 ].defaultDepth();
    QSize vRes = KXData::global()->currentScreens()[ 0 ].displays()[ 0 ].virtualResolution();
    QStringList modes = KXData::global()->currentScreens()[ 0 ].displays()[ 0 ].modes();

    Node::List children = section->children();

    // Go through Screen subnodes and fill in correponding values
    Node::List::Iterator it = children.begin();
    Node::List::Iterator end = children.end();
    for (; it != end; ++it )
    {
        if ( (*it)->name().lower() == "defaultdepth" )
        {
            // This works as long the corresponding display section already
            // exists.
            (*it)->setValue( NodeValue( defaultDepth ) );
        }
        else if ( (*it)->name().lower() == "subsection" &&
             (*it)->value().stringValue().lower() == "display" )
        {
            Node::List displayChildren = (*it)->children();

            bool hasVirtual = false;

            // Iterate through display section and fill in the Modes entries
            Node::List::Iterator it2 = displayChildren.begin();
            Node::List::Iterator end2 = displayChildren.end();
            for(; it2 != end2; ++it2)
            {
                if ( (*it2)->name().lower() == "depth" )
                {
                    int depth = (*it2)->value().intValue();
                    if ( depth == defaultDepth )
                        depthFound = true;
                }
                else if ( (*it2)->name().lower() == "modes" )
                {
                    NodeValue::List modeValues;

		    bool defaultModeSeen = false;
		    
                    QStringList::ConstIterator it3 = modes.begin();
                    QStringList::ConstIterator end3 = modes.end();
                    for(; it3 != end3; ++it3)
                    {
		        if (*it3 == DEFAULT_MODE)
			  defaultModeSeen = true;
			
                        modeValues << NodeValue( *it3, true, " " );
                    }
		    if (!defaultModeSeen)
		      modeValues << NodeValue(DEFAULT_MODE, true, " " );
		    
                    (*it2)->setValues(modeValues);
                }
                else if ( (*it2)->name().lower() == "virtual" )
                {
                    NodeValue::List values;

                    values << NodeValue(  vRes.width() , false, " " );
                    values << NodeValue(  vRes.height() );
                    (*it2)->setValues(values);

                    hasVirtual = true;

//                    kdDebug() << "** write back virtual: " << vRes.width() << "," << vRes.height() << endl;
                }
            }
            
            if ( !hasVirtual ) {
                NodeValue::List values;
                Node::Ptr n = new Node;
                n->setLead( "\t\t" );
                n->setName( "Virtual" );
                values << NodeValue( vRes.width(), false, " " ) << NodeValue( vRes.height(), false );
                n->setValues( values );
                (*it)->appendChild( n );
            }
        }
    }

    if ( !depthFound )
    {
        kdDebug() << "No Display subsection for default depth" << endl;

        Node::Ptr display = new Node;
        display->setLead( "\t" );
        display->setName( "Subsection" );
        display->setValue( NodeValue( QString::fromLatin1( "Display" ), true, " " ) );
        section->appendChild( display );

        Node::Ptr n = new Node;
        n->setLead( "\t\t" );
        n->setName( "Depth" );
        n->setValue( NodeValue( defaultDepth ) );
        display->appendChild( n );

        n = new Node;
        n->setLead( "\t\t" );
        n->setName( "Modes" );
        NodeValue::List values;
        QStringList::ConstIterator it4 = modes.begin();
        QStringList::ConstIterator end4 = modes.end();
        for(; it4 != end4; ++it4)
        {
            values << NodeValue( *it4, true, " " );
        }
        n->setValues(values);
        display->appendChild( n );

        values.clear();
        n = new Node;
        n->setLead( "\t\t" );
        n->setName( "Virtual" );
        values << NodeValue( vRes.width(), false, " " ) << NodeValue( vRes.height(), false );
        n->setValues( values );
        display->appendChild( n );

        n = new Node;
        n->setLead( "\t" );
        n->setName( "EndSubsection" );
        display->appendChild( n );
    }
}

void KXFreeConfig::saveMonitorConfig()
{
//    kdDebug() << "KXFreeConfig::saveMonitorConfig()" << endl;

    KXMonitorData monitor = KXData::global()->currentMonitors()[ 0 ];

    Node::Ptr section = monitor.node();

    if ( !section->parent() ) {
        kdDebug() << "Monitor section does not have a parent." << endl;
    }

    // create section if it didn't exist before.
    if ( !section )
    {
        // TODO: create new section
        kdDebug() << "Monitor section didn't exist" << endl;
        return;
    }

#if 0
    kdDebug() << "Vendor: " << monitor.vendor() << "  Model: " << monitor.model()
              << "  HSync: " << monitor.hSyncString()
              << "  VSync: " << monitor.vSyncString() << endl;
#endif

    Node::Ptr vendorNode = findNode( section, "vendorname" );
    // ### HACK !!!
    QString vendor = monitor.vendor();
    if ( vendor == i18n( "Typical Monitors" ) )
        vendor = QString::fromLatin1( "Typical Monitors" );
    vendorNode->setValue( NodeValue( vendor ) );

    Node::Ptr modelNode = findNode( section, "modelname" );
    modelNode->setValue( NodeValue( monitor.model() ) );

    if ( monitor.hSyncMax() > 0 )
    {
        Node::Ptr hSyncNode = findNode( section, "HorizSync" );
        hSyncNode->setValue( NodeValue( monitor.hSyncString(), false ) );
    }

    if ( monitor.vSyncMax() > 0 )
    {
        Node::Ptr vSyncNode = findNode( section, "VertRefresh" );
        vSyncNode->setValue( NodeValue( monitor.vSyncString(), false ) );
    }

//    dumpNode( section );

    // Iterate through used modes
    QStringList currentModes = KXData::global()->currentScreens()[ 0 ].displays()[ 0 ].modes();
    QStringList::ConstIterator modeNameIt = currentModes.begin();
    QStringList::ConstIterator modeNameEnd = currentModes.end();
    for(; modeNameIt != modeNameEnd; ++modeNameIt)
    {
//        kdDebug() << "  used mode: " << (*modeNameIt) << endl;

        // Look, if monitor section contains current mode
        Node::List children = section->children();
        Node::List::ConstIterator modeLineIt;
//        kdDebug() << "---- MonitorModeLines" << endl;
        for (modeLineIt = children.begin(); modeLineIt != children.end(); ++modeLineIt ) {
            if ((*modeLineIt)->name().lower() == "modeline") {
                KXModeData mode = parseModeLine( *modeLineIt );
                if ( mode.isValid() ) {
//                    kdDebug() << "---- ModeLine: " << mode.modeLine() << endl;
                    if ( mode.name() == (*modeNameIt) ) break;
                }
            }
        }

        if ( modeLineIt == children.end() )
        {
            // Monitor data object does not contain current mode. Look it up in the global
            // modes list and add it to the monitor, if it is not already in the config file.
            KXModeDataList globalModes = KXData::global()->currentModes();
            KXModeDataList::Iterator it2 = KXData::global()->currentModes().begin();
            KXModeDataList::Iterator end2 = KXData::global()->currentModes().end();
            for(; it2 != end2; ++it2)
            {
//                kdDebug() << "  current: " << (*it2).name() << endl;
                if ( (*it2).name() == (*modeNameIt) && !(*it2).node() )
                {
                    kdDebug() << "Adding Mode " << (*it2).name() << " to config." << endl;
                    Node::Ptr n = new Node;
                    n->setName( "ModeLine" );
                    n->setValue( NodeValue( (*it2).modeLine(), false ) );
                    section->appendChild( n );
                    n->setLead( vendorNode->lead() );
                    n->setMidSpacing( vendorNode->midSpacing() );
                    (*it2).setNode( n );
                    break;
                }
            }
        }
    }
}

/**
 * Returns the name of the device the configuration was written for.
 * (to be used in saveMonitorConfig())
 */
QString KXFreeConfig::saveDeviceConfig()
{
    QString deviceName;

    KXVideoCardData videoCard = KXData::global()->currentVideoCards()[ 0 ];

    Node::Ptr section = KXData::global()->currentVideoCards()[ 0 ].node();

    if ( !section )
    {
        // create section if it didn't exist before.
        kdDebug() << "Creating new Device section." << endl;

        section = createSection(m_config,"Device");

        deviceName = QString::fromLatin1( "Card0" );
        Node::Ptr n = new Node;
        n->setLead( "\t" );
        n->setName( "Identifier" );
        n->setValue( NodeValue( deviceName ) );
        section->appendChild( n );

        n = new Node;
        n->setLead( "\t" );
        n->setName( "Driver" );
        n->setValue( NodeValue( videoCard.driver() ) );
        section->appendChild( n );

        n = new Node;
        n->setLead( "\t" );
        n->setName( "VendorName" );
        n->setValue( NodeValue( videoCard.vendor() ) );
        section->appendChild( n );

        n = new Node;
        n->setLead( "\t" );
        n->setName( "BoardName" );
        n->setValue( NodeValue( videoCard.model() ) );
        section->appendChild( n );
        
        KXData::global()->currentVideoCards()[ 0 ].setNode( section );
    }
    else
    {
        bool driverChanged = false;

        Node::Ptr vendorNode = findNode( section, "vendorname" );

        driverChanged |= ( vendorNode->value().stringValue() != videoCard.vendor() );

        vendorNode->setValue( NodeValue( videoCard.vendor() ) );

        Node::Ptr modelNode = findNode( section, "boardname" );

//      driverChanged |= ( modelNode->value().stringValue() != videoCard.model() );

        modelNode->setValue( NodeValue( videoCard.model() ) );

        Node::Ptr driverNode = findNode( section, "driver" );

        driverChanged |= ( driverNode->value().stringValue() != videoCard.driver() );

        driverNode->setValue( NodeValue( videoCard.driver() ) );

        if ( driverChanged ) // if the driver changed then we have to remove the
                             // nice busid string ;(
        {
            Node::Ptr busIdNode = findNode( section, "busid" );
            section->removeChild( busIdNode );
        }


        Node::Ptr videoRamNode = findOptionNode( section, mNodeNameVideoRam );
        setOptionValue( videoRamNode, QString::number( videoCard.videoRam() ) );
        
        Node::Ptr clockNode = findOptionNode( section, mNodeNamePixelClock );
        setOptionValue( clockNode, QString::number( videoCard.maxClock() ) );
        
        
        Node::Ptr identifierNode = findNode( section, "identifier" );
        deviceName = identifierNode->value().stringValue();        
    }

    return deviceName;
}

bool KXFreeConfig::saveConfig()
{
    kdDebug() << "KXFreeConfig::saveConfig()" << endl;

    QString configFile = m_configFile;

    if ( configFile.isEmpty() )
        configFile = findSuitableConfigFilePath();

    assert( !configFile.isEmpty() );

    QFile newCfg( configFile + ".new" );

    if ( !newCfg.open( IO_WriteOnly ) )
    {
        KMessageBox::error( 0, i18n( "Cannot create file\n%1\nfor writing!" ).arg( newCfg.name() ) );
        return false;
    }

    QTextStream stream( &newCfg );

    if ( !m_config )
        m_config = new Node;

    saveKeyboardConfig();
    savePointerConfig();
    saveServerFlagsConfig();
    QString graphicsDeviceName = saveDeviceConfig();
    saveScreenConfig( graphicsDeviceName );
    saveMonitorConfig();

#if 0
    kdDebug() << "Dump output tree: " << endl;
    dumpNode( m_config );
    kdDebug() << "Dumped output tree." << endl;
#endif

    m_config->save( stream );
    kdDebug() << "Saved config to " << newCfg.name() << endl;

    // Backup original file
    if ( !mBackupWritten ) {
        QFile backupCfg( configFile + ".kxconfig.backup" );
        system("cp " + QFile::encodeName( configFile ) + " " + QFile::encodeName( backupCfg.name() ) );
        mBackupWritten = true;
    }

    if ( ::rename( QFile::encodeName( newCfg.name() ),
	           QFile::encodeName( configFile ) ) != 0 )
    {
        KMessageBox::error( 0, i18n( "Cannot rename\n%1\nto\n%2!" ).arg( newCfg.name() ).arg( configFile ) );
	return false;
    }

    kdDebug() << "config successfully saved to " << configFile << endl;

    /* I disabled this code, because I think it causes more harm than doing any
     * good. It works for Caldera, but I'm not sure about playing with symlinks
     * on other distros. (Simon)
    struct stat stbuf1,stbuf2;
    if (-1!=::stat("/usr/X11R6/bin/XFree86",&stbuf1)) {

	if (	(-1==::stat("/usr/X11R6/bin/X",&stbuf2))	||
		(stbuf2.st_dev!=stbuf1.st_dev)		||
		(stbuf2.st_ino!=stbuf1.st_ino)
	) {
		kdDebug() << "relinking X->XFree86" << endl;
		::unlink("/usr/X11R6/bin/X");
		if (-1==::symlink("XFree86","/usr/X11R6/bin/X")) 
			kdDebug() << "relinking X->Xfree86 failed " << errno << endl;
	}

    }
    */

    return true;
}

Node::Ptr KXFreeConfig::findOptionNode( Node::Ptr parent, const QString &name, bool createIfMissing )
{
    Node::List list = parent->children();
    Node::List::ConstIterator it = list.begin();
    Node::List::ConstIterator end = list.end();
    for (; it != end; ++it )
        if ( (*it)->name().lower() == "option" &&
             (*it)->value().stringValue().lower() == name.lower() )
            return *it;

    if ( createIfMissing )
    {
        Node::Ptr res = createOptionNode( parent, name );
//        createNewLine( parent );
        return res;
    }

    return 0;
}

Node::Ptr KXFreeConfig::createOptionNode( Node::Ptr parent, const QString &name )
{
    // create a new node object, append it to <parent> and return it.
    // the node looks like this:
    // \tab Option "<name>"

    Node::Ptr n = new Node;

    n->setLead( "\t" );
    n->setName( "Option" );

    NodeValue::List values;
    values << NodeValue( name );
    n->setValues( values );

    parent->appendChild( n );
    return n;
}

/**
 * Locates a section node by looking at the section name and
 * a specific child node (specified by node name and the node's
 * string value) .
 *
 * Example: sectionByChildNode( m_config, "InputDevice", "Identifier",
 *                              "Keyboard", ... )
 * ...looks for the InputDevice section in m_config which has a
 * child node looking like 'Identifier "Keyboard" '
 */
Node::Ptr KXFreeConfig::sectionByChildNode( Node::Ptr parent,
                                            const QString &sectionName,
                                            const QString &nodeName,
                                            const QString &nodeStringValue,
                                            bool createIfMissing )
{
    Node::List children = parent->children();
    Node::List::ConstIterator it = children.begin();
    Node::List::ConstIterator end = children.end();
    for (; it != end; ++it )
        if ( (*it)->name().lower() == "section" )
        {
            if ( (*it)->value().stringValue().lower() == sectionName.lower() )
            {
                Node::Ptr res = findStringNode( (*it)->children(),
                                                nodeName,
                                                nodeStringValue );

                if ( res )
                    return *it;
            }
        }

    if ( createIfMissing )
        return createSectionWithNode( parent,
                                      sectionName,
                                      nodeName,
                                      nodeStringValue );

    return 0;
}

/**
 * Locates a section node by looking at the section name.
 * See saveServerFlagsConfig() for an example of usage.
 */
Node::Ptr KXFreeConfig::sectionByName( Node::Ptr parent,
                                       const QString &sectionName,
                                       bool createIfMissing )
{
    Node::List children = parent->children();
    Node::List::ConstIterator it = children.begin();
    Node::List::ConstIterator end = children.end();
    for (; it != end; ++it )
        if ( (*it)->name().lower() == "section" &&
             (*it)->value().stringValue().lower() == sectionName.lower() )
                return *it;

    if ( createIfMissing )
        return createSection( parent, sectionName );

    return 0;
}

/**
 * Locate a node by looking at the node's name and the string value
 * of the first entry in the node's value list. Used by
 * sectionByChildNode.
 */
Node::Ptr KXFreeConfig::findStringNode( const Node::List &list,
                                        const QString &nodeName,
                                        const QString &nodeStringValue )
{
    Node::List::ConstIterator it = list.begin();
    Node::List::ConstIterator end = list.end();
    for (; it != end; ++it )
        if ( (*it)->name().lower() == nodeName.lower() &&
             (*it)->value().stringValue().lower() == nodeStringValue.lower() )
            return *it;

    return 0;
}

/**
  Find node with given name by searching the children of the given parent node.
  If the node is not found it is created with no values.
*/
Node::Ptr KXFreeConfig::findNode( Node::Ptr parent, const QString &name )
{
    Node::List children = parent->children();
    Node::Ptr res;

    Node::List::ConstIterator it = children.begin();
    Node::List::ConstIterator end = children.end();
    for (; it != end; ++it )
        if ( (*it)->name().lower() == name.lower() )
        {
            res = *it;
            break;
        }

    if ( !res )
    {
        res = new Node;
        res->setName ( name );
        parent->appendChild ( res );
    }

    return res;
}

/**
 * Creates a plain new node which equals a plain newline (\n) when
 * being saved.
 */
void KXFreeConfig::createNewLine( Node::Ptr parent )
{
    Node::Ptr n = new Node;
    parent->appendChild( n );
}

/**
 * 1) creates a new section node object with <sectionName> being the
 *    section node's name (first entry in node's value list)
 * 2) creates a new node with <nodeName> as name and <nodeStringValue>
 *    as first entry of the node's value list and appends it as
 *    child to the newly created section node
 * 3) appends the section node and a "EndSection" node to the specified
 *    <parent> node
 */
Node::Ptr KXFreeConfig::createSectionWithNode( Node::Ptr parent,
                                               const QString &sectionName,
                                               const QString &nodeName,
                                               const QString &nodeStringValue )
{
    createNewLine( parent );

    Node::Ptr section = new Node;
    parent->appendChild( section );

    Node::Ptr endSection = new Node;
    parent->appendChild( endSection );

    createNewLine( parent );

    section->setName( "Section" );
    endSection->setName( "EndSection" );

    section->setValue( NodeValue( sectionName ) );

    createNewLine( section );

    Node::Ptr n = new Node;
    section->appendChild( n );

    n->setName( nodeName );
    n->setValue( NodeValue( nodeStringValue ) );

    createNewLine( section );

    return section;
}

/**
 * Similar to createSectionWithNode but without creating a child node :)
 */
Node::Ptr KXFreeConfig::createSection( Node::Ptr parent,
                                       const QString &sectionName )
{
    createNewLine( parent);

    Node::Ptr section = new Node;
    parent->appendChild( section );

    Node::Ptr endSection = new Node;
    parent->appendChild( endSection );

    section->setName( "Section" );
    endSection->setName( "EndSection" );

    section->setValue( NodeValue( sectionName ) );

    createNewLine( section );

    return section;
}

/**
 * This method tries to interpret the given node as a boolean value and checks
 * for all sorts of allowed combination of how to specify a boolean value in
 * the xfree config file.
 */
bool KXFreeConfig::parseBoolOptionValue( const Node::Ptr &node )
{
    if ( !node )
        return false;

    assert( node->name().lower() == "option" );

    if ( node->values().count() == 1 ) // Option "Blah" --> true
        return true;

    if ( node->values().count() > 2 ) // Option "Blah" foo bar -> false ??
        return false;

    NodeValue val = node->values()[ 1 ];
    QString valStr = val.stringValue().lower();

    if ( ( valStr == "yes" || valStr == "true" || valStr == "1" || valStr == "on" ) )
        return true;

    if ( val.intValue() == 1 )
        return true;

    return false;
}

/**
  Parse a range value of the form "min-max" and return the values in the references
  given as function parameters.
*/
void KXFreeConfig::parseRangeValue( const Node::Ptr &node, float &min, float &max )
{
    QStringList values = QStringList::split( '-', node->value().stringValue() );
    if ( values.count() != 2 )
    {
        kdDebug() << "Illegal range value: " << node->value().stringValue() << endl;
        return;
    }

    min = values[ 0 ].toFloat();
    max = values[ 1 ].toFloat();
}

void KXFreeConfig::dumpNode(Node::Ptr n)
{
    static int indent = 0;

    QString spaces;
    spaces.fill(' ',indent);

    if (!n->name().isEmpty())
        kdDebug() << "* " << spaces << "Node: " << n->name() << " " << n->value().stringValue() << endl;

    Node::List children = n->children();
    if (children.count() > 0)
    {
        indent += 4;
        Node::List::ConstIterator it = children.begin();
        Node::List::ConstIterator end = children.end();
        for (; it != end; ++it )
            dumpNode( *it );
        indent -= 4;
    }
}

QString KXFreeConfig::findSuitableConfigFilePath()
{
    QStringList standardDirectories;
    standardDirectories << "/etc/X11/";
    standardDirectories << "/etc/";
    standardDirectories << "/usr/X11R6/etc/X11/";
    standardDirectories << "/usr/X11R6/lib/X11/";

    QStringList::ConstIterator it = standardDirectories.begin();
    QStringList::ConstIterator end = standardDirectories.end();
    for (; it != end; ++it )
        if ( KStandardDirs::exists( *it ) )
            return *it + QString::fromLatin1( "XF86Config-4" );

    return QString::null;
}

// add hooks here for detection of xfree 3.x config files
bool KXFreeConfig::isXFree3XFile()
{
    // bad sections
    QStringList badSections; badSections << "pointer" << "xinput";

    Node::List sections = m_config->children();
    Node::List::ConstIterator it = sections.begin();
    Node::List::ConstIterator end = sections.end();
    for (; it != end; ++it )
        if ( (*it)->name().lower() == "section" )
        {
            // ### add more

            QString section = (*it)->value().stringValue().lower();

            if ( badSections.findIndex( section ) != -1 )
                return true;
        }

    return false;
}

void KXFreeConfig::setOptionValue( Node::Ptr optionNode, const QString &value )
{
    if ( !optionNode ) return;

    NodeValue::List values = optionNode->values();

    while ( values.count() > 1 )
        values.remove( values.fromLast() );

    NodeValue nodeValue( value, true );

    values << nodeValue;

    if ( values[ 0 ].spacing().isEmpty() ) values[ 0 ].setSpacing( " " );

    optionNode->setValues( values );
}

#if 0
void KXFreeConfig::dumpCurrentMonitor()
{
    KXMonitorData monitor = KXData::global()->currentMonitors()[ 0 ];

    kdDebug() << "KXFreeConfig::dumpCurrentMonitor(): vendor: " << monitor.vendor() << endl;

    KXMonitorDataList vendorList = KXData::global()->monitors()[ monitor.vendor() ];

    KXMonitorDataList::ConstIterator it2 = vendorList.begin();
    KXMonitorDataList::ConstIterator end = vendorList.end();
    for (; it2 != end; ++it2 )
    {
        kdDebug() << " .. " << (*it2).model() << endl;
        if ( monitor.model() == (*it2).model() )
        {
            kdDebug() << " parseMon() found: " << (*it2).model() << endl;

            kdDebug() << "  *it.node(): "
                      << ((*it2).node() ? "node" : "no node") << endl;
            kdDebug() << "  *it.node.parent(): "
                      << ((*it2).node()->parent() ? "node" : "no node") << endl;
        }
    }
}
#endif

/* vim: et sw=4 ts=4
 */
