Logo Search packages:      
Sourcecode: kde-guidance version File versions  Download package

displayconfig.py

#!/usr/bin/python
# -*- coding: UTF-8 -*-
###########################################################################
# displayconfig.py - description                                          #
# ------------------------------                                          #
# begin     : Fri Mar 26 2004                                             #
# copyright : (C) 2004-2006 by Simon Edwards                              #
# email     : simon@simonzone.com                                         #
#                                                                         #
###########################################################################
#                                                                         #
#   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.                                   #
#                                                                         #
###########################################################################

from qt import *
from kdecore import *
from kdeui import *
import xorgconfig
import xf86misc
import string
import math
import os
import select
import sys
import struct
import csv
import time
import signal
import shutil
from ktimerdialog import *
from displayconfigwidgets import *
from displayconfigabstraction import *
from execwithcapture import *

programname = "Display and Graphics Configuration"
version = "0.6.7"
DUAL_PREVIEW_SIZE = 240

# Are we running as a separate standalone application or in KControl?
standalone = __name__=='__main__'

# Running as the root user or not?
isroot = os.getuid()==0

############################################################################
class GfxCardDialog(KDialogBase):
    video_ram_list = [256,512,1024,2048,4096,8192,16384,32768,65536]
    
    def __init__(self,parent):
        KDialogBase.__init__(self,parent,None,True,"Choose Graphics Card", 
                            KDialogBase.Ok|KDialogBase.Cancel, KDialogBase.Cancel)

        self.gfxcarddb = None
        self.updatingGUI = True
        self.card2listitem = {}
        
        topbox = QVBox(self)
        topbox.setSpacing(KDialog.spacingHint())
        self.setMainWidget(topbox)
        label = QLabel(topbox)
        label.setText(i18n("Select Graphics Card:"))
        self.listview = KListView(topbox)
        self.listview.addColumn("")
        self.listview.header().hide()
        self.listview.setRootIsDecorated(True)
        self.connect(self.listview,SIGNAL("selectionChanged(QListViewItem *)"),self.slotListClicked)
        topbox.setStretchFactor(self.listview,1)
        
        self.driver = KListViewItem(self.listview)
        self.driver.setText(0,i18n("Drivers"))
        self.driver.setSelectable(False)
        
        self.manufacturer = KListViewItem(self.listview)
        self.manufacturer.setText(0,i18n("Manufacturers"))
        self.manufacturer.setSelectable(False)
        
        hbox = QHBox(topbox)
        topbox.setStretchFactor(hbox,0)
        vbox = QVBox(hbox)
        
        self.detected_label = QLabel("",vbox)
        
        self.detected_button = KPushButton(vbox)
        self.detected_button.setText(i18n("Select"))
        self.connect(self.detected_button,SIGNAL("clicked()"),self.slotSelectDetectedClicked)
        
        spacer = QWidget(vbox)
        vbox.setStretchFactor(self.detected_button,0)
        vbox.setStretchFactor(spacer,1)

        hbox.setStretchFactor(vbox,0)
        spacer = QWidget(hbox)
        hbox.setStretchFactor(spacer,1)
        
        drivergrid = QGrid(2,hbox)
        drivergrid.setSpacing(KDialog.spacingHint())
        QLabel(i18n("Driver:"),drivergrid)
        self.standarddriverradio = QRadioButton(i18n("Standard"),drivergrid)
        self.connect(self.standarddriverradio,SIGNAL("clicked()"),self.slotStandardDriverClicked)
        QWidget(drivergrid)
        self.proprietarydriverradio = QRadioButton(i18n("Proprietary"),drivergrid)
        self.connect(self.proprietarydriverradio,SIGNAL("clicked()"),self.slotProprietaryDriverClicked)
        
        QLabel(i18n("Video RAM:"),drivergrid)
        self.videoramcombo = QComboBox(drivergrid)
        for s in [i18n("256 kB"),
                i18n("512 kB"),
                i18n("1 MB"),
                i18n("2 MB"),
                i18n("4 MB"),
                i18n("8 MB"),
                i18n("16 MB"),
                i18n("32 MB"),
                i18n("64 MB or more")]:
            self.videoramcombo.insertItem(s)    
        
        self.updatingGUI = False
        self._setGfxCardDB(GetGfxCardModelDB())
        
    def _setGfxCardDB(self,gfxcarddb):
        self.updatingGUI = True
        self.gfxcarddb = gfxcarddb
        
        # Add the GfxCards under the Manufacturer item.
        keys = gfxcarddb.vendordb.keys()
        keys.sort()
        for key in keys:
            cardkeys = self.gfxcarddb.vendordb[key].keys()
            vendoritem = KListViewItem(self.manufacturer)
            vendoritem.setText(0,key)
            vendoritem.setSelectable(False)
            for cardkey in cardkeys:
                item = KListViewItem(vendoritem)
                item.setText(0,cardkey)
                self.card2listitem[self.gfxcarddb.vendordb[key][cardkey]] = item

        # Add the GfxCard _drivers_ under the Drivers item
        drivers = gfxcarddb.driverdb.keys()
        drivers.sort()
        for driver in drivers:
            driveritem = KListViewItem(self.driver)
            driveritem.setText(0,driver)
            self.card2listitem[gfxcarddb.driverdb[driver]] = driveritem

        self.updatingGUI = False
        
    def do(self,card,proprietarydriver,detected_card,video_ram):
        self.updatingGUI = True
        item = self.card2listitem[card]
        self.listview.setSelected(item,True)
        self.listview.ensureItemVisible(item)

        self.selected_video_ram = video_ram
        
        if detected_card is None:
            self.detected_button.setEnabled(False)
            self.detected_label.setText(i18n("Detected graphics card:\n(unknown)"))
        else:
            self.detected_button.setEnabled(True)
            self.detected_label.setText(i18n("Detected graphics card:\n'%1'.").arg(detected_card.getName()))

        self.__syncDriver(card,proprietarydriver,video_ram)
        
        self.detected_card = detected_card
        self.selected_card = card
        self.updatingGUI = False
        
        if self.exec_loop()==QDialog.Accepted:
            return (self.selected_card,
                self.proprietarydriverradio.isChecked() and (self.selected_card is not None),
                self.video_ram_list[self.videoramcombo.currentItem()])
        else:
            return (card, proprietarydriver,video_ram)

    def __syncDriver(self,card,proprietarydriver,videoram):
        if card.getProprietaryDriver() is None:
            self.standarddriverradio.setChecked(True)
            self.standarddriverradio.setEnabled(False)
            self.proprietarydriverradio.setEnabled(False)
        else:
            self.standarddriverradio.setEnabled(True)
            self.proprietarydriverradio.setEnabled(True)
            self.standarddriverradio.setChecked(not proprietarydriver)
            self.proprietarydriverradio.setChecked(proprietarydriver)
            
        self.videoramcombo.setEnabled(card.getNeedVideoRam())
        if card.getNeedVideoRam():
            self.videoramcombo.setCurrentItem(self.video_ram_list.index(videoram))
            
    def slotSelectDetectedClicked(self):
        self.updatingGUI = True
        item = self.card2listitem[self.detected_card]
        self.listview.setSelected(item,True)
        self.listview.ensureItemVisible(item)
        self.selected_card = self.detected_card
        self.__syncDriver(self.selected_card, self.proprietarydriverradio.isChecked(), self.selected_video_ram)
        self.updatingGUI = False

    def slotListClicked(self,item):
        if self.updatingGUI:
            return
            
        for key in self.card2listitem:
            value = self.card2listitem[key]
            if value is item:
                self.selected_card = key
                self.__syncDriver(self.selected_card, self.proprietarydriverradio.isChecked(), self.selected_video_ram)
                
    def slotStandardDriverClicked(self):
            self.proprietarydriverradio.setChecked(False)
            self.standarddriverradio.setChecked(True)
            
    def slotProprietaryDriverClicked(self):
            self.standarddriverradio.setChecked(False)
            self.proprietarydriverradio.setChecked(True)
            
