multiagendaview.cpp 24.6 KB
Newer Older
1
2
/*
  Copyright (c) 2007 Volker Krause <vkrause@kde.org>
3
4
  Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
  Author: Sergio Martins <sergio.martins@kdab.com>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

  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 "multiagendaview.h"
21
#include "configdialoginterface.h"
Allen Winter's avatar
Allen Winter committed
22
#include "prefs.h"
23

Allen Winter's avatar
Allen Winter committed
24
25
26
#include "agenda/agenda.h"
#include "agenda/agendaview.h"
#include "agenda/timelabelszone.h"
27

28
#include <Akonadi/EntityTreeModel>
Sergio Martins's avatar
Sergio Martins committed
29
#include <Akonadi/Calendar/ETMCalendar>
30
31
32
#include <calendarsupport/collectionselection.h>
#include <calendarsupport/utils.h>

Allen Winter's avatar
Allen Winter committed
33
#include <Akonadi/ETMViewStateSaver>
34

35
#include <akonadi_next/kcolumnfilterproxymodel.h>
Allen Winter's avatar
Allen Winter committed
36
using namespace Future;
37

Allen Winter's avatar
Allen Winter committed
38
#include <KCalCore/Event>
39

40
#include <KCheckableProxyModel>
41
#include <KGlobalSettings>
Allen Winter's avatar
Allen Winter committed
42
43
#include <KLocale>
#include <KVBox>
44
#include <KViewStateMaintainer>
45
46

#include <QHBoxLayout>
Allen Winter's avatar
Allen Winter committed
47
#include <QItemSelectionModel>
48
49
50
51
#include <QLabel>
#include <QResizeEvent>
#include <QScrollArea>
#include <QScrollBar>
Allen Winter's avatar
Allen Winter committed
52
#include <QSplitter>
53
#include <QSortFilterProxyModel>
Allen Winter's avatar
Allen Winter committed
54
#include <QTimer>
55

56
using namespace Akonadi;
57
using namespace EventViews;
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**
   Function for debugging purposes:
   prints an object's sizeHint()/minimumSizeHint()/policy
   and it's children's too, recursively
*/
/*
static void printObject( QObject *o, int level = 0 )
{
  QMap<int,QString> map;
  map.insert( 0, "Fixed" );
  map.insert( 1, "Minimum" );
  map.insert( 4, "Maximum" );
  map.insert( 5, "Preferred" );
  map.insert( 7, "Expanding" );
  map.insert( 3, "MinimumExpaning" );
  map.insert( 13, "Ignored" );

  QWidget *w = qobject_cast<QWidget*>( o );

  if ( w ) {
    qDebug() << QString( level*2, '-' ) << o
             << w->sizeHint() << "/" << map[w->sizePolicy().verticalPolicy()]
             << "; minimumSize = " << w->minimumSize()
             << "; minimumSizeHint = " << w->minimumSizeHint();
  } else {
    qDebug() << QString( level*2, '-' ) << o ;
  }

  foreach( QObject *child, o->children() ) {
    printObject( child, level + 1 );
  }
}
*/

93
94
95
96
static QString generateColumnLabel( int c )
{
  return i18n( "Agenda %1", c + 1 );
}
Sergio Luis Martins's avatar
Sergio Luis Martins committed
97

Allen Winter's avatar
Allen Winter committed
98
99
class MultiAgendaView::Private
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
100
  public:
Allen Winter's avatar
Allen Winter committed
101
102
103
104
105
106
    Private( MultiAgendaView *qq ) :
      q( qq ),
      mUpdateOnShow( true ),
      mPendingChanges( true ),
      mCustomColumnSetupUsed( false ),
      mCustomNumberOfColumns( 2 )
Sergio Luis Martins's avatar
Sergio Luis Martins committed
107
108
109
110
111
    {
    }

    ~Private()
    {
112
      qDeleteAll( mSelectionSavers );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
113
114
115
    }

    void addView( const Akonadi::Collection &collection );
116
    void addView( KCheckableProxyModel *selectionProxy, const QString &title );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
117
118
119
120
121
    AgendaView *createView( const QString &title );
    void deleteViews();
    void setupViews();
    void resizeScrollView( const QSize &size );

122
    MultiAgendaView *q;
Sergio Luis Martins's avatar
Sergio Luis Martins committed
123
124
125
126
127
128
129
130
131
132
133
134
    QList<AgendaView*> mAgendaViews;
    QList<QWidget*> mAgendaWidgets;
    KHBox *mTopBox;
    QScrollArea *mScrollArea;
    TimeLabelsZone *mTimeLabelsZone;
    QSplitter *mLeftSplitter, *mRightSplitter;
    QScrollBar *mScrollBar;
    QWidget *mLeftBottomSpacer, *mRightBottomSpacer;
    QDate mStartDate, mEndDate;
    bool mUpdateOnShow;
    bool mPendingChanges;
    bool mCustomColumnSetupUsed;
