korganizer

multiagendaview.cpp

00001 /*
00002     Copyright (c) 2007 Volker Krause <vkrause@kde.org>
00003 
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017 */
00018 
00019 #include "multiagendaview.h"
00020 
00021 #include "koagendaview.h"
00022 #include "koagenda.h"
00023 #include "koprefs.h"
00024 #include "timelabels.h"
00025 
00026 #include <libkcal/calendarresources.h>
00027 
00028 #include <kglobalsettings.h>
00029 
00030 #include <qlayout.h>
00031 #include <qvbox.h>
00032 #include <qobjectlist.h>
00033 
00034 #define FOREACH_VIEW(av) \
00035 for(QValueList<KOAgendaView*>::ConstIterator it = mAgendaViews.constBegin(); \
00036   it != mAgendaViews.constEnd();) \
00037   for(KOAgendaView* av = (it != mAgendaViews.constEnd() ? (*it) : 0); \
00038       it != mAgendaViews.constEnd(); ++it, av = (*it)  )
00039 
00040 using namespace KOrg;
00041 
00042 MultiAgendaView::MultiAgendaView(Calendar * cal, QWidget * parent, const char *name ) :
00043     AgendaView( cal, parent, name ),
00044     mLastMovedSplitter( 0 ),
00045     mUpdateOnShow( false ),
00046     mPendingChanges( true )
00047 {
00048   QBoxLayout *topLevelLayout = new QHBoxLayout( this );
00049 
00050   QFontMetrics fm( font() );
00051   int topLabelHeight = 2 * fm.height() + fm.lineSpacing();
00052 
00053   QVBox *topSideBox = new QVBox( this );
00054   QWidget *topSideSpacer = new QWidget( topSideBox );
00055   topSideSpacer->setFixedHeight( topLabelHeight );
00056   mLeftSplitter = new QSplitter( Qt::Vertical, topSideBox );
00057   mLeftSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
00058   QLabel *label = new QLabel( i18n("All Day"), mLeftSplitter );
00059   label->setAlignment( Qt::AlignRight | Qt::AlignVCenter | Qt::WordBreak );
00060   QVBox *sideBox = new QVBox( mLeftSplitter );
00061   EventIndicator *eiSpacer = new EventIndicator( EventIndicator::Top, sideBox );
00062   eiSpacer->changeColumns( 0 );
00063   mTimeLabels = new TimeLabels( 24, sideBox );
00064   eiSpacer = new EventIndicator( EventIndicator::Bottom, sideBox );
00065   eiSpacer->changeColumns( 0 );
00066   mLeftBottomSpacer = new QWidget( topSideBox );
00067   topLevelLayout->addWidget( topSideBox );
00068 
00069   mScrollView = new QScrollView( this );
00070   mScrollView->setResizePolicy( QScrollView::Manual );
00071   mScrollView->setVScrollBarMode( QScrollView::AlwaysOff );
00072   mScrollView->setFrameShape( QFrame::NoFrame );
00073   topLevelLayout->addWidget( mScrollView, 100 );
00074   mTopBox = new QHBox( mScrollView->viewport() );
00075   mScrollView->addChild( mTopBox );
00076 
00077   topSideBox = new QVBox( this );
00078   topSideSpacer = new QWidget( topSideBox );
00079   topSideSpacer->setFixedHeight( topLabelHeight );
00080   mRightSplitter = new QSplitter( Qt::Vertical, topSideBox );
00081   mRightSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
00082   new QWidget( mRightSplitter );
00083   sideBox = new QVBox( mRightSplitter );
00084   eiSpacer = new EventIndicator( EventIndicator::Top, sideBox );
00085   eiSpacer->setFixedHeight( eiSpacer->minimumHeight() );
00086   eiSpacer->changeColumns( 0 );
00087   mScrollBar = new QScrollBar( Qt::Vertical, sideBox );
00088   eiSpacer = new EventIndicator( EventIndicator::Bottom, sideBox );
00089   eiSpacer->setFixedHeight( eiSpacer->minimumHeight() );
00090   eiSpacer->changeColumns( 0 );
00091   mRightBottomSpacer = new QWidget( topSideBox );
00092   topLevelLayout->addWidget( topSideBox );
00093 
00094   recreateViews();
00095 }
00096 
00097 void MultiAgendaView::recreateViews()
00098 {
00099   if ( !mPendingChanges )
00100     return;
00101   mPendingChanges = false;
00102 
00103   deleteViews();
00104 
00105   CalendarResources *calres = dynamic_cast<CalendarResources*>( calendar() );
00106   if ( !calres ) {
00107     // fallback to single-agenda
00108     KOAgendaView* av = new KOAgendaView( calendar(), mTopBox );
00109     mAgendaViews.append( av );
00110     mAgendaWidgets.append( av );
00111     av->show();
00112   } else {
00113     CalendarResourceManager *manager = calres->resourceManager();
00114     for ( CalendarResourceManager::ActiveIterator it = manager->activeBegin(); it != manager->activeEnd(); ++it ) {
00115       if ( (*it)->canHaveSubresources() ) {
00116         QStringList subResources = (*it)->subresources();
00117         for ( QStringList::ConstIterator subit = subResources.constBegin(); subit != subResources.constEnd(); ++subit ) {
00118           QString type = (*it)->subresourceType( *subit );
00119           if ( !(*it)->subresourceActive( *subit ) || (!type.isEmpty() && type != "event") )
00120             continue;
00121           addView( (*it)->labelForSubresource( *subit ), *it, *subit );
00122         }
00123       } else {
00124         addView( (*it)->resourceName(), *it );
00125       }
00126     }
00127   }
00128 
00129   // no resources activated, so stop here to avoid crashing somewhere down the line, TODO: show a nice message instead
00130   if ( mAgendaViews.isEmpty() )
00131     return;
00132 
00133   setupViews();
00134   QTimer::singleShot( 0, this, SLOT(slotResizeScrollView()) );
00135   mTimeLabels->updateConfig();
00136 
00137   QScrollBar *scrollBar = mAgendaViews.first()->agenda()->verticalScrollBar();
00138   mScrollBar->setMinValue( scrollBar->minValue() );
00139   mScrollBar->setMaxValue( scrollBar->maxValue() );
00140   mScrollBar->setLineStep( scrollBar->lineStep() );
00141   mScrollBar->setPageStep( scrollBar->pageStep() );
00142   mScrollBar->setValue( scrollBar->value() );
00143   connect( mTimeLabels->verticalScrollBar(), SIGNAL(valueChanged(int)),
00144            mScrollBar, SLOT(setValue(int)) );
00145   connect( mScrollBar, SIGNAL(valueChanged(int)),
00146            mTimeLabels, SLOT(positionChanged(int)) );
00147 
00148   installSplitterEventFilter( mLeftSplitter );
00149   installSplitterEventFilter( mRightSplitter );
00150 
00151   QValueList<int> sizes = KOGlobals::self()->config()->readIntListEntry("Separator AgendaView");
00152   if (sizes.count() != 2) {
00153     sizes = mLeftSplitter->sizes();
00154   }
00155   FOREACH_VIEW( agenda ) {
00156     agenda->splitter()->setSizes( sizes );
00157   }
00158   mLeftSplitter->setSizes( sizes );
00159   mRightSplitter->setSizes( sizes );
00160 
00161   mTimeLabels->positionChanged();
00162 }
00163 
00164 void MultiAgendaView::deleteViews()
00165 {
00166   for ( QValueList<QWidget*>::ConstIterator it = mAgendaWidgets.constBegin();
00167         it != mAgendaWidgets.constEnd(); ++it ) {
00168     delete *it;
00169   }
00170   mAgendaViews.clear();
00171   mAgendaWidgets.clear();
00172   mLastMovedSplitter = 0;
00173 }
00174 
00175 void MultiAgendaView::setupViews()
00176 {
00177   FOREACH_VIEW( agenda ) {
00178     connect( agenda, SIGNAL( newEventSignal() ),
00179              SIGNAL( newEventSignal() ) );
00180     connect( agenda, SIGNAL( editIncidenceSignal( Incidence * ) ),
00181              SIGNAL( editIncidenceSignal( Incidence * ) ) );
00182     connect( agenda, SIGNAL( showIncidenceSignal( Incidence * ) ),
00183              SIGNAL( showIncidenceSignal( Incidence * ) ) );
00184     connect( agenda, SIGNAL( deleteIncidenceSignal( Incidence * ) ),
00185              SIGNAL( deleteIncidenceSignal( Incidence * ) ) );
00186     connect( agenda, SIGNAL( startMultiModify( const QString & ) ),
00187              SIGNAL( startMultiModify( const QString & ) ) );
00188     connect( agenda, SIGNAL( endMultiModify() ),
00189              SIGNAL( endMultiModify() ) );
00190 
00191     connect( agenda, SIGNAL( incidenceSelected( Incidence *,const QDate & ) ),
00192              SIGNAL( incidenceSelected( Incidence *,const QDate & ) ) );
00193 
00194     connect( agenda, SIGNAL(cutIncidenceSignal(Incidence*)),
00195              SIGNAL(cutIncidenceSignal(Incidence*)) );
00196     connect( agenda, SIGNAL(copyIncidenceSignal(Incidence*)),
00197              SIGNAL(copyIncidenceSignal(Incidence*)) );
00198     connect( agenda, SIGNAL(pasteIncidenceSignal()),
00199              SIGNAL(pasteIncidenceSignal()) );
00200     connect( agenda, SIGNAL(toggleAlarmSignal(Incidence*)),
00201              SIGNAL(toggleAlarmSignal(Incidence*)) );
00202     connect( agenda, SIGNAL(dissociateOccurrenceSignal(Incidence*, const QDate&)),
00203              SIGNAL(dissociateOccurrenceSignal(Incidence*, const QDate&)) );
00204     connect( agenda, SIGNAL(dissociateFutureOccurrenceSignal(Incidence*, const QDate&)),
00205              SIGNAL(dissociateFutureOccurrenceSignal(Incidence*, const QDate&)) );
00206 
00207     connect( agenda, SIGNAL(newEventSignal(const QDate&)),
00208              SIGNAL(newEventSignal(const QDate&)) );
00209     connect( agenda, SIGNAL(newEventSignal(const QDateTime&)),
00210              SIGNAL(newEventSignal(const QDateTime&)) );
00211     connect( agenda, SIGNAL(newEventSignal(const QDateTime&, const QDateTime&)),
00212              SIGNAL(newEventSignal(const QDateTime&, const QDateTime&)) );
00213     connect( agenda, SIGNAL(newTodoSignal(const QDate&)),
00214              SIGNAL(newTodoSignal(const QDate&)) );
00215 
00216     connect( agenda, SIGNAL(incidenceSelected(Incidence*, const QDate &)),
00217              SLOT(slotSelectionChanged()) );
00218 
00219     connect( agenda, SIGNAL(timeSpanSelectionChanged()),
00220              SLOT(slotClearTimeSpanSelection()) );
00221 
00222     disconnect( agenda->agenda(), SIGNAL(zoomView(const int,const QPoint&,const Qt::Orientation)), agenda, 0 );
00223     connect( agenda->agenda(), SIGNAL(zoomView(const int,const QPoint&,const Qt::Orientation)),
00224              SLOT(zoomView(const int,const QPoint&,const Qt::Orientation)) );
00225   }
00226 
00227   FOREACH_VIEW( agenda ) {
00228     agenda->readSettings();
00229   }
00230 
00231   int minWidth = 0;
00232   for ( QValueList<QWidget*>::ConstIterator it = mAgendaWidgets.constBegin(); it != mAgendaWidgets.constEnd(); ++it )
00233     minWidth = QMAX( minWidth, (*it)->minimumSizeHint().width() );
00234   for ( QValueList<QWidget*>::ConstIterator it = mAgendaWidgets.constBegin(); it != mAgendaWidgets.constEnd(); ++it )
00235     (*it)->setMinimumWidth( minWidth );
00236 }
00237 
00238 MultiAgendaView::~ MultiAgendaView()
00239 {
00240 }
00241 
00242 Incidence::List MultiAgendaView::selectedIncidences()
00243 {
00244   Incidence::List list;
00245   FOREACH_VIEW(agendaView) {
00246     list += agendaView->selectedIncidences();
00247   }
00248   return list;
00249 }
00250 
00251 DateList MultiAgendaView::selectedDates()
00252 {
00253   DateList list;
00254   FOREACH_VIEW(agendaView) {
00255     list += agendaView->selectedDates();
00256   }
00257   return list;
00258 }
00259 
00260 int MultiAgendaView::currentDateCount()
00261 {
00262   FOREACH_VIEW( agendaView )
00263     return agendaView->currentDateCount();
00264   return 0;
00265 }
00266 
00267 void MultiAgendaView::showDates(const QDate & start, const QDate & end)
00268 {
00269   mStartDate = start;
00270   mEndDate = end;
00271   recreateViews();
00272   FOREACH_VIEW( agendaView )
00273     agendaView->showDates( start, end );
00274 }
00275 
00276 void MultiAgendaView::showIncidences(const Incidence::List & incidenceList, const QDate &date)
00277 {
00278   FOREACH_VIEW( agendaView )
00279     agendaView->showIncidences( incidenceList, date );
00280 }
00281 
00282 void MultiAgendaView::updateView()
00283 {
00284   recreateViews();
00285   FOREACH_VIEW( agendaView )
00286     agendaView->updateView();
00287 }
00288 
00289 void MultiAgendaView::changeIncidenceDisplay(Incidence * incidence, int mode)
00290 {
00291   FOREACH_VIEW( agendaView )
00292     agendaView->changeIncidenceDisplay( incidence, mode );
00293 }
00294 
00295 int MultiAgendaView::maxDatesHint()
00296 {
00297   FOREACH_VIEW( agendaView )
00298     return agendaView->maxDatesHint();
00299   return 0;
00300 }
00301 
00302 void MultiAgendaView::slotSelectionChanged()
00303 {
00304   FOREACH_VIEW( agenda ) {
00305     if ( agenda != sender() )
00306       agenda->clearSelection();
00307   }
00308 }
00309 
00310 bool MultiAgendaView::eventDurationHint(QDateTime & startDt, QDateTime & endDt, bool & allDay)
00311 {
00312   FOREACH_VIEW( agenda ) {
00313     bool valid = agenda->eventDurationHint( startDt, endDt, allDay );
00314     if ( valid )
00315       return true;
00316   }
00317   return false;
00318 }
00319 
00320 void MultiAgendaView::slotClearTimeSpanSelection()
00321 {
00322   FOREACH_VIEW( agenda ) {
00323     if ( agenda != sender() )
00324       agenda->clearTimeSpanSelection();
00325   }
00326 }
00327 
00328 void MultiAgendaView::setTypeAheadReceiver(QObject * o)
00329 {
00330   FOREACH_VIEW( agenda )
00331     agenda->setTypeAheadReceiver( o );
00332 }
00333 
00334 void MultiAgendaView::finishTypeAhead()
00335 {
00336   FOREACH_VIEW( agenda )
00337     agenda->finishTypeAhead();
00338 }
00339 
00340 void MultiAgendaView::addView( const QString &label, KCal::ResourceCalendar * res, const QString & subRes )
00341 {
00342   QVBox *box = new QVBox( mTopBox );
00343   QLabel *l = new QLabel( label, box );
00344   l->setAlignment( AlignVCenter | AlignHCenter );
00345   KOAgendaView* av = new KOAgendaView( calendar(), box, 0, true );
00346   av->setResource( res, subRes );
00347   av->setIncidenceChanger( mChanger );
00348   av->agenda()->setVScrollBarMode( QScrollView::AlwaysOff );
00349   mAgendaViews.append( av );
00350   mAgendaWidgets.append( box );
00351   box->show();
00352   mTimeLabels->setAgenda( av->agenda() );
00353 
00354   connect( av->agenda()->verticalScrollBar(), SIGNAL(valueChanged(int)),
00355            mTimeLabels, SLOT(positionChanged(int)) );
00356   connect( mTimeLabels->verticalScrollBar(), SIGNAL(valueChanged(int)),
00357            av, SLOT(setContentsPos(int)) );
00358 
00359   installSplitterEventFilter( av->splitter() );
00360 }
00361 
00362 void MultiAgendaView::resizeEvent(QResizeEvent * ev)
00363 {
00364   resizeScrollView( ev->size() );
00365   AgendaView::resizeEvent( ev );
00366 }
00367 
00368 void MultiAgendaView::resizeScrollView(const QSize & size)
00369 {
00370   const int widgetWidth = size.width() - mTimeLabels->width() - mScrollBar->width();
00371   int width = QMAX( mTopBox->sizeHint().width(), widgetWidth );
00372   int height = size.height();
00373   if ( width > widgetWidth ) {
00374     const int sbHeight = mScrollView->horizontalScrollBar()->height();
00375     height -= sbHeight;
00376     mLeftBottomSpacer->setFixedHeight( sbHeight );
00377     mRightBottomSpacer->setFixedHeight( sbHeight );
00378   } else {
00379     mLeftBottomSpacer->setFixedHeight( 0 );
00380     mRightBottomSpacer->setFixedHeight( 0 );
00381   }
00382   mScrollView->resizeContents( width, height );
00383   mTopBox->resize( width, height );
00384 }
00385 
00386 void MultiAgendaView::setIncidenceChanger(IncidenceChangerBase * changer)
00387 {
00388   AgendaView::setIncidenceChanger( changer );
00389   FOREACH_VIEW( agenda )
00390     agenda->setIncidenceChanger( changer );
00391 }
00392 
00393 void MultiAgendaView::updateConfig()
00394 {
00395   AgendaView::updateConfig();
00396   mTimeLabels->updateConfig();
00397   FOREACH_VIEW( agenda )
00398     agenda->updateConfig();
00399 }
00400 
00401 // KDE4: not needed anymore, QSplitter has a moved signal there
00402 bool MultiAgendaView::eventFilter(QObject * obj, QEvent * event)
00403 {
00404   if ( obj->className() == QCString("QSplitterHandle") ) {
00405     if ( (event->type() == QEvent::MouseMove && KGlobalSettings::opaqueResize())
00406            || event->type() == QEvent::MouseButtonRelease ) {
00407       FOREACH_VIEW( agenda ) {
00408         if ( agenda->splitter() == obj->parent() )
00409           mLastMovedSplitter = agenda->splitter();
00410       }
00411       if ( mLeftSplitter == obj->parent() )
00412         mLastMovedSplitter = mLeftSplitter;
00413       else if ( mRightSplitter == obj->parent() )
00414         mLastMovedSplitter = mRightSplitter;
00415       QTimer::singleShot( 0, this, SLOT(resizeSplitters()) );
00416     }
00417   }
00418   return AgendaView::eventFilter( obj, event );
00419 }
00420 
00421 void MultiAgendaView::resizeSplitters()
00422 {
00423   if ( !mLastMovedSplitter )
00424     mLastMovedSplitter = mAgendaViews.first()->splitter();
00425   FOREACH_VIEW( agenda ) {
00426     if ( agenda->splitter() == mLastMovedSplitter )
00427       continue;
00428     agenda->splitter()->setSizes( mLastMovedSplitter->sizes() );
00429   }
00430   if ( mLastMovedSplitter != mLeftSplitter )
00431     mLeftSplitter->setSizes( mLastMovedSplitter->sizes() );
00432   if ( mLastMovedSplitter != mRightSplitter )
00433     mRightSplitter->setSizes( mLastMovedSplitter->sizes() );
00434 }
00435 
00436 void MultiAgendaView::zoomView( const int delta, const QPoint & pos, const Qt::Orientation ori )
00437 {
00438   if ( ori == Qt::Vertical ) {
00439     if ( delta > 0 ) {
00440       if ( KOPrefs::instance()->mHourSize > 4 )
00441         KOPrefs::instance()->mHourSize--;
00442     } else {
00443       KOPrefs::instance()->mHourSize++;
00444     }
00445   }
00446 
00447   FOREACH_VIEW( agenda )
00448     agenda->zoomView( delta, pos, ori );
00449 
00450   mTimeLabels->updateConfig();
00451   mTimeLabels->positionChanged();
00452   mTimeLabels->repaint();
00453 }
00454 
00455 // KDE4: not needed, use existing QSplitter signals instead
00456 void MultiAgendaView::installSplitterEventFilter(QSplitter * splitter)
00457 {
00458   QObjectList *objlist = splitter->queryList( "QSplitterHandle" );
00459   // HACK: when not being visible, the splitter handle is sometimes not found
00460   // for unknown reasons, so trigger an update when we are shown again
00461   if ( objlist->count() == 0 && !isVisible() )
00462     mUpdateOnShow = true;
00463   QObjectListIt it( *objlist );
00464   QObject *obj;
00465   while ( (obj = it.current()) != 0 ) {
00466     obj->removeEventFilter( this );
00467     obj->installEventFilter( this );
00468     ++it;
00469   }
00470   delete objlist;
00471 }
00472 
00473 void MultiAgendaView::slotResizeScrollView()
00474 {
00475   resizeScrollView( size() );
00476 }
00477 
00478 void MultiAgendaView::show()
00479 {
00480   AgendaView::show();
00481   if ( mUpdateOnShow ) {
00482     mUpdateOnShow = false;
00483     mPendingChanges = true; // force a full view recreation
00484     showDates( mStartDate, mEndDate );
00485   }
00486 }
00487 
00488 void MultiAgendaView::resourcesChanged()
00489 {
00490   mPendingChanges = true;
00491   FOREACH_VIEW( agenda )
00492     agenda->resourcesChanged();
00493 }
00494 
00495 #include "multiagendaview.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys