File indexing completed on 2024-05-19 09:43:13
0001 /***************************************************************************** 0002 * Copyright 2007 - 2010 Craig Drummond <craig.p.drummond@gmail.com> * 0003 * Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU Lesser General Public License as * 0007 * published by the Free Software Foundation; either version 2.1 of the * 0008 * License, or (at your option) version 3, or any later version accepted * 0009 * by the membership of KDE e.V. (or its successor approved by the * 0010 * membership of KDE e.V.), which shall act as a proxy defined in * 0011 * Section 6 of version 3 of the license. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, * 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 0016 * Lesser General Public License for more details. * 0017 * * 0018 * You should have received a copy of the GNU Lesser General Public * 0019 * License along with this library. If not, * 0020 * see <http://www.gnu.org/licenses/>. * 0021 *****************************************************************************/ 0022 0023 /* 0024 based on the window decoration "Plastik": 0025 Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> 0026 0027 based on the window decoration "Web": 0028 Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> 0029 */ 0030 0031 #include <QBitmap> 0032 #include <QPainter> 0033 #include <QImage> 0034 #include <QPixmap> 0035 #include <QStyleFactory> 0036 #include <QStyle> 0037 #include <QDir> 0038 #include <QFile> 0039 #include <QTextStream> 0040 #include <QApplication> 0041 #include <QDBusConnection> 0042 #include <QDBusMessage> 0043 #include "qtcurvehandler.h" 0044 #include "qtcurveclient.h" 0045 #include "qtcurvebutton.h" 0046 #include "qtcurvedbus.h" 0047 #include <KConfig> 0048 #include <KConfigGroup> 0049 #include <KColorUtils> 0050 #include <KColorScheme> 0051 #include <KGlobalSettings> 0052 #include <KSaveFile> 0053 #include <KWindowSystem> 0054 #include <unistd.h> 0055 #include <sys/types.h> 0056 #include <kde_file.h> 0057 #include <common/common.h> 0058 0059 #include <QX11Info> 0060 #include <qtcurve-utils/x11base.h> 0061 #include <qtcurve-utils/dirs.h> 0062 0063 static time_t getTimeStamp(const QString &item) 0064 { 0065 KDE_struct_stat info; 0066 0067 return !item.isEmpty() && 0==KDE_lstat(QFile::encodeName(item), &info) ? info.st_mtime : 0; 0068 } 0069 0070 namespace QtCurve { 0071 0072 static const QString& 0073 xdgConfigFolder() 0074 { 0075 static QString xdgDir = QString::fromLocal8Bit(getXDGConfigHome()); 0076 return xdgDir; 0077 } 0078 0079 namespace KWin { 0080 0081 // make the handler accessible to other classes... 0082 static QtCurveHandler *handler = 0; 0083 QtCurveHandler* 0084 Handler() 0085 { 0086 return handler; 0087 } 0088 0089 QtCurveHandler::QtCurveHandler() : 0090 m_lastMenuXid(0), 0091 m_lastStatusXid(0), 0092 m_style(nullptr), 0093 m_dBus(nullptr) 0094 { 0095 qtcX11InitXlib(QX11Info::display()); 0096 handler = this; 0097 setStyle(); 0098 reset(0); 0099 0100 m_dBus = new QtCurveDBus(this); 0101 QDBusConnection::sessionBus().registerObject("/QtCurve", this); 0102 } 0103 0104 QtCurveHandler::~QtCurveHandler() 0105 { 0106 handler = 0; 0107 delete m_style; 0108 } 0109 0110 void QtCurveHandler::setStyle() 0111 { 0112 // Need to use our own style instance, as want to update this when 0113 // settings change... 0114 if (!m_style) { 0115 KConfig kglobals("kdeglobals", KConfig::CascadeConfig); 0116 KConfigGroup general(&kglobals, "General"); 0117 QString styleName = general.readEntry("widgetStyle", QString()).toLower(); 0118 0119 m_style = QStyleFactory::create(styleName.isEmpty() || 0120 (styleName != "qtcurve" 0121 #ifdef QTC_QT4_STYLE_SUPPORT 0122 && !styleName.startsWith(THEME_PREFIX) 0123 #endif 0124 ) ? QString("QtCurve") : styleName); 0125 // Looks wrong with style support 0126 m_timeStamp = getTimeStamp(xdgConfigFolder() + "/qtcurve/stylerc"); 0127 } 0128 } 0129 0130 bool QtCurveHandler::reset(unsigned long changed) 0131 { 0132 bool styleChanged = false; 0133 if (std::abs(m_timeStamp - getTimeStamp(xdgConfigFolder() + 0134 "/qtcurve/stylerc")) > 2) { 0135 delete m_style; 0136 m_style = nullptr; 0137 setStyle(); 0138 styleChanged = true; 0139 } 0140 0141 // we assume the active font to be the same as the inactive font since the 0142 // control center doesn't offer different settings anyways. 0143 m_titleFont = KDecoration::options()->font(true, false); // not small 0144 m_titleFontTool = KDecoration::options()->font(true, true); // small 0145 0146 m_hoverCols[0]=KColorScheme(QPalette::Inactive).decoration(KColorScheme::HoverColor).color(); 0147 m_hoverCols[1]=KColorScheme(QPalette::Active).decoration(KColorScheme::HoverColor).color(); 0148 0149 // read in the configuration 0150 bool configChanged = readConfig(changed & SettingCompositing); 0151 0152 setBorderSize(); 0153 0154 for (int t = 0;t < 2;++t) { 0155 for (int i = 0;i < NumButtonIcons;i++) { 0156 m_bitmaps[t][i] = QPixmap(); 0157 } 0158 } 0159 0160 // Do we need to "hit the wooden hammer" ? 0161 bool needHardReset = true; 0162 // TODO: besides the Color and Font settings I can maybe handle more changes 0163 // without a hard reset. I will do this later... 0164 if (!styleChanged && (changed & ~(SettingColors | SettingFont | SettingButtons)) == 0) 0165 needHardReset = false; 0166 0167 if (needHardReset || configChanged) { 0168 return true; 0169 } else { 0170 resetDecorations(changed); 0171 return false; 0172 } 0173 } 0174 0175 void QtCurveHandler::setBorderSize() 0176 { 0177 switch (m_config.borderSize()) { 0178 case QtCurveConfig::BORDER_NONE: 0179 case QtCurveConfig::BORDER_NO_SIDES: 0180 m_borderSize = 1; 0181 break; 0182 case QtCurveConfig::BORDER_TINY: 0183 m_borderSize = 2; 0184 break; 0185 case QtCurveConfig::BORDER_LARGE: 0186 m_borderSize = 8; 0187 break; 0188 case QtCurveConfig::BORDER_VERY_LARGE: 0189 m_borderSize = 12; 0190 break; 0191 case QtCurveConfig::BORDER_HUGE: 0192 m_borderSize = 18; 0193 break; 0194 case QtCurveConfig::BORDER_VERY_HUGE: 0195 m_borderSize = 27; 0196 break; 0197 case QtCurveConfig::BORDER_OVERSIZED: 0198 m_borderSize = 40; 0199 break; 0200 case QtCurveConfig::BORDER_NORMAL: 0201 default: 0202 m_borderSize = 4; 0203 } 0204 0205 if (!outerBorder() && (m_borderSize == 1 || m_borderSize > 4)) { 0206 m_borderSize--; 0207 } else if (outerBorder() && innerBorder() && 0208 m_config.borderSize() <= QtCurveConfig::BORDER_NORMAL) { 0209 m_borderSize += 2; 0210 } 0211 } 0212 0213 KDecoration* 0214 QtCurveHandler::createDecoration(KDecorationBridge *bridge) 0215 { 0216 return (new QtCurveClient(bridge, this))->decoration(); 0217 } 0218 0219 bool QtCurveHandler::supports(Ability ability) const 0220 { 0221 switch (ability) { 0222 // announce 0223 case AbilityAnnounceButtons: 0224 case AbilityAnnounceColors: 0225 // buttons 0226 case AbilityButtonMenu: 0227 case AbilityButtonOnAllDesktops: 0228 case AbilityButtonSpacer: 0229 case AbilityButtonHelp: 0230 case AbilityButtonMinimize: 0231 case AbilityButtonMaximize: 0232 case AbilityButtonClose: 0233 case AbilityButtonAboveOthers: 0234 case AbilityButtonBelowOthers: 0235 case AbilityButtonShade: 0236 // TODO 0237 // case AbilityButtonResize: 0238 #if KDE_IS_VERSION(4, 9, 85) 0239 case AbilityButtonApplicationMenu: 0240 #endif 0241 // colors 0242 case AbilityColorTitleBack: 0243 case AbilityColorTitleFore: 0244 case AbilityColorFrame: 0245 return true; 0246 case AbilityUsesAlphaChannel: 0247 return true; // !Handler()->outerBorder(); ??? 0248 case AbilityProvidesShadow: 0249 return customShadows(); 0250 case AbilityUsesBlurBehind: 0251 return opacity(true)<100 || opacity(false)<100 || wStyle()->pixelMetric((QStyle::PixelMetric)QtC_CustomBgnd, nullptr, nullptr); 0252 // TODO's 0253 default: 0254 return false; 0255 } 0256 } 0257 0258 bool QtCurveHandler::readConfig(bool compositingToggled) 0259 { 0260 QtCurveConfig oldConfig=m_config; 0261 KConfig configFile("kwinqtcurverc"); 0262 const KConfigGroup config(&configFile, "General"); 0263 QFontMetrics fm(m_titleFont); // active font = inactive font 0264 int oldSize=m_titleHeight, 0265 oldToolSize=m_titleHeightTool; 0266 bool changedBorder=false; 0267 0268 // The title should stretch with bigger font sizes! 0269 m_titleHeight = qMax(16, fm.height() + 4); // 4 px for the shadow etc. 0270 // have an even title/button size so the button icons are fully centered... 0271 if (m_titleHeight%2 == 0) 0272 m_titleHeight++; 0273 0274 fm = QFontMetrics(m_titleFontTool); // active font = inactive font 0275 // The title should stretch with bigger font sizes! 0276 m_titleHeightTool = qMax(13, fm.height()); // don't care about the shadow etc. 0277 // have an even title/button size so the button icons are fully centered... 0278 if (m_titleHeightTool%2 == 0) 0279 m_titleHeightTool++; 0280 0281 m_config.load(&configFile); 0282 0283 static bool borderHack = false; 0284 if (borderHack) { 0285 m_config.setOuterBorder(KWindowSystem::compositingActive() ? 0286 QtCurveConfig::SHADE_NONE : 0287 (m_config.customShadows() ? 0288 QtCurveConfig::SHADE_SHADOW : 0289 QtCurveConfig::SHADE_DARK)); 0290 changedBorder = true; 0291 borderHack = false; 0292 } else if (compositingToggled && !m_config.outerBorder() && 0293 (m_config.borderSize() < QtCurveConfig::BORDER_TINY || 0294 (wStyle()->pixelMetric((QStyle::PixelMetric)QtC_WindowBorder, 0295 nullptr, nullptr) & 0296 WINDOW_BORDER_COLOR_TITLEBAR_ONLY))) { 0297 QDBusConnection::sessionBus().send( 0298 QDBusMessage::createSignal("/KWin", "org.kde.KWin", 0299 "reloadConfig")); 0300 borderHack = true; 0301 } 0302 0303 m_titleHeight += 2 * titleBarPad(); 0304 0305 QFile in(xdgConfigFolder() + "/qtcurve/" BORDER_SIZE_FILE); 0306 int prevSize(-1), prevToolSize(-1), prevSide(-1), prevBot(-1); 0307 0308 if (in.open(QIODevice::ReadOnly)) { 0309 QTextStream stream(&in); 0310 prevSize=in.readLine().toInt(); 0311 prevToolSize=in.readLine().toInt(); 0312 prevBot=in.readLine().toInt(); 0313 prevSide=in.readLine().toInt(); 0314 in.close(); 0315 } 0316 0317 setBorderSize(); 0318 0319 int borderEdge=borderEdgeSize()*2; 0320 bool borderSizesChanged=prevSize!=(m_titleHeight+borderEdge) || prevToolSize!=(m_titleHeightTool+borderEdge) || 0321 prevBot!=borderSize(true) || prevSide!=borderSize(false); 0322 if(borderSizesChanged) 0323 { 0324 KSaveFile sizeFile(xdgConfigFolder() + "/qtcurve/" BORDER_SIZE_FILE); 0325 0326 if (sizeFile.open()) 0327 { 0328 QTextStream stream(&sizeFile); 0329 stream << m_titleHeight+borderEdge << endl 0330 << m_titleHeightTool+borderEdge << endl 0331 << borderSize(true) << endl 0332 << borderSize(false) << endl; 0333 stream.flush(); 0334 sizeFile.finalize(); 0335 sizeFile.close(); 0336 } 0337 } 0338 bool shadowChanged(false); 0339 0340 if (customShadows()) { 0341 ShadowConfig actShadow(QPalette::Active); 0342 ShadowConfig inactShadow(QPalette::Inactive); 0343 0344 actShadow.load(&configFile); 0345 inactShadow.load(&configFile); 0346 0347 shadowChanged = (m_shadowCache.shadowConfigChanged(actShadow) || 0348 m_shadowCache.shadowConfigChanged(inactShadow)); 0349 0350 m_shadowCache.setShadowConfig(actShadow); 0351 m_shadowCache.setShadowConfig(inactShadow); 0352 0353 if(shadowChanged || oldConfig.roundBottom()!=roundBottom()) 0354 m_shadowCache.reset(); 0355 } 0356 0357 if(m_dBus && (borderSizesChanged || changedBorder)) 0358 { 0359 m_dBus->emitBorderSizes(); // KDE4 apps... 0360 borderSizeChanged(); // Gtk2 apps... 0361 } 0362 0363 return (changedBorder || oldSize != m_titleHeight || 0364 oldToolSize != m_titleHeightTool || shadowChanged || 0365 m_config != oldConfig); 0366 } 0367 0368 const QBitmap & QtCurveHandler::buttonBitmap(ButtonIcon type, const QSize &size, bool toolWindow) 0369 { 0370 int typeIndex(type), 0371 reduceW(size.width()>14 ? static_cast<int>((2.0*(size.width()/3.5))+0.5) : 6), 0372 reduceH(size.height()>14 ? static_cast<int>((2.0*(size.height()/3.5))+0.5) : 6), 0373 w(size.width() - reduceW), 0374 h(size.height() - reduceH); 0375 0376 if (m_bitmaps[toolWindow][typeIndex].size()!=QSize(w,h)) 0377 m_bitmaps[toolWindow][typeIndex] = IconEngine::icon(type /*icon*/, qMin(w, h), wStyle()); 0378 return m_bitmaps[toolWindow][typeIndex]; 0379 } 0380 0381 int QtCurveHandler::borderSize(bool bot) const 0382 { 0383 if(bot) 0384 { 0385 if(QtCurveConfig::BORDER_NO_SIDES==m_config.borderSize()) 0386 return m_borderSize+5; 0387 else if(QtCurveConfig::BORDER_TINY==m_config.borderSize() && m_config.roundBottom() && m_config.outerBorder()) 0388 return m_borderSize+1; 0389 } 0390 return m_borderSize; 0391 } 0392 0393 void QtCurveHandler::borderSizeChanged() 0394 { 0395 foreach (QtCurveClient *client, m_clients) { 0396 client->informAppOfBorderSizeChanges(); 0397 } 0398 } 0399 0400 void QtCurveHandler::menuBarSize(unsigned int xid, int size) 0401 { 0402 foreach (QtCurveClient *client, m_clients) { 0403 if (client->windowId() == xid) { 0404 client->menuBarSize(size); 0405 break; 0406 } 0407 } 0408 m_lastMenuXid = xid; 0409 } 0410 0411 void QtCurveHandler::statusBarState(unsigned int xid, bool state) 0412 { 0413 foreach (QtCurveClient *client, m_clients) { 0414 if (client->windowId() == xid) { 0415 client->statusBarState(state); 0416 break; 0417 } 0418 } 0419 m_lastStatusXid = xid; 0420 } 0421 0422 void QtCurveHandler::emitToggleMenuBar(int xid) 0423 { 0424 m_dBus->emitMbToggle(xid); 0425 } 0426 0427 void QtCurveHandler::emitToggleStatusBar(int xid) 0428 { 0429 m_dBus->emitSbToggle(xid); 0430 } 0431 0432 int 0433 QtCurveHandler::borderEdgeSize() const 0434 { 0435 auto edgePad = m_config.edgePad(); 0436 if (!outerBorder()) { 0437 return edgePad + 1; 0438 } else if (m_config.borderSize() <= QtCurveConfig::BORDER_NO_SIDES || 0439 wStyle()->pixelMetric((QStyle::PixelMetric)QtC_Round, 0440 nullptr, nullptr) >= ROUND_FULL) { 0441 return edgePad + 3; 0442 } else if (wStyle()->pixelMetric((QStyle::PixelMetric)QtC_WindowBorder, 0443 nullptr, nullptr) & 0444 WINDOW_BORDER_ADD_LIGHT_BORDER) { 0445 return edgePad + 2; 0446 } else { 0447 return edgePad + 1; 0448 } 0449 } 0450 0451 void QtCurveHandler::removeClient(QtCurveClient *c) 0452 { 0453 if (c->windowId() == m_lastMenuXid) 0454 m_lastMenuXid = 0; 0455 if (c->windowId() == m_lastStatusXid) 0456 m_lastStatusXid = 0; 0457 m_clients.removeAll(c); 0458 } 0459 0460 } 0461 } 0462 0463 KWIN_DECORATION(QtCurve::KWin::QtCurveHandler) 0464 0465 #include "qtcurvedbus.moc" 0466 #include "qtcurvehandler.moc"