~ubuntu-branches/ubuntu/oneiric/kdepim/oneiric-updates

« back to all changes in this revision

Viewing changes to korganizer/views/agendaview/koagenda.cpp

  • Committer: Package Import Robot
  • Author(s): Philip Muškovac
  • Date: 2011-06-28 19:33:24 UTC
  • mfrom: (0.2.13) (0.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20110628193324-8yvjs8sdv9rdoo6c
Tags: 4:4.7.0-0ubuntu1
* New upstream release
  - update install files
  - add missing kdepim-doc package to control file
  - Fix Vcs lines
  - kontact breaks/replaces korganizer << 4:4.6.80
  - tighten the dependency of kdepim-dev on libkdepim4 to fix lintian error

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
  This file is part of KOrganizer.
3
 
  Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
4
 
  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
5
 
 
6
 
  Marcus Bains line.
7
 
  Copyright (c) 2001 Ali Rahimi <ali@mit.edu>
8
 
 
9
 
  This program is free software; you can redistribute it and/or modify
10
 
  it under the terms of the GNU General Public License as published by
11
 
  the Free Software Foundation; either version 2 of the License, or
12
 
  (at your option) any later version.
13
 
 
14
 
  This program is distributed in the hope that it will be useful,
15
 
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 
  GNU General Public License for more details.
18
 
 
19
 
  You should have received a copy of the GNU General Public License along
20
 
  with this program; if not, write to the Free Software Foundation, Inc.,
21
 
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
 
 
23
 
  As a special exception, permission is given to link this program
24
 
  with any edition of Qt, and distribute the resulting executable,
25
 
  without including the source code for Qt in the source distribution.
26
 
*/
27
 
#include "koagenda.h"
28
 
#include "koagendaitem.h"
29
 
#include "koprefs.h"
30
 
#include "koglobals.h"
31
 
#include "komessagebox.h"
32
 
#include "incidencechanger.h"
33
 
#include "kohelper.h"
34
 
#include "korganizer/baseview.h"
35
 
 
36
 
#include <KCal/Calendar>
37
 
#include <KCal/DndFactory>
38
 
#include <KCal/ICalDrag>
39
 
#include <KCal/Todo>
40
 
#include <KCal/VCalDrag>
41
 
 
42
 
#include <KDebug>
43
 
#include <KGlobal>
44
 
#include <KLocale>
45
 
#include <KMessageBox>
46
 
 
47
 
#include <QDateTime>
48
 
#include <QApplication>
49
 
#include <QCursor>
50
 
#include <QPainter>
51
 
#include <QLabel>
52
 
#include <QWheelEvent>
53
 
#include <QPixmap>
54
 
#include <QVector>
55
 
#include <QList>
56
 
#include <QEvent>
57
 
#include <QKeyEvent>
58
 
#include <QFrame>
59
 
#include <QDropEvent>
60
 
#include <QResizeEvent>
61
 
#include <QMouseEvent>
62
 
 
63
 
#include <cmath>
64
 
 
65
 
///////////////////////////////////////////////////////////////////////////////
66
 
MarcusBains::MarcusBains( KOAgenda *agenda )
67
 
  : QFrame( agenda->viewport() ), mAgenda( agenda )
68
 
{
69
 
  mTimeBox = new QLabel( this );
70
 
  mTimeBox->setAlignment( Qt::AlignRight | Qt::AlignBottom );
71
 
  mAgenda->addChild( mTimeBox );
72
 
 
73
 
  mTimer = new QTimer( this );
74
 
  mTimer->setSingleShot( true );
75
 
  connect( mTimer, SIGNAL(timeout()), this, SLOT(updateLocation()) );
76
 
  mTimer->start( 0 );
77
 
 
78
 
  mOldTodayCol = -1;
79
 
}
80
 
 
81
 
MarcusBains::~MarcusBains()
82
 
{
83
 
}
84
 
 
85
 
int MarcusBains::todayColumn()
86
 
{
87
 
  QDate currentDate = QDate::currentDate();
88
 
 
89
 
  DateList dateList = mAgenda->dateList();
90
 
  DateList::ConstIterator it;
91
 
  int col = 0;
92
 
  for ( it = dateList.constBegin(); it != dateList.constEnd(); ++it ) {
93
 
    if ( (*it) == currentDate ) {
94
 
      return KOGlobals::self()->reverseLayout() ? mAgenda->columns() - 1 - col : col;
95
 
    }
96
 
    ++col;
97
 
  }
98
 
 
99
 
  return -1;
100
 
}
101
 
 
102
 
void MarcusBains::updateLocation()
103
 
{
104
 
  updateLocationRecalc();
105
 
}
106
 
 
107
 
void MarcusBains::updateLocationRecalc( bool recalculate )
108
 
{
109
 
  bool showSeconds = KOPrefs::instance()->marcusBainsShowSeconds();
110
 
  QColor color = KOPrefs::instance()->agendaMarcusBainsLineLineColor();
111
 
 
112
 
  QTime tim = QTime::currentTime();
113
 
  if ( ( tim.hour() == 0 ) && ( mOldTime.hour() == 23 ) ) {
114
 
    // We are on a new day
115
 
    recalculate = true;
116
 
  }
117
 
  int todayCol = recalculate ? todayColumn() : mOldTodayCol;
118
 
 
119
 
  // Number of minutes since beginning of the day
120
 
  int minutes = tim.hour() * 60 + tim.minute();
121
 
  int minutesPerCell = 24 * 60 / mAgenda->rows();
122
 
 
123
 
  mOldTime = tim;
124
 
  mOldTodayCol = todayCol;
125
 
 
126
 
  int y = int( minutes  *  mAgenda->gridSpacingY() / minutesPerCell );
127
 
  int x = int( mAgenda->gridSpacingX() * todayCol );
128
 
 
129
 
  if ( !( KOPrefs::instance()->marcusBainsEnabled() ) || ( todayCol < 0 ) ) {
130
 
    hide();
131
 
    mTimeBox->hide();
132
 
    return;
133
 
  } else {
134
 
    show();
135
 
    mTimeBox->show();
136
 
  }
137
 
 
138
 
  /* Line */
139
 
  // It seems logical to adjust the line width with the label's font weight
140
 
  int fw = KOPrefs::instance()->agendaMarcusBainsLineFont().weight();
141
 
  setLineWidth( 1 + abs( fw - QFont::Normal ) / QFont::Light );
142
 
  setFrameStyle( QFrame::HLine | QFrame::Plain );
143
 
  QPalette pal = palette();
144
 
  pal.setColor( QPalette::Window, color ); // for Oxygen
145
 
  pal.setColor( QPalette::WindowText, color ); // for Plastique
146
 
  setPalette( pal );
147
 
  if ( recalculate ) {
148
 
    setFixedSize( int( mAgenda->gridSpacingX() ), 1 );
149
 
  }
150
 
  mAgenda->moveChild( this, x, y );
151
 
  raise();
152
 
 
153
 
  /* Label */
154
 
  mTimeBox->setFont( KOPrefs::instance()->agendaMarcusBainsLineFont() );
155
 
  QPalette pal1 = mTimeBox->palette();
156
 
  pal1.setColor( QPalette::WindowText, color );
157
 
  mTimeBox->setPalette( pal1 );
158
 
  mTimeBox->setText( KGlobal::locale()->formatTime( tim, showSeconds ) );
159
 
  mTimeBox->adjustSize();
160
 
  if ( y-mTimeBox->height() >= 0 ) {
161
 
    y -= mTimeBox->height();
162
 
  } else {
163
 
    y++;
164
 
  }
165
 
  if ( x - mTimeBox->width() + mAgenda->gridSpacingX() > 0 ) {
166
 
    x += int( mAgenda->gridSpacingX() - mTimeBox->width() - 1 );
167
 
  } else {
168
 
    x++;
169
 
  }
170
 
  mAgenda->moveChild( mTimeBox, x, y );
171
 
  mTimeBox->raise();
172
 
 
173
 
  if ( showSeconds || recalculate ) {
174
 
    mTimer->start( 1000 );
175
 
  } else {
176
 
    mTimer->start( 1000 * ( 60 - tim.second() ) );
177
 
  }
178
 
}
179
 
 
180
 
////////////////////////////////////////////////////////////////////////////
181
 
 
182
 
/*
183
 
  Create an agenda widget with rows rows and columns columns.
184
 
*/
185
 
KOAgenda::KOAgenda( KOEventView *eventView, int columns, int rows, int rowSize, QWidget *parent,
186
 
                    Qt::WFlags f )
187
 
  : Q3ScrollView( parent, /*name*/0, f ), mHolidayMask( 0 ), mChanger( 0 )
188
 
{
189
 
  mColumns = columns;
190
 
  mRows = rows;
191
 
  mGridSpacingY = rowSize;
192
 
  mAllDayMode = false;
193
 
  mEventView = eventView;
194
 
 
195
 
  init();
196
 
 
197
 
  viewport()->setMouseTracking( true );
198
 
}
199
 
 
200
 
/*
201
 
  Create an agenda widget with columns columns and one row. This is used for
202
 
  all-day events.
203
 
*/
204
 
KOAgenda::KOAgenda( KOEventView *eventView, int columns, QWidget *parent, Qt::WFlags f )
205
 
  : Q3ScrollView( parent, /*name*/0, f ), mHolidayMask( 0 ), mChanger( 0 )
206
 
{
207
 
  mColumns = columns;
208
 
  mRows = 1;
209
 
  mGridSpacingY = 24;
210
 
  mAllDayMode = true;
211
 
  setVScrollBarMode( AlwaysOff );
212
 
  mEventView = eventView;
213
 
 
214
 
  init();
215
 
}
216
 
 
217
 
KOAgenda::~KOAgenda()
218
 
{
219
 
  delete mMarcusBains;
220
 
}
221
 
 
222
 
Incidence *KOAgenda::selectedIncidence() const
223
 
{
224
 
  return ( mSelectedItem ? mSelectedItem->incidence() : 0 );
225
 
}
226
 
 
227
 
QDate KOAgenda::selectedIncidenceDate() const
228
 
{
229
 
  return ( mSelectedItem ? mSelectedItem->itemDate() : QDate() );
230
 
}
231
 
 
232
 
const QString KOAgenda::lastSelectedUid() const
233
 
{
234
 
  return mSelectedUid;
235
 
}
236
 
 
237
 
void KOAgenda::init()
238
 
{
239
 
  mGridSpacingX = 100;
240
 
 
241
 
  mResizeBorderWidth = 8;
242
 
  mScrollBorderWidth = 8;
243
 
  mScrollDelay = 30;
244
 
  mScrollOffset = 10;
245
 
 
246
 
  enableClipper( true );
247
 
 
248
 
  // Grab key strokes for keyboard navigation of agenda. Seems to have no
249
 
  // effect. Has to be fixed.
250
 
  setFocusPolicy( Qt::WheelFocus );
251
 
 
252
 
  connect( &mScrollUpTimer, SIGNAL(timeout()), SLOT(scrollUp()) );
253
 
  connect( &mScrollDownTimer, SIGNAL(timeout()), SLOT(scrollDown()) );
254
 
 
255
 
  mStartCell = QPoint( 0, 0 );
256
 
  mEndCell = QPoint( 0, 0 );
257
 
 
258
 
  mHasSelection = false;
259
 
  mSelectionStartPoint = QPoint( 0, 0 );
260
 
  mSelectionStartCell = QPoint( 0, 0 );
261
 
  mSelectionEndCell = QPoint( 0, 0 );
262
 
 
263
 
  mOldLowerScrollValue = -1;
264
 
  mOldUpperScrollValue = -1;
265
 
 
266
 
  mClickedItem = 0;
267
 
  mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
268
 
 
269
 
  mActionItem = 0;
270
 
  mActionType = NOP;
271
 
  mItemMoved = false;
272
 
 
273
 
  mSelectedItem = 0;
274
 
  mSelectedUid.clear();
275
 
 
276
 
  setAcceptDrops( true );
277
 
  installEventFilter( this );
278
 
 
279
 
  resizeContents( int( mGridSpacingX * mColumns ),
280
 
                  int( mGridSpacingY * mRows ) );
281
 
 
282
 
  viewport()->update();
283
 
  viewport()->setAttribute( Qt::WA_NoSystemBackground, true );
284
 
  viewport()->setFocusPolicy( Qt::WheelFocus );
285
 
 
286
 
  setMinimumSize( 30, int( mGridSpacingY + 1 ) );
287
 
//  setMaximumHeight( mGridSpacingY * mRows + 5 );
288
 
 
289
 
  // Disable horizontal scrollbar. This is a hack. The geometry should be
290
 
  // controlled in a way that the contents horizontally always fits. Then it is
291
 
  // not necessary to turn off the scrollbar.
292
 
  setHScrollBarMode( AlwaysOff );
293
 
 
294
 
  setStartTime( KOPrefs::instance()->mDayBegins.time() );
295
 
 
296
 
  calculateWorkingHours();
297
 
 
298
 
  connect( verticalScrollBar(), SIGNAL(valueChanged(int)),
299
 
           SLOT(checkScrollBoundaries(int)) );
300
 
 
301
 
  // Create the Marcus Bains line.
302
 
  if( mAllDayMode ) {
303
 
    mMarcusBains = 0;
304
 
  } else {
305
 
    mMarcusBains = new MarcusBains( this );
306
 
    addChild( mMarcusBains );
307
 
  }
308
 
}
309
 
 
310
 
void KOAgenda::clear()
311
 
{
312
 
  foreach ( KOAgendaItem *item, mItems ) {
313
 
    removeChild( item );
314
 
  }
315
 
  qDeleteAll( mItems );
316
 
  qDeleteAll( mItemsToDelete );
317
 
  mItems.clear();
318
 
  mItemsToDelete.clear();
319
 
 
320
 
  mSelectedItem = 0;
321
 
 
322
 
  clearSelection();
323
 
}
324
 
 
325
 
void KOAgenda::clearSelection()
326
 
{
327
 
  mHasSelection = false;
328
 
  mActionType = NOP;
329
 
  updateContents();
330
 
}
331
 
 
332
 
void KOAgenda::marcus_bains()
333
 
{
334
 
    if ( mMarcusBains ) {
335
 
      mMarcusBains->updateLocationRecalc( true );
336
 
    }
337
 
}
338
 
 
339
 
void KOAgenda::changeColumns( int columns )
340
 
{
341
 
  if ( columns == 0 ) {
342
 
    kDebug() << "called with argument 0";
343
 
    return;
344
 
  }
345
 
 
346
 
  clear();
347
 
  mColumns = columns;
348
 
//  setMinimumSize( mColumns * 10, mGridSpacingY + 1 );
349
 
//  init();
350
 
//  update();
351
 
 
352
 
  QResizeEvent event( size(), size() );
353
 
 
354
 
  QApplication::sendEvent( this, &event );
355
 
}
356
 
 
357
 
/*
358
 
  This is the eventFilter function, which gets all events from the KOAgendaItems
359
 
  contained in the agenda. It has to handle moving and resizing for all items.
360
 
*/
361
 
bool KOAgenda::eventFilter ( QObject *object, QEvent *event )
362
 
{
363
 
  switch( event->type() ) {
364
 
  case QEvent::MouseButtonPress:
365
 
  case QEvent::MouseButtonDblClick:
366
 
  case QEvent::MouseButtonRelease:
367
 
  case QEvent::MouseMove:
368
 
    return eventFilter_mouse( object, static_cast<QMouseEvent *>( event ) );
369
 
#ifndef QT_NO_WHEELEVENT
370
 
  case QEvent::Wheel:
371
 
    return eventFilter_wheel( object, static_cast<QWheelEvent *>( event ) );
372
 
#endif
373
 
  case QEvent::KeyPress:
374
 
  case QEvent::KeyRelease:
375
 
    return eventFilter_key( object, static_cast<QKeyEvent *>( event ) );
376
 
 
377
 
  case ( QEvent::Leave ):
378
 
    if ( !mActionItem ) {
379
 
      setCursor( Qt::ArrowCursor );
380
 
    }
381
 
    if ( object == viewport() ) {
382
 
      emit leaveAgenda();
383
 
    }
384
 
    return true;
385
 
 
386
 
  case QEvent::Enter:
387
 
    emit enterAgenda();
388
 
    return Q3ScrollView::eventFilter( object, event );
389
 
 
390
 
#ifndef KORG_NODND
391
 
  case QEvent::DragEnter:
392
 
  case QEvent::DragMove:
393
 
  case QEvent::DragLeave:
394
 
  case QEvent::Drop:
395
 
//  case QEvent::DragResponse:
396
 
    return eventFilter_drag( object, static_cast<QDropEvent*>( event ) );
397
 
#endif
398
 
 
399
 
  default:
400
 
    return Q3ScrollView::eventFilter( object, event );
401
 
  }
402
 
}
403
 
 
404
 
bool KOAgenda::eventFilter_drag( QObject *object, QDropEvent *de )
405
 
{
406
 
#ifndef KORG_NODND
407
 
  // FIXME: Implement dropping of events!
408
 
  QPoint viewportPos;
409
 
  if ( object != viewport() && object != this ) {
410
 
    viewportPos = static_cast<QWidget *>( object )->mapToParent( de->pos() );
411
 
  } else {
412
 
    viewportPos = de->pos();
413
 
  }
414
 
  const QMimeData *md = de->mimeData();
415
 
 
416
 
  switch ( de->type() ) {
417
 
  case QEvent::DragEnter:
418
 
  case QEvent::DragMove:
419
 
    if ( ICalDrag::canDecode( md ) || VCalDrag::canDecode( md ) ) {
420
 
 
421
 
      DndFactory factory( mCalendar );
422
 
      Todo *todo = factory.createDropTodo( de );
423
 
      if ( todo ) {
424
 
        de->accept();
425
 
        delete todo;
426
 
      } else {
427
 
        de->ignore();
428
 
      }
429
 
      return true;
430
 
    } else {
431
 
      return false;
432
 
    }
433
 
    break;
434
 
  case QEvent::DragLeave:
435
 
    return false;
436
 
    break;
437
 
  case QEvent::Drop:
438
 
  {
439
 
    if ( !ICalDrag::canDecode( md ) && !VCalDrag::canDecode( md ) ) {
440
 
      return false;
441
 
    }
442
 
 
443
 
    DndFactory factory( mCalendar );
444
 
    Todo *todo = factory.createDropTodo( de );
445
 
 
446
 
    if ( todo ) {
447
 
      de->setDropAction( Qt::MoveAction );
448
 
      QPoint pos;
449
 
      // FIXME: This is a bad hack, as the viewportToContents seems to be off by
450
 
      // 2000 (which is the left upper corner of the viewport). It works correctly
451
 
      // for agendaItems.
452
 
      if ( object == this ) {
453
 
        pos = viewportPos + QPoint( contentsX(), contentsY() );
454
 
      } else {
455
 
        pos = viewportToContents( viewportPos );
456
 
      }
457
 
      QPoint gpos = contentsToGrid( pos );
458
 
      emit droppedToDo( todo, gpos, mAllDayMode );
459
 
      return true;
460
 
    }
461
 
  }
462
 
  break;
463
 
 
464
 
  case QEvent::DragResponse:
465
 
  default:
466
 
    break;
467
 
  }
468
 
#endif
469
 
 
470
 
  return false;
471
 
}
472
 
 
473
 
#ifndef QT_NO_WHEELEVENT
474
 
bool KOAgenda::eventFilter_wheel ( QObject *object, QWheelEvent *e )
475
 
{
476
 
  QPoint viewportPos;
477
 
  bool accepted=false;
478
 
  if  ( ( e->modifiers() & Qt::ShiftModifier ) == Qt::ShiftModifier ) {
479
 
    if ( object != viewport() ) {
480
 
      viewportPos = ( (QWidget *) object )->mapToParent( e->pos() );
481
 
    } else {
482
 
      viewportPos = e->pos();
483
 
    }
484
 
    //kDebug() << type:" << e->type() << "delta:" << e->delta();
485
 
    emit zoomView( -e->delta(),
486
 
                   contentsToGrid( viewportToContents( viewportPos ) ), Qt::Horizontal );
487
 
    accepted = true;
488
 
  }
489
 
 
490
 
  if  ( ( e->modifiers() & Qt::ControlModifier ) == Qt::ControlModifier ){
491
 
    if ( object != viewport() ) {
492
 
      viewportPos = ( (QWidget *)object )->mapToParent( e->pos() );
493
 
    } else {
494
 
      viewportPos = e->pos();
495
 
    }
496
 
    emit zoomView( -e->delta(), contentsToGrid( viewportToContents( viewportPos ) ), Qt::Vertical );
497
 
    emit mousePosSignal( gridToContents( contentsToGrid( viewportToContents( viewportPos ) ) ) );
498
 
    accepted = true;
499
 
  }
500
 
  if ( accepted ) {
501
 
    e->accept();
502
 
  }
503
 
  return accepted;
504
 
}
505
 
#endif
506
 
 
507
 
bool KOAgenda::eventFilter_key( QObject *, QKeyEvent *ke )
508
 
{
509
 
  return mEventView->processKeyEvent( ke );
510
 
}
511
 
 
512
 
bool KOAgenda::eventFilter_mouse( QObject *object, QMouseEvent *me )
513
 
{
514
 
  QPoint viewportPos;
515
 
  if ( object != viewport() && object != this ) {
516
 
    viewportPos = static_cast<QWidget *>( object )->mapToParent( me->pos() );
517
 
  } else {
518
 
    viewportPos = me->pos();
519
 
  }
520
 
 
521
 
  switch ( me->type() )  {
522
 
  case QEvent::MouseButtonPress:
523
 
    if ( object != viewport() ) {
524
 
      if ( me->button() == Qt::RightButton ) {
525
 
        mClickedItem = dynamic_cast<KOAgendaItem *>( object );
526
 
        if ( mClickedItem ) {
527
 
          selectItem( mClickedItem );
528
 
          emit showIncidencePopupSignal( mCalendar,
529
 
                                         mClickedItem->incidence(),
530
 
                                         mClickedItem->itemDate() );
531
 
        }
532
 
      } else {
533
 
        KOAgendaItem *item = dynamic_cast<KOAgendaItem *>(object);
534
 
        if (item) {
535
 
          Incidence *incidence = item->incidence();
536
 
          if ( incidence->isReadOnly() ) {
537
 
            mActionItem = 0;
538
 
          } else {
539
 
            mActionItem = item;
540
 
            startItemAction( viewportPos );
541
 
          }
542
 
          // Warning: do selectItem() as late as possible, since all
543
 
          // sorts of things happen during this call. Some can lead to
544
 
          // this filter being run again and mActionItem being set to
545
 
          // null.
546
 
          selectItem( item );
547
 
        }
548
 
      }
549
 
    } else {
550
 
      if ( me->button() == Qt::RightButton ) {
551
 
        // if mouse pointer is not in selection, select the cell below the cursor
552
 
        QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
553
 
        if ( !ptInSelection( gpos ) ) {
554
 
          mSelectionStartCell = gpos;
555
 
          mSelectionEndCell = gpos;
556
 
          mHasSelection = true;
557
 
          emit newStartSelectSignal();
558
 
          emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
559
 
          updateContents();
560
 
        }
561
 
        showNewEventPopupSignal();
562
 
      } else {
563
 
        selectItem( 0 );
564
 
        mActionItem = 0;
565
 
        setCursor( Qt::ArrowCursor );
566
 
        startSelectAction( viewportPos );
567
 
      }
568
 
    }
569
 
    break;
570
 
 
571
 
  case QEvent::MouseButtonRelease:
572
 
    if ( mActionItem ) {
573
 
      endItemAction();
574
 
    } else if ( mActionType == SELECT ) {
575
 
      endSelectAction( viewportPos );
576
 
    }
577
 
    // This nasty gridToContents(contentsToGrid(..)) is needed to
578
 
    // avoid an offset of a few pixels. Don't ask me why...
579
 
    emit mousePosSignal( gridToContents( contentsToGrid( viewportToContents( viewportPos ) ) ) );
580
 
    break;
581
 
 
582
 
  case QEvent::MouseMove:
583
 
  {
584
 
    // This nasty gridToContents(contentsToGrid(..)) is needed to
585
 
    // avoid an offset of a few pixels. Don't ask me why...
586
 
    QPoint indicatorPos = gridToContents( contentsToGrid( viewportToContents( viewportPos ) ) );
587
 
    if ( object != viewport() ) {
588
 
      KOAgendaItem *moveItem = dynamic_cast<KOAgendaItem *>( object );
589
 
      if ( moveItem && moveItem->incidence() && !moveItem->incidence()->isReadOnly() ) {
590
 
        if ( !mActionItem ) {
591
 
          setNoActionCursor( moveItem, viewportPos );
592
 
        } else {
593
 
          performItemAction( viewportPos );
594
 
 
595
 
          if ( mActionType == MOVE ) {
596
 
            // show cursor at the current begin of the item
597
 
            KOAgendaItem *firstItem = mActionItem->firstMultiItem();
598
 
            if ( !firstItem ) {
599
 
              firstItem = mActionItem;
600
 
            }
601
 
            indicatorPos = gridToContents(
602
 
              QPoint( firstItem->cellXLeft(), firstItem->cellYTop() ) );
603
 
 
604
 
          } else if ( mActionType == RESIZEBOTTOM ) {
605
 
            // RESIZETOP is handled correctly, only resizebottom works differently
606
 
            indicatorPos = gridToContents(
607
 
              QPoint( mActionItem->cellXLeft(), mActionItem->cellYBottom() + 1 ) );
608
 
          }
609
 
 
610
 
        } // If we have an action item
611
 
      } // If move item && !read only
612
 
    } else {
613
 
      if ( mActionType == SELECT ) {
614
 
        performSelectAction( viewportPos );
615
 
 
616
 
        // show cursor at end of timespan
617
 
        if ( ( ( mStartCell.y() < mEndCell.y() ) && ( mEndCell.x() >= mStartCell.x() ) ) ||
618
 
             ( mEndCell.x() > mStartCell.x() ) ) {
619
 
          indicatorPos = gridToContents( QPoint( mEndCell.x(), mEndCell.y() + 1 ) );
620
 
        } else {
621
 
          indicatorPos = gridToContents( mEndCell );
622
 
        }
623
 
      }
624
 
    }
625
 
    emit mousePosSignal( indicatorPos );
626
 
    break;
627
 
  }
628
 
 
629
 
  case QEvent::MouseButtonDblClick:
630
 
    if ( object == viewport() ) {
631
 
      selectItem( 0 );
632
 
      emit newEventSignal();
633
 
    } else {
634
 
      KOAgendaItem *doubleClickedItem = dynamic_cast<KOAgendaItem *>( object );
635
 
      if ( doubleClickedItem ) {
636
 
        selectItem( doubleClickedItem );
637
 
        emit editIncidenceSignal( doubleClickedItem->incidence() );
638
 
      }
639
 
    }
640
 
    break;
641
 
 
642
 
  default:
643
 
    break;
644
 
  }
645
 
 
646
 
  return true;
647
 
}
648
 
 
649
 
bool KOAgenda::ptInSelection( const QPoint &gpos ) const
650
 
{
651
 
  if ( !mHasSelection ) {
652
 
    return false;
653
 
  } else if ( gpos.x() < mSelectionStartCell.x() || gpos.x() > mSelectionEndCell.x() ) {
654
 
    return false;
655
 
  } else if ( ( gpos.x() == mSelectionStartCell.x() ) && ( gpos.y() < mSelectionStartCell.y() ) ) {
656
 
    return false;
657
 
  } else if ( ( gpos.x() == mSelectionEndCell.x() ) && ( gpos.y() > mSelectionEndCell.y() ) ) {
658
 
    return false;
659
 
  }
660
 
  return true;
661
 
}
662
 
 
663
 
void KOAgenda::startSelectAction( const QPoint &viewportPos )
664
 
{
665
 
  emit newStartSelectSignal();
666
 
 
667
 
  mActionType = SELECT;
668
 
  mSelectionStartPoint = viewportPos;
669
 
  mHasSelection = true;
670
 
 
671
 
  QPoint pos = viewportToContents( viewportPos );
672
 
  QPoint gpos = contentsToGrid( pos );
673
 
 
674
 
  // Store new selection
675
 
  mStartCell = gpos;
676
 
  mEndCell = gpos;
677
 
  mSelectionStartCell = gpos;
678
 
  mSelectionEndCell = gpos;
679
 
 
680
 
  updateContents();
681
 
}
682
 
 
683
 
void KOAgenda::performSelectAction( const QPoint &viewportPos )
684
 
{
685
 
  QPoint pos = viewportToContents( viewportPos );
686
 
  QPoint gpos = contentsToGrid( pos );
687
 
 
688
 
  QPoint clipperPos = clipper()->mapFromGlobal( viewport()->mapToGlobal( viewportPos ) );
689
 
 
690
 
  // Scroll if cursor was moved to upper or lower end of agenda.
691
 
  if ( clipperPos.y() < mScrollBorderWidth ) {
692
 
    mScrollUpTimer.start(mScrollDelay);
693
 
  } else if ( visibleHeight() - clipperPos.y() < mScrollBorderWidth ) {
694
 
    mScrollDownTimer.start( mScrollDelay );
695
 
  } else {
696
 
    mScrollUpTimer.stop();
697
 
    mScrollDownTimer.stop();
698
 
  }
699
 
 
700
 
  if ( gpos != mEndCell ) {
701
 
    mEndCell = gpos;
702
 
    if ( mStartCell.x()>mEndCell.x() ||
703
 
         ( mStartCell.x() == mEndCell.x() && mStartCell.y() > mEndCell.y() ) ) {
704
 
      // backward selection
705
 
      mSelectionStartCell = mEndCell;
706
 
      mSelectionEndCell = mStartCell;
707
 
    } else {
708
 
      mSelectionStartCell = mStartCell;
709
 
      mSelectionEndCell = mEndCell;
710
 
    }
711
 
 
712
 
    updateContents();
713
 
  }
714
 
}
715
 
 
716
 
void KOAgenda::endSelectAction( const QPoint &currentPos )
717
 
{
718
 
  mScrollUpTimer.stop();
719
 
  mScrollDownTimer.stop();
720
 
 
721
 
  mActionType = NOP;
722
 
 
723
 
  emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
724
 
 
725
 
  if ( KOPrefs::instance()->mSelectionStartsEditor ) {
726
 
    if ( ( mSelectionStartPoint - currentPos ).manhattanLength() >
727
 
         QApplication::startDragDistance() ) {
728
 
       emit newEventSignal();
729
 
    }
730
 
  }
731
 
}
732
 
 
733
 
KOAgenda::MouseActionType KOAgenda::isInResizeArea( bool horizontal,
734
 
                                                    const QPoint &pos,
735
 
                                                    KOAgendaItem *item )
736
 
{
737
 
  if ( !item ) {
738
 
    return NOP;
739
 
  }
740
 
  QPoint gridpos = contentsToGrid( pos );
741
 
  QPoint contpos = gridToContents(
742
 
    gridpos + QPoint( ( KOGlobals::self()->reverseLayout() ) ? 1 : 0, 0 ) );
743
 
 
744
 
//kDebug() << "contpos=" << contpos << ", pos=" << pos << ", gpos=" << gpos;
745
 
//kDebug() << "clXLeft=" << clXLeft << ", clXRight=" << clXRight;
746
 
 
747
 
  if ( horizontal ) {
748
 
    int clXLeft = item->cellXLeft();
749
 
    int clXRight = item->cellXRight();
750
 
    if ( KOGlobals::self()->reverseLayout() ) {
751
 
      int tmp = clXLeft;
752
 
      clXLeft = clXRight;
753
 
      clXRight = tmp;
754
 
    }
755
 
    int gridDistanceX = int( pos.x() - contpos.x() );
756
 
    if ( gridDistanceX < mResizeBorderWidth && clXLeft == gridpos.x() ) {
757
 
      if ( KOGlobals::self()->reverseLayout() ) {
758
 
        return RESIZERIGHT;
759
 
      } else {
760
 
        return RESIZELEFT;
761
 
      }
762
 
    } else if ( ( mGridSpacingX - gridDistanceX ) < mResizeBorderWidth &&
763
 
                clXRight == gridpos.x() ) {
764
 
      if ( KOGlobals::self()->reverseLayout() ) {
765
 
        return RESIZELEFT;
766
 
      } else {
767
 
        return RESIZERIGHT;
768
 
      }
769
 
    } else {
770
 
      return MOVE;
771
 
    }
772
 
  } else {
773
 
    int gridDistanceY = int( pos.y() - contpos.y() );
774
 
    if ( gridDistanceY < mResizeBorderWidth &&
775
 
         item->cellYTop() == gridpos.y() && !item->firstMultiItem() ) {
776
 
      return RESIZETOP;
777
 
    } else if ( ( mGridSpacingY - gridDistanceY ) < mResizeBorderWidth &&
778
 
                item->cellYBottom() == gridpos.y() && !item->lastMultiItem() )  {
779
 
      return RESIZEBOTTOM;
780
 
    } else {
781
 
      return MOVE;
782
 
    }
783
 
  }
784
 
}
785
 
 
786
 
void KOAgenda::startItemAction( const QPoint &viewportPos )
787
 
{
788
 
  QPoint pos = viewportToContents( viewportPos );
789
 
  mStartCell = contentsToGrid( pos );
790
 
  mEndCell = mStartCell;
791
 
 
792
 
  bool noResize = ( mActionItem->incidence()->type() == "Todo" );
793
 
 
794
 
  mActionType = MOVE;
795
 
  if ( !noResize ) {
796
 
    mActionType = isInResizeArea( mAllDayMode, pos, mActionItem );
797
 
  }
798
 
 
799
 
  mActionItem->startMove();
800
 
  setActionCursor( mActionType, true );
801
 
}
802
 
 
803
 
void KOAgenda::performItemAction( const QPoint &viewportPos )
804
 
{
805
 
//  kDebug() << "viewportPos:" << viewportPos.x() << "," << viewportPos.y();
806
 
//  QPoint point = viewport()->mapToGlobal(viewportPos);
807
 
//  kDebug() << "Global:" << point.x() << "," << point.y();
808
 
//  point = clipper()->mapFromGlobal(point);
809
 
//  kDebug() << "clipper:" << point.x() << "," << point.y();
810
 
//  kDebug() << "visible height:" << visibleHeight();
811
 
  QPoint pos = viewportToContents( viewportPos );
812
 
//  kDebug() << "contents:" << x << "," << y;
813
 
  QPoint gpos = contentsToGrid( pos );
814
 
  QPoint clipperPos = clipper()->mapFromGlobal( viewport()->mapToGlobal( viewportPos ) );
815
 
 
816
 
  // Cursor left active agenda area.
817
 
  // This starts a drag.
818
 
  if ( clipperPos.y() < 0 || clipperPos.y() >= visibleHeight() ||
819
 
       clipperPos.x() < 0 || clipperPos.x() >= visibleWidth() ) {
820
 
    if ( mActionType == MOVE ) {
821
 
      mScrollUpTimer.stop();
822
 
      mScrollDownTimer.stop();
823
 
      mActionItem->resetMove();
824
 
      placeSubCells( mActionItem );
825
 
      emit startDragSignal( mActionItem->incidence() );
826
 
      setCursor( Qt::ArrowCursor );
827
 
      mActionItem = 0;
828
 
      mActionType = NOP;
829
 
      mItemMoved = false;
830
 
      if ( mItemMoved && mChanger ) {
831
 
        mChanger->endChange( mActionItem->incidence() );
832
 
      }
833
 
      return;
834
 
    }
835
 
  } else {
836
 
    setActionCursor( mActionType, true );
837
 
  }
838
 
 
839
 
  // Scroll if item was moved to upper or lower end of agenda.
840
 
  if ( clipperPos.y() < mScrollBorderWidth ) {
841
 
    mScrollUpTimer.start( mScrollDelay );
842
 
  } else if ( visibleHeight() - clipperPos.y() < mScrollBorderWidth ) {
843
 
    mScrollDownTimer.start( mScrollDelay );
844
 
  } else {
845
 
    mScrollUpTimer.stop();
846
 
    mScrollDownTimer.stop();
847
 
  }
848
 
 
849
 
  // Move or resize item if necessary
850
 
  if ( mEndCell != gpos ) {
851
 
    if ( !mItemMoved ) {
852
 
      if ( !mChanger || !mChanger->beginChange( mActionItem->incidence() ) ) {
853
 
        KMessageBox::information( this,
854
 
                                  i18n( "Unable to lock item for modification. "
855
 
                                        "You cannot make any changes." ),
856
 
                                  i18n( "Locking Failed" ), "AgendaLockingFailed" );
857
 
        mScrollUpTimer.stop();
858
 
        mScrollDownTimer.stop();
859
 
        mActionItem->resetMove();
860
 
        placeSubCells( mActionItem );
861
 
        setCursor( Qt::ArrowCursor );
862
 
        mActionItem = 0;
863
 
        mActionType = NOP;
864
 
        mItemMoved = false;
865
 
        return;
866
 
      }
867
 
      mItemMoved = true;
868
 
    }
869
 
    mActionItem->raise();
870
 
    if ( mActionType == MOVE ) {
871
 
      // Move all items belonging to a multi item
872
 
      KOAgendaItem *firstItem = mActionItem->firstMultiItem();
873
 
      if ( !firstItem ) {
874
 
        firstItem = mActionItem;
875
 
      }
876
 
      KOAgendaItem *lastItem = mActionItem->lastMultiItem();
877
 
      if ( !lastItem ) {
878
 
        lastItem = mActionItem;
879
 
      }
880
 
      QPoint deltapos = gpos - mEndCell;
881
 
      KOAgendaItem *moveItem = firstItem;
882
 
      while ( moveItem ) {
883
 
        bool changed = false;
884
 
        if ( deltapos.x() != 0 ) {
885
 
          moveItem->moveRelative( deltapos.x(), 0 );
886
 
          changed = true;
887
 
        }
888
 
        // in all day view don't try to move multi items, since there are none
889
 
        if ( moveItem == firstItem && !mAllDayMode ) { // is the first item
890
 
          int newY = deltapos.y() + moveItem->cellYTop();
891
 
          // If event start moved earlier than 0:00, it starts the previous day
892
 
          if ( newY < 0 ) {
893
 
            moveItem->expandTop( -moveItem->cellYTop() );
894
 
            // prepend a new item at ( x-1, rows()+newY to rows() )
895
 
            KOAgendaItem *newFirst = firstItem->prevMoveItem();
896
 
            // cell's y values are first and last cell of the bar,
897
 
            // so if newY=-1, they need to be the same
898
 
            if ( newFirst ) {
899
 
              newFirst->setCellXY( moveItem->cellXLeft() - 1, rows() + newY, rows() - 1 );
900
 
              mItems.append( newFirst );
901
 
              moveItem->resize( int( mGridSpacingX * newFirst->cellWidth() ),
902
 
                                int( mGridSpacingY * newFirst->cellHeight() ) );
903
 
              QPoint cpos = gridToContents(
904
 
                QPoint( newFirst->cellXLeft(), newFirst->cellYTop() ) );
905
 
              addChild( newFirst, cpos.x(), cpos.y() );
906
 
            } else {
907
 
              newFirst = insertItem( moveItem->incidence(), moveItem->itemDate(),
908
 
                moveItem->cellXLeft() - 1, rows() + newY, rows() - 1 ) ;
909
 
            }
910
 
            if ( newFirst ) {
911
 
              newFirst->show();
912
 
            }
913
 
            moveItem->prependMoveItem( newFirst );
914
 
            firstItem = newFirst;
915
 
          } else if ( newY >= rows() ) {
916
 
            // If event start is moved past 24:00, it starts the next day
917
 
            // erase current item (i.e. remove it from the multiItem list)
918
 
            firstItem = moveItem->nextMultiItem();
919
 
            moveItem->hide();
920
 
            mItems.removeAll( moveItem );
921
 
            removeChild( moveItem );
922
 
            mActionItem->removeMoveItem( moveItem );
923
 
            moveItem=firstItem;
924
 
            // adjust next day's item
925
 
            if ( moveItem ) {
926
 
              moveItem->expandTop( rows() - newY );
927
 
            }
928
 
          } else {
929
 
            moveItem->expandTop( deltapos.y(), true );
930
 
          }
931
 
          changed=true;
932
 
        }
933
 
        if ( moveItem && !moveItem->lastMultiItem() && !mAllDayMode ) { // is the last item
934
 
          int newY = deltapos.y() + moveItem->cellYBottom();
935
 
          if ( newY < 0 ) {
936
 
            // erase current item
937
 
            lastItem = moveItem->prevMultiItem();
938
 
            moveItem->hide();
939
 
            mItems.removeAll( moveItem );
940
 
            removeChild( moveItem );
941
 
            moveItem->removeMoveItem( moveItem );
942
 
            moveItem = lastItem;
943
 
            moveItem->expandBottom( newY + 1 );
944
 
          } else if ( newY >= rows() ) {
945
 
            moveItem->expandBottom( rows()-moveItem->cellYBottom() - 1 );
946
 
            // append item at ( x+1, 0 to newY-rows() )
947
 
            KOAgendaItem *newLast = lastItem->nextMoveItem();
948
 
            if ( newLast ) {
949
 
              newLast->setCellXY( moveItem->cellXLeft() + 1, 0, newY-rows() - 1 );
950
 
              mItems.append( newLast );
951
 
              moveItem->resize( int( mGridSpacingX * newLast->cellWidth() ),
952
 
                                int( mGridSpacingY * newLast->cellHeight() ) );
953
 
              QPoint cpos = gridToContents( QPoint( newLast->cellXLeft(), newLast->cellYTop() ) ) ;
954
 
              addChild( newLast, cpos.x(), cpos.y() );
955
 
            } else {
956
 
              newLast = insertItem( moveItem->incidence(), moveItem->itemDate(),
957
 
                moveItem->cellXLeft() + 1, 0, newY - rows() - 1 ) ;
958
 
            }
959
 
            moveItem->appendMoveItem( newLast );
960
 
            newLast->show();
961
 
            lastItem = newLast;
962
 
          } else {
963
 
            moveItem->expandBottom( deltapos.y() );
964
 
          }
965
 
          changed = true;
966
 
        }
967
 
        if ( changed ) {
968
 
          adjustItemPosition( moveItem );
969
 
        }
970
 
        if ( moveItem ) {
971
 
          moveItem = moveItem->nextMultiItem();
972
 
        }
973
 
      }
974
 
    } else if ( mActionType == RESIZETOP ) {
975
 
      if ( mEndCell.y() <= mActionItem->cellYBottom() ) {
976
 
        mActionItem->expandTop( gpos.y() - mEndCell.y() );
977
 
        adjustItemPosition( mActionItem );
978
 
      }
979
 
    } else if ( mActionType == RESIZEBOTTOM ) {
980
 
      if ( mEndCell.y() >= mActionItem->cellYTop() ) {
981
 
        mActionItem->expandBottom( gpos.y() - mEndCell.y() );
982
 
        adjustItemPosition( mActionItem );
983
 
      }
984
 
    } else if ( mActionType == RESIZELEFT ) {
985
 
      if ( mEndCell.x() <= mActionItem->cellXRight() ) {
986
 
        mActionItem->expandLeft( gpos.x() - mEndCell.x() );
987
 
        adjustItemPosition( mActionItem );
988
 
      }
989
 
    } else if ( mActionType == RESIZERIGHT ) {
990
 
      if ( mEndCell.x() >= mActionItem->cellXLeft() ) {
991
 
        mActionItem->expandRight( gpos.x() - mEndCell.x() );
992
 
        adjustItemPosition( mActionItem );
993
 
      }
994
 
    }
995
 
    mEndCell = gpos;
996
 
  }
997
 
}
998
 
 
999
 
void KOAgenda::endItemAction()
1000
 
{
1001
 
// kDebug();
1002
 
  mActionType = NOP;
1003
 
  mScrollUpTimer.stop();
1004
 
  mScrollDownTimer.stop();
1005
 
  setCursor( Qt::ArrowCursor );
1006
 
  bool multiModify = false;
1007
 
  // FIXME: do the cloning here...
1008
 
  Incidence *inc = mActionItem->incidence();
1009
 
 
1010
 
  mItemMoved = mItemMoved && !( mStartCell.x() == mEndCell.x() &&
1011
 
                                mStartCell.y() == mEndCell.y() );
1012
 
 
1013
 
  if ( mItemMoved ) {
1014
 
    bool modify = true;
1015
 
    if ( mActionItem->incidence()->recurs() ) {
1016
 
      int res = mEventView->showMoveRecurDialog( mActionItem->incidence(),
1017
 
                                                 mActionItem->itemDate() );
1018
 
      switch ( res ) {
1019
 
      case KMessageBox::Ok: // All occurrences
1020
 
        // Moving the whole sequene of events is handled by the itemModified below.
1021
 
        modify = true;
1022
 
        break;
1023
 
      case KMessageBox::Yes:
1024
 
      { // Just this occurrence
1025
 
        // Dissociate this occurrence:
1026
 
        // create clone of event, set relation to old event, set cloned event
1027
 
        // for mActionItem, add exception date to old event, changeIncidence
1028
 
        // for the old event, remove the recurrence from the new copy and then
1029
 
        // just go on with the newly adjusted mActionItem and let the usual
1030
 
        // code take care of the new time!
1031
 
        modify = true;
1032
 
        multiModify = true;
1033
 
        emit startMultiModify( i18n( "Dissociate event from recurrence" ) );
1034
 
        Incidence *oldInc = mActionItem->incidence();
1035
 
        Incidence *oldIncSaved = mActionItem->incidence()->clone();
1036
 
        Incidence *newInc = mCalendar->dissociateOccurrence(
1037
 
          oldInc, mActionItem->itemDate(), KOPrefs::instance()->timeSpec() );
1038
 
        if ( newInc ) {
1039
 
          // don't recreate items, they already have the correct position
1040
 
          emit enableAgendaUpdate( false );
1041
 
          mChanger->changeIncidence( oldIncSaved, oldInc,
1042
 
                                     KOGlobals::RECURRENCE_MODIFIED_ONE_ONLY, this );
1043
 
          mActionItem->setIncidence( newInc );
1044
 
 
1045
 
          mActionItem->dissociateFromMultiItem();
1046
 
          mChanger->addIncidence( newInc, this );
1047
 
          emit enableAgendaUpdate( true );
1048
 
        } else {
1049
 
          KMessageBox::sorry(
1050
 
            this,
1051
 
            i18n( "Unable to add the exception item to the calendar. "
1052
 
                  "No change will be done." ),
1053
 
            i18n( "Error Occurred" ) );
1054
 
        }
1055
 
        delete oldIncSaved;
1056
 
        break;
1057
 
      }
1058
 
      case KMessageBox::No/*Future*/:
1059
 
      { // All future occurrences
1060
 
        // Dissociate this occurrence:
1061
 
        // create clone of event, set relation to old event, set cloned event
1062
 
        // for mActionItem, add recurrence end date to old event, changeIncidence
1063
 
        // for the old event, adjust the recurrence for the new copy and then just
1064
 
        // go on with the newly adjusted mActionItem and let the usual code take
1065
 
        // care of the new time!
1066
 
        modify = true;
1067
 
        multiModify = true;
1068
 
        emit startMultiModify( i18n( "Split future recurrences" ) );
1069
 
        Incidence *oldInc = mActionItem->incidence();
1070
 
        Incidence *oldIncSaved = mActionItem->incidence()->clone();
1071
 
        Incidence *newInc = mCalendar->dissociateOccurrence(
1072
 
          oldInc, mActionItem->itemDate(), KOPrefs::instance()->timeSpec(), false );
1073
 
        if ( newInc ) {
1074
 
          emit enableAgendaUpdate( false );
1075
 
          mActionItem->dissociateFromMultiItem();
1076
 
          mActionItem->setIncidence( newInc );
1077
 
          mChanger->addIncidence( newInc, this );
1078
 
          emit enableAgendaUpdate( true );
1079
 
          mChanger->changeIncidence( oldIncSaved, oldInc,
1080
 
                                     KOGlobals::RECURRENCE_MODIFIED_ALL_FUTURE, this );
1081
 
        } else {
1082
 
          KMessageBox::sorry(
1083
 
            this,
1084
 
            i18n( "Unable to add the future items to the calendar. "
1085
 
                  "No change will be done." ),
1086
 
            i18n( "Error Occurred" ) );
1087
 
        }
1088
 
        delete oldIncSaved;
1089
 
        break;
1090
 
      }
1091
 
      default:
1092
 
        modify = false;
1093
 
        mActionItem->resetMove();
1094
 
        placeSubCells( mActionItem );
1095
 
      }
1096
 
    }
1097
 
 
1098
 
    if ( modify ) {
1099
 
      mActionItem->endMove();
1100
 
      KOAgendaItem *placeItem = mActionItem->firstMultiItem();
1101
 
      if  ( !placeItem ) {
1102
 
        placeItem = mActionItem;
1103
 
      }
1104
 
 
1105
 
      KOAgendaItem *modif = placeItem;
1106
 
 
1107
 
      QList<KOAgendaItem*> oldconflictItems = placeItem->conflictItems();
1108
 
      QList<KOAgendaItem*>::iterator it;
1109
 
      for ( it = oldconflictItems.begin(); it != oldconflictItems.end(); ++it ) {
1110
 
        placeSubCells( *it );
1111
 
      }
1112
 
      while ( placeItem ) {
1113
 
        placeSubCells( placeItem );
1114
 
        placeItem = placeItem->nextMultiItem();
1115
 
      }
1116
 
 
1117
 
      // Notify about change
1118
 
      // the agenda view will apply the changes to the actual Incidence*!
1119
 
      emit itemModified( modif );
1120
 
    }
1121
 
    // FIXME: If the change failed, we need to update the view!
1122
 
    mChanger->endChange( inc );
1123
 
  }
1124
 
 
1125
 
  mActionItem = 0;
1126
 
  mItemMoved = false;
1127
 
 
1128
 
  if ( multiModify ) {
1129
 
    emit endMultiModify();
1130
 
  }
1131
 
 
1132
 
  kDebug() << "done";
1133
 
}
1134
 
 
1135
 
void KOAgenda::setActionCursor( int actionType, bool acting )
1136
 
{
1137
 
  switch ( actionType ) {
1138
 
    case MOVE:
1139
 
      if ( acting ) {
1140
 
        setCursor( Qt::SizeAllCursor );
1141
 
      } else {
1142
 
        setCursor( Qt::ArrowCursor );
1143
 
      }
1144
 
      break;
1145
 
    case RESIZETOP:
1146
 
    case RESIZEBOTTOM:
1147
 
      setCursor( Qt::SizeVerCursor );
1148
 
      break;
1149
 
    case RESIZELEFT:
1150
 
    case RESIZERIGHT:
1151
 
      setCursor( Qt::SizeHorCursor );
1152
 
      break;
1153
 
    default:
1154
 
      setCursor( Qt::ArrowCursor );
1155
 
  }
1156
 
}
1157
 
 
1158
 
void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const QPoint &viewportPos )
1159
 