############################################################################
class MonitorDialog(KDialogBase):
    def __init__(self,parent):
        KDialogBase.__init__(self,parent,None,True,"Choose Monitor", 
            KDialogBase.Ok|KDialogBase.Cancel, KDialogBase.Cancel)
            
        self.monitordb = None
        self.selectedmonitor = None
        self.aspect = ModeLine.ASPECT_4_3
        self.monitor2listitem = {}
        self.updatingGUI = True
        
        topbox = QVBox(self)
        topbox.setSpacing(KDialog.spacingHint())
        self.setMainWidget(topbox)
        label = QLabel(topbox)
        label.setText(i18n("Select Monitor:"))
        self.listview = KListView(topbox)
        self.listview.addColumn("")
        self.listview.header().hide()
        self.listview.setRootIsDecorated(True)
        self.connect(self.listview,SIGNAL("selectionChanged(QListViewItem *)"),self.slotListClicked)
        
        self.generic = KListViewItem(self.listview)
        self.generic.setText(0,i18n("Generic"))
        self.generic.setSelectable(False)
        
        self.manufacturer = KListViewItem(self.listview)
        self.manufacturer.setText(0,i18n("Manufacturers"))
        self.manufacturer.setSelectable(False)
        
        grid = QGroupBox(4,QGroupBox.Horizontal,topbox)
        grid.setTitle(i18n("Details"))
        
        label = QLabel(grid)
        label.setText(i18n("Horizontal Range:"))
        
        self.horizrange = KLineEdit(grid)
        self.horizrange.setReadOnly(True)
        
        label = QLabel(grid)
        label.setText(i18n("Vertical Refresh:"))

        self.vertrange = KLineEdit(grid)
        self.vertrange.setReadOnly(True)
        
        hbox = QHBox(topbox)

        self.detectbutton = KPushButton(hbox)
        self.detectbutton.setText(i18n("Detect Monitor")) # FIXME better label/text?
        self.connect(self.detectbutton,SIGNAL("clicked()"),self.slotDetectClicked)

        spacer = QWidget(hbox)
        hbox.setStretchFactor(self.detectbutton,0)
        hbox.setStretchFactor(spacer,1)
        
        label = QLabel(hbox)
        label.setText(i18n("Image format:"))
        hbox.setStretchFactor(label,0)

        self.aspectcombobox = KComboBox(hbox)
        self.aspectcombobox.insertItem(i18n("Standard 4:3"))
        self.aspectcombobox.insertItem(i18n("Widescreen 16:9"))
        hbox.setStretchFactor(self.aspectcombobox,0)
        
        self.updatingGUI = False

    def setMonitorDB(self,monitordb):
        self.monitordb = monitordb
        
        # Add the Monitors
        vendors = monitordb.vendordb.keys()
        vendors.sort()
        for vendor in vendors:
            monitorkeys = self.monitordb.vendordb[vendor].keys()
            vendoritem = KListViewItem(self.manufacturer)
            vendoritem.setText(0,vendor)
            vendoritem.setSelectable(False)
            for monitorkey in monitorkeys:
                item = KListViewItem(vendoritem)
                item.setText(0,monitorkey)
                self.monitor2listitem[self.monitordb.vendordb[vendor][monitorkey]] = item

        generics = monitordb.genericdb.keys()
        generics.sort()
        for generic in generics:
            genericitem = KListViewItem(self.generic)
            genericitem.setText(0,generic)
            self.monitor2listitem[monitordb.genericdb[generic]] = genericitem
            
        customs = monitordb.getCustomMonitors().keys()
        customs.sort()
        for custom in customs:
            customitem = KListViewItem(self.listview)
            customitem.setText(0,custom)
            self.monitor2listitem[monitordb.getCustomMonitors()[custom]] = customitem
            
    def do(self,monitor,aspect,is_primary_monitor=True):
        """Run the monitor selection dialog.
        
        Parameters:
        
        monitor - Currently selected 'Monitor' object.
        
        Returns the newly selected monitor object and aspect ratio as a tuple.
        """
        if monitor is not None:
            self.selectedmonitor = monitor
            item = self.monitor2listitem[monitor]
                
            self.listview.setSelected(item,True)
            self.listview.ensureItemVisible(item)
            
        else:
            self.selectedmonitor = None
            self.listview.clearSelection()
        self.aspect = aspect
        
        # Only the first/primary monitor can be detected. :-/
        self.detectbutton.setEnabled(is_primary_monitor)
            
        self.updatingGUI = True
        self._syncGUI()
        self.updatingGUI = False

        if self.exec_loop()!=QDialog.Accepted:
            # Dialog was cancelled. Return the original monitor.
            self.selectedmonitor = monitor
        else:
            self.aspect = [ModeLine.ASPECT_4_3,ModeLine.ASPECT_16_9][self.aspectcombobox.currentItem()]

        return (self.selectedmonitor,self.aspect)

    def slotDetectClicked(self):
        detectedmonitor = self.monitordb.detect()
        if detectedmonitor is not None:
            self.selectedmonitor = detectedmonitor
            self._syncGUI()
        else:
            KMessageBox.error(self, i18n("Sorry, the model and capabilities of your\nmonitor couldn't be detected."),
                i18n("Monitor detection failed"))

    def slotListClicked(self,item):
        if self.updatingGUI:
            return
        self.updatingGUI = True
        for key in self.monitor2listitem:
            value = self.monitor2listitem[key]
            if value is item:
                self.selectedmonitor = key
                break
        self._syncGUI()
        self.updatingGUI = False
    
    def _syncGUI(self):
        if self.selectedmonitor is not None:
            item = self.monitor2listitem[self.selectedmonitor]
            self.listview.setSelected(item,True)
            self.listview.ensureItemVisible(item)
            self.vertrange.setText(self.selectedmonitor.getVerticalSync())
            self.horizrange.setText(self.selectedmonitor.getHorizontalSync())
        else:
            self.vertrange.setText("-")
            self.horizrange.setText("-")
        
        self.aspectcombobox.setCurrentItem({ModeLine.ASPECT_4_3:0,ModeLine.ASPECT_16_9:1}[self.aspect])
        
############################################################################
if standalone:
    programbase = KDialogBase
else:
    programbase = KCModule
    
