00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <QWidget>
00011 #include <qdialog.h>
00012
00013 #include <QRegExp>
00014 #include <QTimer>
00015 #include <qevent.h>
00016
00017 #include <QMouseEvent>
00018 #include <Q3ValueList>
00019 #include <Q3CString>
00020 #include <kdeversion.h>
00021 #include <kapplication.h>
00022 #include <kaction.h>
00023 #include <kactioncollection.h>
00024 #include <ksimpleconfig.h>
00025 #include <ksystemtray.h>
00026 #include <kiconloader.h>
00027 #include <kpassivepopup.h>
00028 #include <kmessagebox.h>
00029 #include <kmenu.h>
00030 #include <kdebug.h>
00031 #include <klocale.h>
00032 #include <kwinmodule.h>
00033 #include <kwin.h>
00034 #include <khelpmenu.h>
00035 #include <kglobal.h>
00036 #include <kstdguiitem.h>
00037
00038 #include <ktoolinvocation.h>
00039
00040 #include "profileserver.h"
00041 #include "irkick.h"
00042 #include <QtDBus/qdbusconnection.h>
00043
00044 void IRKTrayIcon::mousePressEvent(QMouseEvent *e)
00045 {
00046 KSystemTray::mousePressEvent(new QMouseEvent(QEvent::MouseButtonPress, e->pos(), e->globalPos(), e->button() == Qt::LeftButton ? Qt::RightButton : e->button(), e->state()));
00047 }
00048
00049 IRKick::IRKick(const QString &obj)
00050 : QObject(), npApp(QString::null)
00051 {
00052 QDBus::sessionBus().registerObject(obj, this, QDBusConnection::ExportSlots);
00053 theClient = new KLircClient();
00054
00055 theTrayIcon = new IRKTrayIcon();
00056 if(theClient->isConnected())
00057 { theTrayIcon->setPixmap(SmallIcon("irkick"));
00058 theTrayIcon->setToolTip( i18n("KDE Lirc Server: Ready."));
00059 }
00060 else
00061 { theTrayIcon->setPixmap(SmallIcon("irkickoff"));
00062 theTrayIcon->setToolTip( i18n("KDE Lirc Server: No infra-red remote controls found."));
00063 QTimer::singleShot(10000, this, SLOT(checkLirc()));
00064 }
00065 theFlashOff = new QTimer(theTrayIcon);
00066 connect(theFlashOff, SIGNAL(timeout()), SLOT(flashOff()));
00067
00068 theResetCount = 0;
00069 slotReloadConfiguration();
00070 connect(theClient, SIGNAL(connectionClosed()), this, SLOT(slotClosed()));
00071 connect(theClient, SIGNAL(remotesRead()), this, SLOT(resetModes()));
00072 connect(theClient, SIGNAL(commandReceived(const QString &, const QString &, int)), this, SLOT(gotMessage(const QString &, const QString &, int)));
00073
00074 theTrayIcon->contextMenu()->addTitle( "IRKick");
00075 theTrayIcon->contextMenu()->insertItem(SmallIcon( "configure" ), i18n("&Configure..."), this, SLOT(slotConfigure()));
00076 theTrayIcon->contextMenu()->insertSeparator();
00077 theTrayIcon->contextMenu()->insertItem(SmallIcon( "help-contents" ), KStdGuiItem::help().text(), (new KHelpMenu(theTrayIcon, KGlobal::mainComponent().aboutData()))->menu());
00078 theTrayIcon->actionCollection()->action("file_quit")->disconnect(SIGNAL(activated()));
00079 connect(theTrayIcon->actionCollection()->action("file_quit"), SIGNAL(activated()), SLOT(doQuit()));
00080
00081 theTrayIcon->show();
00082 }
00083
00084 IRKick::~IRKick()
00085 {
00086 delete theTrayIcon;
00087 for(QMap<QString,IRKTrayIcon *>::iterator i = currentModeIcons.begin(); i != currentModeIcons.end(); ++i)
00088 if(*i) delete *i;
00089 }
00090
00091 void IRKick::slotClosed()
00092 {
00093 theTrayIcon->setPixmap(SmallIcon("irkickoff"));
00094 KPassivePopup::message("IRKick", i18n("The infrared system has severed its connection. Remote controls are no longer available."), SmallIcon("irkick"), theTrayIcon);
00095 QTimer::singleShot(1000, this, SLOT(checkLirc()));
00096 }
00097
00098 void IRKick::checkLirc()
00099 {
00100 if(!theClient->isConnected())
00101 if(theClient->connectToLirc())
00102 { KPassivePopup::message("IRKick", i18n("A connection to the infrared system has been made. Remote controls may now be available."), SmallIcon("irkick"), theTrayIcon);
00103 theTrayIcon->setPixmap(SmallIcon("irkick"));
00104 }
00105 else
00106 QTimer::singleShot(10000, this, SLOT(checkLirc()));
00107 }
00108
00109 void IRKick::flashOff()
00110 {
00111 theTrayIcon->setPixmap(SmallIcon("irkick"));
00112 }
00113
00114 void IRKick::doQuit()
00115 {
00116 KSimpleConfig theConfig("irkickrc");
00117 theConfig.setGroup("General");
00118 switch(KMessageBox::questionYesNoCancel(0, i18n("Should the Infrared Remote Control server start automatically when you begin KDE?"), i18n("Automatically Start?"), i18n("Start Automatically"), i18n("Do Not Start")))
00119 { case KMessageBox::No: theConfig.writeEntry("AutoStart", false); break;
00120 case KMessageBox::Yes: theConfig.writeEntry("AutoStart", true); break;
00121 case KMessageBox::Cancel: return;
00122 }
00123 KApplication::kApplication()->quit();
00124 }
00125
00126 void IRKick::resetModes()
00127 {
00128 if(theResetCount > 1)
00129 KPassivePopup::message("IRKick", i18n("Resetting all modes."), SmallIcon("irkick"), theTrayIcon);
00130 if(!theResetCount)
00131 allModes.generateNulls(theClient->remotes());
00132
00133 QStringList remotes = theClient->remotes();
00134 for(QStringList::iterator i = remotes.begin(); i != remotes.end(); ++i)
00135 { currentModes[*i] = allModes.getDefault(*i).name();
00136 if(theResetCount && currentModeIcons[*i]) delete currentModeIcons[*i];
00137 currentModeIcons[*i] = 0;
00138 }
00139 updateModeIcons();
00140 theResetCount++;
00141 }
00142
00143 void IRKick::slotReloadConfiguration()
00144 {
00145
00146 KSimpleConfig theConfig("irkickrc");
00147 allActions.loadFromConfig(theConfig);
00148 allModes.loadFromConfig(theConfig);
00149 if(currentModes.count() && theResetCount)
00150 resetModes();
00151 }
00152
00153 void IRKick::slotConfigure()
00154 {
00155 KToolInvocation::startServiceByDesktopName("kcmlirc");
00156 }
00157
00158 void IRKick::updateModeIcons()
00159 {
00160 for(QMap<QString,QString>::iterator i = currentModes.begin(); i != currentModes.end(); ++i)
00161 { Mode mode = allModes.getMode(i.key(), i.data());
00162 if(mode.iconFile().isNull() || mode.iconFile().isEmpty())
00163 { if(currentModeIcons[i.key()])
00164 { delete currentModeIcons[i.key()];
00165 currentModeIcons[i.key()] = 0;
00166 }
00167 }
00168 else
00169 { if(!currentModeIcons[i.key()])
00170 { currentModeIcons[i.key()] = new IRKTrayIcon();
00171 currentModeIcons[i.key()]->show();
00172 currentModeIcons[i.key()]->contextMenu()->addTitle( mode.remoteName());
00173 currentModeIcons[i.key()]->actionCollection()->action("file_quit")->setEnabled(false);
00174 }
00175 currentModeIcons[i.key()]->setPixmap(KIconLoader().loadIcon(mode.iconFile(), KIconLoader::Panel));
00176 currentModeIcons[i.key()]->setToolTip( mode.remoteName() + ": <b>" + mode.name() + "</b>");
00177 }
00178 }
00179 }
00180
00181 bool IRKick::getPrograms(const IRAction &action, QStringList &programs)
00182 {
00183 DCOPClient *theDC = KApplication::dcopClient();
00184 programs.clear();
00185
00186 if(action.unique())
00187 { if(theDC->isApplicationRegistered(action.program().utf8()))
00188 programs += action.program();
00189 }
00190 else
00191 {
00192 QRegExp r = QRegExp("^" + action.program() + "-(\\d+)$");
00193
00194 DCOPCStringList buf = theDC->registeredApplications();
00195 for(DCOPCStringList::iterator i = buf.begin(); i != buf.end(); ++i)
00196 {
00197 QString program = QString::fromUtf8(*i);
00198 if(program.contains(r))
00199 programs += program;
00200 }
00201 if(programs.size() > 1 && action.ifMulti() == IM_DONTSEND)
00202 return false;
00203 else if(programs.size() > 1 && action.ifMulti() == IM_SENDTOTOP)
00204 { Q3ValueList<WId> s = KWinModule().stackingOrder();
00205
00206 for(Q3ValueList<WId>::iterator i = s.fromLast(); i != s.end(); i--)
00207 { int p = KWin::windowInfo(*i,NET::WMPid).win();
00208 QString id = action.program() + "-" + QString().setNum(p);
00209 if(programs.contains(id))
00210 { programs.clear();
00211 programs += id;
00212 break;
00213 }
00214 }
00215 while(programs.size() > 1) programs.remove(programs.begin());
00216 }
00217 else if(programs.size() > 1 && action.ifMulti() == IM_SENDTOBOTTOM)
00218 { Q3ValueList<WId> s = KWinModule().stackingOrder();
00219
00220 for(Q3ValueList<WId>::iterator i = s.begin(); i != s.end(); ++i)
00221 { int p = KWin::windowInfo(*i,NET::WMPid).win();
00222 QString id = action.program() + "-" + QString().setNum(p);
00223 if(programs.contains(id))
00224 { programs.clear();
00225 programs += id;
00226 break;
00227 }
00228 }
00229 while(programs.size() > 1) programs.remove(programs.begin());
00230 }
00231 }
00232 return true;
00233 }
00234
00235 void IRKick::executeAction(const IRAction &action)
00236 {
00237 DCOPClient *theDC = KApplication::dcopClient();
00238 QStringList programs;
00239
00240 if(!getPrograms(action, programs)) return;
00241
00242
00243 if(action.autoStart() && !programs.size())
00244 { QString sname = ProfileServer::profileServer()->getServiceName(action.program());
00245 if(!sname.isNull())
00246 {
00247 KPassivePopup::message("IRKick", i18n("Starting <b>%1</b>...", action.application()), SmallIcon("irkick"), theTrayIcon);
00248 KToolInvocation::startServiceByDesktopName(sname);
00249 }
00250 }
00251 if(action.isJustStart()) return;
00252
00253 if(!getPrograms(action, programs)) return;
00254
00255 for(QStringList::iterator i = programs.begin(); i != programs.end(); ++i)
00256 { const QString &program = *i;
00257 if(theDC->isApplicationRegistered(program.utf8()))
00258 { QByteArray data; QDataStream arg(&data, QIODevice::WriteOnly);
00259 arg.setVersion(QDataStream::Qt_3_1);
00260 kDebug() << "Sending data (" << program << ", " << action.object() << ", " << action.method().prototypeNR() ;
00261 for(Arguments::const_iterator j = action.arguments().begin(); j != action.arguments().end(); ++j)
00262 { kDebug() << "Got argument..." ;
00263 switch((*j).type())
00264 { case QVariant::Int: arg << (*j).toInt(); break;
00265 case QVariant::CString: arg << (*j).toCString(); break;
00266 case QVariant::StringList: arg << (*j).toStringList(); break;
00267 case QVariant::UInt: arg << (*j).toUInt(); break;
00268 case QVariant::Bool: arg << (*j).toBool(); break;
00269 case QVariant::Double: arg << (*j).toDouble(); break;
00270 default: arg << (*j).toString(); break;
00271 }
00272 }
00273 theDC->send(program.utf8(), action.object().utf8(), action.method().prototypeNR().utf8(), data);
00274 }
00275 }
00276 }
00277
00278 void IRKick::gotMessage(const QString &theRemote, const QString &theButton, int theRepeatCounter)
00279 {
00280 kDebug() << "Got message: " << theRemote << ": " << theButton << " (" << theRepeatCounter << ")" ;
00281 theTrayIcon->setPixmap(SmallIcon("irkickflash"));
00282 theFlashOff->start(200, true);
00283 if(!npApp.isNull())
00284 {
00285 QString theApp = npApp;
00286 npApp = QString();
00287
00288 QByteArray data; QDataStream arg(&data, QIODevice::WriteOnly);
00289 arg.setVersion(QDataStream::Qt_3_1);
00290 arg << theRemote << theButton;
00291 KApplication::dcopClient()->send(theApp.utf8(), npModule.utf8(), npMethod.utf8(), data);
00292 }
00293 else
00294 {
00295 if(currentModes[theRemote].isNull()) currentModes[theRemote] = "";
00296 IRAItList l = allActions.findByModeButton(Mode(theRemote, currentModes[theRemote]), theButton);
00297 if(!currentModes[theRemote].isEmpty())
00298 l += allActions.findByModeButton(Mode(theRemote, ""), theButton);
00299 bool doBefore = true, doAfter = false;
00300 for(IRAItList::const_iterator i = l.begin(); i != l.end(); ++i)
00301 if((**i).isModeChange() && !theRepeatCounter)
00302 {
00303 currentModes[theRemote] = (**i).modeChange();
00304 Mode mode = allModes.getMode(theRemote, (**i).modeChange());
00305 updateModeIcons();
00306 doBefore = (**i).doBefore();
00307 doAfter = (**i).doAfter();
00308 break;
00309 }
00310
00311 for(int after = 0; after < 2; after++)
00312 { if(doBefore && !after || doAfter && after)
00313 for(IRAItList::const_iterator i = l.begin(); i != l.end(); ++i)
00314 if(!(**i).isModeChange() && ((**i).repeat() || !theRepeatCounter))
00315 { executeAction(**i);
00316 }
00317 if(!after && doAfter)
00318 { l = allActions.findByModeButton(Mode(theRemote, currentModes[theRemote]), theButton);
00319 if(!currentModes[theRemote].isEmpty())
00320 l += allActions.findByModeButton(Mode(theRemote, ""), theButton);
00321 }
00322 }
00323 }
00324 }
00325
00326 void IRKick::stealNextPress(QString app, QString module, QString method)
00327 {
00328 npApp = app;
00329 npModule = module;
00330 npMethod = method;
00331 }
00332
00333 void IRKick::dontStealNextPress()
00334 {
00335 npApp = QString();
00336 }
00337
00338 #include "irkick.moc"