kde-workspace/kcontrol/randr/randrconfig.cpp
Ivailo Monev 740366f940 generic: tweak margins
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2024-04-20 00:07:52 +03:00

418 lines
14 KiB
C++

/*
* Copyright (c) 2007, 2008 Harry Bock <hbock@providence.edu>
* Copyright (c) 2007 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "outputconfig.h"
#include "outputgraphicsitem.h"
#include "collapsiblewidget.h"
#include "layoutmanager.h"
#include "randrconfig.h"
#include "randroutput.h"
#include "randrdisplay.h"
#include "randrscreen.h"
#include <kglobalsettings.h>
#include <kmessagebox.h>
#include <kshell.h>
#include <kconfiggroup.h>
#include <kdialog.h>
#include <qmenu.h>
QT_BEGIN_NAMESPACE
uint qHash(const QPoint& p)
{
return p.x() * 10000 + p.y();
}
QT_END_NAMESPACE
RandRConfig::RandRConfig(QWidget *parent, RandRDisplay *display)
: QWidget(parent), Ui::RandRConfigBase()
{
m_display = display;
Q_ASSERT(m_display);
if (!m_display->isValid()) {
// FIXME: this needs much better handling of this error...
return;
}
setupUi(this);
layout()->setMargin(0);
connect( identifyOutputsButton, SIGNAL(clicked()), SLOT(identifyOutputs()));
connect( &identifyTimer, SIGNAL(timeout()), SLOT(clearIndicators()));
connect( &compressUpdateViewTimer, SIGNAL(timeout()), SLOT(slotDelayedUpdateView()));
connect(unifyOutputs, SIGNAL(toggled(bool)), SLOT(unifiedOutputChanged(bool)));
identifyTimer.setSingleShot( true );
compressUpdateViewTimer.setSingleShot( true );
connect( saveAsDefaultButton, SIGNAL(clicked()), SLOT(saveStartup()));
QMenu* saveMenu = new QMenu(saveAsDefaultButton);
saveMenu->addAction(i18n("Save as Default"),this, SLOT(saveStartup()));
saveMenu->addAction(i18n("Reset"),this, SLOT(disableStartup()));
saveAsDefaultButton->setMenu(saveMenu);
// create the container for the settings widget
QHBoxLayout *layout = new QHBoxLayout(outputList);
layout->setSpacing(0);
layout->setContentsMargins(0,0,0,0);
m_container = new SettingsContainer(outputList);
m_container->setSizePolicy(QSizePolicy::Minimum,
QSizePolicy::Minimum);
layout->addWidget(m_container);
primaryDisplayBox->setVisible(true);
label->setVisible(true);
KConfig config("krandrrc");
if (config.hasGroup("Screen_0") && config.group("Screen_0").readEntry("OutputsUnified", false)) {
unifyOutputs->setChecked(true);
}
// create the scene
m_scene = new QGraphicsScene(m_display->currentScreen()->rect(), screenView);
screenView->setScene(m_scene);
screenView->installEventFilter(this);
m_layoutManager = new LayoutManager(m_display->currentScreen(), m_scene);
}
RandRConfig::~RandRConfig()
{
clearIndicators();
}
void RandRConfig::load(void)
{
if (!m_display->isValid()) {
kDebug() << "Invalid display! Aborting config load.";
return;
}
m_scene->clear();
qDeleteAll(m_outputList);
m_outputList.clear();
m_configs.clear(); // objects deleted above
OutputMap outputs = m_display->currentScreen()->outputs();
RandROutput *primary = m_display->currentScreen()->primaryOutput();
// disconnect while we repopulate the combo box
disconnect(primaryDisplayBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed()));
disconnect(primaryDisplayBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePrimaryDisplay()));
primaryDisplayBox->clear();
primaryDisplayBox->addItem(i18nc("No display selected", "None"));
// FIXME: adjust it to run on a multi screen system
CollapsibleWidget *w;
OutputGraphicsItem *o;
OutputConfigList preceding;
foreach(RandROutput *output, outputs) {
OutputConfig *config = new OutputConfig(this, output, preceding, unifyOutputs->isChecked());
m_configs.append( config );
preceding.append( config );
QString description = output->isConnected() ? i18n("%1 (Connected)", output->name()) : output->name();
w = m_container->insertWidget(config, description);
if (output->isConnected()) {
w->setExpanded(true);
kDebug() << "Output rect:" << output->rect();
}
connect(config, SIGNAL(connectedChanged(bool)), this, SLOT(outputConnectedChanged(bool)));
m_outputList.append(w);
o = new OutputGraphicsItem(config);
m_scene->addItem(o);
connect(o, SIGNAL(itemChanged(OutputGraphicsItem*)), this, SLOT(slotAdjustOutput(OutputGraphicsItem*)));
connect(config, SIGNAL(updateView()), this, SLOT(slotUpdateView()));
connect(config, SIGNAL(optionChanged()), this, SIGNAL(changed()));
if (output->isConnected()) {
primaryDisplayBox->addItem(output->name(), QVariant::fromValue(output->id()));
if (primary == output) {
primaryDisplayBox->setCurrentIndex(primaryDisplayBox->count()-1);
}
}
}
connect(primaryDisplayBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed()));
connect(primaryDisplayBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePrimaryDisplay()));
slotUpdateView();
}
void RandRConfig::outputConnectedChanged(bool connected)
{
OutputConfig *config = static_cast <OutputConfig *> (sender());
int index = m_configs.indexOf(config);
QString description = connected ? i18n("%1 (Connected)", config->output()->name()) : config->output()->name();
m_outputList.at(index)->setCaption(description);
}
void RandRConfig::save()
{
if (!m_display->isValid()) {
return;
}
KConfig config("krandrrc");
if (config.hasGroup("Screen_0")) {
config.group("Screen_0").writeEntry("OutputsUnified", unifyOutputs->isChecked());
config.sync();
}
apply();
}
void RandRConfig::defaults()
{
update();
}
void RandRConfig::apply()
{
kDebug() << "Applying settings...";
// normalize positions so that the coordinate system starts at (0,0)
QPoint normalizePos;
bool first = true;
foreach(CollapsibleWidget *w, m_outputList) {
OutputConfig *config = static_cast<OutputConfig *>(w->innerWidget());
if ( config->isActive()) {
QPoint pos = config->position();
if( first ) {
normalizePos = pos;
first = false;
} else {
if( pos.x() < normalizePos.x()) {
normalizePos.setX( pos.x());
}
if( pos.y() < normalizePos.y()) {
normalizePos.setY( pos.y());
}
}
}
}
normalizePos = -normalizePos;
kDebug() << "Normalizing positions by" << normalizePos;
foreach(CollapsibleWidget *w, m_outputList) {
OutputConfig *config = static_cast<OutputConfig *>(w->innerWidget());
RandROutput *output = config->output();
if (!output->isConnected()) {
continue;
}
QSize res = config->resolution();
if (!res.isNull()) {
if (!config->hasPendingChanges( normalizePos )) {
kDebug() << "Ignoring identical config for" << output->name();
continue;
}
QRect configuredRect(config->position(), res);
kDebug() << "Output config for" << output->name() << ":\n"
" rect =" << configuredRect
<< ", rot =" << config->rotation()
<< ", rate =" << config->refreshRate();
// Break the connection with the previous CRTC for changed outputs, since
// otherwise the code could try to use the same CRTC for two different outputs.
// This is probably rather hackish and may not always work, but I don't see
// a better way with this codebase, definitely not with the time I have now.
output->disconnectFromCrtc();
output->proposeRect(configuredRect.translated( normalizePos ));
output->proposeRotation(config->rotation());
output->proposeRefreshRate(config->refreshRate());
} else { // user wants to disable this output
kDebug() << "Disabling" << output->name();
output->slotDisable();
}
}
int primaryOutputIndex = primaryDisplayBox->currentIndex();
RandRScreen *screen = m_display->currentScreen();
if (primaryOutputIndex > 0) {
QVariant output = primaryDisplayBox->itemData(primaryOutputIndex);
screen->proposePrimaryOutput(screen->output(output.value<RROutput>()));
} else {
screen->proposePrimaryOutput(0);
}
m_display->applyProposed();
update();
}
void RandRConfig::updatePrimaryDisplay()
{
QString primary=primaryDisplayBox->currentText();
foreach( QGraphicsItem* item, m_scene->items()) {
OutputGraphicsItem* itemo = dynamic_cast< OutputGraphicsItem* >( item );
if (itemo && (itemo->objectName() == primary) != itemo->isPrimary()) {
itemo->setPrimary(itemo->objectName() == primary);
}
}
}
void RandRConfig::update()
{
// TODO: implement
emit changed(false);
}
void RandRConfig::saveStartup()
{
if (!m_display->isValid()) {
return;
}
KConfig config("krandrrc");
m_display->saveStartup(config);
// Add setting the primary screen to the list of commands
KConfigGroup group = config.group("Display");
QStringList commands = group.readEntry("StartupCommands").split("\n");
int primaryOutputIndex = primaryDisplayBox->currentIndex();
if (primaryOutputIndex > 0) {
QString primaryOutput = primaryDisplayBox->itemText(primaryOutputIndex);
commands += QString("xrandr --output %1 --primary").arg( KShell::quoteArg( primaryOutput ));
} else {
commands += QString("xrandr --noprimary");
}
group.writeEntry("StartupCommands",commands.join("\n"));
KMessageBox::information( window(), i18n( "Configuration has been set as the desktop default." ));
}
void RandRConfig::disableStartup()
{
if (!m_display->isValid()) {
return;
}
KConfig config("krandrrc");
m_display->disableStartup(config);
KMessageBox::information( window(), i18n( "Default desktop setup has been reset." ));
}
void RandRConfig::unifiedOutputChanged(bool checked)
{
Q_FOREACH(OutputConfig *config, m_configs) {
config->setUnifyOutput(checked);
config->updateSizeList();
}
emit changed(true);
}
bool RandRConfig::eventFilter(QObject *obj, QEvent *event)
{
if ( obj == screenView && event->type() == QEvent::Resize ) {
slotUpdateView();
return false;
}
return QWidget::eventFilter(obj, event);
}
void RandRConfig::slotAdjustOutput(OutputGraphicsItem *o)
{
Q_UNUSED(o);
kDebug() << "Output graphics item changed:";
// TODO: Implement
}
void RandRConfig::slotUpdateView()
{
compressUpdateViewTimer.start( 0 );
}
void RandRConfig::slotDelayedUpdateView()
{
QRect r;
bool first = true;
// updates the graphics view so that all outputs fit inside of it
foreach (OutputConfig *config, m_configs) {
if (first) {
first = false;
r = config->rect();
} else {
r = r.united(config->rect());
}
}
// scale the total bounding rectangle for all outputs to fit
// 80% of the containing QGraphicsView
qreal scaleX = qreal(screenView->width()) / r.width();
qreal scaleY = qreal(screenView->height()) / r.height();
qreal scale = (scaleX < scaleY) ? scaleX : scaleY;
scale *= 0.80f;
screenView->resetMatrix();
screenView->scale(scale,scale);
screenView->ensureVisible(r);
screenView->setSceneRect(r);
foreach( QGraphicsItem* item, m_scene->items()) {
if( OutputGraphicsItem* itemo = dynamic_cast< OutputGraphicsItem* >( item )) {
itemo->configUpdated();
}
}
updatePrimaryDisplay();
screenView->update();
}
void RandRConfig::identifyOutputs()
{
identifyTimer.stop();
clearIndicators();
QHash< QPoint, QStringList > ids; // outputs at centers of screens (can be more in case of clone mode)
OutputMap outputs = m_display->currentScreen()->outputs();
foreach(RandROutput *output, outputs) {
if( !output->isConnected() || output->rect().isEmpty()) {
continue;
}
ids[ output->rect().center() ].append( output->name());
}
for( QHash< QPoint, QStringList >::ConstIterator it = ids.constBegin(); it != ids.constEnd(); ++it ) {
KHBox *hb = new KHBox(nullptr);
hb->setWindowFlags(Qt::X11BypassWindowManagerHint);
hb->setFrameStyle(QFrame::Panel);
hb->setFrameShadow(QFrame::Plain);
QLabel *si = new QLabel(it->join("\n"), hb);
QFont fnt = KGlobalSettings::generalFont();
fnt.setPixelSize(100);
si->setFont(fnt);
si->setAlignment(Qt::AlignCenter);
const int margin = (KDialog::marginHint() * 2);
si->setContentsMargins(margin, 0, margin, 0);
QRect targetGeometry(QPoint(0,0), si->sizeHint());
targetGeometry.moveCenter(it.key());
hb->setGeometry(targetGeometry);
hb->show();
m_indicators.append( hb );
}
identifyTimer.start( 1500 );
}
void RandRConfig::clearIndicators()
{
qDeleteAll( m_indicators );
m_indicators.clear();
}
#include "moc_randrconfig.cpp"
// vim:noet:sts=8:sw=8: