/* * Copyright 2001 Stefan Schimanski <1Stein@gmx.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ktimer.h" #include #include #include #include #include #include #include #include #include #include #include #include "kstatusnotifieritem.h" class KTimerJobItem : public QTreeWidgetItem { public: KTimerJobItem( KTimerJob *job, QTreeWidget *parent ) : QTreeWidgetItem() { parent->addTopLevelItem(this); m_job = job; m_error = false; update(); } KTimerJobItem( KTimerJob *job, QTreeWidget * parent, QTreeWidgetItem *after ) : QTreeWidgetItem() { int otherItemIndex = parent->indexOfTopLevelItem(after); parent->insertTopLevelItem(otherItemIndex + 1, this); m_job = job; m_error = false; update(); } virtual ~KTimerJobItem() { delete m_job; } KTimerJob *job() { return m_job; } void setStatus( bool error ) { m_error = error; update(); } void update() { setText( 0, m_job->formatTime(m_job->value()) ); if( m_error ) setIcon( 0, KIcon( QLatin1String( "process-stop" )) ); else setIcon( 0, QPixmap() ); setText( 1, m_job->formatTime(m_job->delay()) ); switch( m_job->state() ) { case KTimerJob::Stopped: setIcon( 2, KIcon( QLatin1String( "media-playback-stop" )) ); break; case KTimerJob::Paused: setIcon( 2, KIcon( QLatin1String( "media-playback-pause" )) ); break; case KTimerJob::Started: setIcon( 2, KIcon( QLatin1String( "arrow-right" )) ); break; } setText( 3, m_job->command() ); } private: bool m_error; KTimerJob *m_job; }; /***************************************************************/ struct KTimerPrefPrivate { QList jobs; }; KTimerPref::KTimerPref( QWidget *parent) : QDialog( parent ) { d = new KTimerPrefPrivate; setupUi(this); // set icons m_stop->setIcon( KIcon( QLatin1String( "media-playback-stop" )) ); m_pause->setIcon( KIcon( QLatin1String( "media-playback-pause" )) ); m_start->setIcon( KIcon( QLatin1String( "arrow-right" )) ); // create tray icon KStatusNotifierItem *tray = new KStatusNotifierItem(this); tray->setIconByName(QLatin1String( "ktimer" )); tray->setCategory(KStatusNotifierItem::ApplicationStatus); tray->setStatus(KStatusNotifierItem::Active); // set help button gui item m_help->setGuiItem(KStandardGuiItem::help()); // Exit KAction *exit = KStandardAction::quit(this, SLOT(exit()), this); addAction(exit); // connect connect( m_add, SIGNAL(clicked()), SLOT(add()) ); connect( m_remove, SIGNAL(clicked()), SLOT(remove()) ); connect( m_help, SIGNAL(clicked()), SLOT(help()) ); connect( m_list, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(currentChanged(QTreeWidgetItem*,QTreeWidgetItem*)) ); loadJobs( KGlobal::config().data() ); show(); } KTimerPref::~KTimerPref() { delete d; } void KTimerPref::saveAllJobs() { saveJobs( KGlobal::config().data() ); } void KTimerPref::add() { KTimerJob *job = new KTimerJob; KTimerJobItem *item = new KTimerJobItem( job, m_list ); connect( job, SIGNAL(delayChanged(KTimerJob*,uint)), SLOT(jobChanged(KTimerJob*)) ); connect( job, SIGNAL(valueChanged(KTimerJob*,uint)), SLOT(jobChanged(KTimerJob*)) ); connect( job, SIGNAL(stateChanged(KTimerJob*,States)), SLOT(jobChanged(KTimerJob*)) ); connect( job, SIGNAL(commandChanged(KTimerJob*,QString)), SLOT(jobChanged(KTimerJob*)) ); connect( job, SIGNAL(finished(KTimerJob*,bool)), SLOT(jobFinished(KTimerJob*,bool)) ); job->setUser( item ); // Qt drops currentChanged signals on first item (bug?) if( m_list->topLevelItemCount()==1 ) currentChanged( item , NULL); m_list->setCurrentItem( item ); m_list->update(); } void KTimerPref::remove() { delete m_list->currentItem(); m_list->update(); } void KTimerPref::help() { KToolInvocation::invokeHelp(); } // note, don't use old, but added it so we can connect to the new one void KTimerPref::currentChanged( QTreeWidgetItem *i , QTreeWidgetItem * /* old */) { KTimerJobItem *item = static_cast(i); if( item ) { KTimerJob *job = item->job(); m_state->setEnabled( true ); m_settings->setEnabled( true ); m_remove->setEnabled( true ); m_delayH->disconnect(); m_delayM->disconnect(); m_delay->disconnect(); m_loop->disconnect(); m_one->disconnect(); m_start->disconnect(); m_pause->disconnect(); m_stop->disconnect(); m_counter->disconnect(); m_slider->disconnect(); m_commandLine->disconnect(); m_commandLine->lineEdit()->disconnect(); // Set hour, minute and second QSpinBoxes before we connect to signals. int h, m, s; job->secondsToHMS( job->delay(), &h, &m, &s ); m_delayH->setValue( h ); m_delayM->setValue( m ); m_delay->setValue( s ); connect( m_commandLine->lineEdit(), SIGNAL(textChanged(QString)), job, SLOT(setCommand(QString)) ); connect( m_delayH, SIGNAL(valueChanged(int)), SLOT(delayChanged()) ); connect( m_delayM, SIGNAL(valueChanged(int)), SLOT(delayChanged()) ); connect( m_delay, SIGNAL(valueChanged(int)), SLOT(delayChanged()) ); connect( m_loop, SIGNAL(toggled(bool)), job, SLOT(setLoop(bool)) ); connect( m_one, SIGNAL(toggled(bool)), job, SLOT(setOneInstance(bool)) ); connect( m_stop, SIGNAL(clicked()), job, SLOT(stop()) ); connect( m_pause, SIGNAL(clicked()), job, SLOT(pause()) ); connect( m_start, SIGNAL(clicked()), job, SLOT(start()) ); connect( m_slider, SIGNAL(valueChanged(int)), job, SLOT(setValue(int)) ); m_commandLine->lineEdit()->setText( job->command() ); m_loop->setChecked( job->loop() ); m_one->setChecked( job->oneInstance() ); m_counter->display( (int)job->value() ); m_slider->setMaximum( job->delay() ); m_slider->setValue( job->value() ); } else { m_state->setEnabled( false ); m_settings->setEnabled( false ); m_remove->setEnabled( false ); } } void KTimerPref::jobChanged( KTimerJob *job ) { KTimerJobItem *item = static_cast(job->user()); if( item ) { item->update(); m_list->update(); if( item==m_list->currentItem() ) { // XXX optimize m_slider->setMaximum( job->delay() ); m_slider->setValue( job->value() ); m_counter->display( (int)job->value() ); } } } void KTimerPref::jobFinished( KTimerJob *job, bool error ) { KTimerJobItem *item = static_cast(job->user()); item->setStatus( error ); m_list->update(); } /* Hour/Minute/Second was changed. This slot calculates the seconds until we start the job and inform the current job */ void KTimerPref::delayChanged() { KTimerJobItem *item = static_cast(m_list->currentItem()); if ( item ) { KTimerJob *job = item->job(); int time_sec = job->timeToSeconds( m_delayH->value(), m_delayM->value(), m_delay->value() ); job->setDelay( time_sec ); } } // Really quits the application void KTimerPref::exit() { done(0); qApp->quit(); } void KTimerPref::done(int result) { saveAllJobs(); QDialog::done(result); } void KTimerPref::saveJobs( KConfig *cfg ) { const int nbList=m_list->topLevelItemCount(); for (int num = 0; num < nbList; ++num) { KTimerJobItem *item = static_cast(m_list->topLevelItem(num)); item->job()->save( cfg, QString(QLatin1String( "Job%1" )).arg( num ) ); } KConfigGroup jobscfg = cfg->group("Jobs"); jobscfg.writeEntry( "Number", m_list->topLevelItemCount()); jobscfg.sync(); } void KTimerPref::loadJobs( KConfig *cfg ) { const int num = cfg->group("Jobs").readEntry( "Number", 0 ); for( int n=0; nload( cfg, QString( QLatin1String( "Job%1" ) ).arg(n) ); job->setUser( item ); jobChanged ( job); } m_list->update(); } /*********************************************************************/ struct KTimerJobPrivate { unsigned delay; QString command; bool loop; bool oneInstance; unsigned value; KTimerJob::States state; QList processes; void *user; QTimer *timer; }; KTimerJob::KTimerJob( QObject *parent) : QObject( parent ) { d = new KTimerJobPrivate; d->delay = 100; d->loop = false; d->oneInstance = true; d->value = 100; d->state = Stopped; d->user = 0; d->timer = new QTimer( this ); connect( d->timer, SIGNAL(timeout()), SLOT(timeout()) ); } KTimerJob::~KTimerJob() { delete d; } void KTimerJob::save( KConfig *cfg, const QString& grp ) { KConfigGroup groupcfg = cfg->group(grp); groupcfg.writeEntry( "Delay", d->delay ); groupcfg.writePathEntry( "Command", d->command ); groupcfg.writeEntry( "Loop", d->loop ); groupcfg.writeEntry( "OneInstance", d->oneInstance ); groupcfg.writeEntry( "State", (int)d->state ); } void KTimerJob::load( KConfig *cfg, const QString& grp ) { KConfigGroup groupcfg = cfg->group(grp); setDelay( groupcfg.readEntry( "Delay", 100 ) ); setCommand( groupcfg.readPathEntry( "Command", QString() ) ); setLoop( groupcfg.readEntry( "Loop", false ) ); setOneInstance( groupcfg.readEntry( "OneInstance", d->oneInstance ) ); setState( (States)groupcfg.readEntry( "State", (int)Stopped ) ); } // Format given seconds to hour:minute:seconds and return QString QString KTimerJob::formatTime( int seconds ) const { int h, m, s; secondsToHMS( seconds, &h, &m, &s ); return QString( QLatin1String( "%1:%2:%3" ) ).arg( h ).arg( m, 2, 10, QLatin1Char( '0' ) ).arg( s,2, 10, QLatin1Char( '0' ) ); } // calculate seconds from hour, minute and seconds, returns seconds int KTimerJob::timeToSeconds( int hours, int minutes, int seconds ) const { return hours * 3600 + minutes * 60 + seconds; } // calculates hours, minutes and seconds from given secs. void KTimerJob::secondsToHMS( int secs, int *hours, int *minutes, int *seconds ) const { int s = secs; (*hours) = s / 3600; s = s % 3600; (*minutes) = s / 60; (*seconds) = s % 60; } void *KTimerJob::user() { return d->user; } void KTimerJob::setUser( void *user ) { d->user = user; } unsigned KTimerJob::delay() const { return d->delay; } void KTimerJob::pause() { setState( Paused ); } void KTimerJob::stop() { setState( Stopped ); } void KTimerJob::start() { setState( Started ); } void KTimerJob::setDelay( int sec ) { setDelay( (unsigned)sec ); } void KTimerJob::setValue( int value ) { setValue( (unsigned)value ); } void KTimerJob::setDelay( unsigned sec ) { if( d->delay!=sec ) { d->delay = sec; if( d->state==Stopped ) setValue( sec ); emit delayChanged( this, sec ); emit changed( this ); } } QString KTimerJob::command() const { return d->command; } void KTimerJob::setCommand( const QString &cmd ) { if( d->command!=cmd ) { d->command = cmd; emit commandChanged( this, cmd ); emit changed( this ); } } bool KTimerJob::loop() const { return d->loop; } void KTimerJob::setLoop( bool loop ) { if( d->loop!=loop ) { d->loop = loop; emit loopChanged( this, loop ); emit changed( this ); } } bool KTimerJob::oneInstance() const { return d->oneInstance; } void KTimerJob::setOneInstance( bool one ) { if( d->oneInstance!=one ) { d->oneInstance = one; emit oneInstanceChanged( this, one ); emit changed( this ); } } unsigned KTimerJob::value() const { return d->value; } void KTimerJob::setValue( unsigned value ) { if( d->value!=value ) { d->value = value; emit valueChanged( this, value ); emit changed( this ); } } KTimerJob::States KTimerJob::state() const { return d->state; } void KTimerJob::setState( KTimerJob::States state ) { if( d->state!=state ) { if( state==Started ) d->timer->start( 1000 ); else d->timer->stop(); if( state==Stopped ) setValue( d->delay ); d->state = state; emit stateChanged( this, state ); emit changed( this ); } } void KTimerJob::timeout() { if( d->state==Started && d->value!=0 ) { setValue( d->value-1 ); if( d->value==0 ) { fire(); if( d->loop ) setValue( d->delay ); else stop(); } } } void KTimerJob::processExited(int, QProcess::ExitStatus status) { QProcess * proc = static_cast(sender()); const bool ok = status==0; const int i = d->processes.indexOf( proc); if (i != -1) delete d->processes.takeAt(i); if( !ok ) emit error( this ); emit finished( this, !ok ); } void KTimerJob::fire() { if( !d->oneInstance || d->processes.isEmpty() ) { QProcess *proc = new QProcess; d->processes.append( proc ); connect( proc, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(processExited(int,QProcess::ExitStatus)) ); if (!d->command.simplified ().isEmpty()) { proc->start(d->command); emit fired( this ); } if(proc->state() == QProcess::NotRunning) { const int i = d->processes.indexOf( proc); if (i != -1) delete d->processes.takeAt(i); emit error( this ); emit finished( this, true ); } } } #include "moc_ktimer.cpp"