135
    QVector<KCheckableProxyModel*> mCollectionSelectionModels;
Sergio Luis Martins's avatar
Sergio Luis Martins committed
136
137
    QVector<QString> mCustomColumnTitles;
    int mCustomNumberOfColumns;
138
139
    QLabel *mLabel;
    QWidget *mRightDummyWidget;
140
    QHash<QString, KViewStateMaintainer<ETMViewStateSaver>* > mSelectionSavers;
Sergio Luis Martins's avatar
Sergio Luis Martins committed
141
142
};

143
MultiAgendaView::MultiAgendaView( QWidget *parent )
Sergio Luis Martins's avatar
Sergio Luis Martins committed
144
145
  : EventView( parent ), d( new Private( this ) )

146
147
148
149
150
151
152
153
154
155
156
157
158
{
  QHBoxLayout *topLevelLayout = new QHBoxLayout( this );
  topLevelLayout->setSpacing( 0 );
  topLevelLayout->setMargin( 0 );

  QFontMetrics fm( font() );
  int topLabelHeight = 2 * fm.height() + fm.lineSpacing();

  KVBox *topSideBox = new KVBox( this );

  QWidget *topSideSpacer = new QWidget( topSideBox );
  topSideSpacer->setFixedHeight( topLabelHeight );

Sergio Luis Martins's avatar
Sergio Luis Martins committed
159
160
  d->mLeftSplitter = new QSplitter( Qt::Vertical, topSideBox );
  d->mLeftSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
161

162
163
164
  d->mLabel = new QLabel( i18n( "All Day" ), d->mLeftSplitter );
  d->mLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
  d->mLabel->setWordWrap( true );
165

Sergio Luis Martins's avatar
Sergio Luis Martins committed
166
  KVBox *sideBox = new KVBox( d->mLeftSplitter );
167
168
169
170

  // compensate for the frame the agenda views but not the timelabels have
  QWidget *timeLabelTopAlignmentSpacer = new QWidget( sideBox );

Sergio Luis Martins's avatar
Sergio Luis Martins committed
171
  d->mTimeLabelsZone = new TimeLabelsZone( sideBox, PrefsPtr( new Prefs() ) );
172
173
174

  QWidget *timeLabelBotAlignmentSpacer = new QWidget( sideBox );

Sergio Luis Martins's avatar
Sergio Luis Martins committed
175
  d->mLeftBottomSpacer = new QWidget( topSideBox );
176
177
178

  topLevelLayout->addWidget( topSideBox );

Sergio Luis Martins's avatar
Sergio Luis Martins committed
179
  d->mScrollArea = new QScrollArea( this );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
180
  d->mScrollArea->setWidgetResizable( true );
181

Sergio Luis Martins's avatar
Sergio Luis Martins committed
182
  d->mScrollArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
183

184
  // BUG: timelabels aren't aligned with the agenda's grid, 2 or 3 pixels of offset.
185
  // asymetric since the timelabels
Sergio Luis Martins's avatar
Sergio Luis Martins committed
186
  timeLabelTopAlignmentSpacer->setFixedHeight( d->mScrollArea->frameWidth() - 1 );
187
  // have 25 horizontal lines
Sergio Luis Martins's avatar
Sergio Luis Martins committed
188
  timeLabelBotAlignmentSpacer->setFixedHeight( d->mScrollArea->frameWidth() - 2 );
189

Sergio Luis Martins's avatar
Sergio Luis Martins committed
190
191
192
193
  d->mScrollArea->setFrameShape( QFrame::NoFrame );
  topLevelLayout->addWidget( d->mScrollArea, 100 );
  d->mTopBox = new KHBox( d->mScrollArea->viewport() );
  d->mScrollArea->setWidget( d->mTopBox );
194
195
196
197
198
199

  topSideBox = new KVBox( this );

  topSideSpacer = new QWidget( topSideBox );
  topSideSpacer->setFixedHeight( topLabelHeight );

Sergio Luis Martins's avatar
Sergio Luis Martins committed
200
201
  d->mRightSplitter = new QSplitter( Qt::Vertical, topSideBox );
  d->mRightSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
202

Sergio Martins's avatar
Sergio Martins committed
203
204
205
  connect( d->mLeftSplitter, SIGNAL(splitterMoved(int,int)), SLOT(resizeSplitters()) );
  connect( d->mRightSplitter, SIGNAL(splitterMoved(int,int)), SLOT(resizeSplitters()) );

206
  d->mRightDummyWidget = new QWidget( d->mRightSplitter );
207
208

  d->mScrollBar = new QScrollBar( Qt::Vertical, d->mRightSplitter );
209

Sergio Luis Martins's avatar
Sergio Luis Martins committed
210
  d->mRightBottomSpacer = new QWidget( topSideBox );
211
212
213
  topLevelLayout->addWidget( topSideBox );
}

