File indexing completed on 2025-01-26 05:09:32
0001 /* 0002 * This file is part of the KDE wacomtablet project. For copyright 0003 * information and license terms see the AUTHORS and COPYING files 0004 * in the top-level directory of this distribution. 0005 * 0006 * This program is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU General Public License as 0008 * published by the Free Software Foundation; either version 2 of 0009 * the License, or (at your option) any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License 0017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0018 */ 0019 0020 #include "tabletareaselectioncontroller.h" 0021 0022 #include "calibrationdialog.h" 0023 #include "logging.h" 0024 #include "tabletareaselectionview.h" 0025 0026 #include "screensinfo.h" 0027 #include "stringutils.h" 0028 #include "x11wacom.h" 0029 0030 using namespace Wacom; 0031 0032 namespace Wacom 0033 { 0034 class TabletAreaSelectionControllerPrivate 0035 { 0036 public: 0037 TabletAreaSelectionView *view = nullptr; 0038 TabletArea tabletGeometry; // the original tablet geometry 0039 TabletArea tabletGeometryRotated; // the rotated tablet geometry if rotation is active 0040 QMap<QString, QRect> screenGeometries; // the geometries of all screens which form the desktop 0041 ScreenSpace currentScreen; 0042 QString deviceName; // the device this instance is handling 0043 ScreenMap screenMap; // the current screen mappings 0044 ScreenRotation tabletRotation = ScreenRotation::NONE; // the tablet rotation 0045 }; 0046 } 0047 0048 TabletAreaSelectionController::TabletAreaSelectionController() 0049 : QObject() 0050 , d_ptr(new TabletAreaSelectionControllerPrivate) 0051 { 0052 // nothing to do except for initializing our private class 0053 } 0054 0055 TabletAreaSelectionController::~TabletAreaSelectionController() 0056 { 0057 delete this->d_ptr; 0058 } 0059 0060 const ScreenMap &TabletAreaSelectionController::getScreenMap() 0061 { 0062 Q_D(const TabletAreaSelectionController); 0063 0064 // make sure the current mapping is included 0065 setMapping(d->currentScreen, d->view->getSelection()); 0066 0067 // return mapping 0068 return d->screenMap; 0069 } 0070 0071 const ScreenSpace TabletAreaSelectionController::getScreenSpace() const 0072 { 0073 Q_D(const TabletAreaSelectionController); 0074 0075 return d->currentScreen; 0076 } 0077 0078 void TabletAreaSelectionController::select(const ScreenSpace &screenSpace) 0079 { 0080 Q_D(TabletAreaSelectionController); 0081 0082 if (!hasView()) { 0083 return; 0084 } 0085 0086 setMapping(d->currentScreen, d->view->getSelection()); 0087 0088 d->currentScreen = screenSpace; 0089 d->view->select(screenSpace.toString(), screenSpace.isDesktop(), getMapping(d->currentScreen)); 0090 } 0091 0092 void TabletAreaSelectionController::setView(TabletAreaSelectionView *view) 0093 { 0094 Q_D(TabletAreaSelectionController); 0095 0096 // cleanup signal/slot connections if we already have a view 0097 if (d->view) { 0098 disconnect(d->view, SIGNAL(signalCalibrateClicked()), this, SLOT(onCalibrateClicked())); 0099 disconnect(d->view, SIGNAL(signalFullTabletSelection()), this, SLOT(onFullTabletSelected())); 0100 disconnect(d->view, SIGNAL(signalScreenToggle()), this, SLOT(onScreenToggle())); 0101 disconnect(d->view, SIGNAL(signalSetScreenProportions()), this, SLOT(onSetScreenProportions())); 0102 disconnect(d->view, SIGNAL(signalTabletAreaSelection()), this, SLOT(onTabletAreaSelected())); 0103 } 0104 0105 // save view and connect signals 0106 d->view = view; 0107 0108 if (view) { 0109 connect(view, SIGNAL(signalCalibrateClicked()), this, SLOT(onCalibrateClicked())); 0110 connect(view, SIGNAL(signalFullTabletSelection()), this, SLOT(onFullTabletSelected())); 0111 connect(view, SIGNAL(signalScreenToggle()), this, SLOT(onScreenToggle())); 0112 connect(view, SIGNAL(signalSetScreenProportions()), this, SLOT(onSetScreenProportions())); 0113 connect(view, SIGNAL(signalTabletAreaSelection()), this, SLOT(onTabletAreaSelected())); 0114 } 0115 } 0116 0117 void TabletAreaSelectionController::setupController(const ScreenMap &mappings, const QString &deviceName, const ScreenRotation &rotation) 0118 { 0119 Q_D(TabletAreaSelectionController); 0120 0121 if (!hasView()) { 0122 return; 0123 } 0124 0125 d->deviceName = deviceName; 0126 d->tabletGeometry = X11Wacom::getMaximumTabletArea(deviceName); 0127 d->screenGeometries = ScreensInfo::getScreenGeometries(); 0128 d->screenMap = mappings; 0129 0130 if (rotation == ScreenRotation::AUTO) { 0131 // TODO 0132 d->tabletRotation = ScreensInfo::getScreenRotation(); 0133 // we have a tablet (not a canvas) viewpoint here, so we need to invert the screen rotation 0134 d->tabletRotation = d->tabletRotation.invert(); 0135 0136 } else if (rotation == ScreenRotation::AUTO_INVERTED) { 0137 // TODO 0138 d->tabletRotation = ScreensInfo::getScreenRotation(); 0139 0140 } else { 0141 d->tabletRotation = rotation; 0142 } 0143 0144 d->tabletGeometryRotated = d->tabletGeometry; 0145 0146 if (d->tabletRotation == ScreenRotation::CW || d->tabletRotation == ScreenRotation::CCW) { 0147 d->tabletGeometryRotated.setWidth(d->tabletGeometry.height()); 0148 d->tabletGeometryRotated.setHeight(d->tabletGeometry.width()); 0149 } 0150 0151 qCDebug(KCM) << "Calling setupScreens and setupTablet from setupController. ScreenGeometries: " << d->screenGeometries; 0152 d->view->setupScreens(d->screenGeometries, QSize(200, 200)); 0153 d->view->setupTablet(d->tabletGeometryRotated, QSize(400, 400)); 0154 0155 // make sure we have valid data set 0156 d->view->select(d->currentScreen.toString(), d->currentScreen.isDesktop(), getMapping(d->currentScreen)); 0157 } 0158 0159 void TabletAreaSelectionController::onCalibrateClicked() 0160 { 0161 Q_D(TabletAreaSelectionController); 0162 0163 QScopedPointer<CalibrationDialog> calibDialog(new CalibrationDialog(d->deviceName, d->currentScreen.toString())); 0164 calibDialog->exec(); 0165 0166 setSelection(TabletArea(calibDialog->calibratedArea())); 0167 } 0168 0169 void TabletAreaSelectionController::onFullTabletSelected() 0170 { 0171 checkConfigurationForTrackingModeProblems(); 0172 } 0173 0174 void TabletAreaSelectionController::onScreenToggle() 0175 { 0176 Q_D(TabletAreaSelectionController); 0177 0178 select(d->currentScreen.next()); 0179 } 0180 0181 void TabletAreaSelectionController::onSetScreenProportions() 0182 { 0183 Q_D(TabletAreaSelectionController); 0184 0185 QRect tabletGeometry = d->tabletGeometryRotated; 0186 QRect screenSelection = getScreenGeometry(d->currentScreen.toString()); 0187 0188 if (screenSelection.isEmpty()) { 0189 return; 0190 } 0191 0192 // calculate new height and width of the selection 0193 qreal screenAreaSelectionRatio = (float)screenSelection.width() / screenSelection.height(); 0194 qreal newWidth, newHeight; 0195 0196 if (screenSelection.width() > screenSelection.height()) { 0197 newWidth = tabletGeometry.width(); 0198 newHeight = newWidth / screenAreaSelectionRatio; 0199 0200 if (newHeight > tabletGeometry.height()) { 0201 newHeight = tabletGeometry.height(); 0202 newWidth = newHeight * screenAreaSelectionRatio; 0203 } 0204 0205 } else { 0206 newHeight = tabletGeometry.height(); 0207 newWidth = newHeight * screenAreaSelectionRatio; 0208 0209 if (newWidth > tabletGeometry.width()) { 0210 newWidth = tabletGeometry.width(); 0211 newHeight = newWidth / screenAreaSelectionRatio; 0212 } 0213 } 0214 0215 // calculate x and y to center the selection 0216 int newX = tabletGeometry.x() + (int)((tabletGeometry.width() - newWidth) / 2.); 0217 int newY = tabletGeometry.y() + (int)((tabletGeometry.height() - newHeight) / 2.); 0218 0219 setSelection(TabletArea(QRect(newX, newY, qRound(newWidth), qRound(newHeight)))); 0220 } 0221 0222 void TabletAreaSelectionController::onTabletAreaSelected() 0223 { 0224 checkConfigurationForTrackingModeProblems(); 0225 } 0226 0227 bool TabletAreaSelectionController::hasView() const 0228 { 0229 Q_D(const TabletAreaSelectionController); 0230 0231 return (d->view != nullptr); 0232 } 0233 0234 void TabletAreaSelectionController::checkConfigurationForTrackingModeProblems() 0235 { 0236 Q_D(TabletAreaSelectionController); 0237 0238 // a device can not be mapped to a single screen in relative mode 0239 if (d->currentScreen.isMonitor()) { 0240 d->view->setTrackingModeWarning(true); 0241 } else { 0242 d->view->setTrackingModeWarning(false); 0243 } 0244 } 0245 0246 const TabletArea TabletAreaSelectionController::convertAreaFromRotation(const TabletArea &tablet, const TabletArea &area, const ScreenRotation &rotation) const 0247 { 0248 TabletArea result = area; 0249 0250 if (rotation == ScreenRotation::CW) { 0251 result.setX(area.y()); 0252 result.setY(tablet.height() - area.x() - area.width()); 0253 result.setWidth(area.height()); 0254 result.setHeight(area.width()); 0255 0256 } else if (rotation == ScreenRotation::CCW) { 0257 result.setX(tablet.width() - area.y() - area.height()); 0258 result.setY(area.x()); 0259 result.setWidth(area.height()); 0260 result.setHeight(area.width()); 0261 0262 } else if (rotation == ScreenRotation::HALF) { 0263 result.setX(tablet.width() - area.width() - area.x()); 0264 result.setY(tablet.height() - area.height() - area.y()); 0265 result.setWidth(area.width()); 0266 result.setHeight(area.height()); 0267 } 0268 0269 return result; 0270 } 0271 0272 const TabletArea TabletAreaSelectionController::convertAreaToRotation(const TabletArea &tablet, const TabletArea &area, const ScreenRotation &rotation) const 0273 { 0274 TabletArea result = area; 0275 0276 if (rotation == ScreenRotation::CW) { 0277 result.setX(tablet.height() - area.height() - area.y()); 0278 result.setY(area.x()); 0279 result.setWidth(area.height()); 0280 result.setHeight(area.width()); 0281 0282 } else if (rotation == ScreenRotation::CCW) { 0283 result.setX(area.y()); 0284 result.setY(tablet.width() - area.width() - area.x()); 0285 result.setWidth(area.height()); 0286 result.setHeight(area.width()); 0287 0288 } else if (rotation == ScreenRotation::HALF) { 0289 result.setX(tablet.width() - area.width() - area.x()); 0290 result.setY(tablet.height() - area.height() - area.y()); 0291 result.setWidth(area.width()); 0292 result.setHeight(area.height()); 0293 } 0294 0295 return result; 0296 } 0297 0298 const QRect TabletAreaSelectionController::getScreenGeometry(QString output) const 0299 { 0300 Q_D(const TabletAreaSelectionController); 0301 0302 return d->screenGeometries.value(output, ScreensInfo::getUnifiedDisplayGeometry()); 0303 } 0304 0305 const TabletArea TabletAreaSelectionController::getMapping(ScreenSpace screenSpace) const 0306 { 0307 Q_D(const TabletAreaSelectionController); 0308 0309 return convertAreaToRotation(d->tabletGeometry, d->screenMap.getMapping(screenSpace), d->tabletRotation); 0310 } 0311 0312 void TabletAreaSelectionController::setMapping(ScreenSpace screenSpace, const TabletArea &mapping) 0313 { 0314 Q_D(TabletAreaSelectionController); 0315 0316 TabletArea area = convertAreaFromRotation(d->tabletGeometry, mapping, d->tabletRotation); 0317 d->screenMap.setMapping(screenSpace, area); 0318 } 0319 0320 void TabletAreaSelectionController::setSelection(const TabletArea &selection) 0321 { 0322 Q_D(TabletAreaSelectionController); 0323 0324 if (!hasView()) { 0325 return; 0326 } 0327 0328 if (selection.isEmpty() || selection == d->tabletGeometryRotated) { 0329 d->view->selectFullTablet(); 0330 } else { 0331 d->view->selectPartOfTablet(selection); 0332 } 0333 } 0334 0335 #include "moc_tabletareaselectioncontroller.cpp"