Detect absence of a TTY, and refuse to use the CLI backend interactively in that...
[lilass.git] / qt_dialogue.py
index c82b8f9ee6f543aaa5c89dbf0776d55bf8203fea..26f6717df32a95b82531fe899c04c6e56cc4ffc6 100644 (file)
@@ -1,5 +1,5 @@
 # DSL - easy Display Setup for Laptops
 # DSL - easy Display Setup for Laptops
-# Copyright (C) 2012 Ralf Jung <post@ralfj.de>
+# Copyright (C) 2012-2015 Ralf Jung <post@ralfj.de>
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 #
 # 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
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-from dsl import RelativeScreenPosition, ScreenSetup, res2user
-from PyQt4 import QtCore, QtGui
-
-def makeLayout(layout, members):
-    for m in members:
-        if isinstance(m, QtGui.QLayout):
-            layout.addLayout(m)
-        else:
-            layout.addWidget(m)
-    return layout
+import os
+from screen import RelativeScreenPosition, ScreenSetup
+from PyQt4 import QtCore, QtGui, uic
 
 class PositionSelection(QtGui.QDialog):
 
 class PositionSelection(QtGui.QDialog):
-    def __init__(self, internalResolutions, externalResolutions, commonResolutions):
+    def __init__(self, situation):
         # set up main window
         super(PositionSelection, self).__init__()
         # set up main window
         super(PositionSelection, self).__init__()
