You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2000 lines
61 KiB
2000 lines
61 KiB
/*
|
|
This file is part of KOrganizer.
|
|
Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
|
|
Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
|
|
|
|
Marcus Bains line.
|
|
Copyright (c) 2001 Ali Rahimi
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
As a special exception, permission is given to link this program
|
|
with any edition of Qt, and distribute the resulting executable,
|
|
without including the source code for Qt in the source distribution.
|
|
*/
|
|
#include <assert.h>
|
|
|
|
#include <qintdict.h>
|
|
#include <qdatetime.h>
|
|
#include <qapplication.h>
|
|
#include <qpopupmenu.h>
|
|
#include <qcursor.h>
|
|
#include <qpainter.h>
|
|
#include <qlabel.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
#include <kiconloader.h>
|
|
#include <kglobal.h>
|
|
#include <kmessagebox.h>
|
|
|
|
#include "koagendaitem.h"
|
|
#include "koprefs.h"
|
|
#include "koglobals.h"
|
|
#include "komessagebox.h"
|
|
#include "incidencechanger.h"
|
|
#include "kohelper.h"
|
|
|
|
#include "koagenda.h"
|
|
#include "koagenda.moc"
|
|
#include <korganizer/baseview.h>
|
|
|
|
#include <libkcal/event.h>
|
|
#include <libkcal/todo.h>
|
|
#include <libkcal/dndfactory.h>
|
|
#include <libkcal/icaldrag.h>
|
|
#include <libkcal/vcaldrag.h>
|
|
#include <libkcal/calendar.h>
|
|
#include <libkcal/calendarresources.h>
|
|
#include <math.h>
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
MarcusBains::MarcusBains(KOAgenda *_agenda,const char *name)
|
|
: QFrame(_agenda->viewport(),name), agenda(_agenda)
|
|
{
|
|
setLineWidth(0);
|
|
setMargin(0);
|
|
setBackgroundColor(Qt::red);
|
|
minutes = new QTimer(this);
|
|
connect(minutes, SIGNAL(timeout()), this, SLOT(updateLocation()));
|
|
minutes->start(0, true);
|
|
|
|
mTimeBox = new QLabel(this);
|
|
mTimeBox->setAlignment(Qt::AlignRight | Qt::AlignBottom);
|
|
QPalette pal = mTimeBox->palette();
|
|
pal.setColor(QColorGroup::Foreground, Qt::red);
|
|
mTimeBox->setPalette(pal);
|
|
mTimeBox->setAutoMask(true);
|
|
|
|
agenda->addChild(mTimeBox);
|
|
|
|
oldToday = -1;
|
|
}
|
|
|
|
MarcusBains::~MarcusBains()
|
|
{
|
|
delete minutes;
|
|
}
|
|
|
|
int MarcusBains::todayColumn()
|
|
{
|
|
QDate currentDate = QDate::currentDate();
|
|
|
|
DateList dateList = agenda->dateList();
|
|
DateList::ConstIterator it;
|
|
int col = 0;
|
|
for(it = dateList.begin(); it != dateList.end(); ++it) {
|
|
if((*it) == currentDate)
|
|
return KOGlobals::self()->reverseLayout() ?
|
|
agenda->columns() - 1 - col : col;
|
|
++col;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void MarcusBains::updateLocation(bool recalculate)
|
|
{
|
|
QTime tim = QTime::currentTime();
|
|
if((tim.hour() == 0) && (oldTime.hour()==23))
|
|
recalculate = true;
|
|
|
|
int mins = tim.hour()*60 + tim.minute();
|
|
int minutesPerCell = 24 * 60 / agenda->rows();
|
|
int y = int( mins * agenda->gridSpacingY() / minutesPerCell );
|
|
int today = recalculate ? todayColumn() : oldToday;
|
|
int x = int( agenda->gridSpacingX() * today );
|
|
bool disabled = !(KOPrefs::instance()->mMarcusBainsEnabled);
|
|
|
|
oldTime = tim;
|
|
oldToday = today;
|
|
|
|
if(disabled || (today<0)) {
|
|
hide();
|
|
mTimeBox->hide();
|
|
return;
|
|
} else {
|
|
show();
|
|
mTimeBox->show();
|
|
}
|
|
|
|
if ( recalculate ) setFixedSize( int( agenda->gridSpacingX() ), 1 );
|
|
agenda->moveChild( this, x, y );
|
|
raise();
|
|
|
|
if(recalculate)
|
|
mTimeBox->setFont(KOPrefs::instance()->mMarcusBainsFont);
|
|
|
|
mTimeBox->setText(KGlobal::locale()->formatTime(tim, KOPrefs::instance()->mMarcusBainsShowSeconds));
|
|
mTimeBox->adjustSize();
|
|
if (y-mTimeBox->height()>=0) y-=mTimeBox->height(); else y++;
|
|
if (x-mTimeBox->width()+agenda->gridSpacingX() > 0)
|
|
x += int( agenda->gridSpacingX() - mTimeBox->width() - 1 );
|
|
else x++;
|
|
agenda->moveChild(mTimeBox,x,y);
|
|
mTimeBox->raise();
|
|
mTimeBox->setAutoMask(true);
|
|
|
|
minutes->start(1000,true);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/*
|
|
Create an agenda widget with rows rows and columns columns.
|
|
*/
|
|
KOAgenda::KOAgenda( int columns, int rows, int rowSize, QWidget *parent,
|
|
const char *name, WFlags f )
|
|
: QScrollView( parent, name, f ), mChanger( 0 )
|
|
{
|
|
mColumns = columns;
|
|
mRows = rows;
|
|
mGridSpacingY = rowSize;
|
|
mAllDayMode = false;
|
|
|
|
init();
|
|
|
|
viewport()->setMouseTracking(true);
|
|
}
|
|
|
|
/*
|
|
Create an agenda widget with columns columns and one row. This is used for
|
|
all-day events.
|
|
*/
|
|
KOAgenda::KOAgenda( int columns, QWidget *parent, const char *name, WFlags f )
|
|
: QScrollView( parent, name, f )
|
|
{
|
|
mColumns = columns;
|
|
mRows = 1;
|
|
mGridSpacingY = 24;
|
|
mAllDayMode = true;
|
|
setVScrollBarMode( AlwaysOff );
|
|
|
|
init();
|
|
}
|
|
|
|
|
|
KOAgenda::~KOAgenda()
|
|
{
|
|
delete mMarcusBains;
|
|
}
|
|
|
|
|
|
Incidence *KOAgenda::selectedIncidence() const
|
|
{
|
|
return ( mSelectedItem ? mSelectedItem->incidence() : 0 );
|
|
}
|
|
|
|
|
|
QDate KOAgenda::selectedIncidenceDate() const
|
|
{
|
|
return ( mSelectedItem ? mSelectedItem->itemDate() : QDate() );
|
|
}
|
|
|
|
const QString KOAgenda::lastSelectedUid() const
|
|
{
|
|
return mSelectedUid;
|
|
}
|
|
|
|
|
|
void KOAgenda::init()
|
|
{
|
|
mGridSpacingX = 100;
|
|
|
|
mResizeBorderWidth = 8;
|
|
mScrollBorderWidth = 8;
|
|
mScrollDelay = 30;
|
|
mScrollOffset = 10;
|
|
|
|
enableClipper( true );
|
|
|
|
// Grab key strokes for keyboard navigation of agenda. Seems to have no
|
|
// effect. Has to be fixed.
|
|
setFocusPolicy( WheelFocus );
|
|
|
|
connect( &mScrollUpTimer, SIGNAL( timeout() ), SLOT( scrollUp() ) );
|
|
connect( &mScrollDownTimer, SIGNAL( timeout() ), SLOT( scrollDown() ) );
|
|
|
|
mStartCell = QPoint( 0, 0 );
|
|
mEndCell = QPoint( 0, 0 );
|
|
|
|
mHasSelection = false;
|
|
mSelectionStartPoint = QPoint( 0, 0 );
|
|
mSelectionStartCell = QPoint( 0, 0 );
|
|
mSelectionEndCell = QPoint( 0, 0 );
|
|
|
|
mOldLowerScrollValue = -1;
|
|
mOldUpperScrollValue = -1;
|
|
|
|
mClickedItem = 0;
|
|
|
|
mActionItem = 0;
|
|
mActionType = NOP;
|
|
mItemMoved = false;
|
|
|
|
mSelectedItem = 0;
|
|
mSelectedUid = QString::null;
|
|
|
|
setAcceptDrops( true );
|
|
installEventFilter( this );
|
|
mItems.setAutoDelete( true );
|
|
mItemsToDelete.setAutoDelete( true );
|
|
|
|
resizeContents( int( mGridSpacingX * mColumns ),
|
|
int( mGridSpacingY * mRows ) );
|
|
|
|
viewport()->update();
|
|
viewport()->setBackgroundMode( NoBackground );
|
|
viewport()->setFocusPolicy( WheelFocus );
|
|
|
|
setMinimumSize( 30, int( mGridSpacingY + 1 ) );
|
|
// setMaximumHeight(mGridSpacingY * mRows + 5);
|
|
|
|
// Disable horizontal scrollbar. This is a hack. The geometry should be
|
|
// controlled in a way that the contents horizontally always fits. Then it is
|
|
// not necessary to turn off the scrollbar.
|
|
setHScrollBarMode( AlwaysOff );
|
|
|
|
setStartTime( KOPrefs::instance()->mDayBegins.time() );
|
|
|
|
calculateWorkingHours();
|
|
|
|
connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ),
|
|
SLOT( checkScrollBoundaries( int ) ) );
|
|
|
|
// Create the Marcus Bains line.
|
|
if( mAllDayMode ) {
|
|
mMarcusBains = 0;
|
|
} else {
|
|
mMarcusBains = new MarcusBains( this );
|
|
addChild( mMarcusBains );
|
|
}
|
|
|
|
mTypeAhead = false;
|
|
mTypeAheadReceiver = 0;
|
|
|
|
mReturnPressed = false;
|
|
}
|
|
|
|
|
|
void KOAgenda::clear()
|
|
{
|
|
// kdDebug(5850) << "KOAgenda::clear()" << endl;
|
|
|
|
KOAgendaItem *item;
|
|
for ( item = mItems.first(); item != 0; item = mItems.next() ) {
|
|
removeChild( item );
|
|
}
|
|
mItems.clear();
|
|
mItemsToDelete.clear();
|
|
|
|
mSelectedItem = 0;
|
|
|
|
clearSelection();
|
|
}
|
|
|
|
|
|
void KOAgenda::clearSelection()
|
|
{
|
|
mHasSelection = false;
|
|
mActionType = NOP;
|
|
updateContents();
|
|
}
|
|
|
|
void KOAgenda::marcus_bains()
|
|
{
|
|
if(mMarcusBains) mMarcusBains->updateLocation(true);
|
|
}
|
|
|
|
|
|
void KOAgenda::changeColumns(int columns)
|
|
{
|
|
if (columns == 0) {
|
|
kdDebug(5850) << "KOAgenda::changeColumns() called with argument 0" << endl;
|
|
return;
|
|
}
|
|
|
|
clear();
|
|
mColumns = columns;
|
|
// setMinimumSize(mColumns * 10, mGridSpacingY + 1);
|
|
// init();
|
|
// update();
|
|
|
|
QResizeEvent event( size(), size() );
|
|
|
|
QApplication::sendEvent( this, &event );
|
|
}
|
|
|
|
/*
|
|
This is the eventFilter function, which gets all events from the KOAgendaItems
|
|
contained in the agenda. It has to handle moving and resizing for all items.
|
|
*/
|
|
bool KOAgenda::eventFilter ( QObject *object, QEvent *event )
|
|
{
|
|
// kdDebug(5850) << "KOAgenda::eventFilter() " << int( event->type() ) << endl;
|
|
|
|
switch( event->type() ) {
|
|
case QEvent::MouseButtonPress:
|
|
case QEvent::MouseButtonDblClick:
|
|
case QEvent::MouseButtonRelease:
|
|
case QEvent::MouseMove:
|
|
return eventFilter_mouse( object, static_cast<QMouseEvent *>( event ) );
|
|
#ifndef QT_NO_WHEELEVENT
|
|
case QEvent::Wheel:
|
|
return eventFilter_wheel( object, static_cast<QWheelEvent *>( event ) );
|
|
#endif
|
|
case QEvent::KeyPress:
|
|
case QEvent::KeyRelease:
|
|
return eventFilter_key( object, static_cast<QKeyEvent *>( event ) );
|
|
|
|
case ( QEvent::Leave ):
|
|
if ( !mActionItem )
|
|
setCursor( arrowCursor );
|
|
if ( object == viewport() )
|
|
emit leaveAgenda();
|
|
return true;
|
|
|
|
case QEvent::Enter:
|
|
emit enterAgenda();
|
|
return QScrollView::eventFilter( object, event );
|
|
|
|
#ifndef KORG_NODND
|
|
case QEvent::DragEnter:
|
|
case QEvent::DragMove:
|
|
case QEvent::DragLeave:
|
|
case QEvent::Drop:
|
|
// case QEvent::DragResponse:
|
|
return eventFilter_drag(object, static_cast<QDropEvent*>(event));
|
|
#endif
|
|
|
|
default:
|
|
return QScrollView::eventFilter( object, event );
|
|
}
|
|
}
|
|
|
|
bool KOAgenda::eventFilter_drag( QObject *object, QDropEvent *de )
|
|
{
|
|
#ifndef KORG_NODND
|
|
QPoint viewportPos;
|
|
if ( object != viewport() && object != this ) {
|
|
viewportPos = static_cast<QWidget *>( object )->mapToParent( de->pos() );
|
|
} else {
|
|
viewportPos = de->pos();
|
|
}
|
|
|
|
switch ( de->type() ) {
|
|
case QEvent::DragEnter:
|
|
case QEvent::DragMove:
|
|
if ( ICalDrag::canDecode( de ) || VCalDrag::canDecode( de ) ) {
|
|
|
|
DndFactory factory( mCalendar );
|
|
Todo *todo = factory.createDropTodo( de );
|
|
if ( todo ) {
|
|
de->accept();
|
|
delete todo;
|
|
} else {
|
|
de->ignore();
|
|
}
|
|
return true;
|
|
} else return false;
|
|
break;
|
|
case QEvent::DragLeave:
|
|
return false;
|
|
break;
|
|
case QEvent::Drop:
|
|
{
|
|
if ( !ICalDrag::canDecode( de ) && !VCalDrag::canDecode( de ) ) {
|
|
return false;
|
|
}
|
|
|
|
DndFactory factory( mCalendar );
|
|
Todo *todo = factory.createDropTodo( de );
|
|
|
|
if ( todo ) {
|
|
de->acceptAction();
|
|
QPoint pos;
|
|
// FIXME: This is a bad hack, as the viewportToContents seems to be off by
|
|
// 2000 (which is the left upper corner of the viewport). It works correctly
|
|
// for agendaItems.
|
|
if ( object == this ) {
|
|
pos = viewportPos + QPoint( contentsX(), contentsY() );
|
|
} else {
|
|
pos = viewportToContents( viewportPos );
|
|
}
|
|
QPoint gpos = contentsToGrid( pos );
|
|
emit droppedToDo( todo, gpos, mAllDayMode );
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QEvent::DragResponse:
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KOAgenda::eventFilter_key( QObject *, QKeyEvent *ke )
|
|
{
|
|
// kdDebug(5850) << "KOAgenda::eventFilter_key() " << ke->type() << endl;
|
|
|
|
// If Return is pressed bring up an editor for the current selected time span.
|
|
if ( ke->key() == Key_Return ) {
|
|
if ( ke->type() == QEvent::KeyPress ) mReturnPressed = true;
|
|
else if ( ke->type() == QEvent::KeyRelease ) {
|
|
if ( mReturnPressed ) {
|
|
emitNewEventForSelection();
|
|
mReturnPressed = false;
|
|
return true;
|
|
} else {
|
|
mReturnPressed = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ignore all input that does not produce any output
|
|
if ( ke->text().isEmpty() ) return false;
|
|
|
|
if ( ke->type() == QEvent::KeyPress || ke->type() == QEvent::KeyRelease ) {
|
|
switch ( ke->key() ) {
|
|
case Key_Escape:
|
|
case Key_Return:
|
|
case Key_Enter:
|
|
case Key_Tab:
|
|
case Key_Backtab:
|
|
case Key_Left:
|
|
case Key_Right:
|
|
case Key_Up:
|
|
case Key_Down:
|
|
case Key_Backspace:
|
|
case Key_Delete:
|
|
case Key_Prior:
|
|
case Key_Next:
|
|
case Key_Home:
|
|
case Key_End:
|
|
case Key_Control:
|
|
case Key_Meta:
|
|
case Key_Alt:
|
|
break;
|
|
default:
|
|
mTypeAheadEvents.append( new QKeyEvent( ke->type(), ke->key(),
|
|
ke->ascii(), ke->state(),
|
|
ke->text(), ke->isAutoRepeat(),
|
|
ke->count() ) );
|
|
if ( !mTypeAhead ) {
|
|
mTypeAhead = true;
|
|
emitNewEventForSelection();
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void KOAgenda::emitNewEventForSelection()
|
|
{
|
|
emit newEventSignal();
|
|
}
|
|
|
|
void KOAgenda::finishTypeAhead()
|
|
{
|
|
// kdDebug(5850) << "KOAgenda::finishTypeAhead()" << endl;
|
|
if ( typeAheadReceiver() ) {
|
|
for( QEvent *e = mTypeAheadEvents.first(); e;
|
|
e = mTypeAheadEvents.next() ) {
|
|
// kdDebug(5850) << "sendEvent() " << int( typeAheadReceiver() ) << endl;
|
|
QApplication::sendEvent( typeAheadReceiver(), e );
|
|
}
|
|
}
|
|
mTypeAheadEvents.clear();
|
|
mTypeAhead = false;
|
|
}
|
|
#ifndef QT_NO_WHEELEVENT
|
|
bool KOAgenda::eventFilter_wheel ( QObject *object, QWheelEvent *e )
|
|
{
|
|
QPoint viewportPos;
|
|
bool accepted=false;
|
|
if ( ( e->state() & ShiftButton) == ShiftButton ) {
|
|
if ( object != viewport() ) {
|
|
viewportPos = ( (QWidget *) object )->mapToParent( e->pos() );
|
|
} else {
|
|
viewportPos = e->pos();
|
|
}
|
|
//kdDebug(5850)<< "KOAgenda::eventFilter_wheel: type:"<<
|
|
// e->type()<<" delta: "<< e->delta()<< endl;
|
|
emit zoomView( -e->delta() ,
|
|
contentsToGrid( viewportToContents( viewportPos ) ),
|
|
Qt::Horizontal );
|
|
accepted=true;
|
|
}
|
|
|
|
if ( ( e->state() & ControlButton ) == ControlButton ){
|
|
if ( object != viewport() ) {
|
|
viewportPos = ( (QWidget *)object )->mapToParent( e->pos() );
|
|
} else {
|
|
viewportPos = e->pos();
|
|
}
|
|
emit zoomView( -e->delta() ,
|
|
contentsToGrid( viewportToContents( viewportPos ) ),
|
|
Qt::Vertical );
|
|
emit mousePosSignal(gridToContents(contentsToGrid(viewportToContents( viewportPos ))));
|
|
accepted=true;
|
|
}
|
|
if (accepted ) e->accept();
|
|
return accepted;
|
|
}
|
|
#endif
|
|
bool KOAgenda::eventFilter_mouse(QObject *object, QMouseEvent *me)
|
|
{
|
|
QPoint viewportPos;
|
|
if (object != viewport()) {
|
|
viewportPos = ((QWidget *)object)->mapToParent(me->pos());
|
|
} else {
|
|
viewportPos = me->pos();
|
|
}
|
|
|
|
switch (me->type()) {
|
|
case QEvent::MouseButtonPress:
|
|
// kdDebug(5850) << "koagenda: filtered button press" << endl;
|
|
if (object != viewport()) {
|
|
if (me->button() == RightButton) {
|
|
mClickedItem = dynamic_cast<KOAgendaItem *>(object);
|
|
if (mClickedItem) {
|
|
selectItem(mClickedItem);
|
|
emit showIncidencePopupSignal( mClickedItem->incidence(),
|
|
mClickedItem->itemDate() );
|
|
}
|
|
} else {
|
|
KOAgendaItem* item = dynamic_cast<KOAgendaItem *>(object);
|
|
if (item) {
|
|
Incidence *incidence = item->incidence();
|
|
if ( incidence->isReadOnly() ) {
|
|
mActionItem = 0;
|
|
} else {
|
|
mActionItem = item;
|
|
startItemAction(viewportPos);
|
|
}
|
|
// Warning: do selectItem() as late as possible, since all
|
|
// sorts of things happen during this call. Some can lead to
|
|
// this filter being run again and mActionItem being set to
|
|
// null.
|
|
selectItem( item );
|
|
}
|
|
}
|
|
} else {
|
|
if (me->button() == RightButton)
|
|
{
|
|
// if mouse pointer is not in selection, select the cell below the cursor
|
|
QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
|
|
if ( !ptInSelection( gpos ) ) {
|
|
mSelectionStartCell = gpos;
|
|
mSelectionEndCell = gpos;
|
|
mHasSelection = true;
|
|
emit newStartSelectSignal();
|
|
emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
|
|
updateContents();
|
|
}
|
|
showNewEventPopupSignal();
|
|
}
|
|
else
|
|
{
|
|
// if mouse pointer is in selection, don't change selection
|
|
QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
|
|
if ( !ptInSelection( gpos ) ) {
|
|
selectItem(0);
|
|
mActionItem = 0;
|
|
setCursor(arrowCursor);
|
|
startSelectAction(viewportPos);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QEvent::MouseButtonRelease:
|
|
if (mActionItem) {
|
|
endItemAction();
|
|
} else if ( mActionType == SELECT ) {
|
|
endSelectAction( viewportPos );
|
|
}
|
|
// This nasty gridToContents(contentsToGrid(..)) is needed to
|
|
// avoid an offset of a few pixels. Don't ask me why...
|
|
emit mousePosSignal( gridToContents(contentsToGrid(
|
|
viewportToContents( viewportPos ) ) ));
|
|
break;
|
|
|
|
case QEvent::MouseMove: {
|
|
// This nasty gridToContents(contentsToGrid(..)) is needed to
|
|
// avoid an offset of a few pixels. Don't ask me why...
|
|
QPoint indicatorPos = gridToContents(contentsToGrid(
|
|
viewportToContents( viewportPos )));
|
|
if (object != viewport()) {
|
|
KOAgendaItem *moveItem = dynamic_cast<KOAgendaItem *>(object);
|
|
if (moveItem && !moveItem->incidence()->isReadOnly() ) {
|
|
if (!mActionItem)
|
|
setNoActionCursor(moveItem,viewportPos);
|
|
else {
|
|
performItemAction(viewportPos);
|
|
|
|
if ( mActionType == MOVE ) {
|
|
// show cursor at the current begin of the item
|
|
KOAgendaItem *firstItem = mActionItem->firstMultiItem();
|
|
if (!firstItem) firstItem = mActionItem;
|
|
indicatorPos = gridToContents( QPoint( firstItem->cellXLeft(),
|
|
firstItem->cellYTop() ) );
|
|
|
|
} else if ( mActionType == RESIZEBOTTOM ) {
|
|
// RESIZETOP is handled correctly, only resizebottom works differently
|
|
indicatorPos = gridToContents( QPoint( mActionItem->cellXLeft(),
|
|
mActionItem->cellYBottom()+1 ) );
|
|
}
|
|
|
|
} // If we have an action item
|
|
} // If move item && !read only
|
|
} else {
|
|
if ( mActionType == SELECT ) {
|
|
performSelectAction( viewportPos );
|
|
|
|
// show cursor at end of timespan
|
|
if ( ((mStartCell.y() < mEndCell.y()) && (mEndCell.x() >= mStartCell.x())) ||
|
|
(mEndCell.x() > mStartCell.x()) )
|
|
indicatorPos = gridToContents( QPoint(mEndCell.x(), mEndCell.y()+1) );
|
|
else
|
|
indicatorPos = gridToContents( mEndCell );
|
|
}
|
|
}
|
|
emit mousePosSignal( indicatorPos );
|
|
break; }
|
|
|
|
case QEvent::MouseButtonDblClick:
|
|
if (object == viewport()) {
|
|
selectItem(0);
|
|
emit newEventSignal();
|
|
} else {
|
|
KOAgendaItem *doubleClickedItem = dynamic_cast<KOAgendaItem *>(object);
|
|
if (doubleClickedItem) {
|
|
selectItem(doubleClickedItem);
|
|
emit editIncidenceSignal(doubleClickedItem->incidence());
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool KOAgenda::ptInSelection( QPoint gpos ) const
|
|
{
|
|
if ( !mHasSelection ) {
|
|
return false;
|
|
} else if ( gpos.x()<mSelectionStartCell.x() || gpos.x()>mSelectionEndCell.x() ) {
|
|
return false;
|
|
} else if ( (gpos.x()==mSelectionStartCell.x()) && (gpos.y()<mSelectionStartCell.y()) ) {
|
|
return false;
|
|
} else if ( (gpos.x()==mSelectionEndCell.x()) && (gpos.y()>mSelectionEndCell.y()) ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void KOAgenda::startSelectAction( const QPoint &viewportPos )
|
|
{
|
|
emit newStartSelectSignal();
|
|
|
|
mActionType = SELECT;
|
|
mSelectionStartPoint = viewportPos;
|
|
mHasSelection = true;
|
|
|
|
QPoint pos = viewportToContents( viewportPos );
|
|
QPoint gpos = contentsToGrid( pos );
|
|
|
|
// Store new selection
|
|
mStartCell = gpos;
|
|
mEndCell = gpos;
|
|
mSelectionStartCell = gpos;
|
|
mSelectionEndCell = gpos;
|
|
|
|
updateContents();
|
|
}
|
|
|
|
void KOAgenda::performSelectAction(const QPoint& viewportPos)
|
|
{
|
|
QPoint pos = viewportToContents( viewportPos );
|
|
QPoint gpos = contentsToGrid( pos );
|
|
|
|
QPoint clipperPos = clipper()->
|
|
mapFromGlobal(viewport()->mapToGlobal(viewportPos));
|
|
|
|
// Scroll if cursor was moved to upper or lower end of agenda.
|
|
if (clipperPos.y() < mScrollBorderWidth) {
|
|
mScrollUpTimer.start(mScrollDelay);
|
|
} else if (visibleHeight() - clipperPos.y() <
|
|
mScrollBorderWidth) {
|
|
mScrollDownTimer.start(mScrollDelay);
|
|
} else {
|
|
mScrollUpTimer.stop();
|
|
mScrollDownTimer.stop();
|
|
}
|
|
|
|
if ( gpos != mEndCell ) {
|
|
mEndCell = gpos;
|
|
if ( mStartCell.x()>mEndCell.x() ||
|
|
( mStartCell.x()==mEndCell.x() && mStartCell.y()>mEndCell.y() ) ) {
|
|
// backward selection
|
|
mSelectionStartCell = mEndCell;
|
|
mSelectionEndCell = mStartCell;
|
|
} else {
|
|
mSelectionStartCell = mStartCell;
|
|
mSelectionEndCell = mEndCell;
|
|
}
|
|
|
|
updateContents();
|
|
}
|
|
}
|
|
|
|
void KOAgenda::endSelectAction( const QPoint ¤tPos )
|
|
{
|
|
mScrollUpTimer.stop();
|
|
mScrollDownTimer.stop();
|
|
|
|
mActionType = NOP;
|
|
|
|
emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
|
|
|
|
if ( KOPrefs::instance()->mSelectionStartsEditor ) {
|
|
if ( ( mSelectionStartPoint - currentPos ).manhattanLength() >
|
|
QApplication::startDragDistance() ) {
|
|
emitNewEventForSelection();
|
|
}
|
|
}
|
|
}
|
|
|
|
KOAgenda::MouseActionType KOAgenda::isInResizeArea( bool horizontal,
|
|
const QPoint &pos, KOAgendaItem*item )
|
|
{
|
|
if (!item) return NOP;
|
|
QPoint gridpos = contentsToGrid( pos );
|
|
QPoint contpos = gridToContents( gridpos +
|
|
QPoint( (KOGlobals::self()->reverseLayout())?1:0, 0 ) );
|
|
|
|
//kdDebug(5850)<<"contpos="<<contpos<<", pos="<<pos<<", gpos="<<gpos<<endl;
|
|
//kdDebug(5850)<<"clXLeft="<<clXLeft<<", clXRight="<<clXRight<<endl;
|
|
|
|
if ( horizontal ) {
|
|
int clXLeft = item->cellXLeft();
|
|
int clXRight = item->cellXRight();
|
|
if ( KOGlobals::self()->reverseLayout() ) {
|
|
int tmp = clXLeft;
|
|
clXLeft = clXRight;
|
|
clXRight = tmp;
|
|
}
|
|
int gridDistanceX = int( pos.x() - contpos.x() );
|
|
if (gridDistanceX < mResizeBorderWidth && clXLeft == gridpos.x() ) {
|
|
if ( KOGlobals::self()->reverseLayout() ) return RESIZERIGHT;
|
|
else return RESIZELEFT;
|
|
} else if ((mGridSpacingX - gridDistanceX) < mResizeBorderWidth &&
|
|
clXRight == gridpos.x() ) {
|
|
if ( KOGlobals::self()->reverseLayout() ) return RESIZELEFT;
|
|
else return RESIZERIGHT;
|
|
} else {
|
|
return MOVE;
|
|
}
|
|
} else {
|
|
int gridDistanceY = int( pos.y() - contpos.y() );
|
|
if (gridDistanceY < mResizeBorderWidth &&
|
|
item->cellYTop() == gridpos.y() &&
|
|
!item->firstMultiItem() ) {
|
|
return RESIZETOP;
|
|
} else if ((mGridSpacingY - gridDistanceY) < mResizeBorderWidth &&
|
|
item->cellYBottom() == gridpos.y() &&
|
|
!item->lastMultiItem() ) {
|
|
return RESIZEBOTTOM;
|
|
} else {
|
|
return MOVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KOAgenda::startItemAction(const QPoint& viewportPos)
|
|
{
|
|
QPoint pos = viewportToContents( viewportPos );
|
|
mStartCell = contentsToGrid( pos );
|
|
mEndCell = mStartCell;
|
|
|
|
bool noResize = ( mActionItem->incidence()->type() == "Todo");
|
|
|
|
mActionType = MOVE;
|
|
if ( !noResize ) {
|
|
mActionType = isInResizeArea( mAllDayMode, pos, mActionItem );
|
|
}
|
|
|
|
|
|
mActionItem->startMove();
|
|
setActionCursor( mActionType, true );
|
|
}
|
|
|
|
void KOAgenda::performItemAction(const QPoint& viewportPos)
|
|
{
|
|
// kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
|
|
// QPoint point = viewport()->mapToGlobal(viewportPos);
|
|
// kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
|
|
// point = clipper()->mapFromGlobal(point);
|
|
// kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
|
|
// kdDebug(5850) << "visible height: " << visibleHeight() << endl;
|
|
QPoint pos = viewportToContents( viewportPos );
|
|
// kdDebug(5850) << "contents: " << x << "," << y << "\n" << endl;
|
|
QPoint gpos = contentsToGrid( pos );
|
|
QPoint clipperPos = clipper()->
|
|
mapFromGlobal(viewport()->mapToGlobal(viewportPos));
|
|
|
|
// Cursor left active agenda area.
|
|
// This starts a drag.
|
|
if ( clipperPos.y() < 0 || clipperPos.y() > visibleHeight() ||
|
|
clipperPos.x() < 0 || clipperPos.x() > visibleWidth() ) {
|
|
if ( mActionType == MOVE ) {
|
|
mScrollUpTimer.stop();
|
|
mScrollDownTimer.stop();
|
|
mActionItem->resetMove();
|
|
placeSubCells( mActionItem );
|
|
emit startDragSignal( mActionItem->incidence() );
|
|
setCursor( arrowCursor );
|
|
mActionItem = 0;
|
|
mActionType = NOP;
|
|
mItemMoved = false;
|
|
if ( mItemMoved && mChanger )
|
|
mChanger->endChange( mActionItem->incidence() );
|
|
return;
|
|
}
|
|
} else {
|
|
setActionCursor( mActionType );
|
|
}
|
|
|
|
// Scroll if item was moved to upper or lower end of agenda.
|
|
if (clipperPos.y() < mScrollBorderWidth) {
|
|
mScrollUpTimer.start(mScrollDelay);
|
|
} else if (visibleHeight() - clipperPos.y() <
|
|
mScrollBorderWidth) {
|
|
mScrollDownTimer.start(mScrollDelay);
|
|
} else {
|
|
mScrollUpTimer.stop();
|
|
mScrollDownTimer.stop();
|
|
}
|
|
|
|
// Move or resize item if necessary
|
|
if ( mEndCell != gpos ) {
|
|
if ( !mItemMoved ) {
|
|
if ( !mChanger || !mChanger->beginChange( mActionItem->incidence() ) ) {
|
|
KMessageBox::information( this, i18n("Unable to lock item for "
|
|
"modification. You cannot make any changes."),
|
|
i18n("Locking Failed"), "AgendaLockingFailed" );
|
|
mScrollUpTimer.stop();
|
|
mScrollDownTimer.stop();
|
|
mActionItem->resetMove();
|
|
placeSubCells( mActionItem );
|
|
setCursor( arrowCursor );
|
|
mActionItem = 0;
|
|
mActionType = NOP;
|
|
mItemMoved = false;
|
|
return;
|
|
}
|
|
mItemMoved = true;
|
|
}
|
|
mActionItem->raise();
|
|
if (mActionType == MOVE) {
|
|
// Move all items belonging to a multi item
|
|
KOAgendaItem *firstItem = mActionItem->firstMultiItem();
|
|
if (!firstItem) firstItem = mActionItem;
|
|
KOAgendaItem *lastItem = mActionItem->lastMultiItem();
|
|
if (!lastItem) lastItem = mActionItem;
|
|
QPoint deltapos = gpos - mEndCell;
|
|
KOAgendaItem *moveItem = firstItem;
|
|
while (moveItem) {
|
|
bool changed=false;
|
|
if ( deltapos.x()!=0 ) {
|
|
moveItem->moveRelative( deltapos.x(), 0 );
|
|
changed=true;
|
|
}
|
|
// in agenda's all day view don't try to move multi items, since there are none
|
|
if ( moveItem==firstItem && !mAllDayMode ) { // is the first item
|
|
int newY = deltapos.y() + moveItem->cellYTop();
|
|
// If event start moved earlier than 0:00, it starts the previous day
|
|
if ( newY<0 ) {
|
|
moveItem->expandTop( -moveItem->cellYTop() );
|
|
// prepend a new item at ( x-1, rows()+newY to rows() )
|
|
KOAgendaItem *newFirst = firstItem->prevMoveItem();
|
|
// cell's y values are first and last cell of the bar, so if newY=-1, they need to be the same
|
|
if (newFirst) {
|
|
newFirst->setCellXY(moveItem->cellXLeft()-1, rows()+newY, rows()-1);
|
|
mItems.append( newFirst );
|
|
moveItem->resize( int( mGridSpacingX * newFirst->cellWidth() ),
|
|
int( mGridSpacingY * newFirst->cellHeight() ));
|
|
QPoint cpos = gridToContents( QPoint( newFirst->cellXLeft(), newFirst->cellYTop() ) );
|
|
addChild( newFirst, cpos.x(), cpos.y() );
|
|
} else {
|
|
newFirst = insertItem( moveItem->incidence(), moveItem->itemDate(),
|
|
moveItem->cellXLeft()-1, rows()+newY, rows()-1 ) ;
|
|
}
|
|
if (newFirst) newFirst->show();
|
|
moveItem->prependMoveItem(newFirst);
|
|
firstItem=newFirst;
|
|
} else if ( newY>=rows() ) {
|
|
// If event start is moved past 24:00, it starts the next day
|
|
// erase current item (i.e. remove it from the multiItem list)
|
|
firstItem = moveItem->nextMultiItem();
|
|
moveItem->hide();
|
|
mItems.take( mItems.find( moveItem ) );
|
|
removeChild( moveItem );
|
|
mActionItem->removeMoveItem(moveItem);
|
|
moveItem=firstItem;
|
|
// adjust next day's item
|
|
if (moveItem) moveItem->expandTop( rows()-newY );
|
|
} else {
|
|
moveItem->expandTop(deltapos.y());
|
|
}
|
|
changed=true;
|
|
}
|
|
if ( !moveItem->lastMultiItem() && !mAllDayMode ) { // is the last item
|
|
int newY = deltapos.y()+moveItem->cellYBottom();
|
|
if (newY<0) {
|
|
// erase current item
|
|
lastItem = moveItem->prevMultiItem();
|
|
moveItem->hide();
|
|
mItems.take( mItems.find(moveItem) );
|
|
removeChild( moveItem );
|
|
moveItem->removeMoveItem( moveItem );
|
|
moveItem = lastItem;
|
|
moveItem->expandBottom(newY+1);
|
|
} else if (newY>=rows()) {
|
|
moveItem->expandBottom( rows()-moveItem->cellYBottom()-1 );
|
|
// append item at ( x+1, 0 to newY-rows() )
|
|
KOAgendaItem *newLast = lastItem->nextMoveItem();
|
|
if (newLast) {
|
|
newLast->setCellXY( moveItem->cellXLeft()+1, 0, newY-rows()-1 );
|
|
mItems.append(newLast);
|
|
moveItem->resize( int( mGridSpacingX * newLast->cellWidth() ),
|
|
int( mGridSpacingY * newLast->cellHeight() ));
|
|
QPoint cpos = gridToContents( QPoint( newLast->cellXLeft(), newLast->cellYTop() ) ) ;
|
|
addChild( newLast, cpos.x(), cpos.y() );
|
|
} else {
|
|
newLast = insertItem( moveItem->incidence(), moveItem->itemDate(),
|
|
moveItem->cellXLeft()+1, 0, newY-rows()-1 ) ;
|
|
}
|
|
moveItem->appendMoveItem( newLast );
|
|
newLast->show();
|
|
lastItem = newLast;
|
|
} else {
|
|
moveItem->expandBottom( deltapos.y() );
|
|
}
|
|
changed=true;
|
|
}
|
|
if (changed) {
|
|
adjustItemPosition( moveItem );
|
|
}
|
|
moveItem = moveItem->nextMultiItem();
|
|
}
|
|
} else if (mActionType == RESIZETOP) {
|
|
if (mEndCell.y() <= mActionItem->cellYBottom()) {
|
|
mActionItem->expandTop(gpos.y() - mEndCell.y());
|
|
adjustItemPosition( mActionItem );
|
|
}
|
|
} else if (mActionType == RESIZEBOTTOM) {
|
|
if (mEndCell.y() >= mActionItem->cellYTop()) {
|
|
mActionItem->expandBottom(gpos.y() - mEndCell.y());
|
|
adjustItemPosition( mActionItem );
|
|
}
|
|
} else if (mActionType == RESIZELEFT) {
|
|
if (mEndCell.x() <= mActionItem->cellXRight()) {
|
|
mActionItem->expandLeft( gpos.x() - mEndCell.x() );
|
|
adjustItemPosition( mActionItem );
|
|
}
|
|
} else if (mActionType == RESIZERIGHT) {
|
|
if (mEndCell.x() >= mActionItem->cellXLeft()) {
|
|
mActionItem->expandRight(gpos.x() - mEndCell.x());
|
|
adjustItemPosition( mActionItem );
|
|
}
|
|
}
|
|
mEndCell = gpos;
|
|
}
|
|
}
|
|
|
|
void KOAgenda::endItemAction()
|
|
{
|
|
// kdDebug(5850) << "KOAgenda::endItemAction() " << endl;
|
|
mActionType = NOP;
|
|
mScrollUpTimer.stop();
|
|
mScrollDownTimer.stop();
|
|
setCursor( arrowCursor );
|
|
bool multiModify = false;
|
|
// FIXME: do the cloning here...
|
|
Incidence* inc = mActionItem->incidence();
|
|
|
|
if ( mItemMoved ) {
|
|
bool modify = true;
|
|
if ( mActionItem->incidence()->doesRecur() ) {
|
|
int res = KOMessageBox::fourBtnMsgBox( this, QMessageBox::Question,
|
|
i18n("The item you try to change is a recurring item. Shall the changes "
|
|
"be applied only to this single occurrence, only to the future items, "
|
|
"or to all items in the recurrence?"),
|
|
i18n("Changing Recurring Item"),
|
|
i18n("Only &This Item"), i18n("Only &Future Items"), i18n("&All Occurrences") );
|
|
switch ( res ) {
|
|
case KMessageBox::Ok: // All occurrences
|
|
// Moving the whole sequene of events is handled by the itemModified below.
|
|
modify = true;
|
|
break;
|
|
case KMessageBox::Yes: { // Just this occurrence
|
|
// Dissociate this occurrence:
|
|
// create clone of event, set relation to old event, set cloned event
|
|
// for mActionItem, add exception date to old event, changeIncidence
|
|
// for the old event, remove the recurrence from the new copy and then just
|
|
// go on with the newly adjusted mActionItem and let the usual code take
|
|
// care of the new time!
|
|
modify = true;
|
|
multiModify = true;
|
|
emit startMultiModify( i18n("Dissociate event from recurrence") );
|
|
Incidence* oldInc = mActionItem->incidence();
|
|
Incidence* oldIncSaved = mActionItem->incidence()->clone();
|
|
Incidence* newInc = mCalendar->dissociateOccurrence(
|
|
oldInc, mActionItem->itemDate() );
|
|
if ( newInc ) {
|
|
// don't recreate items, they already have the correct position
|
|
emit enableAgendaUpdate( false );
|
|
mActionItem->dissociateFromMultiItem();
|
|
mActionItem->setIncidence( newInc );
|
|
mChanger->addIncidence( newInc, this );
|
|
emit enableAgendaUpdate( true );
|
|
mChanger->changeIncidence( oldIncSaved, oldInc );
|
|
} else {
|
|
KMessageBox::sorry( this, i18n("Unable to add the exception item to the "
|
|
"calendar. No change will be done."), i18n("Error Occurred") );
|
|
}
|
|
delete oldIncSaved;
|
|
break; }
|
|
case KMessageBox::No/*Future*/: { // All future occurrences
|
|
// Dissociate this occurrence:
|
|
// create clone of event, set relation to old event, set cloned event
|
|
// for mActionItem, add recurrence end date to old event, changeIncidence
|
|
// for the old event, adjust the recurrence for the new copy and then just
|
|
// go on with the newly adjusted mActionItem and let the usual code take
|
|
// care of the new time!
|
|
modify = true;
|
|
multiModify = true;
|
|
emit startMultiModify( i18n("Split future recurrences") );
|
|
Incidence* oldInc = mActionItem->incidence();
|
|
Incidence* oldIncSaved = mActionItem->incidence()->clone();
|
|
Incidence* newInc = mCalendar->dissociateOccurrence(
|
|
oldInc, mActionItem->itemDate(), false );
|
|
if ( newInc ) {
|
|
emit enableAgendaUpdate( false );
|
|
mActionItem->dissociateFromMultiItem();
|
|
mActionItem->setIncidence( newInc );
|
|
mChanger->addIncidence( newInc, this );
|
|
emit enableAgendaUpdate( true );
|
|
mChanger->changeIncidence( oldIncSaved, oldInc );
|
|
} else {
|
|
KMessageBox::sorry( this, i18n("Unable to add the future items to the "
|
|
"calendar. No change will be done."), i18n("Error Occurred") );
|
|
}
|
|
delete oldIncSaved;
|
|
break; }
|
|
default:
|
|
modify = false;
|
|
mActionItem->resetMove();
|
|
placeSubCells( mActionItem );
|
|
}
|
|
}
|
|
|
|
if ( modify ) {
|
|
mActionItem->endMove();
|
|
KOAgendaItem *placeItem = mActionItem->firstMultiItem();
|
|
if ( !placeItem ) {
|
|
placeItem = mActionItem;
|
|
}
|
|
|
|
KOAgendaItem *modif = placeItem;
|
|
|
|
QPtrList<KOAgendaItem> oldconflictItems = placeItem->conflictItems();
|
|
KOAgendaItem *item;
|
|
for ( item = oldconflictItems.first(); item != 0;
|
|
item = oldconflictItems.next() ) {
|
|
placeSubCells( item );
|
|
}
|
|
while ( placeItem ) {
|
|
placeSubCells( placeItem );
|
|
placeItem = placeItem->nextMultiItem();
|
|
}
|
|
|
|
// Notify about change
|
|
// the agenda view will apply the changes to the actual Incidence*!
|
|
emit itemModified( modif );
|
|
}
|
|
// FIXME: If the change failed, we need to update the view!
|
|
mChanger->endChange( inc );
|
|
}
|
|
|
|
mActionItem = 0;
|
|
mItemMoved = false;
|
|
|
|
if ( multiModify ) emit endMultiModify();
|
|
|
|
kdDebug(5850) << "KOAgenda::endItemAction() done" << endl;
|
|
}
|
|
|
|
void KOAgenda::setActionCursor( int actionType, bool acting )
|
|
{
|
|
switch ( actionType ) {
|
|
case MOVE:
|
|
if (acting) setCursor( sizeAllCursor );
|
|
else setCursor( arrowCursor );
|
|
break;
|
|
case RESIZETOP:
|
|
case RESIZEBOTTOM:
|
|
setCursor( sizeVerCursor );
|
|
break;
|
|
case RESIZELEFT:
|
|
case RESIZERIGHT:
|
|
setCursor( sizeHorCursor );
|
|
break;
|
|
default:
|
|
setCursor( arrowCursor );
|
|
}
|
|
}
|
|
|
|
void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const QPoint& viewportPos )
|
|
{
|
|
// kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
|
|
// QPoint point = viewport()->mapToGlobal(viewportPos);
|
|
// kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
|
|
// point = clipper()->mapFromGlobal(point);
|
|
// kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
|
|
|
|
QPoint pos = viewportToContents( viewportPos );
|
|
bool noResize = (moveItem && moveItem->incidence() &&
|
|
moveItem->incidence()->type() == "Todo");
|
|
|
|
KOAgenda::MouseActionType resizeType = MOVE;
|
|
if ( !noResize ) resizeType = isInResizeArea( mAllDayMode, pos , moveItem);
|
|
setActionCursor( resizeType );
|
|
}
|
|
|
|
|
|
/** calculate the width of the column subcells of the given item
|
|
*/
|
|
double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
|
|
{
|
|
QPoint pt, pt1;
|
|
pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
|
|
pt1 = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) +
|
|
QPoint( 1, 1 ) );
|
|
pt1 -= pt;
|
|
int maxSubCells = item->subCells();
|
|
double newSubCellWidth;
|
|
if ( mAllDayMode ) {
|
|
newSubCellWidth = double( pt1.y() ) / maxSubCells;
|
|
} else {
|
|
newSubCellWidth = double( pt1.x() ) / maxSubCells;
|
|
}
|
|
return newSubCellWidth;
|
|
}
|
|
|
|
void KOAgenda::adjustItemPosition( KOAgendaItem *item )
|
|
{
|
|
if (!item) return;
|
|
item->resize( int( mGridSpacingX * item->cellWidth() ),
|
|
int( mGridSpacingY * item->cellHeight() ) );
|
|
int clXLeft = item->cellXLeft();
|
|
if ( KOGlobals::self()->reverseLayout() )
|
|
clXLeft = item->cellXRight() + 1;
|
|
QPoint cpos = gridToContents( QPoint( clXLeft, item->cellYTop() ) );
|
|
moveChild( item, cpos.x(), cpos.y() );
|
|
}
|
|
|
|
void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
|
|
{
|
|
// kdDebug(5850) << "KOAgenda::placeAgendaItem(): " << item->incidence()->summary()
|
|
// << " subCellWidth: " << subCellWidth << endl;
|
|
|
|
// "left" upper corner, no subcells yet, RTL layouts have right/left switched, widths are negative then
|
|
QPoint pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
|
|
// right lower corner
|
|
QPoint pt1 = gridToContents( QPoint( item->cellXLeft() + item->cellWidth(),
|
|
item->cellYBottom()+1 ) );
|
|
|
|
double subCellPos = item->subCell() * subCellWidth;
|
|
|
|
// we need to add 0.01 to make sure we don't loose one pixed due to
|
|
// numerics (i.e. if it would be x.9998, we want the integer, not rounded down.
|
|
double delta=0.01;
|
|
if (subCellWidth<0) delta=-delta;
|
|
int height, width, xpos, ypos;
|
|
if (mAllDayMode) {
|
|
width = pt1.x()-pt.x();
|
|
height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
|
|
xpos = pt.x();
|
|
ypos = pt.y() + int( subCellPos );
|
|
} else {
|
|
width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
|
|
height = pt1.y()-pt.y();
|
|
xpos = pt.x() + int( subCellPos );
|
|
ypos = pt.y();
|
|
}
|
|
if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
|
|
xpos += width;
|
|
width = -width;
|
|
}
|
|
if ( height<0 ) { // BTT (bottom-to-top) layout ?!?
|
|
ypos += height;
|
|
height = -height;
|
|
}
|
|
item->resize( width, height );
|
|
moveChild( item, xpos, ypos );
|
|
}
|
|
|
|
/*
|
|
Place item in cell and take care that multiple items using the same cell do
|
|
not overlap. This method is not yet optimal. It doesn't use the maximum space
|
|
it can get in all cases.
|
|
At the moment the method has a bug: When an item is placed only the sub cell
|
|
widths of the items are changed, which are within the Y region the item to
|
|
place spans. When the sub cell width change of one of this items affects a
|
|
cell, where other items are, which do not overlap in Y with the item to place,
|
|
the display gets corrupted, although the corruption looks quite nice.
|
|
*/
|
|
void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
|
|
{
|
|
#if 0
|
|
kdDebug(5850) << "KOAgenda::placeSubCells()" << endl;
|
|
if ( placeItem ) {
|
|
Incidence *event = placeItem->incidence();
|
|
if ( !event ) {
|
|
kdDebug(5850) << " event is 0" << endl;
|
|
} else {
|
|
kdDebug(5850) << " event: " << event->summary() << endl;
|
|
}
|
|
} else {
|
|
kdDebug(5850) << " placeItem is 0" << endl;
|
|
}
|
|
kdDebug(5850) << "KOAgenda::placeSubCells()..." << endl;
|
|
#endif
|
|
|
|
QPtrList<KOrg::CellItem> cells;
|
|
KOAgendaItem *item;
|
|
for ( item = mItems.first(); item != 0; item = mItems.next() ) {
|
|
cells.append( item );
|
|
}
|
|
|
|
QPtrList<KOrg::CellItem> items = KOrg::CellItem::placeItem( cells,
|
|
placeItem );
|
|
|
|
placeItem->setConflictItems( QPtrList<KOAgendaItem>() );
|
|
double newSubCellWidth = calcSubCellWidth( placeItem );
|
|
KOrg::CellItem *i;
|
|
for ( i = items.first(); i; i = items.next() ) {
|
|
item = static_cast<KOAgendaItem *>( i );
|
|
placeAgendaItem( item, newSubCellWidth );
|
|
item->addConflictItem( placeItem );
|
|
placeItem->addConflictItem( item );
|
|
}
|
|
if ( items.isEmpty() ) {
|
|
placeAgendaItem( placeItem, newSubCellWidth );
|
|
}
|
|
placeItem->update();
|
|
}
|
|
|
|
int KOAgenda::columnWidth( int column )
|
|
{
|
|
int start = gridToContents( QPoint( column, 0 ) ).x();
|
|
if (KOGlobals::self()->reverseLayout() )
|
|
column--;
|
|
else
|
|
column++;
|
|
int end = gridToContents( QPoint( column, 0 ) ).x();
|
|
return end - start;
|
|
}
|
|
/*
|
|
Draw grid in the background of the agenda.
|
|
*/
|
|
void KOAgenda::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
|
|
{
|
|
QPixmap db(cw, ch);
|
|
db.fill(KOPrefs::instance()->mAgendaBgColor);
|
|
QPainter dbp(&db);
|
|
dbp.translate(-cx,-cy);
|
|
|
|
// kdDebug(5850) << "KOAgenda::drawContents()" << endl;
|
|
double lGridSpacingY = mGridSpacingY*2;
|
|
|
|
// Highlight working hours
|
|
if (mWorkingHoursEnable) {
|
|
QPoint pt1( cx, mWorkingHoursYTop );
|
|
QPoint pt2( cx+cw, mWorkingHoursYBottom );
|
|
if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
|
|
int gxStart = contentsToGrid( pt1 ).x();
|
|
int gxEnd = contentsToGrid( pt2 ).x();
|
|
// correct start/end for rtl layouts
|
|
if ( gxStart > gxEnd ) {
|
|
int tmp = gxStart;
|
|
gxStart = gxEnd;
|
|
gxEnd = tmp;
|
|
}
|
|
int xoffset = ( KOGlobals::self()->reverseLayout()?1:0 );
|
|
while( gxStart <= gxEnd ) {
|
|
int xStart = gridToContents( QPoint( gxStart+xoffset, 0 ) ).x();
|
|
int xWidth = columnWidth( gxStart ) + 1;
|
|
if ( pt2.y() < pt1.y() ) {
|
|
// overnight working hours
|
|
if ( ( (gxStart==0) && !mHolidayMask->at(mHolidayMask->count()-1) ) ||
|
|
( (gxStart>0) && (gxStart<int(mHolidayMask->count())) && (!mHolidayMask->at(gxStart-1) ) ) ) {
|
|
if ( pt2.y() > cy ) {
|
|
dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1,
|
|
KOPrefs::instance()->mWorkingHoursColor);
|
|
}
|
|
}
|
|
if ( (gxStart < int(mHolidayMask->count()-1)) && (!mHolidayMask->at(gxStart)) ) {
|
|
if ( pt1.y() < cy + ch - 1 ) {
|
|
dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
|
|
KOPrefs::instance()->mWorkingHoursColor);
|
|
}
|
|
}
|
|
} else {
|
|
// last entry in holiday mask denotes the previous day not visible (needed for overnight shifts)
|
|
if ( gxStart < int(mHolidayMask->count()-1) && !mHolidayMask->at(gxStart)) {
|
|
dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
|
|
KOPrefs::instance()->mWorkingHoursColor );
|
|
}
|
|
}
|
|
++gxStart;
|
|
}
|
|
}
|
|
}
|
|
|
|
// draw selection
|
|
if ( mHasSelection ) {
|
|
QPoint pt, pt1;
|
|
|
|
if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
|
|
// draw start day
|
|
pt = gridToContents( mSelectionStartCell );
|
|
pt1 = gridToContents( QPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
|
|
dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
|
|
// draw all other days between the start day and the day of the selection end
|
|
for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
|
|
pt = gridToContents( QPoint( c, 0 ) );
|
|
pt1 = gridToContents( QPoint( c + 1, mRows + 1 ) );
|
|
dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
|
|
}
|
|
// draw end day
|
|
pt = gridToContents( QPoint( mSelectionEndCell.x(), 0 ) );
|
|
pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
|
|
dbp.fillRect( QRect( pt, pt1), KOPrefs::instance()->mHighlightColor );
|
|
} else { // single day selection
|
|
pt = gridToContents( mSelectionStartCell );
|
|
pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
|
|
dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
|
|
}
|
|
}
|
|
|
|
QPen hourPen( KOPrefs::instance()->mAgendaBgColor.dark( 150 ) );
|
|
QPen halfHourPen( KOPrefs::instance()->mAgendaBgColor.dark( 125 ) );
|
|
dbp.setPen( hourPen );
|
|
|
|
// Draw vertical lines of grid, start with the last line not yet visible
|
|
// kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
|
|
double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
|
|
while (x < cx + cw) {
|
|
dbp.drawLine( int( x ), cy, int( x ), cy + ch );
|
|
x+=mGridSpacingX;
|
|
}
|
|
|
|
// Draw horizontal lines of grid
|
|
double y = ( int( cy / (2*lGridSpacingY) ) ) * 2 * lGridSpacingY;
|
|
while (y < cy + ch) {
|
|
// kdDebug(5850) << " y: " << y << endl;
|
|
dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
|
|
y += 2 * lGridSpacingY;
|
|
}
|
|
y = ( 2 * int( cy / (2*lGridSpacingY) ) + 1) * lGridSpacingY;
|
|
dbp.setPen( halfHourPen );
|
|
while (y < cy + ch) {
|
|
// kdDebug(5850) << " y: " << y << endl;
|
|
dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
|
|
y+=2*lGridSpacingY;
|
|
}
|
|
p->drawPixmap(cx,cy, db);
|
|
}
|
|
|
|
/*
|
|
Convert srcollview contents coordinates to agenda grid coordinates.
|
|
*/
|
|
QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
|
|
{
|
|
int gx = int( KOGlobals::self()->reverseLayout() ?
|
|
mColumns - pos.x()/mGridSpacingX : pos.x()/mGridSpacingX );
|
|
int gy = int( pos.y()/mGridSpacingY );
|
|
return QPoint( gx, gy );
|
|
}
|
|
|
|
/*
|
|
Convert agenda grid coordinates to scrollview contents coordinates.
|
|
*/
|
|
QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
|
|
{
|
|
int x = int( KOGlobals::self()->reverseLayout() ?
|
|
(mColumns - gpos.x())*mGridSpacingX : gpos.x()*mGridSpacingX );
|
|
int y = int( gpos.y()*mGridSpacingY );
|
|
return QPoint( x, y );
|
|
}
|
|
|
|
|
|
/*
|
|
Return Y coordinate corresponding to time. Coordinates are rounded to fit into
|
|
the grid.
|
|
*/
|
|
int KOAgenda::timeToY(const QTime &time)
|
|
{
|
|
// kdDebug(5850) << "Time: " << time.toString() << endl;
|
|
int minutesPerCell = 24 * 60 / mRows;
|
|
// kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
|
|
int timeMinutes = time.hour() * 60 + time.minute();
|
|
// kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
|
|
int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
|
|
// kdDebug(5850) << "y: " << Y << endl;
|
|
// kdDebug(5850) << "\n" << endl;
|
|
return Y;
|
|
}
|
|
|
|
|
|
/*
|
|
Return time corresponding to cell y coordinate. Coordinates are rounded to
|
|
fit into the grid.
|
|
*/
|
|
QTime KOAgenda::gyToTime(int gy)
|
|
{
|
|
// kdDebug(5850) << "gyToTime: " << gy << endl;
|
|
int secondsPerCell = 24 * 60 * 60/ mRows;
|
|
|
|
int timeSeconds = secondsPerCell * gy;
|
|
|
|
QTime time( 0, 0, 0 );
|
|
if ( timeSeconds < 24 * 60 * 60 ) {
|
|
time = time.addSecs(timeSeconds);
|
|
} else {
|
|
time.setHMS( 23, 59, 59 );
|
|
}
|
|
// kdDebug(5850) << " gyToTime: " << time.toString() << endl;
|
|
|
|
return time;
|
|
}
|
|
|
|
QMemArray<int> KOAgenda::minContentsY()
|
|
{
|
|
QMemArray<int> minArray;
|
|
minArray.fill( timeToY( QTime(23, 59) ), mSelectedDates.count() );
|
|
for ( KOAgendaItem *item = mItems.first();
|
|
item != 0; item = mItems.next() ) {
|
|
int ymin = item->cellYTop();
|
|
int index = item->cellXLeft();
|
|
if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
|
|
if ( ymin < minArray[index] && mItemsToDelete.findRef( item ) == -1 )
|
|
minArray[index] = ymin;
|
|
}
|
|
}
|
|
|
|
return minArray;
|
|
}
|
|
|
|
QMemArray<int> KOAgenda::maxContentsY()
|
|
{
|
|
QMemArray<int> maxArray;
|
|
maxArray.fill( timeToY( QTime(0, 0) ), mSelectedDates.count() );
|
|
for ( KOAgendaItem *item = mItems.first();
|
|
item != 0; item = mItems.next() ) {
|
|
int ymax = item->cellYBottom();
|
|
int index = item->cellXLeft();
|
|
if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
|
|
if ( ymax > maxArray[index] && mItemsToDelete.findRef( item ) == -1 )
|
|
maxArray[index] = ymax;
|
|
}
|
|
}
|
|
|
|
return maxArray;
|
|
}
|
|
|
|
void KOAgenda::setStartTime( const QTime &startHour )
|
|
{
|
|
double startPos = ( startHour.hour()/24. + startHour.minute()/1440. +
|
|
startHour.second()/86400. ) * mRows * gridSpacingY();
|
|
setContentsPos( 0, int( startPos ) );
|
|
}
|
|
|
|
|
|
/*
|
|
Insert KOAgendaItem into agenda.
|
|
*/
|
|
KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd, int X,
|
|
int YTop, int YBottom )
|
|
{
|
|
#if 0
|
|
kdDebug(5850) << "KOAgenda::insertItem:" << incidence->summary() << "-"
|
|
<< qd.toString() << " ;top, bottom:" << YTop << "," << YBottom
|
|
<< endl;
|
|
#endif
|
|
|
|
if ( mAllDayMode ) {
|
|
kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
|
|
return 0;
|
|
}
|
|
|
|
|
|
mActionType = NOP;
|
|
|
|
KOAgendaItem *agendaItem = new KOAgendaItem( incidence, qd, viewport() );
|
|
connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem * ) ),
|
|
SLOT( removeAgendaItem( KOAgendaItem * ) ) );
|
|
connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem * ) ),
|
|
SLOT( showAgendaItem( KOAgendaItem * ) ) );
|
|
|
|
if ( YBottom <= YTop ) {
|
|
kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
|
|
YBottom = YTop;
|
|
}
|
|
|
|
agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
|
|
int( X * mGridSpacingX ),
|
|
int( YTop * mGridSpacingY ) -
|
|
int( ( YBottom + 1 ) * mGridSpacingY ) );
|
|
agendaItem->setCellXY( X, YTop, YBottom );
|
|
agendaItem->setCellXRight( X );
|
|
agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
|
|
agendaItem->installEventFilter( this );
|
|
|
|
addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
|
|
mItems.append( agendaItem );
|
|
|
|
placeSubCells( agendaItem );
|
|
|
|
agendaItem->show();
|
|
|
|
marcus_bains();
|
|
|
|
return agendaItem;
|
|
}
|
|
|
|
/*
|
|
Insert all-day KOAgendaItem into agenda.
|
|
*/
|
|
KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
|
|
int XBegin, int XEnd )
|
|
{
|
|
if ( !mAllDayMode ) {
|
|
kdDebug(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
|
|
return 0;
|
|
}
|
|
|
|
mActionType = NOP;
|
|
|
|
KOAgendaItem *agendaItem = new KOAgendaItem( event, qd, viewport() );
|
|
connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem* ) ),
|
|
SLOT( removeAgendaItem( KOAgendaItem* ) ) );
|
|
connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem* ) ),
|
|
SLOT( showAgendaItem( KOAgendaItem* ) ) );
|
|
|
|
agendaItem->setCellXY( XBegin, 0, 0 );
|
|
agendaItem->setCellXRight( XEnd );
|
|
|
|
double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
|
|
double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
|
|
agendaItem->cellXLeft() );
|
|
|
|
agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
|
|
|
|
agendaItem->installEventFilter( this );
|
|
agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
|
|
addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
|
|
mItems.append( agendaItem );
|
|
|
|
placeSubCells( agendaItem );
|
|
|
|
agendaItem->show();
|
|
|
|
return agendaItem;
|
|
}
|
|
|
|
|
|
void KOAgenda::insertMultiItem (Event *event,const QDate &qd,int XBegin,int XEnd,
|
|
int YTop,int YBottom)
|
|
{
|
|
if (mAllDayMode) {
|
|
kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
|
|
return;
|
|
}
|
|
mActionType = NOP;
|
|
|
|
int cellX,cellYTop,cellYBottom;
|
|
QString newtext;
|
|
int width = XEnd - XBegin + 1;
|
|
int count = 0;
|
|
KOAgendaItem *current = 0;
|
|
QPtrList<KOAgendaItem> multiItems;
|
|
int visibleCount = mSelectedDates.first().daysTo(mSelectedDates.last());
|
|
for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
|
|
++count;
|
|
//Only add the items that are visible.
|
|
if( cellX >=0 && cellX <= visibleCount ) {
|
|
if ( cellX == XBegin ) cellYTop = YTop;
|
|
else cellYTop = 0;
|
|
if ( cellX == XEnd ) cellYBottom = YBottom;
|
|
else cellYBottom = rows() - 1;
|
|
newtext = QString("(%1/%2): ").arg( count ).arg( width );
|
|
newtext.append( event->summary() );
|
|
|
|
current = insertItem( event, qd, cellX, cellYTop, cellYBottom );
|
|
current->setText( newtext );
|
|
multiItems.append( current );
|
|
}
|
|
}
|
|
|
|
KOAgendaItem *next = 0;
|
|
KOAgendaItem *prev = 0;
|
|
KOAgendaItem *last = multiItems.last();
|
|
KOAgendaItem *first = multiItems.first();
|
|
KOAgendaItem *setFirst,*setLast;
|
|
current = first;
|
|
while (current) {
|
|
next = multiItems.next();
|
|
if (current == first) setFirst = 0;
|
|
else setFirst = first;
|
|
if (current == last) setLast = 0;
|
|
else setLast = last;
|
|
|
|
current->setMultiItem(setFirst, prev, next, setLast);
|
|
prev=current;
|
|
current = next;
|
|
}
|
|
|
|
marcus_bains();
|
|
}
|
|
|
|
void KOAgenda::removeIncidence( Incidence *incidence )
|
|
{
|
|
// First find all items to be deleted and store them
|
|
// in its own list. Otherwise removeAgendaItem will reset
|
|
// the current position and mess this up.
|
|
QPtrList<KOAgendaItem> itemsToRemove;
|
|
|
|
KOAgendaItem *item = mItems.first();
|
|
while ( item ) {
|
|
if ( item->incidence() == incidence ) {
|
|
itemsToRemove.append( item );
|
|
}
|
|
item = mItems.next();
|
|
}
|
|
item = itemsToRemove.first();
|
|
while ( item ) {
|
|
removeAgendaItem( item );
|
|
item = itemsToRemove.next();
|
|
}
|
|
}
|
|
|
|
void KOAgenda::showAgendaItem( KOAgendaItem *agendaItem )
|
|
{
|
|
if ( !agendaItem ) return;
|
|
agendaItem->hide();
|
|
addChild( agendaItem );
|
|
if ( !mItems.containsRef( agendaItem ) )
|
|
mItems.append( agendaItem );
|
|
placeSubCells( agendaItem );
|
|
|
|
agendaItem->show();
|
|
}
|
|
|
|
bool KOAgenda::removeAgendaItem( KOAgendaItem *item )
|
|
{
|
|
// we found the item. Let's remove it and update the conflicts
|
|
bool taken = false;
|
|
KOAgendaItem *thisItem = item;
|
|
QPtrList<KOAgendaItem> conflictItems = thisItem->conflictItems();
|
|
removeChild( thisItem );
|
|
int pos = mItems.find( thisItem );
|
|
if ( pos>=0 ) {
|
|
mItems.take( pos );
|
|
taken = true;
|
|
}
|
|
|
|
KOAgendaItem *confitem;
|
|
for ( confitem = conflictItems.first(); confitem != 0;
|
|
confitem = conflictItems.next() ) {
|
|
// the item itself is also in its own conflictItems list!
|
|
if ( confitem != thisItem ) placeSubCells(confitem);
|
|
|
|
}
|
|
mItemsToDelete.append( thisItem );
|
|
QTimer::singleShot( 0, this, SLOT( deleteItemsToDelete() ) );
|
|
return taken;
|
|
}
|
|
|
|
void KOAgenda::deleteItemsToDelete()
|
|
{
|
|
mItemsToDelete.clear();
|
|
}
|
|
|
|
/*QSizePolicy KOAgenda::sizePolicy() const
|
|
{
|
|
// Thought this would make the all-day event agenda minimum size and the
|
|
// normal agenda take the remaining space. But it doesnt work. The QSplitter
|
|
// dont seem to think that an Expanding widget needs more space than a
|
|
// Preferred one.
|
|
// But it doesnt hurt, so it stays.
|
|
if (mAllDayMode) {
|
|
return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
|
|
} else {
|
|
return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
Overridden from QScrollView to provide proper resizing of KOAgendaItems.
|
|
*/
|
|
void KOAgenda::resizeEvent ( QResizeEvent *ev )
|
|
{
|
|
// kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
|
|
|
|
QSize newSize( ev->size() );
|
|
if (mAllDayMode) {
|
|
mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
|
|
mGridSpacingY = newSize.height() - 2 * frameWidth();
|
|
} else {
|
|
int scrollbarWidth = vScrollBarMode() != AlwaysOff ? verticalScrollBar()->width() : 0;
|
|
mGridSpacingX = double( newSize.width() - scrollbarWidth - 2 * frameWidth()) / double(mColumns);
|
|
// make sure that there are not more than 24 per day
|
|
mGridSpacingY = double(newSize.height() - 2 * frameWidth()) / double(mRows);
|
|
if ( mGridSpacingY < mDesiredGridSpacingY )
|
|
mGridSpacingY = mDesiredGridSpacingY;
|
|
}
|
|
calculateWorkingHours();
|
|
QTimer::singleShot( 0, this, SLOT( resizeAllContents() ) );
|
|
emit gridSpacingYChanged( mGridSpacingY * 4 );
|
|
QScrollView::resizeEvent(ev);
|
|
}
|
|
|
|
void KOAgenda::resizeAllContents()
|
|
{
|
|
double subCellWidth;
|
|
KOAgendaItem *item;
|
|
if (mAllDayMode) {
|
|
for ( item=mItems.first(); item != 0; item=mItems.next() ) {
|
|
subCellWidth = calcSubCellWidth( item );
|
|
placeAgendaItem( item, subCellWidth );
|
|
}
|
|
} else {
|
|
for ( item=mItems.first(); item != 0; item=mItems.next() ) {
|
|
subCellWidth = calcSubCellWidth( item );
|
|
placeAgendaItem( item, subCellWidth );
|
|
}
|
|
}
|
|
checkScrollBoundaries();
|
|
marcus_bains();
|
|
}
|
|
|
|
|
|
void KOAgenda::scrollUp()
|
|
{
|
|
scrollBy(0,-mScrollOffset);
|
|
}
|
|
|
|
|
|
void KOAgenda::scrollDown()
|
|
{
|
|
scrollBy(0,mScrollOffset);
|
|
}
|
|
|
|
|
|
/*
|
|
Calculates the minimum width
|
|
*/
|
|
int KOAgenda::minimumWidth() const
|
|
{
|
|
// FIXME:: develop a way to dynamically determine the minimum width
|
|
int min = 100;
|
|
|
|
return min;
|
|
}
|
|
|
|
void KOAgenda::updateConfig()
|
|
{
|
|
double oldGridSpacingY = mGridSpacingY;
|
|
mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
|
|
// make sure that there are not more than 24 per day
|
|
mGridSpacingY = (double)height()/(double)mRows;
|
|
if (mGridSpacingY<mDesiredGridSpacingY) mGridSpacingY=mDesiredGridSpacingY;
|
|
|
|
//can be two doubles equal?, it's better to compare them with an epsilon
|
|
if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 )
|
|
resizeContents( int( mGridSpacingX * mColumns ),
|
|
int( mGridSpacingY * mRows ) );
|
|
|
|
calculateWorkingHours();
|
|
|
|
marcus_bains();
|
|
}
|
|
|
|
void KOAgenda::checkScrollBoundaries()
|
|
{
|
|
// Invalidate old values to force update
|
|
mOldLowerScrollValue = -1;
|
|
mOldUpperScrollValue = -1;
|
|
|
|
checkScrollBoundaries(verticalScrollBar()->value());
|
|
}
|
|
|
|
void KOAgenda::checkScrollBoundaries( int v )
|
|
{
|
|
int yMin = int( (v) / mGridSpacingY );
|
|
int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
|
|
|
|
// kdDebug(5850) << "--- yMin: " << yMin << " yMax: " << yMax << endl;
|
|
|
|
if ( yMin != mOldLowerScrollValue ) {
|
|
mOldLowerScrollValue = yMin;
|
|
emit lowerYChanged(yMin);
|
|
}
|
|
if ( yMax != mOldUpperScrollValue ) {
|
|
mOldUpperScrollValue = yMax;
|
|
emit upperYChanged(yMax);
|
|
}
|
|
}
|
|
|
|
int KOAgenda::visibleContentsYMin()
|
|
{
|
|
int v = verticalScrollBar()->value();
|
|
return int( v / mGridSpacingY );
|
|
}
|
|
|
|
int KOAgenda::visibleContentsYMax()
|
|
{
|
|
int v = verticalScrollBar()->value();
|
|
return int( ( v + visibleHeight() ) / mGridSpacingY );
|
|
}
|
|
|
|
void KOAgenda::deselectItem()
|
|
{
|
|
if (mSelectedItem.isNull()) return;
|
|
mSelectedItem->select(false);
|
|
mSelectedItem = 0;
|
|
}
|
|
|
|
void KOAgenda::selectItem(KOAgendaItem *item)
|
|
{
|
|
if ((KOAgendaItem *)mSelectedItem == item) return;
|
|
deselectItem();
|
|
if (item == 0) {
|
|
emit incidenceSelected( 0 );
|
|
return;
|
|
}
|
|
mSelectedItem = item;
|
|
mSelectedItem->select();
|
|
assert( mSelectedItem->incidence() );
|
|
mSelectedUid = mSelectedItem->incidence()->uid();
|
|
emit incidenceSelected( mSelectedItem->incidence() );
|
|
}
|
|
|
|
void KOAgenda::selectItemByUID( const QString& uid )
|
|
{
|
|
KOAgendaItem *item;
|
|
for ( item = mItems.first(); item != 0; item = mItems.next() ) {
|
|
if( item->incidence() && item->incidence()->uid() == uid ) {
|
|
selectItem( item );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This function seems never be called.
|
|
void KOAgenda::keyPressEvent( QKeyEvent *kev )
|
|
{
|
|
switch(kev->key()) {
|
|
case Key_PageDown:
|
|
verticalScrollBar()->addPage();
|
|
break;
|
|
case Key_PageUp:
|
|
verticalScrollBar()->subtractPage();
|
|
break;
|
|
case Key_Down:
|
|
verticalScrollBar()->addLine();
|
|
break;
|
|
case Key_Up:
|
|
verticalScrollBar()->subtractLine();
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
void KOAgenda::calculateWorkingHours()
|
|
{
|
|
mWorkingHoursEnable = !mAllDayMode;
|
|
|
|
QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
|
|
mWorkingHoursYTop = int( 4 * mGridSpacingY *
|
|
( tmp.hour() + tmp.minute() / 60. +
|
|
tmp.second() / 3600. ) );
|
|
tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
|
|
mWorkingHoursYBottom = int( 4 * mGridSpacingY *
|
|
( tmp.hour() + tmp.minute() / 60. +
|
|
tmp.second() / 3600. ) - 1 );
|
|
}
|
|
|
|
|
|
DateList KOAgenda::dateList() const
|
|
{
|
|
return mSelectedDates;
|
|
}
|
|
|
|
void KOAgenda::setDateList(const DateList &selectedDates)
|
|
{
|
|
mSelectedDates = selectedDates;
|
|
marcus_bains();
|
|
}
|
|
|
|
void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
|
|
{
|
|
mHolidayMask = mask;
|
|
|
|
}
|
|
|
|
void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
|
|
{
|
|
kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
|
|
QScrollView::contentsMousePressEvent(event);
|
|
}
|
|
|
|
void KOAgenda::setTypeAheadReceiver( QObject *o )
|
|
{
|
|
mTypeAheadReceiver = o;
|
|
}
|
|
|
|
QObject *KOAgenda::typeAheadReceiver() const
|
|
{
|
|
return mTypeAheadReceiver;
|
|
}
|