############################################################################
class DisplayApp(programbase):
    ########################################################################
    def __init__(self,parent=None,name=None):
        global standalone,isroot,kapp
        KGlobal.locale().insertCatalogue("guidance")

        if standalone:
            KDialogBase.__init__(self,KJanusWidget.Tabbed,"Display Configuration",\
                KDialogBase.Apply|KDialogBase.User1|KDialogBase.User2|KDialogBase.Close, KDialogBase.Close)
            self.setButtonText(KDialogBase.User1,i18n("Reset"))
            self.setButtonText(KDialogBase.User2,i18n("About"))
        else:
            KCModule.__init__(self,parent,name)
            self.setButtons(KCModule.Apply|KCModule.Reset)
            self.aboutdata = MakeAboutData()
            
            # This line has the effect of hiding the "Admin only" message and also forcing
            # the Apply/Reset buttons to be shown. Yippie! Only had to read the source
            # to work that out.
            self.setUseRootOnlyMsg(False) 
            
        # Create a configuration object.
        self.config = KConfig("displayconfigrc")
        
        # Compact mode means that we have to make the GUI 
        # much smaller to fit on low resolution screens.
        self.compact_mode = kapp.desktop().height()<=600
        
        KGlobal.iconLoader().addAppDir("guidance")

        global imagedir
        imagedir = unicode(KGlobal.dirs().findDirs("data","guidance/pics/displayconfig")[0])
        
        self.imagedir = imagedir

        self.xconfigchanged = False
        self.xconfigtested = True
        
        self.availabletargetgammas = [unicode(i18n('1.4')),unicode(i18n('1.6')),unicode(i18n('1.8')),unicode(i18n('2.0')),unicode(i18n('2.2')),unicode(i18n('2.4'))]
        self.lightimages = []
        self.mediumimages = []
        self.darkimages = []

        # X Server stuff
        self.xf86server = xf86misc.XF86Server()
        
        self.xconfigpath = self._findXorgConfig()
        SetDataFileDir(unicode(KGlobal.dirs().findResourceDir("data","guidance/pcitable")) + "guidance/")
        self.xsetup = XSetup(self.xconfigpath)

        self.updatingGUI = True
        self.gfxcarddb = GfxCardModelDB()
        self.monitordb = GetMonitorModelDB()
        self.monitormodedb = GetMonitorModeDB()
        
        self._buildGUI()

        # Work out if the currently running Gfxdriver is safe enough that we
        # can test other drivers at the same time.
        self.badfbrestore = self._badFbRestore()
        self.testbutton.setEnabled(isroot and not self._badFbRestore())
        if isroot and not self._badFbRestore():
            self.testunavailablelabel.hide()
        else:
            self.testunavailablelabel.show()

        # Load up some of our databases, and initialise our state variables.
        if len(self.xsetup.getUsedScreens()):
            self.currentsizescreen = self.xsetup.getUsedScreens()[0]
            self.currentgammascreen = self.xsetup.getUsedScreens()[0]
        else:
            # FIXME
            print "Houston, we have a problem: No screens found in configuration file, exiting. :("
            sys.exit(1)
        
        self.monitordialog.setMonitorDB(self.monitordb)
        
        self.aboutus = KAboutApplication(self)

        # For centering the timed Apply dialog.
        self.applytimerdialog = None
        self.connect(kapp.desktop(), SIGNAL("resized(int)"), self.slotDesktopResized)
        self.applydialogscreenindex = 0
        
        self.__loadImages()
        self._loadConfig()
        self._syncGUI()
        
        if standalone:
            self.enableButton(KDialogBase.User1,False) # Reset button
            self.enableButtonApply(False) # Apply button

        self.updatingGUI = False
    
    def _findXorgConfig(self):
        # Lookup location of X configfile
        for line in ExecWithCapture("xset", ["xset","q"],True).split('\n'):
            if line.strip().startswith("Config file"):
                return line.split(":")[1].strip()
        # Sometimes, xset doesn't know about the configfile location, hence ...
        if os.path.isfile("/etc/X11/xorg.conf"):
                return "/etc/X11/xorg.conf"
        return None

    def _buildGUI(self):
        global standalone
        if not standalone:
            toplayout = QVBoxLayout( self, 0, KDialog.spacingHint() )
            tabcontrol = KTabCtl(self)
            toplayout.addWidget(tabcontrol)
            toplayout.setStretchFactor(tabcontrol,1)
            
        #--- Size, Orientation and Positioning ---
        tabname = i18n("Size, Orientation && Positioning")
        if standalone:
            sopage = self.addGridPage(1,QGrid.Horizontal,tabname)
            sopage.setSpacing(0)
            self.SizePage = SizeOrientationPage(sopage,self.xsetup,self.compact_mode)
        else:
            self.SizePage = SizeOrientationPage(tabcontrol,self.xsetup,self.compact_mode)
            self.SizePage.setMargin(KDialog.marginHint())
            
        # Connect all PYSIGNALs from SizeOrientationPage Widget to appropriate actions.
        self.connect(self.SizePage,PYSIGNAL("changedSignal()"),self._sendChangedSignal)
        self.connect(self.SizePage,PYSIGNAL("resolutionChange(int)"),self.slotResolutionChange)
        
        if not standalone:
            tabcontrol.addTab(self.SizePage,tabname)
        
        #--- Color & Gamma tab ---
        tabname = i18n("Color && Gamma")
        if standalone:
            gammapage = self.addVBoxPage(tabname)
            vbox = QVBox(gammapage)
        else:
            vbox = QVBox(tabcontrol)
            vbox.setMargin(KDialog.marginHint())
        vbox.setSpacing(KDialog.spacingHint())
        
        hbox = QHBox(vbox)
        hbox.setSpacing(KDialog.spacingHint())
        vbox.setStretchFactor(hbox,0)
        label = QLabel(hbox,"textLabel1")
        label.setText(i18n("Screen:"))
        hbox.setStretchFactor(label,0)
        self.gammadisplaycombobox = QComboBox(0,hbox,"comboBox11")
        hbox.setStretchFactor(self.gammadisplaycombobox,0)
        spacer = QWidget(hbox)
        hbox.setStretchFactor(spacer,1)
        self.connect(self.gammadisplaycombobox,SIGNAL("activated(int)"),self.slotGammaScreenCombobox)
        
        # fill the combobox.
        for screen in self.xsetup.getUsedScreens():
            self.gammadisplaycombobox.insertItem(screen.getName())
        
        if not self.compact_mode:
            # Create the colour matching pics
            nospacervbox = QVBox(vbox)
            hbox = QHBox(nospacervbox)
            spacer = QWidget(hbox)
            hbox.setStretchFactor(spacer,1)
            self.darkpic = QLabel(hbox)
            self.darkpic.setFixedSize(305,105)
            self.mediumpic = QLabel(hbox)
            self.mediumpic.setFixedSize(305,105)
            spacer = QWidget(hbox)
            hbox.setStretchFactor(spacer,1)
            vbox2 = QVBox(nospacervbox)
            hbox = QHBox(vbox2)
            spacer = QWidget(hbox)
            hbox.setStretchFactor(spacer,1)
            self.lightpic = QLabel(hbox)
            self.lightpic.setFixedSize(305,105)
            self.rangepic = QLabel(hbox)
            self.rangepic.setFixedSize(305,105)
            spacer = QWidget(hbox)
            hbox.setStretchFactor(spacer,1)
            
            vbox2.setStretchFactor(hbox,0)
    
            label = QLabel(i18n("Adjust the gamma correction sliders until the color of the squares \nabove match their background as closely as possible."),vbox)
            label.setTextFormat(Qt.PlainText)
            vbox2.setStretchFactor(label,1)
            
            spacer = QWidget(vbox)
            vbox2.setStretchFactor(spacer,1)
        
        sliderspace = QWidget(vbox)
        
        grid = QGridLayout(sliderspace, 9, 4, 0, KDialog.spacingHint())
        grid.setSpacing(KDialog.spacingHint())
        grid.setColStretch(0,0)
        grid.setColStretch(1,0)
        grid.setColStretch(2,0)
        grid.setColStretch(3,1)

        label = QLabel(i18n("Gamma correction:"),sliderspace)
        grid.addWidget(label, 0, 0)
        
        self.gammaradiogroup = QButtonGroup()
        self.gammaradiogroup.setRadioButtonExclusive(True)
        self.connect(self.gammaradiogroup,SIGNAL("clicked(int)"),self.slotGammaRadioClicked)
        
        self.allradio = QRadioButton(sliderspace)
        grid.addWidget(self.allradio, 0, 1, Qt.AlignTop)
        
        label = QLabel(i18n("All:"),sliderspace)
        grid.addWidget(label, 0, 2)
        
        self.gammaslider = KDoubleNumInput(0.4, 3.5, 2.0, 0.05, 2, sliderspace, 'gammaslider')
        grid.addMultiCellWidget(self.gammaslider,0,1,3,3)
        self.gammaslider.setRange(0.5, 2.5, 0.05, True)
        self.connect(self.gammaslider, SIGNAL("valueChanged(double)"), self.slotGammaChanged)

        self.componentradio = QRadioButton(sliderspace)
        grid.addWidget(self.componentradio, 2, 1, Qt.AlignTop)

        label = QLabel(i18n("Red:"),sliderspace)
        grid.addWidget(label, 2, 2)

        self.redslider = KDoubleNumInput(self.gammaslider,0.4, 3.5, 2.0, 0.05, 2, sliderspace, 'redslider')
        grid.addMultiCellWidget(self.redslider,2,3,3,3)
        self.redslider.setRange(0.5, 2.5, 0.05, True)
        self.connect(self.redslider, SIGNAL("valueChanged(double)"), self.slotRedChanged)

        label = QLabel(i18n("Green:"),sliderspace)
        grid.addWidget(label, 4, 2)
        
        self.greenslider = KDoubleNumInput(self.redslider,0.4, 3.5, 2.0, 0.05, 2, sliderspace, 'greenslider')
        grid.addMultiCellWidget(self.greenslider,4,5,3,3)
        self.greenslider.setRange(0.5, 2.5, 0.05, True)
        self.connect(self.greenslider, SIGNAL("valueChanged(double)"), self.slotGreenChanged)

        label = QLabel(i18n("Blue:"),sliderspace)
        grid.addWidget(label, 6, 2)

        self.blueslider = KDoubleNumInput(self.greenslider,0.4, 3.5, 2.0, 0.05, 2, sliderspace, 'blueslider')
        grid.addMultiCellWidget(self.blueslider,6,7,3,3)
        self.blueslider.setRange(0.5, 2.5, 0.05, True)
        self.connect(self.blueslider, SIGNAL("valueChanged(double)"), self.slotBlueChanged)

        self.gammaradiogroup.insert(self.allradio,0)
        self.gammaradiogroup.insert(self.componentradio,1)

        if not self.compact_mode:
            label = QLabel(i18n("Target gamma:"),sliderspace)
            grid.addWidget(label, 8, 0)
    
            hbox = QHBox(sliderspace)
            self.targetgammacombo = KComboBox(False,hbox)
            self.targetgammacombo.insertItem(i18n('1.4'))
            self.targetgammacombo.insertItem(i18n('1.6'))
            self.targetgammacombo.insertItem(i18n('1.8 Apple Macintosh standard'))
            self.targetgammacombo.insertItem(i18n('2.0 Recommend'))
            self.targetgammacombo.insertItem(i18n('2.2 PC standard, sRGB'))
            self.targetgammacombo.insertItem(i18n('2.4'))
            hbox.setStretchFactor(self.targetgammacombo,0)
            spacer = QWidget(hbox)
            hbox.setStretchFactor(spacer,1)
            grid.addMultiCellWidget(hbox, 8, 8, 1, 3)
            
            self.connect(self.targetgammacombo,SIGNAL("activated(int)"),self.slotTargetGammaChanged)

        spacer = QWidget(vbox)
        vbox.setStretchFactor(spacer,1)

        if not standalone:
            tabcontrol.addTab(vbox,tabname)
        
        #--- Hardware tab ---
        if standalone:
            hardwarepage = self.addVBoxPage(i18n("Hardware"))
            vbox = QVBox(hardwarepage)
        else:
            vbox = QVBox(tabcontrol)
            vbox.setMargin(KDialog.marginHint())
        self.gfxcarddialog = GfxCardDialog(None)
        self.monitordialog = MonitorDialog(None)

        self.xscreenwidgets = []
        
        for gfxcard in self.xsetup.getGfxCards():
            w = GfxCardWidget(vbox,self.xsetup, gfxcard, self.gfxcarddialog, self.monitordialog)
            self.xscreenwidgets.append(w)
            self.connect(w,PYSIGNAL("configChanged"),self.slotConfigChanged)
            
        spacer = QWidget(vbox)
        vbox.setStretchFactor(spacer,1)

        if not self.xsetup.mayModifyXorgConfig():
            QLabel(i18n("Changes on this tab require 'root' access."),vbox)
            if not standalone:
                QLabel(i18n("Click the \"Administrator Mode\" button to allow modifications on this tab."),vbox)
        
        hbox = QHBox(vbox)
        hbox.setSpacing(KDialog.spacingHint())
        self.testbutton = KPushButton(i18n("Test"),hbox)
        self.connect(self.testbutton,SIGNAL("clicked()"),self.slotTestClicked)
        hbox.setStretchFactor(self.testbutton,0)
        
        self.testunavailablelabel = QHBox(hbox)
        self.testunavailablelabel.setSpacing(KDialog.spacingHint())
        tmplabel = QLabel(self.testunavailablelabel)
        self.testunavailablelabel.setStretchFactor(tmplabel,0)
        tmplabel.setPixmap(SmallIcon('info'))
        label = QLabel(i18n("This configuration cannot be safely tested."),self.testunavailablelabel)
        self.testunavailablelabel.setStretchFactor(label,1)
        self.testunavailablelabel.hide()
        
        spacer = QWidget(hbox)
        hbox.setStretchFactor(spacer,1)
        vbox.setStretchFactor(hbox,0)

        if not standalone:
            tabcontrol.addTab(vbox,i18n("Hardware"))
            
        #--- Display Power Saving ---
        tabname = i18n("Power saving")
        if standalone:
            powerpage = self.addGridPage(1,QGrid.Horizontal,tabname)
            self.dpmspage = DpmsPage(powerpage)
        else:
            self.dpmspage = DpmsPage(tabcontrol)
            self.dpmspage.setMargin(KDialog.marginHint())

        #self.SizePage.setScreens(self.xsetup.getScreens())
        
        # Connect all PYSIGNALs from SizeOrientationPage Widget to appropriate actions.
        #self.connect(self.SizePage,PYSIGNAL("dualheadEnabled(bool)"),self.slotDualheadEnabled)
        self.connect(self.dpmspage,PYSIGNAL("changedSignal()"),self._sendChangedSignal)
        
        if not standalone:
            tabcontrol.addTab(self.dpmspage,tabname)

    def save(self): # KCModule
        xorg_config_changed = self.xsetup.isXorgConfigChanged()
        restart_recommended = self.xsetup.getRestartHint()
        
        # Check the Size & Orientation tab.
        if self.applytimerdialog is None:
            self.applytimerdialog = KTimerDialog(15000, KTimerDialog.CountDown, self, "mainKTimerDialog",
                True, i18n("Confirm Display Setting Change"), KTimerDialog.Ok | KTimerDialog.Cancel, \
                KTimerDialog.Cancel)
            self.applytimerdialog.setButtonOK(KGuiItem(i18n("&Keep"), "button_ok"))
            self.applytimerdialog.setButtonCancel(KGuiItem(i18n("&Cancel"), "button_cancel"))
            label = KActiveLabel(i18n("Trying new screen settings. Keep these new settings? (Automatically cancelling in 15 seconds.)"),
                self.applytimerdialog, "userSpecifiedLabel")
            self.applytimerdialog.setMainWidget(label)
        
        if self.xsetup.isLiveResolutionConfigChanged():
            if self.xsetup.applyLiveResolutionChanges():
                # running X server config has changed. Ask the user.
                KDialog.centerOnScreen(self.applytimerdialog, 0)
                if self.applytimerdialog.exec_loop():
                    self.xsetup.acceptLiveResolutionChanges()
                else:
                    self.xsetup.rejectLiveResolutionChanges()
                    return
            else:
                # Nothing really changed, just accept the changes.
                self.xsetup.acceptLiveResolutionChanges()
                    
        
        self.xsetup.acceptLiveGammaChanges()
        self.dpmspage.apply()
        
        # Save the X server config.
        if isroot and xorg_config_changed:
            
            if not self.xconfigtested:
                if self.badfbrestore or self._badFbRestore():
                    if KMessageBox.warningContinueCancel(self, \
                            i18n("The selected driver and monitor configuration can not be safely tested on this computer.\nContinue with this configuration?"),
                            i18n("Configuration not tested"))!=KMessageBox.Continue:
                        return
                else:
                    if KMessageBox.warningContinueCancel(self,
                            i18n("The selected driver and monitor configuration has not been successfully tested on this computer.\nContinue with this configuration?"),
                            i18n("Configuration not tested"))!=KMessageBox.Continue:
                        return
            
            try:
                # Backup up the current config file.
                i = 1
                while os.path.exists("%s.%i" % (self.xconfigpath,i)):
                    i += 1
                shutil.copyfile(self.xconfigpath,"%s.%i" % (self.xconfigpath,i))
                
                # Write out the new config
                tmpfilename = self.xconfigpath + ".tmp"
                self.xsetup.writeXorgConfig(tmpfilename)

                os.rename(tmpfilename,self.xconfigpath)
            except (IOError,TypeError):
                print "******* Bang"
                raise
                return
                # FIXME error
            
            # FIXME the instructions in these messages are probably not quite right.
            if restart_recommended==XSetup.RESTART_X:
                KMessageBox.information(self,
                    i18n("Some changes require that the X server be restarted before they take effect. Log out and select \"Restart X server\" from the menu button."),
                    i18n("X Server restart recommend"))
        
            if restart_recommended==XSetup.RESTART_SYSTEM:
                KMessageBox.information(self,
                    i18n("Some changes require that the entire system be restarted before they take effect. Log out and select \"Restart computer\" from the log in screen."),
                    i18n("System restart recommend"))

        self._saveConfig()
        self._sendChangedSignal()
        
    # Called when the desktop is resized. Just center the confirm dialog.
    def slotDesktopResized(self):
        if self.applytimerdialog is not None:
            KDialog.centerOnScreen(self.applytimerdialog, self.applydialogscreenindex)

    def slotApply(self): # KDialogBase
        self.save()

    def slotClose(self): # KDialogBase
        self.xsetup.rejectLiveGammaChanges()
        KDialogBase.slotClose(self)
    
    def load(self): # KCModule
        self.__reset()
        self._sendChangedSignal()

    def slotUser1(self): # Reset button, KDialogBase
        self.load()

    def slotUser2(self): # About button, KDialogBase
        self.aboutus.show()
        
    def slotResolutionChange(self,i):
        self.currentsizescreen.setResolutionIndex(i)
        self._sendChangedSignal()
        
    def slotTargetGammaChanged(self,i):
        self.targetgamma = i
        self._selectGamma(self.targetgamma)
        self._sendChangedSignal()
    
    def slotGammaRadioClicked(self,i):
        self.settingall = i==0
        self.gammaslider.setDisabled(not self.settingall)
        self.redslider.setDisabled(self.settingall)
        self.greenslider.setDisabled(self.settingall)
        self.blueslider.setDisabled(self.settingall)
        
        if self.settingall:
            self.currentgammascreen.setAllGamma(self.currentgammascreen.getAllGamma())
        else:
            self.currentgammascreen.setRedGamma(self.currentgammascreen.getRedGamma())
            self.currentgammascreen.setGreenGamma(self.currentgammascreen.getGreenGamma())
            self.currentgammascreen.setBlueGamma(self.currentgammascreen.getBlueGamma())
        self._sendChangedSignal()
        
    def slotGammaChanged(self,value):
        if self.updatingGUI:
            return
        self.currentgammascreen.setAllGamma(value)
        self._sendChangedSignal()

    def slotRedChanged(self,value):
        if self.updatingGUI:
            return
        self.currentgammascreen.setRedGamma(value)
        self._sendChangedSignal()
        
    def slotGreenChanged(self,value):
        if self.updatingGUI:
            return
        self.currentgammascreen.setGreenGamma(value)
        self._sendChangedSignal()

    def slotBlueChanged(self,value):
        if self.updatingGUI:
            return
        self.currentgammascreen.setBlueGamma(value)
        self._sendChangedSignal()
        
    def slotGammaScreenCombobox(self,i):
        self.currentgammascreen = self.xsetup.getUsedScreens()[i]
        self._syncGUI()
        self._sendChangedSignal()

    def slotConfigChanged(self):
        self.xconfigchanged = True
        self.xconfigtested = False
        
        # Check if the current X config can be tested.
        self.SizePage._syncGUI()
        for widget in self.xscreenwidgets:
            widget.syncConfig()
        self._syncTestButton()
        self._sendChangedSignal()
        
    def slotTestClicked(self):
        self.xconfigtested = self.testX()
        
    def testX(self):
        
        self.xserverbin = "/usr/X11R6/bin/XFree86"
        if not os.path.isfile(self.xserverbin):
            self.xserverbin = "/usr/X11R6/bin/Xorg"
        rc = False
        
        # Remove an stale X server lock
        try: os.remove("/tmp/.X9-lock")
        except OSError: pass
        
        # Try to find a safe tmp dir.
        tmp_dir = None
        if os.environ.get("TMPDIR") is not None:
            tmp_dir = os.environ.get("TMPDIR")
        if tmp_dir is None or not os.path.isdir(tmp_dir):
            tmp_dir = os.path.join(os.environ.get("HOME"),"tmp")
            if not os.path.isdir(tmp_dir):
                tmp_dir = "/tmp"
        working_tmp_dir = os.path.join(tmp_dir,"guidance."+str(os.getpid()))
        error_filename = os.path.join(working_tmp_dir,"testserver.xoutput")
        config_filename = os.path.join(working_tmp_dir,"testserver.config")
        auth_filename = os.path.join(working_tmp_dir,"xauthority")
        
        # Start the Xserver up with the new config file.
        try:
            # Create our private dir.
            os.mkdir(working_tmp_dir,0700)
            
            # Backup the XAUTHORITY environment variable.
            old_xauthority = os.environ.get("XAUTHORITY",None)
            
            # Write out the new config file.
            self.xsetup.writeXorgConfig(config_filename)
        
            os.system("xauth -f %s add :9 . `mcookie`" % (auth_filename,) )
            # FIXME:: -xf86config is nowhere in man X ??
            pid = os.spawnv(os.P_NOWAIT,"/bin/bash",\
                ["bash","-c","exec %s :9 -xf86config %s -auth %s &> %s" % \
                    (self.xserverbin, config_filename, auth_filename, error_filename)])
            print "Got pid",pid

            # Wait for the server to show up.
            print str(os.waitpid(pid,os.WNOHANG))
            
            # Use our private xauthority file.
            os.environ["XAUTHORITY"] = auth_filename
            
            time.sleep(1) # Wait a sec.
            testserver = None
            while True:
                # Try connecting to the server.
                try:
                    testserver = xf86misc.XF86Server(":9")
                    break
                except xf86misc.XF86Error:
                    testserver = None
                # Check if the server process is still alive.
                if os.waitpid(pid,os.WNOHANG) != (0,0):
                    break
                time.sleep(1) # Give the server some more time.
            
            print "checkpoint 1"
            print str(testserver)
            
            if testserver is not None:
                # Start the timed popup on the :9 display.
                #servertestpy = str(KGlobal.dirs().findResource("data","guidance/servertestdialog.py"))
                servertestpy = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])),"servertestdialog.py")
                pythonexe = unicode(KStandardDirs.findExe("python"))
                
                testrc = os.system(pythonexe + " " + servertestpy + " '" + auth_filename+"' ")
                rc = (rc >> 8) == 0 # Test is good if the return code was 0.
                testserver = None
                os.kill(pid,signal.SIGINT)
            else:
                # Server failed, read the error info.
                msg = ""
                try:
                    fhandle = open(error_filename,'r')
                    for line in fhandle.readlines():
                        if (line.startswith("(EE)") and ("Disabling" not in line)) or line.startswith("Fatal"):
                            msg += line
                    msg = unicode(i18n("Messages from the X server:\n")) + msg
                except IOError:
                    msg += unicode(i18n("Sorry, unable to capture the error messages from the X server."))
                KMessageBox.detailedSorry(self,i18n("Sorry, this configuration video card driver\nand monitor doesn't appear to work."),msg)

        finally:
            # Attempt some cleanup before we go.
            try: os.remove(error_filename)
            except OSError: pass
            try: os.remove(config_filename)
            except OSError: pass
            try: os.remove(auth_filename)
            except OSError: pass
            try: os.rmdir(working_tmp_dir)
            except OSError: pass
            
            if old_xauthority is None:
                del os.environ["XAUTHORITY"]
            else:
                os.environ["XAUTHORITY"] = old_xauthority
                
        return rc
        
    def _syncGUI(self):
        self.SizePage._syncGUI()
        
        for gfxcard_widget in self.xscreenwidgets:
            gfxcard_widget.syncConfig()

        # Sync the gamma tab.
        if not self.compact_mode:
            self.targetgammacombo.setCurrentItem(self.targetgamma)
            self._selectGamma(self.targetgamma)
        
        if self.currentgammascreen.isGammaEqual():
            self.gammaradiogroup.setButton(0)
        else:
            self.gammaradiogroup.setButton(1)
        self.gammaslider.setValue(self.currentgammascreen.getAllGamma())
        self.redslider.setValue(self.currentgammascreen.getRedGamma())
        self.greenslider.setValue(self.currentgammascreen.getGreenGamma())
        self.blueslider.setValue(self.currentgammascreen.getBlueGamma())

        self.gammaslider.setDisabled(not self.currentgammascreen.isGammaEqual())
        self.redslider.setDisabled(self.currentgammascreen.isGammaEqual())
        self.greenslider.setDisabled(self.currentgammascreen.isGammaEqual())
        self.blueslider.setDisabled(self.currentgammascreen.isGammaEqual())
        self._syncTestButton()
        
    def _syncTestButton(self):
        currentbadfbrestore = self._badFbRestore()
        self.testbutton.setEnabled(self.xsetup.mayModifyXorgConfig() and not (self.badfbrestore or currentbadfbrestore))
        if not isroot or (self.xsetup.mayModifyXorgConfig() and not (self.badfbrestore or currentbadfbrestore)):
            self.testunavailablelabel.hide()
        else:
            self.testunavailablelabel.show()
        
    def _loadConfig(self):
        self.config.setGroup("General")
        t = self.config.readEntry("targetgamma","2.0")
        if t in self.availabletargetgammas:
            t = '2.0'
        self.targetgamma = self.availabletargetgammas.index(t)
        
    def _saveConfig(self):
        global isroot
        if isroot:
            return
        self.config.setGroup("General")
        self.config.writeEntry("targetgamma",self.availabletargetgammas[self.targetgamma])
        for s in self.xsetup.getUsedScreens():
            self._saveRandRConfig(s)
        self.config.sync()

    def _saveRandRConfig(self,screen):
        w,h = screen.getAvailableResolutions()[screen.getResolutionIndex()]
        self.config.setGroup("Screen"+str(screen.getScreenIndex()))
        self.config.writeEntry("width",w)
        self.config.writeEntry("height",h)
        self.config.writeEntry("reflectX", int( (screen.getReflection() & screen.RR_Reflect_X)!=0) )
        self.config.writeEntry("reflectY", int((screen.getReflection() & screen.RR_Reflect_Y)!=0) )
        self.config.writeEntry("refresh", screen.getAvailableRefreshRates()[screen.getRefreshRateIndex()])
        rotationmap = {screen.RR_Rotate_0: "0", screen.RR_Rotate_90: "90",
                        screen.RR_Rotate_180:"180", screen.RR_Rotate_270: "270"}
        self.config.writeEntry("rotate", rotationmap[screen.getRotation()])
        
    def _selectGamma(self,i):
        self.lightpic.setPixmap(self.lightimages[i])
        self.mediumpic.setPixmap(self.mediumimages[i])
        self.darkpic.setPixmap(self.darkimages[i])
        
    def __loadImages(self):
        if not self.compact_mode:
            for g in ['14','16','18','20','22','24']:
                self.lightimages.append( QPixmap(self.imagedir+'gammapics/Gamma'+g+'.png') )
                self.mediumimages.append( QPixmap(self.imagedir+'gammapics/MGam'+g+'.png') )
                self.darkimages.append( QPixmap(self.imagedir+'gammapics/DGam'+g+'.png') )
            self.rangepic.setPixmap(QPixmap(self.imagedir+'gammapics/ranges.png'))

        self.previewscreen = QPixmap(self.imagedir+'monitor_screen_1280x1024.png')
        self.previewscreenportrait = QPixmap(self.imagedir+'monitor_screen_1024x1280.png')
    
    def __reset(self):
        # Reset the screen settings.
        self.xsetup.reset()
        self.dpmspage.reset()
        self._syncGUI()
            
    # Kcontrol expects updates about whether the contents have changed.
    # Also we fake the Apply and Reset buttons here when running outside kcontrol.
    def _sendChangedSignal(self):
        global standalone
        
        changed = False
        for s in self.xsetup.getUsedScreens():
            changed = changed or s.isResolutionSettingsChanged()
        
        changed = changed or self.xsetup.isXorgConfigChanged()
        changed = changed or self.dpmspage.isChanged()
            
        if standalone:
            self.enableButton(KDialogBase.User1,changed) # Reset button
            self.enableButtonApply(changed) # Apply button
        else:
            self.emit(SIGNAL("changed(bool)"), (changed,) )

    def _badFbRestore(self):
        bad_fb_restore = False
        for card in self.xsetup.getGfxCards():
            bad_fb_restore = bad_fb_restore or \
                ((card.getGfxCardModel() is not None) and card.getGfxCardModel().getBadFbRestore(card.isProprietaryDriver()))
        return bad_fb_restore
        
