kalarm

messagewin.cpp

00001 /*
00002  *  messagewin.cpp  -  displays an alarm message
00003  *  Program:  kalarm
00004  *  Copyright © 2001-2009 by David Jarvie <djarvie@kde.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kalarm.h"
00022 
00023 #include <stdlib.h>
00024 #include <string.h>
00025 
00026 #include <tqfile.h>
00027 #include <tqfileinfo.h>
00028 #include <tqlayout.h>
00029 #include <tqpushbutton.h>
00030 #include <tqlabel.h>
00031 #include <tqwhatsthis.h>
00032 #include <tqtooltip.h>
00033 #include <tqdragobject.h>
00034 #include <tqtextedit.h>
00035 #include <tqtimer.h>
00036 
00037 #include <kstandarddirs.h>
00038 #include <tdeaction.h>
00039 #include <kstdguiitem.h>
00040 #include <tdeaboutdata.h>
00041 #include <tdelocale.h>
00042 #include <tdeconfig.h>
00043 #include <kiconloader.h>
00044 #include <kdialog.h>
00045 #include <ktextbrowser.h>
00046 #include <tdeglobalsettings.h>
00047 #include <kmimetype.h>
00048 #include <tdemessagebox.h>
00049 #include <twin.h>
00050 #include <twinmodule.h>
00051 #include <kprocess.h>
00052 #include <tdeio/netaccess.h>
00053 #include <knotifyclient.h>
00054 #include <kpushbutton.h>
00055 #ifdef WITHOUT_ARTS
00056 #include <kaudioplayer.h>
00057 #else
00058 #include <arts/kartsdispatcher.h>
00059 #include <arts/kartsserver.h>
00060 #include <arts/kplayobjectfactory.h>
00061 #include <arts/kplayobject.h>
00062 #endif
00063 #include <dcopclient.h>
00064 #include <kdebug.h>
00065 
00066 #include "alarmcalendar.h"
00067 #include "deferdlg.h"
00068 #include "editdlg.h"
00069 #include "functions.h"
00070 #include "kalarmapp.h"
00071 #include "mainwindow.h"
00072 #include "preferences.h"
00073 #include "synchtimer.h"
00074 #include "messagewin.moc"
00075 
00076 using namespace KCal;
00077 
00078 #ifndef WITHOUT_ARTS
00079 static const char* KMIX_APP_NAME    = "kmix";
00080 static const char* KMIX_DCOP_OBJECT = "Mixer0";
00081 static const char* KMIX_DCOP_WINDOW = "kmix-mainwindow#1";
00082 #endif
00083 static const char* KMAIL_DCOP_OBJECT = "KMailIface";
00084 
00085 // The delay for enabling message window buttons if a zero delay is
00086 // configured, i.e. the windows are placed far from the cursor.
00087 static const int proximityButtonDelay = 1000;    // (milliseconds)
00088 static const int proximityMultiple = 10;         // multiple of button height distance from cursor for proximity
00089 
00090 static bool wantModal();
00091 
00092 // A text label widget which can be scrolled and copied with the mouse
00093 class MessageText : public TQTextEdit
00094 {
00095     public:
00096         MessageText(const TQString& text, const TQString& context = TQString(), TQWidget* parent = 0, const char* name = 0)
00097         : TQTextEdit(text, context, parent, name)
00098         {
00099             setReadOnly(true);
00100             setWordWrap(TQTextEdit::NoWrap);
00101         }
00102         int scrollBarHeight() const     { return horizontalScrollBar()->height(); }
00103         int scrollBarWidth() const      { return verticalScrollBar()->width(); }
00104         virtual TQSize sizeHint() const  { return TQSize(contentsWidth() + scrollBarWidth(), contentsHeight() + scrollBarHeight()); }
00105 };
00106 
00107 
00108 class MWMimeSourceFactory : public TQMimeSourceFactory
00109 {
00110     public:
00111         MWMimeSourceFactory(const TQString& absPath, KTextBrowser*);
00112         virtual ~MWMimeSourceFactory();
00113         virtual const TQMimeSource* data(const TQString& abs_name) const;
00114     private:
00115         // Prohibit the following methods
00116         virtual void setData(const TQString&, TQMimeSource*) {}
00117         virtual void setExtensionType(const TQString&, const char*) {}
00118 
00119         TQString   mTextFile;
00120         TQCString  mMimeType;
00121         mutable const TQMimeSource* mLast;
00122 };
00123 
00124 
00125 // Basic flags for the window
00126 static const TQt::WFlags WFLAGS = TQt::WStyle_StaysOnTop | TQt::WDestructiveClose;
00127 
00128 // Error message bit masks
00129 enum {
00130     ErrMsg_Speak     = 0x01,
00131     ErrMsg_AudioFile = 0x02,
00132     ErrMsg_Volume    = 0x04
00133 };
00134 
00135 
00136 TQValueList<MessageWin*> MessageWin::mWindowList;
00137 TQMap<TQString, unsigned> MessageWin::mErrorMessages;
00138 
00139 
00140 /******************************************************************************
00141 *  Construct the message window for the specified alarm.
00142 *  Other alarms in the supplied event may have been updated by the caller, so
00143 *  the whole event needs to be stored for updating the calendar file when it is
00144 *  displayed.
00145 */
00146 MessageWin::MessageWin(const KAEvent& event, const KAAlarm& alarm, bool reschedule_event, bool allowDefer)
00147     : MainWindowBase(0, "MessageWin", WFLAGS | TQt::WGroupLeader | TQt::WStyle_ContextHelp
00148                                              | (wantModal() ? 0 : TQt::WX11BypassWM)),
00149       mMessage(event.cleanText()),
00150       mFont(event.font()),
00151       mBgColour(event.bgColour()),
00152       mFgColour(event.fgColour()),
00153       mDateTime((alarm.type() & KAAlarm::REMINDER_ALARM) ? event.mainDateTime(true) : alarm.dateTime(true)),
00154       mEventID(event.id()),
00155       mAudioFile(event.audioFile()),
00156       mVolume(event.soundVolume()),
00157       mFadeVolume(event.fadeVolume()),
00158       mFadeSeconds(TQMIN(event.fadeSeconds(), 86400)),
00159       mDefaultDeferMinutes(event.deferDefaultMinutes()),
00160       mAlarmType(alarm.type()),
00161       mAction(event.action()),
00162       mKMailSerialNumber(event.kmailSerialNumber()),
00163       mRestoreHeight(0),
00164       mAudioRepeat(event.repeatSound()),
00165       mConfirmAck(event.confirmAck()),
00166       mShowEdit(!mEventID.isEmpty()),
00167       mNoDefer(!allowDefer || alarm.repeatAtLogin()),
00168       mInvalid(false),
00169       mArtsDispatcher(0),
00170       mPlayObject(0),
00171       mOldVolume(-1),
00172       mEvent(event),
00173       mEditButton(0),
00174       mDeferButton(0),
00175       mSilenceButton(0),
00176       mDeferDlg(0),
00177       mWinModule(0),
00178       mFlags(event.flags()),
00179       mLateCancel(event.lateCancel()),
00180       mErrorWindow(false),
00181       mNoPostAction(alarm.type() & KAAlarm::REMINDER_ALARM),
00182       mRecreating(false),
00183       mBeep(event.beep()),
00184       mSpeak(event.speak()),
00185       mRescheduleEvent(reschedule_event),
00186       mShown(false),
00187       mPositioning(false),
00188       mNoCloseConfirm(false),
00189       mDisableDeferral(false)
00190 {
00191     kdDebug(5950) << "MessageWin::MessageWin(event)" << endl;
00192     // Set to save settings automatically, but don't save window size.
00193     // File alarm window size is saved elsewhere.
00194     setAutoSaveSettings(TQString::fromLatin1("MessageWin"), false);
00195     initView();
00196     mWindowList.append(this);
00197     if (event.autoClose())
00198         mCloseTime = alarm.dateTime().dateTime().addSecs(event.lateCancel() * 60);
00199 }
00200 
00201 /******************************************************************************
00202 *  Construct the message window for a specified error message.
00203 */
00204 MessageWin::MessageWin(const KAEvent& event, const DateTime& alarmDateTime, const TQStringList& errmsgs)
00205     : MainWindowBase(0, "MessageWin", WFLAGS | TQt::WGroupLeader | TQt::WStyle_ContextHelp),
00206       mMessage(event.cleanText()),
00207       mDateTime(alarmDateTime),
00208       mEventID(event.id()),
00209       mAlarmType(KAAlarm::MAIN_ALARM),
00210       mAction(event.action()),
00211       mKMailSerialNumber(0),
00212       mErrorMsgs(errmsgs),
00213       mRestoreHeight(0),
00214       mConfirmAck(false),
00215       mShowEdit(false),
00216       mNoDefer(true),
00217       mInvalid(false),
00218       mArtsDispatcher(0),
00219       mPlayObject(0),
00220       mEvent(event),
00221       mEditButton(0),
00222       mDeferButton(0),
00223       mSilenceButton(0),
00224       mDeferDlg(0),
00225       mWinModule(0),
00226       mErrorWindow(true),
00227       mNoPostAction(true),
00228       mRecreating(false),
00229       mRescheduleEvent(false),
00230       mShown(false),
00231       mPositioning(false),
00232       mNoCloseConfirm(false),
00233       mDisableDeferral(false)
00234 {
00235     kdDebug(5950) << "MessageWin::MessageWin(errmsg)" << endl;
00236     initView();
00237     mWindowList.append(this);
00238 }
00239 
00240 /******************************************************************************
00241 *  Construct the message window for restoration by session management.
00242 *  The window is initialised by readProperties().
00243 */
00244 MessageWin::MessageWin()
00245     : MainWindowBase(0, "MessageWin", WFLAGS),
00246       mArtsDispatcher(0),
00247       mPlayObject(0),
00248       mEditButton(0),
00249       mDeferButton(0),
00250       mSilenceButton(0),
00251       mDeferDlg(0),
00252       mWinModule(0),
00253       mErrorWindow(false),
00254       mRecreating(false),
00255       mRescheduleEvent(false),
00256       mShown(false),
00257       mPositioning(false),
00258       mNoCloseConfirm(false),
00259       mDisableDeferral(false)
00260 {
00261     kdDebug(5950) << "MessageWin::MessageWin(restore)\n";
00262     mWindowList.append(this);
00263 }
00264 
00265 /******************************************************************************
00266 * Destructor. Perform any post-alarm actions before tidying up.
00267 */
00268 MessageWin::~MessageWin()
00269 {
00270     kdDebug(5950) << "MessageWin::~MessageWin(" << mEventID << ")" << endl;
00271     stopPlay();
00272     delete mWinModule;
00273     mWinModule = 0;
00274     mErrorMessages.remove(mEventID);
00275     mWindowList.remove(this);
00276     if (!mRecreating)
00277     {
00278         if (!mNoPostAction  &&  !mEvent.postAction().isEmpty())
00279             theApp()->alarmCompleted(mEvent);
00280         if (!mWindowList.count())
00281             theApp()->quitIf();
00282     }
00283 }
00284 
00285 /******************************************************************************
00286 *  Construct the message window.
00287 */
00288 void MessageWin::initView()
00289 {
00290     bool reminder = (!mErrorWindow  &&  (mAlarmType & KAAlarm::REMINDER_ALARM));
00291     int leading = fontMetrics().leading();
00292     setCaption((mAlarmType & KAAlarm::REMINDER_ALARM) ? i18n("Reminder") : i18n("Message"));
00293     TQWidget* topWidget = new TQWidget(this, "messageWinTop");
00294     setCentralWidget(topWidget);
00295     TQVBoxLayout* topLayout = new TQVBoxLayout(topWidget, KDialog::marginHint(), KDialog::spacingHint());
00296 
00297     if (mDateTime.isValid())
00298     {
00299         // Show the alarm date/time, together with an "Advance reminder" text where appropriate
00300         TQFrame* frame = 0;
00301         TQVBoxLayout* layout = topLayout;
00302         if (reminder)
00303         {
00304             frame = new TQFrame(topWidget);
00305             frame->setFrameStyle(TQFrame::Box | TQFrame::Raised);
00306             topLayout->addWidget(frame, 0, TQt::AlignHCenter);
00307             layout = new TQVBoxLayout(frame, leading + frame->frameWidth(), leading);
00308         }
00309 
00310         // Alarm date/time
00311         TQLabel* label = new TQLabel(frame ? frame : topWidget);
00312         label->setText(mDateTime.isDateOnly()
00313                        ? TDEGlobal::locale()->formatDate(mDateTime.date(), true)
00314                        : TDEGlobal::locale()->formatDateTime(mDateTime.dateTime()));
00315         if (!frame)
00316             label->setFrameStyle(TQFrame::Box | TQFrame::Raised);
00317         label->setFixedSize(label->sizeHint());
00318         layout->addWidget(label, 0, TQt::AlignHCenter);
00319         TQWhatsThis::add(label,
00320               i18n("The scheduled date/time for the message (as opposed to the actual time of display)."));
00321 
00322         if (frame)
00323         {
00324             label = new TQLabel(frame);
00325             label->setText(i18n("Reminder"));
00326             label->setFixedSize(label->sizeHint());
00327             layout->addWidget(label, 0, TQt::AlignHCenter);
00328             frame->setFixedSize(frame->sizeHint());
00329         }
00330     }
00331 
00332     if (!mErrorWindow)
00333     {
00334         // It's a normal alarm message window
00335         switch (mAction)
00336         {
00337             case KAEvent::FILE:
00338             {
00339                 // Display the file name
00340                 TQLabel* label = new TQLabel(mMessage, topWidget);
00341                 label->setFrameStyle(TQFrame::Box | TQFrame::Raised);
00342                 label->setFixedSize(label->sizeHint());
00343                 TQWhatsThis::add(label, i18n("The file whose contents are displayed below"));
00344                 topLayout->addWidget(label, 0, TQt::AlignHCenter);
00345 
00346                 // Display contents of file
00347                 bool opened = false;
00348                 bool dir = false;
00349                 TQString tmpFile;
00350                 KURL url(mMessage);
00351                 if (TDEIO::NetAccess::download(url, tmpFile, MainWindow::mainMainWindow()))
00352                 {
00353                     TQFile qfile(tmpFile);
00354                     TQFileInfo info(qfile);
00355                     if (!(dir = info.isDir()))
00356                     {
00357                         opened = true;
00358                         KTextBrowser* view = new KTextBrowser(topWidget, "fileContents");
00359                         MWMimeSourceFactory msf(tmpFile, view);
00360                         view->setMinimumSize(view->sizeHint());
00361                         topLayout->addWidget(view);
00362 
00363                         // Set the default size to 20 lines square.
00364                         // Note that after the first file has been displayed, this size
00365                         // is overridden by the user-set default stored in the config file.
00366                         // So there is no need to calculate an accurate size.
00367                         int h = 20*view->fontMetrics().lineSpacing() + 2*view->frameWidth();
00368                         view->resize(TQSize(h, h).expandedTo(view->sizeHint()));
00369                         TQWhatsThis::add(view, i18n("The contents of the file to be displayed"));
00370                     }
00371                     TDEIO::NetAccess::removeTempFile(tmpFile);
00372                 }
00373                 if (!opened)
00374                 {
00375                     // File couldn't be opened
00376                     bool exists = TDEIO::NetAccess::exists(url, true, MainWindow::mainMainWindow());
00377                     mErrorMsgs += dir ? i18n("File is a folder") : exists ? i18n("Failed to open file") : i18n("File not found");
00378                 }
00379                 break;
00380             }
00381             case KAEvent::MESSAGE:
00382             {
00383                 // Message label
00384                 // Using MessageText instead of TQLabel allows scrolling and mouse copying
00385                 MessageText* text = new MessageText(mMessage, TQString(), topWidget);
00386                 text->setFrameStyle(TQFrame::NoFrame);
00387                 text->setPaper(mBgColour);
00388                 text->setPaletteForegroundColor(mFgColour);
00389                 text->setFont(mFont);
00390                 int lineSpacing = text->fontMetrics().lineSpacing();
00391                 TQSize s = text->sizeHint();
00392                 int h = s.height();
00393                 text->setMaximumHeight(h + text->scrollBarHeight());
00394                 text->setMinimumHeight(TQMIN(h, lineSpacing*4));
00395                 text->setMaximumWidth(s.width() + text->scrollBarWidth());
00396                 TQWhatsThis::add(text, i18n("The alarm message"));
00397                 int vspace = lineSpacing/2;
00398                 int hspace = lineSpacing - KDialog::marginHint();
00399                 topLayout->addSpacing(vspace);
00400                 topLayout->addStretch();
00401                 // Don't include any horizontal margins if message is 2/3 screen width
00402                 if (!mWinModule)
00403                     mWinModule = new KWinModule(0, KWinModule::INFO_DESKTOP);
00404                 if (text->sizeHint().width() >= mWinModule->workArea().width()*2/3)
00405                     topLayout->addWidget(text, 1, TQt::AlignHCenter);
00406                 else
00407                 {
00408                     TQBoxLayout* layout = new TQHBoxLayout(topLayout);
00409                     layout->addSpacing(hspace);
00410                     layout->addWidget(text, 1, TQt::AlignHCenter);
00411                     layout->addSpacing(hspace);
00412                 }
00413                 if (!reminder)
00414                     topLayout->addStretch();
00415                 break;
00416             }
00417             case KAEvent::COMMAND:
00418             case KAEvent::EMAIL:
00419             default:
00420                 break;
00421         }
00422 
00423         if (reminder)
00424         {
00425             // Reminder: show remaining time until the actual alarm
00426             mRemainingText = new TQLabel(topWidget);
00427             mRemainingText->setFrameStyle(TQFrame::Box | TQFrame::Raised);
00428             mRemainingText->setMargin(leading);
00429             if (mDateTime.isDateOnly()  ||  TQDate::currentDate().daysTo(mDateTime.date()) > 0)
00430             {
00431                 setRemainingTextDay();
00432                 MidnightTimer::connect(TQT_TQOBJECT(this), TQT_SLOT(setRemainingTextDay()));    // update every day
00433             }
00434             else
00435             {
00436                 setRemainingTextMinute();
00437                 MinuteTimer::connect(TQT_TQOBJECT(this), TQT_SLOT(setRemainingTextMinute()));   // update every minute
00438             }
00439             topLayout->addWidget(mRemainingText, 0, TQt::AlignHCenter);
00440             topLayout->addSpacing(KDialog::spacingHint());
00441             topLayout->addStretch();
00442         }
00443     }
00444     else
00445     {
00446         // It's an error message
00447         switch (mAction)
00448         {
00449             case KAEvent::EMAIL:
00450             {
00451                 // Display the email addresses and subject.
00452                 TQFrame* frame = new TQFrame(topWidget);
00453                 frame->setFrameStyle(TQFrame::Box | TQFrame::Raised);
00454                 TQWhatsThis::add(frame, i18n("The email to send"));
00455                 topLayout->addWidget(frame, 0, TQt::AlignHCenter);
00456                 TQGridLayout* grid = new TQGridLayout(frame, 2, 2, KDialog::marginHint(), KDialog::spacingHint());
00457 
00458                 TQLabel* label = new TQLabel(i18n("Email addressee", "To:"), frame);
00459                 label->setFixedSize(label->sizeHint());
00460                 grid->addWidget(label, 0, 0, TQt::AlignLeft);
00461                 label = new TQLabel(mEvent.emailAddresses("\n"), frame);
00462                 label->setFixedSize(label->sizeHint());
00463                 grid->addWidget(label, 0, 1, TQt::AlignLeft);
00464 
00465                 label = new TQLabel(i18n("Email subject", "Subject:"), frame);
00466                 label->setFixedSize(label->sizeHint());
00467                 grid->addWidget(label, 1, 0, TQt::AlignLeft);
00468                 label = new TQLabel(mEvent.emailSubject(), frame);
00469                 label->setFixedSize(label->sizeHint());
00470                 grid->addWidget(label, 1, 1, TQt::AlignLeft);
00471                 break;
00472             }
00473             case KAEvent::COMMAND:
00474             case KAEvent::FILE:
00475             case KAEvent::MESSAGE:
00476             default:
00477                 // Just display the error message strings
00478                 break;
00479         }
00480     }
00481 
00482     if (!mErrorMsgs.count())
00483         topWidget->setBackgroundColor(mBgColour);
00484     else
00485     {
00486         setCaption(i18n("Error"));
00487         TQBoxLayout* layout = new TQHBoxLayout(topLayout);
00488         layout->setMargin(2*KDialog::marginHint());
00489         layout->addStretch();
00490         TQLabel* label = new TQLabel(topWidget);
00491         label->setPixmap(DesktopIcon("error"));
00492         label->setFixedSize(label->sizeHint());
00493         layout->addWidget(label, 0, TQt::AlignRight);
00494         TQBoxLayout* vlayout = new TQVBoxLayout(layout);
00495         for (TQStringList::Iterator it = mErrorMsgs.begin();  it != mErrorMsgs.end();  ++it)
00496         {
00497             label = new TQLabel(*it, topWidget);
00498             label->setFixedSize(label->sizeHint());
00499             vlayout->addWidget(label, 0, TQt::AlignLeft);
00500         }
00501         layout->addStretch();
00502     }
00503 
00504     TQGridLayout* grid = new TQGridLayout(1, 4);
00505     topLayout->addLayout(grid);
00506     grid->setColStretch(0, 1);     // keep the buttons right-adjusted in the window
00507     int gridIndex = 1;
00508 
00509     // Close button
00510     mOkButton = new KPushButton(KStdGuiItem::close(), topWidget);
00511     // Prevent accidental acknowledgement of the message if the user is typing when the window appears
00512     mOkButton->clearFocus();
00513     mOkButton->setFocusPolicy(TQ_ClickFocus);    // don't allow keyboard selection
00514     mOkButton->setFixedSize(mOkButton->sizeHint());
00515     connect(mOkButton, TQT_SIGNAL(clicked()), TQT_SLOT(close()));
00516     grid->addWidget(mOkButton, 0, gridIndex++, AlignHCenter);
00517     TQWhatsThis::add(mOkButton, i18n("Acknowledge the alarm"));
00518 
00519     if (mShowEdit)
00520     {
00521         // Edit button
00522         mEditButton = new TQPushButton(i18n("&Edit..."), topWidget);
00523         mEditButton->setFocusPolicy(TQ_ClickFocus);    // don't allow keyboard selection
00524         mEditButton->setFixedSize(mEditButton->sizeHint());
00525         connect(mEditButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotEdit()));
00526         grid->addWidget(mEditButton, 0, gridIndex++, AlignHCenter);
00527         TQWhatsThis::add(mEditButton, i18n("Edit the alarm."));
00528     }
00529 
00530     if (!mNoDefer)
00531     {
00532         // Defer button
00533         mDeferButton = new TQPushButton(i18n("&Defer..."), topWidget);
00534         mDeferButton->setFocusPolicy(TQ_ClickFocus);    // don't allow keyboard selection
00535         mDeferButton->setFixedSize(mDeferButton->sizeHint());
00536         connect(mDeferButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotDefer()));
00537         grid->addWidget(mDeferButton, 0, gridIndex++, AlignHCenter);
00538         TQWhatsThis::add(mDeferButton,
00539               i18n("Defer the alarm until later.\n"
00540                    "You will be prompted to specify when the alarm should be redisplayed."));
00541 
00542         setDeferralLimit(mEvent);    // ensure that button is disabled when alarm can't be deferred any more
00543     }
00544 
00545 #ifndef WITHOUT_ARTS
00546     if (!mAudioFile.isEmpty()  &&  (mVolume || mFadeVolume > 0))
00547     {
00548         // Silence button to stop sound repetition
00549         TQPixmap pixmap = MainBarIcon("media-playback-stop");
00550         mSilenceButton = new TQPushButton(topWidget);
00551         mSilenceButton->setPixmap(pixmap);
00552         mSilenceButton->setFixedSize(mSilenceButton->sizeHint());
00553         connect(mSilenceButton, TQT_SIGNAL(clicked()), TQT_SLOT(stopPlay()));
00554         grid->addWidget(mSilenceButton, 0, gridIndex++, AlignHCenter);
00555         TQToolTip::add(mSilenceButton, i18n("Stop sound"));
00556         TQWhatsThis::add(mSilenceButton, i18n("Stop playing the sound"));
00557         // To avoid getting in a mess, disable the button until sound playing has been set up
00558         mSilenceButton->setEnabled(false);
00559     }
00560 #endif
00561 
00562     TDEIconLoader iconLoader;
00563     if (mKMailSerialNumber)
00564     {
00565         // KMail button
00566         TQPixmap pixmap = iconLoader.loadIcon(TQString::fromLatin1("kmail"), TDEIcon::MainToolbar);
00567         mKMailButton = new TQPushButton(topWidget);
00568         mKMailButton->setPixmap(pixmap);
00569         mKMailButton->setFixedSize(mKMailButton->sizeHint());
00570         connect(mKMailButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotShowKMailMessage()));
00571         grid->addWidget(mKMailButton, 0, gridIndex++, AlignHCenter);
00572         TQToolTip::add(mKMailButton, i18n("Locate this email in KMail", "Locate in KMail"));
00573         TQWhatsThis::add(mKMailButton, i18n("Locate and highlight this email in KMail"));
00574     }
00575     else
00576         mKMailButton = 0;
00577 
00578     // KAlarm button
00579     TQPixmap pixmap = iconLoader.loadIcon(TQString::fromLatin1(kapp->aboutData()->appName()), TDEIcon::MainToolbar);
00580     mKAlarmButton = new TQPushButton(topWidget);
00581     mKAlarmButton->setPixmap(pixmap);
00582     mKAlarmButton->setFixedSize(mKAlarmButton->sizeHint());
00583     connect(mKAlarmButton, TQT_SIGNAL(clicked()), TQT_SLOT(displayMainWindow()));
00584     grid->addWidget(mKAlarmButton, 0, gridIndex++, AlignHCenter);
00585     TQString actKAlarm = i18n("Activate KAlarm");
00586     TQToolTip::add(mKAlarmButton, actKAlarm);
00587     TQWhatsThis::add(mKAlarmButton, actKAlarm);
00588 
00589     // Disable all buttons initially, to prevent accidental clicking on if they happen to be
00590     // under the mouse just as the window appears.
00591     mOkButton->setEnabled(false);
00592     if (mDeferButton)
00593         mDeferButton->setEnabled(false);
00594     if (mEditButton)
00595         mEditButton->setEnabled(false);
00596     if (mKMailButton)
00597         mKMailButton->setEnabled(false);
00598     mKAlarmButton->setEnabled(false);
00599 
00600     topLayout->activate();
00601     setMinimumSize(TQSize(grid->sizeHint().width() + 2*KDialog::marginHint(), sizeHint().height()));
00602 
00603     bool modal = !(getWFlags() & TQt::WX11BypassWM);
00604 
00605     unsigned long wstate = (modal ? NET::Modal : 0) | NET::Sticky | NET::KeepAbove;
00606     WId winid = winId();
00607     KWin::setState(winid, wstate);
00608     KWin::setOnAllDesktops(winid, true);
00609 }
00610 
00611 /******************************************************************************
00612 * Set the remaining time text in a reminder window.
00613 * Called at the start of every day (at the user-defined start-of-day time).
00614 */
00615 void MessageWin::setRemainingTextDay()
00616 {
00617     TQString text;
00618     int days = TQDate::currentDate().daysTo(mDateTime.date());
00619     if (days <= 0  &&  !mDateTime.isDateOnly())
00620     {
00621         // The alarm is due today, so start refreshing every minute
00622         MidnightTimer::disconnect(TQT_TQOBJECT(this), TQT_SLOT(setRemainingTextDay()));
00623         setRemainingTextMinute();
00624         MinuteTimer::connect(TQT_TQOBJECT(this), TQT_SLOT(setRemainingTextMinute()));   // update every minute
00625     }
00626     else
00627     {
00628         if (days <= 0)
00629             text = i18n("Today");
00630         else if (days % 7)
00631             text = i18n("Tomorrow", "in %n days' time", days);
00632         else
00633             text = i18n("in 1 week's time", "in %n weeks' time", days/7);
00634     }
00635     mRemainingText->setText(text);
00636 }
00637 
00638 /******************************************************************************
00639 * Set the remaining time text in a reminder window.
00640 * Called on every minute boundary.
00641 */
00642 void MessageWin::setRemainingTextMinute()
00643 {
00644     TQString text;
00645     int mins = (TQDateTime::currentDateTime().secsTo(mDateTime.dateTime()) + 59) / 60;
00646     if (mins < 60)
00647         text = i18n("in 1 minute's time", "in %n minutes' time", (mins > 0 ? mins : 0));
00648     else if (mins % 60 == 0)
00649         text = i18n("in 1 hour's time", "in %n hours' time", mins/60);
00650     else if (mins % 60 == 1)
00651         text = i18n("in 1 hour 1 minute's time", "in %n hours 1 minute's time", mins/60);
00652     else
00653         text = i18n("in 1 hour %1 minutes' time", "in %n hours %1 minutes' time", mins/60).arg(mins%60);
00654     mRemainingText->setText(text);
00655 }
00656 
00657 /******************************************************************************
00658 * Save settings to the session managed config file, for restoration
00659 * when the program is restored.
00660 */
00661 void MessageWin::saveProperties(TDEConfig* config)
00662 {
00663     if (mShown  &&  !mErrorWindow)
00664     {
00665         config->writeEntry(TQString::fromLatin1("EventID"), mEventID);
00666         config->writeEntry(TQString::fromLatin1("AlarmType"), mAlarmType);
00667         config->writeEntry(TQString::fromLatin1("Message"), mMessage);
00668         config->writeEntry(TQString::fromLatin1("Type"), mAction);
00669         config->writeEntry(TQString::fromLatin1("Font"), mFont);
00670         config->writeEntry(TQString::fromLatin1("BgColour"), mBgColour);
00671         config->writeEntry(TQString::fromLatin1("FgColour"), mFgColour);
00672         config->writeEntry(TQString::fromLatin1("ConfirmAck"), mConfirmAck);
00673         if (mDateTime.isValid())
00674         {
00675             config->writeEntry(TQString::fromLatin1("Time"), mDateTime.dateTime());
00676             config->writeEntry(TQString::fromLatin1("DateOnly"), mDateTime.isDateOnly());
00677         }
00678         if (mCloseTime.isValid())
00679             config->writeEntry(TQString::fromLatin1("Expiry"), mCloseTime);
00680 #ifndef WITHOUT_ARTS
00681         if (mAudioRepeat  &&  mSilenceButton  &&  mSilenceButton->isEnabled())
00682         {
00683             // Only need to restart sound file playing if it's being repeated
00684             config->writePathEntry(TQString::fromLatin1("AudioFile"), mAudioFile);
00685             config->writeEntry(TQString::fromLatin1("Volume"), static_cast<int>(mVolume * 100));
00686         }
00687 #endif
00688         config->writeEntry(TQString::fromLatin1("Speak"), mSpeak);
00689         config->writeEntry(TQString::fromLatin1("Height"), height());
00690         config->writeEntry(TQString::fromLatin1("DeferMins"), mDefaultDeferMinutes);
00691         config->writeEntry(TQString::fromLatin1("NoDefer"), mNoDefer);
00692         config->writeEntry(TQString::fromLatin1("NoPostAction"), mNoPostAction);
00693         config->writeEntry(TQString::fromLatin1("KMailSerial"), mKMailSerialNumber);
00694     }
00695     else
00696         config->writeEntry(TQString::fromLatin1("Invalid"), true);
00697 }
00698 
00699 /******************************************************************************
00700 * Read settings from the session managed config file.
00701 * This function is automatically called whenever the app is being restored.
00702 * Read in whatever was saved in saveProperties().
00703 */
00704 void MessageWin::readProperties(TDEConfig* config)
00705 {
00706     mInvalid             = config->readBoolEntry(TQString::fromLatin1("Invalid"), false);
00707     mEventID             = config->readEntry(TQString::fromLatin1("EventID"));
00708     mAlarmType           = KAAlarm::Type(config->readNumEntry(TQString::fromLatin1("AlarmType")));
00709     mMessage             = config->readEntry(TQString::fromLatin1("Message"));
00710     mAction              = KAEvent::Action(config->readNumEntry(TQString::fromLatin1("Type")));
00711     mFont                = config->readFontEntry(TQString::fromLatin1("Font"));
00712     mBgColour            = config->readColorEntry(TQString::fromLatin1("BgColour"));
00713     mFgColour            = config->readColorEntry(TQString::fromLatin1("FgColour"));
00714     mConfirmAck          = config->readBoolEntry(TQString::fromLatin1("ConfirmAck"));
00715     TQDateTime invalidDateTime;
00716     TQDateTime dt         = config->readDateTimeEntry(TQString::fromLatin1("Time"), &invalidDateTime);
00717     bool dateOnly        = config->readBoolEntry(TQString::fromLatin1("DateOnly"));
00718     mDateTime.set(dt, dateOnly);
00719     mCloseTime           = config->readDateTimeEntry(TQString::fromLatin1("Expiry"), &invalidDateTime);
00720 #ifndef WITHOUT_ARTS
00721     mAudioFile           = config->readPathEntry(TQString::fromLatin1("AudioFile"));
00722     mVolume              = static_cast<float>(config->readNumEntry(TQString::fromLatin1("Volume"))) / 100;
00723     mFadeVolume          = -1;
00724     mFadeSeconds         = 0;
00725     if (!mAudioFile.isEmpty())
00726         mAudioRepeat = true;
00727 #endif
00728     mSpeak               = config->readBoolEntry(TQString::fromLatin1("Speak"));
00729     mRestoreHeight       = config->readNumEntry(TQString::fromLatin1("Height"));
00730     mDefaultDeferMinutes = config->readNumEntry(TQString::fromLatin1("DeferMins"));
00731     mNoDefer             = config->readBoolEntry(TQString::fromLatin1("NoDefer"));
00732     mNoPostAction        = config->readBoolEntry(TQString::fromLatin1("NoPostAction"));
00733     mKMailSerialNumber   = config->readUnsignedLongNumEntry(TQString::fromLatin1("KMailSerial"));
00734     mShowEdit            = false;
00735     kdDebug(5950) << "MessageWin::readProperties(" << mEventID << ")" << endl;
00736     if (mAlarmType != KAAlarm::INVALID_ALARM)
00737     {
00738         // Recreate the event from the calendar file (if possible)
00739         if (!mEventID.isEmpty())
00740         {
00741             const Event* kcalEvent = AlarmCalendar::activeCalendar()->event(mEventID);
00742             if (!kcalEvent)
00743             {
00744                 // It's not in the active calendar, so try the displaying calendar
00745                 AlarmCalendar* cal = AlarmCalendar::displayCalendar();
00746                 if (cal->isOpen())
00747                     kcalEvent = cal->event(KAEvent::uid(mEventID, KAEvent::DISPLAYING));
00748             }
00749             if (kcalEvent)
00750             {
00751                 mEvent.set(*kcalEvent);
00752                 mEvent.setUid(KAEvent::ACTIVE);    // in case it came from the display calendar
00753                 mShowEdit = true;
00754             }
00755         }
00756         initView();
00757     }
00758 }
00759 
00760 /******************************************************************************
00761 *  Returns the existing message window (if any) which is displaying the event
00762 *  with the specified ID.
00763 */
00764 MessageWin* MessageWin::findEvent(const TQString& eventID)
00765 {
00766     for (TQValueList<MessageWin*>::Iterator it = mWindowList.begin();  it != mWindowList.end();  ++it)
00767     {
00768         MessageWin* w = *it;
00769         if (w->mEventID == eventID  &&  !w->mErrorWindow)
00770             return w;
00771     }
00772     return 0;
00773 }
00774 
00775 /******************************************************************************
00776 *  Beep and play the audio file, as appropriate.
00777 */
00778 void MessageWin::playAudio()
00779 {
00780     if (mBeep)
00781     {
00782         // Beep using two methods, in case the sound card/speakers are switched off or not working
00783         KNotifyClient::beep();     // beep through the sound card & speakers
00784         TQApplication::beep();      // beep through the internal speaker
00785     }
00786     if (!mAudioFile.isEmpty())
00787     {
00788         if (!mVolume  &&  mFadeVolume <= 0)
00789             return;    // ensure zero volume doesn't play anything
00790 #ifdef WITHOUT_ARTS
00791         TQString play = mAudioFile;
00792         TQString file = TQString::fromLatin1("file:");
00793         if (mAudioFile.startsWith(file))
00794             play = mAudioFile.mid(file.length());
00795         KAudioPlayer::play(TQFile::encodeName(play));
00796 #else
00797         // An audio file is specified. Because loading it may take some time,
00798         // call it on a timer to allow the window to display first.
00799         TQTimer::singleShot(0, this, TQT_SLOT(slotPlayAudio()));
00800 #endif
00801     }
00802     else if (mSpeak)
00803     {
00804         // The message is to be spoken. In case of error messges,
00805         // call it on a timer to allow the window to display first.
00806         TQTimer::singleShot(0, this, TQT_SLOT(slotSpeak()));
00807     }
00808 }
00809 
00810 /******************************************************************************
00811 *  Speak the message.
00812 *  Called asynchronously to avoid delaying the display of the message.
00813 */
00814 void MessageWin::slotSpeak()
00815 {
00816     DCOPClient* client = kapp->dcopClient();
00817     if (!client->isApplicationRegistered("kttsd"))
00818     {
00819         // kttsd is not running, so start it
00820         TQString error;
00821         if (kapp->startServiceByDesktopName("kttsd", TQStringList(), &error))
00822         {
00823             kdDebug(5950) << "MessageWin::slotSpeak(): failed to start kttsd: " << error << endl;
00824             if (!haveErrorMessage(ErrMsg_Speak))
00825             {
00826                 KMessageBox::detailedError(0, i18n("Unable to speak message"), error);
00827                 clearErrorMessage(ErrMsg_Speak);
00828             }
00829             return;
00830         }
00831     }
00832     TQByteArray  data;
00833     TQDataStream arg(data, IO_WriteOnly);
00834     arg << mMessage << "";
00835     if (!client->send("kttsd", "KSpeech", "sayMessage(TQString,TQString)", data))
00836     {
00837         kdDebug(5950) << "MessageWin::slotSpeak(): sayMessage() DCOP error" << endl;
00838         if (!haveErrorMessage(ErrMsg_Speak))
00839         {
00840             KMessageBox::detailedError(0, i18n("Unable to speak message"), i18n("DCOP Call sayMessage failed"));
00841             clearErrorMessage(ErrMsg_Speak);
00842         }
00843     }
00844 }
00845 
00846 /******************************************************************************
00847 *  Play the audio file.
00848 *  Called asynchronously to avoid delaying the display of the message.
00849 */
00850 void MessageWin::slotPlayAudio()
00851 {
00852 #ifndef WITHOUT_ARTS
00853     // First check that it exists, to avoid possible crashes if the filename is badly specified
00854     MainWindow* mmw = MainWindow::mainMainWindow();
00855     KURL url(mAudioFile);
00856     if (!url.isValid()  ||  !TDEIO::NetAccess::exists(url, true, mmw)
00857     ||  !TDEIO::NetAccess::download(url, mLocalAudioFile, mmw))
00858     {
00859         kdError(5950) << "MessageWin::playAudio(): Open failure: " << mAudioFile << endl;
00860         if (!haveErrorMessage(ErrMsg_AudioFile))
00861         {
00862             KMessageBox::error(this, i18n("Cannot open audio file:\n%1").arg(mAudioFile));
00863             clearErrorMessage(ErrMsg_AudioFile);
00864         }
00865         return;
00866     }
00867     if (!mArtsDispatcher)
00868     {
00869         mFadeTimer = 0;
00870         mPlayTimer = new TQTimer(this);
00871         connect(mPlayTimer, TQT_SIGNAL(timeout()), TQT_SLOT(checkAudioPlay()));
00872         mArtsDispatcher = new KArtsDispatcher;
00873         mPlayedOnce = false;
00874         mAudioFileStart = TQTime::currentTime();
00875         initAudio(true);
00876         if (!mPlayObject->object().isNull())
00877             checkAudioPlay();
00878         if (!mUsingKMix  &&  mVolume >= 0)
00879         {
00880             // Output error message now that everything else has been done.
00881             // (Outputting it earlier would delay things until it is acknowledged.)
00882             kdWarning(5950) << "Unable to set master volume (KMix: " << mKMixError << ")\n";
00883             if (!haveErrorMessage(ErrMsg_Volume))
00884             {
00885                 KMessageBox::information(this, i18n("Unable to set master volume\n(Error accessing KMix:\n%1)").arg(mKMixError),
00886                                          TQString(), TQString::fromLatin1("KMixError"));
00887                 clearErrorMessage(ErrMsg_Volume);
00888             }
00889         }
00890     }
00891 #endif
00892 }
00893 
00894 #ifndef WITHOUT_ARTS
00895 /******************************************************************************
00896 *  Set up the audio file for playing.
00897 */
00898 void MessageWin::initAudio(bool firstTime)
00899 {
00900     KArtsServer aserver;
00901     Arts::SoundServerV2 sserver = aserver.server();
00902     KDE::PlayObjectFactory factory(sserver);
00903     mPlayObject = factory.createPlayObject(mLocalAudioFile, true);
00904     if (firstTime)
00905     {
00906         // Save the existing sound volume setting for restoration afterwards,
00907         // and set the desired volume for the alarm.
00908         mUsingKMix = false;
00909         float volume = mVolume;    // initial volume
00910         if (volume >= 0)
00911         {
00912             // The volume has been specified
00913             if (mFadeVolume >= 0)
00914                 volume = mFadeVolume;    // fading, so adjust the initial volume
00915 
00916             // Get the current master volume from KMix
00917             int vol = getKMixVolume();
00918             if (vol >= 0)
00919             {
00920                 mOldVolume = vol;    // success
00921                 mUsingKMix = true;
00922                 setKMixVolume(static_cast<int>(volume * 100));
00923             }
00924         }
00925         if (!mUsingKMix)
00926         {
00927             /* Adjust within the current master volume, because either
00928              * a) the volume is not specified, in which case we want to play
00929              *    at 100% of the current master volume setting, or
00930              * b) KMix is not available to set the master volume.
00931              */
00932             mOldVolume = sserver.outVolume().scaleFactor();    // save volume for restoration afterwards
00933             sserver.outVolume().scaleFactor(volume >= 0 ? volume : 1);
00934         }
00935     }
00936     mSilenceButton->setEnabled(true);
00937     mPlayed = false;
00938     connect(mPlayObject, TQT_SIGNAL(playObjectCreated()), TQT_SLOT(checkAudioPlay()));
00939     if (!mPlayObject->object().isNull())
00940         checkAudioPlay();
00941 }
00942 #endif
00943 
00944 /******************************************************************************
00945 *  Called when the audio file has loaded and is ready to play, or on a timer
00946 *  when play is expected to have completed.
00947 *  If it is ready to play, start playing it (for the first time or repeated).
00948 *  If play has not yet completed, wait a bit longer.
00949 */
00950 void MessageWin::checkAudioPlay()
00951 {
00952 #ifndef WITHOUT_ARTS
00953     if (!mPlayObject)
00954         return;
00955     if (mPlayObject->state() == Arts::posIdle)
00956     {
00957         // The file has loaded and is ready to play, or play has completed
00958         if (mPlayedOnce  &&  !mAudioRepeat)
00959         {
00960             // Play has completed
00961             stopPlay();
00962             return;
00963         }
00964 
00965         // Start playing the file, either for the first time or again
00966         kdDebug(5950) << "MessageWin::checkAudioPlay(): start\n";
00967         if (!mPlayedOnce)
00968         {
00969             // Start playing the file for the first time
00970             TQTime now = TQTime::currentTime();
00971             mAudioFileLoadSecs = mAudioFileStart.secsTo(now);
00972             if (mAudioFileLoadSecs < 0)
00973                 mAudioFileLoadSecs += 86400;
00974             if (mVolume >= 0  &&  mFadeVolume >= 0  &&  mFadeSeconds > 0)
00975             {
00976                 // Set up volume fade
00977                 mAudioFileStart = now;
00978                 mFadeTimer = new TQTimer(this);
00979                 connect(mFadeTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotFade()));
00980                 mFadeTimer->start(1000);     // adjust volume every second
00981             }
00982             mPlayedOnce = true;
00983         }
00984         if (mAudioFileLoadSecs < 3)
00985         {
00986             /* The aRts library takes several attempts before a PlayObject can
00987              * be replayed, leaving a gap of perhaps 5 seconds between plays.
00988              * So if loading the file takes a short time, it's better to reload
00989              * the PlayObject rather than try to replay the same PlayObject.
00990              */
00991             if (mPlayed)
00992             {
00993                 // Playing has completed. Start playing again.
00994                 delete mPlayObject;
00995                 initAudio(false);
00996                 if (mPlayObject->object().isNull())
00997                     return;
00998             }
00999             mPlayed = true;
01000             mPlayObject->play();
01001         }
01002         else
01003         {
01004             // The file is slow to load, so attempt to replay the PlayObject
01005             static Arts::poTime t0((long)0, (long)0, 0, std::string());
01006             Arts::poTime current = mPlayObject->currentTime();
01007             if (current.seconds || current.ms)
01008                 mPlayObject->seek(t0);
01009             else
01010                 mPlayObject->play();
01011         }
01012     }
01013 
01014     // The sound file is still playing
01015     Arts::poTime overall = mPlayObject->overallTime();
01016     Arts::poTime current = mPlayObject->currentTime();
01017     int time = 1000*(overall.seconds - current.seconds) + overall.ms - current.ms;
01018     if (time < 0)
01019         time = 0;
01020     kdDebug(5950) << "MessageWin::checkAudioPlay(): wait for " << (time+100) << "ms\n";
01021     mPlayTimer->start(time + 100, true);
01022 #endif
01023 }
01024 
01025 /******************************************************************************
01026 *  Called when play completes, the Silence button is clicked, or the window is
01027 *  closed, to reset the sound volume and terminate audio access.
01028 */
01029 void MessageWin::stopPlay()
01030 {
01031 #ifndef WITHOUT_ARTS
01032     if (mArtsDispatcher)
01033     {
01034         // Restore the sound volume to what it was before the sound file
01035         // was played, provided that nothing else has modified it since.
01036         if (!mUsingKMix)
01037         {
01038             KArtsServer aserver;
01039             Arts::StereoVolumeControl svc = aserver.server().outVolume();
01040             float currentVolume = svc.scaleFactor();
01041             float eventVolume = mVolume;
01042             if (eventVolume < 0)
01043                 eventVolume = 1;
01044             if (currentVolume == eventVolume)
01045                 svc.scaleFactor(mOldVolume);
01046         }
01047         else if (mVolume >= 0)
01048         {
01049             int eventVolume = static_cast<int>(mVolume * 100);
01050             int currentVolume = getKMixVolume();
01051             // Volume returned isn't always exactly equal to volume set
01052             if (currentVolume < 0  ||  abs(currentVolume - eventVolume) < 5)
01053                 setKMixVolume(static_cast<int>(mOldVolume));
01054         }
01055     }
01056     delete mPlayObject;      mPlayObject = 0;
01057     delete mArtsDispatcher;  mArtsDispatcher = 0;
01058     if (!mLocalAudioFile.isEmpty())
01059     {
01060         TDEIO::NetAccess::removeTempFile(mLocalAudioFile);   // removes it only if it IS a temporary file
01061         mLocalAudioFile = TQString();
01062     }
01063     if (mSilenceButton)
01064         mSilenceButton->setEnabled(false);
01065 #endif
01066 }
01067 
01068 /******************************************************************************
01069 *  Called every second to fade the volume when the audio file starts playing.
01070 */
01071 void MessageWin::slotFade()
01072 {
01073 #ifndef WITHOUT_ARTS
01074     TQTime now = TQTime::currentTime();
01075     int elapsed = mAudioFileStart.secsTo(now);
01076     if (elapsed < 0)
01077         elapsed += 86400;    // it's the next day
01078     float volume;
01079     if (elapsed >= mFadeSeconds)
01080     {
01081         // The fade has finished. Set to normal volume.
01082         volume = mVolume;
01083         delete mFadeTimer;
01084         mFadeTimer = 0;
01085         if (!mVolume)
01086         {
01087             kdDebug(5950) << "MessageWin::slotFade(0)\n";
01088             stopPlay();
01089             return;
01090         }
01091     }
01092     else
01093         volume = mFadeVolume  +  ((mVolume - mFadeVolume) * elapsed) / mFadeSeconds;
01094     kdDebug(5950) << "MessageWin::slotFade(" << volume << ")\n";
01095     if (mArtsDispatcher)
01096     {
01097         if (mUsingKMix)
01098             setKMixVolume(static_cast<int>(volume * 100));
01099         else if (mArtsDispatcher)
01100         {
01101             KArtsServer aserver;
01102             aserver.server().outVolume().scaleFactor(volume);
01103         }
01104     }
01105 #endif
01106 }
01107 
01108 #ifndef WITHOUT_ARTS
01109 /******************************************************************************
01110 *  Get the master volume from KMix.
01111 *  Reply < 0 if failure.
01112 */
01113 int MessageWin::getKMixVolume()
01114 {
01115     if (!KAlarm::runProgram(KMIX_APP_NAME, KMIX_DCOP_WINDOW, mKMixName, mKMixError))   // start KMix if it isn't already running
01116         return -1;
01117     TQByteArray  data, replyData;
01118     TQCString    replyType;
01119     TQDataStream arg(data, IO_WriteOnly);
01120     if (!kapp->dcopClient()->call(mKMixName, KMIX_DCOP_OBJECT, "masterVolume()", data, replyType, replyData)
01121     ||  replyType != "int")
01122         return -1;
01123     int result;
01124     TQDataStream reply(replyData, IO_ReadOnly);
01125     reply >> result;
01126     return (result >= 0) ? result : 0;
01127 }
01128 
01129 /******************************************************************************
01130 *  Set the master volume using KMix.
01131 */
01132 void MessageWin::setKMixVolume(int percent)
01133 {
01134     if (!mUsingKMix)
01135         return;
01136     if (!KAlarm::runProgram(KMIX_APP_NAME, KMIX_DCOP_WINDOW, mKMixName, mKMixError))   // start KMix if it isn't already running
01137         return;
01138     TQByteArray  data;
01139     TQDataStream arg(data, IO_WriteOnly);
01140     arg << percent;
01141     if (!kapp->dcopClient()->send(mKMixName, KMIX_DCOP_OBJECT, "setMasterVolume(int)", data))
01142         kdError(5950) << "MessageWin::setKMixVolume(): kmix dcop call failed\n";
01143 }
01144 #endif
01145 
01146 /******************************************************************************
01147 *  Raise the alarm window, re-output any required audio notification, and
01148 *  reschedule the alarm in the calendar file.
01149 */
01150 void MessageWin::repeat(const KAAlarm& alarm)
01151 {
01152     if (mDeferDlg)
01153     {
01154         // Cancel any deferral dialogue so that the user notices something's going on,
01155         // and also because the deferral time limit will have changed.
01156         delete mDeferDlg;
01157         mDeferDlg = 0;
01158     }
01159     const Event* kcalEvent = mEventID.isNull() ? 0 : AlarmCalendar::activeCalendar()->event(mEventID);
01160     if (kcalEvent)
01161     {
01162         mAlarmType = alarm.type();    // store new alarm type for use if it is later deferred
01163         if (!mDeferDlg  ||  Preferences::modalMessages())
01164         {
01165             raise();
01166             playAudio();
01167         }
01168         KAEvent event(*kcalEvent);
01169         mDeferButton->setEnabled(true);
01170         setDeferralLimit(event);    // ensure that button is disabled when alarm can't be deferred any more
01171         theApp()->alarmShowing(event, mAlarmType, mDateTime);
01172     }
01173 }
01174 
01175 /******************************************************************************
01176 *  Display the window.
01177 *  If windows are being positioned away from the mouse cursor, it is initially
01178 *  positioned at the top left to slightly reduce the number of times the
01179 *  windows need to be moved in showEvent().
01180 */
01181 void MessageWin::show()
01182 {
01183     if (mCloseTime.isValid())
01184     {
01185         // Set a timer to auto-close the window
01186         int delay = TQDateTime::currentDateTime().secsTo(mCloseTime);
01187         if (delay < 0)
01188             delay = 0;
01189         TQTimer::singleShot(delay * 1000, this, TQT_SLOT(close()));
01190         if (!delay)
01191             return;    // don't show the window if auto-closing is already due
01192     }
01193     if (Preferences::messageButtonDelay() == 0)
01194         move(0, 0);
01195     MainWindowBase::show();
01196 }
01197 
01198 /******************************************************************************
01199 *  Returns the window's recommended size exclusive of its frame.
01200 *  For message windows, the size if limited to fit inside the working area of
01201 *  the desktop.
01202 */
01203 TQSize MessageWin::sizeHint() const
01204 {
01205     if (mAction != KAEvent::MESSAGE)
01206         return MainWindowBase::sizeHint();
01207     if (!mWinModule)
01208         mWinModule = new KWinModule(0, KWinModule::INFO_DESKTOP);
01209     TQSize frame = frameGeometry().size();
01210     TQSize contents = geometry().size();
01211     TQSize desktop  = mWinModule->workArea().size();
01212     TQSize maxSize(desktop.width() - (frame.width() - contents.width()),
01213                   desktop.height() - (frame.height() - contents.height()));
01214     return MainWindowBase::sizeHint().boundedTo(maxSize);
01215 }
01216 
01217 /******************************************************************************
01218 *  Called when the window is shown.
01219 *  The first time, output any required audio notification, and reschedule or
01220 *  delete the event from the calendar file.
01221 */
01222 void MessageWin::showEvent(TQShowEvent* se)
01223 {
01224     MainWindowBase::showEvent(se);
01225     if (!mShown)
01226     {
01227         if (mErrorWindow)
01228             enableButtons();    // don't bother repositioning error messages
01229         else
01230         {
01231             /* Set the window size.
01232              * Note that the frame thickness is not yet known when this
01233              * method is called, so for large windows the size needs to be
01234              * set again later.
01235              */
01236             TQSize s = sizeHint();     // fit the window round the message
01237             if (mAction == KAEvent::FILE  &&  !mErrorMsgs.count())
01238                 KAlarm::readConfigWindowSize("FileMessage", s);
01239             resize(s);
01240 
01241             mButtonDelay = Preferences::messageButtonDelay() * 1000;
01242             if (!mButtonDelay)
01243             {
01244                 /* Try to ensure that the window can't accidentally be acknowledged
01245                  * by the user clicking the mouse just as it appears.
01246                  * To achieve this, move the window so that the OK button is as far away
01247                  * from the cursor as possible. If the buttons are still too close to the
01248                  * cursor, disable the buttons for a short time.
01249                  * N.B. This can't be done in show(), since the geometry of the window
01250                  *      is not known until it is displayed. Unfortunately by moving the
01251                  *      window in showEvent(), a flicker is unavoidable.
01252                  *      See the TQt documentation on window geometry for more details.
01253                  */
01254                 // PROBLEM: The frame size is not known yet!
01255 
01256                 /* Find the usable area of the desktop or, if the desktop comprises
01257                  * multiple screens, the usable area of the current screen. (If the
01258                  * message is displayed on a screen other than that currently being
01259                  * worked with, it might not be noticed.)
01260                  */
01261                 TQPoint cursor = TQCursor::pos();
01262                 if (!mWinModule)
01263                     mWinModule = new KWinModule(0, KWinModule::INFO_DESKTOP);
01264                 TQRect desk = mWinModule->workArea();
01265                 TQDesktopWidget* dw = TQApplication::desktop();
01266                 if (dw->numScreens() > 1)
01267                     desk &= dw->screenGeometry(dw->screenNumber(cursor));
01268 
01269                 TQRect frame = frameGeometry();
01270                 TQRect rect  = geometry();
01271                 // Find the offsets from the outside of the frame to the edges of the OK button
01272                 TQRect button(mOkButton->mapToParent(TQPoint(0, 0)), mOkButton->mapToParent(mOkButton->rect().bottomRight()));
01273                 int buttonLeft   = button.left() + rect.left() - frame.left();
01274                 int buttonRight  = width() - button.right() + frame.right() - rect.right();
01275                 int buttonTop    = button.top() + rect.top() - frame.top();
01276                 int buttonBottom = height() - button.bottom() + frame.bottom() - rect.bottom();
01277 
01278                 int centrex = (desk.width() + buttonLeft - buttonRight) / 2;
01279                 int centrey = (desk.height() + buttonTop - buttonBottom) / 2;
01280                 int x = (cursor.x() < centrex) ? desk.right() - frame.width() : desk.left();
01281                 int y = (cursor.y() < centrey) ? desk.bottom() - frame.height() : desk.top();
01282 
01283                 // Find the enclosing rectangle for the new button positions
01284                 // and check if the cursor is too near
01285                 TQRect buttons = mOkButton->geometry().unite(mKAlarmButton->geometry());
01286                 buttons.moveBy(rect.left() + x - frame.left(), rect.top() + y - frame.top());
01287                 int minDistance = proximityMultiple * mOkButton->height();
01288                 if ((abs(cursor.x() - buttons.left()) < minDistance
01289                   || abs(cursor.x() - buttons.right()) < minDistance)
01290                 &&  (abs(cursor.y() - buttons.top()) < minDistance
01291                   || abs(cursor.y() - buttons.bottom()) < minDistance))
01292                     mButtonDelay = proximityButtonDelay;    // too near - disable buttons initially
01293 
01294                 if (x != frame.left()  ||  y != frame.top())
01295                 {
01296                     mPositioning = true;
01297                     move(x, y);
01298                 }
01299             }
01300             if (!mPositioning)
01301                 displayComplete();    // play audio, etc.
01302             if (mAction == KAEvent::MESSAGE)
01303             {
01304                 // Set the window size once the frame size is known
01305                 TQTimer::singleShot(0, this, TQT_SLOT(setMaxSize()));
01306             }
01307         }
01308         mShown = true;
01309     }
01310 }
01311 
01312 /******************************************************************************
01313 *  Called when the window has been moved.
01314 */
01315 void MessageWin::moveEvent(TQMoveEvent* e)
01316 {
01317     MainWindowBase::moveEvent(e);
01318     if (mPositioning)
01319     {
01320         // The window has just been initially positioned
01321         mPositioning = false;
01322         displayComplete();    // play audio, etc.
01323     }
01324 }
01325 
01326 /******************************************************************************
01327 *  Reset the iniital window size if it exceeds the working area of the desktop.
01328 */
01329 void MessageWin::setMaxSize()
01330 {
01331     TQSize s = sizeHint();
01332     if (width() > s.width()  ||  height() > s.height())
01333         resize(s);
01334 }
01335 
01336 /******************************************************************************
01337 *  Called when the window has been displayed properly (in its correct position),
01338 *  to play sounds and reschedule the event.
01339 */
01340 void MessageWin::displayComplete()
01341 {
01342     playAudio();
01343     if (mRescheduleEvent)
01344         theApp()->alarmShowing(mEvent, mAlarmType, mDateTime);
01345 
01346     // Enable the window's buttons either now or after the configured delay
01347     if (mButtonDelay > 0)
01348         TQTimer::singleShot(mButtonDelay, this, TQT_SLOT(enableButtons()));
01349     else
01350         enableButtons();
01351 }
01352 
01353 /******************************************************************************
01354 *  Enable the window's buttons.
01355 */
01356 void MessageWin::enableButtons()
01357 {
01358     mOkButton->setEnabled(true);
01359     mKAlarmButton->setEnabled(true);
01360     if (mDeferButton  &&  !mDisableDeferral)
01361         mDeferButton->setEnabled(true);
01362     if (mEditButton)
01363         mEditButton->setEnabled(true);
01364     if (mKMailButton)
01365         mKMailButton->setEnabled(true);
01366 }
01367 
01368 /******************************************************************************
01369 *  Called when the window's size has changed (before it is painted).
01370 */
01371 void MessageWin::resizeEvent(TQResizeEvent* re)
01372 {
01373     if (mRestoreHeight)
01374     {
01375         // Restore the window height on session restoration
01376         if (mRestoreHeight != re->size().height())
01377         {
01378             TQSize size = re->size();
01379             size.setHeight(mRestoreHeight);
01380             resize(size);
01381         }
01382         else if (isVisible())
01383             mRestoreHeight = 0;
01384     }
01385     else
01386     {
01387         if (mShown  &&  mAction == KAEvent::FILE  &&  !mErrorMsgs.count())
01388             KAlarm::writeConfigWindowSize("FileMessage", re->size());
01389         MainWindowBase::resizeEvent(re);
01390     }
01391 }
01392 
01393 /******************************************************************************
01394 *  Called when a close event is received.
01395 *  Only quits the application if there is no system tray icon displayed.
01396 */
01397 void MessageWin::closeEvent(TQCloseEvent* ce)
01398 {
01399     // Don't prompt or delete the alarm from the display calendar if the session is closing
01400     if (!mErrorWindow  &&  !theApp()->sessionClosingDown())
01401     {
01402         if (mConfirmAck  &&  !mNoCloseConfirm)
01403         {
01404             // Ask for confirmation of acknowledgement. Use warningYesNo() because its default is No.
01405             if (KMessageBox::warningYesNo(this, i18n("Do you really want to acknowledge this alarm?"),
01406                                                 i18n("Acknowledge Alarm"), i18n("&Acknowledge"), KStdGuiItem::cancel())
01407                 != KMessageBox::Yes)
01408             {
01409                 ce->ignore();
01410                 return;
01411             }
01412         }
01413         if (!mEventID.isNull())
01414         {
01415             // Delete from the display calendar
01416             KAlarm::deleteDisplayEvent(KAEvent::uid(mEventID, KAEvent::DISPLAYING));
01417         }
01418     }
01419     MainWindowBase::closeEvent(ce);
01420 }
01421 
01422 /******************************************************************************
01423 *  Called when the KMail button is clicked.
01424 *  Tells KMail to display the email message displayed in this message window.
01425 */
01426 void MessageWin::slotShowKMailMessage()
01427 {
01428     kdDebug(5950) << "MessageWin::slotShowKMailMessage()\n";
01429     if (!mKMailSerialNumber)
01430         return;
01431     TQString err = KAlarm::runKMail(false);
01432     if (!err.isNull())
01433     {
01434         KMessageBox::sorry(this, err);
01435         return;
01436     }
01437     TQCString    replyType;
01438     TQByteArray  data, replyData;
01439     TQDataStream arg(data, IO_WriteOnly);
01440     arg << (TQ_UINT32)mKMailSerialNumber << TQString();
01441     if (kapp->dcopClient()->call("kmail", KMAIL_DCOP_OBJECT, "showMail(TQ_UINT32,TQString)", data, replyType, replyData)
01442     &&  replyType == "bool")
01443     {
01444         bool result;
01445         TQDataStream replyStream(replyData, IO_ReadOnly);
01446         replyStream >> result;
01447         if (result)
01448             return;    // success
01449     }
01450     kdError(5950) << "MessageWin::slotShowKMailMessage(): kmail dcop call failed\n";
01451     KMessageBox::sorry(this, i18n("Unable to locate this email in KMail"));
01452 }
01453 
01454 /******************************************************************************
01455 *  Called when the Edit... button is clicked.
01456 *  Displays the alarm edit dialog.
01457 */
01458 void MessageWin::slotEdit()
01459 {
01460     kdDebug(5950) << "MessageWin::slotEdit()" << endl;
01461     EditAlarmDlg editDlg(false, i18n("Edit Alarm"), this, "editDlg", &mEvent);
01462     if (editDlg.exec() == TQDialog::Accepted)
01463     {
01464         KAEvent event;
01465         editDlg.getEvent(event);
01466 
01467         // Update the displayed lists and the calendar file
01468         KAlarm::UpdateStatus status;
01469         if (AlarmCalendar::activeCalendar()->event(mEventID))
01470         {
01471             // The old alarm hasn't expired yet, so replace it
01472             status = KAlarm::modifyEvent(mEvent, event, 0, &editDlg);
01473             Undo::saveEdit(mEvent, event);
01474         }
01475         else
01476         {
01477             // The old event has expired, so simply create a new one
01478             status = KAlarm::addEvent(event, 0, &editDlg);
01479             Undo::saveAdd(event);
01480         }
01481 
01482         if (status == KAlarm::UPDATE_KORG_ERR)
01483             KAlarm::displayKOrgUpdateError(&editDlg, KAlarm::KORG_ERR_MODIFY, 1);
01484         KAlarm::outputAlarmWarnings(&editDlg, &event);
01485 
01486         // Close the alarm window
01487         mNoCloseConfirm = true;   // allow window to close without confirmation prompt
01488         close();
01489     }
01490 }
01491 
01492 /******************************************************************************
01493 * Set up to disable the defer button when the deferral limit is reached.
01494 */
01495 void MessageWin::setDeferralLimit(const KAEvent& event)
01496 {
01497     if (mDeferButton)
01498     {
01499         mDeferLimit = event.deferralLimit().dateTime();
01500         MidnightTimer::connect(TQT_TQOBJECT(this), TQT_SLOT(checkDeferralLimit()));   // check every day
01501         mDisableDeferral = false;
01502         checkDeferralLimit();
01503     }
01504 }
01505 
01506 /******************************************************************************
01507 * Check whether the deferral limit has been reached.
01508 * If so, disable the Defer button.
01509 * N.B. Ideally, just a single TQTimer::singleShot() call would be made to disable
01510 *      the defer button at the corret time. But for a 32-bit integer, the
01511 *      milliseconds parameter overflows in about 25 days, so instead a daily
01512 *      check is done until the day when the deferral limit is reached, followed
01513 *      by a non-overflowing TQTimer::singleShot() call.
01514 */
01515 void MessageWin::checkDeferralLimit()
01516 {
01517     if (!mDeferButton  ||  !mDeferLimit.isValid())
01518         return;
01519     int n = TQDate::currentDate().daysTo(mDeferLimit.date());
01520     if (n > 0)
01521         return;
01522     MidnightTimer::disconnect(TQT_TQOBJECT(this), TQT_SLOT(checkDeferralLimit()));
01523     if (n == 0)
01524     {
01525         // The deferral limit will be reached today
01526         n = TQTime::currentTime().secsTo(mDeferLimit.time());
01527         if (n > 0)
01528         {
01529             TQTimer::singleShot(n * 1000, this, TQT_SLOT(checkDeferralLimit()));
01530             return;
01531         }
01532     }
01533     mDeferButton->setEnabled(false);
01534     mDisableDeferral = true;
01535 }
01536 
01537 /******************************************************************************
01538 *  Called when the Defer... button is clicked.
01539 *  Displays the defer message dialog.
01540 */
01541 void MessageWin::slotDefer()
01542 {
01543     mDeferDlg = new DeferAlarmDlg(i18n("Defer Alarm"), TQDateTime(TQDateTime::currentDateTime().addSecs(60)),
01544                                   false, this, "deferDlg");
01545     if (mDefaultDeferMinutes > 0)
01546         mDeferDlg->setDeferMinutes(mDefaultDeferMinutes);
01547     mDeferDlg->setLimit(mEventID);
01548     if (!Preferences::modalMessages())
01549         lower();
01550     if (mDeferDlg->exec() == TQDialog::Accepted)
01551     {
01552         DateTime dateTime  = mDeferDlg->getDateTime();
01553         int      delayMins = mDeferDlg->deferMinutes();
01554         const Event* kcalEvent = mEventID.isNull() ? 0 : AlarmCalendar::activeCalendar()->event(mEventID);
01555         if (kcalEvent)
01556         {
01557             // The event still exists in the calendar file.
01558             KAEvent event(*kcalEvent);
01559             bool repeat = event.defer(dateTime, (mAlarmType & KAAlarm::REMINDER_ALARM), true);
01560             event.setDeferDefaultMinutes(delayMins);
01561             KAlarm::updateEvent(event, 0, mDeferDlg, true, !repeat);
01562             if (event.deferred())
01563                 mNoPostAction = true;
01564         }
01565         else
01566         {
01567             KAEvent event;
01568             kcalEvent = AlarmCalendar::displayCalendar()->event(KAEvent::uid(mEventID, KAEvent::DISPLAYING));
01569             if (kcalEvent)
01570             {
01571                 event.reinstateFromDisplaying(KAEvent(*kcalEvent));
01572                 event.defer(dateTime, (mAlarmType & KAAlarm::REMINDER_ALARM), true);
01573             }
01574             else
01575             {
01576                 // The event doesn't exist any more !?!, so create a new one
01577                 event.set(dateTime.dateTime(), mMessage, mBgColour, mFgColour, mFont, mAction, mLateCancel, mFlags);
01578                 event.setAudioFile(mAudioFile, mVolume, mFadeVolume, mFadeSeconds);
01579                 event.setArchive();
01580                 event.setEventID(mEventID);
01581             }
01582             event.setDeferDefaultMinutes(delayMins);
01583             // Add the event back into the calendar file, retaining its ID
01584             // and not updating KOrganizer
01585             KAlarm::addEvent(event, 0, mDeferDlg, true, false);
01586             if (event.deferred())
01587                 mNoPostAction = true;
01588             if (kcalEvent)
01589             {
01590                 event.setUid(KAEvent::EXPIRED);
01591                 KAlarm::deleteEvent(event, false);
01592             }
01593         }
01594         if (theApp()->wantRunInSystemTray())
01595         {
01596             // Alarms are to be displayed only if the system tray icon is running,
01597             // so start it if necessary so that the deferred alarm will be shown.
01598             theApp()->displayTrayIcon(true);
01599         }
01600         mNoCloseConfirm = true;   // allow window to close without confirmation prompt
01601         close();
01602     }
01603     else
01604         raise();
01605     delete mDeferDlg;
01606     mDeferDlg = 0;
01607 }
01608 
01609 /******************************************************************************
01610 *  Called when the KAlarm icon button in the message window is clicked.
01611 *  Displays the main window, with the appropriate alarm selected.
01612 */
01613 void MessageWin::displayMainWindow()
01614 {
01615     KAlarm::displayMainWindowSelected(mEventID);
01616 }
01617 
01618 /******************************************************************************
01619 * Check whether the specified error message is already displayed for this
01620 * alarm, and note that it will now be displayed.
01621 * Reply = true if message is already displayed.
01622 */
01623 bool MessageWin::haveErrorMessage(unsigned msg) const
01624 {
01625     if (!mErrorMessages.contains(mEventID))
01626         mErrorMessages.insert(mEventID, 0);
01627     bool result = (mErrorMessages[mEventID] & msg);
01628     mErrorMessages[mEventID] |= msg;
01629     return result;
01630 }
01631 
01632 void MessageWin::clearErrorMessage(unsigned msg) const
01633 {
01634     if (mErrorMessages.contains(mEventID))
01635     {
01636         if (mErrorMessages[mEventID] == msg)
01637             mErrorMessages.remove(mEventID);
01638         else
01639             mErrorMessages[mEventID] &= ~msg;
01640     }
01641 }
01642 
01643 
01644 /******************************************************************************
01645 * Check whether the message window should be modal, i.e. with title bar etc.
01646 * Normally this follows the Preferences setting, but if there is a full screen
01647 * window displayed, on X11 the message window has to bypass the window manager
01648 * in order to display on top of it (which has the side effect that it will have
01649 * no window decoration).
01650 */
01651 bool wantModal()
01652 {
01653     bool modal = Preferences::modalMessages();
01654     if (modal)
01655     {
01656         KWinModule wm(0, KWinModule::INFO_DESKTOP);
01657         KWin::WindowInfo wi = KWin::windowInfo(wm.activeWindow(), NET::WMState);
01658         modal = !(wi.valid()  &&  wi.hasState(NET::FullScreen));
01659     }
01660     return modal;
01661 }
01662 
01663 
01664 /*=============================================================================
01665 = Class MWMimeSourceFactory
01666 * Gets the mime type of a text file from not only its extension (as per
01667 * TQMimeSourceFactory), but also from its contents. This allows the detection
01668 * of plain text files without file name extensions.
01669 =============================================================================*/
01670 MWMimeSourceFactory::MWMimeSourceFactory(const TQString& absPath, KTextBrowser* view)
01671     : TQMimeSourceFactory(),
01672       mMimeType("text/plain"),
01673       mLast(0)
01674 {
01675     view->setMimeSourceFactory(this);
01676     TQString type = KMimeType::findByPath(absPath)->name();
01677     switch (KAlarm::fileType(type))
01678     {
01679         case KAlarm::TextPlain:
01680         case KAlarm::TextFormatted:
01681             mMimeType = type.latin1();
01682             // fall through to 'TextApplication'
01683         case KAlarm::TextApplication:
01684         default:
01685             // It's assumed to be a text file
01686             mTextFile = absPath;
01687             view->TQTextBrowser::setSource(absPath);
01688             break;
01689 
01690         case KAlarm::Image:
01691             // It's an image file
01692             TQString text = "<img source=\"";
01693             text += absPath;
01694             text += "\">";
01695             view->setText(text);
01696             break;
01697     }
01698     setFilePath(TQFileInfo(absPath).dirPath(true));
01699 }
01700 
01701 MWMimeSourceFactory::~MWMimeSourceFactory()
01702 {
01703     delete mLast;
01704 }
01705 
01706 const TQMimeSource* MWMimeSourceFactory::data(const TQString& abs_name) const
01707 {
01708     if (abs_name == mTextFile)
01709     {
01710         TQFileInfo fi(abs_name);
01711         if (fi.isReadable())
01712         {
01713             TQFile f(abs_name);
01714             if (f.open(IO_ReadOnly)  &&  f.size())
01715             {
01716                 TQByteArray ba(f.size());
01717                 f.readBlock(ba.data(), ba.size());
01718                 TQStoredDrag* sr = new TQStoredDrag(mMimeType);
01719                 sr->setEncodedData(ba);
01720                 delete mLast;
01721                 mLast = sr;
01722                 return sr;
01723             }
01724         }
01725     }
01726     return TQMimeSourceFactory::data(abs_name);
01727 }