korganizer

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