-        self.setWindowTitle('DSL - easy Display Setup for Laptops')
-        
-        ## position selection
-        posBox = QtGui.QGroupBox('Position of external screen', self)
-        self.posLeft = QtGui.QRadioButton('Left of internal screen', posBox)
-        self.posRight = QtGui.QRadioButton('Right of internal screen', posBox)
-        self.posRight.setChecked(True)
-        self.posRight.setFocus()
-        self.extOnly = QtGui.QRadioButton('Use external screen exclusively', posBox)
-        self.mirror = QtGui.QRadioButton('Mirror internal screen', posBox)
-        positions = [self.posLeft, self.posRight, self.extOnly, self.mirror]
-        posBox.setLayout(makeLayout(QtGui.QVBoxLayout(), positions))
-        for pos in positions:
-            pos.toggled.connect(self.updateForm)
-        
-        ## primary screen
-        self.primBox = QtGui.QGroupBox('Which should be the primary screen?', self)
-        self.primExt = QtGui.QRadioButton('The external screen', self.primBox)
-        self.primInt = QtGui.QRadioButton('The internal screen', self.primBox)
-        self.primInt.setChecked(True)
-        self.primBox.setLayout(makeLayout(QtGui.QVBoxLayout(), [self.primExt, self.primInt]))
+        self._situation = situation
+        uifile = os.path.join(os.path.dirname(__file__), 'qt_dialogue.ui')
+        uic.loadUi(uifile, self)
         
         
-        ## resolution selection
-        resBox = QtGui.QGroupBox('Screen resolutions', self)
-        # external screen
-        self.extResLabel = QtGui.QLabel('Resolution of external screen:', resBox)
-        self.extResolutions = externalResolutions
-        self.extResolutionsBox = QtGui.QComboBox(resBox)
-        for res in externalResolutions:
-            self.extResolutionsBox.addItem(res2user(res))
-        self.extResolutionsBox.setCurrentIndex(0) # select first resolution
-        self.extRow = makeLayout(QtGui.QHBoxLayout(), [self.extResLabel, self.extResolutionsBox])
-        # internal screen
-        self.intResLabel = QtGui.QLabel('Resolution of internal screen:', resBox)
-        self.intResolutions = internalResolutions
-        self.intResolutionsBox = QtGui.QComboBox(resBox)
-        for res in internalResolutions:
-            self.intResolutionsBox.addItem(res2user(res))
-        self.intResolutionsBox.setCurrentIndex(0) # select first resolution
-        self.intRow = makeLayout(QtGui.QHBoxLayout(), [self.intResLabel, self.intResolutionsBox])
-        # both screens
-        self.mirrorResLabel = QtGui.QLabel('Resolution of both screens:', resBox)
-        self.mirrorResolutions = commonResolutions
-        self.mirrorResolutionsBox = QtGui.QComboBox(resBox)
-        for res in commonResolutions:
-            self.mirrorResolutionsBox.addItem(res2user(res))
-        self.mirrorResolutionsBox.setCurrentIndex(0) # select first resolution
-        self.mirrorRow = makeLayout(QtGui.QHBoxLayout(), [self.mirrorResLabel, self.mirrorResolutionsBox])
-        # show them all
-        resBox.setLayout(makeLayout(QtGui.QVBoxLayout(), [self.extRow, self.intRow, self.mirrorRow]))
+        # fill relative position box
+        for pos in RelativeScreenPosition:
+            self.relPos.addItem(pos.text, pos)
         
         
-        # last row: buttons
-        buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, self)
-        buttons.accepted.connect(self.accept)
-        buttons.rejected.connect(self.reject)
-        
-        # add them all to the window
-        self.setLayout(makeLayout(QtGui.QVBoxLayout(), [posBox, self.primBox, resBox, buttons]))
-        
-        # make sure we are consistent
-        self.updateForm()
+        # keep resolutions in sync when in mirror mode
+        def syncIfMirror(source, target):
+            def _slot(idx):
+                if self.isMirror:
+                    target.setCurrentIndex(idx)
+            source.currentIndexChanged.connect(_slot)
+        syncIfMirror(self.intRes, self.extRes)
+        syncIfMirror(self.extRes, self.intRes)
+
+        # connect the update function, and make sure we are in a correct state
+        self.intEnabled.toggled.connect(self.updateEnabledControls)
+        self.extEnabled.toggled.connect(self.updateEnabledControls)
+        self.relPos.currentIndexChanged.connect(self.updateEnabledControls)
+        self.updateEnabledControls()
+    
+    def getRelativeScreenPosition(self):
+        idx = self.relPos.currentIndex()
+        return self.relPos.itemData(idx)
+    
+    def fillResolutionBox(self, box, resolutions):
+        # if the count did not change, update in-place (this avoids flicker)
+        if box.count() == len(resolutions):
+            for idx, res in enumerate(resolutions):
+                box.setItemText(idx, str(res))
+                box.setItemData(idx, res)
+        else:
+            # first clear it
+            while box.count() > 0:
+                box.removeItem(0)
+            # then fill it
+            for res in resolutions:
+                box.addItem(str(res), res)
     
     
-    def updateForm(self):
-        self.primBox.setEnabled(self.posLeft.isChecked() or self.posRight.isChecked())
-        self.extResolutionsBox.setEnabled(not self.mirror.isChecked())
-        self.extResLabel.setEnabled(not self.mirror.isChecked())
-        self.intResolutionsBox.setEnabled(self.posLeft.isChecked() or self.posRight.isChecked())
-        self.intResLabel.setEnabled(self.posLeft.isChecked() or self.posRight.isChecked())
-        self.mirrorResolutionsBox.setEnabled(self.mirror.isChecked())
-        self.mirrorResLabel.setEnabled(self.mirror.isChecked())
+    def updateEnabledControls(self):
+        intEnabled = self.intEnabled.isChecked()
+        extEnabled = self.extEnabled.isChecked()
+        bothEnabled = intEnabled and extEnabled
+        self.isMirror = bothEnabled and self.getRelativeScreenPosition() == RelativeScreenPosition.MIRROR # only if both are enabled, we can really mirror
+        # configure screen controls
+        self.intRes.setEnabled(intEnabled)
+        self.intPrimary.setEnabled(intEnabled and not self.isMirror)
+        self.extRes.setEnabled(extEnabled)
+        self.extPrimary.setEnabled(extEnabled and not self.isMirror)
+        if not intEnabled and extEnabled:
+            self.extPrimary.setChecked(True)
+        elif not extEnabled and intEnabled:
+            self.intPrimary.setChecked(True)
+        # which resolutions do we offer?
+        if self.isMirror:
+            commonRes = self._situation.commonResolutions()
+            self.fillResolutionBox(self.intRes, commonRes)
+            self.fillResolutionBox(self.extRes, commonRes)
+            self.intRes.setCurrentIndex(self.extRes.currentIndex())
+        else:
+            self.fillResolutionBox(self.intRes, self._situation.internalResolutions())
+            self.fillResolutionBox(self.extRes, self._situation.externalResolutions())
+        # configure position control
+        self.posGroup.setEnabled(bothEnabled)
+        self.posLabel1.setEnabled(bothEnabled)
+        self.posLabel2.setEnabled(bothEnabled)
+        self.relPos.setEnabled(bothEnabled)
+        # avoid having no screen
+        self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(intEnabled or extEnabled)
     
     def run(self):
         self.exec_()
         if not self.result(): return None
     
     def run(self):
         self.exec_()
         if not self.result(): return None
-        if self.mirror.isChecked():
-            return ScreenSetup(RelativeScreenPosition.MIRROR,
-                self.mirrorResolutions[self.mirrorResolutionsBox.currentIndex()],
-                self.mirrorResolutions[self.mirrorResolutionsBox.currentIndex()],
-                extIsPrimary = True)
-        else:
-            return ScreenSetup(self.getRelativeScreenPosition(),
-                self.intResolutions[self.intResolutionsBox.currentIndex()],
-                self.extResolutions[self.extResolutionsBox.currentIndex()],
-                self.primExt.isChecked())
-    
-    def getRelativeScreenPosition(self):
-        if self.posLeft.isChecked():
-            return RelativeScreenPosition.LEFT
-        elif self.posRight.isChecked():
-            return RelativeScreenPosition.RIGHT
-        elif self.extOnly.isChecked():
-            return RelativeScreenPosition.EXTERNAL_ONLY
-        else:
-            raise Exception("Nothing is checked?")
+        intRes = self.intRes.itemData(self.intRes.currentIndex()) if self.intEnabled.isChecked() else None
+        extRes = self.extRes.itemData(self.extRes.currentIndex()) if self.extEnabled.isChecked() else None
+        return ScreenSetup(intRes, extRes, self.getRelativeScreenPosition(), self.extPrimary.isChecked())