{
1160
 
//  kDebug() << "viewportPos:" << viewportPos.x() << "," << viewportPos.y();
1161
 
//  QPoint point = viewport()->mapToGlobal( viewportPos );
1162
 
//  kDebug() << "Global:" << point.x() << "," << point.y();
1163
 
//  point = clipper()->mapFromGlobal( point );
1164
 
//  kDebug() << "clipper:" << point.x() << "," << point.y();
1165
 
 
1166
 
  QPoint pos = viewportToContents( viewportPos );
1167
 
  bool noResize = ( moveItem && moveItem->incidence() && moveItem->incidence()->type() == "Todo" );
1168
 
 
1169
 
  KOAgenda::MouseActionType resizeType = MOVE;
1170
 
  if ( !noResize ) {
1171
 
    resizeType = isInResizeArea( mAllDayMode, pos, moveItem );
1172
 
  }
1173
 
  setActionCursor( resizeType );
1174
 
}
1175
 
 
1176
 
/** calculate the width of the column subcells of the given item
1177
 
*/
1178
 
double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
1179
 
{
1180
 
  QPoint pt, pt1;
1181
 
  pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
1182
 
  pt1 = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) + QPoint( 1, 1 ) );
1183
 
  pt1 -= pt;
1184
 
  int maxSubCells = item->subCells();
