korganizer

koeditorgeneralevent.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 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 As a special exception, permission is given to link this program
21 with any edition of TQt, and distribute the resulting executable,
22 without including the source code for TQt in the source distribution.
23*/
24
25#include <tqtooltip.h>
26#include <tqlayout.h>
27#include <tqvbox.h>
28#include <tqbuttongroup.h>
29#include <tqvgroupbox.h>
30#include <tqwidgetstack.h>
31#include <tqspinbox.h>
32#include <tqdatetime.h>
33#include <tqlabel.h>
34#include <tqcheckbox.h>
35#include <tqcombobox.h>
36#include <tqpushbutton.h>
37#include <tqwhatsthis.h>
38
39#include <kdebug.h>
40#include <tdeglobal.h>
41#include <tdelocale.h>
42#include <tdemessagebox.h>
43#include <tdefiledialog.h>
44#include <tdestandarddirs.h>
45#include <ktextedit.h>
46
47#include <libkcal/event.h>
48#include <libkcal/incidenceformatter.h>
49
50#include "ktimeedit.h"
51#include <libtdepim/kdateedit.h>
52
53#include "koprefs.h"
54#include "koglobals.h"
55
56#include "koeditorgeneralevent.h"
57#include "koeditorgeneralevent.moc"
58
59KOEditorGeneralEvent::KOEditorGeneralEvent(TQObject* parent,
60 const char* name) :
61 KOEditorGeneral( parent, name)
62{
63 connect( this, TQ_SIGNAL( dateTimesChanged( const TQDateTime &, const TQDateTime & )),
64 TQ_SLOT( setDuration() ) );
65 connect( this, TQ_SIGNAL( dateTimesChanged( const TQDateTime &, const TQDateTime & )),
66 TQ_SLOT( emitDateTimeStr() ));
67}
68
69KOEditorGeneralEvent::~KOEditorGeneralEvent()
70{
71}
72
73void KOEditorGeneralEvent::finishSetup()
74{
75 TQWidget::setTabOrder( mSummaryEdit, mLocationEdit );
76 TQWidget::setTabOrder( mLocationEdit, mStartDateEdit );
77 TQWidget::setTabOrder( mStartDateEdit, mStartTimeEdit );
78 TQWidget::setTabOrder( mStartTimeEdit, mEndDateEdit );
79 TQWidget::setTabOrder( mEndDateEdit, mEndTimeEdit );
80 TQWidget::setTabOrder( mEndTimeEdit, mAlldayEventCheckbox );
81 TQWidget::setTabOrder( mAlldayEventCheckbox, mRecEditButton );
82 TQWidget::setTabOrder( mRecEditButton, mAlarmButton );
83 TQWidget::setTabOrder( mAlarmButton, mAlarmTimeEdit );
84 TQWidget::setTabOrder( mAlarmTimeEdit, mAlarmIncrCombo );
85 TQWidget::setTabOrder( mAlarmIncrCombo, mAlarmAdvancedButton );
86 TQWidget::setTabOrder( mAlarmAdvancedButton, mFreeTimeCombo );
87 TQWidget::setTabOrder( mFreeTimeCombo, mDescriptionEdit );
88 TQWidget::setTabOrder( mDescriptionEdit, mCategoriesButton );
89 TQWidget::setTabOrder( mCategoriesButton, mSecrecyCombo );
90
91 mSummaryEdit->setFocus();
92}
93
94void KOEditorGeneralEvent::initTime(TQWidget *parent,TQBoxLayout *topLayout)
95{
96 TQBoxLayout *timeLayout = new TQVBoxLayout(topLayout);
97
98 TQGroupBox *timeGroupBox = new TQGroupBox(1,TQt::Horizontal,
99 i18n("Date && Time"),parent);
100 TQWhatsThis::add( timeGroupBox,
101 i18n("Sets options related to the date and time of the "
102 "event or to-do.") );
103 timeLayout->addWidget(timeGroupBox);
104
105 TQFrame *timeBoxFrame = new TQFrame(timeGroupBox);
106
107 TQGridLayout *layoutTimeBox = new TQGridLayout( timeBoxFrame );
108 layoutTimeBox->setSpacing(topLayout->spacing());
109 layoutTimeBox->setColStretch( 3, 1 );
110
111 mStartDateLabel = new TQLabel(i18n("&Start:"),timeBoxFrame);
112 layoutTimeBox->addWidget(mStartDateLabel,0,0);
113
114 mStartDateEdit = new KDateEdit(timeBoxFrame);
115 layoutTimeBox->addWidget(mStartDateEdit,0,1);
116 mStartDateLabel->setBuddy( mStartDateEdit );
117
118 mStartTimeEdit = new KTimeEdit(timeBoxFrame);
119 layoutTimeBox->addWidget(mStartTimeEdit,0,2);
120
121 mEndDateLabel = new TQLabel(i18n("&End:"),timeBoxFrame);
122 layoutTimeBox->addWidget(mEndDateLabel,1,0);
123
124 mEndDateEdit = new KDateEdit(timeBoxFrame);
125 layoutTimeBox->addWidget(mEndDateEdit,1,1);
126 mEndDateLabel->setBuddy( mEndDateEdit );
127
128 mEndTimeEdit = new KTimeEdit(timeBoxFrame);
129 layoutTimeBox->addWidget(mEndTimeEdit,1,2);
130
131 mAlldayEventCheckbox = new TQCheckBox(i18n("All-&day"),timeBoxFrame);
132 layoutTimeBox->addWidget( mAlldayEventCheckbox, 0, 3 );
133 connect(mAlldayEventCheckbox, TQ_SIGNAL(toggled(bool)),TQ_SLOT(associateTime(bool)));
134
135 mDurationLabel = new TQLabel( timeBoxFrame );
136 layoutTimeBox->addWidget( mDurationLabel, 1, 3 );
137
138 // time widgets are checked if they contain a valid time
139 connect(mStartTimeEdit, TQ_SIGNAL(timeChanged(TQTime)),
140 this, TQ_SLOT(startTimeChanged(TQTime)));
141 connect(mEndTimeEdit, TQ_SIGNAL(timeChanged(TQTime)),
142 this, TQ_SLOT(endTimeChanged(TQTime)));
143
144 // date widgets are checked if they contain a valid date
145 connect(mStartDateEdit, TQ_SIGNAL(dateChanged(const TQDate&)),
146 this, TQ_SLOT(startDateChanged(const TQDate&)));
147 connect(mEndDateEdit, TQ_SIGNAL(dateChanged(const TQDate&)),
148 this, TQ_SLOT(endDateChanged(const TQDate&)));
149
150 TQLabel *label = new TQLabel( i18n( "Recurrence:" ), timeBoxFrame );
151 layoutTimeBox->addWidget( label, 2, 0 );
152 TQBoxLayout *recLayout = new TQHBoxLayout();
153 layoutTimeBox->addMultiCellLayout( recLayout, 2, 2, 1, 4 );
154 mRecEditButton = new TQPushButton( timeBoxFrame );
155 mRecEditButton->setIconSet( KOGlobals::self()->smallIconSet( "recur", 16 ) );
156 recLayout->addWidget( mRecEditButton );
157 connect( mRecEditButton, TQ_SIGNAL(clicked()), TQ_SIGNAL(editRecurrence()) );
158 mRecEditLabel = new TQLabel( TQString(), timeBoxFrame );
159 recLayout->addWidget( mRecEditLabel );
160 recLayout->addStretch( 1 );
161
162 label = new TQLabel( i18n("Reminder:"), timeBoxFrame );
163 layoutTimeBox->addWidget( label, 3, 0 );
164 TQBoxLayout *alarmLineLayout = new TQHBoxLayout();
165 layoutTimeBox->addMultiCellLayout( alarmLineLayout, 3, 3, 1, 4 );
166 initAlarm( timeBoxFrame, alarmLineLayout );
167 alarmLineLayout->addStretch( 1 );
168
169 TQBoxLayout *secLayout = new TQHBoxLayout();
170 layoutTimeBox->addLayout( secLayout, 0, 4 );
171 initSecrecy( timeBoxFrame, secLayout );
172
173 TQBoxLayout *classLayout = new TQHBoxLayout();
174 layoutTimeBox->addLayout( classLayout, 1, 4 );
175 initClass( timeBoxFrame, classLayout );
176}
177
178void KOEditorGeneralEvent::initClass(TQWidget *parent,TQBoxLayout *topLayout)
179{
180 TQBoxLayout *classLayout = new TQHBoxLayout(topLayout);
181
182 TQLabel *freeTimeLabel = new TQLabel(i18n("S&how time as:"),parent);
183 TQString whatsThis = i18n("Sets how this time will appear on your Free/Busy "
184 "information.");
185 TQWhatsThis::add( freeTimeLabel, whatsThis );
186 classLayout->addWidget(freeTimeLabel);
187
188 mFreeTimeCombo = new TQComboBox(false, parent);
189 TQWhatsThis::add( mFreeTimeCombo, whatsThis );
190 mFreeTimeCombo->insertItem(i18n("Busy"));
191 mFreeTimeCombo->insertItem(i18n("Free"));
192 classLayout->addWidget(mFreeTimeCombo);
193 freeTimeLabel->setBuddy( mFreeTimeCombo );
194}
195
196void KOEditorGeneralEvent::initInvitationBar(TQWidget * parent, TQBoxLayout * layout)
197{
198 TQBoxLayout *topLayout = new TQHBoxLayout( layout );
199 mInvitationBar = new TQFrame( parent );
200 mInvitationBar->setPaletteBackgroundColor( TDEGlobalSettings::alternateBackgroundColor() );
201 topLayout->addWidget( mInvitationBar );
202
203 TQBoxLayout *barLayout = new TQHBoxLayout( mInvitationBar );
204 barLayout->setSpacing( layout->spacing() );
205 TQLabel *label = new TQLabel( i18n("You have not yet definitely responded to this invitation." ), mInvitationBar );
206 barLayout->addWidget( label );
207 barLayout->addStretch( 1 );
208 TQPushButton *button = new TQPushButton( i18n("Accept"), mInvitationBar );
209 connect( button, TQ_SIGNAL(clicked()), TQ_SIGNAL(acceptInvitation()) );
210 connect( button, TQ_SIGNAL(clicked()), mInvitationBar, TQ_SLOT(hide()) );
211 barLayout->addWidget( button );
212 button = new TQPushButton( i18n("Decline"), mInvitationBar );
213 connect( button, TQ_SIGNAL(clicked()), TQ_SIGNAL(declineInvitation()) );
214 connect( button, TQ_SIGNAL(clicked()), mInvitationBar, TQ_SLOT(hide()) );
215 barLayout->addWidget( button );
216
217 mInvitationBar->hide();
218}
219
220void KOEditorGeneralEvent::timeStuffDisable(bool disable)
221{
222 mStartTimeEdit->setEnabled( !disable );
223 mEndTimeEdit->setEnabled( !disable );
224
225 setDuration();
226 emitDateTimeStr();
227}
228
229void KOEditorGeneralEvent::associateTime(bool time)
230{
231 timeStuffDisable(time);
232 allDayChanged(time);
233}
234
235void KOEditorGeneralEvent::setDateTimes( const TQDateTime &start, const TQDateTime &end )
236{
237// kdDebug(5850) << "KOEditorGeneralEvent::setDateTimes(): Start DateTime: " << start.toString() << endl;
238
239 mStartDateEdit->setDate(start.date());
240 // KTimeEdit seems to emit some signals when setTime() is called.
241 mStartTimeEdit->blockSignals( true );
242 mStartTimeEdit->setTime(start.time());
243 mStartTimeEdit->blockSignals( false );
244 mEndDateEdit->setDate(end.date());
245 mEndTimeEdit->setTime(end.time());
246
247 mCurrStartDateTime = start;
248 mCurrEndDateTime = end;
249
250 setDuration();
251 emitDateTimeStr();
252}
253
254void KOEditorGeneralEvent::startTimeChanged( TQTime newtime )
255{
256 kdDebug(5850) << "KOEditorGeneralEvent::startTimeChanged() " << TQString(newtime.toString()) << endl;
257
258 int secsep = mCurrStartDateTime.secsTo(mCurrEndDateTime);
259
260 mCurrStartDateTime.setTime(newtime);
261
262 // adjust end time so that the event has the same duration as before.
263 mCurrEndDateTime = mCurrStartDateTime.addSecs(secsep);
264 mEndTimeEdit->setTime(mCurrEndDateTime.time());
265 mEndDateEdit->setDate(mCurrEndDateTime.date());
266
267 emit dateTimesChanged(mCurrStartDateTime,mCurrEndDateTime);
268}
269
270void KOEditorGeneralEvent::endTimeChanged( TQTime newtime )
271{
272// kdDebug(5850) << "KOEditorGeneralEvent::endTimeChanged " << newtime.toString() << endl;
273
274 TQDateTime newdt(mCurrEndDateTime.date(), newtime);
275 mCurrEndDateTime = newdt;
276
277 emit dateTimesChanged(mCurrStartDateTime,mCurrEndDateTime);
278}
279
280void KOEditorGeneralEvent::startDateChanged( const TQDate &newdate )
281{
282 if ( !newdate.isValid() )
283 return;
284
285 int daysep = mCurrStartDateTime.daysTo(mCurrEndDateTime);
286
287 mCurrStartDateTime.setDate(newdate);
288
289 // adjust end date so that the event has the same duration as before
290 mCurrEndDateTime.setDate(mCurrStartDateTime.date().addDays(daysep));
291 mEndDateEdit->setDate(mCurrEndDateTime.date());
292
293 emit dateTimesChanged(mCurrStartDateTime,mCurrEndDateTime);
294}
295
296void KOEditorGeneralEvent::endDateChanged( const TQDate &newdate )
297{
298 if ( !newdate.isValid() )
299 return;
300
301 TQDateTime newdt(newdate, mCurrEndDateTime.time());
302 mCurrEndDateTime = newdt;
303
304 emit dateTimesChanged(mCurrStartDateTime,mCurrEndDateTime);
305}
306
307void KOEditorGeneralEvent::setDefaults( const TQDateTime &from,
308 const TQDateTime &to, bool allDay)
309{
310 KOEditorGeneral::setDefaults(allDay);
311
312 mAlldayEventCheckbox->setChecked(allDay);
313 timeStuffDisable(allDay);
314
315 setDateTimes(from,to);
316}
317
318void KOEditorGeneralEvent::readEvent( Event *event, Calendar *calendar, const TQDate &date, bool tmpl )
319{
320 TQString tmpStr;
321
322 mAlldayEventCheckbox->setChecked(event->doesFloat());
323 timeStuffDisable(event->doesFloat());
324
325 if ( !tmpl ) {
326 TQDateTime startDT = event->dtStart();
327 TQDateTime endDT = event->dtEnd();
328 if ( event->doesRecur() && date.isValid() ) {
329 // Consider the active date when editing recurring Events.
330 TQDateTime kdt( date, TQTime( 0, 0, 0 ) );
331 const int eventLength = startDT.daysTo( endDT );
332 kdt = kdt.addSecs( -1 );
333 startDT.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
334 if ( event->hasEndDate() ) {
335 endDT.setDate( startDT.addDays( eventLength ).date() );
336 } else {
337 if ( event->hasDuration() ) {
338 endDT = startDT.addSecs( event->duration() );
339 } else {
340 endDT = startDT;
341 }
342 }
343 }
344 // the rest is for the events only
345 setDateTimes( startDT, endDT );
346 }
347
348 switch( event->transparency() ) {
349 case Event::Transparent:
350 mFreeTimeCombo->setCurrentItem(1);
351 break;
352 case Event::Opaque:
353 mFreeTimeCombo->setCurrentItem(0);
354 break;
355 }
356
357 updateRecurrenceSummary( event );
358
359 Attendee *me = event->attendeeByMails( KOPrefs::instance()->allEmails() );
360 if ( event->attendeeCount() > 1 &&
361 me && ( me->status() == Attendee::NeedsAction ||
362 me->status() == Attendee::Tentative ||
363 me->status() == Attendee::InProcess ) ) {
364 mInvitationBar->show();
365 } else {
366 mInvitationBar->hide();
367 }
368
369 readIncidence(event, calendar);
370}
371
372void KOEditorGeneralEvent::writeEvent(Event *event)
373{
374// kdDebug(5850) << "KOEditorGeneralEvent::writeEvent()" << endl;
375
376 writeIncidence(event);
377
378 TQDate tmpDate;
379 TQTime tmpTime;
380 TQDateTime tmpDT;
381
382 // temp. until something better happens.
383 TQString tmpStr;
384
385 if (mAlldayEventCheckbox->isChecked()) {
386 event->setFloats(true);
387 // need to change this.
388 tmpDate = mStartDateEdit->date();
389 tmpTime.setHMS(0,0,0);
390 tmpDT.setDate(tmpDate);
391 tmpDT.setTime(tmpTime);
392 event->setDtStart(tmpDT);
393
394 tmpDate = mEndDateEdit->date();
395 tmpTime.setHMS(0,0,0);
396 tmpDT.setDate(tmpDate);
397 tmpDT.setTime(tmpTime);
398 event->setDtEnd(tmpDT);
399 } else {
400 event->setFloats(false);
401
402 // set date/time end
403 tmpDate = mEndDateEdit->date();
404 tmpTime = mEndTimeEdit->getTime();
405 tmpDT.setDate(tmpDate);
406 tmpDT.setTime(tmpTime);
407 event->setDtEnd(tmpDT);
408
409 // set date/time start
410 tmpDate = mStartDateEdit->date();
411 tmpTime = mStartTimeEdit->getTime();
412 tmpDT.setDate(tmpDate);
413 tmpDT.setTime(tmpTime);
414 event->setDtStart(tmpDT);
415 } // check for float
416
417 event->setTransparency(mFreeTimeCombo->currentItem() > 0
418 ? KCal::Event::Transparent
419 : KCal::Event::Opaque);
420
421// kdDebug(5850) << "KOEditorGeneralEvent::writeEvent() done" << endl;
422}
423
424void KOEditorGeneralEvent::setDuration()
425{
426 TQString tmpStr, catStr;
427 int hourdiff, minutediff;
428 // end<date is an accepted temporary state while typing, but don't show
429 // any duration if this happens
430 if(mCurrEndDateTime >= mCurrStartDateTime) {
431
432 if (mAlldayEventCheckbox->isChecked()) {
433 int daydiff = mCurrStartDateTime.date().daysTo(mCurrEndDateTime.date()) + 1;
434 tmpStr = i18n("Duration: ");
435 tmpStr.append(i18n("1 Day","%n Days",daydiff));
436 } else {
437 hourdiff = mCurrStartDateTime.date().daysTo(mCurrEndDateTime.date()) * 24;
438 hourdiff += mCurrEndDateTime.time().hour() -
439 mCurrStartDateTime.time().hour();
440 minutediff = mCurrEndDateTime.time().minute() -
441 mCurrStartDateTime.time().minute();
442 // If minutediff is negative, "borrow" 60 minutes from hourdiff
443 if (minutediff < 0 && hourdiff > 0) {
444 hourdiff -= 1;
445 minutediff += 60;
446 }
447 if (hourdiff || minutediff){
448 tmpStr = i18n("Duration: ");
449 if (hourdiff){
450 catStr = i18n("1 hour","%n hours",hourdiff);
451 tmpStr.append(catStr);
452 }
453 if (hourdiff && minutediff){
454 tmpStr += i18n(", ");
455 }
456 if (minutediff){
457 catStr = i18n("1 minute","%n minutes",minutediff);
458 tmpStr += catStr;
459 }
460 } else tmpStr = "";
461 }
462 }
463 mDurationLabel->setText(tmpStr);
464 TQWhatsThis::add( mDurationLabel,
465 i18n("Shows the duration of the event or to-do with the "
466 "current start and end dates and times.") );
467}
468
469void KOEditorGeneralEvent::emitDateTimeStr()
470{
471 TDELocale *l = TDEGlobal::locale();
472
473 TQString from,to;
474 if (mAlldayEventCheckbox->isChecked()) {
475 from = l->formatDate(mCurrStartDateTime.date());
476 to = l->formatDate(mCurrEndDateTime.date());
477 } else {
478 from = l->formatDateTime(mCurrStartDateTime);
479 to = l->formatDateTime(mCurrEndDateTime);
480 }
481
482 TQString str = i18n("From: %1 To: %2 %3").arg(from).arg(to)
483 .arg(mDurationLabel->text());
484
485 emit dateTimeStrChanged(str);
486}
487
488bool KOEditorGeneralEvent::validateInput()
489{
490// kdDebug(5850) << "KOEditorGeneralEvent::validateInput()" << endl;
491
492 if (!mAlldayEventCheckbox->isChecked()) {
493 if (!mStartTimeEdit->inputIsValid()) {
494 KMessageBox::sorry( 0,
495 i18n("Please specify a valid start time, for example '%1'.")
496 .arg( TDEGlobal::locale()->formatTime( TQTime::currentTime() ) ) );
497 return false;
498 }
499
500 if (!mEndTimeEdit->inputIsValid()) {
501 KMessageBox::sorry( 0,
502 i18n("Please specify a valid end time, for example '%1'.")
503 .arg( TDEGlobal::locale()->formatTime( TQTime::currentTime() ) ) );
504 return false;
505 }
506 }
507
508 if (!mStartDateEdit->date().isValid()) {
509 KMessageBox::sorry( 0,
510 i18n("Please specify a valid start date, for example '%1'.")
511 .arg( TDEGlobal::locale()->formatDate( TQDate::currentDate() ) ) );
512 return false;
513 }
514
515 if (!mEndDateEdit->date().isValid()) {
516 KMessageBox::sorry( 0,
517 i18n("Please specify a valid end date, for example '%1'.")
518 .arg( TDEGlobal::locale()->formatDate( TQDate::currentDate() ) ) );
519 return false;
520 }
521
522 TQDateTime startDt,endDt;
523 startDt.setDate(mStartDateEdit->date());
524 endDt.setDate(mEndDateEdit->date());
525 if (!mAlldayEventCheckbox->isChecked()) {
526 startDt.setTime(mStartTimeEdit->getTime());
527 endDt.setTime(mEndTimeEdit->getTime());
528 }
529
530 if ( startDt > endDt ) {
531 KMessageBox::sorry(
532 0,
533 i18n( "The event ends before it starts.\n"
534 "Please correct dates and times." ) );
535 return false;
536 }
537
538 return KOEditorGeneral::validateInput();
539}
540
541void KOEditorGeneralEvent::updateRecurrenceSummary( Event *event )
542{
543 if ( event->doesRecur() ) {
544 mRecEditLabel->setText( IncidenceFormatter::recurrenceString( event ) );
545 } else {
546 mRecEditLabel->setText( TQString() );
547 }
548}
PartStat status() const
Transparency transparency() const
bool hasEndDate() const
int attendeeCount() const
bool doesFloat() const
bool doesRecur() const
Recurrence * recurrence() const
TQDateTime getNextDateTime(const TQDateTime &preDateTime) const