214
void MultiAgendaView::setCalendar( const Akonadi::ETMCalendar::Ptr &cal )
215
{
216
  EventView::setCalendar( cal );
217
  Q_FOREACH ( KCheckableProxyModel *const i, d->mCollectionSelectionModels ) {
218
    i->setSourceModel( cal->entityTreeModel() );
219
  }
Sergio Martins's avatar
Sergio Martins committed
220
221
222
223
224
225
226

  disconnect( 0, SIGNAL(selectionChanged(Akonadi::Collection::List,Akonadi::Collection::List)),
              this, SLOT(forceRecreateViews()));

  connect( collectionSelection(), SIGNAL(selectionChanged(Akonadi::Collection::List,Akonadi::Collection::List)),
           SLOT(forceRecreateViews()) );

227
228
229
230
231
  recreateViews();
}

void MultiAgendaView::recreateViews()
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
232
  if ( !d->mPendingChanges ) {
233
234
    return;
  }
Sergio Luis Martins's avatar
Sergio Luis Martins committed
235
  d->mPendingChanges = false;
236

Sergio Luis Martins's avatar
Sergio Luis Martins committed
237
  d->deleteViews();
238

Sergio Luis Martins's avatar
Sergio Luis Martins committed
239
240
241
242
  if ( d->mCustomColumnSetupUsed ) {
    Q_ASSERT( d->mCollectionSelectionModels.size() == d->mCustomNumberOfColumns );
    for ( int i = 0; i < d->mCustomNumberOfColumns; ++i ) {
      d->addView( d->mCollectionSelectionModels[i], d->mCustomColumnTitles[i] );
243
244
    }
  } else {
Allen Winter's avatar
Allen Winter committed
245
    Q_FOREACH ( const Akonadi::Collection &i, collectionSelection()->selectedCollections() ) {
Allen Winter's avatar
Allen Winter committed
246
      if ( i.contentMimeTypes().contains( KCalCore::Event::eventMimeType() ) ) {
Sergio Luis Martins's avatar
Sergio Luis Martins committed
247
        d->addView( i );
248
249
250
251
252
      }
    }
  }
  // no resources activated, so stop here to avoid crashing somewhere down the line
  // TODO: show a nice message instead
Sergio Luis Martins's avatar
Sergio Luis Martins committed
253
  if ( d->mAgendaViews.isEmpty() ) {
254
255
256
    return;
  }

Sergio Luis Martins's avatar
Sergio Luis Martins committed
257
  d->setupViews();
258
  QTimer::singleShot( 0, this, SLOT(slotResizeScrollView()) );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
259
  d->mTimeLabelsZone->updateAll();
260

Sergio Luis Martins's avatar
Sergio Luis Martins committed
261
  QScrollArea *timeLabel = d->mTimeLabelsZone->timeLabels().first();
262
  connect( timeLabel->verticalScrollBar(), SIGNAL(valueChanged(int)),
Sergio Luis Martins's avatar
Sergio Luis Martins committed
263
264
           d->mScrollBar, SLOT(setValue(int)) );
  connect( d->mScrollBar, SIGNAL(valueChanged(int)),
265
266
           timeLabel->verticalScrollBar(), SLOT(setValue(int)) );

267
  resizeSplitters();
268
269
  QTimer::singleShot( 0, this, SLOT(setupScrollBar()) );

Sergio Luis Martins's avatar
Sergio Luis Martins committed
270
  d->mTimeLabelsZone->updateTimeLabelsPosition();
Sergio Martins's avatar
Sergio Martins committed
271
}
272

Sergio Martins's avatar
Sergio Martins committed
273
274
275
276
void MultiAgendaView::forceRecreateViews()
{
  d->mPendingChanges = true;
  recreateViews();
277
278
}

Sergio Luis Martins's avatar
Sergio Luis Martins committed
279
void MultiAgendaView::Private::deleteViews()
280
{
281
  Q_FOREACH ( AgendaView *const i, mAgendaViews ) {
282
    KCheckableProxyModel *proxy = i->takeCustomCollectionSelectionProxyModel();
283
284
285
286
287
288
289
    if ( proxy && !mCollectionSelectionModels.contains( proxy ) ) {
      delete proxy;
    }
    delete i;
  }

  mAgendaViews.clear();
290
  mTimeLabelsZone->setAgendaView( 0 );
291
292
293
294
  qDeleteAll( mAgendaWidgets );
  mAgendaWidgets.clear();
}

