2014-11-13 19:30:51 +02:00
/*
KSysGuard , the KDE System Guard
Copyright ( c ) 1999 - 2001 Chris Schlaeger < cs @ kde . org >
Copyright ( c ) 2006 - 2007 John Tapsell < john . tapsell @ kde . org >
This library 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 .
This library 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 .
*/
2015-02-27 09:28:46 +00:00
# include "moc_ksysguardprocesslist.cpp"
2014-11-13 19:30:51 +02:00
# include "ksysguardprocesslist.h"
# include "../config-ksysguard.h"
# include <QApplication>
# include <QTimer>
# include <QList>
2015-08-12 13:11:16 +03:00
# include <QtGui/qevent.h>
# include <QtGui/qevent.h>
2014-11-13 19:30:51 +02:00
# include <QHeaderView>
# include <QAction>
# include <QMenu>
# include <QSet>
# include <QComboBox>
# include <QStyle>
# include <QStyledItemDelegate>
# include <QPainter>
# include <QLineEdit>
# include <QSignalMapper>
# include <QToolTip>
# include <QAbstractItemModel>
# include <QtDBus>
# include <signal.h> //For SIGTERM
2016-04-09 03:27:21 +00:00
# include <kauthaction.h>
# include <kauthactionreply.h>
# include <kauthhelpersupport.h>
2014-11-13 19:30:51 +02:00
# include <kaction.h>
# include <klocale.h>
# include <kmessagebox.h>
# include <kdialog.h>
# include <kicon.h>
# include <kdebug.h>
# include <KWindowSystem>
# include "ReniceDlg.h"
# include "ui_ProcessWidgetUI.h"
# include <sys/types.h>
# include <unistd.h>
//Trolltech have a testing class for classes that inherit QAbstractItemModel. If you want to run with this run-time testing enabled, put the modeltest.* files in this directory and uncomment the next line
//#define DO_MODELCHECK
# ifdef DO_MODELCHECK
# include "modeltest.h"
# endif
class ProgressBarItemDelegate : public QStyledItemDelegate
{
public :
ProgressBarItemDelegate ( QObject * parent ) : QStyledItemDelegate ( parent ) {
}
virtual void paint ( QPainter * painter , const QStyleOptionViewItem & opt , const QModelIndex & index ) const
{
QStyleOptionViewItemV4 option = opt ;
initStyleOption ( & option , index ) ;
float percentage = index . data ( ProcessModel : : PercentageRole ) . toFloat ( ) ;
if ( percentage > = 0 )
drawPercentageDisplay ( painter , option , percentage ) ;
else
QStyledItemDelegate : : paint ( painter , option , index ) ;
}
private :
inline void drawPercentageDisplay ( QPainter * painter , QStyleOptionViewItemV4 & option , float percentage ) const
{
QStyle * style = option . widget ? option . widget - > style ( ) : QApplication : : style ( ) ;
// draw the background
style - > drawPrimitive ( QStyle : : PE_PanelItemViewItem , & option , painter , option . widget ) ;
QPalette : : ColorGroup cg = option . state & QStyle : : State_Enabled
? QPalette : : Normal : QPalette : : Disabled ;
if ( cg = = QPalette : : Normal & & ! ( option . state & QStyle : : State_Active ) )
cg = QPalette : : Inactive ;
//Now draw our percentage thingy
const QRect & rect = option . rect ;
int size = qMin ( percentage , 1.0f ) * rect . width ( ) ;
if ( size > 2 ) { //make sure the line will have a width of more than 1 pixel
painter - > setPen ( Qt : : NoPen ) ;
QColor color = option . palette . color ( cg , QPalette : : Link ) ;
color . setAlpha ( 50 ) ;
painter - > fillRect ( rect . x ( ) , rect . y ( ) , size , rect . height ( ) , color ) ;
}
// draw the text
if ( ! option . text . isEmpty ( ) ) {
QRect textRect = style - > subElementRect ( QStyle : : SE_ItemViewItemText , & option , option . widget ) ;
if ( option . state & QStyle : : State_Selected ) {
painter - > setPen ( option . palette . color ( cg , QPalette : : HighlightedText ) ) ;
} else {
painter - > setPen ( option . palette . color ( cg , QPalette : : Text ) ) ;
}
painter - > setFont ( option . font ) ;
QTextOption textOption ;
textOption . setWrapMode ( QTextOption : : ManualWrap ) ;
textOption . setTextDirection ( option . direction ) ;
textOption . setAlignment ( QStyle : : visualAlignment ( option . direction , option . displayAlignment ) ) ;
painter - > drawText ( textRect , option . text , textOption ) ;
}
// draw the focus rect
if ( option . state & QStyle : : State_HasFocus ) {
QStyleOptionFocusRect o ;
o . QStyleOption : : operator = ( option ) ;
o . rect = style - > subElementRect ( QStyle : : SE_ItemViewItemFocusRect , & option , option . widget ) ;
o . state | = QStyle : : State_KeyboardFocusChange ;
o . state | = QStyle : : State_Item ;
QPalette : : ColorGroup cg = ( option . state & QStyle : : State_Enabled )
? QPalette : : Normal : QPalette : : Disabled ;
o . backgroundColor = option . palette . color ( cg , ( option . state & QStyle : : State_Selected )
? QPalette : : Highlight : QPalette : : Window ) ;
style - > drawPrimitive ( QStyle : : PE_FrameFocusRect , & o , painter , option . widget ) ;
}
}
} ;
struct KSysGuardProcessListPrivate {
KSysGuardProcessListPrivate ( KSysGuardProcessList * q , const QString & hostName )
: mModel ( q , hostName ) , mFilterModel ( q ) , mUi ( new Ui : : ProcessWidget ( ) ) , mProcessContextMenu ( NULL ) , mUpdateTimer ( NULL )
{
mNeedToExpandInit = false ;
mNumItemsSelected = - 1 ;
mResortCountDown = 2 ; //The items added initially will be already sorted, but without CPU info. On the second refresh we will have CPU usage, so /then/ we can resort
renice = new KAction ( i18np ( " Set Priority... " , " Set Priority... " , 1 ) , q ) ;
renice - > setShortcut ( Qt : : Key_F8 ) ;
selectParent = new KAction ( i18n ( " Jump to Parent Process " ) , q ) ;
selectTracer = new KAction ( i18n ( " Jump to Process Debugging This One " ) , q ) ;
window = new KAction ( i18n ( " Show Application Window " ) , q ) ;
resume = new KAction ( i18n ( " Resume Stopped Process " ) , q ) ;
terminate = new KAction ( i18np ( " End Process " , " End Processes " , 1 ) , q ) ;
terminate - > setIcon ( KIcon ( " process-stop " ) ) ;
terminate - > setShortcut ( Qt : : Key_Delete ) ;
kill = new KAction ( i18np ( " Forcibly Kill Process " , " Forcibly Kill Processes " , 1 ) , q ) ;
kill - > setIcon ( KIcon ( " process-stop " ) ) ;
kill - > setShortcut ( Qt : : SHIFT + Qt : : Key_Delete ) ;
sigStop = new KAction ( i18n ( " Suspend (STOP) " ) , q ) ;
sigCont = new KAction ( i18n ( " Continue (CONT) " ) , q ) ;
sigHup = new KAction ( i18n ( " Hangup (HUP) " ) , q ) ;
sigInt = new KAction ( i18n ( " Interrupt (INT) " ) , q ) ;
sigTerm = new KAction ( i18n ( " Terminate (TERM) " ) , q ) ;
sigKill = new KAction ( i18n ( " Kill (KILL) " ) , q ) ;
sigUsr1 = new KAction ( i18n ( " User 1 (USR1) " ) , q ) ;
sigUsr2 = new KAction ( i18n ( " User 2 (USR2) " ) , q ) ;
//Set up '/' as a shortcut to jump to the quick search text widget
jumpToSearchFilter = new KAction ( i18n ( " Focus on Quick Search " ) , q ) ;
jumpToSearchFilter - > setShortcuts ( QList < QKeySequence > ( ) < < QKeySequence : : Find < < ' / ' ) ;
}
~ KSysGuardProcessListPrivate ( ) { delete mUi ; mUi = NULL ; }
/** The number rows and their children for the given parent in the mFilterModel model */
int totalRowCount ( const QModelIndex & parent ) const ;
/** Helper function to setup 'action' with the given pids */
void setupKAuthAction ( KAuth : : Action * action , const QList < long long > & pids ) const ;
/** fire a timer event if we are set to use our internal timer*/
void fireTimerEvent ( ) ;
/** The process model. This contains all the data on all the processes running on the system */
ProcessModel mModel ;
/** The process filter. The mModel is connected to this, and this filter model connects to the view. This lets us
* sort the view and filter ( by using the combo box or the search line )
*/
ProcessFilter mFilterModel ;
/** The graphical user interface for this process list widget, auto-generated by Qt Designer */
Ui : : ProcessWidget * mUi ;
/** The context menu when you right click on a process */
QMenu * mProcessContextMenu ;
/** A timer to call updateList() every mUpdateIntervalMSecs.
* NULL is mUpdateIntervalMSecs is < = 0. */
QTimer * mUpdateTimer ;
/** The time to wait, in milliseconds, between updating the process list */
int mUpdateIntervalMSecs ;
/** Number of items that are selected */
int mNumItemsSelected ;
/** A counter to mark when to resort, so that we do not resort on every update */
int mResortCountDown ;
bool mNeedToExpandInit ;
KAction * renice ;
KAction * terminate ;
KAction * kill ;
KAction * selectParent ;
KAction * selectTracer ;
KAction * jumpToSearchFilter ;
KAction * window ;
KAction * resume ;
KAction * sigStop ;
KAction * sigCont ;
KAction * sigHup ;
KAction * sigInt ;
KAction * sigTerm ;
KAction * sigKill ;
KAction * sigUsr1 ;
KAction * sigUsr2 ;
} ;
KSysGuardProcessList : : KSysGuardProcessList ( QWidget * parent , const QString & hostName )
: QWidget ( parent ) , d ( new KSysGuardProcessListPrivate ( this , hostName ) )
{
qRegisterMetaType < QList < long long > > ( ) ;
qDBusRegisterMetaType < QList < long long > > ( ) ;
d - > mUpdateIntervalMSecs = 0 ; //Set process to not update manually by default
d - > mUi - > setupUi ( this ) ;
d - > mFilterModel . setSourceModel ( & d - > mModel ) ;
d - > mUi - > treeView - > setModel ( & d - > mFilterModel ) ;
# ifdef DO_MODELCHECK
new ModelTest ( & d - > mModel , this ) ;
# endif
d - > mUi - > treeView - > setItemDelegate ( new ProgressBarItemDelegate ( d - > mUi - > treeView ) ) ;
d - > mUi - > treeView - > header ( ) - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
connect ( d - > mUi - > treeView - > header ( ) , SIGNAL ( customContextMenuRequested ( QPoint ) ) , this , SLOT ( showColumnContextMenu ( QPoint ) ) ) ;
d - > mProcessContextMenu = new QMenu ( d - > mUi - > treeView ) ;
d - > mUi - > treeView - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
connect ( d - > mUi - > treeView , SIGNAL ( customContextMenuRequested ( QPoint ) ) , this , SLOT ( showProcessContextMenu ( QPoint ) ) ) ;
d - > mUi - > treeView - > header ( ) - > setClickable ( true ) ;
d - > mUi - > treeView - > header ( ) - > setSortIndicatorShown ( true ) ;
d - > mUi - > treeView - > header ( ) - > setCascadingSectionResizes ( false ) ;
connect ( d - > mUi - > btnKillProcess , SIGNAL ( clicked ( ) ) , this , SLOT ( killSelectedProcesses ( ) ) ) ;
connect ( d - > mUi - > txtFilter , SIGNAL ( textChanged ( QString ) ) , this , SLOT ( filterTextChanged ( QString ) ) ) ;
connect ( d - > mUi - > cmbFilter , SIGNAL ( currentIndexChanged ( int ) ) , this , SLOT ( setStateInt ( int ) ) ) ;
connect ( d - > mUi - > treeView , SIGNAL ( expanded ( QModelIndex ) ) , this , SLOT ( expandAllChildren ( QModelIndex ) ) ) ;
connect ( d - > mUi - > treeView - > selectionModel ( ) , SIGNAL ( selectionChanged ( QItemSelection , QItemSelection ) ) , this , SLOT ( selectionChanged ( ) ) ) ;
connect ( & d - > mFilterModel , SIGNAL ( rowsInserted ( QModelIndex , int , int ) ) , this , SLOT ( rowsInserted ( QModelIndex , int , int ) ) ) ;
connect ( & d - > mFilterModel , SIGNAL ( rowsRemoved ( QModelIndex , int , int ) ) , this , SIGNAL ( processListChanged ( ) ) ) ;
setMinimumSize ( sizeHint ( ) ) ;
d - > mFilterModel . setFilterKeyColumn ( - 1 ) ;
/* Hide various columns by default, to reduce information overload */
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingVmSize ) ;
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingNiceness ) ;
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingTty ) ;
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingCommand ) ;
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingPid ) ;
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingCPUTime ) ;
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingIoRead ) ;
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingIoWrite ) ;
d - > mUi - > treeView - > header ( ) - > hideSection ( ProcessModel : : HeadingXMemory ) ;
// NOTE! After this is all setup, the settings for the header are restored
// from the user's last run. (in restoreHeaderState)
// So making changes here only affects the default settings. To
// test changes temporarily, comment out the lines in restoreHeaderState.
// When you are happy with the changes and want to commit, increase the
// value of PROCESSHEADERVERSION. This will force the header state
// to be reset back to the defaults for all users.
d - > mUi - > treeView - > header ( ) - > resizeSection ( ProcessModel : : HeadingCPUUsage , d - > mUi - > treeView - > header ( ) - > sectionSizeHint ( ProcessModel : : HeadingCPUUsage ) ) ;
d - > mUi - > treeView - > header ( ) - > resizeSection ( ProcessModel : : HeadingMemory , d - > mUi - > treeView - > header ( ) - > sectionSizeHint ( ProcessModel : : HeadingMemory ) ) ;
d - > mUi - > treeView - > header ( ) - > resizeSection ( ProcessModel : : HeadingSharedMemory , d - > mUi - > treeView - > header ( ) - > sectionSizeHint ( ProcessModel : : HeadingSharedMemory ) ) ;
d - > mUi - > treeView - > header ( ) - > setResizeMode ( 0 , QHeaderView : : Interactive ) ;
d - > mUi - > treeView - > header ( ) - > setStretchLastSection ( true ) ;
//Process names can have mixed case. Make the filter case insensitive.
d - > mFilterModel . setFilterCaseSensitivity ( Qt : : CaseInsensitive ) ;
d - > mFilterModel . setSortCaseSensitivity ( Qt : : CaseInsensitive ) ;
d - > mUi - > txtFilter - > installEventFilter ( this ) ;
d - > mUi - > treeView - > installEventFilter ( this ) ;
d - > mUi - > treeView - > setDragEnabled ( true ) ;
d - > mUi - > treeView - > setDragDropMode ( QAbstractItemView : : DragOnly ) ;
//Sort by username by default
d - > mUi - > treeView - > sortByColumn ( ProcessModel : : HeadingUser , Qt : : AscendingOrder ) ;
// Add all the actions to the main widget, and get all the actions to call actionTriggered when clicked
QSignalMapper * signalMapper = new QSignalMapper ( this ) ;
QList < QAction * > actions ;
actions < < d - > renice < < d - > kill < < d - > terminate < < d - > selectParent < < d - > selectTracer < < d - > window < < d - > jumpToSearchFilter ;
actions < < d - > resume < < d - > sigStop < < d - > sigCont < < d - > sigHup < < d - > sigInt < < d - > sigTerm < < d - > sigKill < < d - > sigUsr1 < < d - > sigUsr2 ;
foreach ( QAction * action , actions ) {
addAction ( action ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , signalMapper , SLOT ( map ( ) ) ) ;
signalMapper - > setMapping ( action , action ) ;
}
connect ( signalMapper , SIGNAL ( mapped ( QObject * ) ) , SLOT ( actionTriggered ( QObject * ) ) ) ;
retranslateUi ( ) ;
d - > mUi - > btnKillProcess - > setIcon ( KIcon ( " process-stop " ) ) ;
d - > mUi - > btnKillProcess - > setToolTip ( i18n ( " <qt>End the selected process. Warning - you may lose unsaved work.<br>Right click on a process to send other signals.<br>See What's This for technical information.<br>To target a specific window to kill, press Ctrl+Alt+Esc at any time. " ) ) ;
}
KSysGuardProcessList : : ~ KSysGuardProcessList ( )
{
delete d ;
}
QTreeView * KSysGuardProcessList : : treeView ( ) const {
return d - > mUi - > treeView ;
}
QLineEdit * KSysGuardProcessList : : filterLineEdit ( ) const {
return d - > mUi - > txtFilter ;
}
ProcessFilter : : State KSysGuardProcessList : : state ( ) const
{
return d - > mFilterModel . filter ( ) ;
}
void KSysGuardProcessList : : setStateInt ( int state ) {
setState ( ( ProcessFilter : : State ) state ) ;
d - > mUi - > treeView - > scrollTo ( d - > mUi - > treeView - > currentIndex ( ) ) ;
}
void KSysGuardProcessList : : setState ( ProcessFilter : : State state )
{ //index is the item the user selected in the combo box
d - > mFilterModel . setFilter ( state ) ;
d - > mModel . setSimpleMode ( ( state ! = ProcessFilter : : AllProcessesInTreeForm ) ) ;
d - > mUi - > cmbFilter - > setCurrentIndex ( ( int ) state ) ;
if ( isVisible ( ) )
expandInit ( ) ;
}
void KSysGuardProcessList : : filterTextChanged ( const QString & newText ) {
d - > mFilterModel . setFilterRegExp ( newText . trimmed ( ) ) ;
if ( isVisible ( ) )
expandInit ( ) ;
d - > mUi - > btnKillProcess - > setEnabled ( d - > mUi - > treeView - > selectionModel ( ) - > hasSelection ( ) ) ;
d - > mUi - > treeView - > scrollTo ( d - > mUi - > treeView - > currentIndex ( ) ) ;
}
int KSysGuardProcessList : : visibleProcessesCount ( ) const {
//This assumes that all the visible rows are processes. This is true currently, but might not be
//true if we add support for showing threads etc
if ( d - > mModel . isSimpleMode ( ) )
return d - > mFilterModel . rowCount ( ) ;
return d - > totalRowCount ( QModelIndex ( ) ) ;
}
int KSysGuardProcessListPrivate : : totalRowCount ( const QModelIndex & parent ) const {
int numRows = mFilterModel . rowCount ( parent ) ;
int total = numRows ;
for ( int i = 0 ; i < numRows ; + + i ) {
QModelIndex index = mFilterModel . index ( i , 0 , parent ) ;
//if it has children add the total
if ( mFilterModel . hasChildren ( index ) )
total + = totalRowCount ( index ) ;
}
return total ;
}
void KSysGuardProcessListPrivate : : setupKAuthAction ( KAuth : : Action * action , const QList < long long > & pids ) const
{
action - > setHelperID ( " org.kde.ksysguard.processlisthelper " ) ;
int processCount = pids . count ( ) ;
for ( int i = 0 ; i < processCount ; i + + ) {
action - > addArgument ( QString ( " pid%1 " ) . arg ( i ) , pids [ i ] ) ;
}
action - > addArgument ( " pidcount " , processCount ) ;
}
void KSysGuardProcessList : : selectionChanged ( )
{
int numSelected = d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) . size ( ) ;
if ( numSelected = = d - > mNumItemsSelected )
return ;
d - > mNumItemsSelected = numSelected ;
d - > mUi - > btnKillProcess - > setEnabled ( numSelected ! = 0 ) ;
d - > renice - > setText ( i18np ( " Set Priority... " , " Set Priority... " , numSelected ) ) ;
d - > kill - > setText ( i18np ( " Forcibly Kill Process " , " Forcibly Kill Processes " , numSelected ) ) ;
d - > terminate - > setText ( i18ncp ( " Context menu " , " End Process " , " End Processes " , numSelected ) ) ;
}
void KSysGuardProcessList : : showProcessContextMenu ( const QModelIndex & index ) {
if ( ! index . isValid ( ) ) return ;
QRect rect = d - > mUi - > treeView - > visualRect ( index ) ;
QPoint point ( rect . x ( ) + rect . width ( ) / 4 , rect . y ( ) + rect . height ( ) / 2 ) ;
showProcessContextMenu ( point ) ;
}
void KSysGuardProcessList : : showProcessContextMenu ( const QPoint & point ) {
d - > mProcessContextMenu - > clear ( ) ;
QModelIndexList selectedIndexes = d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) ;
int numProcesses = selectedIndexes . size ( ) ;
if ( numProcesses = = 0 ) {
//No processes selected, so no process context menu
//Check just incase we have no columns visible. In which case show the column context menu
//so that users can unhide columns if there are no columns visible
for ( int i = 0 ; i < d - > mFilterModel . columnCount ( ) ; + + i ) {
if ( ! d - > mUi - > treeView - > header ( ) - > isSectionHidden ( i ) )
return ;
}
showColumnContextMenu ( point ) ;
return ;
}
QModelIndex realIndex = d - > mFilterModel . mapToSource ( selectedIndexes . at ( 0 ) ) ;
KSysGuard : : Process * process = reinterpret_cast < KSysGuard : : Process * > ( realIndex . internalPointer ( ) ) ;
//If the selected process is a zombie, do not bother offering renice and kill options
bool showSignalingEntries = numProcesses ! = 1 | | process - > status ! = KSysGuard : : Process : : Zombie ;
if ( showSignalingEntries ) {
d - > mProcessContextMenu - > addAction ( d - > renice ) ;
QMenu * signalMenu = d - > mProcessContextMenu - > addMenu ( i18n ( " Send Signal " ) ) ;
signalMenu - > addAction ( d - > sigStop ) ;
signalMenu - > addAction ( d - > sigCont ) ;
signalMenu - > addAction ( d - > sigHup ) ;
signalMenu - > addAction ( d - > sigInt ) ;
signalMenu - > addAction ( d - > sigTerm ) ;
signalMenu - > addAction ( d - > sigKill ) ;
signalMenu - > addAction ( d - > sigUsr1 ) ;
signalMenu - > addAction ( d - > sigUsr2 ) ;
}
if ( numProcesses = = 1 & & process - > parent_pid > 1 ) {
//As a design decision, I do not show the 'Jump to parent process' option when the
//parent is just 'init'.
KSysGuard : : Process * parent_process = d - > mModel . getProcess ( process - > parent_pid ) ;
if ( parent_process ) { //it should not be possible for this process to not exist, but check just incase
QString parent_name = parent_process - > name ;
if ( parent_name . size ( ) > 20 ) //Elide the text if it is too long
parent_name = parent_process - > name . left ( 15 ) + QString : : fromUtf8 ( " … " ) ;
d - > selectParent - > setText ( i18n ( " Jump to Parent Process (%1) " , parent_name ) ) ;
d - > mProcessContextMenu - > addAction ( d - > selectParent ) ;
}
}
if ( numProcesses = = 1 & & process - > tracerpid > = 0 ) {
//If the process is being debugged, offer to select it
d - > mProcessContextMenu - > addAction ( d - > selectTracer ) ;
}
if ( numProcesses = = 1 & & ! d - > mModel . data ( realIndex , ProcessModel : : WindowIdRole ) . isNull ( ) ) {
d - > mProcessContextMenu - > addAction ( d - > window ) ;
}
if ( numProcesses = = 1 & & process - > status = = KSysGuard : : Process : : Stopped ) {
//If the process is stopped, offer to resume it
d - > mProcessContextMenu - > addAction ( d - > resume ) ;
}
if ( showSignalingEntries ) {
d - > mProcessContextMenu - > addSeparator ( ) ;
d - > mProcessContextMenu - > addAction ( d - > terminate ) ;
if ( numProcesses = = 1 & & ! process - > timeKillWasSent . isNull ( ) )
d - > mProcessContextMenu - > addAction ( d - > kill ) ;
}
d - > mProcessContextMenu - > popup ( d - > mUi - > treeView - > viewport ( ) - > mapToGlobal ( point ) ) ;
}
void KSysGuardProcessList : : actionTriggered ( QObject * object ) {
if ( ! isVisible ( ) ) //Ignore triggered actions if we are not visible!
return ;
//Reset the text back to normal
d - > selectParent - > setText ( i18n ( " Jump to Parent Process " ) ) ;
QAction * result = qobject_cast < QAction * > ( object ) ;
if ( result = = 0 ) {
//Escape was pressed. Do nothing.
} else if ( result = = d - > renice ) {
reniceSelectedProcesses ( ) ;
} else if ( result = = d - > terminate ) {
sendSignalToSelectedProcesses ( SIGTERM , true ) ;
} else if ( result = = d - > kill ) {
sendSignalToSelectedProcesses ( SIGKILL , true ) ;
} else if ( result = = d - > selectParent ) {
QModelIndexList selectedIndexes = d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) ;
int numProcesses = selectedIndexes . size ( ) ;
if ( numProcesses = = 0 ) return ; //No processes selected
QModelIndex realIndex = d - > mFilterModel . mapToSource ( selectedIndexes . at ( 0 ) ) ;
KSysGuard : : Process * process = reinterpret_cast < KSysGuard : : Process * > ( realIndex . internalPointer ( ) ) ;
if ( process )
selectAndJumpToProcess ( process - > parent_pid ) ;
} else if ( result = = d - > selectTracer ) {
QModelIndexList selectedIndexes = d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) ;
int numProcesses = selectedIndexes . size ( ) ;
if ( numProcesses = = 0 ) return ; //No processes selected
QModelIndex realIndex = d - > mFilterModel . mapToSource ( selectedIndexes . at ( 0 ) ) ;
KSysGuard : : Process * process = reinterpret_cast < KSysGuard : : Process * > ( realIndex . internalPointer ( ) ) ;
if ( process )
selectAndJumpToProcess ( process - > tracerpid ) ;
} else if ( result = = d - > window ) {
QModelIndexList selectedIndexes = d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) ;
int numProcesses = selectedIndexes . size ( ) ;
if ( numProcesses = = 0 ) return ; //No processes selected
foreach ( const QModelIndex & index , selectedIndexes ) {
QModelIndex realIndex = d - > mFilterModel . mapToSource ( index ) ;
QVariant widVar = d - > mModel . data ( realIndex , ProcessModel : : WindowIdRole ) ;
if ( ! widVar . isNull ( ) ) {
int wid = widVar . toInt ( ) ;
KWindowSystem : : activateWindow ( wid ) ;
}
}
} else if ( result = = d - > jumpToSearchFilter ) {
d - > mUi - > txtFilter - > setFocus ( ) ;
} else {
int sig ;
if ( result = = d - > resume | | result = = d - > sigCont )
sig = SIGCONT ; //Despite the function name, this sends a signal, rather than kill it. Silly unix :)
else if ( result = = d - > sigStop )
sig = SIGSTOP ;
else if ( result = = d - > sigHup )
sig = SIGHUP ;
else if ( result = = d - > sigInt )
sig = SIGINT ;
else if ( result = = d - > sigTerm )
sig = SIGTERM ;
else if ( result = = d - > sigKill )
sig = SIGKILL ;
else if ( result = = d - > sigUsr1 )
sig = SIGUSR1 ;
else if ( result = = d - > sigUsr2 )
sig = SIGUSR2 ;
else
return ;
sendSignalToSelectedProcesses ( sig , false ) ;
}
}
void KSysGuardProcessList : : selectAndJumpToProcess ( int pid ) {
KSysGuard : : Process * process = d - > mModel . getProcess ( pid ) ;
if ( ! process ) return ;
QModelIndex sourceIndex = d - > mModel . getQModelIndex ( process , 0 ) ;
QModelIndex filterIndex = d - > mFilterModel . mapFromSource ( sourceIndex ) ;
if ( ! filterIndex . isValid ( ) & & ! d - > mUi - > txtFilter - > text ( ) . isEmpty ( ) ) {
//The filter is preventing us from finding the parent. Clear the filter
//(It could also be the combo box - should we deal with that case as well?)
d - > mUi - > txtFilter - > clear ( ) ;
filterIndex = d - > mFilterModel . mapFromSource ( sourceIndex ) ;
}
d - > mUi - > treeView - > clearSelection ( ) ;
d - > mUi - > treeView - > setCurrentIndex ( filterIndex ) ;
d - > mUi - > treeView - > scrollTo ( filterIndex , QAbstractItemView : : PositionAtCenter ) ;
}
void KSysGuardProcessList : : showColumnContextMenu ( const QPoint & point ) {
QMenu menu ;
QAction * action ;
int num_headings = d - > mFilterModel . columnCount ( ) ;
int index = d - > mUi - > treeView - > header ( ) - > logicalIndexAt ( point ) ;
if ( index > = 0 ) {
bool anyOtherVisibleColumns = false ;
for ( int i = 0 ; i < num_headings ; + + i ) {
if ( i ! = index & & ! d - > mUi - > treeView - > header ( ) - > isSectionHidden ( i ) ) {
anyOtherVisibleColumns = true ;
break ;
}
}
if ( anyOtherVisibleColumns ) {
//selected a column. Give the option to hide it
action = new QAction ( & menu ) ;
action - > setData ( - index - 1 ) ; //We set data to be negative (and minus 1) to hide a column, and positive to show a column
action - > setText ( i18n ( " Hide Column '%1' " , d - > mFilterModel . headerData ( index , Qt : : Horizontal , Qt : : DisplayRole ) . toString ( ) ) ) ;
menu . addAction ( action ) ;
if ( d - > mUi - > treeView - > header ( ) - > sectionsHidden ( ) ) {
menu . addSeparator ( ) ;
}
}
}
if ( d - > mUi - > treeView - > header ( ) - > sectionsHidden ( ) ) {
for ( int i = 0 ; i < num_headings ; + + i ) {
if ( d - > mUi - > treeView - > header ( ) - > isSectionHidden ( i ) ) {
# ifndef HAVE_XRES
if ( i = = ProcessModel : : HeadingXMemory )
continue ;
# endif
action = new QAction ( & menu ) ;
action - > setText ( i18n ( " Show Column '%1' " , d - > mFilterModel . headerData ( i , Qt : : Horizontal , Qt : : DisplayRole ) . toString ( ) ) ) ;
action - > setData ( i ) ; //We set data to be negative (and minus 1) to hide a column, and positive to show a column
menu . addAction ( action ) ;
}
}
}
QAction * actionAuto = NULL ;
QAction * actionKB = NULL ;
QAction * actionMB = NULL ;
QAction * actionGB = NULL ;
QAction * actionPercentage = NULL ;
QAction * actionShowCmdlineOptions = NULL ;
QAction * actionShowTooltips = NULL ;
QAction * actionNormalizeCPUUsage = NULL ;
QAction * actionIoCharacters = NULL ;
QAction * actionIoSyscalls = NULL ;
QAction * actionIoActualCharacters = NULL ;
QAction * actionIoShowRate = NULL ;
bool showIoRate = false ;
if ( index = = ProcessModel : : HeadingIoRead | | index = = ProcessModel : : HeadingIoWrite )
showIoRate = d - > mModel . ioInformation ( ) = = ProcessModel : : BytesRate | |
d - > mModel . ioInformation ( ) = = ProcessModel : : SyscallsRate | |
d - > mModel . ioInformation ( ) = = ProcessModel : : ActualBytesRate ;
if ( index = = ProcessModel : : HeadingVmSize | | index = = ProcessModel : : HeadingMemory | | index = = ProcessModel : : HeadingXMemory | | index = = ProcessModel : : HeadingSharedMemory | | ( ( index = = ProcessModel : : HeadingIoRead | | index = = ProcessModel : : HeadingIoWrite ) & & d - > mModel . ioInformation ( ) ! = ProcessModel : : Syscalls ) ) {
//If the user right clicks on a column that contains a memory size, show a toggle option for displaying
//the memory in different units. e.g. "2000 k" or "2 m"
menu . addSeparator ( ) - > setText ( i18n ( " Display Units " ) ) ;
QActionGroup * unitsGroup = new QActionGroup ( & menu ) ;
/* Automatic (human readable)*/
actionAuto = new QAction ( & menu ) ;
actionAuto - > setText ( i18n ( " Mixed " ) ) ;
actionAuto - > setCheckable ( true ) ;
menu . addAction ( actionAuto ) ;
unitsGroup - > addAction ( actionAuto ) ;
/* Kilobytes */
actionKB = new QAction ( & menu ) ;
actionKB - > setText ( ( showIoRate ) ? i18n ( " Kilobytes per second " ) : i18n ( " Kilobytes " ) ) ;
actionKB - > setCheckable ( true ) ;
menu . addAction ( actionKB ) ;
unitsGroup - > addAction ( actionKB ) ;
/* Megabytes */
actionMB = new QAction ( & menu ) ;
actionMB - > setText ( ( showIoRate ) ? i18n ( " Megabytes per second " ) : i18n ( " Megabytes " ) ) ;
actionMB - > setCheckable ( true ) ;
menu . addAction ( actionMB ) ;
unitsGroup - > addAction ( actionMB ) ;
/* Gigabytes */
actionGB = new QAction ( & menu ) ;
actionGB - > setText ( ( showIoRate ) ? i18n ( " Gigabytes per second " ) : i18n ( " Gigabytes " ) ) ;
actionGB - > setCheckable ( true ) ;
menu . addAction ( actionGB ) ;
unitsGroup - > addAction ( actionGB ) ;
ProcessModel : : Units currentUnit ;
if ( index = = ProcessModel : : HeadingIoRead | | index = = ProcessModel : : HeadingIoWrite ) {
currentUnit = d - > mModel . ioUnits ( ) ;
} else {
actionPercentage = new QAction ( & menu ) ;
actionPercentage - > setText ( i18n ( " Percentage " ) ) ;
actionPercentage - > setCheckable ( true ) ;
menu . addAction ( actionPercentage ) ;
unitsGroup - > addAction ( actionPercentage ) ;
currentUnit = d - > mModel . units ( ) ;
}
switch ( currentUnit ) {
case ProcessModel : : UnitsAuto :
actionAuto - > setChecked ( true ) ;
break ;
case ProcessModel : : UnitsKB :
actionKB - > setChecked ( true ) ;
break ;
case ProcessModel : : UnitsMB :
actionMB - > setChecked ( true ) ;
break ;
case ProcessModel : : UnitsGB :
actionGB - > setChecked ( true ) ;
break ;
case ProcessModel : : UnitsPercentage :
actionPercentage - > setChecked ( true ) ;
break ;
default :
break ;
}
unitsGroup - > setExclusive ( true ) ;
} else if ( index = = ProcessModel : : HeadingName ) {
menu . addSeparator ( ) ;
actionShowCmdlineOptions = new QAction ( & menu ) ;
actionShowCmdlineOptions - > setText ( i18n ( " Display command line options " ) ) ;
actionShowCmdlineOptions - > setCheckable ( true ) ;
actionShowCmdlineOptions - > setChecked ( d - > mModel . isShowCommandLineOptions ( ) ) ;
menu . addAction ( actionShowCmdlineOptions ) ;
} else if ( index = = ProcessModel : : HeadingCPUUsage ) {
menu . addSeparator ( ) ;
actionNormalizeCPUUsage = new QAction ( & menu ) ;
actionNormalizeCPUUsage - > setText ( i18n ( " Divide CPU usage by number of CPUs " ) ) ;
actionNormalizeCPUUsage - > setCheckable ( true ) ;
actionNormalizeCPUUsage - > setChecked ( d - > mModel . isNormalizedCPUUsage ( ) ) ;
menu . addAction ( actionNormalizeCPUUsage ) ;
}
if ( index = = ProcessModel : : HeadingIoRead | | index = = ProcessModel : : HeadingIoWrite ) {
menu . addSeparator ( ) - > setText ( i18n ( " Displayed Information " ) ) ;
QActionGroup * ioInformationGroup = new QActionGroup ( & menu ) ;
actionIoCharacters = new QAction ( & menu ) ;
actionIoCharacters - > setText ( i18n ( " Characters read/written " ) ) ;
actionIoCharacters - > setCheckable ( true ) ;
menu . addAction ( actionIoCharacters ) ;
ioInformationGroup - > addAction ( actionIoCharacters ) ;
actionIoSyscalls = new QAction ( & menu ) ;
actionIoSyscalls - > setText ( i18n ( " Number of Read/Write operations " ) ) ;
actionIoSyscalls - > setCheckable ( true ) ;
menu . addAction ( actionIoSyscalls ) ;
ioInformationGroup - > addAction ( actionIoSyscalls ) ;
actionIoActualCharacters = new QAction ( & menu ) ;
actionIoActualCharacters - > setText ( i18n ( " Bytes actually read/written " ) ) ;
actionIoActualCharacters - > setCheckable ( true ) ;
menu . addAction ( actionIoActualCharacters ) ;
ioInformationGroup - > addAction ( actionIoActualCharacters ) ;
actionIoShowRate = new QAction ( & menu ) ;
actionIoShowRate - > setText ( i18n ( " Show I/O rate " ) ) ;
actionIoShowRate - > setCheckable ( true ) ;
actionIoShowRate - > setChecked ( showIoRate ) ;
menu . addAction ( actionIoShowRate ) ;
switch ( d - > mModel . ioInformation ( ) ) {
case ProcessModel : : Bytes :
case ProcessModel : : BytesRate :
actionIoCharacters - > setChecked ( true ) ;
break ;
case ProcessModel : : Syscalls :
case ProcessModel : : SyscallsRate :
actionIoSyscalls - > setChecked ( true ) ;
break ;
case ProcessModel : : ActualBytes :
case ProcessModel : : ActualBytesRate :
actionIoActualCharacters - > setChecked ( true ) ;
break ;
default :
break ;
}
}
menu . addSeparator ( ) ;
actionShowTooltips = new QAction ( & menu ) ;
actionShowTooltips - > setCheckable ( true ) ;
actionShowTooltips - > setChecked ( d - > mModel . isShowingTooltips ( ) ) ;
actionShowTooltips - > setText ( i18n ( " Show Tooltips " ) ) ;
menu . addAction ( actionShowTooltips ) ;
QAction * result = menu . exec ( d - > mUi - > treeView - > header ( ) - > mapToGlobal ( point ) ) ;
if ( ! result ) return ; //Menu cancelled
if ( result = = actionAuto ) {
if ( index = = ProcessModel : : HeadingIoRead | | index = = ProcessModel : : HeadingIoWrite )
d - > mModel . setIoUnits ( ProcessModel : : UnitsAuto ) ;
else
d - > mModel . setUnits ( ProcessModel : : UnitsAuto ) ;
return ;
} else if ( result = = actionKB ) {
if ( index = = ProcessModel : : HeadingIoRead | | index = = ProcessModel : : HeadingIoWrite )
d - > mModel . setIoUnits ( ProcessModel : : UnitsKB ) ;
else
d - > mModel . setUnits ( ProcessModel : : UnitsKB ) ;
return ;
} else if ( result = = actionMB ) {
if ( index = = ProcessModel : : HeadingIoRead | | index = = ProcessModel : : HeadingIoWrite )
d - > mModel . setIoUnits ( ProcessModel : : UnitsMB ) ;
else
d - > mModel . setUnits ( ProcessModel : : UnitsMB ) ;
return ;
} else if ( result = = actionGB ) {
if ( index = = ProcessModel : : HeadingIoRead | | index = = ProcessModel : : HeadingIoWrite )
d - > mModel . setIoUnits ( ProcessModel : : UnitsGB ) ;
else
d - > mModel . setUnits ( ProcessModel : : UnitsGB ) ;
return ;
} else if ( result = = actionPercentage ) {
d - > mModel . setUnits ( ProcessModel : : UnitsPercentage ) ;
return ;
} else if ( result = = actionShowCmdlineOptions ) {
d - > mModel . setShowCommandLineOptions ( actionShowCmdlineOptions - > isChecked ( ) ) ;
return ;
} else if ( result = = actionNormalizeCPUUsage ) {
d - > mModel . setNormalizedCPUUsage ( actionNormalizeCPUUsage - > isChecked ( ) ) ;
return ;
} else if ( result = = actionShowTooltips ) {
d - > mModel . setShowingTooltips ( actionShowTooltips - > isChecked ( ) ) ;
return ;
} else if ( result = = actionIoCharacters ) {
d - > mModel . setIoInformation ( ( showIoRate ) ? ProcessModel : : BytesRate : ProcessModel : : Bytes ) ;
return ;
} else if ( result = = actionIoSyscalls ) {
d - > mModel . setIoInformation ( ( showIoRate ) ? ProcessModel : : SyscallsRate : ProcessModel : : Syscalls ) ;
return ;
} else if ( result = = actionIoActualCharacters ) {
d - > mModel . setIoInformation ( ( showIoRate ) ? ProcessModel : : ActualBytesRate : ProcessModel : : ActualBytes ) ;
return ;
} else if ( result = = actionIoShowRate ) {
showIoRate = actionIoShowRate - > isChecked ( ) ;
switch ( d - > mModel . ioInformation ( ) ) {
case ProcessModel : : Bytes :
case ProcessModel : : BytesRate :
d - > mModel . setIoInformation ( ( showIoRate ) ? ProcessModel : : BytesRate : ProcessModel : : Bytes ) ;
break ;
case ProcessModel : : Syscalls :
case ProcessModel : : SyscallsRate :
d - > mModel . setIoInformation ( ( showIoRate ) ? ProcessModel : : SyscallsRate : ProcessModel : : Syscalls ) ;
break ;
case ProcessModel : : ActualBytes :
case ProcessModel : : ActualBytesRate :
d - > mModel . setIoInformation ( ( showIoRate ) ? ProcessModel : : ActualBytesRate : ProcessModel : : ActualBytes ) ;
break ;
default :
break ;
}
}
int i = result - > data ( ) . toInt ( ) ;
//We set data to be negative to hide a column, and positive to show a column
if ( i < 0 )
d - > mUi - > treeView - > hideColumn ( - 1 - i ) ;
else {
d - > mUi - > treeView - > showColumn ( i ) ;
updateList ( ) ;
d - > mUi - > treeView - > resizeColumnToContents ( i ) ;
d - > mUi - > treeView - > resizeColumnToContents ( d - > mFilterModel . columnCount ( ) ) ;
}
menu . deleteLater ( ) ;
}
void KSysGuardProcessList : : expandAllChildren ( const QModelIndex & parent )
{
//This is called when the user expands a node. This then expands all of its
//children. This will trigger this function again recursively.
QModelIndex sourceParent = d - > mFilterModel . mapToSource ( parent ) ;
for ( int i = 0 ; i < d - > mModel . rowCount ( sourceParent ) ; i + + ) {
d - > mUi - > treeView - > expand ( d - > mFilterModel . mapFromSource ( d - > mModel . index ( i , 0 , sourceParent ) ) ) ;
}
}
void KSysGuardProcessList : : rowsInserted ( const QModelIndex & parent , int start , int end )
{
if ( d - > mModel . isSimpleMode ( ) | | parent . isValid ( ) ) {
emit processListChanged ( ) ;
return ; //No tree or not a root node - no need to expand init
}
disconnect ( & d - > mFilterModel , SIGNAL ( rowsInserted ( QModelIndex , int , int ) ) , this , SLOT ( rowsInserted ( QModelIndex , int , int ) ) ) ;
//It is a root node that we just inserted - expand it
bool expanded = false ;
for ( int i = start ; i < = end ; i + + ) {
QModelIndex index = d - > mFilterModel . index ( i , 0 , QModelIndex ( ) ) ;
if ( ! d - > mUi - > treeView - > isExpanded ( index ) ) {
if ( ! expanded ) {
disconnect ( d - > mUi - > treeView , SIGNAL ( expanded ( QModelIndex ) ) , this , SLOT ( expandAllChildren ( QModelIndex ) ) ) ;
expanded = true ;
}
d - > mUi - > treeView - > expand ( index ) ;
d - > mNeedToExpandInit = true ;
}
}
if ( expanded )
connect ( d - > mUi - > treeView , SIGNAL ( expanded ( QModelIndex ) ) , this , SLOT ( expandAllChildren ( QModelIndex ) ) ) ;
connect ( & d - > mFilterModel , SIGNAL ( rowsInserted ( QModelIndex , int , int ) ) , this , SLOT ( rowsInserted ( QModelIndex , int , int ) ) ) ;
emit processListChanged ( ) ;
}
void KSysGuardProcessList : : expandInit ( )
{
if ( d - > mModel . isSimpleMode ( ) ) return ; //No tree - no need to expand init
bool expanded = false ;
for ( int i = 0 ; i < d - > mFilterModel . rowCount ( QModelIndex ( ) ) ; i + + ) {
QModelIndex index = d - > mFilterModel . index ( i , 0 , QModelIndex ( ) ) ;
if ( ! d - > mUi - > treeView - > isExpanded ( index ) ) {
if ( ! expanded ) {
disconnect ( d - > mUi - > treeView , SIGNAL ( expanded ( QModelIndex ) ) , this , SLOT ( expandAllChildren ( QModelIndex ) ) ) ;
expanded = true ;
}
d - > mUi - > treeView - > expand ( index ) ;
}
}
if ( expanded )
connect ( d - > mUi - > treeView , SIGNAL ( expanded ( QModelIndex ) ) , this , SLOT ( expandAllChildren ( QModelIndex ) ) ) ;
}
void KSysGuardProcessList : : hideEvent ( QHideEvent * event ) //virtual protected from QWidget
{
//Stop updating the process list if we are hidden
if ( d - > mUpdateTimer )
d - > mUpdateTimer - > stop ( ) ;
QWidget : : hideEvent ( event ) ;
}
void KSysGuardProcessList : : showEvent ( QShowEvent * event ) //virtual protected from QWidget
{
//Start updating the process list again if we are shown again
updateList ( ) ;
QHeaderView * header = d - > mUi - > treeView - > header ( ) ;
d - > mUi - > treeView - > sortByColumn ( header - > sortIndicatorSection ( ) , header - > sortIndicatorOrder ( ) ) ;
QWidget : : showEvent ( event ) ;
}
void KSysGuardProcessList : : changeEvent ( QEvent * event )
{
if ( event - > type ( ) = = QEvent : : LanguageChange ) {
d - > mModel . retranslateUi ( ) ;
d - > mUi - > retranslateUi ( this ) ;
retranslateUi ( ) ;
}
QWidget : : changeEvent ( event ) ;
}
void KSysGuardProcessList : : retranslateUi ( )
{
d - > mUi - > cmbFilter - > setItemIcon ( ProcessFilter : : AllProcesses , KIcon ( " view-process-all " ) ) ;
d - > mUi - > cmbFilter - > setItemIcon ( ProcessFilter : : AllProcessesInTreeForm , KIcon ( " view-process-all-tree " ) ) ;
d - > mUi - > cmbFilter - > setItemIcon ( ProcessFilter : : SystemProcesses , KIcon ( " view-process-system " ) ) ;
d - > mUi - > cmbFilter - > setItemIcon ( ProcessFilter : : UserProcesses , KIcon ( " view-process-users " ) ) ;
d - > mUi - > cmbFilter - > setItemIcon ( ProcessFilter : : OwnProcesses , KIcon ( " view-process-own " ) ) ;
d - > mUi - > cmbFilter - > setItemIcon ( ProcessFilter : : ProgramsOnly , KIcon ( " view-process-all " ) ) ;
}
void KSysGuardProcessList : : updateList ( )
{
if ( isVisible ( ) ) {
KSysGuard : : Processes : : UpdateFlags updateFlags = KSysGuard : : Processes : : StandardInformation ;
if ( ! d - > mUi - > treeView - > isColumnHidden ( ProcessModel : : HeadingIoRead ) | | ! d - > mUi - > treeView - > isColumnHidden ( ProcessModel : : HeadingIoWrite ) )
updateFlags | = KSysGuard : : Processes : : IOStatistics ;
if ( ! d - > mUi - > treeView - > isColumnHidden ( ProcessModel : : HeadingXMemory ) )
updateFlags | = KSysGuard : : Processes : : XMemory ;
d - > mModel . update ( d - > mUpdateIntervalMSecs , updateFlags ) ;
if ( d - > mUpdateTimer )
d - > mUpdateTimer - > start ( d - > mUpdateIntervalMSecs ) ;
emit updated ( ) ;
if ( QToolTip : : isVisible ( ) & & qApp - > topLevelAt ( QCursor : : pos ( ) ) = = window ( ) ) {
QWidget * w = d - > mUi - > treeView - > viewport ( ) ;
if ( w - > geometry ( ) . contains ( d - > mUi - > treeView - > mapFromGlobal ( QCursor : : pos ( ) ) ) ) {
QHelpEvent event ( QEvent : : ToolTip , w - > mapFromGlobal ( QCursor : : pos ( ) ) , QCursor : : pos ( ) ) ;
qApp - > notify ( w , & event ) ;
}
}
if ( - - d - > mResortCountDown < = 0 ) {
d - > mResortCountDown = 2 ; //resort every second time
//resort now
QHeaderView * header = d - > mUi - > treeView - > header ( ) ;
d - > mUi - > treeView - > sortByColumn ( header - > sortIndicatorSection ( ) , header - > sortIndicatorOrder ( ) ) ;
}
if ( d - > mNeedToExpandInit ) {
expandInit ( ) ;
d - > mNeedToExpandInit = false ;
}
}
}
int KSysGuardProcessList : : updateIntervalMSecs ( ) const
{
return d - > mUpdateIntervalMSecs ;
}
void KSysGuardProcessList : : setUpdateIntervalMSecs ( int intervalMSecs )
{
if ( intervalMSecs = = d - > mUpdateIntervalMSecs )
return ;
d - > mUpdateIntervalMSecs = intervalMSecs ;
if ( intervalMSecs < = 0 ) { //no point keep the timer around if we aren't updating automatically
delete d - > mUpdateTimer ;
d - > mUpdateTimer = NULL ;
return ;
}
if ( ! d - > mUpdateTimer ) {
//intervalMSecs is a valid time, so set up a timer
d - > mUpdateTimer = new QTimer ( this ) ;
d - > mUpdateTimer - > setSingleShot ( true ) ;
connect ( d - > mUpdateTimer , SIGNAL ( timeout ( ) ) , SLOT ( updateList ( ) ) ) ;
if ( isVisible ( ) )
d - > mUpdateTimer - > start ( d - > mUpdateIntervalMSecs ) ;
} else
d - > mUpdateTimer - > setInterval ( d - > mUpdateIntervalMSecs ) ;
}
bool KSysGuardProcessList : : reniceProcesses ( const QList < long long > & pids , int niceValue )
{
QList < long long > unreniced_pids ;
for ( int i = 0 ; i < pids . size ( ) ; + + i ) {
bool success = d - > mModel . processController ( ) - > setNiceness ( pids . at ( i ) , niceValue ) ;
if ( ! success ) {
unreniced_pids < < pids . at ( i ) ;
}
}
if ( unreniced_pids . isEmpty ( ) ) return true ; //All processes were reniced successfully
if ( ! d - > mModel . isLocalhost ( ) ) return false ; //We can't use kauth to renice non-localhost processes
KAuth : : Action * action = new KAuth : : Action ( " org.kde.ksysguard.processlisthelper.renice " ) ;
action - > setParentWidget ( window ( ) ) ;
d - > setupKAuthAction ( action , unreniced_pids ) ;
action - > addArgument ( " nicevalue " , niceValue ) ;
KAuth : : ActionReply reply = action - > execute ( ) ;
if ( reply = = KAuth : : ActionReply : : SuccessReply ) {
updateList ( ) ;
delete action ;
} else if ( reply ! = KAuth : : ActionReply : : UserCancelled & & reply ! = KAuth : : ActionReply : : AuthorizationDenied ) {
KMessageBox : : sorry ( this , i18n ( " You do not have the permission to renice the process and there "
" was a problem trying to run as root. Error %1 %2 " , reply . errorCode ( ) , reply . errorDescription ( ) ) ) ;
delete action ;
return false ;
}
return true ;
}
QList < KSysGuard : : Process * > KSysGuardProcessList : : selectedProcesses ( ) const
{
QList < KSysGuard : : Process * > processes ;
QModelIndexList selectedIndexes = d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) ;
for ( int i = 0 ; i < selectedIndexes . size ( ) ; + + i ) {
KSysGuard : : Process * process = reinterpret_cast < KSysGuard : : Process * > ( d - > mFilterModel . mapToSource ( selectedIndexes . at ( i ) ) . internalPointer ( ) ) ;
processes < < process ;
}
return processes ;
}
void KSysGuardProcessList : : reniceSelectedProcesses ( )
{
QList < long long > pids ;
QPointer < ReniceDlg > reniceDlg ;
{
QList < KSysGuard : : Process * > processes = selectedProcesses ( ) ;
QStringList selectedAsStrings ;
if ( processes . isEmpty ( ) ) {
KMessageBox : : sorry ( this , i18n ( " You must select a process first. " ) ) ;
return ;
}
int sched = - 2 ;
int iosched = - 2 ;
foreach ( KSysGuard : : Process * process , processes ) {
pids < < process - > pid ;
selectedAsStrings < < d - > mModel . getStringForProcess ( process ) ;
if ( sched = = - 2 ) sched = ( int ) process - > scheduler ;
else if ( sched ! = - 1 & & sched ! = ( int ) process - > scheduler ) sched = - 1 ; //If two processes have different schedulers, disable the cpu scheduler stuff
if ( iosched = = - 2 ) iosched = ( int ) process - > ioPriorityClass ;
else if ( iosched ! = - 1 & & iosched ! = ( int ) process - > ioPriorityClass ) iosched = - 1 ; //If two processes have different schedulers, disable the cpu scheduler stuff
}
int firstPriority = processes . first ( ) - > niceLevel ;
int firstIOPriority = processes . first ( ) - > ioniceLevel ;
bool supportsIoNice = d - > mModel . processController ( ) - > supportsIoNiceness ( ) ;
if ( ! supportsIoNice ) { iosched = - 2 ; firstIOPriority = - 2 ; }
reniceDlg = new ReniceDlg ( d - > mUi - > treeView , selectedAsStrings , firstPriority , sched , firstIOPriority , iosched ) ;
if ( reniceDlg - > exec ( ) = = QDialog : : Rejected ) {
delete reniceDlg ;
return ;
}
}
//Because we've done into ReniceDlg, which calls processEvents etc, our processes list is no
//longer valid
QList < long long > renicePids ;
QList < long long > changeCPUSchedulerPids ;
QList < long long > changeIOSchedulerPids ;
foreach ( long long pid , pids ) {
KSysGuard : : Process * process = d - > mModel . getProcess ( pid ) ;
if ( ! process )
continue ;
switch ( reniceDlg - > newCPUSched ) {
case - 2 :
case - 1 : //Invalid, not changed etc.
break ; //So do nothing
case KSysGuard : : Process : : Other :
case KSysGuard : : Process : : Fifo :
if ( reniceDlg - > newCPUSched ! = ( int ) process - > scheduler ) {
changeCPUSchedulerPids < < pid ;
renicePids < < pid ;
} else if ( reniceDlg - > newCPUPriority ! = process - > niceLevel )
renicePids < < pid ;
break ;
case KSysGuard : : Process : : RoundRobin :
case KSysGuard : : Process : : Batch :
if ( reniceDlg - > newCPUSched ! = ( int ) process - > scheduler | | reniceDlg - > newCPUPriority ! = process - > niceLevel ) {
changeCPUSchedulerPids < < pid ;
}
break ;
}
switch ( reniceDlg - > newIOSched ) {
case - 2 :
case - 1 : //Invalid, not changed etc.
break ; //So do nothing
case KSysGuard : : Process : : None :
if ( reniceDlg - > newIOSched ! = ( int ) process - > ioPriorityClass ) {
// Unfortunately linux doesn't actually let us set the ioniceness back to none after being set to something else
if ( process - > ioPriorityClass ! = KSysGuard : : Process : : BestEffort | | reniceDlg - > newIOPriority ! = process - > ioniceLevel )
changeIOSchedulerPids < < pid ;
}
break ;
case KSysGuard : : Process : : Idle :
if ( reniceDlg - > newIOSched ! = ( int ) process - > ioPriorityClass ) {
changeIOSchedulerPids < < pid ;
}
break ;
case KSysGuard : : Process : : BestEffort :
if ( process - > ioPriorityClass = = KSysGuard : : Process : : None & & reniceDlg - > newIOPriority = = ( process - > niceLevel + 20 ) / 5 )
break ; //Don't set to BestEffort if it's on None and the nicelevel wouldn't change
case KSysGuard : : Process : : RealTime :
if ( reniceDlg - > newIOSched ! = ( int ) process - > ioPriorityClass | | reniceDlg - > newIOPriority ! = process - > ioniceLevel ) {
changeIOSchedulerPids < < pid ;
}
break ;
}
}
if ( ! changeCPUSchedulerPids . isEmpty ( ) ) {
Q_ASSERT ( reniceDlg - > newCPUSched > = 0 ) ;
if ( ! changeCpuScheduler ( changeCPUSchedulerPids , ( KSysGuard : : Process : : Scheduler ) reniceDlg - > newCPUSched , reniceDlg - > newCPUPriority ) ) {
delete reniceDlg ;
return ;
}
}
if ( ! renicePids . isEmpty ( ) ) {
Q_ASSERT ( reniceDlg - > newCPUPriority < = 20 & & reniceDlg - > newCPUPriority > = - 20 ) ;
if ( ! reniceProcesses ( renicePids , reniceDlg - > newCPUPriority ) ) {
delete reniceDlg ;
return ;
}
}
if ( ! changeIOSchedulerPids . isEmpty ( ) ) {
if ( ! changeIoScheduler ( changeIOSchedulerPids , ( KSysGuard : : Process : : IoPriorityClass ) reniceDlg - > newIOSched , reniceDlg - > newIOPriority ) ) {
delete reniceDlg ;
return ;
}
}
delete reniceDlg ;
updateList ( ) ;
}
bool KSysGuardProcessList : : changeIoScheduler ( const QList < long long > & pids , KSysGuard : : Process : : IoPriorityClass newIoSched , int newIoSchedPriority )
{
if ( newIoSched = = KSysGuard : : Process : : None ) newIoSched = KSysGuard : : Process : : BestEffort ;
if ( newIoSched = = KSysGuard : : Process : : Idle ) newIoSchedPriority = 0 ;
QList < long long > unchanged_pids ;
for ( int i = 0 ; i < pids . size ( ) ; + + i ) {
bool success = d - > mModel . processController ( ) - > setIoNiceness ( pids . at ( i ) , newIoSched , newIoSchedPriority ) ;
if ( ! success ) {
unchanged_pids < < pids . at ( i ) ;
}
}
if ( unchanged_pids . isEmpty ( ) ) return true ;
if ( ! d - > mModel . isLocalhost ( ) ) return false ; //We can't use kauth to affect non-localhost processes
KAuth : : Action * action = new KAuth : : Action ( " org.kde.ksysguard.processlisthelper.changeioscheduler " ) ;
action - > setParentWidget ( window ( ) ) ;
d - > setupKAuthAction ( action , unchanged_pids ) ;
action - > addArgument ( " ioScheduler " , ( int ) newIoSched ) ;
action - > addArgument ( " ioSchedulerPriority " , newIoSchedPriority ) ;
KAuth : : ActionReply reply = action - > execute ( ) ;
if ( reply = = KAuth : : ActionReply : : SuccessReply ) {
updateList ( ) ;
delete action ;
} else if ( reply ! = KAuth : : ActionReply : : UserCancelled & & reply ! = KAuth : : ActionReply : : AuthorizationDenied ) {
KMessageBox : : sorry ( this , i18n ( " You do not have the permission to change the I/O priority of the process and there "
" was a problem trying to run as root. Error %1 %2 " , reply . errorCode ( ) , reply . errorDescription ( ) ) ) ;
delete action ;
return false ;
}
return true ;
}
bool KSysGuardProcessList : : changeCpuScheduler ( const QList < long long > & pids , KSysGuard : : Process : : Scheduler newCpuSched , int newCpuSchedPriority )
{
if ( newCpuSched = = KSysGuard : : Process : : Other | | newCpuSched = = KSysGuard : : Process : : Batch ) newCpuSchedPriority = 0 ;
QList < long long > unchanged_pids ;
for ( int i = 0 ; i < pids . size ( ) ; + + i ) {
bool success = d - > mModel . processController ( ) - > setScheduler ( pids . at ( i ) , newCpuSched , newCpuSchedPriority ) ;
if ( ! success ) {
unchanged_pids < < pids . at ( i ) ;
}
}
if ( unchanged_pids . isEmpty ( ) ) return true ;
if ( ! d - > mModel . isLocalhost ( ) ) return false ; //We can't use KAuth to affect non-localhost processes
KAuth : : Action * action = new KAuth : : Action ( " org.kde.ksysguard.processlisthelper.changecpuscheduler " ) ;
action - > setParentWidget ( window ( ) ) ;
d - > setupKAuthAction ( action , unchanged_pids ) ;
action - > addArgument ( " cpuScheduler " , ( int ) newCpuSched ) ;
action - > addArgument ( " cpuSchedulerPriority " , newCpuSchedPriority ) ;
KAuth : : ActionReply reply = action - > execute ( ) ;
if ( reply = = KAuth : : ActionReply : : SuccessReply ) {
updateList ( ) ;
delete action ;
} else if ( reply ! = KAuth : : ActionReply : : UserCancelled & & reply ! = KAuth : : ActionReply : : AuthorizationDenied ) {
KMessageBox : : sorry ( this , i18n ( " You do not have the permission to change the CPU Scheduler for the process and there "
" was a problem trying to run as root. Error %1 %2 " , reply . errorCode ( ) , reply . errorDescription ( ) ) ) ;
delete action ;
return false ;
}
return true ;
}
bool KSysGuardProcessList : : killProcesses ( const QList < long long > & pids , int sig )
{
QList < long long > unkilled_pids ;
for ( int i = 0 ; i < pids . size ( ) ; + + i ) {
bool success = d - > mModel . processController ( ) - > sendSignal ( pids . at ( i ) , sig ) ;
// If we failed due to a reason other than insufficient permissions, elevating to root can't
// help us
if ( ! success & & ( d - > mModel . processController ( ) - > lastError ( ) = = KSysGuard : : Processes : : InsufficientPermissions | | d - > mModel . processController ( ) - > lastError ( ) = = KSysGuard : : Processes : : Unknown ) ) {
unkilled_pids < < pids . at ( i ) ;
}
}
if ( unkilled_pids . isEmpty ( ) ) return true ;
if ( ! d - > mModel . isLocalhost ( ) ) return false ; //We can't elevate privileges to kill non-localhost processes
KAuth : : Action action ( " org.kde.ksysguard.processlisthelper.sendsignal " ) ;
action . setParentWidget ( window ( ) ) ;
d - > setupKAuthAction ( & action , unkilled_pids ) ;
action . addArgument ( " signal " , sig ) ;
KAuth : : ActionReply reply = action . execute ( ) ;
if ( reply = = KAuth : : ActionReply : : SuccessReply ) {
updateList ( ) ;
} else if ( reply . type ( ) = = KAuth : : ActionReply : : HelperError ) {
if ( reply . errorCode ( ) > 0 )
KMessageBox : : sorry ( this , i18n ( " You do not have the permission to kill the process and there "
" was a problem trying to run as root. %1 " , reply . errorDescription ( ) ) ) ;
return false ;
} else if ( reply ! = KAuth : : ActionReply : : UserCancelled & & reply ! = KAuth : : ActionReply : : AuthorizationDenied ) {
KMessageBox : : sorry ( this , i18n ( " You do not have the permission to kill the process and there "
" was a problem trying to run as root. Error %1 %2 " , reply . errorCode ( ) , reply . errorDescription ( ) ) ) ;
return false ;
}
return true ;
}
void KSysGuardProcessList : : killSelectedProcesses ( )
{
sendSignalToSelectedProcesses ( SIGTERM , true ) ;
}
void KSysGuardProcessList : : sendSignalToSelectedProcesses ( int sig , bool confirm )
{
QModelIndexList selectedIndexes = d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) ;
QStringList selectedAsStrings ;
QList < long long > selectedPids ;
QList < KSysGuard : : Process * > processes = selectedProcesses ( ) ;
foreach ( KSysGuard : : Process * process , processes ) {
selectedPids < < process - > pid ;
if ( ! confirm )
continue ;
QString name = d - > mModel . getStringForProcess ( process ) ;
if ( name . size ( ) > 100 )
name = name . left ( 95 ) + QString : : fromUtf8 ( " … " ) ;
selectedAsStrings < < name ;
}
if ( selectedPids . isEmpty ( ) ) {
if ( confirm )
KMessageBox : : sorry ( this , i18n ( " You must select a process first. " ) ) ;
return ;
} else if ( confirm & & ( sig = = SIGTERM | | sig = = SIGKILL ) ) {
int count = selectedAsStrings . count ( ) ;
QString msg ;
QString title ;
QString dontAskAgainKey ;
QString closeButton ;
if ( sig = = SIGTERM ) {
msg = i18np ( " Are you sure you want to end this process? Any unsaved work may be lost. " ,
" Are you sure you want to end these %1 processes? Any unsaved work may be lost " ,
count ) ;
title = i18ncp ( " Dialog title " , " End Process " , " End %1 Processes " , count ) ;
dontAskAgainKey = " endconfirmation " ;
closeButton = i18n ( " End " ) ;
} else if ( sig = = SIGKILL ) {
msg = i18np ( " <qt>Are you sure you want to <b>immediately and forcibly kill</b> this process? Any unsaved work may be lost. " ,
" <qt>Are you sure you want to <b>immediately and forcibly kill</b> these %1 processes? Any unsaved work may be lost " ,
count ) ;
title = i18ncp ( " Dialog title " , " Forcibly Kill Process " , " Forcibly Kill %1 Processes " , count ) ;
dontAskAgainKey = " killconfirmation " ;
closeButton = i18n ( " Kill " ) ;
}
int res = KMessageBox : : warningContinueCancelList ( this , msg , selectedAsStrings ,
title ,
KGuiItem ( closeButton , " process-stop " ) ,
KStandardGuiItem : : cancel ( ) ,
dontAskAgainKey ) ;
if ( res ! = KMessageBox : : Continue )
return ;
}
//We have shown a GUI dialog box, which processes events etc.
//So processes is NO LONGER VALID
if ( ! killProcesses ( selectedPids , sig ) )
return ;
if ( sig = = SIGTERM | | sig = = SIGKILL ) {
foreach ( long long pid , selectedPids ) {
KSysGuard : : Process * process = d - > mModel . getProcess ( pid ) ;
if ( process )
process - > timeKillWasSent . start ( ) ;
d - > mUi - > treeView - > selectionModel ( ) - > clearSelection ( ) ;
}
}
updateList ( ) ;
}
bool KSysGuardProcessList : : showTotals ( ) const {
return d - > mModel . showTotals ( ) ;
}
void KSysGuardProcessList : : setShowTotals ( bool showTotals ) //slot
{
d - > mModel . setShowTotals ( showTotals ) ;
}
ProcessModel : : Units KSysGuardProcessList : : units ( ) const {
return d - > mModel . units ( ) ;
}
void KSysGuardProcessList : : setUnits ( ProcessModel : : Units unit ) {
d - > mModel . setUnits ( unit ) ;
}
void KSysGuardProcessList : : saveSettings ( KConfigGroup & cg ) {
/* Note that the ksysguard program does not use these functions. It saves the settings itself to an xml file instead */
cg . writeEntry ( " units " , ( int ) ( units ( ) ) ) ;
cg . writeEntry ( " ioUnits " , ( int ) ( d - > mModel . ioUnits ( ) ) ) ;
cg . writeEntry ( " ioInformation " , ( int ) ( d - > mModel . ioInformation ( ) ) ) ;
cg . writeEntry ( " showCommandLineOptions " , d - > mModel . isShowCommandLineOptions ( ) ) ;
cg . writeEntry ( " normalizeCPUUsage " , d - > mModel . isNormalizedCPUUsage ( ) ) ;
cg . writeEntry ( " showTooltips " , d - > mModel . isShowingTooltips ( ) ) ;
cg . writeEntry ( " showTotals " , showTotals ( ) ) ;
cg . writeEntry ( " filterState " , ( int ) ( state ( ) ) ) ;
cg . writeEntry ( " updateIntervalMSecs " , updateIntervalMSecs ( ) ) ;
cg . writeEntry ( " headerState " , d - > mUi - > treeView - > header ( ) - > saveState ( ) ) ;
//If we change, say, the header between versions of ksysguard, then the old headerState settings will not be valid.
//The version property lets us keep track of which version we are
cg . writeEntry ( " version " , PROCESSHEADERVERSION ) ;
}
void KSysGuardProcessList : : loadSettings ( const KConfigGroup & cg ) {
/* Note that the ksysguard program does not use these functions. It saves the settings itself to an xml file instead */
setUnits ( ( ProcessModel : : Units ) cg . readEntry ( " units " , ( int ) ProcessModel : : UnitsKB ) ) ;
d - > mModel . setIoUnits ( ( ProcessModel : : Units ) cg . readEntry ( " ioUnits " , ( int ) ProcessModel : : UnitsKB ) ) ;
d - > mModel . setIoInformation ( ( ProcessModel : : IoInformation ) cg . readEntry ( " ioInformation " , ( int ) ProcessModel : : ActualBytesRate ) ) ;
d - > mModel . setShowCommandLineOptions ( cg . readEntry ( " showCommandLineOptions " , false ) ) ;
d - > mModel . setNormalizedCPUUsage ( cg . readEntry ( " normalizeCPUUsage " , true ) ) ;
d - > mModel . setShowingTooltips ( cg . readEntry ( " showTooltips " , true ) ) ;
setShowTotals ( cg . readEntry ( " showTotals " , true ) ) ;
setStateInt ( cg . readEntry ( " filterState " , ( int ) ProcessFilter : : AllProcesses ) ) ;
setUpdateIntervalMSecs ( cg . readEntry ( " updateIntervalMSecs " , 2000 ) ) ;
int version = cg . readEntry ( " version " , 0 ) ;
if ( version = = PROCESSHEADERVERSION ) { //If the header has changed, the old settings are no longer valid. Only restore if version is the same
restoreHeaderState ( cg . readEntry ( " headerState " , QByteArray ( ) ) ) ;
}
}
void KSysGuardProcessList : : restoreHeaderState ( const QByteArray & state ) {
d - > mUi - > treeView - > header ( ) - > restoreState ( state ) ;
}
bool KSysGuardProcessList : : eventFilter ( QObject * obj , QEvent * event ) {
if ( event - > type ( ) = = QEvent : : KeyPress ) {
QKeyEvent * keyEvent = static_cast < QKeyEvent * > ( event ) ;
if ( obj = = d - > mUi - > treeView ) {
if ( keyEvent - > key ( ) = = Qt : : Key_Enter | | keyEvent - > key ( ) = = Qt : : Key_Return ) {
d - > mUi - > treeView - > selectionModel ( ) - > select ( d - > mUi - > treeView - > currentIndex ( ) , QItemSelectionModel : : Select | QItemSelectionModel : : Rows ) ;
showProcessContextMenu ( d - > mUi - > treeView - > currentIndex ( ) ) ;
return true ;
} else if ( keyEvent - > matches ( QKeySequence : : MoveToPreviousLine ) | | keyEvent - > matches ( QKeySequence : : SelectPreviousLine ) | |
keyEvent - > matches ( QKeySequence : : MoveToPreviousPage ) | | keyEvent - > matches ( QKeySequence : : SelectPreviousPage ) ) {
if ( d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) . size ( ) = = 1 & &
d - > mUi - > treeView - > selectionModel ( ) - > selectedRows ( ) . first ( ) . row ( ) = = 0 ) {
// when first row is selected, pressing up or pgup moves to the textfield
d - > mUi - > txtFilter - > setFocus ( ) ;
return true ;
}
} else if ( ! keyEvent - > text ( ) . isEmpty ( ) & & keyEvent - > key ( ) ! = Qt : : Key_Tab ) {
// move to textfield and forward keyevent if user starts typing from treeview
d - > mUi - > txtFilter - > setFocus ( ) ;
QApplication : : sendEvent ( d - > mUi - > txtFilter , event ) ;
return true ;
}
} else {
Q_ASSERT ( obj = = d - > mUi - > txtFilter ) ;
if ( d - > mUi - > treeView - > model ( ) - > rowCount ( ) = = 0 ) {
// treeview is empty, do nothing
return false ;
}
if ( keyEvent - > key ( ) = = Qt : : Key_Enter | | keyEvent - > key ( ) = = Qt : : Key_Return ) {
// pressing enter will send enter to the first row in the list
// the focusin eventfilter will make sure the first row is selected if there was
// no previous selection
d - > mUi - > treeView - > setFocus ( ) ;
QApplication : : sendEvent ( d - > mUi - > treeView , event ) ;
return true ;
} else if ( keyEvent - > matches ( QKeySequence : : MoveToNextLine ) | | keyEvent - > matches ( QKeySequence : : SelectNextLine ) | |
keyEvent - > matches ( QKeySequence : : MoveToNextPage ) | | keyEvent - > matches ( QKeySequence : : SelectNextPage ) ) {
// attempting to move down by down-key or pgdown, or pressing enter will move focus
// to the treeview
d - > mUi - > treeView - > setFocus ( ) ;
return true ;
}
}
} else if ( event - > type ( ) = = QEvent : : FocusIn ) {
if ( obj = = d - > mUi - > treeView & & ! d - > mUi - > treeView - > selectionModel ( ) - > hasSelection ( ) ) {
// treeview is focused, but there is no current selection. select the first row
d - > mUi - > treeView - > setCurrentIndex ( d - > mUi - > treeView - > model ( ) - > index ( 0 , 0 ) ) ;
} else if ( obj = = d - > mUi - > txtFilter ) {
// text field is focused, so clear treeview selection to communicate that you will not
// interact with any elements in the view until the view is focused explicitly
d - > mUi - > treeView - > setCurrentIndex ( QModelIndex ( ) ) ;
}
}
return false ;
}
ProcessModel * KSysGuardProcessList : : processModel ( ) {
return & d - > mModel ;
}
void KSysGuardProcessList : : setKillButtonVisible ( bool visible )
{
d - > mUi - > btnKillProcess - > setVisible ( visible ) ;
}
bool KSysGuardProcessList : : isKillButtonVisible ( ) const
{
return d - > mUi - > btnKillProcess - > isVisible ( ) ;
}