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>
7
Copyright (c) 2001 Ali Rahimi <ali@mit.edu>
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.
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.
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.
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.
28
#include "koagendaitem.h"
30
#include "koglobals.h"
31
#include "komessagebox.h"
32
#include "incidencechanger.h"
34
#include "korganizer/baseview.h"
36
#include <KCal/Calendar>
37
#include <KCal/DndFactory>
38
#include <KCal/ICalDrag>
40
#include <KCal/VCalDrag>
45
#include <KMessageBox>
48
#include <QApplication>
52
#include <QWheelEvent>
60
#include <QResizeEvent>
61
#include <QMouseEvent>
65
///////////////////////////////////////////////////////////////////////////////
66
MarcusBains::MarcusBains( KOAgenda *agenda )
67
: QFrame( agenda->viewport() ), mAgenda( agenda )
69
mTimeBox = new QLabel( this );
70
mTimeBox->setAlignment( Qt::AlignRight | Qt::AlignBottom );
71
mAgenda->addChild( mTimeBox );
73
mTimer = new QTimer( this );
74
mTimer->setSingleShot( true );
75
connect( mTimer, SIGNAL(timeout()), this, SLOT(updateLocation()) );
81
MarcusBains::~MarcusBains()
85
int MarcusBains::todayColumn()
87
QDate currentDate = QDate::currentDate();
89
DateList dateList = mAgenda->dateList();
90
DateList::ConstIterator it;
92
for ( it = dateList.constBegin(); it != dateList.constEnd(); ++it ) {
93
if ( (*it) == currentDate ) {
94
return KOGlobals::self()->reverseLayout() ? mAgenda->columns() - 1 - col : col;
102
void MarcusBains::updateLocation()
104
updateLocationRecalc();
107
void MarcusBains::updateLocationRecalc( bool recalculate )
109
bool showSeconds = KOPrefs::instance()->marcusBainsShowSeconds();
110
QColor color = KOPrefs::instance()->agendaMarcusBainsLineLineColor();
112
QTime tim = QTime::currentTime();
113
if ( ( tim.hour() == 0 ) && ( mOldTime.hour() == 23 ) ) {
114
// We are on a new day
117
int todayCol = recalculate ? todayColumn() : mOldTodayCol;
119
// Number of minutes since beginning of the day
120
int minutes = tim.hour() * 60 + tim.minute();
121
int minutesPerCell = 24 * 60 / mAgenda->rows();
124
mOldTodayCol = todayCol;
126
int y = int( minutes * mAgenda->gridSpacingY() / minutesPerCell );
127
int x = int( mAgenda->gridSpacingX() * todayCol );
129
if ( !( KOPrefs::instance()->marcusBainsEnabled() ) || ( todayCol < 0 ) ) {
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
148
setFixedSize( int( mAgenda->gridSpacingX() ), 1 );
150
mAgenda->moveChild( this, x, y );
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();
165
if ( x - mTimeBox->width() + mAgenda->gridSpacingX() > 0 ) {
166
x += int( mAgenda->gridSpacingX() - mTimeBox->width() - 1 );
170
mAgenda->moveChild( mTimeBox, x, y );
173
if ( showSeconds || recalculate ) {
174
mTimer->start( 1000 );
176
mTimer->start( 1000 * ( 60 - tim.second() ) );
180
////////////////////////////////////////////////////////////////////////////
183
Create an agenda widget with rows rows and columns columns.
185
KOAgenda::KOAgenda( KOEventView *eventView, int columns, int rows, int rowSize, QWidget *parent,
187
: Q3ScrollView( parent, /*name*/0, f ), mHolidayMask( 0 ), mChanger( 0 )
191
mGridSpacingY = rowSize;
193
mEventView = eventView;
197
viewport()->setMouseTracking( true );
201
Create an agenda widget with columns columns and one row. This is used for
204
KOAgenda::KOAgenda( KOEventView *eventView, int columns, QWidget *parent, Qt::WFlags f )
205
: Q3ScrollView( parent, /*name*/0, f ), mHolidayMask( 0 ), mChanger( 0 )
211
setVScrollBarMode( AlwaysOff );
212
mEventView = eventView;
217
KOAgenda::~KOAgenda()
222
Incidence *KOAgenda::selectedIncidence() const
224
return ( mSelectedItem ? mSelectedItem->incidence() : 0 );
227
QDate KOAgenda::selectedIncidenceDate() const
229
return ( mSelectedItem ? mSelectedItem->itemDate() : QDate() );
232
const QString KOAgenda::lastSelectedUid() const
237
void KOAgenda::init()
241
mResizeBorderWidth = 8;
242
mScrollBorderWidth = 8;
246
enableClipper( true );
248
// Grab key strokes for keyboard navigation of agenda. Seems to have no
249
// effect. Has to be fixed.
250
setFocusPolicy( Qt::WheelFocus );
252
connect( &mScrollUpTimer, SIGNAL(timeout()), SLOT(scrollUp()) );
253
connect( &mScrollDownTimer, SIGNAL(timeout()), SLOT(scrollDown()) );
255
mStartCell = QPoint( 0, 0 );
256
mEndCell = QPoint( 0, 0 );
258
mHasSelection = false;
259
mSelectionStartPoint = QPoint( 0, 0 );
260
mSelectionStartCell = QPoint( 0, 0 );
261
mSelectionEndCell = QPoint( 0, 0 );
263
mOldLowerScrollValue = -1;
264
mOldUpperScrollValue = -1;
267
mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
274
mSelectedUid.clear();
276
setAcceptDrops( true );
277
installEventFilter( this );
279
resizeContents( int( mGridSpacingX * mColumns ),
280
int( mGridSpacingY * mRows ) );
282
viewport()->update();
283
viewport()->setAttribute( Qt::WA_NoSystemBackground, true );
284
viewport()->setFocusPolicy( Qt::WheelFocus );
286
setMinimumSize( 30, int( mGridSpacingY + 1 ) );
287
// setMaximumHeight( mGridSpacingY * mRows + 5 );
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 );
294
setStartTime( KOPrefs::instance()->mDayBegins.time() );
296
calculateWorkingHours();
298
connect( verticalScrollBar(), SIGNAL(valueChanged(int)),
299
SLOT(checkScrollBoundaries(int)) );
301
// Create the Marcus Bains line.
305
mMarcusBains = new MarcusBains( this );
306
addChild( mMarcusBains );
310
void KOAgenda::clear()
312
foreach ( KOAgendaItem *item, mItems ) {
315
qDeleteAll( mItems );
316
qDeleteAll( mItemsToDelete );
318
mItemsToDelete.clear();
325
void KOAgenda::clearSelection()
327
mHasSelection = false;
332
void KOAgenda::marcus_bains()
334
if ( mMarcusBains ) {
335
mMarcusBains->updateLocationRecalc( true );
339
void KOAgenda::changeColumns( int columns )
341
if ( columns == 0 ) {
342
kDebug() << "called with argument 0";
348
// setMinimumSize( mColumns * 10, mGridSpacingY + 1 );
352
QResizeEvent event( size(), size() );
354
QApplication::sendEvent( this, &event );
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.
361
bool KOAgenda::eventFilter ( QObject *object, QEvent *event )
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
371
return eventFilter_wheel( object, static_cast<QWheelEvent *>( event ) );
373
case QEvent::KeyPress:
374
case QEvent::KeyRelease:
375
return eventFilter_key( object, static_cast<QKeyEvent *>( event ) );
377
case ( QEvent::Leave ):
378
if ( !mActionItem ) {
379
setCursor( Qt::ArrowCursor );
381
if ( object == viewport() ) {
388
return Q3ScrollView::eventFilter( object, event );
391
case QEvent::DragEnter:
392
case QEvent::DragMove:
393
case QEvent::DragLeave:
395
// case QEvent::DragResponse:
396
return eventFilter_drag( object, static_cast<QDropEvent*>( event ) );
400
return Q3ScrollView::eventFilter( object, event );
404
bool KOAgenda::eventFilter_drag( QObject *object, QDropEvent *de )
407
// FIXME: Implement dropping of events!
409
if ( object != viewport() && object != this ) {
410
viewportPos = static_cast<QWidget *>( object )->mapToParent( de->pos() );
412
viewportPos = de->pos();
414
const QMimeData *md = de->mimeData();
416
switch ( de->type() ) {
417
case QEvent::DragEnter:
418
case QEvent::DragMove:
419
if ( ICalDrag::canDecode( md ) || VCalDrag::canDecode( md ) ) {
421
DndFactory factory( mCalendar );
422
Todo *todo = factory.createDropTodo( de );
434
case QEvent::DragLeave:
439
if ( !ICalDrag::canDecode( md ) && !VCalDrag::canDecode( md ) ) {
443
DndFactory factory( mCalendar );
444
Todo *todo = factory.createDropTodo( de );
447
de->setDropAction( Qt::MoveAction );
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
452
if ( object == this ) {
453
pos = viewportPos + QPoint( contentsX(), contentsY() );
455
pos = viewportToContents( viewportPos );
457
QPoint gpos = contentsToGrid( pos );
458
emit droppedToDo( todo, gpos, mAllDayMode );
464
case QEvent::DragResponse:
473
#ifndef QT_NO_WHEELEVENT
474
bool KOAgenda::eventFilter_wheel ( QObject *object, QWheelEvent *e )
478
if ( ( e->modifiers() & Qt::ShiftModifier ) == Qt::ShiftModifier ) {
479
if ( object != viewport() ) {
480
viewportPos = ( (QWidget *) object )->mapToParent( e->pos() );
482
viewportPos = e->pos();
484
//kDebug() << type:" << e->type() << "delta:" << e->delta();
485
emit zoomView( -e->delta(),
486
contentsToGrid( viewportToContents( viewportPos ) ), Qt::Horizontal );
490
if ( ( e->modifiers() & Qt::ControlModifier ) == Qt::ControlModifier ){
491
if ( object != viewport() ) {
492
viewportPos = ( (QWidget *)object )->mapToParent( e->pos() );
494
viewportPos = e->pos();
496
emit zoomView( -e->delta(), contentsToGrid( viewportToContents( viewportPos ) ), Qt::Vertical );
497
emit mousePosSignal( gridToContents( contentsToGrid( viewportToContents( viewportPos ) ) ) );
507
bool KOAgenda::eventFilter_key( QObject *, QKeyEvent *ke )
509
return mEventView->processKeyEvent( ke );
512
bool KOAgenda::eventFilter_mouse( QObject *object, QMouseEvent *me )
515
if ( object != viewport() && object != this ) {
516
viewportPos = static_cast<QWidget *>( object )->mapToParent( me->pos() );
518
viewportPos = me->pos();
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() );
533
KOAgendaItem *item = dynamic_cast<KOAgendaItem *>(object);
535
Incidence *incidence = item->incidence();
536
if ( incidence->isReadOnly() ) {
540
startItemAction( viewportPos );
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
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 );
561
showNewEventPopupSignal();
565
setCursor( Qt::ArrowCursor );
566
startSelectAction( viewportPos );
571
case QEvent::MouseButtonRelease:
574
} else if ( mActionType == SELECT ) {
575
endSelectAction( viewportPos );
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 ) ) ) );
582
case QEvent::MouseMove:
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 );
593
performItemAction( viewportPos );
595
if ( mActionType == MOVE ) {
596
// show cursor at the current begin of the item
597
KOAgendaItem *firstItem = mActionItem->firstMultiItem();
599
firstItem = mActionItem;
601
indicatorPos = gridToContents(
602
QPoint( firstItem->cellXLeft(), firstItem->cellYTop() ) );
604
} else if ( mActionType == RESIZEBOTTOM ) {
605
// RESIZETOP is handled correctly, only resizebottom works differently
606
indicatorPos = gridToContents(
607
QPoint( mActionItem->cellXLeft(), mActionItem->cellYBottom() + 1 ) );
610
} // If we have an action item
611
} // If move item && !read only
613
if ( mActionType == SELECT ) {
614
performSelectAction( viewportPos );
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 ) );
621
indicatorPos = gridToContents( mEndCell );
625
emit mousePosSignal( indicatorPos );
629
case QEvent::MouseButtonDblClick:
630
if ( object == viewport() ) {
632
emit newEventSignal();
634
KOAgendaItem *doubleClickedItem = dynamic_cast<KOAgendaItem *>( object );
635
if ( doubleClickedItem ) {
636
selectItem( doubleClickedItem );
637
emit editIncidenceSignal( doubleClickedItem->incidence() );
649
bool KOAgenda::ptInSelection( const QPoint &gpos ) const
651
if ( !mHasSelection ) {
653
} else if ( gpos.x() < mSelectionStartCell.x() || gpos.x() > mSelectionEndCell.x() ) {
655
} else if ( ( gpos.x() == mSelectionStartCell.x() ) && ( gpos.y() < mSelectionStartCell.y() ) ) {
657
} else if ( ( gpos.x() == mSelectionEndCell.x() ) && ( gpos.y() > mSelectionEndCell.y() ) ) {
663
void KOAgenda::startSelectAction( const QPoint &viewportPos )
665
emit newStartSelectSignal();
667
mActionType = SELECT;
668
mSelectionStartPoint = viewportPos;
669
mHasSelection = true;
671
QPoint pos = viewportToContents( viewportPos );
672
QPoint gpos = contentsToGrid( pos );
674
// Store new selection
677
mSelectionStartCell = gpos;
678
mSelectionEndCell = gpos;
683
void KOAgenda::performSelectAction( const QPoint &viewportPos )
685
QPoint pos = viewportToContents( viewportPos );
686
QPoint gpos = contentsToGrid( pos );
688
QPoint clipperPos = clipper()->mapFromGlobal( viewport()->mapToGlobal( viewportPos ) );
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 );
696
mScrollUpTimer.stop();
697
mScrollDownTimer.stop();
700
if ( gpos != mEndCell ) {
702
if ( mStartCell.x()>mEndCell.x() ||
703
( mStartCell.x() == mEndCell.x() && mStartCell.y() > mEndCell.y() ) ) {
704
// backward selection
705
mSelectionStartCell = mEndCell;
706
mSelectionEndCell = mStartCell;
708
mSelectionStartCell = mStartCell;
709
mSelectionEndCell = mEndCell;
716
void KOAgenda::endSelectAction( const QPoint ¤tPos )
718
mScrollUpTimer.stop();
719
mScrollDownTimer.stop();
723
emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
725
if ( KOPrefs::instance()->mSelectionStartsEditor ) {
726
if ( ( mSelectionStartPoint - currentPos ).manhattanLength() >
727
QApplication::startDragDistance() ) {
728
emit newEventSignal();
733
KOAgenda::MouseActionType KOAgenda::isInResizeArea( bool horizontal,
740
QPoint gridpos = contentsToGrid( pos );
741
QPoint contpos = gridToContents(
742
gridpos + QPoint( ( KOGlobals::self()->reverseLayout() ) ? 1 : 0, 0 ) );
744
//kDebug() << "contpos=" << contpos << ", pos=" << pos << ", gpos=" << gpos;
745
//kDebug() << "clXLeft=" << clXLeft << ", clXRight=" << clXRight;
748
int clXLeft = item->cellXLeft();
749
int clXRight = item->cellXRight();
750
if ( KOGlobals::self()->reverseLayout() ) {
755
int gridDistanceX = int( pos.x() - contpos.x() );
756
if ( gridDistanceX < mResizeBorderWidth && clXLeft == gridpos.x() ) {
757
if ( KOGlobals::self()->reverseLayout() ) {
762
} else if ( ( mGridSpacingX - gridDistanceX ) < mResizeBorderWidth &&
763
clXRight == gridpos.x() ) {
764
if ( KOGlobals::self()->reverseLayout() ) {
773
int gridDistanceY = int( pos.y() - contpos.y() );
774
if ( gridDistanceY < mResizeBorderWidth &&
775
item->cellYTop() == gridpos.y() && !item->firstMultiItem() ) {
777
} else if ( ( mGridSpacingY - gridDistanceY ) < mResizeBorderWidth &&
778
item->cellYBottom() == gridpos.y() && !item->lastMultiItem() ) {
786
void KOAgenda::startItemAction( const QPoint &viewportPos )
788
QPoint pos = viewportToContents( viewportPos );
789
mStartCell = contentsToGrid( pos );
790
mEndCell = mStartCell;
792
bool noResize = ( mActionItem->incidence()->type() == "Todo" );
796
mActionType = isInResizeArea( mAllDayMode, pos, mActionItem );
799
mActionItem->startMove();
800
setActionCursor( mActionType, true );
803
void KOAgenda::performItemAction( const QPoint &viewportPos )
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 ) );
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 );
830
if ( mItemMoved && mChanger ) {
831
mChanger->endChange( mActionItem->incidence() );
836
setActionCursor( mActionType, true );
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 );
845
mScrollUpTimer.stop();
846
mScrollDownTimer.stop();
849
// Move or resize item if necessary
850
if ( mEndCell != gpos ) {
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 );
869
mActionItem->raise();
870
if ( mActionType == MOVE ) {
871
// Move all items belonging to a multi item
872
KOAgendaItem *firstItem = mActionItem->firstMultiItem();
874
firstItem = mActionItem;
876
KOAgendaItem *lastItem = mActionItem->lastMultiItem();
878
lastItem = mActionItem;
880
QPoint deltapos = gpos - mEndCell;
881
KOAgendaItem *moveItem = firstItem;
883
bool changed = false;
884
if ( deltapos.x() != 0 ) {
885
moveItem->moveRelative( deltapos.x(), 0 );
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
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
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() );
907
newFirst = insertItem( moveItem->incidence(), moveItem->itemDate(),
908
moveItem->cellXLeft() - 1, rows() + newY, rows() - 1 ) ;
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();
920
mItems.removeAll( moveItem );
921
removeChild( moveItem );
922
mActionItem->removeMoveItem( moveItem );
924
// adjust next day's item
926
moveItem->expandTop( rows() - newY );
929
moveItem->expandTop( deltapos.y(), true );
933
if ( moveItem && !moveItem->lastMultiItem() && !mAllDayMode ) { // is the last item
934
int newY = deltapos.y() + moveItem->cellYBottom();
936
// erase current item
937
lastItem = moveItem->prevMultiItem();
939
mItems.removeAll( moveItem );
940
removeChild( moveItem );
941
moveItem->removeMoveItem( moveItem );
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();
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() );
956
newLast = insertItem( moveItem->incidence(), moveItem->itemDate(),
957
moveItem->cellXLeft() + 1, 0, newY - rows() - 1 ) ;
959
moveItem->appendMoveItem( newLast );
963
moveItem->expandBottom( deltapos.y() );
968
adjustItemPosition( moveItem );
971
moveItem = moveItem->nextMultiItem();
974
} else if ( mActionType == RESIZETOP ) {
975
if ( mEndCell.y() <= mActionItem->cellYBottom() ) {
976
mActionItem->expandTop( gpos.y() - mEndCell.y() );
977
adjustItemPosition( mActionItem );
979
} else if ( mActionType == RESIZEBOTTOM ) {
980
if ( mEndCell.y() >= mActionItem->cellYTop() ) {
981
mActionItem->expandBottom( gpos.y() - mEndCell.y() );
982
adjustItemPosition( mActionItem );
984
} else if ( mActionType == RESIZELEFT ) {
985
if ( mEndCell.x() <= mActionItem->cellXRight() ) {
986
mActionItem->expandLeft( gpos.x() - mEndCell.x() );
987
adjustItemPosition( mActionItem );
989
} else if ( mActionType == RESIZERIGHT ) {
990
if ( mEndCell.x() >= mActionItem->cellXLeft() ) {
991
mActionItem->expandRight( gpos.x() - mEndCell.x() );
992
adjustItemPosition( mActionItem );
999
void KOAgenda::endItemAction()
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();
1010
mItemMoved = mItemMoved && !( mStartCell.x() == mEndCell.x() &&
1011
mStartCell.y() == mEndCell.y() );
1015
if ( mActionItem->incidence()->recurs() ) {
1016
int res = mEventView->showMoveRecurDialog( mActionItem->incidence(),
1017
mActionItem->itemDate() );
1019
case KMessageBox::Ok: // All occurrences
1020
// Moving the whole sequene of events is handled by the itemModified below.
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!
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() );
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 );
1045
mActionItem->dissociateFromMultiItem();
1046
mChanger->addIncidence( newInc, this );
1047
emit enableAgendaUpdate( true );
1051
i18n( "Unable to add the exception item to the calendar. "
1052
"No change will be done." ),
1053
i18n( "Error Occurred" ) );
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!
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 );
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 );
1084
i18n( "Unable to add the future items to the calendar. "
1085
"No change will be done." ),
1086
i18n( "Error Occurred" ) );
1093
mActionItem->resetMove();
1094
placeSubCells( mActionItem );
1099
mActionItem->endMove();
1100
KOAgendaItem *placeItem = mActionItem->firstMultiItem();
1102
placeItem = mActionItem;
1105
KOAgendaItem *modif = placeItem;
1107
QList<KOAgendaItem*> oldconflictItems = placeItem->conflictItems();
1108
QList<KOAgendaItem*>::iterator it;
1109
for ( it = oldconflictItems.begin(); it != oldconflictItems.end(); ++it ) {
1110
placeSubCells( *it );
1112
while ( placeItem ) {
1113
placeSubCells( placeItem );
1114
placeItem = placeItem->nextMultiItem();
1117
// Notify about change
1118
// the agenda view will apply the changes to the actual Incidence*!
1119
emit itemModified( modif );
1121
// FIXME: If the change failed, we need to update the view!
1122
mChanger->endChange( inc );
1128
if ( multiModify ) {
1129
emit endMultiModify();
1135
void KOAgenda::setActionCursor( int actionType, bool acting )
1137
switch ( actionType ) {
1140
setCursor( Qt::SizeAllCursor );
1142
setCursor( Qt::ArrowCursor );
1147
setCursor( Qt::SizeVerCursor );
1151
setCursor( Qt::SizeHorCursor );
1154
setCursor( Qt::ArrowCursor );
1158
void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const QPoint &viewportPos )
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();
1166
QPoint pos = viewportToContents( viewportPos );
1167
bool noResize = ( moveItem && moveItem->incidence() && moveItem->incidence()->type() == "Todo" );
1169
KOAgenda::MouseActionType resizeType = MOVE;
1171
resizeType = isInResizeArea( mAllDayMode, pos, moveItem );
1173
setActionCursor( resizeType );
1176
/** calculate the width of the column subcells of the given item
1178
double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
1181
pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
1182
pt1 = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) + QPoint( 1, 1 ) );
1184
int maxSubCells = item->subCells();
1185
double newSubCellWidth;
1186
if ( mAllDayMode ) {
1187
newSubCellWidth = double( pt1.y() ) / maxSubCells;
1189
newSubCellWidth = double( pt1.x() ) / maxSubCells;
1191
return newSubCellWidth;
1194
void KOAgenda::adjustItemPosition( KOAgendaItem *item )
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;
1205
QPoint cpos = gridToContents( QPoint( clXLeft, item->cellYTop() ) );
1206
moveChild( item, cpos.x(), cpos.y() );
1209
void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
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 ) );
1218
double subCellPos = item->subCell() * subCellWidth;
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 ) {
1226
int height, width, xpos, ypos;
1227
if ( mAllDayMode ) {
1228
width = pt1.x() - pt.x();
1229
height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
1231
ypos = pt.y() + int( subCellPos );
1233
width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
1234
height = pt1.y() - pt.y();
1235
xpos = pt.x() + int( subCellPos );
1238
if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
1242
if ( height < 0 ) { // BTT (bottom-to-top) layout ?!?
1246
item->resize( width, height );
1247
moveChild( item, xpos, ypos );
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.
1260
void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
1265
Incidence *event = placeItem->incidence();
1267
kDebug() << " event is 0";
1269
kDebug() << " event:" << event->summary();
1272
kDebug() << " placeItem is 0";
1274
kDebug() << "KOAgenda::placeSubCells()...";
1277
QList<KOrg::CellItem*> cells;
1278
foreach ( KOrg::CellItem *item, mItems ) {
1279
cells.append( item );
1282
QList<KOrg::CellItem*> items = KOrg::CellItem::placeItem( cells, placeItem );
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 );
1293
if ( items.isEmpty() ) {
1294
placeAgendaItem( placeItem, newSubCellWidth );
1296
placeItem->update();
1299
int KOAgenda::columnWidth( int column ) const
1301
int start = gridToContents( QPoint( column, 0 ) ).x();
1302
if ( KOGlobals::self()->reverseLayout() ) {
1307
int end = gridToContents( QPoint( column, 0 ) ).x();
1311
Draw grid in the background of the agenda.
1313
void KOAgenda::drawContents( QPainter *p, int cx, int cy, int cw, int ch )
1315
QPixmap db( cw, ch );
1316
db.fill(); // We don't want to see leftovers from previous paints
1317
QPainter dbp( &db );
1319
// if ( ! KOPrefs::instance()->agendaGridBackgroundImage().isEmpty() ) {
1320
// QPixmap bgImage( KOPrefs::instance()->agendaGridBackgroundImage() );
1321
// dbp.drawPixmap( 0, 0, cw, ch, bgImage ); FIXME
1323
dbp.fillRect( 0, 0, cw, ch,
1324
KOPrefs::instance()->agendaGridBackgroundColor() );
1325
dbp.translate( -cx, -cy );
1327
double lGridSpacingY = mGridSpacingY * 2;
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 ) {
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() );
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() );
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() );
1377
if ( mHasSelection ) {
1380
if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
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() );
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() );
1402
QPen hourPen( KOPrefs::instance()->agendaGridBackgroundColor().dark( 150 ) );
1403
QPen halfHourPen( KOPrefs::instance()->agendaGridBackgroundColor().dark( 125 ) );
1404
dbp.setPen( hourPen );
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 );
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;
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;
1425
p->drawPixmap( cx, cy, db );
1429
Convert srcollview contents coordinates to agenda grid coordinates.
1431
QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
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 );
1440
Convert agenda grid coordinates to scrollview contents coordinates.
1442
QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
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 );
1451
Return Y coordinate corresponding to time. Coordinates are rounded to
1454
int KOAgenda::timeToY( const QTime &time ) const
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;
1467
Return time corresponding to cell y coordinate. Coordinates are rounded to
1470
QTime KOAgenda::gyToTime( int gy ) const
1473
int secondsPerCell = 24 * 60 * 60 / mRows;
1474
int timeSeconds = secondsPerCell * gy;
1476
QTime time( 0, 0, 0 );
1477
if ( timeSeconds < 24 * 60 * 60 ) {
1478
time = time.addSecs(timeSeconds);
1480
time.setHMS( 23, 59, 59 );
1485
QVector<int> KOAgenda::minContentsY() const
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;
1502
QVector<int> KOAgenda::maxContentsY() const
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;
1519
void KOAgenda::setStartTime( const QTime &startHour )
1522
( startHour.hour() / 24. + startHour.minute() / 1440. + startHour.second() / 86400. ) *
1523
mRows * gridSpacingY();
1524
setContentsPos( 0, int( startPos ) );
1528
Insert KOAgendaItem into agenda.
1530
KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd,
1531
int X, int YTop, int YBottom )
1533
if ( mAllDayMode ) {
1534
kDebug() << "using this in all-day mode is illegal.";
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 *)) );
1546
if ( YBottom <= YTop ) {
1547
kDebug() << "Text:" << agendaItem->text() << " YSize<0";
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 );
1560
addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
1561
mItems.append( agendaItem );
1563
placeSubCells( agendaItem );
1573
Insert all-day KOAgendaItem into agenda.
1575
KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
1576
int XBegin, int XEnd )
1578
if ( !mAllDayMode ) {
1579
kDebug() << "using this in non all-day mode is illegal.";
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 *)) );
1591
agendaItem->setCellXY( XBegin, 0, 0 );
1592
agendaItem->setCellXRight( XEnd );
1594
double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
1595
double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
1596
agendaItem->cellXLeft() );
1598
agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
1600
agendaItem->installEventFilter( this );
1601
agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
1602
addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
1603
mItems.append( agendaItem );
1605
placeSubCells( agendaItem );
1612
void KOAgenda::insertMultiItem( Event *event, const QDate &qd, int XBegin,
1613
int XEnd, int YTop, int YBottom )
1615
if ( mAllDayMode ) {
1616
kDebug() << "using this in all-day mode is illegal.";
1621
int cellX, cellYTop, cellYBottom;
1623
int width = XEnd - XBegin + 1;
1625
KOAgendaItem *current = 0;
1626
QList<KOAgendaItem*> multiItems;
1627
int visibleCount = mSelectedDates.first().daysTo( mSelectedDates.last() );
1628
for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
1630
//Only add the items that are visible.
1631
if( cellX >=0 && cellX <= visibleCount ) {
1632
if ( cellX == XBegin ) {
1637
if ( cellX == XEnd ) {
1638
cellYBottom = YBottom;
1640
cellYBottom = rows() - 1;
1642
newtext = QString( "(%1/%2): " ).arg( count ).arg( width );
1643
newtext.append( event->summary() );
1645
current = insertItem( event, qd, cellX, cellYTop, cellYBottom );
1646
current->setText( newtext );
1647
multiItems.append( current );
1651
QList<KOAgendaItem*>::iterator it = multiItems.begin();
1652
QList<KOAgendaItem*>::iterator e = multiItems.end();
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;
1660
KOAgendaItem *item = *it;
1662
next = ( it == e ) ? 0 : (*it);
1664
item->setMultiItem( ( item == first ) ? 0 : first,
1666
( item == last ) ? 0 : last );
1675
void KOAgenda::removeIncidence( Incidence *incidence )
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;
1683
foreach ( item, mItems ) {
1684
if ( item && item->incidence() == incidence ) {
1685
itemsToRemove.append( item );
1689
foreach ( item, itemsToRemove ) {
1690
removeAgendaItem( item );
1694
void KOAgenda::showAgendaItem( KOAgendaItem *agendaItem )
1696
if ( !agendaItem ) {
1701
addChild( agendaItem );
1702
if ( !mItems.contains( agendaItem ) ) {
1703
mItems.append( agendaItem );
1705
placeSubCells( agendaItem );
1710
bool KOAgenda::removeAgendaItem( KOAgendaItem *item )
1712
// we found the item. Let's remove it and update the conflicts
1714
KOAgendaItem *thisItem = item;
1715
QList<KOAgendaItem*> conflictItems = thisItem->conflictItems();
1716
removeChild( thisItem );
1718
taken = ( mItems.removeAll( thisItem ) > 0 );
1720
QList<KOAgendaItem*>::iterator it;
1721
for ( it = conflictItems.begin(); it != conflictItems.end(); ++it ) {
1722
(*it)->setSubCells( ( *it )->subCells()-1 );
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 );
1731
mItemsToDelete.append( thisItem );
1732
QTimer::singleShot( 0, this, SLOT(deleteItemsToDelete()) );
1736
void KOAgenda::deleteItemsToDelete()
1738
qDeleteAll( mItemsToDelete );
1739
mItemsToDelete.clear();
1742
/*QSizePolicy KOAgenda::sizePolicy() const
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
1748
// But it doesn't hurt, so it stays.
1750
return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
1752
return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
1758
Overridden from QScrollView to provide proper resizing of KOAgendaItems.
1760
void KOAgenda::resizeEvent ( QResizeEvent *ev )
1764
QSize newSize( ev->size() );
1766
mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
1767
mGridSpacingY = newSize.height() - 2 * frameWidth();
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;
1779
calculateWorkingHours();
1780
QTimer::singleShot( 0, this, SLOT(resizeAllContents()) );
1781
emit gridSpacingYChanged( mGridSpacingY * 4 );
1782
Q3ScrollView::resizeEvent(ev);
1785
void KOAgenda::resizeAllContents()
1787
double subCellWidth;
1789
if ( mAllDayMode ) {
1790
foreach ( item, mItems ) {
1791
subCellWidth = calcSubCellWidth( item );
1792
placeAgendaItem( item, subCellWidth );
1795
foreach ( item, mItems ) {
1796
subCellWidth = calcSubCellWidth( item );
1797
placeAgendaItem( item, subCellWidth );
1800
checkScrollBoundaries();
1804
void KOAgenda::scrollUp()
1806
scrollBy( 0, -mScrollOffset );
1809
void KOAgenda::scrollDown()
1811
scrollBy( 0, mScrollOffset );
1815
Calculates the minimum width
1817
int KOAgenda::minimumWidth() const
1819
// FIXME:: develop a way to dynamically determine the minimum width
1825
void KOAgenda::updateConfig()
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;
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 ) );
1840
calculateWorkingHours();
1845
void KOAgenda::checkScrollBoundaries()
1847
// Invalidate old values to force update
1848
mOldLowerScrollValue = -1;
1849
mOldUpperScrollValue = -1;
1851
checkScrollBoundaries( verticalScrollBar()->value() );
1854
void KOAgenda::checkScrollBoundaries( int v )
1856
int yMin = int( (v) / mGridSpacingY );
1857
int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
1859
// kDebug() << "--- yMin:" << yMin << " yMax:" << yMax;
1861
if ( yMin != mOldLowerScrollValue ) {
1862
mOldLowerScrollValue = yMin;
1863
emit lowerYChanged( yMin );
1865
if ( yMax != mOldUpperScrollValue ) {
1866
mOldUpperScrollValue = yMax;
1867
emit upperYChanged( yMax );
1871
int KOAgenda::visibleContentsYMin()
1873
int v = verticalScrollBar()->value();
1874
return int( v / mGridSpacingY );
1877
int KOAgenda::visibleContentsYMax()
1879
int v = verticalScrollBar()->value();
1880
return int( ( v + visibleHeight() ) / mGridSpacingY );
1883
void KOAgenda::deselectItem()
1885
if ( mSelectedItem.isNull() ) {
1889
Incidence *selectedInc = mSelectedItem->incidence();
1891
foreach ( KOAgendaItem *item, mItems ) {
1892
Incidence *itemInc = item->incidence();
1893
if( itemInc && selectedInc && itemInc->uid() == selectedInc->uid() ) {
1894
item->select( false );
1901
void KOAgenda::selectItem( KOAgendaItem *item )
1903
if ( (KOAgendaItem *)mSelectedItem == item ) {
1908
emit incidenceSelected( 0, QDate() );
1911
mSelectedItem = item;
1912
mSelectedItem->select();
1913
Q_ASSERT( mSelectedItem->incidence() );
1914
mSelectedUid = mSelectedItem->incidence()->uid();
1916
foreach ( KOAgendaItem *item, mItems ) {
1917
if( item->incidence() && item->incidence()->uid() == mSelectedUid ) {
1921
emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
1924
void KOAgenda::selectItemByUID( const QString &uid )
1926
foreach ( KOAgendaItem *item, mItems ) {
1927
if( item->incidence() && item->incidence()->uid() == uid ) {
1934
// This function seems never be called.
1935
void KOAgenda::keyPressEvent( QKeyEvent *kev )
1937
switch( kev->key() ) {
1938
case Qt::Key_PageDown:
1939
verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepAdd );
1941
case Qt::Key_PageUp:
1942
verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
1945
verticalScrollBar()->triggerAction( QAbstractSlider::SliderSingleStepAdd );
1948
verticalScrollBar()->triggerAction( QAbstractSlider::SliderSingleStepSub );
1955
void KOAgenda::calculateWorkingHours()
1957
mWorkingHoursEnable = !mAllDayMode;
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 );
1969
DateList KOAgenda::dateList() const
1971
return mSelectedDates;
1974
void KOAgenda::setDateList( const DateList &selectedDates )
1976
mSelectedDates = selectedDates;
1980
void KOAgenda::setHolidayMask( QVector<bool> *mask )
1982
mHolidayMask = mask;
1985
void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
1987
Q3ScrollView::contentsMousePressEvent( event );
1990
#include "koagenda.moc"