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 <kstandarddirs.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 
59 KOEditorGeneralEvent::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 
69 KOEditorGeneralEvent::~KOEditorGeneralEvent()
70 {
71 }
72 
73 void 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 
94 void 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 
178 void 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 
196 void 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 
220 void KOEditorGeneralEvent::timeStuffDisable(bool disable)
221 {
222  mStartTimeEdit->setEnabled( !disable );
223  mEndTimeEdit->setEnabled( !disable );
224 
225  setDuration();
226  emitDateTimeStr();
227 }
228 
229 void KOEditorGeneralEvent::associateTime(bool time)
230 {
231  timeStuffDisable(time);
232  allDayChanged(time);
233 }
234 
235 void 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 
254 void 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 
270 void 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 
280 void 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 
296 void 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 
307 void 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 
318 void 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 
372 void 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 
424 void 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 
469 void 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 
488 bool 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 
541 void 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