############################################################################
01114 class SizeOrientationPage(QWidget):
    """
    A TabPage with all the settings for Size and Orientation of the screens,
    also features Refreshrates and Dualheadsettings.
    
    Emits the following signals:
    
        changeSignal()

        ...
        
        TODO:
            * Update __doc__ with emitted signals, connect these.
            * Choose screen (more than one preview)
            * Relative positioning.
            * Call setRefreshCombo after switching screens.
    """
    def __init__(self,parent,xsetup,compact):
        QWidget.__init__(self,parent)
        
        global imagedir
        self.xsetup = xsetup
        self.imagedir = imagedir
        self.parent = parent
        self.current_screen = self.xsetup.getPrimaryScreen()
        self.current_is_primary = True
        self.compact_mode = compact

        self._buildGUI()
        self._syncGUI()
    
    def _syncGUI(self):
        if self.current_is_primary:
            self.current_screen = self.xsetup.getPrimaryScreen()
        else:
            self.current_screen = self.xsetup.getSecondaryScreen()
        
        self._syncGUILayout()
        self._syncGUIScreen()
    
    def _syncGUILayout(self):
        # Secondary monitor radios.
        available_layouts = self.xsetup.getAvailableLayouts()
        
        may = self.xsetup.mayModifyLayout()
        self.secondary_clone_radio.setEnabled(may and available_layouts & self.xsetup.LAYOUT_CLONE)
        self.secondary_dual_radio.setEnabled(may and available_layouts & self.xsetup.LAYOUT_DUAL)
        self.secondary_position_combo.setEnabled(may and self.xsetup.getLayout()==self.xsetup.LAYOUT_DUAL)
        self.secondary_groupbox.setEnabled(may and available_layouts != self.xsetup.LAYOUT_SINGLE)
        
        if self.xsetup.getLayout()!=self.xsetup.LAYOUT_SINGLE:
            self.secondary_radios.setButton(self.secondary_option_ids[self.xsetup.getLayout()])
        else:
            if available_layouts & XSetup.LAYOUT_CLONE:
                self.secondary_radios.setButton(self.secondary_option_ids[XSetup.LAYOUT_CLONE])
            else:
                self.secondary_radios.setButton(self.secondary_option_ids[XSetup.LAYOUT_DUAL])
            
        self.secondary_groupbox.setChecked(self.xsetup.getLayout() != self.xsetup.LAYOUT_SINGLE)
        
    def _syncGUIScreen(self):
        # Sync the size tab.
        self.resize_slider.setScreen(self.current_screen)
        
        if self.xsetup.getLayout()!=self.xsetup.LAYOUT_DUAL:
            self.resize_slider.setTitle(i18n("Screen size"))
        else:
            self.resize_slider.setTitle(i18n("Screen size #%1").arg(self.xsetup.getUsedScreens().index(self.current_screen)+1))
            
        if self.xsetup.getLayout()==self.xsetup.LAYOUT_DUAL:
            if not self.compact_mode:
                self.monitor_preview_stack.raiseWidget(self.dual_monitor_preview)
        else:
            if not self.compact_mode:
                self.monitor_preview_stack.raiseWidget(self.monitor_preview)
            
        # Sync the screen orientation.
        width,height = self.current_screen.getAvailableResolutions()[self.current_screen.getResolutionIndex()]
        
        if not self.compact_mode:
            self.monitor_preview.setResolution(width,height)
            
            if self.current_screen.getRotation()==Screen.RR_Rotate_0:
                self.monitor_preview.setRotation(MonitorPreview.ROTATE_0)
            elif self.current_screen.getRotation()==Screen.RR_Rotate_90:
                self.monitor_preview.setRotation(MonitorPreview.ROTATE_90)
            elif self.current_screen.getRotation()==Screen.RR_Rotate_270:
                self.monitor_preview.setRotation(MonitorPreview.ROTATE_270)
            else:
                self.monitor_preview.setRotation(MonitorPreview.ROTATE_180)
    
            self.monitor_preview.setReflectX(self.current_screen.getReflection() & Screen.RR_Reflect_X)
            self.monitor_preview.setReflectY(self.current_screen.getReflection() & Screen.RR_Reflect_Y)

            # Set the resolutions for the dual screen preview.
            if self.xsetup.getAvailableLayouts() & XSetup.LAYOUT_DUAL:
                for i in [0,1]:
                    screen = [self.xsetup.getPrimaryScreen(), self.xsetup.getSecondaryScreen()][i]
                    width,height = screen.getAvailableResolutions()[screen.getResolutionIndex()]
                    self.dual_monitor_preview.setScreenResolution(i,width,height)
            self.dual_monitor_preview.setPosition(self.xsetup.getDualheadOrientation())
            
        self._fillRefreshCombo()

        self.orientation_radio_group.setButton( \
            [Screen.RR_Rotate_0, Screen.RR_Rotate_90, Screen.RR_Rotate_270, 
                Screen.RR_Rotate_180].index(self.current_screen.getRotation()))
        # This construct above just maps an rotation to a radiobutton index.
        self.mirror_horizontal_checkbox.setChecked(self.current_screen.getReflection() & Screen.RR_Reflect_X)
        self.mirror_vertical_checkbox.setChecked(self.current_screen.getReflection() & Screen.RR_Reflect_Y)
       
        width,height = self.current_screen.getAvailableResolutions()[self.current_screen.getResolutionIndex()]
        if not self.compact_mode:
            self.monitor_preview.setResolution(width,height)
        
        # Enable/disable the resolution/rotation/reflection widgets.
        may_edit = self.xsetup.mayModifyResolution()
        self.normal_orientation_radio.setEnabled(may_edit)
        self.left_orientation_radio.setEnabled((self.current_screen.getAvailableRotations() & Screen.RR_Rotate_90)!=0 and may_edit)
        self.right_orientation_radio.setEnabled((self.current_screen.getAvailableRotations() & Screen.RR_Rotate_270)!=0 and may_edit)
        self.upside_orientation_radio.setEnabled((self.current_screen.getAvailableRotations() & Screen.RR_Rotate_180)!=0 and may_edit)
        self.mirror_horizontal_checkbox.setEnabled((self.current_screen.getAvailableReflections() & Screen.RR_Reflect_X)!=0 and may_edit)
        self.mirror_vertical_checkbox.setEnabled((self.current_screen.getAvailableReflections() & Screen.RR_Reflect_Y)!=0 and may_edit)
        
        self.resize_slider.setEnabled(may_edit)
        self.size_refresh_combo.setEnabled(may_edit)
        
        # Set the dual orientation combo.
        self.secondary_position_combo.setCurrentItem(
            [XSetup.POSITION_LEFTOF,
            XSetup.POSITION_RIGHTOF,
            XSetup.POSITION_ABOVE,
            XSetup.POSITION_BELOW].index(self.xsetup.getDualheadOrientation()))
        
    def _fillRefreshCombo(self):
        # Update refresh combobox
        self.size_refresh_combo.clear()
        for rate in self.current_screen.getAvailableRefreshRates():
            self.size_refresh_combo.insertItem(i18n("%1 Hz").arg(rate))
        self.size_refresh_combo.setCurrentItem(self.current_screen.getRefreshRateIndex())

    def slotMonitorFocussed(self,currentMonitor):
        if currentMonitor==0:
            self.current_screen = self.xsetup.getPrimaryScreen()
            self.current_is_primary = True
        else:
            self.current_screen = self.xsetup.getSecondaryScreen()
            self.current_is_primary = False

        self._syncGUIScreen()
        
    def _sendChangedSignal(self):
        self.emit(PYSIGNAL("changedSignal()"),())

