summaryrefslogtreecommitdiff
path: root/core/launcher/runningappbar.cpp
authorharlekin <harlekin>2002-09-09 18:21:06 (UTC)
committer harlekin <harlekin>2002-09-09 18:21:06 (UTC)
commitc9442bf567252553da8ce216de1e64cbf70db3bd (patch) (unidiff)
tree8eedf088cc2ad2608287a5e6c94ad05587fa9dc0 /core/launcher/runningappbar.cpp
parent3044db24e632adbcf5dbbf1874944d54cee7c8e3 (diff)
downloadopie-c9442bf567252553da8ce216de1e64cbf70db3bd.zip
opie-c9442bf567252553da8ce216de1e64cbf70db3bd.tar.gz
opie-c9442bf567252553da8ce216de1e64cbf70db3bd.tar.bz2
mrulist is dead, long live runningappbar, hopefully
Diffstat (limited to 'core/launcher/runningappbar.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/launcher/runningappbar.cpp287
1 files changed, 287 insertions, 0 deletions
diff --git a/core/launcher/runningappbar.cpp b/core/launcher/runningappbar.cpp
new file mode 100644
index 0000000..298f671
--- a/dev/null
+++ b/core/launcher/runningappbar.cpp
@@ -0,0 +1,287 @@
1/**********************************************************************
2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3**
4** This file is part of the Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************
20*/
21
22#define QTOPIA_INTERNAL_PRELOADACCESS
23
24// For "kill"
25#include <sys/types.h>
26#include <signal.h>
27
28#include <qtimer.h>
29#include <qpopupmenu.h>
30#include <qmessagebox.h>
31#include <qpainter.h>
32#include "qprocess.h"
33#include <qpe/qpeapplication.h>
34#include <qpe/applnk.h>
35#include <qpe/qcopenvelope_qws.h>
36#include <qpe/global.h>
37#include <qwindowsystem_qws.h>
38#include "runningappbar.h"
39
40RunningAppBar::RunningAppBar(QWidget* parent)
41 : QFrame(parent), m_AppLnkSet(0L), m_SelectedAppIndex(-1)
42{
43 m_AppLnkSet = new AppLnkSet( QPEApplication::qpeDir() + "apps" );
44
45 connect(qwsServer, SIGNAL(newChannel(const QString&)), this, SLOT(newQcopChannel(const QString&)));
46 connect(qwsServer, SIGNAL(removedChannel(const QString&)), this, SLOT(removedQcopChannel(const QString&)));
47 QCopChannel* channel = new QCopChannel( "QPE/System", this );
48 connect( channel, SIGNAL(received(const QCString&, const QByteArray&)),
49 this, SLOT(received(const QCString&, const QByteArray&)) );
50
51 spacing = AppLnk::smallIconSize()+3;
52}
53
54RunningAppBar::~RunningAppBar() {
55}
56
57void RunningAppBar::newQcopChannel(const QString& channelName) {
58 QString prefix("QPE/Application/");
59 if (channelName.startsWith(prefix)) {
60 QString appName = channelName.mid(prefix.length());
61// qDebug("App %s just connected!", appName.latin1());
62 const AppLnk* newGuy = m_AppLnkSet->findExec(appName);
63 if (newGuy && !newGuy->isPreloaded()) {
64 addTask(*newGuy);
65 }
66 }
67}
68
69void RunningAppBar::removedQcopChannel(const QString& channelName) {
70 QString prefix("QPE/Application/");
71 if (channelName.startsWith(prefix)) {
72 QString appName = channelName.mid(prefix.length());
73 qDebug("App %s just disconnected!", appName.latin1());
74 const AppLnk* newGuy = m_AppLnkSet->findExec(appName);
75 if (newGuy) {
76 removeTask(*newGuy);
77 }
78 }
79}
80
81void RunningAppBar::received(const QCString& msg, const QByteArray& data) {
82 // Since fast apps appear and disappear without disconnecting from their
83 // channel we need to watch for the showing/hiding events and update according.
84 QDataStream stream( data, IO_ReadOnly );
85 if ( msg == "fastAppShowing(QString)") {
86 QString appName;
87 stream >> appName;
88 addTask(*m_AppLnkSet->findExec(appName));
89 } else if ( msg == "fastAppHiding(QString)") {
90 QString appName;
91 stream >> appName;
92 removeTask(*m_AppLnkSet->findExec(appName));
93 }
94}
95
96void RunningAppBar::addTask(const AppLnk& appLnk) {
97// qDebug("Added %s to app list.", appLnk.name().latin1());
98 AppLnk* newApp = new AppLnk(appLnk);
99 newApp->setExec(appLnk.exec());
100 m_AppList.prepend(newApp);
101 update();
102}
103
104void RunningAppBar::removeTask(const AppLnk& appLnk) {
105 unsigned int i = 0;
106 for (; i < m_AppList.count() ; i++) {
107 AppLnk* target = m_AppList.at(i);
108 if (target->exec() == appLnk.exec()) {
109 qDebug("Removing %s from app list.", appLnk.name().latin1());
110 m_AppList.remove();
111 delete target;
112 }
113 }
114 update();
115}
116
117void RunningAppBar::mousePressEvent(QMouseEvent *e)
118{
119 // Find out if the user is clicking on an app icon...
120 // If so, snag the index so when we repaint we show it
121 // as highlighed.
122 m_SelectedAppIndex = 0;
123 int x=0;
124 QListIterator<AppLnk> it( m_AppList );
125 for ( ; it.current(); ++it,++m_SelectedAppIndex,x+=spacing ) {
126 if ( x + spacing <= width() ) {
127 if ( e->x() >= x && e->x() < x+spacing ) {
128 if ( m_SelectedAppIndex < (int)m_AppList.count() ) {
129 repaint(FALSE);
130 return;
131 }
132 }
133 } else {
134 break;
135 }
136 }
137 m_SelectedAppIndex = -1;
138 repaint( FALSE );
139}
140
141void RunningAppBar::mouseReleaseEvent(QMouseEvent *e)
142{
143 if (e->button() == QMouseEvent::RightButton) {
144 return;
145 }
146 if ( m_SelectedAppIndex >= 0 ) {
147 QString channel = QString("QPE/Application/") + m_AppList.at(m_SelectedAppIndex)->exec();
148 if (QCopChannel::isRegistered(channel.latin1())) {
149// qDebug("%s is running!", m_AppList.at(m_SelectedAppIndex)->exec().latin1());
150 QCopEnvelope e(channel.latin1(), "raise()");
151 // This class will delete itself after hearing from the app or the timer expiring
152 (void)new AppMonitor(*m_AppList.at(m_SelectedAppIndex), *this);
153 }
154 else {
155 removeTask(*m_AppList.at(m_SelectedAppIndex));
156 }
157
158 m_SelectedAppIndex = -1;
159 update();
160 }
161}
162
163void RunningAppBar::paintEvent( QPaintEvent * )
164{
165 QPainter p( this );
166 AppLnk *curApp;
167 int x = 0;
168 int y = (height() - AppLnk::smallIconSize()) / 2;
169 int i = 0;
170
171 p.fillRect( 0, 0, width(), height(), colorGroup().background() );
172
173 QListIterator<AppLnk> it(m_AppList);
174
175 for (; it.current(); i++, ++it ) {
176 if ( x + spacing <= width() ) {
177 curApp = it.current();
178 if ( (int)i == m_SelectedAppIndex )
179 p.fillRect( x, y, spacing, curApp->pixmap().height()+1, colorGroup().highlight() );
180 else
181 p.eraseRect( x, y, spacing, curApp->pixmap().height()+1 );
182 p.drawPixmap( x, y, curApp->pixmap() );
183 x += spacing;
184 }
185 }
186}
187
188QSize RunningAppBar::sizeHint() const
189{
190 return QSize( frameWidth(), AppLnk::smallIconSize()+frameWidth()*2+3 );
191}
192
193const int AppMonitor::RAISE_TIMEOUT_MS = 500;
194
195AppMonitor::AppMonitor(const AppLnk& app, RunningAppBar& owner)
196 : QObject(0L), m_Owner(owner), m_App(app), m_PsProc(0L), m_AppKillerBox(0L) {
197 QCopChannel* channel = new QCopChannel( "QPE/System", this );
198 connect( channel, SIGNAL(received(const QCString&, const QByteArray&)),
199 this, SLOT(received(const QCString&, const QByteArray&)) );
200 connect(&m_Timer, SIGNAL(timeout()), this, SLOT(timerExpired()));
201 m_Timer.start(RAISE_TIMEOUT_MS, TRUE);
202}
203
204AppMonitor::~AppMonitor() {
205 if (m_AppKillerBox) {
206 delete m_AppKillerBox;
207 m_AppKillerBox = 0L;
208 }
209}
210
211void AppMonitor::received(const QCString& msg, const QByteArray& data) {
212 QDataStream stream( data, IO_ReadOnly );
213
214 if (msg == "appRaised(QString)") {
215 QString appName;
216 stream >> appName;
217 if (appName == m_App.exec()) {
218 // qDebug("Got a heartbeat from %s", appName.latin1());
219 m_Timer.stop();
220 // Check to make sure we're not waiting on user input...
221 if (m_AppKillerBox) {
222 // If we are, we kill the dialog box, and the code waiting on the result
223 // will clean us up (basically the user said "no").
224 delete m_AppKillerBox;
225 m_AppKillerBox = 0L;
226 }
227 else {
228 // Ok, we're not waiting on user input, so clean us up now.
229 // WE DELETE OURSELVES HERE! Don't do anything else!!
230 delete this;
231 }
232 }
233 }
234}
235
236void AppMonitor::timerExpired() {
237 // qDebug("Checking in on %s", m_App.name().latin1());
238 // We store this incase the application responds while we're
239 // waiting for user input so we know not to delete ourselves. This
240 // will be cleaned up in the destructor.
241 m_AppKillerBox = new QMessageBox(tr("Application Problem"),
242 tr("<p>%1 is not responding.</p>").arg(m_App.name()) +
243 tr("<p>Would you like to force the application to exit?</p>"),
244 QMessageBox::Warning, QMessageBox::Yes,
245 QMessageBox::No | QMessageBox::Default,
246 QMessageBox::NoButton);
247 if (m_AppKillerBox->exec() == QMessageBox::Yes) {
248 // qDebug("Killing the app!!! Bwuhahahaha!");
249 m_PsProc = new QProcess(QString("ps"));
250 m_PsProc->addArgument("h");
251 m_PsProc->addArgument("-C");
252 m_PsProc->addArgument(m_App.exec());
253 m_PsProc->addArgument("-o");
254 m_PsProc->addArgument("pid");
255 connect(m_PsProc, SIGNAL(processExited()), this, SLOT(psProcFinished()));
256 m_PsProc->start();
257 }
258 else {
259 // qDebug("Wuss..");
260 // WE DELETE OURSELVES HERE! Don't do anything else!!
261 delete this;
262 }
263}
264
265void AppMonitor::psProcFinished() {
266 QString pid = m_PsProc->readLineStdout();
267 delete m_PsProc;
268 m_PsProc = 0L;
269
270 // qDebug("Killing app %s", pid.latin1());
271 if (pid.isEmpty()) {
272 // Hmm.. did the application bail before we got there?
273 qDebug("AppMonitor: Tried to kill application %s but ps couldn't find it.", m_App.exec().latin1());
274 }
275 else {
276 int success = kill(pid.toUInt(), SIGKILL);
277 if (success == 0) {
278 m_Owner.removeTask(m_App);
279 }
280 else {
281 qWarning("Could not kill task %s", m_App.exec().latin1());
282 }
283 }
284
285 // WE DELETE OURSELVES HERE! Don't do anything else!!
286 delete this;
287}