1185
 
  double newSubCellWidth;
1186
 
  if ( mAllDayMode ) {
1187
 
    newSubCellWidth = double( pt1.y() ) / maxSubCells;
1188
 
  } else {
1189
 
    newSubCellWidth = double( pt1.x() ) / maxSubCells;
1190
 
  }
1191
 
  return newSubCellWidth;
1192
 
}
1193
 
 
1194
 
void KOAgenda::adjustItemPosition( KOAgendaItem *item )
1195
 
{
1196
 
  if ( !item ) {
1197
 
    return;
1198
 
  }
1199
 
  item->resize( int( mGridSpacingX * item->cellWidth() ),
1200
 
                int( mGridSpacingY * item->cellHeight() ) );
1201
 
  int clXLeft = item->cellXLeft();
1202
 
  if ( KOGlobals::self()->reverseLayout() ) {
1203
 
    clXLeft = item->cellXRight() + 1;
1204
 
  }
1205
 
  QPoint cpos = gridToContents( QPoint( clXLeft, item->cellYTop() ) );
1206
 
  moveChild( item, cpos.x(), cpos.y() );
1207
 
}
1208
 
 
1209
 
void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
1210
 
{
1211
 
  // "left" upper corner, no subcells yet, RTL layouts have right/left
1212
 
  // switched, widths are negative then
1213
 
  QPoint pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
1214
 
  // right lower corner
1215
 
  QPoint pt1 = gridToContents(
1216
 
    QPoint( item->cellXLeft() + item->cellWidth(), item->cellYBottom() + 1 ) );
1217
 
 
1218
 
  double subCellPos = item->subCell() * subCellWidth;
1219
 
 
1220
 
  // we need to add 0.01 to make sure we don't loose one pixed due to numerics
1221
 
  // (i.e. if it would be x.9998, we want the integer, not rounded down.
1222
 
  double delta = 0.01;
1223
 
  if ( subCellWidth < 0 ) {
1224
 
    delta = -delta;
1225
 
  }
1226
 
  int height, width, xpos, ypos;
1227
 
  if ( mAllDayMode ) {
1228
 
    width = pt1.x() - pt.x();
1229
 
    height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
1230
 
    xpos = pt.x();
1231
 
    ypos = pt.y() + int( subCellPos );
1232
 
  } else {
1233
 
    width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
1234
 
    height = pt1.y() - pt.y();
1235
 
    xpos = pt.x() + int( subCellPos );
1236
 
    ypos = pt.y();
1237
 
  }
1238
 
  if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
1239
 
    xpos += width;
1240
 
    width = -width;
1241
 
  }