01268     def _buildGUI(self):
        """ Assemble all GUI elements """
        # Layout stuff.
        top_layout = QHBoxLayout(self,0,KDialog.spacingHint())
        self.top_layout = top_layout
        
        # -- Left column with orientation and dualhead box.
        vbox = QVBox(self)
        top_layout.addWidget(vbox,0)
        
        # -- Orientation group
        self.orientation_group_box = QVGroupBox(vbox)
        self.orientation_group_box.setTitle(i18n("Monitor Orientation"))
        self.orientation_group_box.setInsideSpacing(KDialog.spacingHint())
        self.orientation_group_box.setInsideMargin(KDialog.marginHint())
        self.orientation_radio_group = QButtonGroup()
        self.orientation_radio_group.setRadioButtonExclusive(True)
 
        self.normal_orientation_radio = QRadioButton(self.orientation_group_box)
        self.normal_orientation_radio.setText(i18n("Normal"))
        self.left_orientation_radio = QRadioButton(self.orientation_group_box)
        self.left_orientation_radio .setText(i18n("Left edge on top"))
        self.right_orientation_radio = QRadioButton(self.orientation_group_box)
        self.right_orientation_radio.setText(i18n("Right edge on top"))
        self.upside_orientation_radio = QRadioButton(self.orientation_group_box)
        self.upside_orientation_radio.setText(i18n("Upsidedown"))

        self.mirror_horizontal_checkbox = QCheckBox(self.orientation_group_box)
        self.mirror_horizontal_checkbox.setText(i18n("Mirror horizontally"))
        self.connect(self.mirror_horizontal_checkbox,SIGNAL("toggled(bool)"),self.slotMirrorHorizontallyToggled)
        
        self.mirror_vertical_checkbox = QCheckBox(self.orientation_group_box)
        self.mirror_vertical_checkbox.setText(i18n("Mirror vertically"))
        self.connect(self.mirror_vertical_checkbox,SIGNAL("toggled(bool)"),self.slotMirrorVerticallyToggled)
        
        self.orientation_radio_group.insert(self.normal_orientation_radio,0)
        self.orientation_radio_group.insert(self.left_orientation_radio,1)
        self.orientation_radio_group.insert(self.right_orientation_radio,2)
        self.orientation_radio_group.insert(self.upside_orientation_radio,3)
        self.connect(self.orientation_radio_group,SIGNAL("clicked(int)"),self.slotOrientationRadioClicked)

        # -- Dualhead Box.
        self.secondary_groupbox = QVGroupBox(vbox)
        self.secondary_groupbox.setCheckable(True)
        self.secondary_groupbox.setTitle(i18n("Second screen"))
        self.connect(self.secondary_groupbox,SIGNAL("toggled(bool)"),self.slotSecondMonitorToggled)
        
        self.secondary_radios = QVButtonGroup(None)  # Invisible
        self.connect(self.secondary_radios,SIGNAL("pressed(int)"),self.slotSecondMonitorRadioPressed)
        
        self.secondary_options = {}
        self.secondary_option_ids = {}
        
        # Clone radio
        self.secondary_clone_radio = QRadioButton(i18n("Clone primary screen"),self.secondary_groupbox)
        radio_id = self.secondary_radios.insert(self.secondary_clone_radio)
        self.secondary_options[radio_id] = self.xsetup.LAYOUT_CLONE
        self.secondary_option_ids[self.xsetup.LAYOUT_CLONE] = radio_id
        
        # Dual radio
        self.secondary_dual_radio = QRadioButton(i18n("Dual screen"),self.secondary_groupbox)
        radio_id = self.secondary_radios.insert(self.secondary_dual_radio)
        self.secondary_options[radio_id] = self.xsetup.LAYOUT_DUAL
        self.secondary_option_ids[self.xsetup.LAYOUT_DUAL] = radio_id
        
        self.secondary_radios.setButton(radio_id)

        hbox = QHBox(self.secondary_groupbox)
        spacer = QWidget(hbox)
        spacer.setFixedSize(20,1)
        hbox.setStretchFactor(spacer,0)
        
        self.secondary_position_combo = QComboBox(0,hbox,"")
        self.secondary_position_combo.insertItem(i18n("1 left of 2"))
        self.secondary_position_combo.insertItem(i18n("1 right of 2"))
        self.secondary_position_combo.insertItem(i18n("1 above 2"))
        self.secondary_position_combo.insertItem(i18n("1 below 2"))
        self.connect(self.secondary_position_combo,SIGNAL("activated(int)"),self.slotSecondaryPositionChange)

        spacer = QWidget(vbox)
        vbox.setStretchFactor(spacer,1)
        
        vbox = QVBox(self)
        top_layout.addWidget(vbox,1)
        
        if not self.compact_mode:
            # -- Right columns with preview, size and refresh widgets.
            
            # -- Preview Box.
            self.monitor_preview_stack = QWidgetStack(vbox)
            
            self.monitor_preview = MonitorPreview(self.monitor_preview_stack,self.imagedir)
            self.monitor_preview_stack.addWidget(self.monitor_preview)
            self.connect(self.monitor_preview,PYSIGNAL("focussed()"),self.slotMonitorFocussed)
            
            self.dual_monitor_preview = DualMonitorPreview(self.monitor_preview_stack, DUAL_PREVIEW_SIZE, self.imagedir)

            self.monitor_preview_stack.addWidget(self.dual_monitor_preview)
            self.connect(self.dual_monitor_preview,PYSIGNAL("pressed()"),self.slotMonitorFocussed)
            self.connect(self.dual_monitor_preview,PYSIGNAL("positionChanged()"),self.slotDualheadPreviewPositionChanged)
        
        # -- Size & Refresh Box.
        if not self.compact_mode:
            hbox = QHBox(vbox)
        else:
            hbox = QVBox(vbox)
        hbox.setSpacing(KDialog.spacingHint())
        
        self.resize_slider = ResizeSlider(hbox)
        self.connect(self.resize_slider,PYSIGNAL("resolutionChange(int)"),self.slotResolutionChange)

        hbox2 = QHBox(hbox)
        self.refresh_label = QLabel(hbox2,"RefreshLabel")
        self.refresh_label.setText(i18n("Refresh:"))
        
        self.size_refresh_combo = QComboBox(0,hbox2,"comboBox1") # gets filled in setRefreshRates()
        self.connect(self.size_refresh_combo,SIGNAL("activated(int)"),self.slotRefreshRateChange)
        if self.compact_mode:
            spacer = QWidget(hbox2)
            hbox2.setStretchFactor(spacer,1)
        
        spacer = QWidget(vbox)
        vbox.setStretchFactor(spacer,1)
        
        self.clearWState(Qt.WState_Polished)
        
    def setNotification(self,text):
        self.notify.setText(text)
    
    def slotOrientationRadioClicked(self,i):
        self.current_screen.setRotation(
            [Screen.RR_Rotate_0, Screen.RR_Rotate_90,Screen.RR_Rotate_270, Screen.RR_Rotate_180][i])
        
        if self.current_screen.getRotation()==Screen.RR_Rotate_0:
            self.monitor_preview.setRotation(MonitorPreview.ROTATE_0)
        elif self.current_screen.getRotation()==Screen.RR_Rotate_90:
            self.monitor_preview.setRotation(MonitorPreview.ROTATE_90)
        elif self.current_screen.getRotation()==Screen.RR_Rotate_270:
            self.monitor_preview.setRotation(MonitorPreview.ROTATE_270)
        else:
            self.monitor_preview.setRotation(MonitorPreview.ROTATE_180)

        self._sendChangedSignal()
        
    def slotMirrorHorizontallyToggled(self,flag):
        # Bit flippin'
        if flag:
            self.current_screen.setReflection(self.current_screen.getReflection() | Screen.RR_Reflect_X)
        else:
            self.current_screen.setReflection(self.current_screen.getReflection() & ~Screen.RR_Reflect_X)
        self.monitor_preview.setReflectX(flag)
        self._sendChangedSignal()

    def slotMirrorVerticallyToggled(self,flag):
        # Bit flippin'
        if flag:
            self.current_screen.setReflection(self.current_screen.getReflection() | Screen.RR_Reflect_Y)
        else:
            self.current_screen.setReflection(self.current_screen.getReflection() & ~Screen.RR_Reflect_Y)
        self.monitor_preview.setReflectY(flag)
        self._sendChangedSignal()

    def slotResolutionChange(self,i):
        self.current_screen.setResolutionIndex(i)
        width,height = self.current_screen.getAvailableResolutions()[i]
        
        if not self.compact_mode:
            self.monitor_preview.setResolution(width,height)
            self.dual_monitor_preview.setScreenResolution(
                self.xsetup.getUsedScreens().index(self.current_screen),
                width,height)
        
        self._fillRefreshCombo()
        self._sendChangedSignal()
    
    def slotRefreshRateChange(self,index):
        self.current_screen.setRefreshRateIndex(index)
        self._sendChangedSignal()
    
    def setScreen(self,screen):
        self.current_screen = screen
        self._syncGUI()
            
    def slotSecondMonitorToggled(self,enabled):
        if enabled:
            pressed_id = self.secondary_radios.selectedId()
            self.xsetup.setLayout(self.secondary_options[pressed_id])
        else:
            self.xsetup.setLayout(self.xsetup.LAYOUT_SINGLE)

        if self.xsetup.getLayout()!=self.xsetup.LAYOUT_DUAL:
            self.current_screen = self.xsetup.getUsedScreens()[0]

        self.secondary_position_combo.setEnabled(self.xsetup.getLayout()==XSetup.LAYOUT_DUAL)
            
        self._syncGUIScreen()
        self._sendChangedSignal()
        
    def slotSecondMonitorRadioPressed(self,pressedId):
        self.xsetup.setLayout(self.secondary_options[pressedId])
        
        if self.xsetup.getLayout()!=XSetup.LAYOUT_DUAL:
            self.current_screen = self.xsetup.getUsedScreens()[0]
       
        self.secondary_position_combo.setEnabled(self.xsetup.getLayout()==XSetup.LAYOUT_DUAL)

        if self.xsetup.getLayout()==XSetup.LAYOUT_DUAL:
            if not self.compact_mode:
                self.monitor_preview_stack.raiseWidget(self.dual_monitor_preview)
        else:
            if not self.compact_mode:
                self.monitor_preview_stack.raiseWidget(self.monitor_preview)
        
        self._syncGUIScreen()
        self._sendChangedSignal()
        
    def slotSecondaryPositionChange(self,index):
        position = [XSetup.POSITION_LEFTOF,XSetup.POSITION_RIGHTOF,XSetup.POSITION_ABOVE,XSetup.POSITION_BELOW][index]
        self.xsetup.setDualheadOrientation(position)
        self.dual_monitor_preview.setPosition(position)
        self._sendChangedSignal()
        
    def slotDualheadPreviewPositionChanged(self,position):
        self.xsetup.setDualheadOrientation(position)
        index = {
            XSetup.POSITION_LEFTOF:0,
            XSetup.POSITION_RIGHTOF:1,
            XSetup.POSITION_ABOVE:2,
            XSetup.POSITION_BELOW:3
            }[position]
        self.secondary_position_combo.setCurrentItem(index)
        self._sendChangedSignal()
        
    def setMargin(self,margin):
        self.top_layout.setMargin(margin)
        
    def setSpacing(self,spacing):
        self.top_layout.setSpacing(spacing)

