Diffstat (limited to 'core/launcher/runningappbar.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/launcher/runningappbar.cpp | 287 |
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 | |||
40 | RunningAppBar::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 | |||
54 | RunningAppBar::~RunningAppBar() { | ||
55 | } | ||
56 | |||
57 | void 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 | |||
69 | void 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 | |||
81 | void 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 | |||
96 | void 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 | |||
104 | void 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 | |||
117 | void 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 | |||
141 | void 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 | |||
163 | void 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 | |||
188 | QSize RunningAppBar::sizeHint() const | ||
189 | { | ||
190 | return QSize( frameWidth(), AppLnk::smallIconSize()+frameWidth()*2+3 ); | ||
191 | } | ||
192 | |||
193 | const int AppMonitor::RAISE_TIMEOUT_MS = 500; | ||
194 | |||
195 | AppMonitor::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 | |||
204 | AppMonitor::~AppMonitor() { | ||
205 | if (m_AppKillerBox) { | ||
206 | delete m_AppKillerBox; | ||
207 | m_AppKillerBox = 0L; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | void 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 | |||
236 | void 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 | |||
265 | void 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 | } | ||