Sergio Luis Martins's avatar
Sergio Luis Martins committed
295
void MultiAgendaView::Private::setupViews()
296
{
297
  foreach ( AgendaView *agenda, mAgendaViews ) {
298
299
300
301
302
303
    q->connect( agenda, SIGNAL(newEventSignal()),
                q, SIGNAL(newEventSignal()) );
    q->connect( agenda, SIGNAL(newEventSignal(QDate)),
                q, SIGNAL(newEventSignal(QDate)) );
    q->connect( agenda, SIGNAL(newEventSignal(QDateTime)),
                q, SIGNAL(newEventSignal(QDateTime)) );
304
    q->connect( agenda,
305
306
                SIGNAL(newEventSignal(QDateTime,QDateTime)),
                q, SIGNAL(newEventSignal(QDateTime,QDateTime)) );
307

Sergio Luis Martins's avatar
Sergio Luis Martins committed
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
    q->connect( agenda, SIGNAL(editIncidenceSignal(Akonadi::Item)),
                q, SIGNAL(editIncidenceSignal(Akonadi::Item)) );
    q->connect( agenda, SIGNAL(showIncidenceSignal(Akonadi::Item)),
                q, SIGNAL(showIncidenceSignal(Akonadi::Item)) );
    q->connect( agenda, SIGNAL(deleteIncidenceSignal(Akonadi::Item)),
                q, SIGNAL(deleteIncidenceSignal(Akonadi::Item)) );

    q->connect( agenda, SIGNAL(incidenceSelected(Akonadi::Item,QDate)),
                q, SIGNAL(incidenceSelected(Akonadi::Item,QDate)) );

    q->connect( agenda, SIGNAL(cutIncidenceSignal(Akonadi::Item)),
                q, SIGNAL(cutIncidenceSignal(Akonadi::Item)) );
    q->connect( agenda, SIGNAL(copyIncidenceSignal(Akonadi::Item)),
                q, SIGNAL(copyIncidenceSignal(Akonadi::Item)) );
    q->connect( agenda, SIGNAL(pasteIncidenceSignal()),
                q, SIGNAL(pasteIncidenceSignal()) );
    q->connect( agenda, SIGNAL(toggleAlarmSignal(Akonadi::Item)),
                q, SIGNAL(toggleAlarmSignal(Akonadi::Item)) );
Laurent Montel's avatar
Laurent Montel committed
326
    q->connect( agenda, SIGNAL(dissociateOccurrencesSignal(Akonadi::Item,QDate)),
Sergio Luis Martins's avatar
Sergio Luis Martins committed
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
                q, SIGNAL(dissociateOccurrencesSignal(Akonadi::Item,QDate)) );

    q->connect( agenda, SIGNAL(newTodoSignal(QDate)),
                q, SIGNAL(newTodoSignal(QDate)) );

    q->connect( agenda, SIGNAL(incidenceSelected(Akonadi::Item,QDate)),
                q, SLOT(slotSelectionChanged()) );

    q->connect( agenda, SIGNAL(timeSpanSelectionChanged()),
                q, SLOT(slotClearTimeSpanSelection()) );

    q->disconnect( agenda->agenda(),
                   SIGNAL(zoomView(int,QPoint,Qt::Orientation)),
                   agenda, 0 );
    q->connect( agenda->agenda(),
                SIGNAL(zoomView(int,QPoint,Qt::Orientation)),
                q, SLOT(zoomView(int,QPoint,Qt::Orientation)) );
344
345
  }

346
347
  AgendaView *lastView = mAgendaViews.last();
  foreach ( AgendaView *agenda, mAgendaViews ) {
348
349
350
351
352
353
    if ( agenda != lastView ) {
      connect( agenda->agenda()->verticalScrollBar(), SIGNAL(valueChanged(int)),
               lastView->agenda()->verticalScrollBar(), SLOT(setValue(int)) );
    }
  }

354
  foreach ( AgendaView *agenda, mAgendaViews ) {
355
356
357
358
359
360
361
362
363
364
365
366
367
368
    agenda->readSettings();
  }

  int minWidth = 0;
  foreach ( QWidget *widget, mAgendaWidgets ) {
    minWidth = qMax( minWidth, widget->minimumSizeHint().width() );
  }
  foreach ( QWidget *widget, mAgendaWidgets ) {
    widget->setMinimumWidth( minWidth );
  }
}

MultiAgendaView::~MultiAgendaView()
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
369
  delete d;
370
371
}