1242
 
  if ( height < 0 ) { // BTT (bottom-to-top) layout ?!?
1243
 
    ypos += height;
1244
 
    height = -height;
1245
 
  }
1246
 
  item->resize( width, height );
1247
 
  moveChild( item, xpos, ypos );
1248
 
}
1249
 
 
1250
 
/*
1251
 
  Place item in cell and take care that multiple items using the same cell do
1252
 
  not overlap. This method is not yet optimal. It doesn't use the maximum space
1253
 
  it can get in all cases.
1254
 
  At the moment the method has a bug: When an item is placed only the sub cell
1255
 
  widths of the items are changed, which are within the Y region the item to
1256
 
  place spans. When the sub cell width change of one of this items affects a
1257
 
  cell, where other items are, which do not overlap in Y with the item to
1258
 
  place, the display gets corrupted, although the corruption looks quite nice.
1259
 
*/
1260
 
void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
1261
 
{
1262
 
#if 0
1263
 
  kDebug();
1264
 
  if ( placeItem ) {
1265
 
    Incidence *event = placeItem->incidence();
1266
 
    if ( !event ) {
1267
 
      kDebug() << "  event is 0";
1268
 
    } else {
1269
 
      kDebug() << "  event:" << event->summary();
1270
 
    }
1271
 
  } else {
1272
 
    kDebug() << "  placeItem is 0";
1273
 
  }
1274
 
  kDebug() << "KOAgenda::placeSubCells()...";
1275
 
#endif
1276
 
 
1277
 
  QList<KOrg::CellItem*> cells;
1278
 
  foreach ( KOrg::CellItem *item, mItems ) {
1279
 
    cells.append( item );
1280
 
  }
1281
 
 
1282
 
  QList<KOrg::CellItem*> items = KOrg::CellItem::placeItem( cells, placeItem );
1283
 
 
1284
 
  placeItem->setConflictItems( QList<KOAgendaItem*>() );
1285
 
  double newSubCellWidth = calcSubCellWidth( placeItem );
1286
 
  QList<KOrg::CellItem*>::iterator it;
1287
 
  for ( it = items.begin(); it != items.end(); ++it ) {
1288
 
    KOAgendaItem *item = static_cast<KOAgendaItem *>( *it );
1289
 
    placeAgendaItem( item, newSubCellWidth );
1290
 
    item->addConflictItem( placeItem );
1291
 
    placeItem->addConflictItem( item );
1292
 
  }
1293
 
  if ( items.isEmpty() ) {
1294
 
    placeAgendaItem( placeItem, newSubCellWidth );
1295
 
  }
1296
 
  placeItem->update();
1297
 
}
1298
 
 
1299
 
