You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
amarok/amarok/src/statusbar/statusBarBase.cpp

679 lines
18 KiB

/***************************************************************************
* Copyright (C) 2005 by Max Howell <max.howell@methylblue.com> *
* Copyright (C) 2005 by Ian Monroe <ian@monroe.nu> *
* *
* 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 Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#define DEBUG_PREFIX "StatusBar"
#include "amarok.h"
#include "debug.h"
#include "squeezedtextlabel.h"
#include "statusBarBase.h"
#include "threadmanager.h"
#include "enginecontroller.h"
#include <kio/job.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kstdguiitem.h>
#include <tqapplication.h>
#include <tqdatetime.h> //writeLogFile()
#include <tqfile.h> //writeLogFile()
#include <tqpushbutton.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqmessagebox.h>
#include <tqobjectlist.h> //polish()
#include <tqpainter.h>
#include <tqpalette.h>
#include <tqprogressbar.h>
#include <tqstyle.h> //class CloseButton
#include <tqtimer.h>
#include <tqtoolbutton.h>
#include <tqtooltip.h> //TQToolTip::palette()
#include <tqvbox.h>
//segregated classes
#include "popupMessage.h"
#include "progressBar.h"
namespace KDE {
namespace SingleShotPool
{
static void startTimer( int timeout, TQObject *receiver, const char *slot )
{
TQTimer *timer = static_cast<TQTimer*>( receiver->child( slot ) );
if( !timer ) {
timer = new TQTimer( receiver, slot );
receiver->connect( timer, TQT_SIGNAL(timeout()), slot );
}
timer->start( timeout, true );
}
static inline bool isActive( TQObject *parent, const char *slot )
{
TQTimer *timer = static_cast<TQTimer*>( parent->child( slot ) );
return timer && timer->isA( TQTIMER_OBJECT_NAME_STRING ) && timer->isActive();
}
}
//TODO allow for uncertain progress periods
StatusBar::StatusBar( TQWidget *parent, const char *name )
: TQWidget( parent, name )
, m_logCounter( -1 )
{
TQBoxLayout *mainlayout = new TQHBoxLayout( this, 2, /*spacing*/5 );
//we need extra spacing due to the way we paint the surrounding boxes
TQBoxLayout *layout = new TQHBoxLayout( mainlayout, /*spacing*/5 );
TQHBox *statusBarTextBox = new TQHBox( this, "statusBarTextBox" );
m_mainTextLabel = new KDE::SqueezedTextLabel( statusBarTextBox, "mainTextLabel" );
TQToolButton *shortLongButton = new TQToolButton( statusBarTextBox, "shortLongButton" );
shortLongButton->hide();
TQHBox *mainProgressBarBox = new TQHBox( this, "progressBox" );
TQToolButton *b1 = new TQToolButton( mainProgressBarBox, "cancelButton" );
m_mainProgressBar = new TQProgressBar( mainProgressBarBox, "mainProgressBar" );
TQToolButton *b2 = new TQToolButton( mainProgressBarBox, "showAllProgressDetails" );
mainProgressBarBox->setSpacing( 2 );
mainProgressBarBox->hide();
layout->add( statusBarTextBox );
layout->add( mainProgressBarBox );
layout->setStretchFactor( statusBarTextBox, 3 );
layout->setStretchFactor( mainProgressBarBox, 1 );
m_otherWidgetLayout = new TQHBoxLayout( mainlayout, /*spacing*/5 );
mainlayout->setStretchFactor( layout, 6 );
mainlayout->setStretchFactor( m_otherWidgetLayout, 4 );
shortLongButton->setIconSet( SmallIconSet( "edit_add" ) );
TQToolTip::add( shortLongButton, i18n( "Show details" ) );
connect( shortLongButton, TQT_SIGNAL(clicked()), TQT_SLOT(showShortLongDetails()) );
b1->setIconSet( SmallIconSet( "cancel" ) );
b2->setIconSet( SmallIconSet( "2uparrow") );
b2->setToggleButton( true );
TQToolTip::add( b1, i18n( "Abort all background-operations" ) );
TQToolTip::add( b2, i18n( "Show progress detail" ) );
connect( b1, TQT_SIGNAL(clicked()), TQT_SLOT(abortAllProgressOperations()) );
connect( b2, TQT_SIGNAL(toggled( bool )), TQT_SLOT(toggleProgressWindow( bool )) );
m_popupProgress = new OverlayWidget( this, mainProgressBarBox, "popupProgress" );
m_popupProgress->setMargin( 1 );
m_popupProgress->setFrameStyle( TQFrame::Panel | TQFrame::Raised );
m_popupProgress->setFrameShape( TQFrame::StyledPanel );
m_popupProgress->setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum );
(new TQGridLayout( m_popupProgress, 1 /*rows*/, 3 /*cols*/, 6, 3 ))->setAutoAdd( true );
}
void
StatusBar::addWidget( TQWidget *widget )
{
m_otherWidgetLayout->add( widget );
}
/// reimplemented functions
void
StatusBar::polish()
{
TQWidget::polish();
int h = 0;
TQObjectList *list = queryList( TQWIDGET_OBJECT_NAME_STRING, 0, false, false );
for( TQObject * o = list->first(); o; o = list->next() ) {
int _h = TQT_TQWIDGET( o ) ->minimumSizeHint().height();
if ( _h > h )
h = _h;
// debug() << o->className() << ", " << o->name() << ": " << _h << ": " << TQT_TQWIDGET(o)->minimumHeight() << endl;
if ( o->inherits( TQLABEL_OBJECT_NAME_STRING ) )
static_cast<TQLabel*>(TQT_TQWIDGET(o))->setIndent( 4 );
}
h -= 4; // it's too big usually
for ( TQObject * o = list->first(); o; o = list->next() )
TQT_TQWIDGET(o)->setFixedHeight( h );
delete list;
}
void
StatusBar::paintEvent( TQPaintEvent* )
{
TQObjectList *list = queryList( TQWIDGET_OBJECT_NAME_STRING, 0, false, false );
TQPainter p( this );
for( TQObject * o = list->first(); o; o = list->next() ) {
TQWidget *w = TQT_TQWIDGET( o );
if ( !w->isVisible() )
continue;
style().tqdrawPrimitive(
TQStyle::PE_StatusBarSection,
&p,
TQRect( w->x() - 1, w->y() - 1, w->width() + 2, w->height() + 2 ),
colorGroup(),
TQStyle::Style_Default,
TQStyleOption( w ) );
}
delete list;
}
bool
StatusBar::event( TQEvent *e )
{
if ( e->type() == TQEvent::LayoutHint )
update();
return TQWidget::event( e );
}
/// Messaging system
void
StatusBar::setMainText( const TQString &text )
{
SHOULD_BE_GUI
m_mainText = text;
// it may not be appropriate for us to set the mainText yet
resetMainText();
}
void
StatusBar::shortMessage( const TQString &text, bool longShort )
{
SHOULD_BE_GUI
m_mainTextLabel->setText( text );
m_mainTextLabel->setPalette( TQToolTip::palette() );
SingleShotPool::startTimer( longShort ? 8000 : 5000, TQT_TQOBJECT(this), TQT_SLOT(resetMainText()) );
writeLogFile( text );
}
void
StatusBar::resetMainText()
{
// if( sender() )
// debug() << sender()->name() << endl;
// don't reset if we are showing a shortMessage
if( SingleShotPool::isActive( TQT_TQOBJECT(this), TQT_SLOT(resetMainText()) ) )
return;
m_mainTextLabel->unsetPalette();
shortLongButton()->hide();
if( allDone() )
m_mainTextLabel->setText( m_mainText );
else {
ProgressBar *bar = 0;
uint count = 0;
foreachType( ProgressMap, m_progressMap )
if( !(*it)->m_done ) {
bar = *it;
count++;
}
if( count == 1 )
m_mainTextLabel->setText( bar->description() + i18n("...") );
else
m_mainTextLabel->setText( i18n("Multiple background-tasks running") );
}
}
void
StatusBar::shortLongMessage( const TQString &_short, const TQString &_long, int type )
{
SHOULD_BE_GUI
m_shortLongType = type;
if( !_short.isEmpty() )
shortMessage( _short, true );
if ( !_long.isEmpty() ) {
m_shortLongText = _long;
shortLongButton()->show();
writeLogFile( _long );
}
}
void
StatusBar::longMessage( const TQString &text, int type )
{
SHOULD_BE_GUI
if( text.isEmpty() )
return;
PopupMessage *message;
message = new PopupMessage( this, m_mainTextLabel );
connect( message, TQT_SIGNAL(destroyed(TQObject *)), this, TQT_SLOT(popupDeleted(TQObject *)) );
message->setText( text );
TQString image;
switch( type )
{
case Information:
case Question:
image = TDEGlobal::iconLoader()->iconPath( "messagebox_info", -KIcon::SizeHuge );
break;
case Sorry:
case Warning:
image = TDEGlobal::iconLoader()->iconPath( "messagebox_warning", -KIcon::SizeHuge );
break;
case Error:
image = TDEGlobal::iconLoader()->iconPath( "messagebox_critical", -KIcon::SizeHuge );
// don't hide error messages.
// message->setTimeout( 0 );
break;
}
if( !image.isEmpty() )
message->setImage( image );
if ( !m_messageQueue.isEmpty() )
message->stackUnder( m_messageQueue.last() );
message->display();
raise();
m_messageQueue += message;
writeLogFile( text );
}
void
StatusBar::popupDeleted( TQObject *obj )
{
m_messageQueue.remove( TQT_TQWIDGET( obj ) );
}
void
StatusBar::longMessageThreadSafe( const TQString &text, int /*type*/ )
{
TQCustomEvent * e = new TQCustomEvent( 1000 );
e->setData( new TQString( text ) );
TQApplication::postEvent( this, e );
}
void
StatusBar::customEvent( TQCustomEvent *e )
{
if(e->type() == 1000 ){
TQString *s = static_cast<TQString*>( e->data() );
longMessage( *s );
delete s;
}else if(e->type() == 2000 ){
EngineController::instance()->unplayableNotification();
}
}
/// application wide progress monitor
inline bool
StatusBar::allDone()
{
for( ProgressMap::Iterator it = m_progressMap.begin(), end = m_progressMap.end(); it != end; ++it )
if( (*it)->m_done == false )
return false;
return true;
}
ProgressBar&
StatusBar::newProgressOperation( TQObject *owner )
{
SHOULD_BE_GUI
if ( m_progressMap.contains( owner ) )
return *m_progressMap[owner];
if( allDone() )
// if we're allDone then we need to remove the old progressBars before
// we start anything new or the total progress will not be accurate
pruneProgressBars();
else
toggleProgressWindowButton()->show();
TQLabel *label = new TQLabel( m_popupProgress );
m_progressMap.insert( owner, new ProgressBar( m_popupProgress, label ) );
m_popupProgress->reposition();
connect( owner, TQT_SIGNAL(destroyed( TQObject* )), TQT_SLOT(endProgressOperation( TQObject* )) );
// so we can show the correct progress information
// after the ProgressBar is setup
SingleShotPool::startTimer( 0, TQT_TQOBJECT(this), TQT_SLOT(updateProgressAppearance()) );
progressBox()->show();
cancelButton()->setEnabled( true );
return *m_progressMap[ owner ];
}
ProgressBar&
StatusBar::newProgressOperation( KIO::Job *job )
{
SHOULD_BE_GUI
ProgressBar & bar = newProgressOperation( static_cast<TQObject*>( job ) );
bar.setTotalSteps( 100 );
if(!allDone())
toggleProgressWindowButton()->show();
connect( job, TQT_SIGNAL(result( KIO::Job* )), TQT_SLOT(endProgressOperation()) );
//TODO connect( job, TQT_SIGNAL(infoMessage( KIO::Job *job, const TQString& )), TQT_SLOT() );
connect( job, TQT_SIGNAL(percent( KIO::Job*, unsigned long )), TQT_SLOT(setProgress( KIO::Job*, unsigned long )) );
return bar;
}
void
StatusBar::endProgressOperation()
{
TQObject *owner = TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>( sender() )); //HACK deconsting it
KIO::Job *job = dynamic_cast<KIO::Job*>( owner );
//FIXME doesn't seem to work for KIO::DeleteJob, it has it's own error handler and returns no error too
// if you try to delete http urls for instance <- KDE SUCKS!
if( job && job->error() )
shortLongMessage( TQString(), job->errorString(), Error );
endProgressOperation( owner );
}
void
StatusBar::endProgressOperation( TQObject *owner )
{
//the owner of this progress operation has been deleted
//we need to stop listening for progress from it
//NOTE we don't delete it yet, as this upsets some
//things, we just call setDone().
if ( !m_progressMap.contains( owner ) )
{
SingleShotPool::startTimer( 2000, TQT_TQOBJECT(this), TQT_SLOT(hideMainProgressBar()) );
return ;
}
m_progressMap[owner]->setDone();
if( allDone() && !m_popupProgress->isShown() ) {
cancelButton()->setEnabled( false );
SingleShotPool::startTimer( 2000, TQT_TQOBJECT(this), TQT_SLOT(hideMainProgressBar()) );
}
updateTotalProgress();
}
void
StatusBar::abortAllProgressOperations() //slot
{
for( ProgressMap::Iterator it = m_progressMap.begin(), end = m_progressMap.end(); it != end; ++it )
(*it)->m_abort->animateClick();
m_mainTextLabel->setText( i18n("Aborting all jobs...") );
cancelButton()->setEnabled( false );
}
void
StatusBar::toggleProgressWindow( bool show ) //slot
{
m_popupProgress->reposition(); //FIXME shouldn't be needed, adding bars doesn't seem to do this
m_popupProgress->setShown( show );
if( !show )
SingleShotPool::startTimer( 2000, TQT_TQOBJECT(this), TQT_SLOT(hideMainProgressBar()) );
}
void
StatusBar::showShortLongDetails()
{
if( !m_shortLongText.isEmpty() )
longMessage( m_shortLongText, m_shortLongType );
m_shortLongType = Information;
m_shortLongText = TQString();
shortLongButton()->hide();
}
void
StatusBar::showMainProgressBar()
{
if( !allDone() )
progressBox()->show();
}
void
StatusBar::hideMainProgressBar()
{
if( allDone() && !m_popupProgress->isShown() )
{
pruneProgressBars();
resetMainText();
m_mainProgressBar->setProgress( 0 );
progressBox()->hide();
}
}
void
StatusBar::setProgress( int steps )
{
setProgress( TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender())), steps );
}
void
StatusBar::setProgress( KIO::Job *job, unsigned long percent )
{
setProgress( static_cast<TQObject*>( job ), percent );
}
void
StatusBar::setProgress( const TQObject *owner, int steps )
{
if ( !m_progressMap.contains( owner ) )
return ;
m_progressMap[ owner ] ->setProgress( steps );
updateTotalProgress();
}
void
StatusBar::incrementProgressTotalSteps( const TQObject *owner, int inc )
{
if ( !m_progressMap.contains( owner ) )
return ;
m_progressMap[ owner ] ->setTotalSteps( m_progressMap[ owner ] ->totalSteps() + inc );
updateTotalProgress();
}
void
StatusBar::setProgressStatus( const TQObject *owner, const TQString &text )
{
if ( !m_progressMap.contains( owner ) )
return ;
m_progressMap[owner]->setStatus( text );
}
void StatusBar::incrementProgress()
{
incrementProgress( TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()) ));
}
void
StatusBar::incrementProgress( const TQObject *owner )
{
if ( !m_progressMap.contains( owner ) )
return;
m_progressMap[owner]->setProgress( m_progressMap[ owner ] ->progress() + 1 );
updateTotalProgress();
}
void
StatusBar::updateTotalProgress()
{
uint totalSteps = 0;
uint progress = 0;
foreachType( ProgressMap, m_progressMap ) {
totalSteps += (*it)->totalSteps();
progress += (*it)->progress();
}
if( totalSteps == 0 && progress == 0 )
return;
m_mainProgressBar->setTotalSteps( totalSteps );
m_mainProgressBar->setProgress( progress );
pruneProgressBars();
}
void
StatusBar::updateProgressAppearance()
{
toggleProgressWindowButton()->setShown( m_progressMap.count() > 1 );
resetMainText();
updateTotalProgress();
}
void
StatusBar::pruneProgressBars()
{
ProgressMap::Iterator it = m_progressMap.begin();
const ProgressMap::Iterator end = m_progressMap.end();
int count = 0;
bool removedBar = false;
while( it != end )
if( (*it)->m_done == true ) {
delete (*it)->m_label;
delete (*it)->m_abort;
delete (*it);
ProgressMap::Iterator jt = it;
++it;
m_progressMap.erase( jt );
removedBar = true;
}
else {
++it;
++count;
}
if(count==1 && removedBar) //if its gone from 2 or more bars to one bar...
{
resetMainText();
toggleProgressWindowButton()->hide();
m_popupProgress->setShown(false);
}
}
/// Method which writes to a rotating log file.
void
StatusBar::writeLogFile( const TQString &text )
{
if( text.isEmpty() ) return;
const int counter = 4; // number of logs to keep
const uint maxSize = 30000; // approximately 1000 lines per log file
int c = counter;
TQString logBase = Amarok::saveLocation() + "statusbar.log.";
TQFile file;
if( m_logCounter < 0 ) //find which log to write to
{
for( ; c > 0; c-- )
{
TQString log = logBase + TQString::number(c);
file.setName( log );
if( TQFile::exists( log ) && file.size() <= maxSize )
break;
}
if( c == 0 ) file.setName( logBase + '0' );
m_logCounter = c;
}
else
{
file.setName( logBase + TQString::number(m_logCounter) );
}
if( file.size() > maxSize )
{
m_logCounter++;
m_logCounter = m_logCounter % counter;
file.setName( logBase + TQString::number(m_logCounter) );
// if we have overflown the log, then we want to overwrite the previous content
if( !file.open( IO_WriteOnly ) ) return;
}
else if( !file.open( IO_WriteOnly|IO_Append ) ) return;
TQTextStream stream( &file );
stream.setEncoding( TQTextStream::UnicodeUTF8 );
stream << "[" << TDEGlobal::locale()->formatDateTime( TQDateTime::currentDateTime() ) << "] " << text << endl;
}
} //namespace KDE
#include "statusBarBase.moc"