You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kbiff/kbiff/kbiff.cpp

996 lines
26 KiB

/*
* kbiff.cpp
* Copyright (C) 1999-2008 Kurt Granroth <granroth@kde.org>
*
* This file contains the implementation of the main KBiff
* widget
*/
#include "kbiff.h"
#include "kbiff.moc"
#include <ntqmovie.h>
#include <ntqtooltip.h>
#include <kaudioplayer.h>
#include <tdeconfig.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <tdepopupmenu.h>
#include <kprocess.h>
#include <krun.h>
#include <twin.h>
#include "setupdlg.h"
#include "notify.h"
#include "status.h"
#include "led.h"
#include <unistd.h>
#include <dcopclient.h>
KBiff::KBiff(DCOPClient *client_, TQWidget *parent_)
: DCOPObjectProxy(client_),
TQLabel(parent_),
statusTimer(0),
status(0),
statusChanged(true),
mled( new Led("mled") )
{
setBackgroundMode(X11ParentRelative);
setAutoResize(true);
setMargin(0);
setAlignment(AlignLeft | AlignTop);
// enable the session management stuff
connect(kapp, TQ_SIGNAL(saveYourself()), this, TQ_SLOT(saveYourself()));
// nuke the list stuff when removed
monitorList.setAutoDelete(true);
notifyList.setAutoDelete(true);
statusList.setAutoDelete(true);
// register with DCOP
registerMe(client_);
reset();
}
KBiff::~KBiff()
{
monitorList.clear();
notifyList.clear();
statusList.clear();
delete mled;
// we no longer want to be registered
DCOPClient *client = kapp->dcopClient();
TQCString proxy = TQCString("kbiff-") + TQCString().setNum(getpid());
if (client->isApplicationRegistered(proxy) == true)
{
TQByteArray params;
TQDataStream ds(params, IO_WriteOnly);
ds << proxy;
client->send("kbiff", "kbiff", "proxyDeregister(TQString)", params);
}
client->detach();
}
void KBiff::processSetup(const KBiffSetup* setup_, bool run_)
{
// General settings
isSecure = setup_->getSecure();
profile = setup_->getProfile();
mailClient = setup_->getMailClient();
sessions = setup_->getSessionManagement();
skipcheck = setup_->getCheckStartup();
noMailIcon = setup_->getNoMailIcon();
newMailIcon = setup_->getNewMailIcon();
oldMailIcon = setup_->getOldMailIcon();
noConnIcon = setup_->getNoConnIcon();
stoppedIcon = setup_->getStoppedIcon();
// New mail
systemBeep = setup_->getSystemBeep();
runCommand = setup_->getRunCommand();
runCommandPath = setup_->getRunCommandPath();
runResetCommand = setup_->getRunResetCommand();
runResetCommandPath = setup_->getRunResetCommandPath();
playSound = setup_->getPlaySound();
playSoundPath = setup_->getPlaySoundPath();
notify = setup_->getNotify();
dostatus = setup_->getStatus();
// if we aren't going the status route, we should at least
// provide a tooltip!
if (dostatus == false)
TQToolTip::add(this, profile);
else
TQToolTip::remove(this);
// set all the new mailboxes
setMailboxList(setup_->getMailboxList(), setup_->getPoll());
// change the dock state if necessary
if (docked != setup_->getDock())
dock();
if (run_ && !skipcheck)
start();
skipcheck = false;
// handle session management disabling
if (sessions == false)
{
disconnect(this, TQ_SLOT(saveYourself()));
kapp->disableSessionManagement();
}
// if we are going to be doing status, we might as well create
// one now
if ( dostatus )
{
statusList.clear();
KBiffMonitor *monitor;
for (monitor = monitorList.first(); monitor; monitor = monitorList.next())
{
statusList.append(new KBiffStatusItem(monitor->getMailboxKey(),
monitor->newMessages(),
monitor->curMessages()));
}
if (status)
{
status->hide();
delete status;
status = 0;
}
status = new KBiffStatus(this, profile, statusList);
}
delete setup_;
}
void KBiff::setMailboxList(const TQPtrList<KBiffMailbox>& mailbox_list, unsigned int poll)
{
TQPtrList<KBiffMailbox> tmp_list = mailbox_list;
myMUTEX = true;
if (isRunning())
stop();
monitorList.clear();
KBiffMailbox *mbox;
for (mbox = tmp_list.first(); mbox != 0; mbox = tmp_list.next())
{
KBiffURL *url = &(mbox->url);
KBiffMonitor *monitor = new KBiffMonitor();
monitor->setMailbox(*url);
monitor->setPollInterval(poll);
monitor->setMailboxKey(mbox->key);
connect(monitor, TQ_SIGNAL(signal_newMail(const int, const TQString&)),
this, TQ_SLOT(haveNewMail(const int, const TQString&)));
connect(monitor, TQ_SIGNAL(signal_currentStatus(const int, const TQString&, const KBiffMailState)),
this, TQ_SLOT(currentStatus(const int, const TQString&, const KBiffMailState)));
connect(monitor, TQ_SIGNAL(signal_noMail()), this, TQ_SLOT(displayPixmap()));
connect(monitor, TQ_SIGNAL(signal_noMail()),
this, TQ_SLOT(haveNoNewMail()));
connect(monitor, TQ_SIGNAL(signal_oldMail()), this, TQ_SLOT(displayPixmap()));
connect(monitor, TQ_SIGNAL(signal_oldMail()),
this, TQ_SLOT(haveNoNewMail()));
connect(monitor, TQ_SIGNAL(signal_noConn()), this, TQ_SLOT(displayPixmap()));
connect(monitor, TQ_SIGNAL(signal_noConn()),
this, TQ_SLOT(haveNoNewMail()));
connect(monitor, TQ_SIGNAL(signal_invalidLogin(const TQString&)),
this, TQ_SLOT(invalidLogin(const TQString&)));
connect(monitor, TQ_SIGNAL(signal_fetchMail(const TQString&)),
this, TQ_SLOT(slotLaunchFetchClient(const TQString&)));
monitorList.append(monitor);
}
myMUTEX = false;
}
bool KBiff::isDocked() const
{
return docked;
}
void KBiff::readSessionConfig()
{
TDEConfig *config = kapp->sessionConfig();
config->setGroup("KBiff");
profile = config->readEntry("Profile", "Inbox");
docked = config->readBoolEntry("IsDocked", false);
bool run = config->readBoolEntry("IsRunning", true);
KBiffSetup *setup_dlg = new KBiffSetup(profile);
processSetup(setup_dlg, run);
}
///////////////////////////////////////////////////////////////////////////
// Protected Virtuals
///////////////////////////////////////////////////////////////////////////
void KBiff::mousePressEvent(TQMouseEvent *e)
{
// regardless of which button, get rid of the status box
if (status)
status->hide();
// also, ditch the timer
if (statusTimer)
{
statusTimer->stop();
delete statusTimer;
statusTimer = 0;
}
// check if this is a right click
if(e->button() == RightButton)
{
// popup the context menu
popupMenu();
}
else
{
// execute the command
slotLaunchMailClient();
readPop3MailNow();
}
}
void KBiff::enterEvent(TQEvent *e)
{
TQLabel::enterEvent(e);
// return now if the user doesn't want this feature.
// *sniff*.. the ingrate.. I worked so hard on this, too... *sob*
if (dostatus == false)
return;
// don't do anything if we already have a timer
if (statusTimer)
return;
// popup the status in one second
statusTimer = new TQTimer(this);
connect(statusTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(popupStatus()));
statusTimer->start(1000, true);
}
void KBiff::leaveEvent(TQEvent *e)
{
TQLabel::leaveEvent(e);
// stop the timer if it is going
if (statusTimer)
{
statusTimer->stop();
delete statusTimer;
statusTimer = 0;
}
// get rid of the status box if it is activated
if (status)
status->hide();
}
void KBiff::popupStatus()
{
// if we don't get rid of the timer, then the very next
// time we float over the icon, the status box will
// *not* be activated!
if (statusTimer)
{
statusTimer->stop();
delete statusTimer;
statusTimer = 0;
}
if (statusChanged)
{
statusList.clear();
KBiffMonitor *monitor;
for(monitor = monitorList.first(); monitor; monitor = monitorList.next())
{
statusList.append(new KBiffStatusItem(monitor->getMailboxKey(), monitor->newMessages(), monitor->curMessages()));
}
statusChanged = false;
}
status->updateListView(statusList);
status->popup(TQCursor::pos());
}
bool KBiff::isGIF8x(const TQString& file_name)
{
/* The first test checks if we can open the file */
TQFile gif8x(file_name);
if (gif8x.open(IO_ReadOnly) == false)
return false;
/**
* The GIF89 format specifies that the first five bytes of
* the file shall have the characters 'G' 'I' 'F' '8' '9'.
* The GIF87a format specifies that the first six bytes
* shall read 'G' 'I' 'F' '8' '7' 'a'. Knowing that, we
* shall read in the first six bytes and test away.
*/
char header[6];
int bytes_read = gif8x.readBlock(header, 6);
/* Close the file just to be nice */
gif8x.close();
/* If we read less than 6 bytes, then its definitely not GIF8x */
if (bytes_read < 6)
return false;
/* Now test for the magical GIF8(9|7a) */
if (header[0] == 'G' &&
header[1] == 'I' &&
header[2] == 'F' &&
header[3] == '8' &&
(header[4] == '9' || (header[4] == '7' &&
header[5] == 'a')))
{
/* Success! */
return true;
}
/* Apparently not GIF8(9|7a) */
return false;
}
///////////////////////////////////////////////////////////////////////////
// Protected Slots
///////////////////////////////////////////////////////////////////////////
void KBiff::saveYourself()
{
if (sessions)
{
TDEConfig *config = kapp->sessionConfig();
config->setGroup("KBiff");
config->writeEntry("Profile", profile);
config->writeEntry("IsDocked", docked);
config->writeEntry("IsRunning", isRunning());
config->sync();
}
}
void KBiff::invokeHelp()
{
kapp->invokeHelp();
}
void KBiff::displayPixmap()
{
if (myMUTEX)
return;
// we will try to deduce the pixmap (or gif) name now. it will
// vary depending on the dock and mail state
TQString pixmap_name;
bool has_new = false, has_old = false, has_no = true, has_noconn = false;
KBiffMonitor *monitor;
for (monitor = monitorList.first();
monitor != 0 && has_new == false;
monitor = monitorList.next())
{
switch (monitor->getMailState())
{
case NoMail:
has_no = true;
break;
case NewMail:
has_new = true;
break;
case OldMail:
has_old = true;
break;
case NoConn:
has_noconn = true;
break;
default:
has_no = true;
break;
}
}
if ( !isRunning() )
{
pixmap_name = stoppedIcon;
mled->Off();
}
else if (has_new)
{
pixmap_name = newMailIcon;
// turn on led for new mail, otherwise turn off
mled->On();
}
else if (has_old)
{
pixmap_name = oldMailIcon;
mled->Off();
}
else if (has_noconn)
{
pixmap_name = noConnIcon;
mled->Off();
}
else
{
pixmap_name = noMailIcon;
mled->Off();
}
if (docked)
{
// we need to check if this has path info encoded into it
TQFileInfo info(pixmap_name);
// if info.fileName() returns pixmap_name, then we no there
// isn't any paths attached and we can just prepend our 'mini'
if (info.fileName() == pixmap_name)
pixmap_name.prepend("mini-");
else
{
// so we have some path junk on it. we get the filename
// by itself, prepend our 'mini' and tack it onto the end
// of the original dirpath. simple
TQString filename(info.fileName());
filename.prepend("mini-");
// we aren't guaranteed that the dirpath will end in a /
// so we add one (an extra one won't hurt, in any case
pixmap_name = info.dirPath() + "/" + filename;
}
}
TQString filename = TDEGlobal::iconLoader()->iconPath( pixmap_name, TDEIcon::User );
TQFileInfo file(filename);
// at this point, we have the file to display. so display it
if (isGIF8x(file.absFilePath()))
setMovie(TQMovie(file.absFilePath()));
else
setPixmap(TQPixmap(file.absFilePath()));
adjustSize();
}
void KBiff::currentStatus(const int num, const TQString& the_mailbox, const KBiffMailState the_state)
{
statusChanged = true;
// iterate through all saved notify dialogs to see if "our" one is
// currently being displayed
KBiffNotify *notifyptr;
for (notifyptr = notifyList.first();
notifyptr != 0;
notifyptr = notifyList.next())
{
// if this one is not visible, delete it from the list. the only
// way it will again become visible is if the haveNewMail slot
// gets triggered
if (notifyptr->isVisible() == false)
{
notifyList.remove();
}
else
{
// if this box is visible (active), we see if it is the one
// we are looking for
if (notifyptr->getMailbox() == the_mailbox)
{
// yep. now, if there is new mail, we set the new number in
// the dialog. if it is any other state, we remove this
// dialog from the list
switch (the_state)
{
case NewMail:
notifyptr->setNew(num);
break;
case OldMail:
case NoMail:
case NoConn:
default:
notifyList.remove();
break;
}
}
}
}
}
void KBiff::haveNewMail(const int num, const TQString& the_mailbox)
{
displayPixmap();
// beep if we are allowed to
if (systemBeep)
{
kapp->beep();
}
// run a command if we have to
if (runCommand)
{
// make sure the command exists
if (!runCommandPath.isEmpty())
{
executeCommand(replaceCommandArgs(runCommandPath));
}
}
// play a sound if we have to
if (playSound)
slotPlaySound(playSoundPath);
// notify if we must
if (notify)
{
KBiffNotify *notify_dlg = new KBiffNotify(this, num, the_mailbox);
connect(notify_dlg, TQ_SIGNAL(signalLaunchMailClient()),
this, TQ_SLOT(slotLaunchMailClient()));
notifyList.append(notify_dlg);
notify_dlg->show();
// half-hearted attempt to center this
int x_pos = (TDEApplication::desktop()->width() - notify_dlg->width()) / 2;
int y_pos = (TDEApplication::desktop()->height() - notify_dlg->height()) / 2;
notify_dlg->move(x_pos, y_pos);
}
}
void KBiff::haveNoNewMail()
{
displayPixmap();
// run a command if we have to
if (runResetCommand)
{
// make sure the command exists
if (!runResetCommandPath.isEmpty())
{
executeCommand(runResetCommandPath);
}
}
}
TQString KBiff::getURLWithNewMail()
{
KBiffMonitor *monitor;
for (monitor = monitorList.first();
monitor != 0;
monitor = monitorList.next())
{
if(monitor->getMailState() == NewMail)
return monitor->getMailbox();
}
return monitorList.first()->getMailbox();
}
TQString KBiff::getMailBoxWithNewMail()
{
TQString url(getURLWithNewMail());
int slashPos = url.find('/');
if(slashPos == -1)
return url.mid(slashPos + 1);
else
return url.mid(url.find(':') + 1);
}
TQString KBiff::replaceCommandArgs(TQString cmdStr)
{
bool expand = false;
for(unsigned int i = 0; i < cmdStr.length(); i++)
{
if(expand)
{
expand = false;
if(cmdStr[i] == 'm')
cmdStr.replace(i - 1, 2, getMailBoxWithNewMail());
else if(cmdStr[i] == 'u')
cmdStr.replace(i - 1, 2, getURLWithNewMail());
else if(cmdStr[i] == '%')
cmdStr.replace(i - 1, 2, "%");
continue;
}
if(cmdStr[i] == '%')
expand = true;
}
return cmdStr;
}
void KBiff::dock()
{
// destroy the old window
if (this->isVisible())
{
this->hide();
this->destroy(true, true);
this->create(0, true, false);
kapp->setMainWidget(this);
// we don't want a "real" top widget if we are _going_ to
// be docked.
if (docked)
kapp->setTopWidget(this);
else
kapp->setTopWidget(new TQWidget);
}
if (docked == false)
{
docked = true;
// enable docking
KWin::setSystemTrayWindowFor(this->winId(), 0);
}
else
docked = false;
// (un)dock it!
this->show();
TQTimer::singleShot(1000, this, TQ_SLOT(displayPixmap()));
}
void KBiff::setup()
{
KBiffSetup* setup_dlg = new KBiffSetup(profile);
if (setup_dlg->exec())
processSetup(setup_dlg, true);
else
delete setup_dlg;
}
void KBiff::checkMailNow()
{
KBiffMonitor *monitor;
for (monitor = monitorList.first();
monitor != 0;
monitor = monitorList.next())
{
monitor->checkMailNow();
}
}
void KBiff::readMailNow()
{
KBiffMonitor *monitor;
for (monitor = monitorList.first();
monitor != 0;
monitor = monitorList.next())
{
monitor->setMailboxIsRead();
}
}
void KBiff::readPop3MailNow()
{
KBiffMonitor *monitor;
for (monitor = monitorList.first();
monitor != 0;
monitor = monitorList.next())
{
if (monitor->getProtocol() == "pop3")
monitor->setMailboxIsRead();
}
}
void KBiff::stop()
{
KBiffMonitor *monitor;
for (monitor = monitorList.first();
monitor != 0;
monitor = monitorList.next())
{
monitor->stop();
}
displayPixmap();
}
void KBiff::start()
{
myMUTEX = true;
KBiffMonitor *monitor;
for (unsigned int i = 0; i < monitorList.count(); i++)
{
monitor = monitorList.at(i);
monitor->start();
}
myMUTEX = false;
displayPixmap();
}
///////////////////////////////////////////////////////////////////////////
// Protected Functions
///////////////////////////////////////////////////////////////////////////
void KBiff::popupMenu()
{
TDEPopupMenu *popup = new TDEPopupMenu(0, "popup");
popup->insertTitle(kapp->miniIcon(), profile);
// if secure, disable everything but exit
if (isSecure == false)
{
if (docked)
popup->insertItem(i18n("&UnDock"), this, TQ_SLOT(dock()));
else
popup->insertItem(i18n("&Dock"), this, TQ_SLOT(dock()));
popup->insertItem(i18n("&Setup..."), this, TQ_SLOT(setup()));
popup->insertSeparator();
popup->insertItem(i18n("&Help..."), this, TQ_SLOT(invokeHelp()));
popup->insertSeparator();
int check_id;
check_id = popup->insertItem(i18n("&Check Mail Now"), this, TQ_SLOT(checkMailNow()));
int read_id;
read_id = popup->insertItem(i18n("&Read Mail Now"), this, TQ_SLOT(readMailNow()));
if (isRunning())
{
popup->setItemEnabled(check_id, true);
popup->setItemEnabled(read_id, true);
popup->insertItem(i18n("&Stop"), this, TQ_SLOT(stop()));
}
else
{
popup->setItemEnabled(check_id, false);
popup->setItemEnabled(read_id, false);
popup->insertItem(i18n("&Start"), this, TQ_SLOT(start()));
}
popup->insertSeparator();
}
popup->insertItem(i18n("E&xit"), kapp, TQ_SLOT(quit()));
popup->popup(TQCursor::pos());
}
void KBiff::reset()
{
// reset all the member variables
systemBeep = true;
runCommand = false;
runCommandPath = "";
playSound = false;
playSoundPath = "";
notify = true;
dostatus = true;
noMailIcon = "nomail";
newMailIcon = "newmail";
oldMailIcon = "oldmail";
noConnIcon = "noconn";
stoppedIcon = "stopped";
docked = false;
isSecure = false;
mailClient = "xmutt -f +%m";
myMUTEX = false;
}
bool KBiff::isRunning()
{
bool is_running = false;
KBiffMonitor *monitor;
for (monitor = monitorList.first();
monitor != 0;
monitor = monitorList.next())
{
if (monitor->isRunning())
{
is_running = true;
break;
}
}
return is_running;
}
void KBiff::executeCommand(const TQString& command)
{
KRun::runCommand(command);
}
void KBiff::slotLaunchFetchClient(const TQString& fetchClient)
{
if (!fetchClient.isEmpty())
executeCommand(fetchClient);
}
void KBiff::slotLaunchMailClient()
{
if (!mailClient.isEmpty())
executeCommand(replaceCommandArgs(mailClient));
}
void KBiff::slotPlaySound(const TQString& play_sound)
{
// make sure something is specified
if (!play_sound.isNull())
KAudioPlayer::play(play_sound);
}
bool KBiff::process(const TQCString&, const TQCString& function,
const TQByteArray& data, TQCString& replyType,
TQByteArray &replyData)
{
TQDataStream args(data, IO_ReadOnly);
TQDataStream reply(replyData, IO_WriteOnly);
TQString proxy;
if (function == "proxyRegister(TQString)")
{
args >> proxy;
proxyList.append(proxy);
replyType = "void";
return true;
}
else if (function == "proxyDeregister(TQString)")
{
args >> proxy;
proxyList.remove(proxy);
replyType = "void";
return true;
}
else if (function == "hasMailbox(TQString)")
{
TQString mailbox;
args >> mailbox;
reply << (bool) findMailbox(mailbox, proxy);
replyType = "bool";
return true;
}
else if (function == "mailCount(TQString)")
{
reply << -1;
replyType = "int";
return true;
}
else if (function == "newMailCount(TQString)")
{
TQString mailbox;
args >> mailbox;
reply << newMailCount(mailbox);
replyType = "int";
return true;
}
return false;
}
int KBiff::newMailCount(const TQString& url)
{
int newmail = -1;
TQString proxy;
if (findMailbox(url, proxy) == true)
{
if (proxy != TQString::null)
{
TQByteArray data;
TQDataStream ds(data, IO_WriteOnly);
ds << url;
TQByteArray reply_data;
TQCString reply_type;
TQDataStream reply(reply_data, IO_ReadOnly);
DCOPClient *dcc = kapp->dcopClient();
if (dcc->call(proxy.ascii(), "kbiff",
"newMailCount(TQString)", data, reply_type,
reply_data) == true)
{
reply >> newmail;
}
}
else
{
KBiffMonitor *monitor;
for(monitor = monitorList.first(); monitor;
monitor = monitorList.next())
{
if (monitor->getMailbox() == url)
{
newmail = monitor->newMessages();
break;
}
}
}
}
return newmail;
}
bool KBiff::findMailbox(const TQString& url, TQString& proxy)
{
bool has_mailbox = false;
KBiffMonitor *monitor;
for(monitor = monitorList.first(); monitor; monitor = monitorList.next())
{
if (monitor->getMailbox() == url)
{
has_mailbox = true;
break;
}
}
if (has_mailbox == false)
{
TQByteArray data, replyData;
TQCString replyType;
TQDataStream ds(data, IO_WriteOnly);
ds << url;
// okay, now try to iterate through our proxies
TQStringList::Iterator it = proxyList.begin();
for ( ; it != proxyList.end(); it++)
{
DCOPClient *dcc = kapp->dcopClient();
if (dcc->call(TQCString((*it).ascii()), "kbiff",
"hasMailbox(TQString)", data, replyType,
replyData) == true)
{
has_mailbox = true;
proxy = *it;
break;
}
}
}
return has_mailbox;
}
void KBiff::registerMe(DCOPClient *client)
{
// we need to attach our client before doing anything
client->attach();
// if we aren't registered yet, then we will do so.. and be
// responsible for all *other* kbiff requests, too!
if (client->isApplicationRegistered("kbiff") == false)
client->registerAs("kbiff");
else
{
// okay, there is a running kbiff already. we will let it
// know that we are active and let it feed us requests
TQCString proxy = TQCString("kbiff-") + TQCString().setNum(getpid());
TQByteArray params, reply;
TQCString reply_type;
TQDataStream ds(params, IO_WriteOnly);
ds << proxy;
client->send("kbiff", "kbiff", "proxyRegister(TQString)", params);
client->registerAs(TQCString(proxy));
}
}
void KBiff::invalidLogin(const TQString& mailbox)
{
TQString title(i18n("Invalid Login to %1").arg(mailbox));
KMessageBox::sorry(0,
i18n("I was not able to login to the remote server.\n"
"This means that either the server is down or you have "
"entered an incorrect username or password.\n"
"Please make sure that you have entered the correct settings."),
title);
}