372
Akonadi::Item::List MultiAgendaView::selectedIncidences() const
373
374
{
  Akonadi::Item::List list;
Sergio Luis Martins's avatar
Sergio Luis Martins committed
375
  foreach ( AgendaView *agendaView, d->mAgendaViews ) {
376
377
378
379
380
    list += agendaView->selectedIncidences();
  }
  return list;
}

Allen Winter's avatar
Allen Winter committed
381
KCalCore::DateList MultiAgendaView::selectedIncidenceDates() const
382
{
Allen Winter's avatar
Allen Winter committed
383
  KCalCore::DateList list;
Sergio Luis Martins's avatar
Sergio Luis Martins committed
384
  foreach ( AgendaView *agendaView, d->mAgendaViews ) {
385
386
387
388
389
390
391
    list += agendaView->selectedIncidenceDates();
  }
  return list;
}

int MultiAgendaView::currentDateCount() const
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
392
  foreach ( AgendaView *agendaView, d->mAgendaViews ) {
393
394
395
396
397
    return agendaView->currentDateCount();
  }
  return 0;
}

398
void MultiAgendaView::showDates( const QDate &start, const QDate &end, const QDate &preferredMonth )
399
{
400
  Q_UNUSED( preferredMonth );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
401
402
  d->mStartDate = start;
  d->mEndDate = end;
403
  slotResizeScrollView();
Sergio Luis Martins's avatar
Sergio Luis Martins committed
404
405
  d->mTimeLabelsZone->updateAll();
  foreach ( AgendaView *agendaView, d->mAgendaViews ) {
406
407
408
409
    agendaView->showDates( start, end );
  }
}

410
void MultiAgendaView::showIncidences( const Akonadi::Item::List &incidenceList, const QDate &date )
411
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
412
  foreach ( AgendaView *agendaView, d->mAgendaViews ) {
413
    agendaView->showIncidences( incidenceList, date );
414
415
416
417
418
419
  }
}

void MultiAgendaView::updateView()
{
  recreateViews();
Sergio Luis Martins's avatar
Sergio Luis Martins committed
420
  foreach ( AgendaView *agendaView, d->mAgendaViews ) {
421
422
423
424
425
426
    agendaView->updateView();
  }
}

int MultiAgendaView::maxDatesHint() const
{
427
  // these maxDatesHint functions aren't used
Sergio Luis Martins's avatar
Sergio Luis Martins committed
428
  return AgendaView::MAX_DAY_COUNT;
429
430
431
432
}

void MultiAgendaView::slotSelectionChanged()
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
433
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
434
435
436
437
438
439
    if ( agenda != sender() ) {
      agenda->clearSelection();
    }
  }
}

Sergio Luis Martins's avatar
Sergio Luis Martins committed
440
441
bool MultiAgendaView::eventDurationHint( QDateTime &startDt, QDateTime &endDt,
                                         bool &allDay ) const
442
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
443
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
444
445
446
447
448
449
450
451
452
453
    bool valid = agenda->eventDurationHint( startDt, endDt, allDay );
    if ( valid ) {
      return true;
    }
  }
  return false;
}

void MultiAgendaView::slotClearTimeSpanSelection()
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
454
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
455
456
    if ( agenda != sender() ) {
      agenda->clearTimeSpanSelection();
457
458
    } else {
      setCollectionId( agenda->collectionId() );
459
460
461
462
    }
  }
}

Sergio Luis Martins's avatar
Sergio Luis Martins committed
463
AgendaView *MultiAgendaView::Private::createView( const QString &title )
464
465
466
467
468
469
470
{
  QWidget *box = new QWidget( mTopBox );
  QVBoxLayout *layout = new QVBoxLayout( box );
  layout->setMargin( 0 );
  QLabel *l = new QLabel( title );
  layout->addWidget( l );
  l->setAlignment( Qt::AlignVCenter | Qt::AlignHCenter );
471
472
  AgendaView *av = new AgendaView( q->preferences(),
                                   q->startDateTime().date(),
473
                                   q->endDateTime().date(),
474
                                   true,
475
                                   true, q );
476
  layout->addWidget( av );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
477
478
  av->setCalendar( q->calendar() );
  av->setIncidenceChanger( q->changer() );
479
480
481
482
483
  av->agenda()->scrollArea()->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
  mAgendaViews.append( av );
  mAgendaWidgets.append( box );
  box->show();
  mTimeLabelsZone->setAgendaView( av );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
484
485
486
487
488
489

  q->connect( mScrollBar, SIGNAL(valueChanged(int)),
              av->agenda()->verticalScrollBar(), SLOT(setValue(int)) );

  q->connect( av->splitter(), SIGNAL(splitterMoved(int,int)),
              q, SLOT(resizeSplitters()) );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
490
491
  q->connect( av, SIGNAL(showIncidencePopupSignal(Akonadi::Item,QDate)),
              q, SIGNAL(showIncidencePopupSignal(Akonadi::Item,QDate)) );
492

Sergio Luis Martins's avatar
Sergio Luis Martins committed
493
494
  q->connect( av, SIGNAL(showNewEventPopupSignal()),
              q, SIGNAL(showNewEventPopupSignal()) );
495

496
497
498
499
500
501
502
  const QSize minHint = av->allDayAgenda()->scrollArea()->minimumSizeHint();

  if ( minHint.isValid() ) {
    mLabel->setMinimumHeight( minHint.height() );
    mRightDummyWidget->setMinimumHeight( minHint.height() );
  }

503
504
505
  return av;
}

