/* * kbiff.cpp * Copyright (C) 1999-2008 Kurt Granroth * * This file contains the implementation of the main KBiff * widget */ #include "kbiff.h" #include "kbiff.moc" #include #include #include #include #include #include #include #include #include #include #include #include #include "setupdlg.h" #include "notify.h" #include "status.h" #include "led.h" #include #include 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& mailbox_list, unsigned int poll) { TQPtrList 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); }