diff --git a/src/knemod/interfaceicon.cpp b/src/knemod/interfaceicon.cpp index 6b253a2..206d6ff 100644 --- a/src/knemod/interfaceicon.cpp +++ b/src/knemod/interfaceicon.cpp @@ -1,605 +1,606 @@ /* This file is part of KNemo Copyright (C) 2004, 2005 Percy Leonhardt Copyright (C) 2009, 2010 John Stamp KNemo is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. KNemo 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "global.h" #include "utils.h" #include "interface.h" #include "knemodaemon.h" #include "interfaceicon.h" #include "interfacetray.h" #define SHRINK_MAX 0.75 #define HISTSIZE_STORE 0.5 Q_DECLARE_METATYPE(InterfaceCommand) InterfaceIcon::InterfaceIcon( Interface* interface ) : QObject(), mInterface( interface ), mTray( 0L ), barIncoming( 0 ), barOutgoing( 0 ) { commandActions = new KActionCollection( this ); statusAction = new KAction( i18n( "Show &Status Dialog" ), this ); plotterAction = new KAction( QIcon::fromTheme( "utilities-system-monitor" ), i18n( "Show &Traffic Plotter" ), this ); statisticsAction = new KAction( QIcon::fromTheme( "view-statistics" ), i18n( "Show St&atistics" ), this ); configAction = new KAction( QIcon::fromTheme( "configure" ), i18n( "&Configure KNemo..." ), this ); connect( statusAction, SIGNAL( triggered() ), this, SLOT( showStatus() ) ); connect( plotterAction, SIGNAL( triggered() ), this, SLOT( showGraph() ) ); connect( statisticsAction, SIGNAL( triggered() ), this, SLOT( showStatistics() ) ); connect( configAction, SIGNAL( triggered() ), this, SLOT( showConfigDialog() ) ); } InterfaceIcon::~InterfaceIcon() { // Prevent the "Destroyed while process is still running" message foreach( KProcess* process, processList ) { process->kill(); process->waitForFinished( 2000 ); } delete mTray; } void InterfaceIcon::configChanged() { KConfigGroup cg( KSharedConfig::openConfig(), "System Tray" ); iconWidth = cg.readEntry( "systrayIconWidth", 22 ); barWidth = iconWidth/3; int margins = iconWidth - (barWidth*2); midMargin = margins/3; int rightMargin = (margins - midMargin)/2; leftMargin = margins-midMargin - rightMargin; midMargin = leftMargin + barWidth+ midMargin; histSize = HISTSIZE_STORE/generalSettings->pollInterval; if ( histSize < 2 ) histSize = 2; for ( int i=0; i < histSize; i++ ) { inHist.append( 0 ); outHist.append( 0 ); } inMaxRate = mInterface->settings().inMaxRate; outMaxRate = mInterface->settings().outMaxRate; updateTrayStatus(); if ( mTray != 0L ) { updateMenu(); if ( mInterface->settings().iconTheme == TEXT_THEME ) updateIconText( true ); else if ( mInterface->settings().iconTheme == NETLOAD_THEME ) updateBars( true ); } } void InterfaceIcon::updateIconImage( int status ) { if ( mTray == 0L || mInterface->settings().iconTheme == TEXT_THEME ) return; QString iconName; if ( mInterface->settings().iconTheme == SYSTEM_THEME ) iconName = "network-"; else iconName = "knemo-" + mInterface->settings().iconTheme + "-"; // Now set the correct icon depending on the status of the interface. if ( ( status & KNemoIface::RxTraffic ) && ( status & KNemoIface::TxTraffic ) ) { iconName += ICON_RX_TX; } else if ( status & KNemoIface::RxTraffic ) { iconName += ICON_RX; } else if ( status & KNemoIface::TxTraffic ) { iconName += ICON_TX; } else if ( status & KNemoIface::Connected ) { iconName += ICON_IDLE; } else if ( status & KNemoIface::Available ) { iconName += ICON_OFFLINE; } else { iconName += ICON_ERROR; } mTray->setIconByName( iconName ); } int InterfaceIcon::calcHeight( QList& hist, unsigned int& net_max ) { unsigned long histcalculate = 0; unsigned long rate = 0; foreach( unsigned long j, hist ) { histcalculate += j; } rate = histcalculate / histSize; /* update maximum */ if ( !mInterface->settings().barScale ) { QListsortedMax( hist ); qSort( sortedMax ); unsigned long max = sortedMax.last(); int multiplier = 1024; if ( generalSettings->useBitrate ) multiplier = 1000; if( rate > net_max ) { net_max = rate; } else if( max < net_max * SHRINK_MAX && net_max * SHRINK_MAX >= multiplier ) { net_max *= SHRINK_MAX; } } qreal ratio = static_cast(rate)/net_max; if ( ratio > 1.0 ) ratio = 1.0; return ratio*iconWidth; } QColor InterfaceIcon::calcColor( QList& hist, const QColor& low, const QColor& high, int hival ) { const BackendData * data = mInterface->backendData(); if ( data->status & KNemoIface::Connected ) { if ( ! mInterface->settings().dynamicColor ) return low; } else if ( data->status & KNemoIface::Available ) return mInterface->settings().colorDisabled; else if ( data->status & KNemoIface::Unavailable ) return mInterface->settings().colorUnavailable; unsigned long histcalculate = 0; unsigned long rate = 0; if ( mInterface->settings().iconTheme == NETLOAD_THEME ) { foreach( unsigned long j, hist ) { histcalculate += j; } rate = histcalculate / histSize; } else rate = hist[0]; int lowH, lowS, lowV; int hiH, hiS, hiV; int difH, difS, difV; low.getHsv( &lowH, &lowS, &lowV ); high.getHsv( &hiH, &hiS, &hiV ); difH = hiH - lowH; difS = hiS - lowS; difV = hiV - lowV; qreal percentage = static_cast(rate)/hival; if ( percentage > 1.0 ) percentage = 1.0; QColor retcolor; retcolor.setHsv( lowH + ( percentage*difH ), lowS + ( percentage*difS), lowV + (percentage*difV ) ); return retcolor; } void InterfaceIcon::updateBars( bool doUpdate ) { // Has color changed? QColor rxColor = calcColor( inHist, mInterface->settings().colorIncoming, mInterface->settings().colorIncomingMax, mInterface->settings().inMaxRate ); QColor txColor = calcColor( outHist, mInterface->settings().colorOutgoing, mInterface->settings().colorOutgoingMax, mInterface->settings().outMaxRate ); if ( rxColor != colorIncoming ) { doUpdate = true; colorIncoming = rxColor; } if ( txColor != colorOutgoing ) { doUpdate = true; colorOutgoing = txColor; } // Has height changed? int rateIn = calcHeight( inHist, inMaxRate ); int rateOut = calcHeight( outHist, outMaxRate ); if ( rateIn != barIncoming ) { doUpdate = true; barIncoming = rateIn; } if ( rateOut != barOutgoing ) { doUpdate = true; barOutgoing = rateOut; } if ( !doUpdate ) return; QPixmap barIcon(iconWidth, iconWidth); QLinearGradient inGrad( midMargin, 0, midMargin+barWidth, 0 ); QLinearGradient topInGrad( midMargin, 0, midMargin+barWidth, 0 ); QLinearGradient outGrad( leftMargin, 0, leftMargin+barWidth, 0 ); QLinearGradient topOutGrad( leftMargin, 0, leftMargin+barWidth, 0 ); int top = iconWidth - barOutgoing; QRect topLeftRect( leftMargin, 0, barWidth, top ); QRect leftRect( leftMargin, top, barWidth, iconWidth ); top = iconWidth - barIncoming; QRect topRightRect( midMargin, 0, barWidth, top ); QRect rightRect( midMargin, top, barWidth, iconWidth ); barIcon.fill( Qt::transparent ); QPainter p( &barIcon ); p.setOpacity( 1.0 ); const BackendData * data = mInterface->backendData(); QColor topColor; if ( data->status & KNemoIface::Connected ) { topColor = mInterface->settings().colorBackground; } else if ( data->status & KNemoIface::Available ) { topColor = mInterface->settings().colorDisabled; } else { topColor = mInterface->settings().colorUnavailable; } QColor topColorD = topColor.darker(); topColor.setAlpha( 128 ); topColorD.setAlpha( 128 ); topInGrad.setColorAt(0, topColorD); topInGrad.setColorAt(1, topColor ); topOutGrad.setColorAt(0, topColorD); topOutGrad.setColorAt(1, topColor ); inGrad.setColorAt(0, rxColor ); inGrad.setColorAt(1, rxColor.darker() ); outGrad.setColorAt(0, txColor ); outGrad.setColorAt(1, txColor.darker() ); QBrush brush( inGrad ); p.setBrush( brush ); p.fillRect( rightRect, inGrad ); brush = QBrush( topInGrad ); p.fillRect( topRightRect, topInGrad ); brush = QBrush( outGrad ); p.fillRect( leftRect, outGrad ); brush = QBrush( topOutGrad ); p.fillRect( topLeftRect, topOutGrad ); mTray->setIconByPixmap( barIcon ); QPixmapCache::clear(); } QString InterfaceIcon::compactTrayText(unsigned long data ) { QString dataString; // Space is tight, so no space between number and units, and the complete // string should be no more than 4 chars. /* Visually confusing to display bytes if ( bytes < 922 ) // 922B = 0.9K byteString = i18n( "%1B", bytes ); */ double multiplier = 1024; if ( generalSettings->useBitrate ) multiplier = 1000; int precision = 0; if ( data < multiplier*9.95 ) // < 9.95K { precision = 1; } if ( data < multiplier*999.5 ) // < 999.5K { if ( generalSettings->useBitrate ) dataString = i18n( "%1k", QString::number( data/multiplier, 'f', precision ) ); else dataString = i18n( "%1K", QString::number( data/multiplier, 'f', precision ) ); return dataString; } if ( data < pow(multiplier, 2)*9.95 ) // < 9.95M precision = 1; if ( data < pow(multiplier, 2)*999.5 ) // < 999.5M { dataString = i18n( "%1M", QString::number( data/pow(multiplier, 2), 'f', precision ) ); return dataString; } if ( data < pow(multiplier, 3)*9.95 ) // < 9.95G precision = 1; // xgettext: no-c-format dataString = i18n( "%1G", QString::number( data/pow(multiplier, 3), 'f', precision) ); return dataString; } void InterfaceIcon::updateIconText( bool doUpdate ) { // Has color changed? QColor rxColor = calcColor( inHist, mInterface->settings().colorIncoming, mInterface->settings().colorIncomingMax, mInterface->settings().inMaxRate ); QColor txColor = calcColor( outHist, mInterface->settings().colorOutgoing, mInterface->settings().colorOutgoingMax, mInterface->settings().outMaxRate ); if ( rxColor != colorIncoming ) { doUpdate = true; colorIncoming = rxColor; } if ( rxColor != colorOutgoing ) { doUpdate = true; colorOutgoing = txColor; } // Has text changed? QString byteText = compactTrayText( mInterface->rxRate() ); if ( byteText != textIncoming ) { doUpdate = true; textIncoming = byteText; } byteText = compactTrayText( mInterface->txRate() ); if ( byteText != textOutgoing ) { doUpdate = true; textOutgoing = byteText; } if ( !doUpdate ) return; QPixmap textIcon(iconWidth, iconWidth); QRect topRect( 0, 0, iconWidth, iconWidth/2 ); QRect bottomRect( 0, iconWidth/2, iconWidth, iconWidth/2 ); textIcon.fill( Qt::transparent ); QPainter p( &textIcon ); p.setBrush( Qt::NoBrush ); p.setOpacity( 1.0 ); KColorScheme scheme(QPalette::Active, KColorScheme::View); // rxFont and txFont should be the same size per poll period QFont rxFont = setIconFont( textIncoming, mInterface->settings().iconFont, iconWidth ); QFont txFont = setIconFont( textOutgoing, mInterface->settings().iconFont, iconWidth ); if ( rxFont.pointSizeF() > txFont.pointSizeF() ) rxFont.setPointSizeF( txFont.pointSizeF() ); p.setFont( rxFont ); p.setPen( rxColor ); p.drawText( topRect, Qt::AlignCenter | Qt::AlignRight, textIncoming ); p.setFont( rxFont ); p.setPen( txColor ); p.drawText( bottomRect, Qt::AlignCenter | Qt::AlignRight, textOutgoing ); mTray->setIconByPixmap( textIcon ); QPixmapCache::clear(); } void InterfaceIcon::updateToolTip() { if ( mTray == 0L ) return; inHist.prepend( mInterface->rxRate() ); outHist.prepend( mInterface->txRate() ); while ( inHist.count() > histSize ) { inHist.removeLast(); outHist.removeLast(); } if ( mInterface->settings().iconTheme == TEXT_THEME ) updateIconText(); else if ( mInterface->settings().iconTheme == NETLOAD_THEME ) updateBars(); mTray->updateToolTip(); } void InterfaceIcon::updateMenu() { // Remove all old entries. - KMenu* menu = static_cast(mTray->contextMenu()); + QMenu* menu = mTray->contextMenu(); QList actions = menu->actions(); foreach ( QAction* action, commandActions->actions() ) menu->removeAction( action ); commandActions->clear(); InterfaceSettings& settings = mInterface->settings(); // If the user wants custom commands, add them. if ( settings.commands.size() > 0 ) { int i = 0; foreach ( InterfaceCommand command, settings.commands ) { QAction *action = new QAction( command.menuText, this ); action->setData( QVariant::fromValue( command ) ); commandActions->addAction( QString( "command%1" ).arg( i ), action ); ++i; } QAction* sep = menu->addSeparator(); commandActions->addAction( "sep", sep ); menu->insertActions( statusAction, commandActions->actions() ); } if ( settings.activateStatistics ) menu->insertAction( configAction, statisticsAction ); else menu->removeAction( statisticsAction ); } void InterfaceIcon::updateTrayStatus() { const QString ifaceName( mInterface->ifaceName() ); const BackendData * data = mInterface->backendData(); int currentStatus = data->status; bool hideWhenUnavailable = mInterface->settings().hideWhenUnavailable; bool hideWhenDisconnected = mInterface->settings().hideWhenDisconnected; QString title = mInterface->settings().alias; if ( title.isEmpty() ) title = ifaceName; /* Remove the icon if * - the interface is not available and the option to hide it is selected * - the interface does not exist, the option to hide it is selected * and the other option is not selected */ if ( mTray != 0L && ( ( (currentStatus < KNemoIface::Connected ) && hideWhenDisconnected ) || ( (currentStatus < KNemoIface::Available ) && hideWhenUnavailable && !hideWhenDisconnected ) ) ) { delete mTray; mTray = 0L; } /* Create the icon if * - the interface is available * - the interface is not available and the option to hide it is not * selected and the interface does exist * - the interface does not exist and the option to hide it is not selected * and the other option is not selected */ else if ( mTray == 0L && ( currentStatus & KNemoIface::Connected || ( currentStatus & KNemoIface::Available && !hideWhenDisconnected ) || ( !hideWhenUnavailable && !hideWhenDisconnected ) ) ) { mTray = new InterfaceTray( mInterface, ifaceName ); - KMenu* menu = static_cast(mTray->contextMenu()); + QMenu* menu = mTray->contextMenu(); menu->removeAction( menu->actions().at( 0 ) ); - menu->addTitle( QIcon::fromTheme( "knemo" ), i18n( "KNemo - %1", title ) ); + // FIXME: title for QMenu? + //menu->addTitle( QIcon::fromTheme( "knemo" ), i18n( "KNemo - %1", title ) ); menu->addAction( statusAction ); menu->addAction( plotterAction ); menu->addAction( configAction ); - KHelpMenu* helpMenu( new KHelpMenu( menu, KNemoDaemon::aboutData(), false ) ); + KHelpMenu* helpMenu( new KHelpMenu( menu, KAboutData::applicationData(), false ) ); menu->addMenu( helpMenu->menu() )->setIcon( QIcon::fromTheme( "help-contents" ) ); connect( menu, SIGNAL( triggered( QAction * ) ), this, SLOT( menuTriggered( QAction * ) ) ); if ( mInterface->settings().iconTheme == TEXT_THEME ) updateIconText(); else if ( mInterface->settings().iconTheme == NETLOAD_THEME ) updateBars(); else updateIconImage( mInterface->ifaceState() ); updateMenu(); } else if ( mTray != 0L ) { if ( mInterface->settings().iconTheme != TEXT_THEME && mInterface->settings().iconTheme != NETLOAD_THEME ) updateIconImage( mInterface->ifaceState() ); } } void InterfaceIcon::showConfigDialog() { KNemoDaemon::sSelectedInterface = mInterface->ifaceName(); KProcess process; process << "kcmshell4" << "kcm_knemo"; process.startDetached(); } void InterfaceIcon::menuTriggered( QAction *action ) { if ( !action->data().canConvert() ) return; InterfaceCommand command = action->data().value(); KProcess *process = new KProcess( this ); if ( command.runAsRoot ) *process << KStandardDirs::findExe("kdesu") << command.command; else process->setShellCommand( command.command ); processList << process; connect( process, SIGNAL( finished( int, QProcess::ExitStatus ) ), this, SLOT( processFinished() ) ); process->start(); } void InterfaceIcon::processFinished() { processList.removeAll( static_cast(sender()) ); static_cast(sender())->deleteLater(); } void InterfaceIcon::showStatistics() { emit statisticsSelected(); } void InterfaceIcon::showStatus() { mInterface->showStatusDialog( true ); } void InterfaceIcon::showGraph() { mInterface->showSignalPlotter( true ); } #include "interfaceicon.moc"