Sergio Luis Martins's avatar
Sergio Luis Martins committed
506
void MultiAgendaView::Private::addView( const Akonadi::Collection &collection )
507
{
508
  AgendaView *av = createView( CalendarSupport::displayName( q->calendar().data(), collection ) );
509
  av->setCollectionId( collection.id() );
510
511
}

512
void MultiAgendaView::Private::addView( KCheckableProxyModel *sm, const QString &title )
513
{
514
  AgendaView *av = createView( title );
515
516
517
518
519
  av->setCustomCollectionSelectionProxyModel( sm );
}

void MultiAgendaView::resizeEvent( QResizeEvent *ev )
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
520
  d->resizeScrollView( ev->size() );
521
  EventView::resizeEvent( ev );
522
523
}

Sergio Luis Martins's avatar
Sergio Luis Martins committed
524
void MultiAgendaView::Private::resizeScrollView( const QSize &size )
525
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
526
527
528
  const int widgetWidth = size.width() - mTimeLabelsZone->width() -
                          mScrollBar->width();

529
  int height = size.height();
Sergio Luis Martins's avatar
Sergio Luis Martins committed
530
531
  if ( mScrollArea->horizontalScrollBar()->isVisible() ) {
    // this should never happen, you can't get horizontalScrollBars
532
533
534
535
536
537
538
539
    const int sbHeight = mScrollArea->horizontalScrollBar()->height();
    height -= sbHeight;
    mLeftBottomSpacer->setFixedHeight( sbHeight );
    mRightBottomSpacer->setFixedHeight( sbHeight );
  } else {
    mLeftBottomSpacer->setFixedHeight( 0 );
    mRightBottomSpacer->setFixedHeight( 0 );
  }
Sergio Luis Martins's avatar
Sergio Luis Martins committed
540
541
542
543

  mScrollArea->widget()->setFixedSize( widgetWidth, height );

  mTopBox->resize( widgetWidth, height );
544
545
}

546
void MultiAgendaView::setIncidenceChanger( Akonadi::IncidenceChanger *changer )
547
{
548
  EventView::setIncidenceChanger( changer );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
549
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
550
551
552
553
    agenda->setIncidenceChanger( changer );
  }
}

554
555
556
557
558
559
560
561
void MultiAgendaView::setPreferences( const PrefsPtr &prefs )
{
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
    agenda->setPreferences( prefs );
  }
  EventView::setPreferences( prefs );
}

562
563
void MultiAgendaView::updateConfig()
{
564
  EventView::updateConfig();
565
  d->mTimeLabelsZone->setPreferences( preferences() );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
566
567
  d->mTimeLabelsZone->updateAll();
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
568
569
570
571
572
573
    agenda->updateConfig();
  }
}

void MultiAgendaView::resizeSplitters()
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
574
  if ( d->mAgendaViews.isEmpty() ) {
575
576
577
578
579
    return;
  }

  QSplitter *lastMovedSplitter = qobject_cast<QSplitter*>( sender() );
  if ( !lastMovedSplitter ) {
580
    lastMovedSplitter = d->mLeftSplitter;
581
  }
Sergio Luis Martins's avatar
Sergio Luis Martins committed
582
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
583
584
585
586
587
    if ( agenda->splitter() == lastMovedSplitter ) {
      continue;
    }
    agenda->splitter()->setSizes( lastMovedSplitter->sizes() );
  }
Sergio Luis Martins's avatar
Sergio Luis Martins committed
588
589
  if ( lastMovedSplitter != d->mLeftSplitter ) {
    d->mLeftSplitter->setSizes( lastMovedSplitter->sizes() );
590
  }
Sergio Luis Martins's avatar
Sergio Luis Martins committed
591
592
  if ( lastMovedSplitter != d->mRightSplitter ) {
    d->mRightSplitter->setSizes( lastMovedSplitter->sizes() );
593
594
595
596
597
  }
}

