summaryrefslogtreecommitdiff
path: root/library
Side-by-side diff
Diffstat (limited to 'library') (more/less context) (show whitespace changes)
-rw-r--r--library/global.cpp49
-rw-r--r--library/qpeapplication.cpp38
2 files changed, 56 insertions, 31 deletions
diff --git a/library/global.cpp b/library/global.cpp
index fd7579a..dd15eb7 100644
--- a/library/global.cpp
+++ b/library/global.cpp
@@ -1,274 +1,281 @@
/**********************************************************************
** 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.
**
**********************************************************************/
+#define QTOPIA_INTERNAL_LANGLIST
#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("OPIEDIR");
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 words_lang;
+ QStringList langs = Global::languageList();
+ for (QStringList::ConstIterator it = langs.begin(); it!=langs.end(); ++it) {
+ QString lang = *it;
+ words_lang = dictDir() + "/words." + lang;
QString dawgfilename_lang = dawgfilename + "." + lang;
- QString words_lang = dictDir() + "/words." + lang;
if ( QFile::exists(dawgfilename_lang) ||
- QFile::exists(words_lang) )
+ QFile::exists(words_lang) ) {
dawgfilename = dawgfilename_lang;
+ break;
+ }
+ }
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");
@@ -546,99 +553,135 @@ void Global::execute( const QString &c, const QString& document )
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);
}
}
}
+QStringList Global::languageList()
+{
+ QString lang = getenv("LANG");
+ QStringList langs;
+ langs.append(lang);
+ int i = lang.find(".");
+ if ( i > 0 )
+ lang = lang.left( i );
+ i = lang.find( "_" );
+ if ( i > 0 )
+ langs.append(lang.left(i));
+ return langs;
+}
+
+QStringList Global::helpPath()
+{
+ QStringList path;
+ QStringList langs = Global::languageList();
+ for (QStringList::ConstIterator it = langs.fromLast(); it!=langs.end(); --it) {
+ QString lang = *it;
+ if ( !lang.isEmpty() )
+ path += QPEApplication::qpeDir() + "/help/" + lang + "/html";
+ }
+ path += QPEApplication::qpeDir() + "/pics";
+ path += QPEApplication::qpeDir() + "/help/en/html";
+ path += QPEApplication::qpeDir() + "/docs";
+ QString dir = QDir::current().canonicalPath();
+ if ( dir == "/" )
+ dir += "/docs";
+ else {
+ path += dir + "/../pics";
+ dir += "/../docs";
+ path += dir;
+ }
+ return path;
+}
#include "global.moc"
diff --git a/library/qpeapplication.cpp b/library/qpeapplication.cpp
index 70ffec1..4dbfbd8 100644
--- a/library/qpeapplication.cpp
+++ b/library/qpeapplication.cpp
@@ -1,251 +1,237 @@
/**********************************************************************
** 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.
**
** $Id$
**
**********************************************************************/
+#define QTOPIA_INTERNAL_LANGLIST
#include <stdlib.h>
#include <unistd.h>
#include <qfile.h>
#ifdef Q_WS_QWS
#ifndef QT_NO_COP
#if QT_VERSION <= 231
#define private public
#define sendLocally processEvent
#include "qcopenvelope_qws.h"
#undef private
#else
#include "qcopenvelope_qws.h"
#endif
#endif
#include <qwindowsystem_qws.h>
#endif
#include <qtextstream.h>
#include <qpalette.h>
#include <qbuffer.h>
#include <qptrdict.h>
#include <qregexp.h>
#include <qdir.h>
#include <qlabel.h>
#include <qdialog.h>
#include <qdragobject.h>
#include <qevent.h>
#include <qtooltip.h>
#include <qsignal.h>
#include "qpeapplication.h"
#include "qpestyle.h"
#if QT_VERSION >= 300
#include <qstylefactory.h>
#else
#include <qplatinumstyle.h>
#include <qwindowsstyle.h>
#include <qmotifstyle.h>
#include <qmotifplusstyle.h>
#include "lightstyle.h"
#endif
#include "global.h"
#include "resource.h"
#if QT_VERSION <= 230 && defined(QT_NO_CODECS)
#include "qutfcodec.h"
#endif
#include "config.h"
#include "network.h"
#include "fontmanager.h"
#include "power.h"
#include "alarmserver.h"
#include "applnk.h"
#include "qpemenubar.h"
#include <unistd.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
// for setBacklight()
#if defined(QT_QWS_IPAQ) || defined(QT_QWS_EBX)
#include <linux/fb.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <stdlib.h>
class QPEApplicationData {
public:
QPEApplicationData() : presstimer(0), presswidget(0), rightpressed(FALSE),
kbgrabber(0), kbregrab(FALSE), notbusysent(FALSE), preloaded(FALSE),
forceshow(FALSE), nomaximize(FALSE), qpe_main_widget(0),
keep_running(TRUE)
{
qcopq.setAutoDelete(TRUE);
}
int presstimer;
QWidget* presswidget;
QPoint presspos;
bool rightpressed;
int kbgrabber;
bool kbregrab;
bool notbusysent;
QString appName;
struct QCopRec {
QCopRec(const QCString &ch, const QCString &msg,
const QByteArray &d) :
channel(ch), message(msg), data(d) { }
QCString channel;
QCString message;
QByteArray data;
};
bool preloaded;
bool forceshow;
bool nomaximize;
QWidget* qpe_main_widget;
bool keep_running;
QList<QCopRec> qcopq;
void enqueueQCop(const QCString &ch, const QCString &msg,
const QByteArray &data)
{
qcopq.append(new QCopRec(ch,msg,data));
}
void sendQCopQ()
{
QCopRec* r;
for (QListIterator<QCopRec> it(qcopq); (r=it.current()); ++it)
QCopChannel::sendLocally(r->channel,r->message,r->data);
qcopq.clear();
}
};
class ResourceMimeFactory : public QMimeSourceFactory {
public:
ResourceMimeFactory()
{
- QStringList path;
- QString lang = getenv("LANG");
- if ( !lang.isEmpty() )
- path += QPEApplication::qpeDir() + "/help/" + lang + "/html";
- path += QPEApplication::qpeDir() + "/pics";
- path += QPEApplication::qpeDir() + "/help/en/html";
- path += QPEApplication::qpeDir() + "/docs";
- QString dir = QDir::current().canonicalPath();
- if ( dir == "/" )
- dir += "/docs";
- else {
- path += dir + "/../pics";
- dir += "/../docs";
- path += dir;
- }
- setFilePath( path );
+ setFilePath( Global::helpPath() );
setExtensionType("html","text/html;charset=UTF-8");
}
const QMimeSource* data(const QString& abs_name) const
{
const QMimeSource* r = QMimeSourceFactory::data(abs_name);
if ( !r ) {
int sl = abs_name.length();
do {
sl = abs_name.findRev('/',sl-1);
QString name = sl>=0 ? abs_name.mid(sl+1) : abs_name;
int dot = name.findRev('.');
if ( dot >= 0 )
name = name.left(dot);
QImage img = Resource::loadImage(name);
if ( !img.isNull() )
r = new QImageDrag(img);
} while (!r && sl>0);
}
return r;
}
};
static int muted=0;
static void setVolume(int t=0, int percent=-1)
{
switch (t) {
case 0: {
Config cfg("Sound");
cfg.setGroup("System");
if ( percent < 0 )
percent = cfg.readNumEntry("Volume",50);
int fd = 0;
if ((fd = open("/dev/mixer", O_RDWR))>=0) {
int vol = muted ? 0 : percent;
// set both channels to same volume
vol |= vol << 8;
ioctl(fd, MIXER_WRITE(0), &vol);
::close(fd);
}
} break;
}
}
int qpe_sysBrightnessSteps()
{
#if defined(QT_QWS_IPAQ)
return 255;
#elif defined(QT_QWS_EBX)
return 4;
#else
return 255; // ?
#endif
}
static int& hack(int& i)
{
#if QT_VERSION <= 230 && defined(QT_NO_CODECS)
// These should be created, but aren't in Qt 2.3.0
(void)new QUtf8Codec;
(void)new QUtf16Codec;
#endif
return i;
}
static bool forced_off = FALSE;
static int curbl=-1;
static int backlight()
{
if ( curbl == -1 ) {
// Read from config
Config config( "qpe" );
config.setGroup( "Screensaver" );
curbl = config.readNumEntry("Brightness",255);
}
return curbl;
}
static void setBacklight(int bright)
{
if ( bright == -3 ) {
// Forced on
forced_off = FALSE;
bright = -1;
}
if ( forced_off && bright != -2 )
return;
if ( bright == -2 ) {
// Toggle between off and on
bright = curbl ? 0 : -1;
forced_off = !bright;
}
if ( bright == -1 ) {
@@ -444,212 +430,208 @@ static void setScreenSaverInterval(int interval)
\code
void MyWidget::receive( const QCString& msg, const QByteArray& data )
{
QDataStream stream( data, IO_ReadOnly );
if ( msg == "someMessage(int,int,int)" ) {
int a,b,c;
stream >> a >> b >> c;
...
} else if ( msg == "otherMessage(QString)" ) {
...
}
}
\endcode
\sa qcop.html
*/
/*!
Constructs a QPEApplication just as you would construct
a QApplication, passing \a argc, \a argv, and \a t.
*/
QPEApplication::QPEApplication( int& argc, char **argv, Type t )
: QApplication( hack(argc), argv, t )
{
int dw = desktop()->width();
if ( dw < 200 ) {
setFont( QFont( "helvetica", 8 ) );
AppLnk::setSmallIconSize(10);
AppLnk::setBigIconSize(28);
}
d = new QPEApplicationData;
QMimeSourceFactory::setDefaultFactory(new ResourceMimeFactory);
connect(this, SIGNAL(lastWindowClosed()), this, SLOT(hideOrQuit()));
#if defined(Q_WS_QWS) && !defined(QT_NO_COP)
QString qcopfn("/tmp/qcop-msg-");
qcopfn += QString(argv[0]); // append command name
QFile f(qcopfn);
if ( f.open(IO_ReadOnly) ) {
flock(f.handle(), LOCK_EX);
}
sysChannel = new QCopChannel( "QPE/System", this );
connect( sysChannel, SIGNAL(received(const QCString &, const QByteArray &)),
this, SLOT(systemMessage( const QCString &, const QByteArray &)) );
QCString channel = QCString(argv[0]);
channel.replace(QRegExp(".*/"),"");
d->appName = channel;
channel = "QPE/Application/" + channel;
pidChannel = new QCopChannel( channel, this);
connect( pidChannel, SIGNAL(received(const QCString &, const QByteArray &)),
this, SLOT(pidMessage(const QCString &, const QByteArray &)));
if ( f.isOpen() ) {
d->keep_running = FALSE;
QDataStream ds(&f);
QCString channel, message;
QByteArray data;
while(!ds.atEnd()) {
ds >> channel >> message >> data;
d->enqueueQCop(channel,message,data);
}
flock(f.handle(), LOCK_UN);
f.close();
f.remove();
}
for (int a=0; a<argc; a++) {
if ( qstrcmp(argv[a],"-preload")==0 ) {
argv[a] = argv[a+1];
a++;
d->preloaded = TRUE;
argc-=1;
} else if ( qstrcmp(argv[a],"-preload-show")==0 ) {
argv[a] = argv[a+1];
a++;
d->preloaded = TRUE;
d->forceshow = TRUE;
argc-=1;
}
}
/* overide stored arguments */
setArgs(argc, argv);
#endif
qwsSetDecoration( new QPEDecoration() );
#ifndef QT_NO_TRANSLATION
- char *l = getenv( "LANG" );
- QString lang;
- if ( l ) {
- lang = l;
+ QStringList langs = Global::languageList();
+ for (QStringList::ConstIterator it = langs.begin(); it!=langs.end(); ++it) {
+ QString lang = *it;
- /*
- Config config("qpe");
- config.setGroup( "Appearance" );
- lang = config.readEntry( "Language", lang );
- */
+ QTranslator * trans;
+ QString tfn;
- QTranslator * trans = new QTranslator(this);
- QString tfn = qpeDir()+"/i18n/"+lang+"/"+d->appName+".qm";
+ trans = new QTranslator(this);
+ tfn = qpeDir()+"/i18n/"+lang+"/libqpe.qm";
if ( trans->load( tfn ))
installTranslator( trans );
else
delete trans;
trans = new QTranslator(this);
- tfn = qpeDir()+"/i18n/"+lang+"/libqpe.qm";
+ tfn = qpeDir()+"/i18n/"+lang+"/"+d->appName+".qm";
if ( trans->load( tfn ))
installTranslator( trans );
else
delete trans;
//###language/font hack; should look it up somewhere
if ( lang == "ja" || lang == "zh_CN" || lang == "zh_TW" || lang == "ko" ) {
QFont fn = FontManager::unicodeFont( FontManager::Proportional );
setFont( fn );
}
}
#endif
applyStyle();
if ( type() == GuiServer ) {
setScreenSaverInterval(-1);
setVolume();
QWSServer::setScreenSaver(new QPEScreenSaver);
}
installEventFilter( this );
QPEMenuToolFocusManager::initialize();
#ifdef QT_NO_QWS_CURSOR
// if we have no cursor, probably don't want tooltips
QToolTip::setEnabled( FALSE );
#endif
}
static QPtrDict<void>* inputMethodDict=0;
static void createInputMethodDict()
{
if ( !inputMethodDict )
inputMethodDict = new QPtrDict<void>;
}
/*!
Returns the currently set hint to the system as to whether
\a w has any use for text input methods.
\sa setInputMethodHint()
*/
QPEApplication::InputMethodHint QPEApplication::inputMethodHint( QWidget* w )
{
if ( inputMethodDict && w )
return (InputMethodHint)(int)inputMethodDict->find(w);
return Normal;
}
/*!
\enum QPEApplication::InputMethodHint
\value Normal the application sometimes needs text input (the default).
\value AlwaysOff the application never needs text input.
\value AlwaysOn the application always needs text input.
*/
/*!
Hints to the system that \a w has use for text input methods
as specified by \a mode.
\sa inputMethodHint()
*/
void QPEApplication::setInputMethodHint( QWidget* w, InputMethodHint mode )
{
createInputMethodDict();
if ( mode == Normal ) {
inputMethodDict->remove(w);
} else {
inputMethodDict->insert(w,(void*)mode);
}
}
class HackDialog : public QDialog
{
public:
void acceptIt() { accept(); }
void rejectIt() { reject(); }
};
void QPEApplication::mapToDefaultAction( QWSKeyEvent *ke, int key )
{
// specialised actions for certain widgets. May want to
// add more stuff here.
if ( activePopupWidget() && activePopupWidget()->inherits( "QListBox" )
&& activePopupWidget()->parentWidget()
&& activePopupWidget()->parentWidget()->inherits( "QComboBox" ) )
key = Qt::Key_Return;
if ( activePopupWidget() && activePopupWidget()->inherits( "QPopupMenu" ) )
key = Qt::Key_Return;
ke->simpleData.keycode = key;