-rw-r--r-- | library/global.cpp | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/library/global.cpp b/library/global.cpp new file mode 100644 index 0000000..e1bbf3e --- a/dev/null +++ b/library/global.cpp @@ -0,0 +1,644 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#include <qpe/qpedebug.h> +#include <qpe/global.h> +#include <qpe/qdawg.h> +#include <qpe/qpeapplication.h> +#include <qpe/resource.h> +#include <qpe/storage.h> +#include <qpe/applnk.h> +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) +#include "qpe/qcopenvelope_qws.h" +#endif + +#include <qfile.h> +#include <qlabel.h> +#include <qtimer.h> +#include <qmap.h> +#include <qdict.h> +#include <qdir.h> +#include <qmessagebox.h> +#include <qregexp.h> + +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> + +#include <qwindowsystem_qws.h> // for qwsServer +#include <qdatetime.h> + +//#include "quickexec_p.h" + +class Emitter : public QObject { + Q_OBJECT +public: + Emitter( QWidget* receiver, const QString& document ) + { + connect(this, SIGNAL(setDocument(const QString&)), + receiver, SLOT(setDocument(const QString&))); + emit setDocument(document); + disconnect(this, SIGNAL(setDocument(const QString&)), + receiver, SLOT(setDocument(const QString&))); + } + +signals: + void setDocument(const QString&); +}; + + +class StartingAppList : public QObject { + Q_OBJECT +public: + static void add( const QString& name ); + static bool isStarting( const QString name ); +private slots: + void handleNewChannel( const QString &); +private: + StartingAppList( QObject *parent=0, const char* name=0 ) ; + + QDict<QTime> dict; + static StartingAppList *appl; +}; + +StartingAppList* StartingAppList::appl = 0; + +StartingAppList::StartingAppList( QObject *parent, const char* name ) + :QObject( parent, name ) +{ +#if QT_VERSION >= 232 && !defined(QT_NO_COP) + connect( qwsServer, SIGNAL( newChannel(const QString&)), + this, SLOT( handleNewChannel(const QString&)) ); + dict.setAutoDelete( TRUE ); +#endif +} + +void StartingAppList::add( const QString& name ) +{ +#if QT_VERSION >= 232 && !defined(QT_NO_COP) + if ( !appl ) + appl = new StartingAppList; + QTime *t = new QTime; + t->start(); + appl->dict.insert( "QPE/Application/" + name, t ); +#endif +} + +bool StartingAppList::isStarting( const QString name ) +{ +#if QT_VERSION >= 232 && !defined(QT_NO_COP) + if ( appl ) { + QTime *t = appl->dict.find( "QPE/Application/" + name ); + if ( !t ) + return FALSE; + if ( t->elapsed() > 10000 ) { + // timeout in case of crash or something + appl->dict.remove( "QPE/Application/" + name ); + return FALSE; + } + return TRUE; + } +#endif + return FALSE; +} + +void StartingAppList::handleNewChannel( const QString & name ) +{ +#if QT_VERSION >= 232 && !defined(QT_NO_COP) + dict.remove( name ); +#endif +} + +static bool docDirCreated = FALSE; +static QDawg* fixed_dawg = 0; +static QDict<QDawg> *named_dawg = 0; + +static QString qpeDir() +{ + QString dir = getenv("QPEDIR"); + if ( dir.isEmpty() ) dir = ".."; + return dir; +} + +static QString dictDir() +{ + return qpeDir() + "/etc/dict"; +} + +/*! + \class Global global.h + \brief The Global class collects application-wide global functions. +*/ + +/*! + \internal +*/ +Global::Global() +{ +} + +/*! + Returns the unchangeable QDawg that contains general + words for the current locale. + + \sa addedDawg() +*/ +const QDawg& Global::fixedDawg() +{ + if ( !fixed_dawg ) { + if ( !docDirCreated ) + createDocDir(); + + fixed_dawg = new QDawg; + QString dawgfilename = dictDir() + "/dawg"; + QString lang = getenv( "LANG" ); + QString dawgfilename_lang = dawgfilename + "." + lang; + QString words_lang = dictDir() + "/words." + lang; + if ( QFile::exists(dawgfilename_lang) || + QFile::exists(words_lang) ) + dawgfilename = dawgfilename_lang; + QFile dawgfile(dawgfilename); + + if ( !dawgfile.exists() ) { + QString fn = dictDir() + "/words"; + if ( QFile::exists(words_lang) ) + fn = words_lang; + QFile in(fn); + if ( in.open(IO_ReadOnly) ) { + fixed_dawg->createFromWords(&in); + dawgfile.open(IO_WriteOnly); + fixed_dawg->write(&dawgfile); + dawgfile.close(); + } + } else { + fixed_dawg->readFile(dawgfilename); + } + } + + return *fixed_dawg; +} + +/*! + Returns the changeable QDawg that contains general + words for the current locale. + + \sa fixedDawg() +*/ +const QDawg& Global::addedDawg() +{ + return dawg("local"); +} + +/*! + Returns the QDawg with the given \a name. + This is an application-specific word list. + + \a name should not contain "/". +*/ +const QDawg& Global::dawg(const QString& name) +{ + createDocDir(); + if ( !named_dawg ) + named_dawg = new QDict<QDawg>; + QDawg* r = named_dawg->find(name); + if ( !r ) { + r = new QDawg; + named_dawg->insert(name,r); + QString dawgfilename = dictDir() + "/" + name + ".dawg"; + QFile dawgfile(dawgfilename); + if ( dawgfile.open(IO_ReadOnly) ) + r->readFile(dawgfilename); + } + return *r; +} + +/*! + Adds \a wordlist to the addedDawg(). +*/ +void Global::addWords(const QStringList& wordlist) +{ + addWords("local",wordlist); +} + +/*! + Adds \a wordlist to the dawg() named \a dictname. +*/ +void Global::addWords(const QString& dictname, const QStringList& wordlist) +{ + QDawg& d = (QDawg&)dawg(dictname); + QStringList all = d.allWords() + wordlist; + d.createFromWords(all); + + QString dawgfilename = dictDir() + "/" + dictname + ".dawg"; + QFile dawgfile(dawgfilename); + if ( dawgfile.open(IO_WriteOnly) ) { + d.write(&dawgfile); + dawgfile.close(); + } + + // #### Re-read the dawg here if we use mmap(). + + // #### Signal other processes to re-read. +} + + +/*! + Returns a full path for the application named \a appname, with the + given \a filename or QString::null if there was a problem creating + the directory tree for \a appname. + If \a filename contains "/", it is the caller's responsibility to + ensure those directories exist. +*/ +QString Global::applicationFileName(const QString& appname, const QString& filename) +{ + QDir d; + QString r = getenv("HOME"); + r += "/Applications/"; + if ( !QFile::exists( r ) ) + if ( d.mkdir(r) == false ) + return QString::null; + r += appname; + if ( !QFile::exists( r ) ) + if ( d.mkdir(r) == false ) + return QString::null; + r += "/"; r += filename; + return r; +} + +/*! + \internal +*/ +void Global::createDocDir() +{ + if ( !docDirCreated ) { + docDirCreated = TRUE; + mkdir( QPEApplication::documentDir().latin1(), 0755 ); + } +} + + +/*! + Displays a status \a message to the user. This generally appears + in the taskbar for some amount of time, then disappears. +*/ +void Global::statusMessage(const QString& message) +{ +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + QCopEnvelope e( "QPE/TaskBar", "message(QString)" ); + e << message; +#endif +} + +/*! + \internal +*/ +void Global::applyStyle() +{ +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + QCopChannel::send( "QPE/System", "applyStyle()" ); +#else + ((QPEApplication *)qApp)->applyStyle(); // apply without needing QCop for floppy version +#endif +} + +/*! + \internal +*/ +QWidget *Global::shutdown( bool ) +{ +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + QCopChannel::send( "QPE/System", "shutdown()" ); +#endif + return 0; +} + +/*! + \internal +*/ +QWidget *Global::restart( bool ) +{ +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + QCopChannel::send( "QPE/System", "restart()" ); +#endif + return 0; +} + +/*! + Explicitly show the current input method. +*/ +void Global::showInputMethod() +{ +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + QCopChannel::send( "QPE/TaskBar", "showInputMethod()" ); +#endif +} + +/*! + Explicitly hide the current input method. +*/ +void Global::hideInputMethod() +{ +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + QCopChannel::send( "QPE/TaskBar", "hideInputMethod()" ); +#endif +} + + +/*! + \internal +*/ +bool Global::isBuiltinCommand( const QString &name ) +{ + if(!builtin) + return FALSE; // yes, it can happen + for (int i = 0; builtin[i].file; i++) { + if ( builtin[i].file == name ) { + return TRUE; + } + } + return FALSE; +} + +Global::Command* Global::builtin=0; +QGuardedPtr<QWidget> *Global::running=0; + +/*! + \class Global::Command + \brief The Global::Command class is internal. + \internal +*/ + +/*! + \internal +*/ +void Global::setBuiltinCommands( Command* list ) +{ + if ( running ) + delete [] running; + + builtin = list; + int count = 0; + if (!builtin) + return; + while ( builtin[count].file ) + count++; + + running = new QGuardedPtr<QWidget> [ count ]; +} + +/*! + \internal +*/ +void Global::setDocument( QWidget* receiver, const QString& document ) +{ + Emitter emitter(receiver,document); +} + +/*! + \internal +*/ +bool Global::terminateBuiltin( const QString& n ) +{ + if (!builtin) + return FALSE; + for (int i = 0; builtin[i].file; i++) { + if ( builtin[i].file == n ) { + delete running[i]; + return TRUE; + } + } + return FALSE; +} + +/*! + \internal +*/ +void Global::terminate( const AppLnk* app ) +{ + //if ( terminateBuiltin(app->exec()) ) return; // maybe? haven't tried this + + QCString channel = "QPE/Application/" + app->exec().utf8(); + if ( QCopChannel::isRegistered(channel) ) { + QCopEnvelope e(channel, "quit()"); + } +} + +/*! + Low-level function to run command \a c. Not recommended. +*/ +void Global::invoke(const QString &c) +{ + // Convert the command line in to a list of arguments + QStringList list = QStringList::split(QRegExp(" *"),c); + +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + QString ap=list[0]; + // see if the application is already running + // XXX should lock file /tmp/qcop-msg-ap + if ( QCopChannel::isRegistered( ("QPE/Application/" + ap).latin1() ) ) { + QCopEnvelope e("QPE/System", "notBusy(QString)" ); + e << ap; + return; + } + // XXX should unlock file /tmp/qcop-msg-ap + //see if it is being started + if ( StartingAppList::isStarting( ap ) ) { + QCopEnvelope e("QPE/System", "notBusy(QString)" ); + e << ap; + return; + } + +#endif + +#ifdef QT_NO_QWS_MULTIPROCESS + QMessageBox::warning( 0, "Error", "Could not find the application " + c, "Ok", 0, 0, 0, 1 ); +#else + + QStrList slist; + unsigned int j; + for ( j = 0; j < list.count(); j++ ) + slist.append( list[j].utf8() ); + + const char **args = new (const char *)[slist.count() + 1]; + for ( j = 0; j < slist.count(); j++ ) + args[j] = slist.at(j); + args[j] = NULL; + +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + // an attempt to show a wait... + // more logic should be used, but this will be fine for the moment... + QCopEnvelope ( "QPE/System", "busy()" ); +#endif + +#ifdef HAVE_QUICKEXEC + QString libexe = qpeDir()+"/binlib/lib"+args[0] + ".so"; + qDebug("libfile = %s", libexe.latin1() ); + if ( QFile::exists( libexe ) ) { + qDebug("calling quickexec %s", libexe.latin1() ); + quickexecv( libexe.utf8().data(), (const char **)args ); + } else +#endif + { + if ( !::vfork() ) { + for ( int fd = 3; fd < 100; fd++ ) + ::close( fd ); + ::setpgid( ::getpid(), ::getppid() ); + // Try bindir first, so that foo/bar works too + ::execv( qpeDir()+"/bin/"+args[0], (char * const *)args ); + ::execvp( args[0], (char * const *)args ); + exit( -1 ); + } + } + StartingAppList::add( list[0] ); +#endif //QT_NO_QWS_MULTIPROCESS +} + +/*! + Executes application identfied by \a c, passing \a document. + + Note that you might be better off sending a QCop message to + the application's QPE/Application/<i>appname</i> channel. +*/ +void Global::execute( const QString &c, const QString& document ) +{ + if ( qApp->type() != QApplication::GuiServer ) { + // ask the server to do the work +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + if ( document.isNull() ) { + QCopEnvelope e( "QPE/System", "execute(QString)" ); + e << c; + } else { + QCopEnvelope e( "QPE/System", "execute(QString,QString)" ); + e << c << document; + } +#endif + return; + } + + // Attempt to execute the app using a builtin class for the app first + // else try and find it in the bin directory + if (builtin) { + for (int i = 0; builtin[i].file; i++) { + if ( builtin[i].file == c ) { + if ( running[i] ) { + if ( !document.isNull() && builtin[i].documentary ) + setDocument(running[i], document); + running[i]->raise(); + running[i]->show(); + running[i]->setActiveWindow(); + } else { + running[i] = builtin[i].func( builtin[i].maximized ); + } + QCopEnvelope e("QPE/System", "notBusy(QString)" ); + e << c; // that was quick ;-) + return; + } + } + } + + //Global::invoke(c, document); + + // Convert the command line in to a list of arguments + QStringList list = QStringList::split(QRegExp(" *"),c); + +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + QString ap=list[0]; + + qDebug("executing %s", ap.latin1() ); + if ( ap == "suspend" ) { + QWSServer::sendKeyEvent( 0xffff, Qt::Key_F34, FALSE, TRUE, FALSE ); + return; + } + + /* if need be, sending a qcop message will result in an invoke, see + preceeding function */ + { QCopEnvelope env( ("QPE/Application/" + ap).latin1(), "raise()" ); } + if ( !document.isEmpty() ) { + QCopEnvelope env( ("QPE/Application/" + ap).latin1(), "setDocument(QString)" ); + env << document; + } +#endif +} + +/*! + Returns the string \a s with the characters backslash, ", and $ + quoted by a preceeding backslash. +*/ +QString Global::shellQuote(const QString& s) +{ + QString r="\""; + for (int i=0; i<(int)s.length(); i++) { + char c = s[i].latin1(); + switch (c) { + case '\\': case '"': case '$': + r+="\\"; + } + r += s[i]; + } + r += "\""; + return r; +} + +/*! + Returns the string \a s with the characters backslash and " + quoted by a preceeding backslash. +*/ +QString Global::stringQuote(const QString& s) +{ + QString r="\""; + for (int i=0; i<(int)s.length(); i++) { + char c = s[i].latin1(); + switch (c) { + case '\\': case '"': + r+="\\"; + } + r += s[i]; + } + r += "\""; + return r; +} + +/*! + Finds all documents on the system's document directories which + match the filter \a mimefilter, and appends the resulting DocLnk + objects to \a folder. +*/ +void Global::findDocuments(DocLnkSet* folder, const QString &mimefilter) +{ + QString homedocs = QString(getenv("HOME")) + "/Documents"; + DocLnkSet d(homedocs,mimefilter); + folder->appendFrom(d); + StorageInfo storage; + const QList<FileSystem> &fs = storage.fileSystems(); + QListIterator<FileSystem> it ( fs ); + for ( ; it.current(); ++it ) { + if ( (*it)->isRemovable() ) { + QString path = (*it)->path(); + DocLnkSet ide( path, mimefilter ); + folder->appendFrom(ide); + } + } +} + + + +#include "global.moc" |