mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-25 03:12:51 +00:00
1359 lines
45 KiB
C++
1359 lines
45 KiB
C++
![]() |
/*
|
||
|
* Copyright 2008 Aike J Sommer <dev@aikesommer.name>
|
||
|
*
|
||
|
* 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,
|
||
|
* 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 Library 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.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "xmlconfigurations.h"
|
||
|
|
||
|
#include "xml/configurations_xml.h"
|
||
|
#include "backendoutputs.h"
|
||
|
#include "screens.h"
|
||
|
|
||
|
#include <KDebug>
|
||
|
#include <QDir>
|
||
|
#include <QTimer>
|
||
|
|
||
|
#include "xmlconfiguration.h"
|
||
|
|
||
|
namespace Kephal {
|
||
|
|
||
|
XMLConfigurations::XMLConfigurations(QObject * parent)
|
||
|
: BackendConfigurations(parent),
|
||
|
m_activeConfiguration(0),
|
||
|
m_markedConfiguration(0),
|
||
|
m_currentOutputs(0),
|
||
|
m_currentOutputsKnown(false),
|
||
|
m_confirmTimer(new QTimer(this)),
|
||
|
m_confirmLeft(0),
|
||
|
m_awaitingConfirm(false)
|
||
|
{
|
||
|
QDir dir = QDir::home();
|
||
|
if (!dir.cd(".local"))
|
||
|
{
|
||
|
kDebug() << QDir::homePath() + "/.local directory not found, creating now.";
|
||
|
if (!dir.mkdir(".local"))
|
||
|
qWarning() << "Error during creation of " << QDir::homePath() + "/.local directory.";
|
||
|
|
||
|
dir.cd(".local");
|
||
|
}
|
||
|
m_configPath = dir.filePath("screen-configurations.xml");
|
||
|
|
||
|
m_externalConfiguration = new ExternalConfiguration(this);
|
||
|
connect(m_externalConfiguration, SIGNAL(activateExternal()), this, SLOT(activateExternal()));
|
||
|
|
||
|
connect(m_confirmTimer, SIGNAL(timeout()), this, SLOT(confirmTimerTimeout()));
|
||
|
|
||
|
init();
|
||
|
}
|
||
|
|
||
|
QMap<QString, Configuration *> XMLConfigurations::configurations()
|
||
|
{
|
||
|
QMap<QString, Configuration *> result;
|
||
|
for (QMap<QString, XMLConfiguration *>::const_iterator i = m_configurations.constBegin();
|
||
|
i != m_configurations.constEnd(); ++i) {
|
||
|
result.insert(i.key(), i.value());
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::init()
|
||
|
{
|
||
|
loadXml();
|
||
|
|
||
|
if (! m_configXml) {
|
||
|
m_configXml = new ConfigurationsXML();
|
||
|
|
||
|
/**
|
||
|
* Create default single layout
|
||
|
*/
|
||
|
ConfigurationXML * config = new ConfigurationXML(m_configXml);
|
||
|
m_configXml->configurations().append(config);
|
||
|
|
||
|
config->setName("single");
|
||
|
config->setModifiable(false);
|
||
|
|
||
|
ScreenXML * screen = new ScreenXML(config);
|
||
|
config->screens().append(screen);
|
||
|
|
||
|
screen->setId(0);
|
||
|
screen->setPrivacy(false);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Create default extended-right layout
|
||
|
*/
|
||
|
config = new ConfigurationXML(m_configXml);
|
||
|
m_configXml->configurations().append(config);
|
||
|
|
||
|
config->setName("extended-right");
|
||
|
config->setModifiable(false);
|
||
|
|
||
|
screen = new ScreenXML(config);
|
||
|
config->screens().append(screen);
|
||
|
|
||
|
screen->setId(0);
|
||
|
screen->setPrivacy(false);
|
||
|
|
||
|
screen = new ScreenXML(config);
|
||
|
config->screens().append(screen);
|
||
|
|
||
|
screen->setId(1);
|
||
|
screen->setPrivacy(false);
|
||
|
screen->setRightOf(0);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Create default extended-left layout
|
||
|
*/
|
||
|
config = new ConfigurationXML(m_configXml);
|
||
|
m_configXml->configurations().append(config);
|
||
|
|
||
|
config->setName("extended-left");
|
||
|
config->setModifiable(false);
|
||
|
|
||
|
screen = new ScreenXML(config);
|
||
|
config->screens().append(screen);
|
||
|
|
||
|
screen->setId(0);
|
||
|
screen->setPrivacy(false);
|
||
|
screen->setRightOf(1);
|
||
|
|
||
|
screen = new ScreenXML(config);
|
||
|
config->screens().append(screen);
|
||
|
|
||
|
screen->setId(1);
|
||
|
screen->setPrivacy(false);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Create outputs section for single output
|
||
|
*/
|
||
|
OutputsXML * outputs = new OutputsXML(m_configXml);
|
||
|
m_configXml->outputs().append(outputs);
|
||
|
|
||
|
outputs->setConfiguration("external");
|
||
|
|
||
|
OutputXML * output = new OutputXML(outputs);
|
||
|
outputs->outputs().append(output);
|
||
|
|
||
|
output->setName("*");
|
||
|
output->setScreen(0);
|
||
|
output->setVendor("*");
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Create outputs section for 2 screens
|
||
|
*/
|
||
|
outputs = new OutputsXML(m_configXml);
|
||
|
m_configXml->outputs().append(outputs);
|
||
|
|
||
|
outputs->setConfiguration("external");
|
||
|
|
||
|
output = new OutputXML(outputs);
|
||
|
outputs->outputs().append(output);
|
||
|
|
||
|
output->setName("*");
|
||
|
output->setScreen(0);
|
||
|
output->setVendor("*");
|
||
|
|
||
|
output = new OutputXML(outputs);
|
||
|
outputs->outputs().append(output);
|
||
|
|
||
|
output->setName("*");
|
||
|
output->setScreen(1);
|
||
|
output->setVendor("*");
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Save the default xml
|
||
|
*/
|
||
|
saveXml();
|
||
|
}
|
||
|
|
||
|
QList<ConfigurationXML *> configs = m_configXml->configurations();
|
||
|
for (int i = 0; i < configs.size(); i++) {
|
||
|
ConfigurationXML * config = configs[i];
|
||
|
|
||
|
XMLConfiguration * c = new XMLConfiguration(this, config);
|
||
|
m_configurations.insert(config->name(), c);
|
||
|
connect(c, SIGNAL(configurationActivated(XMLConfiguration*)), this, SLOT(activate(XMLConfiguration*)));
|
||
|
}
|
||
|
|
||
|
findOutputs();
|
||
|
}
|
||
|
|
||
|
Configuration * XMLConfigurations::findConfiguration()
|
||
|
{
|
||
|
kDebug() << "looking for a matching configuration...";
|
||
|
// This should be event-driven
|
||
|
findOutputs();
|
||
|
if (! m_currentOutputs) {
|
||
|
return 0;
|
||
|
}
|
||
|
kDebug() << "found outputs, known:" << m_currentOutputsKnown;
|
||
|
|
||
|
if (m_currentOutputs->configuration() == "external") {
|
||
|
return m_externalConfiguration;
|
||
|
}
|
||
|
|
||
|
XMLConfiguration * config = m_configurations[m_currentOutputs->configuration()];
|
||
|
if (! config) {
|
||
|
//kDebug() << "config" << m_currentOutputs->configuration() << "does not exist!!";
|
||
|
CONFIGURATION_NOT_FOUND(m_currentOutputs->configuration())
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return config;
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::findOutputs()
|
||
|
{
|
||
|
m_currentOutputsKnown = true;
|
||
|
m_currentOutputs = findKnownOutputs();
|
||
|
if (! m_currentOutputs) {
|
||
|
m_currentOutputsKnown = false;
|
||
|
m_currentOutputs = findBestOutputs();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
OutputsXML * XMLConfigurations::findKnownOutputs()
|
||
|
{
|
||
|
QList<Output *> currentOutputs = Outputs::self()->outputs();
|
||
|
int connected = 0;
|
||
|
foreach (Output * output, currentOutputs) {
|
||
|
if (output->isConnected()) {
|
||
|
++connected;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (OutputsXML * knownOutputs, m_configXml->outputs()) {
|
||
|
if (knownOutputs->outputs().size() != connected) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
bool matchedAll = true;
|
||
|
foreach (Output * current, currentOutputs) {
|
||
|
if (! current->isConnected()) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
bool matched = false;
|
||
|
foreach (OutputXML * known, knownOutputs->outputs()) {
|
||
|
if (known->name() != current->id()) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((current->vendor() == known->vendor())
|
||
|
&& (current->productId() == known->product())
|
||
|
&& (current->serialNumber() == known->serial())) {
|
||
|
matched = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (! matched) {
|
||
|
matchedAll = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (matchedAll) {
|
||
|
return knownOutputs;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
OutputsXML * XMLConfigurations::findBestOutputs()
|
||
|
{
|
||
|
QList<Output *> currentOutputs = Outputs::self()->outputs();
|
||
|
int connected = 0;
|
||
|
foreach (Output * output, currentOutputs) {
|
||
|
if (output->isConnected()) {
|
||
|
++connected;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
kDebug() << "connected:" << connected;
|
||
|
|
||
|
qreal scoreAllMax = 0.01;
|
||
|
OutputsXML * knownAllMax = 0;
|
||
|
foreach (OutputsXML * knownOutputs, m_configXml->outputs()) {
|
||
|
if (knownOutputs->outputs().size() != connected) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
qreal scoreAll = 1;
|
||
|
QSet<OutputXML *> knownTaken;
|
||
|
foreach (Output * current, currentOutputs) {
|
||
|
if (! current->isConnected()) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
kDebug() << "looking for current" << current->id();
|
||
|
|
||
|
qreal scoreMax = 0.01;
|
||
|
OutputXML * knownMax = 0;
|
||
|
foreach (OutputXML * known, knownOutputs->outputs()) {
|
||
|
if (knownTaken.contains(known)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
qreal score = 1;
|
||
|
score *= match(known->name(), current->id());
|
||
|
score *= match(known->vendor(), current->vendor());
|
||
|
score *= match(known->product(), current->productId());
|
||
|
|
||
|
kDebug() << "known" << known->name() << "has score:" << score;
|
||
|
if (score > scoreMax) {
|
||
|
knownMax = known;
|
||
|
scoreMax = score;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (knownMax) {
|
||
|
scoreAll *= scoreMax;
|
||
|
knownTaken.insert(knownMax);
|
||
|
knownMax->setActualOutput(current->id());
|
||
|
} else {
|
||
|
scoreAll = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (scoreAll > scoreAllMax) {
|
||
|
scoreAllMax = scoreAll;
|
||
|
knownAllMax = knownOutputs;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return knownAllMax;
|
||
|
}
|
||
|
|
||
|
qreal XMLConfigurations::match(QString known, QString current) {
|
||
|
if (known == current) {
|
||
|
return 1;
|
||
|
} else if (known == "*") {
|
||
|
return 0.5;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qreal XMLConfigurations::match(int known, int current) {
|
||
|
if (known == current) {
|
||
|
return 1;
|
||
|
} else if (known == -1) {
|
||
|
return 0.5;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QList<Configuration *> XMLConfigurations::alternateConfigurations()
|
||
|
{
|
||
|
QList<Configuration *> configs;
|
||
|
foreach (XMLConfiguration * config, m_configurations) {
|
||
|
if (config->layout().size() <= m_currentOutputs->outputs().size()) {
|
||
|
configs.append(config);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return configs;
|
||
|
}
|
||
|
|
||
|
QList<XMLConfiguration *> XMLConfigurations::equivalentConfigurations(int numScreens) {
|
||
|
kDebug() << "looking for equivalent configurations with" << numScreens << "screens";
|
||
|
|
||
|
QList<XMLConfiguration *> result;
|
||
|
foreach (XMLConfiguration * config, m_configurations) {
|
||
|
if ((! config->isModifiable()) && (config->layout().size() == numScreens)) {
|
||
|
kDebug() << "found:" << config->name();
|
||
|
result.append(config);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
QList<QPoint> XMLConfigurations::possiblePositions(Output * output)
|
||
|
{
|
||
|
QList<QPoint> result;
|
||
|
QSet<QPoint> unique;
|
||
|
if (! output->isConnected()) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
if (! m_activeConfiguration) {
|
||
|
kDebug() << "don't have an active configuration";
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
QMap<XMLConfiguration *, QPoint> positions;
|
||
|
if (! m_activeConfiguration->isModifiable()) {
|
||
|
positions = equivalentConfigurationsPositions(output);
|
||
|
foreach (const QPoint& point, positions) {
|
||
|
unique.insert(point);
|
||
|
}
|
||
|
|
||
|
positions = simpleConfigurationsPositions(output, true);
|
||
|
foreach (const QPoint& point, positions) {
|
||
|
unique.insert(point);
|
||
|
}
|
||
|
} else {
|
||
|
positions = sameConfigurationsPositions(output, false);
|
||
|
foreach (const QPoint& point, positions) {
|
||
|
unique.insert(point);
|
||
|
}
|
||
|
|
||
|
positions = simpleConfigurationsPositions(output, false);
|
||
|
foreach (const QPoint& point, positions) {
|
||
|
unique.insert(point);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (const QPoint& p, unique) {
|
||
|
result.append(p);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
QMap<int, QRect> XMLConfigurations::resizeLayout(Output * output, const QSize & size, QMap<Output *, int> & outputScreens, QMap<Output *, QSize> & outputSizes) {
|
||
|
outputScreens.unite(currentOutputScreens());
|
||
|
QMap<int, QPoint> simpleLayout = m_activeConfiguration->layout();
|
||
|
|
||
|
foreach (Output * o, outputScreens.keys()) {
|
||
|
if (o == output) {
|
||
|
outputSizes.insert(output, size);
|
||
|
} else if (o->isActivated()) {
|
||
|
outputSizes.insert(o, o->isActivated() ? o->size() : o->preferredSize());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return m_activeConfiguration->realLayout(simpleLayout, outputScreens, outputSizes);
|
||
|
}
|
||
|
// disabled because they are only used by the dbus api
|
||
|
#if 0
|
||
|
bool XMLConfigurations::move(Output * output, const QPoint & position) {
|
||
|
if ((! m_activeConfiguration) || (! output->isConnected())) {
|
||
|
return false;
|
||
|
}
|
||
|
if (position == output->position()) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
QMap<XMLConfiguration *, QPoint> positions;
|
||
|
if (! m_activeConfiguration->isModifiable()) {
|
||
|
positions = equivalentConfigurationsPositions(output);
|
||
|
kDebug() << "equiv pos for:" << output->id() << position << positions;
|
||
|
for (QMap<XMLConfiguration *, QPoint>::const_iterator i = positions.constBegin(); i != positions.constEnd(); ++i) {
|
||
|
if (i.value() == position) {
|
||
|
requireConfirm();
|
||
|
if (! activate(i.key())) {
|
||
|
revert();
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
positions = simpleConfigurationsPositions(output, true);
|
||
|
foreach (const QPoint& point, positions) {
|
||
|
Q_UNUSED(point)
|
||
|
FIX_ME("handle moving of output");
|
||
|
}
|
||
|
} else {
|
||
|
positions = sameConfigurationsPositions(output, false);
|
||
|
foreach (const QPoint& point, positions) {
|
||
|
Q_UNUSED(point)
|
||
|
FIX_ME("handle moving of output");
|
||
|
}
|
||
|
|
||
|
positions = simpleConfigurationsPositions(output, false);
|
||
|
foreach (const QPoint& point, positions) {
|
||
|
Q_UNUSED(point)
|
||
|
FIX_ME("handle moving of output");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::resize(Output * output, const QSize & size) {
|
||
|
kDebug() << output->id() << size;
|
||
|
if ((! m_activeConfiguration) || (! output->isConnected()) || (! output->isActivated())) {
|
||
|
return false;
|
||
|
}
|
||
|
if (size == output->size()) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
QMap<Output *, QSize> outputSizes;
|
||
|
QMap<Output *, int> outputScreens;
|
||
|
QMap<int, QRect> layout = resizeLayout(output, size, outputScreens, outputSizes);
|
||
|
|
||
|
requireConfirm();
|
||
|
if (activateLayout(layout, outputScreens, outputSizes)) {
|
||
|
OutputXML * o = outputXml(output->id());
|
||
|
if (o) {
|
||
|
o->setWidth(size.width());
|
||
|
o->setHeight(size.height());
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
revert();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::rotate(Output * output, Rotation rotation) {
|
||
|
if (! BackendOutputs::self()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (! m_activeConfiguration) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BackendOutput * o = BackendOutputs::self()->backendOutput(output->id());
|
||
|
if (o) {
|
||
|
bool resizeNeeded = ((output->rotation() + rotation) % 180) != 0;
|
||
|
if (resizeNeeded) {
|
||
|
kDebug() << "resize is needed for changing rotation from" << output->rotation() << "to" << rotation;
|
||
|
|
||
|
QSize size(output->size().height(), output->size().width());
|
||
|
|
||
|
QMap<Output *, QSize> outputSizes;
|
||
|
QMap<Output *, int> outputScreens;
|
||
|
QMap<int, QRect> layout = resizeLayout(output, size, outputScreens, outputSizes);
|
||
|
|
||
|
if (layout.empty()) {
|
||
|
INVALID_CONFIGURATION("layout is empty")
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
requireConfirm();
|
||
|
if (o->applyOrientation(rotation, o->reflectX(), o->reflectY()) && activateLayout(layout, outputScreens, outputSizes)) {
|
||
|
OutputXML * xml = outputXml(output->id());
|
||
|
if (xml) {
|
||
|
xml->setWidth(size.width());
|
||
|
xml->setHeight(size.height());
|
||
|
xml->setRotation(rotation);
|
||
|
//saveXml();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
kDebug() << "setting rotation to" << rotation << "for" << o->id() << "failed";
|
||
|
|
||
|
revert();
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
requireConfirm();
|
||
|
if (o->applyOrientation(rotation, o->reflectX(), o->reflectY())) {
|
||
|
OutputXML * xml = outputXml(o->id());
|
||
|
if (xml) {
|
||
|
xml->setRotation(rotation);
|
||
|
//saveXml();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
kDebug() << "setting rotation to" << rotation << "for" << o->id() << "failed";
|
||
|
|
||
|
revert();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::changeRate(Output * output, float rate) {
|
||
|
if (! BackendOutputs::self()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BackendOutput * o = BackendOutputs::self()->backendOutput(output->id());
|
||
|
if (o) {
|
||
|
requireConfirm();
|
||
|
if (o->applyGeom(o->geom(), rate)) {
|
||
|
OutputXML * xml = outputXml(o->id());
|
||
|
if (xml) {
|
||
|
xml->setRate(rate);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
kDebug() << "setting rate to" << rate << "for" << o->id() << "failed";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
revert();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::reflectX(Output * output, bool reflect) {
|
||
|
if (! BackendOutputs::self()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BackendOutput * o = BackendOutputs::self()->backendOutput(output->id());
|
||
|
if (o) {
|
||
|
requireConfirm();
|
||
|
if (o->applyOrientation(o->rotation(), reflect, o->reflectY())) {
|
||
|
OutputXML * xml = outputXml(o->id());
|
||
|
if (xml) {
|
||
|
xml->setReflectX(reflect);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
kDebug() << "setting reflect-x to" << reflect << "for" << o->id() << "failed";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
revert();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::reflectY(Output * output, bool reflect) {
|
||
|
if (! BackendOutputs::self()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BackendOutput * o = BackendOutputs::self()->backendOutput(output->id());
|
||
|
if (o) {
|
||
|
requireConfirm();
|
||
|
if (o->applyOrientation(o->rotation(), o->reflectY(), reflect)) {
|
||
|
OutputXML * xml = outputXml(o->id());
|
||
|
if (xml) {
|
||
|
xml->setReflectY(reflect);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
kDebug() << "setting reflect-y to" << reflect << "for" << o->id() << "failed";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
revert();
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
QMap<XMLConfiguration *, QMap<int, QPoint> > XMLConfigurations::matchingConfigurationsLayouts(const QMap<int, QPoint> & currentLayout, int removedOutputs) {
|
||
|
//kDebug() << "searching matching layouts for" << currentLayout;
|
||
|
QMap<XMLConfiguration *, QMap<int, QPoint> > result;
|
||
|
QList<XMLConfiguration *> configurations = equivalentConfigurations(currentLayout.size() + removedOutputs);
|
||
|
foreach (XMLConfiguration * configuration, configurations) {
|
||
|
QMap<int, QPoint> layout = configuration->layout();
|
||
|
QMap<int, int> match = matchLayouts(currentLayout, layout);
|
||
|
if (! match.empty()) {
|
||
|
result.insert(configuration, layout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
QMap<int, int> XMLConfigurations::matchLayouts(const QMap<int, QPoint> & currentLayout, const QMap<int, QPoint> & layout) const {
|
||
|
QList<int> indexes = layout.keys();
|
||
|
if (! currentLayout.empty()) {
|
||
|
indexes.insert(0, currentLayout.keys()[0]);
|
||
|
}
|
||
|
|
||
|
QPoint origin = currentLayout.begin().value();
|
||
|
QMap<int, int> result;
|
||
|
foreach (int i, indexes) {
|
||
|
QMap<int, QPoint> l = layout;
|
||
|
translateOrigin(l, l[i] - origin);
|
||
|
for (QMap<int, QPoint>::const_iterator j = currentLayout.constBegin(); j != currentLayout.constEnd(); ++j) {
|
||
|
bool found = false;
|
||
|
for (QMap<int, QPoint>::iterator k = l.begin(); k != l.end(); ++k) {
|
||
|
if (j.value() == k.value()) {
|
||
|
found = true;
|
||
|
result.insert(j.key(), k.key());
|
||
|
l.erase(k);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (! found) {
|
||
|
result.clear();
|
||
|
}
|
||
|
}
|
||
|
if (! result.empty()) {
|
||
|
int j = -1;
|
||
|
for (QMap<int, QPoint>::const_iterator k = l.constBegin(); k != l.constEnd(); ++k) {
|
||
|
result.insert(j, k.key());
|
||
|
--j;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
QMap<int, QRect> XMLConfigurations::calcMatchingLayout(const QMap<int, QPoint> & currentLayout, XMLConfiguration * configuration, QMap<int, QPoint> layout, Output * output, int * outputScreen)
|
||
|
{
|
||
|
QMap<int, int> match = matchLayouts(currentLayout, layout);
|
||
|
kDebug() << "match:" << match;
|
||
|
QMap<Output *, int> outputs;
|
||
|
Output * add = (match.contains(-1) ? output : 0);
|
||
|
Output * remove = (add ? 0 : output);
|
||
|
foreach (Output * o, Outputs::self()->outputs()) {
|
||
|
Screen * screen = o->screen();
|
||
|
if (remove && (remove == o)) {
|
||
|
outputs.insert(o, -1);
|
||
|
remove = 0;
|
||
|
} else if (screen && match.contains(screen->id())) {
|
||
|
outputs.insert(o, match[screen->id()]);
|
||
|
} else if (add && (add == o)) {
|
||
|
outputs.insert(o, match[-1]);
|
||
|
add = 0;
|
||
|
if (outputScreen) {
|
||
|
*outputScreen = match[-1];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QMap<int, QRect> realLayout = configuration->realLayout(layout, outputs);
|
||
|
translateToOther(realLayout, output, match);
|
||
|
|
||
|
return realLayout;
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::translateToOther(QMap<int, QRect> & layout, Output * output, QMap<int, int> match) {
|
||
|
foreach (Output * o, Outputs::self()->outputs()) {
|
||
|
if (o == output) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Screen * screen = o->screen();
|
||
|
if (screen && (match.empty() || match.contains(screen->id()))) {
|
||
|
QPoint offset = layout[match.empty() ? screen->id() : match[screen->id()]].topLeft() - o->position();
|
||
|
translateOrigin(layout, offset);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QMap<XMLConfiguration *, QPoint> XMLConfigurations::equivalentConfigurationsPositions(Output * output)
|
||
|
{
|
||
|
|
||
|
bool cloned = false;
|
||
|
if (! output->isActivated()) {
|
||
|
cloned = true;
|
||
|
} else {
|
||
|
foreach (Output * o, Outputs::self()->outputs()) {
|
||
|
if (o == output) {
|
||
|
continue;
|
||
|
}
|
||
|
if (o->screen() == output->screen()) {
|
||
|
cloned = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QMap<XMLConfiguration *, QPoint> positions;
|
||
|
QMap<int, QPoint> currentLayout = m_activeConfiguration->layout();
|
||
|
QMap<XMLConfiguration *, QMap<int, QPoint> > layouts;
|
||
|
QMap<int, QPoint> cloneLayout;
|
||
|
XMLConfiguration * cloneConfig = 0;
|
||
|
if (! cloned) {
|
||
|
currentLayout.remove(output->screen()->id());
|
||
|
translateOrigin(currentLayout);
|
||
|
layouts = matchingConfigurationsLayouts(currentLayout, 0);
|
||
|
if (! layouts.empty()) {
|
||
|
QMap<XMLConfiguration *, QMap<int, QPoint> >::const_iterator i = layouts.constBegin();
|
||
|
cloneLayout = i.value();
|
||
|
cloneConfig = i.key();
|
||
|
}
|
||
|
} else {
|
||
|
cloneLayout = currentLayout;
|
||
|
cloneConfig = m_activeConfiguration;
|
||
|
}
|
||
|
|
||
|
if (cloneConfig) {
|
||
|
QMap<int, QRect> layout = calcMatchingLayout(currentLayout, cloneConfig, cloneLayout, output);
|
||
|
foreach (const QRect& geom, layout) {
|
||
|
positions.insertMulti(cloneConfig, geom.topLeft());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
kDebug() << "current layout:" << currentLayout;
|
||
|
layouts = matchingConfigurationsLayouts(currentLayout, 1);
|
||
|
for (QMap<XMLConfiguration *, QMap<int, QPoint> >::const_iterator i = layouts.constBegin(); i != layouts.constEnd(); ++i) {
|
||
|
kDebug() << "matching layout:" << i.key()->name() << i.value();
|
||
|
int outputScreen = -1;
|
||
|
QMap<int, QRect> layout = calcMatchingLayout(currentLayout, i.key(), i.value(), output, & outputScreen);
|
||
|
kDebug() << "results in:" << layout;
|
||
|
if (layout.contains(outputScreen)) {
|
||
|
positions.insertMulti(i.key(), layout[outputScreen].topLeft());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (QMap<XMLConfiguration *, QPoint>::iterator i = positions.begin(); i != positions.end();) {
|
||
|
QPoint pos = i.value();
|
||
|
for (QMap<XMLConfiguration *, QPoint>::iterator j = i + 1; j != positions.end();) {
|
||
|
if (j.value() == pos) {
|
||
|
j = positions.erase(j);
|
||
|
} else {
|
||
|
++j;
|
||
|
}
|
||
|
}
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
return positions;
|
||
|
}
|
||
|
|
||
|
QMap<XMLConfiguration *, QPoint> XMLConfigurations::simpleConfigurationsPositions(Output * output, bool sameCount)
|
||
|
{
|
||
|
Screen * screen = output->screen();
|
||
|
bool cloned = false;
|
||
|
if (! output->isActivated()) {
|
||
|
cloned = true;
|
||
|
} else {
|
||
|
int count = 0;
|
||
|
foreach (Output * o, Outputs::self()->outputs()) {
|
||
|
if (o->isActivated()) {
|
||
|
++count;
|
||
|
}
|
||
|
if (o == output) {
|
||
|
continue;
|
||
|
}
|
||
|
if (o->screen() == screen) {
|
||
|
cloned = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (count <= 1) {
|
||
|
return QMap<XMLConfiguration *, QPoint>();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QMap<XMLConfiguration *, QPoint> positions;
|
||
|
QMap<int, QPoint> currentLayout = m_activeConfiguration->layout();
|
||
|
QMap<XMLConfiguration *, QMap<int, QPoint> > layouts;
|
||
|
QMap<int, QPoint> cloneLayout = currentLayout;
|
||
|
XMLConfiguration * cloneConfig = 0;
|
||
|
QMap<int, QPoint> noCloneLayout = currentLayout;
|
||
|
XMLConfiguration * noCloneConfig = 0;
|
||
|
|
||
|
if (! cloned) {
|
||
|
cloneLayout = m_activeConfiguration->cloneLayout(screen->id());
|
||
|
cloneConfig = simpleConfiguration(currentLayout.size() - 1);
|
||
|
cloneConfig->setLayout(cloneLayout);
|
||
|
|
||
|
if (sameCount) {
|
||
|
noCloneConfig = simpleConfiguration(currentLayout.size());
|
||
|
}
|
||
|
} else {
|
||
|
if (sameCount) {
|
||
|
cloneConfig = m_activeConfiguration;
|
||
|
}
|
||
|
|
||
|
noCloneConfig = simpleConfiguration(currentLayout.size() + 1);
|
||
|
}
|
||
|
|
||
|
if (cloneConfig) {
|
||
|
QMap<int, QRect> layout = calcMatchingLayout(currentLayout, cloneConfig, cloneLayout, output);
|
||
|
foreach (const QRect& geom, layout) {
|
||
|
positions.insertMulti(cloneConfig, geom.topLeft());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (noCloneConfig) {
|
||
|
noCloneConfig->setLayout(noCloneLayout);
|
||
|
int screenId = 0;
|
||
|
if (cloned) {
|
||
|
while (noCloneLayout.contains(screenId)) {
|
||
|
++screenId;
|
||
|
}
|
||
|
} else {
|
||
|
screenId = screen->id();
|
||
|
}
|
||
|
QSet<QPoint> possible = noCloneConfig->possiblePositions(screenId);
|
||
|
|
||
|
//kDebug() << "trying" << possible << "as" << screenId << "in layout" << noCloneConfig->name() << noCloneLayout;
|
||
|
|
||
|
QMap<Output *, int> outputIndexes;
|
||
|
foreach (Output * o, Outputs::self()->outputs()) {
|
||
|
Screen * s = o->screen();
|
||
|
outputIndexes.insert(o, (s ? s->id() : -1));
|
||
|
}
|
||
|
outputIndexes.insert(output, screenId);
|
||
|
|
||
|
foreach (const QPoint& p, possible) {
|
||
|
noCloneLayout.insert(screenId, p);
|
||
|
//kDebug() << "layout:" << noCloneLayout;
|
||
|
QMap<int, QRect> layout = noCloneConfig->realLayout(noCloneLayout, outputIndexes);
|
||
|
if (layout.contains(screenId)) {
|
||
|
translateToOther(layout, output);
|
||
|
//kDebug() << "results in:" << layout;
|
||
|
positions.insertMulti(noCloneConfig, layout[screenId].topLeft());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return positions;
|
||
|
}
|
||
|
|
||
|
QMap<XMLConfiguration *, QPoint> XMLConfigurations::sameConfigurationsPositions(Output * output, bool sameCount)
|
||
|
{
|
||
|
Q_UNUSED(sameCount)
|
||
|
|
||
|
Screen * screen = output->screen();
|
||
|
bool cloned = false;
|
||
|
if (! output->isActivated()) {
|
||
|
cloned = true;
|
||
|
} else {
|
||
|
foreach (Output * o, Outputs::self()->outputs()) {
|
||
|
if (o == output) {
|
||
|
continue;
|
||
|
}
|
||
|
if (o->screen() == screen) {
|
||
|
cloned = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QMap<XMLConfiguration *, QPoint> positions;
|
||
|
QMap<int, QPoint> currentLayout = m_activeConfiguration->layout();
|
||
|
QMap<XMLConfiguration *, QMap<int, QPoint> > layouts;
|
||
|
int screenId = (screen ? screen->id() : currentLayout.keys()[0]);
|
||
|
|
||
|
QSet<QPoint> possible = (cloned ? m_activeConfiguration->positions() : m_activeConfiguration->possiblePositions(screenId));
|
||
|
|
||
|
//kDebug() << "trying" << possible << "as" << screenId << "in layout" << m_activeConfiguration->name() << currentLayout;
|
||
|
|
||
|
QMap<Output *, int> outputIndexes;
|
||
|
foreach (Output * o, Outputs::self()->outputs()) {
|
||
|
Screen * s = o->screen();
|
||
|
outputIndexes.insert(o, (s ? s->id() : -1));
|
||
|
}
|
||
|
outputIndexes.insert(output, screenId);
|
||
|
|
||
|
foreach (const QPoint& p, possible) {
|
||
|
currentLayout.insert(screenId, p);
|
||
|
//kDebug() << "layout:" << currentLayout;
|
||
|
QMap<int, QRect> layout = m_activeConfiguration->realLayout(currentLayout, outputIndexes);
|
||
|
if (layout.contains(screenId)) {
|
||
|
translateToOther(layout, output);
|
||
|
//kDebug() << "results in:" << layout;
|
||
|
positions.insertMulti(m_activeConfiguration, layout[screenId].topLeft());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return positions;
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::activateExternal() {
|
||
|
kDebug() << "activate external configuration!!";
|
||
|
m_activeConfiguration = 0;
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::activate(XMLConfiguration * configuration) {
|
||
|
kDebug() << "activate configuration:" << configuration->name();
|
||
|
if (configuration == m_activeConfiguration) {
|
||
|
return true;
|
||
|
}
|
||
|
QMap<int, QPoint> layout = configuration->layout();
|
||
|
|
||
|
if (! m_currentOutputsKnown) {
|
||
|
kDebug() << "saving xml for current outputs...";
|
||
|
|
||
|
OutputsXML * known = new OutputsXML(m_configXml);
|
||
|
m_configXml->outputs().append(known);
|
||
|
|
||
|
known->setConfiguration(configuration->name());
|
||
|
|
||
|
QMap<QString, OutputXML *> currentMap;
|
||
|
foreach (OutputXML * o, m_currentOutputs->outputs()) {
|
||
|
currentMap.insert(o->actualOutput(), o);
|
||
|
}
|
||
|
|
||
|
QList<Output *> outputs = Outputs::self()->outputs();
|
||
|
foreach (Output * output, outputs) {
|
||
|
if (! output->isConnected()) {
|
||
|
continue;
|
||
|
}
|
||
|
if (! currentMap.contains(output->id())) {
|
||
|
INVALID_CONFIGURATION("m_currentOutputs not up to date");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
OutputXML * outputXml = new OutputXML(known);
|
||
|
known->outputs().append(outputXml);
|
||
|
|
||
|
outputXml->setName(output->id());
|
||
|
outputXml->setScreen(currentMap[output->id()]->screen());
|
||
|
outputXml->setVendor(output->vendor());
|
||
|
outputXml->setProduct(output->productId());
|
||
|
outputXml->setSerial(output->serialNumber());
|
||
|
outputXml->setWidth(output->size().width());
|
||
|
outputXml->setHeight(output->size().height());
|
||
|
}
|
||
|
|
||
|
m_currentOutputs = known;
|
||
|
m_currentOutputsKnown = true;
|
||
|
} else {
|
||
|
m_currentOutputs->setConfiguration(configuration->name());
|
||
|
matchOutputScreens(layout);
|
||
|
}
|
||
|
if (! m_awaitingConfirm) {
|
||
|
saveXml();
|
||
|
}
|
||
|
|
||
|
// get the current outputs->screens mapping
|
||
|
QMap<Output *, int> outputScreens = currentOutputScreens();
|
||
|
// convert the symbolic layout to an absolute layout
|
||
|
QMap<int, QRect> screens = configuration->realLayout(layout, outputScreens);
|
||
|
if (activateLayout(screens, outputScreens)) {
|
||
|
m_activeConfiguration = configuration;
|
||
|
emit configurationActivated(configuration);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
kDebug() << "failed to activate configuration:" << configuration->name();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::matchOutputScreens(const QMap<int, QPoint> & layout) {
|
||
|
QMap<QString, int> outputScreens;
|
||
|
QSet<int> screens;
|
||
|
QSet<int> unknownScreens;
|
||
|
QSet<int> takenScreens;
|
||
|
QSet<QString> cloned;
|
||
|
foreach (int screen, layout.keys()) {
|
||
|
screens << screen;
|
||
|
}
|
||
|
|
||
|
if (screens.size() > m_currentOutputs->outputs().size()) {
|
||
|
INVALID_CONFIGURATION("configuration and outputs don't match");
|
||
|
}
|
||
|
|
||
|
foreach (OutputXML * output, m_currentOutputs->outputs()) {
|
||
|
if (output->screen() >= 0) {
|
||
|
if (screens.contains(output->screen())) {
|
||
|
outputScreens.insert(output->name(), output->screen());
|
||
|
if (takenScreens.contains(output->screen())) {
|
||
|
cloned << output->name();
|
||
|
}
|
||
|
takenScreens << output->screen();
|
||
|
} else {
|
||
|
unknownScreens << output->screen();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (int taken, outputScreens) {
|
||
|
screens.remove(taken);
|
||
|
}
|
||
|
|
||
|
while (! (screens.empty() || unknownScreens.empty())) {
|
||
|
int from = * unknownScreens.begin();
|
||
|
unknownScreens.remove(from);
|
||
|
int to = * screens.begin();
|
||
|
screens.remove(to);
|
||
|
|
||
|
foreach (OutputXML * output, m_currentOutputs->outputs()) {
|
||
|
if (output->screen() == from) {
|
||
|
outputScreens.insert(output->name(), to);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (! (screens.empty() || cloned.empty())) {
|
||
|
QString o = * cloned.begin();
|
||
|
cloned.remove(o);
|
||
|
int to = * screens.begin();
|
||
|
screens.remove(to);
|
||
|
outputScreens.insert(o, to);
|
||
|
}
|
||
|
|
||
|
foreach (OutputXML * output, m_currentOutputs->outputs()) {
|
||
|
if (outputScreens.contains(output->name())) {
|
||
|
output->setScreen(outputScreens[output->name()]);
|
||
|
} else {
|
||
|
output->setScreen(-1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QMap<Output *, int> XMLConfigurations::currentOutputScreens()
|
||
|
{
|
||
|
QMap<Output *, int> outputScreens;
|
||
|
foreach (Output * output, Outputs::self()->outputs()) {
|
||
|
int screen = this->screen(output);
|
||
|
if (screen >= 0) {
|
||
|
outputScreens.insert(output, screen);
|
||
|
}
|
||
|
}
|
||
|
return outputScreens;
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::activateLayout(const QMap<int, QRect> & screensLayout, const QMap<Output *, int> & outputScreens) {
|
||
|
// contains the sizes of the outputs
|
||
|
QMap<Output *, QSize> outputSizes;
|
||
|
foreach (Output * output, outputScreens.keys()) {
|
||
|
outputSizes.insert(output, output->isActivated() ? output->size() : output->preferredSize());
|
||
|
}
|
||
|
// passes the outputs' current sizes or preferred sizes through to next step
|
||
|
return activateLayout(screensLayout, outputScreens, outputSizes);
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::activateLayout(const QMap<int, QRect> & screensLayout, const QMap<Output *, int> & outputScreens, const QMap<Output *, QSize> & outputSizes) {
|
||
|
if (screensLayout.empty()) {
|
||
|
INVALID_CONFIGURATION("layout is empty");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (! BackendOutputs::self()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// yet another set of output geometries...
|
||
|
QMap<Output *, QRect> layout;
|
||
|
// for each of the proposed absolute geometries
|
||
|
for (QMap<int, QRect>::const_iterator i = screensLayout.constBegin(); i != screensLayout.constEnd(); ++i) {
|
||
|
// for each of the outputs sizes
|
||
|
for (QMap<Output *, int>::const_iterator j = outputScreens.constBegin(); j != outputScreens.constEnd(); ++j) {
|
||
|
if (j.value() == i.key()) {
|
||
|
// output, (top left of the abs geom, current or preferred size)
|
||
|
layout.insert(j.key(), QRect(i.value().topLeft(), outputSizes[j.key()]));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// memorize the current layout, if no other confirmation is pending
|
||
|
kDebug() << "layout:" << layout;
|
||
|
if (! m_awaitingConfirm) {
|
||
|
foreach (BackendOutput * o, BackendOutputs::self()->backendOutputs()) {
|
||
|
o->mark();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// try to activate the new layout
|
||
|
if (! BackendOutputs::self()->activateLayout(layout)) {
|
||
|
// revert it if it fails, if no other confirmation is pending
|
||
|
if (! m_awaitingConfirm) {
|
||
|
foreach (BackendOutput * o, BackendOutputs::self()->backendOutputs()) {
|
||
|
o->revert();
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
Configuration * XMLConfigurations::activeConfiguration() {
|
||
|
return m_activeConfiguration ? (Configuration *) m_activeConfiguration : (Configuration *) m_externalConfiguration;
|
||
|
}
|
||
|
|
||
|
XMLConfiguration * XMLConfigurations::simpleConfiguration(int numScreens) {
|
||
|
QString name = "simple-" + QString::number(numScreens);
|
||
|
if (m_configurations.contains(name)) {
|
||
|
return m_configurations[name];
|
||
|
}
|
||
|
|
||
|
ConfigurationXML * config = new ConfigurationXML(m_configXml);
|
||
|
m_configXml->configurations().append(config);
|
||
|
|
||
|
config->setName(name);
|
||
|
config->setModifiable(true);
|
||
|
|
||
|
for (int i = 0; i < numScreens; ++i) {
|
||
|
ScreenXML * screen = new ScreenXML(config);
|
||
|
config->screens().append(screen);
|
||
|
|
||
|
screen->setId(i);
|
||
|
screen->setPrivacy(false);
|
||
|
screen->setRightOf(i - 1);
|
||
|
}
|
||
|
|
||
|
saveXml();
|
||
|
|
||
|
// FIXME should also be event driven
|
||
|
//m_configurations.insert(name, new XMLConfiguration(this, config));
|
||
|
return m_configurations[name];
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::saveXml() {
|
||
|
kDebug() << "save xml";
|
||
|
ConfigurationsXMLFactory * factory = new ConfigurationsXMLFactory();
|
||
|
factory->save(m_configXml, m_configPath);
|
||
|
delete factory;
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::loadXml() {
|
||
|
kDebug() << "load xml";
|
||
|
ConfigurationsXMLFactory * factory = new ConfigurationsXMLFactory();
|
||
|
m_configXml = (ConfigurationsXML *) factory->load(m_configPath);
|
||
|
delete factory;
|
||
|
}
|
||
|
|
||
|
int XMLConfigurations::screen(Output * output) {
|
||
|
foreach (OutputXML * o, m_currentOutputs->outputs()) {
|
||
|
if (output->id() == o->name()) {
|
||
|
return o->screen();
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::applyOutputSettings() {
|
||
|
if (! BackendOutputs::self()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
findOutputs();
|
||
|
if (! m_currentOutputs) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
foreach (OutputXML * o, m_currentOutputs->outputs()) {
|
||
|
BackendOutput * output = BackendOutputs::self()->backendOutput(o->name());
|
||
|
if (output) {
|
||
|
bool failed = false;
|
||
|
output->mark();
|
||
|
|
||
|
Rotation rotation = (Rotation) o->rotation();
|
||
|
bool xReflected = o->reflectX();
|
||
|
bool yReflected = o->reflectY();
|
||
|
if ((rotation != output->rotation()) || (xReflected != output->reflectX()) || (yReflected != output->reflectY())) {
|
||
|
kDebug() << "applying orientation to" << output->id() << rotation << xReflected << yReflected;
|
||
|
if (! output->applyOrientation(rotation, xReflected, yReflected)) {
|
||
|
OPERATION_FAILED("apply orientation")
|
||
|
failed = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QSize size(o->width(), o->height());
|
||
|
float rate = o->rate();
|
||
|
if ((! failed) && (! size.isEmpty()) && ((size != output->size()) || ((rate > 1) && (! qFuzzyCompare(rate, output->rate()))))) {
|
||
|
kDebug() << "applying geom to" << output->id() << size << rate;
|
||
|
if (! output->applyGeom(QRect(output->position(), size), rate)) {
|
||
|
OPERATION_FAILED("apply geometry")
|
||
|
failed = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (failed) {
|
||
|
kDebug() << "reverting output" << output->id();
|
||
|
output->revert();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
OutputXML * XMLConfigurations::outputXml(const QString & id) {
|
||
|
foreach (OutputXML * o, m_currentOutputs->outputs()) {
|
||
|
if (o->name() == id) {
|
||
|
return o;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::setPolling(bool polling) {
|
||
|
if (polling != this->polling()) {
|
||
|
m_configXml->setPolling(polling);
|
||
|
saveXml();
|
||
|
if (polling) {
|
||
|
emit pollingActivated();
|
||
|
} else {
|
||
|
emit pollingDeactivated();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool XMLConfigurations::polling() const {
|
||
|
return m_configXml->polling();
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::confirmTimerTimeout() {
|
||
|
--m_confirmLeft;
|
||
|
if (m_confirmLeft <= 0) {
|
||
|
revert();
|
||
|
} else {
|
||
|
emit confirmTimeout(m_confirmLeft);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::confirm() {
|
||
|
m_confirmTimer->stop();
|
||
|
m_awaitingConfirm = false;
|
||
|
saveXml();
|
||
|
emit confirmed();
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::revert() {
|
||
|
m_confirmTimer->stop();
|
||
|
if (! m_awaitingConfirm) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m_awaitingConfirm = false;
|
||
|
|
||
|
m_activeConfiguration = m_markedConfiguration;
|
||
|
if (BackendOutputs::self()) {
|
||
|
foreach (BackendOutput * o, BackendOutputs::self()->backendOutputs()) {
|
||
|
o->revert();
|
||
|
}
|
||
|
}
|
||
|
loadXml();
|
||
|
|
||
|
if (m_activeConfiguration) {
|
||
|
emit configurationActivated(m_activeConfiguration);
|
||
|
}
|
||
|
|
||
|
emit reverted();
|
||
|
}
|
||
|
|
||
|
void XMLConfigurations::requireConfirm() {
|
||
|
if (! BackendOutputs::self()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m_confirmLeft = CONFIRMATION_TIME;
|
||
|
if (! m_awaitingConfirm) {
|
||
|
m_awaitingConfirm = true;
|
||
|
m_confirmTimer->start(1000);
|
||
|
|
||
|
foreach (BackendOutput * o, BackendOutputs::self()->backendOutputs()) {
|
||
|
o->mark();
|
||
|
}
|
||
|
m_markedConfiguration = m_activeConfiguration;
|
||
|
}
|
||
|
|
||
|
emit confirmTimeout(m_confirmLeft);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|