int KOAgenda::columnWidth( int column ) const
1300
 
{
1301
 
  int start = gridToContents( QPoint( column, 0 ) ).x();
1302
 
  if ( KOGlobals::self()->reverseLayout() ) {
1303
 
    column--;
1304
 
  } else {
1305
 
    column++;
1306
 
  }
1307
 
  int end = gridToContents( QPoint( column, 0 ) ).x();
1308
 
  return end - start;
1309
 
}
1310
 
/*
1311
 
  Draw grid in the background of the agenda.
1312
 
*/
1313
 
void KOAgenda::drawContents( QPainter *p, int cx, int cy, int cw, int ch )
1314
 
{
1315
 
  QPixmap db( cw, ch );
1316
 
  db.fill(); // We don't want to see leftovers from previous paints
1317
 
  QPainter dbp( &db );
1318
 
  // TODO: CHECK THIS
1319
 
//  if ( ! KOPrefs::instance()->agendaGridBackgroundImage().isEmpty() ) {
1320
 
//    QPixmap bgImage( KOPrefs::instance()->agendaGridBackgroundImage() );
1321
 
//    dbp.drawPixmap( 0, 0, cw, ch, bgImage ); FIXME
1322
 
//  }
1323
 
  dbp.fillRect( 0, 0, cw, ch,
1324
 
                KOPrefs::instance()->agendaGridBackgroundColor() );
1325
 
  dbp.translate( -cx, -cy );
1326
 
 
1327
 
  double lGridSpacingY = mGridSpacingY * 2;
1328
 
 
1329
 
  // Highlight working hours
1330
 
  if ( mWorkingHoursEnable && mHolidayMask ) {
1331
 
    QPoint pt1( cx, mWorkingHoursYTop );
1332
 
    QPoint pt2( cx + cw, mWorkingHoursYBottom );
1333
 
    if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
1334
 
      int gxStart = contentsToGrid( pt1 ).x();
1335
 
      int gxEnd = contentsToGrid( pt2 ).x();
1336
 
      // correct start/end for rtl layouts
1337
 
      if ( gxStart > gxEnd ) {
1338
 
        int tmp = gxStart;
1339
 
        gxStart = gxEnd;
1340
 
        gxEnd = tmp;
1341
 
      }
1342
 
      int xoffset = ( KOGlobals::self()->reverseLayout()?1:0 );
1343
 
      while ( gxStart <= gxEnd ) {
1344
 
        int xStart = gridToContents( QPoint( gxStart + xoffset, 0 ) ).x();
1345
 
        int xWidth = columnWidth( gxStart ) + 1;
1346
 
        if ( pt2.y() < pt1.y() ) {
1347
 
          // overnight working hours
1348
 
          if ( ( ( gxStart == 0 ) && !mHolidayMask->at( mHolidayMask->count() - 1 ) ) ||
1349
 
               ( ( gxStart > 0 ) && ( gxStart < int( mHolidayMask->count() ) ) &&
1350
 
                 ( !mHolidayMask->at( gxStart - 1 ) ) ) ) {
1351
 
            if ( pt2.y() > cy ) {
1352
 
              dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1,
1353
 
                            KOPrefs::instance()->agendaGridWorkHoursBackgroundColor() );
1354
 
            }
1355
 
          }
1356
 
          if ( ( gxStart < int( mHolidayMask->count() - 1 ) ) &&
1357
 
               ( !mHolidayMask->at( gxStart ) ) ) {
1358
 
            if ( pt1.y() < cy + ch - 1 ) {
1359
 
              dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
1360
 
                            KOPrefs::instance()->agendaGridWorkHoursBackgroundColor() );
1361
 
            }
1362
 
          }
1363
 
        } else {
1364
 
          // last entry in holiday mask denotes the previous day not visible
1365
 
          // (needed for overnight shifts)
1366
 
          if ( gxStart < int( mHolidayMask->count() - 1 ) && !mHolidayMask->at( gxStart ) ) {
1367
 
            dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
1368
 
                          KOPrefs::instance()->agendaGridWorkHoursBackgroundColor() );
1369
 
          }
1370
 
        }