############################################################################
class DpmsPage(QWidget):

    # Mapping values in seconds to human-readable labels.
    intervals = (
            (60,i18n("1 minute")),
            (120,i18n("2 minutes")),
            (180,i18n("3 minutes")),
            (300,i18n("5 minutes")),
            (600,i18n("10 minutes")),
            (900,i18n("15 minutes")),
            (1200,i18n("20 minutes")),
            (1500,i18n("25 minutes")),
            (1800,i18n("30 minutes")),
            (2700,i18n("45 minutes")),
            (3600,i18n("1 hour")),
            (7200,i18n("2 hours")),
            (10800,i18n("3 hours")),
            (14400,i18n("4 hours")),
            (18000,i18n("5 hours")))

    def __init__(self,parent = None,name = None,modal = 0,fl = 0):
        global imagedir
        QWidget.__init__(self,parent)
        
        # Where to find xset.
        self.xset_bin = os.popen('which xset').read()[:-1]
            
        if not name:
            self.setName("DPMSTab")

        dpms_tab_layout = QVBoxLayout(self,0,0,"DPMSTabLayout")
        self.top_layout = dpms_tab_layout
        
        hbox = QHBox(self)
        hbox.setSpacing(KDialog.spacingHint())
        
        dpms_tab_layout.addWidget(hbox)

        self.dpmsgroup = QHGroupBox(hbox,"dpmsgroup")
        self.dpmsgroup.setInsideSpacing(KDialog.spacingHint())
        self.dpmsgroup.setInsideMargin(KDialog.marginHint())
        self.dpmsgroup.setTitle(i18n("Enable power saving"))
        self.dpmsgroup.setCheckable(1)
        
        self.connect(self.dpmsgroup,SIGNAL("toggled(bool)"),self.slotDpmsToggled)

        hbox2 = QHBox(self.dpmsgroup)
        hbox2.setSpacing(KDialog.spacingHint())
        
        dpmstext = QLabel(hbox2,"dpmstext")
        dpmstext.setText(i18n("Switch off monitor after:"))

        self.dpmscombo = QComboBox(0,hbox2,"dpmscombo")
        self.fillCombo(self.dpmscombo)
        self.connect(self.dpmscombo,SIGNAL("activated(int)"),self.slotDpmsActivated)
        
        spacer = QWidget(hbox2)
        hbox2.setStretchFactor(spacer,1)
        
        self.energystarpix = QLabel(hbox,"energystarpix")
        self.energystarpix.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.energystarpix.sizePolicy().hasHeightForWidth()))
        self.energystarpix.setMinimumSize(QSize(150,77))
        self.energystarpix.setPixmap(QPixmap(imagedir+"../energystar.png"))
        self.energystarpix.setScaledContents(1)
        
        bottomspacer = QSpacerItem(51,160,QSizePolicy.Minimum,QSizePolicy.Expanding)
        dpms_tab_layout.addItem(bottomspacer)

        self.clearWState(Qt.WState_Polished)
       
        self.readDpms()
        
    def fillCombo(self,combo):
        """ Fill the combobox with the values from our list """
        for interval in self.intervals:
            combo.insertItem(interval[1])

    def slotDpmsActivated(self,index):
        """ Another dpms value has been chosen, update buttons at bottom. """
        self.emit(PYSIGNAL("changedSignal()"), ())
        
    def slotDpmsToggled(self,bool):
        """ Dpms checkbox has been toggled, update buttons at bottom. """
        self.emit(PYSIGNAL("changedSignal()"), ())
        
    def readDpms(self):
         # FIXME it is not the widget's job to read of change the values, just to present the GUI.
        """ Read output from xset -q and parse DPMS settings from it. """
        # FIXME: localisation problem running this command.
        lines = ExecWithCapture(self.xset_bin,[self.xset_bin,'-q']).split('\n')

        self.dpms_min = 1800
        self.dpms_enabled = False
        
        for line in lines:
            if line.strip().startswith("Standby"):
                self.dpms_min = int(line.strip().split()[5]) # TODO: More subtle exception handling. ;)
            if line.strip().startswith("DPMS is"):
                self.dpms_enabled = line.strip().split()[2]=="Enabled"
        
        if self.dpms_min==0:    # 0 also means don't use Standby mode.
            self.dpms_enabled = False
            self.dpms_min = 1800
        
        self.dpmsgroup.setChecked(self.dpms_enabled)

        found = False
        for i in range(len(self.intervals)):
            if self.dpms_min >= self.intervals[i][0]:
                self.dpmscombo.setCurrentItem(i)
                last_time = self.intervals[i][0]
            else:
                break
        self.dpms_min = last_time
        
    def isChanged(self):
        """ Check if something has changed since startup or last apply(). """
        if self.dpmsgroup.isChecked():
            if self.intervals[self.dpmscombo.currentItem()][0] != self.dpms_min:
                return True
            if self.dpmsgroup.isChecked() != self.dpms_enabled:
                return True
            return False
            
        else:
            # self.dpmsgroup.isChecked() is False
            return self.dpms_enabled    # self.dpms_enabled != False
        
    def applyDpms(self):
        """ Use xset to apply new dpms settings. """
        enabled = self.dpmsgroup.isChecked()
        minutes = self.intervals[self.dpmscombo.currentItem()][0]
        if enabled:
            # Switch dpms on and set timeout interval.
            cmd_on = "%s +dpms" % self.xset_bin
            cmd_set = "%s dpms %i %i %i" % (self.xset_bin, minutes,minutes,minutes)
            print cmd_set
            if os.system(cmd_set) != 0:
                print "DPMS command failed: ", cmd_set
        else:
            # Switch dpms off.
            cmd_on = "%s -dpms" % self.xset_bin
        if os.system(cmd_on) != 0:
            print "DPMS command failed: ", cmd_on
        self.readDpms()
        self.emit(PYSIGNAL("changedSignal()"), ())
    
    def apply(self):
        self.applyDpms()
        
    def reset(self):
        for i in range(len(self.intervals)):
            if self.intervals[i][0] == self.dpms_min:
                self.dpmscombo.setCurrentItem(i)
                break

        self.dpmsgroup.setChecked(self.dpms_enabled)

    def setMargin(self,margin):
        self.top_layout.setMargin(margin)
        
    def setSpacing(self,spacing):
        self.top_layout.setSpacing(spacing)
      
############################################################################
def create_displayconfig(parent,name):
    """ Factory function for KControl """
    global kapp
    kapp = KApplication.kApplication()
    return DisplayApp(parent, name)

############################################################################
def MakeAboutData():
    aboutdata = KAboutData("guidance",programname,version, \
        "Display and Graphics Configuration Tool", KAboutData.License_GPL, \
        "Copyright (C) 2003-2006 Simon Edwards", \
        "Thanks go to  Phil Thompson, Jim Bublitz and David Boddie.")
    aboutdata.addAuthor("Simon Edwards","Developer","simon@simonzone.com", \
            "http://www.simonzone.com/software/")
    aboutdata.addAuthor("Sebastian K├╝gler","Developer","sebas@kde.org", \
            "http://vizZzion.org");
    aboutdata.addCredit("Pete Andrews","Gamma calibration pictures/system",None, \
            "http://www.photoscientia.co.uk/Gamma.htm")
    return aboutdata

if standalone:
    aboutdata = MakeAboutData()
    KCmdLineArgs.init(sys.argv,aboutdata)

    kapp = KApplication()
    
    displayapp = DisplayApp()
    displayapp.exec_loop()

Generated by  Doxygen 1.6.0   Back to index