void MultiAgendaView::zoomView( const int delta, const QPoint &pos, const Qt::Orientation ori )
{
598
  const int hourSz = preferences()->hourSize();
599
600
  if ( ori == Qt::Vertical ) {
    if ( delta > 0 ) {
601
      if ( hourSz > 4 ) {
Allen Winter's avatar
Allen Winter committed
602
        preferences()->setHourSize( hourSz - 1 );
603
604
      }
    } else {
605
      preferences()->setHourSize( hourSz + 1 );
606
607
608
    }
  }

Sergio Luis Martins's avatar
Sergio Luis Martins committed
609
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
610
611
612
    agenda->zoomView( delta, pos, ori );
  }

Sergio Luis Martins's avatar
Sergio Luis Martins committed
613
  d->mTimeLabelsZone->updateAll();
614
615
616
617
}

void MultiAgendaView::slotResizeScrollView()
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
618
  d->resizeScrollView( size() );
619
620
621
622
}

void MultiAgendaView::showEvent( QShowEvent *event )
{
623
  EventView::showEvent( event );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
624
625
626
627
  if ( d->mUpdateOnShow ) {
    d->mUpdateOnShow = false;
    d->mPendingChanges = true; // force a full view recreation
    showDates( d->mStartDate, d->mEndDate );
628
629
630
  }
}

631
void MultiAgendaView::setChanges( Changes changes )
632
{
633
  EventView::setChanges( changes );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
634
  foreach ( AgendaView *agenda, d->mAgendaViews ) {
635
    agenda->setChanges( changes );
636
637
638
639
640
  }
}

void MultiAgendaView::setupScrollBar()
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
641
642
643
644
645
646
647
  if ( !d->mAgendaViews.isEmpty() && d->mAgendaViews.first()->agenda() ) {
    QScrollBar *scrollBar = d->mAgendaViews.first()->agenda()->verticalScrollBar();
    d->mScrollBar->setMinimum( scrollBar->minimum() );
    d->mScrollBar->setMaximum( scrollBar->maximum() );
    d->mScrollBar->setSingleStep( scrollBar->singleStep() );
    d->mScrollBar->setPageStep( scrollBar->pageStep() );
    d->mScrollBar->setValue( scrollBar->value() );
648
649
650
651
652
  }
}

void MultiAgendaView::collectionSelectionChanged()
{
653
  kDebug();
Sergio Luis Martins's avatar
Sergio Luis Martins committed
654
  d->mPendingChanges = true;
655
656
657
658
659
  recreateViews();
}

bool MultiAgendaView::hasConfigurationDialog() const
{
660
661
662
663
664

  /** The wrapper in korg has the dialog. Too complicated to move to CalendarViews.
      Depends on korg/AkonadiCollectionView, and will be refactored some day
      to get rid of CollectionSelectionProxyModel/EntityStateSaver */
  return false;
665
666
667
668
}

void MultiAgendaView::doRestoreConfig( const KConfigGroup &configGroup )
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
669
670
671
672
673
674
675
676
  d->mCustomColumnSetupUsed = configGroup.readEntry( "UseCustomColumnSetup", false );
  d->mCustomNumberOfColumns = configGroup.readEntry( "CustomNumberOfColumns", 2 );
  d->mCustomColumnTitles =  configGroup.readEntry( "ColumnTitles", QStringList() ).toVector();
  if ( d->mCustomColumnTitles.size() != d->mCustomNumberOfColumns ) {
    const int orig = d->mCustomColumnTitles.size();
    d->mCustomColumnTitles.resize( d->mCustomNumberOfColumns );
    for ( int i = orig; i < d->mCustomNumberOfColumns; ++i ) {
      d->mCustomColumnTitles[i] = generateColumnLabel( i );
677
678
    }
  }
679

680
  QVector<KCheckableProxyModel*> oldModels = d->mCollectionSelectionModels;
Sergio Luis Martins's avatar
Sergio Luis Martins committed
681
  d->mCollectionSelectionModels.clear();
682

683