1371
 
        ++gxStart;
1372
 
      }
1373
 
    }
1374
 
  }
1375
 
 
1376
 
  // draw selection
1377
 
  if ( mHasSelection ) {
1378
 
    QPoint pt, pt1;
1379
 
 
1380
 
    if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
1381
 
      // draw start day
1382
 
      pt = gridToContents( mSelectionStartCell );
1383
 
      pt1 = gridToContents( QPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
1384
 
      dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->agendaGridHighlightColor() );
1385
 
      // draw all other days between the start day and the day of the selection end
1386
 
      for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
1387
 
        pt = gridToContents( QPoint( c, 0 ) );
1388
 
        pt1 = gridToContents( QPoint( c + 1, mRows + 1 ) );
1389
 
        dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->agendaGridHighlightColor() );
1390
 
      }
1391
 
      // draw end day
1392
 
      pt = gridToContents( QPoint( mSelectionEndCell.x(), 0 ) );
1393
 
      pt1 = gridToContents( mSelectionEndCell + QPoint( 1, 1 ) );
1394
 
      dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->agendaGridHighlightColor() );
1395
 
    } else { // single day selection
1396
 
      pt = gridToContents( mSelectionStartCell );
1397
 
      pt1 = gridToContents( mSelectionEndCell + QPoint( 1, 1 ) );
1398
 
      dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->agendaGridHighlightColor() );
1399
 
    }
1400
 
  }
1401
 
 
1402
 
  QPen hourPen( KOPrefs::instance()->agendaGridBackgroundColor().dark( 150 ) );
1403
 
  QPen halfHourPen( KOPrefs::instance()->agendaGridBackgroundColor().dark( 125 ) );
1404
 
  dbp.setPen( hourPen );
1405
 
 
1406
 
  // Draw vertical lines of grid, start with the last line not yet visible
1407
 
  double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
1408
 
  while ( x < cx + cw ) {
1409
 
    dbp.drawLine( int( x ), cy, int( x ), cy + ch );
1410
 
    x += mGridSpacingX;
1411
 
  }
1412
 
 
1413
 
  // Draw horizontal lines of grid
1414
 
  double y = ( int( cy / ( 2 * lGridSpacingY ) ) ) * 2 * lGridSpacingY;
1415
 
  while ( y < cy + ch ) {
1416
 
    dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
1417
 
    y += 2 * lGridSpacingY;
1418
 
  }
1419
 
  y = ( 2 * int( cy / ( 2 * lGridSpacingY ) ) + 1 ) * lGridSpacingY;
1420
 
  dbp.setPen( halfHourPen );
1421
 
  while ( y < cy + ch ) {
1422
 
    dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
1423
 
    y += 2 * lGridSpacingY;
1424
 
  }
1425
 
  p->drawPixmap( cx, cy, db );
1426
 
}
1427
 
 
1428
 
/*
1429
 
  Convert srcollview contents coordinates to agenda grid coordinates.
1430
 
*/
1431
 
QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
1432
 
{
1433
 
  int gx = int( KOGlobals::self()->reverseLayout() ?
1434
 
                mColumns - pos.x() / mGridSpacingX : pos.x()/mGridSpacingX );
1435
 
  int gy = int( pos.y() / mGridSpacingY );
1436
 
  return QPoint( gx, gy );
1437
 
}
1438
 
 
1439
 
/*
1440
 
  Convert agenda grid coordinates to scrollview contents coordinates.
1441
 
*/
1442
 
QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
1443
 
{
1444
 
  int x = int( KOGlobals::self()->reverseLayout() ?
1445
 
               ( mColumns - gpos.x() ) * mGridSpacingX : gpos.x() * mGridSpacingX );
1446
 
  int y = int( gpos.y() * mGridSpacingY );
1447
 
  return QPoint( x, y );
1448
 
}
1449
 
 
1450
 
/*
1451
 
  Return Y coordinate corresponding to time. Coordinates are rounded to
1452
 
  fit into the grid.
1453
 
*/
1454
 
int KOAgenda::timeToY( const QTime &time ) const
1455
 