684
685
686
687
688
689
690
691
692
  if ( d->mCustomColumnSetupUsed ) {
    d->mCollectionSelectionModels.resize( d->mCustomNumberOfColumns );
    for ( int i = 0; i < d->mCustomNumberOfColumns; ++i ) {
      // Sort the calanders by name
      QSortFilterProxyModel *sortProxy = new QSortFilterProxyModel( this );
      sortProxy->setDynamicSortFilter( true );
      if ( calendar() ) {
        sortProxy->setSourceModel( calendar()->entityTreeModel() );
      }
693

694
695
696
697
      // Only show the first column
      KColumnFilterProxyModel *columnFilterProxy = new KColumnFilterProxyModel( this );
      columnFilterProxy->setVisibleColumn( Akonadi::ETMCalendar::CollectionTitle );
      columnFilterProxy->setSourceModel( sortProxy );
698

699
700
      // Keep track of selection.
      QItemSelectionModel *qsm = new QItemSelectionModel( columnFilterProxy );
701

702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
      // Make the model checkable.
      KCheckableProxyModel *checkableProxy = new KCheckableProxyModel( this );
      checkableProxy->setSourceModel( columnFilterProxy );
      checkableProxy->setSelectionModel( qsm );
      const QString groupName = configGroup.name() + "_subView_" + QByteArray::number( i );
      const KConfigGroup group = configGroup.config()->group( groupName );

      if ( !d->mSelectionSavers.contains( groupName ) ) {
        d->mSelectionSavers.insert( groupName, new KViewStateMaintainer<ETMViewStateSaver>( group ) );
        d->mSelectionSavers[groupName]->setSelectionModel( checkableProxy->selectionModel() );
      }

      d->mSelectionSavers[groupName]->restoreState();
      d->mCollectionSelectionModels[i] = checkableProxy;
    }
717
  }
718

Sergio Luis Martins's avatar
Sergio Luis Martins committed
719
  d->mPendingChanges = true;
720
721
722
723
724
725
  recreateViews();
  qDeleteAll( oldModels );
}

void MultiAgendaView::doSaveConfig( KConfigGroup &configGroup )
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
726
727
728
  configGroup.writeEntry( "UseCustomColumnSetup", d->mCustomColumnSetupUsed );
  configGroup.writeEntry( "CustomNumberOfColumns", d->mCustomNumberOfColumns );
  const QStringList titleList = d->mCustomColumnTitles.toList();
729
730
  configGroup.writeEntry( "ColumnTitles", titleList );
  int idx = 0;
Allen Winter's avatar
Allen Winter committed
731
  foreach ( KCheckableProxyModel *checkableProxyModel, d->mCollectionSelectionModels ) {
732
733
    const QString groupName = configGroup.name() + "_subView_" + QByteArray::number( idx );
    KConfigGroup group = configGroup.config()->group( groupName );
734
    ++idx;
735
    KViewStateMaintainer<ETMViewStateSaver> saver( group );
736
737
738
739
740
    if ( !d->mSelectionSavers.contains( groupName ) ) {
      d->mSelectionSavers.insert( groupName, new KViewStateMaintainer<ETMViewStateSaver>( group ) );
      d->mSelectionSavers[groupName]->setSelectionModel( checkableProxyModel->selectionModel() );
    }
    d->mSelectionSavers[groupName]->saveState();
741
742
  }
}
743

744
745
void MultiAgendaView::customCollectionsChanged( ConfigDialogInterface *dlg )
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
746
  if ( !d->mCustomColumnSetupUsed && !dlg->useCustomColumns() ) {
747
748
749
750
    // Config didn't change, no need to recreate views
    return;
  }

Sergio Luis Martins's avatar
Sergio Luis Martins committed
751
752
  d->mCustomColumnSetupUsed = dlg->useCustomColumns();
  d->mCustomNumberOfColumns = dlg->numberOfColumns();
753
  QVector<KCheckableProxyModel *> newModels;
Sergio Luis Martins's avatar
Sergio Luis Martins committed
754
755
756
  newModels.resize( d->mCustomNumberOfColumns );
  d->mCustomColumnTitles.resize( d->mCustomNumberOfColumns );
  for ( int i = 0; i < d->mCustomNumberOfColumns; ++i ) {
757
    newModels[i] = dlg->takeSelectionModel( i );
Sergio Luis Martins's avatar
Sergio Luis Martins committed
758
    d->mCustomColumnTitles[i] = dlg->columnTitle( i );
759
  }
Sergio Luis Martins's avatar
Sergio Luis Martins committed
760
761
  d->mCollectionSelectionModels = newModels;
  d->mPendingChanges = true;
762
763
764
  recreateViews();
}

765
766
bool MultiAgendaView::customColumnSetupUsed() const
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
767
  return d->mCustomColumnSetupUsed;
768
769
770
771
}

int MultiAgendaView::customNumberOfColumns() const
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
772
  return d->mCustomNumberOfColumns;
773
774
}

775
QVector<KCheckableProxyModel *> MultiAgendaView::collectionSelectionModels() const
776
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
777
  return d->mCollectionSelectionModels;
778
779
780
781
}

QVector<QString> MultiAgendaView::customColumnTitles() const
{
Sergio Luis Martins's avatar
Sergio Luis Martins committed
782
  return d->mCustomColumnTitles;
783
784
}

785
#include "multiagendaview.moc"