{
1456
 
//  kDebug() << "Time:" << time.toString();
1457
 
  int minutesPerCell = 24 * 60 / mRows;
1458
 
//  kDebug() << "minutesPerCell:" << minutesPerCell;
1459
 
  int timeMinutes = time.hour() * 60 + time.minute();
1460
 
//  kDebug() << "timeMinutes:" << timeMinutes;
1461
 
  int Y = ( timeMinutes + ( minutesPerCell / 2 ) ) / minutesPerCell;
1462
 
//  kDebug() << "y:" << Y;
1463
 
  return Y;
1464
 
}
1465
 
 
1466
 
/*
1467
 
  Return time corresponding to cell y coordinate. Coordinates are rounded to
1468
 
  fit into the grid.
1469
 
*/
1470
 
QTime KOAgenda::gyToTime( int gy ) const
1471
 
{
1472
 
//  kDebug() << gy;
1473
 
  int secondsPerCell = 24 * 60 * 60 / mRows;
1474
 
  int timeSeconds = secondsPerCell * gy;
1475
 
 
1476
 
  QTime time( 0, 0, 0 );
1477
 
  if ( timeSeconds < 24 * 60 * 60 ) {
1478
 
    time = time.addSecs(timeSeconds);
1479
 
  } else {
1480
 
    time.setHMS( 23, 59, 59 );
1481
 
  }
1482
 
  return time;
1483
 
}
1484
 
 
1485
 
QVector<int> KOAgenda::minContentsY() const
1486
 
{
1487
 
  QVector<int> minArray;
1488
 
  minArray.fill( timeToY( QTime( 23, 59 ) ), mSelectedDates.count() );
1489
 
  foreach ( KOAgendaItem *item, mItems ) {
1490
 
    int ymin = item->cellYTop();
1491
 
    int index = item->cellXLeft();
1492
 
    if ( index >= 0 && index < (int)( mSelectedDates.count() ) ) {
1493
 
      if ( ymin < minArray[index] && !mItemsToDelete.contains( item ) ) {
1494
 
        minArray[index] = ymin;
1495
 
      }
1496
 
    }
1497
 
  }
1498
 
 
1499
 
  return minArray;
1500
 
}
1501
 
 
1502
 
QVector<int> KOAgenda::maxContentsY() const
1503
 
{
1504
 
  QVector<int> maxArray;
1505
 
  maxArray.fill( timeToY( QTime( 0, 0 ) ), mSelectedDates.count() );
1506
 
  foreach ( KOAgendaItem *item, mItems ) {
1507
 
    int ymax = item->cellYBottom();
1508
 
    int index = item->cellXLeft();
1509
 
    if ( index >= 0 && index < (int)( mSelectedDates.count() ) ) {
1510
 
      if ( ymax > maxArray[index] && !mItemsToDelete.contains( item ) ) {
1511
 
        maxArray[index] = ymax;
1512
 
      }
1513
 
    }
1514
 
  }
1515
 
 
1516
 
  return maxArray;
1517
 
}
1518
 
 
1519
 
void KOAgenda::setStartTime( const QTime &startHour )
1520
 
{
1521
 
  double startPos =
1522
 
    ( startHour.hour() / 24. + startHour.minute() / 1440. + startHour.second() / 86400. ) *
1523
 
    mRows * gridSpacingY();
1524
 
  setContentsPos( 0, int( startPos ) );
1525
 
}
1526
 
 
1527
 
/*
1528
 
  Insert KOAgendaItem into agenda.
1529
 
*/
1530
 
KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd,
1531
 
                                    int X, int YTop, int YBottom )
1532
 
{
1533
 
  if ( mAllDayMode ) {
1534
 
    kDebug() << "using this in all-day mode is illegal.";
1535
 
    return 0;
1536
 
  }
1537
 
 
1538
 
  mActionType = NOP;
1539
 
 
1540
 
  KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, incidence, qd, viewport() );
1541
 
  connect( agendaItem, SIGNAL(removeAgendaItem(KOAgendaItem *)),
1542
 
           SLOT(removeAgendaItem(KOAgendaItem *)) );
1543
 
  connect( agendaItem, SIGNAL(showAgendaItem(KOAgendaItem *)),
1544
 
           SLOT(showAgendaItem(KOAgendaItem *)) );
1545
 
 
1546
 
  if ( YBottom <= YTop ) {
1547
 
    kDebug() << "Text:" << agendaItem->text() << " YSize<0";
1548
 
    YBottom = YTop;
1549
 
  }
1550
 
 
1551
 
  agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
1552
 
                      int( X * mGridSpacingX ),
1553
 
                      int( YTop * mGridSpacingY ) -
1554
 
                      int( ( YBottom + 1 ) * mGridSpacingY ) );
1555
 
  agendaItem->setCellXY( X, YTop, YBottom );
1556
 
  agendaItem->setCellXRight( X );
1557
 
  agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
1558
 
  agendaItem->installEventFilter( this );
1559
 
 
1560
 
  addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
1561
 
  mItems.append( agendaItem );
1562
 
 
1563
 
  placeSubCells( agendaItem );
1564
 
 
1565
 
  agendaItem->show();
1566
 
 
1567
 
  marcus_bains();
1568
 
 
1569
 
  return agendaItem;
1570
 
}
1571
 
 
1572
 
/*
1573
 
  Insert all-day KOAgendaItem into agenda.
1574
 
*/
1575
 
KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
1576
 
                                          int XBegin, int XEnd )
1577
 
{
1578
 
  if ( !mAllDayMode ) {
1579
 
    kDebug() << "using this in non all-day mode is illegal.";
1580
 
    return 0;
1581
 
  }
1582
 
 
1583
 
  mActionType = NOP;
1584
 
 
1585
 
  KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, event, qd, viewport() );
1586
 
  connect( agendaItem, SIGNAL(removeAgendaItem(KOAgendaItem *)),
1587
 
           SLOT(removeAgendaItem(KOAgendaItem *)) );
1588
 
  connect( agendaItem, SIGNAL(showAgendaItem(KOAgendaItem *)),
1589
 
           SLOT(showAgendaItem(KOAgendaItem *)) );
1590
 
 
1591
 
  agendaItem->setCellXY( XBegin, 0, 0 );
1592
 
  agendaItem->setCellXRight( XEnd );
1593
 
 
1594
 
  double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
1595
 
  double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
1596
 
                                   agendaItem->cellXLeft() );
1597
 
 
1598
 
  agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
1599
 
 
1600
 
  agendaItem->installEventFilter( this );
1601
 
  agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
1602
 
  addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
1603
 
  mItems.append( agendaItem );
1604
 
 
1605
 
  placeSubCells( agendaItem );
1606
 
 
1607
 
  agendaItem->show();
1608
 
 
1609
 
  return agendaItem;
1610
 
}
1611
 
 
1612
 
void KOAgenda::insertMultiItem( Event *event, const QDate &qd, int XBegin,
1613
 
                                int XEnd, int YTop, int YBottom )
1614
 
{
1615
 
  if ( mAllDayMode ) {
1616
 
    kDebug() << "using this in all-day mode is illegal.";
1617
 
    return;
1618
 
  }
1619
 
 
1620
 
  mActionType = NOP;
1621
 
  int cellX, cellYTop, cellYBottom;
1622
 
  QString newtext;
1623
 
  int width = XEnd - XBegin + 1;
1624
 
  int count = 0;
1625
 
  KOAgendaItem *current = 0;
1626
 
  QList<KOAgendaItem*> multiItems;
1627
 
  int visibleCount = mSelectedDates.first().daysTo( mSelectedDates.last() );
1628
 
  for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
1629
 
    ++count;
1630
 
    //Only add the items that are visible.
1631
 
    if( cellX >=0 && cellX <= visibleCount ) {
1632
 
      if ( cellX == XBegin ) {
1633
 
        cellYTop = YTop;
1634
 
      } else {
1635
 
        cellYTop = 0;
1636
 
      }
1637
 
      if ( cellX == XEnd ) {
1638
 
        cellYBottom = YBottom;
1639
 
      } else {
1640
 
        cellYBottom = rows() - 1;
1641
 
      }
1642
 
      newtext = QString( "(%1/%2): " ).arg( count ).arg( width );
1643
 
      newtext.append( event->summary() );
1644
 
 
1645
 
      current = insertItem( event, qd, cellX, cellYTop, cellYBottom );
1646
 
      current->setText( newtext );
1647
 
      multiItems.append( current );
1648
 
    }
1649
 
  }
1650
 
 
1651
 
  QList<KOAgendaItem*>::iterator it = multiItems.begin();
1652
 
  QList<KOAgendaItem*>::iterator e = multiItems.end();
1653
 
 
1654
 
  if ( it != e ) { // .first asserts if the list is empty
1655
 
    KOAgendaItem *first = multiItems.first();
1656
 
    KOAgendaItem *last = multiItems.last();
1657
 
    KOAgendaItem *prev = 0, *next = 0;
1658
 
 
1659
 
    while ( it != e ) {
1660
 
      KOAgendaItem *item = *it;
1661
 
      ++it;
1662
 
      next = ( it == e ) ? 0 : (*it);
1663
 
      if ( item ) {
1664
 
        item->setMultiItem( ( item == first ) ? 0 : first,
1665
 
                            prev, next,
1666
 
                            ( item == last ) ? 0 : last );
1667
 
      }
1668
 
      prev = item;
1669
 
    }
1670
 
  }
1671
 
 
1672
 
  marcus_bains();
1673
 
}
1674
 
 
1675
 
void KOAgenda::removeIncidence( Incidence *incidence )
1676
 
{
1677
 
  // First find all items to be deleted and store them
1678
 
  // in its own list. Otherwise removeAgendaItem will reset
1679
 
  // the current position in the iterator-loop and mess the logic up.
1680
 
  QList<KOAgendaItem*> itemsToRemove;
1681
 
  KOAgendaItem *item;
1682
 
 
1683
 
  foreach ( item, mItems ) {
1684
 
    if ( item && item->incidence() == incidence ) {
1685
 
      itemsToRemove.append( item );
1686
 
    }
1687
 
  }
1688
 
 
1689
 
  foreach ( item, itemsToRemove ) {
1690
 
    removeAgendaItem( item );
1691
 
  }
1692
 
}
1693
 
 
1694
 
void KOAgenda::showAgendaItem( KOAgendaItem *agendaItem )
1695
 
{
1696
 
  if ( !agendaItem ) {
1697
 
    return;
1698
 
  }
1699
 
 
1700
 
  agendaItem->hide();
1701
 
  addChild( agendaItem );
1702
 
  if ( !mItems.contains( agendaItem ) ) {
1703
 
    mItems.append( agendaItem );
1704
 
  }
1705
 
  placeSubCells( agendaItem );
1706
 
 
1707
 
  agendaItem->show();
1708
 
}
1709
 
 
1710
 
bool KOAgenda::removeAgendaItem( KOAgendaItem *item )
1711
 
{
1712
 
  // we found the item. Let's remove it and update the conflicts
1713
 
  bool taken = false;
1714
 
  KOAgendaItem *thisItem = item;
1715
 
  QList<KOAgendaItem*> conflictItems = thisItem->conflictItems();
1716
 
  removeChild( thisItem );
1717
 
 
1718
 
  taken = ( mItems.removeAll( thisItem ) > 0 );
1719
 
 
1720
 
  QList<KOAgendaItem*>::iterator it;
1721
 
  for ( it = conflictItems.begin(); it != conflictItems.end(); ++it ) {
1722
 
      (*it)->setSubCells( ( *it )->subCells()-1 );
1723
 
  }
1724
 
 
1725
 
  for ( it = conflictItems.begin(); it != conflictItems.end(); ++it ) {
1726
 
    // the item itself is also in its own conflictItems list!
1727
 
    if ( *it != thisItem ) {
1728
 
      placeSubCells( *it );
1729
 
    }
1730
 
  }
1731
 
  mItemsToDelete.append( thisItem );
1732
 
  QTimer::singleShot( 0, this, SLOT(deleteItemsToDelete()) );
1733
 
  return taken;
1734
 
}
1735
 
 
1736
 
void KOAgenda::deleteItemsToDelete()
1737
 
{
1738
 
  qDeleteAll( mItemsToDelete );
1739
 
  mItemsToDelete.clear();
1740
 
}
1741
 
 
1742
 
/*QSizePolicy KOAgenda::sizePolicy() const
1743
 
{
1744
 
  // Thought this would make the all-day event agenda minimum size and the
1745
 
  // normal agenda take the remaining space. But it doesn't work. The QSplitter
1746
 
  // don't seem to think that an Expanding widget needs more space than a
1747
 
  // Preferred one.
1748
 
  // But it doesn't hurt, so it stays.
1749
 
  if (mAllDayMode) {
1750
 
    return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
1751
 
  } else {
1752
 
    return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
1753
 
  }
1754
 
}
1755
 
*/
1756
 
 
1757
 
/*
1758
 
  Overridden from QScrollView to provide proper resizing of KOAgendaItems.
1759
 
*/
1760
 
void KOAgenda::resizeEvent ( QResizeEvent *ev )
1761
 
{
1762
 
//  kDebug();
1763
 
 
1764
 
  QSize newSize( ev->size() );
1765
 
  if (mAllDayMode) {
1766
 
    mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
1767
 
    mGridSpacingY = newSize.height() - 2 * frameWidth();
1768
 
  } else {
1769
 
    int scrollbarWidth = vScrollBarMode() != AlwaysOff ? verticalScrollBar()->width() : 0;
1770
 
    mGridSpacingX = double(
1771
 
      newSize.width() - scrollbarWidth - 2 * frameWidth() ) / double( mColumns );
1772
 
    // make sure that there are not more than 24 per day
1773
 
    mGridSpacingY = double(
1774
 
      newSize.height() - 2 * frameWidth() ) / double( mRows );
1775
 
    if ( mGridSpacingY < mDesiredGridSpacingY ) {
1776
 
      mGridSpacingY = mDesiredGridSpacingY;
1777
 
    }
1778
 
  }
1779
 
  calculateWorkingHours();
1780
 
  QTimer::singleShot( 0, this, SLOT(resizeAllContents()) );
1781
 
  emit gridSpacingYChanged( mGridSpacingY * 4 );
1782
 
  Q3ScrollView::resizeEvent(ev);
1783
 
}
1784
 
 
1785
 
void KOAgenda::resizeAllContents()
1786
 
{
1787
 
  double subCellWidth;
1788
 
  KOAgendaItem *item;
1789
 
  if ( mAllDayMode ) {
1790
 
    foreach ( item, mItems ) {
1791
 
      subCellWidth = calcSubCellWidth( item );
1792
 
      placeAgendaItem( item, subCellWidth );
1793
 
    }
1794
 
  } else {
1795
 
    foreach ( item, mItems ) {
1796
 
      subCellWidth = calcSubCellWidth( item );
1797
 
      placeAgendaItem( item, subCellWidth );
1798
 
    }
1799
 
  }
1800
 
  checkScrollBoundaries();
1801
 
  marcus_bains();
1802
 
}
1803
 
 
1804
 
void KOAgenda::scrollUp()
1805
 
{
1806
 
  scrollBy( 0, -mScrollOffset );
1807
 
}
1808
 
 
1809
 
void KOAgenda::scrollDown()
1810
 
{
1811
 
  scrollBy( 0, mScrollOffset );
1812
 
}
1813
 
 
1814
 
/*
1815
 
  Calculates the minimum width
1816
 
*/
1817
 
int KOAgenda::minimumWidth() const
1818
 
{
1819
 
  // FIXME:: develop a way to dynamically determine the minimum width
1820
 
  int min = 100;
1821
 
 
1822
 
  return min;
1823
 
}
1824
 
 
1825
 
void KOAgenda::updateConfig()
1826
 
{
1827
 
  double oldGridSpacingY = mGridSpacingY;
1828
 
  mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
1829
 
 // make sure that there are not more than 24 per day
1830
 
  mGridSpacingY = (double)height() / (double)mRows;
1831
 
  if ( mGridSpacingY<mDesiredGridSpacingY ) {
1832
 
    mGridSpacingY = mDesiredGridSpacingY;
1833
 
  }
1834
 
 
1835
 
  //can be two doubles equal?, it's better to compare them with an epsilon
1836
 
  if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 ) {
1837
 
    resizeContents( int( mGridSpacingX * mColumns ), int( mGridSpacingY * mRows ) );
1838
 
  }
1839
 
 
1840
 
  calculateWorkingHours();
1841
 
 
1842
 
  marcus_bains();
1843
 
}
1844
 
 
1845
 
void KOAgenda::checkScrollBoundaries()
1846
 
{
1847
 
  // Invalidate old values to force update
1848
 
  mOldLowerScrollValue = -1;
1849
 
  mOldUpperScrollValue = -1;
1850
 
 
1851
 
  checkScrollBoundaries( verticalScrollBar()->value() );
1852
 
}
1853
 
 
1854
 
void KOAgenda::checkScrollBoundaries( int v )
1855
 
{
1856
 
  int yMin = int( (v) / mGridSpacingY );
1857
 
  int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
1858
 
 
1859
 
//  kDebug() << "--- yMin:" << yMin << "  yMax:" << yMax;
1860
 
 
1861
 
  if ( yMin != mOldLowerScrollValue ) {
1862
 
    mOldLowerScrollValue = yMin;
1863
 
    emit lowerYChanged( yMin );
1864
 
  }
1865
 
  if ( yMax != mOldUpperScrollValue ) {
1866
 
    mOldUpperScrollValue = yMax;
1867
 
    emit upperYChanged( yMax );
1868
 
  }
1869
 
}
1870
 
 
1871
 
int KOAgenda::visibleContentsYMin()
1872
 
{
1873
 
  int v = verticalScrollBar()->value();
1874
 
  return int( v / mGridSpacingY );
1875
 
}
1876
 
 
1877
 
int KOAgenda::visibleContentsYMax()
1878
 
{
1879
 
  int v = verticalScrollBar()->value();
1880
 
  return int( ( v + visibleHeight() ) / mGridSpacingY );
1881
 
}
1882
 
 
1883
 
void KOAgenda::deselectItem()
1884
 
{
1885
 
  if ( mSelectedItem.isNull() ) {
1886
 
    return;
1887
 
  }
1888
 
 
1889
 
  Incidence *selectedInc = mSelectedItem->incidence();
1890
 
 
1891
 
  foreach ( KOAgendaItem *item, mItems ) {
1892
 
    Incidence *itemInc = item->incidence();
1893
 
    if( itemInc && selectedInc && itemInc->uid() == selectedInc->uid() ) {
1894
 
      item->select( false );
1895
 
    }
1896
 
  }
1897
 
 
1898
 
  mSelectedItem = 0;
1899
 
}
1900
 
 
1901
 
void KOAgenda::selectItem( KOAgendaItem *item )
1902
 
{
1903
 
  if ( (KOAgendaItem *)mSelectedItem == item ) {
1904
 
    return;
1905
 
  }
1906
 
  deselectItem();
1907
 
  if ( item == 0 ) {
1908
 
    emit incidenceSelected( 0, QDate() );
1909
 
    return;
1910
 
  }
1911
 
  mSelectedItem = item;
1912
 
  mSelectedItem->select();
1913
 
  Q_ASSERT( mSelectedItem->incidence() );
1914
 
  mSelectedUid = mSelectedItem->incidence()->uid();
1915
 
 
1916
 
  foreach ( KOAgendaItem *item, mItems ) {
1917
 
    if( item->incidence() && item->incidence()->uid() == mSelectedUid ) {
1918
 
      item->select();
1919
 
    }
1920
 
  }
1921
 
  emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
1922
 
}
1923
 
 
1924
 
void KOAgenda::selectItemByUID( const QString &uid )
1925
 
{
1926
 
  foreach ( KOAgendaItem *item, mItems ) {
1927
 
    if( item->incidence() && item->incidence()->uid() == uid ) {
1928
 
      selectItem( item );
1929
 
      break;
1930
 
    }
1931
 
  }
1932
 
}
1933
 
 
1934
 
// This function seems never be called.
1935
 
void KOAgenda::keyPressEvent( QKeyEvent *kev )
1936
 
{
1937
 
  switch( kev->key() ) {
1938
 
  case Qt::Key_PageDown:
1939
 
    verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepAdd );
1940
 
    break;
1941
 
  case Qt::Key_PageUp:
1942
 
    verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
1943
 
    break;
1944
 
  case Qt::Key_Down:
1945
 
    verticalScrollBar()->triggerAction( QAbstractSlider::SliderSingleStepAdd );
1946
 
    break;
1947
 
  case Qt::Key_Up:
1948
 
    verticalScrollBar()->triggerAction( QAbstractSlider::SliderSingleStepSub );
1949
 
    break;
1950
 
  default:
1951
 
    ;
1952
 
  }
1953
 
}
1954
 
 
1955
 
void KOAgenda::calculateWorkingHours()
1956
 
{
1957
 
  mWorkingHoursEnable = !mAllDayMode;
1958
 
 
1959
 
  QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
1960
 
  mWorkingHoursYTop = int( 4 * mGridSpacingY *
1961
 
                           ( tmp.hour() + tmp.minute() / 60. +
1962
 
                             tmp.second() / 3600. ) );
1963
 
  tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
1964
 
  mWorkingHoursYBottom = int( 4 * mGridSpacingY *
1965
 
                              ( tmp.hour() + tmp.minute() / 60. +
1966
 
                                tmp.second() / 3600. ) - 1 );
1967
 
}
1968
 
 
1969
 
DateList KOAgenda::dateList() const
1970
 
{
1971
 
    return mSelectedDates;
1972
 
}
1973
 
 
1974
 
void KOAgenda::setDateList( const DateList &selectedDates )
1975
 
{
1976
 
    mSelectedDates = selectedDates;
1977
 
    marcus_bains();
1978
 
}
1979
 
 
1980
 
void KOAgenda::setHolidayMask( QVector<bool> *mask )
1981
 
{
1982
 
  mHolidayMask = mask;
1983
 
}
1984
 
 
1985
 
void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
1986
 
{
1987
 
  Q3ScrollView::contentsMousePressEvent( event );
1988
 
}
1989
 
 
1990
 
#include "koagenda.moc"