From 96900dbce3aaa1fcac74a07a71482c5c6fcd3cab Mon Sep 17 00:00:00 2001 From: tpearson Date: Thu, 2 Sep 2010 21:21:15 +0000 Subject: [PATCH] * Large set of SuSE patches to fix bugs and add functionality * kdemm is included but not used by knotify as it does not work out of the box git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1171141 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- Makefile.am.in | 2 +- catalog.xml | 39 ++ dcop/KDE-ICE/Xtranssock.c | 5 + dcop/dcopc.c | 4 +- dcop/dcopclient.cpp | 4 +- dcop/dcopserver_shutdown.c | 4 +- interfaces/kimproxy/library/kimproxy.cpp | 2 +- .../kfileaudiopreview/kfileaudiopreview.cpp | 4 +- kate/part/katehighlight.cpp | 6 +- kdecore/eventsrc | 2 +- kdecore/fakes.c | 2 + kdecore/kapplication.cpp | 20 +- kdecore/kcatalogue.cpp | 6 +- kdecore/kconfigbase.cpp | 18 + kdecore/kconfigbase.h | 1 + kdecore/kcrash.cpp | 4 +- kdecore/kdesktopfile.cpp | 31 +- kdecore/kdesktopfile.h | 2 + kdecore/klocale.cpp | 9 +- kdecore/kstandarddirs.cpp | 17 +- kdecore/kwinmodule.cpp | 4 +- kded/Makefile.am | 6 +- kded/khostname.cpp | 28 +- kded/kmimelist.cpp | 54 ++ kdefx/kstyle.cpp | 75 ++- kdemm/0.png | Bin 0 -> 21253 bytes kdemm/DESIGN | 31 + kdemm/Makefile.am | 43 ++ kdemm/TODO | 27 + kdemm/backend.cpp | 78 +++ kdemm/backend.h | 86 +++ kdemm/channel.cpp | 69 +++ kdemm/channel.h | 96 ++++ kdemm/factory.cpp | 296 ++++++++++ kdemm/factory.h | 222 ++++++++ kdemm/mixeriface.h | 41 ++ kdemm/mmbackendinfo.desktop | 30 + kdemm/player.cpp | 53 ++ kdemm/player.h | 259 +++++++++ kdemm/reqspec.html | 533 ++++++++++++++++++ kdemm/simpleplayer.cpp | 198 +++++++ kdemm/simpleplayer.h | 72 +++ kdemm/test/Makefile.am | 30 + kdemm/test/main.cpp | 38 ++ kdemm/test/statetest.cpp | 299 ++++++++++ kdemm/test/statetest.h | 57 ++ kdemm/test/testwidget.cpp | 194 +++++++ kdemm/test/testwidget.h | 64 +++ kdemm/videoplayer.cpp | 34 ++ kdemm/videoplayer.h | 54 ++ kdemm/volumecontrold/Makefile.am | 31 + kdemm/volumecontrold/volumecontrold.cpp | 47 ++ kdemm/volumecontrold/volumecontrold.desktop | 13 + kdemm/volumecontrold/volumecontrold.h | 53 ++ kdeprint/cups/cupsdconf2/cupsddialog.cpp | 1 + kdeprint/cups/kmcupsmanager.cpp | 2 +- kdeprint/kmvirtualmanager.cpp | 10 +- kdeprint/kprintpreview.cpp | 3 + kdeprint/management/smbview.cpp | 26 +- kdesu/defaults.h | 3 +- kdeui/Makefile.am | 2 +- kdeui/kaction.cpp | 11 + kdeui/kactionclasses.cpp | 23 +- kdeui/kcombobox.cpp | 115 +++- kdeui/kcombobox.h | 46 ++ kdeui/kfontcombo.cpp | 29 + kdeui/kjanuswidget.cpp | 121 +++- kdeui/kjanuswidget.h | 9 + kdeui/kpassdlg.cpp | 5 +- kdeui/ksconfig.cpp | 2 +- kdeui/ksyntaxhighlighter.cpp | 2 +- kdeui/ktip.cpp | 31 +- kdeui/qxembed.cpp | 24 +- kdeui/qxembed.h | 5 + kdoctools/kio_help.cpp | 2 +- khtml/ecma/xmlhttprequest.cpp | 22 +- khtml/khtmlview.cpp | 164 +++++- khtml/khtmlview.h | 20 + khtml/rendering/render_form.cpp | 18 +- khtml/rendering/render_form.h | 4 +- kinit/autostart.cpp | 15 +- kinit/lnusertemp.c | 25 +- kinit/wrapper.c | 4 +- kio/kfile/kfiledialog.cpp | 6 + kio/kfile/kfilesharedlg.cpp | 59 +- kio/kfile/kfilesharedlg.h | 3 + kio/kfile/kurlbar.cpp | 13 +- kio/kio/kdirwatch.cpp | 3 +- kio/kio/kfileshare.cpp | 74 ++- kio/kio/kfileshare.h | 24 +- kio/kio/kmimetype.cpp | 10 +- kio/kio/kremoteencoding.cpp | 6 +- kio/kio/kservice.cpp | 15 +- kio/kio/kservicegroup.cpp | 10 +- kio/kio/netaccess.cpp | 2 +- kio/kssl/kopenssl.cc | 22 +- kio/kssl/kopenssl.h | 16 +- kio/kssl/ksmimecrypto.cc | 2 +- kio/kssl/ksslcertificate.cc | 32 +- kio/misc/kwalletd/kwalletd.cpp | 38 ++ kio/misc/kwalletd/kwalletd.h | 6 +- kioslave/file/Makefile.am | 2 +- kioslave/file/file.cc | 26 + kioslave/ftp/ftp.cc | 23 + kioslave/ftp/ftp.h | 5 + kresources/kresources.desktop | 2 +- mimetypes/application/ogg.desktop | 2 +- .../vnd.oasis.opendocument.formula.desktop | 2 +- .../application/vnd.sun.xml.base.desktop | 2 +- mimetypes/application/x-bittorrent.desktop | 1 + mimetypes/application/x-msdos-program.desktop | 2 +- mimetypes/application/x-ogg.desktop | 2 +- mimetypes/application/x-rpm.desktop | 1 + mimetypes/text/rtf.desktop | 2 +- pics/crystalsvg/cr128-mime-database.png | Bin 0 -> 6866 bytes pics/crystalsvg/cr128-mime-drawing.png | Bin 0 -> 6595 bytes pics/crystalsvg/cr128-mime-presentation.png | Bin 0 -> 5779 bytes pics/crystalsvg/cr128-mime-rtf.png | Bin 0 -> 6096 bytes pics/crystalsvg/cr16-mime-database.png | Bin 0 -> 676 bytes pics/crystalsvg/cr16-mime-drawing.png | Bin 0 -> 752 bytes pics/crystalsvg/cr16-mime-presentation.png | Bin 0 -> 697 bytes pics/crystalsvg/cr16-mime-rtf.png | Bin 0 -> 750 bytes pics/crystalsvg/cr22-mime-database.png | Bin 0 -> 964 bytes pics/crystalsvg/cr22-mime-drawing.png | Bin 0 -> 1032 bytes pics/crystalsvg/cr22-mime-presentation.png | Bin 0 -> 936 bytes pics/crystalsvg/cr22-mime-rtf.png | Bin 0 -> 996 bytes pics/crystalsvg/cr32-mime-database.png | Bin 0 -> 1461 bytes pics/crystalsvg/cr32-mime-drawing.png | Bin 0 -> 1543 bytes pics/crystalsvg/cr32-mime-presentation.png | Bin 0 -> 1394 bytes pics/crystalsvg/cr32-mime-rtf.png | Bin 0 -> 1492 bytes pics/crystalsvg/cr48-mime-database.png | Bin 0 -> 2355 bytes pics/crystalsvg/cr48-mime-drawing.png | Bin 0 -> 2413 bytes pics/crystalsvg/cr48-mime-presentation.png | Bin 0 -> 2192 bytes pics/crystalsvg/cr48-mime-rtf.png | Bin 0 -> 2308 bytes pics/crystalsvg/cr64-mime-database.png | Bin 0 -> 3187 bytes pics/crystalsvg/cr64-mime-drawing.png | Bin 0 -> 3151 bytes pics/crystalsvg/cr64-mime-presentation.png | Bin 0 -> 2755 bytes pics/crystalsvg/cr64-mime-rtf.png | Bin 0 -> 3035 bytes pics/crystalsvg/crsc-mime-database.svgz | Bin 0 -> 6573 bytes pics/crystalsvg/crsc-mime-drawing.svgz | Bin 0 -> 7739 bytes pics/crystalsvg/crsc-mime-presentation.svgz | Bin 0 -> 6035 bytes pics/crystalsvg/crsc-mime-rtf.svgz | Bin 0 -> 5269 bytes 142 files changed, 4366 insertions(+), 188 deletions(-) create mode 100644 catalog.xml create mode 100644 kded/kmimelist.cpp create mode 100644 kdemm/0.png create mode 100644 kdemm/DESIGN create mode 100644 kdemm/Makefile.am create mode 100644 kdemm/TODO create mode 100644 kdemm/backend.cpp create mode 100644 kdemm/backend.h create mode 100644 kdemm/channel.cpp create mode 100644 kdemm/channel.h create mode 100644 kdemm/factory.cpp create mode 100644 kdemm/factory.h create mode 100644 kdemm/mixeriface.h create mode 100644 kdemm/mmbackendinfo.desktop create mode 100644 kdemm/player.cpp create mode 100644 kdemm/player.h create mode 100644 kdemm/reqspec.html create mode 100644 kdemm/simpleplayer.cpp create mode 100644 kdemm/simpleplayer.h create mode 100644 kdemm/test/Makefile.am create mode 100644 kdemm/test/main.cpp create mode 100644 kdemm/test/statetest.cpp create mode 100644 kdemm/test/statetest.h create mode 100644 kdemm/test/testwidget.cpp create mode 100644 kdemm/test/testwidget.h create mode 100644 kdemm/videoplayer.cpp create mode 100644 kdemm/videoplayer.h create mode 100644 kdemm/volumecontrold/Makefile.am create mode 100644 kdemm/volumecontrold/volumecontrold.cpp create mode 100644 kdemm/volumecontrold/volumecontrold.desktop create mode 100644 kdemm/volumecontrold/volumecontrold.h create mode 100644 pics/crystalsvg/cr128-mime-database.png create mode 100644 pics/crystalsvg/cr128-mime-drawing.png create mode 100644 pics/crystalsvg/cr128-mime-presentation.png create mode 100644 pics/crystalsvg/cr128-mime-rtf.png create mode 100644 pics/crystalsvg/cr16-mime-database.png create mode 100644 pics/crystalsvg/cr16-mime-drawing.png create mode 100644 pics/crystalsvg/cr16-mime-presentation.png create mode 100644 pics/crystalsvg/cr16-mime-rtf.png create mode 100644 pics/crystalsvg/cr22-mime-database.png create mode 100644 pics/crystalsvg/cr22-mime-drawing.png create mode 100644 pics/crystalsvg/cr22-mime-presentation.png create mode 100644 pics/crystalsvg/cr22-mime-rtf.png create mode 100644 pics/crystalsvg/cr32-mime-database.png create mode 100644 pics/crystalsvg/cr32-mime-drawing.png create mode 100644 pics/crystalsvg/cr32-mime-presentation.png create mode 100644 pics/crystalsvg/cr32-mime-rtf.png create mode 100644 pics/crystalsvg/cr48-mime-database.png create mode 100644 pics/crystalsvg/cr48-mime-drawing.png create mode 100644 pics/crystalsvg/cr48-mime-presentation.png create mode 100644 pics/crystalsvg/cr48-mime-rtf.png create mode 100644 pics/crystalsvg/cr64-mime-database.png create mode 100644 pics/crystalsvg/cr64-mime-drawing.png create mode 100644 pics/crystalsvg/cr64-mime-presentation.png create mode 100644 pics/crystalsvg/cr64-mime-rtf.png create mode 100644 pics/crystalsvg/crsc-mime-database.svgz create mode 100644 pics/crystalsvg/crsc-mime-drawing.svgz create mode 100644 pics/crystalsvg/crsc-mime-presentation.svgz create mode 100644 pics/crystalsvg/crsc-mime-rtf.svgz diff --git a/Makefile.am.in b/Makefile.am.in index ba1149e5e..6ce0076f2 100644 --- a/Makefile.am.in +++ b/Makefile.am.in @@ -18,7 +18,7 @@ # Boston, MA 02110-1301, USA. -COMPILE_FIRST = dcop libltdl kdefx kdecore kunittest kdeui kdesu kjs kwallet kio kded kded_post +COMPILE_FIRST = dcop libltdl kdefx kdecore kunittest kdeui kdesu kjs kwallet kio kded kded_post kdemm COMPILE_BEFORE_doc = kdoctools COMPILE_AFTER_kparts = kspell2 kmdi kdeprint kinit kate interfaces kcert khtml krandr COMPILE_AFTER_kdeprint = kate khtml diff --git a/catalog.xml b/catalog.xml new file mode 100644 index 000000000..3f5dd5b15 --- /dev/null +++ b/catalog.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dcop/KDE-ICE/Xtranssock.c b/dcop/KDE-ICE/Xtranssock.c index 100d218b1..7b14ca9ab 100644 --- a/dcop/KDE-ICE/Xtranssock.c +++ b/dcop/KDE-ICE/Xtranssock.c @@ -1444,6 +1444,7 @@ UnixHostReallyLocal (char *host) { char hostnamebuf[256]; + char* xauthlocalname = getenv("XAUTHLOCALHOSTNAME"); TRANS(GetHostname) (hostnamebuf, sizeof (hostnamebuf)); @@ -1451,6 +1452,10 @@ UnixHostReallyLocal (char *host) { return (1); } + else if(xauthlocalname && strcmp (xauthlocalname, host) == 0) + { + return (1); + } else { /* diff --git a/dcop/dcopc.c b/dcop/dcopc.c index 39083d4f5..9667eb624 100644 --- a/dcop/dcopc.c +++ b/dcop/dcopc.c @@ -684,7 +684,9 @@ dcop_connect() } hostName[0] = '\0'; - if (gethostname(hostName, sizeof(hostName))) + if (getenv("XAUTHLOCALHOSTNAME")) + strlcpy(hostName, getenv("XAUTHLOCALHOSTNAME"),sizeof(hostName)-1); + else if (gethostname(hostName, sizeof(hostName))) strcpy(hostName, "localhost"); else hostName[sizeof(hostName)-1] = '\0'; diff --git a/dcop/dcopclient.cpp b/dcop/dcopclient.cpp index e446801c9..8006ecd52 100644 --- a/dcop/dcopclient.cpp +++ b/dcop/dcopclient.cpp @@ -277,7 +277,9 @@ static TQCString dcopServerFile(const TQCString &hostname, bool old) { char hostName[256]; hostName[0] = '\0'; - if (gethostname(hostName, sizeof(hostName))) + if (getenv("XAUTHLOCALHOSTNAME")) + fName += getenv("XAUTHLOCALHOSTNAME"); + else if (gethostname(hostName, sizeof(hostName))) { fName += "localhost"; } diff --git a/dcop/dcopserver_shutdown.c b/dcop/dcopserver_shutdown.c index 46a847660..fb25a86fa 100644 --- a/dcop/dcopserver_shutdown.c +++ b/dcop/dcopserver_shutdown.c @@ -95,7 +95,9 @@ static void getDCOPFile(char *dcop_file, char *dcop_file_old, int max_length) strncat(dcop_file, "/.DCOPserver_", n); n -= strlen("/.DCOPserver_"); - if (gethostname(dcop_file+strlen(dcop_file), n) != 0) + if (getenv("XAUTHLOCALHOSTNAME")) + strncat(dcop_file+strlen(dcop_file), getenv("XAUTHLOCALHOSTNAME"), n); + else if (gethostname(dcop_file+strlen(dcop_file), n) != 0) { perror("Error. Could not determine hostname: "); dcop_file[0] = '\0'; diff --git a/interfaces/kimproxy/library/kimproxy.cpp b/interfaces/kimproxy/library/kimproxy.cpp index d3cf1ef24..44f828a02 100644 --- a/interfaces/kimproxy/library/kimproxy.cpp +++ b/interfaces/kimproxy/library/kimproxy.cpp @@ -204,7 +204,7 @@ KIMProxy::KIMProxy( DCOPClient* dc ) : DCOPObject( "KIMProxyIface" ), TQObject() // FIXME: make this work when the sender object id is set to KIMIFace if ( !connectDCOPSignal( 0, 0, method, method, false ) ) - KMessageBox::information( 0, TQString( "Couldn't connect DCOP signal.\nWon't receive any status notifications!" ) ); + kdWarning() << "Couldn't connect DCOP signal. Won't receive any status notifications!" << endl; } KIMProxy::~KIMProxy( ) diff --git a/interfaces/kmediaplayer/kfileaudiopreview/kfileaudiopreview.cpp b/interfaces/kmediaplayer/kfileaudiopreview/kfileaudiopreview.cpp index 11fa58f27..79ef97e42 100644 --- a/interfaces/kmediaplayer/kfileaudiopreview/kfileaudiopreview.cpp +++ b/interfaces/kmediaplayer/kfileaudiopreview/kfileaudiopreview.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include @@ -58,7 +58,7 @@ KFileAudioPreview::KFileAudioPreview( TQWidget *parent, const char *name ) { KGlobal::locale()->insertCatalogue("kfileaudiopreview"); - TQStringList formats = KDE::PlayObjectFactory::mimeTypes(); + TQStringList formats = KDE::Multimedia::Factory::self()->playableMimeTypes(); // ### TQStringList::ConstIterator it = formats.begin(); for ( ; it != formats.end(); ++it ) diff --git a/kate/part/katehighlight.cpp b/kate/part/katehighlight.cpp index 655452184..50c941c15 100644 --- a/kate/part/katehighlight.cpp +++ b/kate/part/katehighlight.cpp @@ -3225,7 +3225,7 @@ void KateHlManager::getDefaults(uint schema, KateAttributeList &list) list.append(charAttribute); KateAttribute* string = new KateAttribute(); - string->setTextColor(TQColor::TQColor("#D00")); + string->setTextColor(TQColor("#D00")); string->setSelectedTextColor(Qt::red); list.append(string); @@ -3242,9 +3242,9 @@ void KateHlManager::getDefaults(uint schema, KateAttributeList &list) KateAttribute* alert = new KateAttribute(); alert->setTextColor(Qt::black); - alert->setSelectedTextColor( TQColor::TQColor("#FCC") ); + alert->setSelectedTextColor( TQColor("#FCC") ); alert->setBold(true); - alert->setBGColor( TQColor::TQColor("#FCC") ); + alert->setBGColor( TQColor("#FCC") ); list.append(alert); KateAttribute* functionAttribute = new KateAttribute(); diff --git a/kdecore/eventsrc b/kdecore/eventsrc index 15b1016dc..0377e819a 100644 --- a/kdecore/eventsrc +++ b/kdecore/eventsrc @@ -2572,7 +2572,7 @@ Comment[wa]=On messaedje critike est håyné Comment[zh_CN]=正在显示关键消息 Comment[zh_HK]=顯示嚴重警告訊息 Comment[zh_TW]=嚴重的警告訊息已顯示 -default_sound=KDE_Glass_Break.ogg +default_sound=KDE_Error_1.ogg default_presentation=65 nopresentation=18 level=4 diff --git a/kdecore/fakes.c b/kdecore/fakes.c index 481eb6880..14f162eca 100644 --- a/kdecore/fakes.c +++ b/kdecore/fakes.c @@ -323,6 +323,7 @@ KDECORE_EXPORT int revoke(const char *tty) #endif #ifndef HAVE_STRLCPY +#include KDECORE_EXPORT unsigned long strlcpy(char* d, const char* s, unsigned long bufsize) { unsigned long len, ret = strlen(s); @@ -341,6 +342,7 @@ KDECORE_EXPORT unsigned long strlcpy(char* d, const char* s, unsigned long bufsi #endif #ifndef HAVE_STRLCAT +#include KDECORE_EXPORT unsigned long strlcat(char* d, const char* s, unsigned long bufsize) { char *cp; diff --git a/kdecore/kapplication.cpp b/kdecore/kapplication.cpp index 8edc1d170..3dc8ca77d 100644 --- a/kdecore/kapplication.cpp +++ b/kdecore/kapplication.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #ifndef QT_NO_SQL #include #endif @@ -87,6 +88,8 @@ #include #endif #include +#include +#include #ifndef Q_WS_WIN #include "kwin.h" @@ -776,10 +779,15 @@ void KApplication::init(bool GUIenabled) { d->guiEnabled = GUIenabled; if ((getuid() != geteuid()) || - (getgid() != getegid())) + (getgid() != getegid()) ) { - fprintf(stderr, "The KDE libraries are not designed to run with suid privileges.\n"); - ::exit(127); + // man permissions are not exploitable and better than + // world writable directories + struct group *man = getgrnam("man"); + if ( !man || man->gr_gid != getegid() ){ + fprintf(stderr, "The KDE libraries are not designed to run with suid privileges.\n"); + ::exit(127); + } } KProcessController::ref(); @@ -2137,6 +2145,12 @@ void KApplication::propagateSettings(SettingsCategory arg) KConfigBase* config = KGlobal::config(); KConfigGroupSaver saver( config, "KDE" ); +#ifdef QT_HAVE_MAX_IMAGE_SIZE + TQSize maxImageSize(4096, 4096); + maxImageSize = config->readSizeEntry("MaxImageSize", &maxImageSize); + TQImage::setMaxImageSize(maxImageSize); +#endif + int num = config->readNumEntry("CursorBlinkRate", TQApplication::cursorFlashTime()); if ((num != 0) && (num < 200)) num = 200; diff --git a/kdecore/kcatalogue.cpp b/kdecore/kcatalogue.cpp index 8bc81125e..3669b40b4 100644 --- a/kdecore/kcatalogue.cpp +++ b/kdecore/kcatalogue.cpp @@ -66,7 +66,11 @@ KCatalogue::KCatalogue(const TQString & name, const TQString & language ) .arg( d->language ) .arg( d->name ); - setFileName( locate( "locale", path ) ); + TQString fileName = locate( "locale", path ); + if (fileName.isEmpty()) + fileName = locate( "locale-bundle", path ); + + setFileName( fileName ); } diff --git a/kdecore/kconfigbase.cpp b/kdecore/kconfigbase.cpp index 56f5292ae..1cbe1f3ab 100644 --- a/kdecore/kconfigbase.cpp +++ b/kdecore/kconfigbase.cpp @@ -131,6 +131,24 @@ bool KConfigBase::hasKey(const char *pKey) const return !entry.mValue.isNull(); } +bool KConfigBase::hasTranslatedKey(const char* pKey) const +{ + KEntryKey aEntryKey(mGroup, 0); + aEntryKey.c_key = pKey; + aEntryKey.bDefault = readDefaults(); + + if (!locale().isNull()) { + // try the localized key first + aEntryKey.bLocal = true; + KEntry entry = lookupData(aEntryKey); + if (!entry.mValue.isNull()) + return true; + aEntryKey.bLocal = false; + } + + return false; +} + bool KConfigBase::hasGroup(const TQString &group) const { return internalHasGroup( group.utf8()); diff --git a/kdecore/kconfigbase.h b/kdecore/kconfigbase.h index 69c7c3bc0..cc7ad8ffe 100644 --- a/kdecore/kconfigbase.h +++ b/kdecore/kconfigbase.h @@ -1985,6 +1985,7 @@ public: protected: TQCString readEntryUtf8( const char *pKey) const; + bool hasTranslatedKey( const char *pKey ) const; /** * The currently selected group. */ diff --git a/kdecore/kcrash.cpp b/kdecore/kcrash.cpp index 2a908670b..5642500f8 100644 --- a/kdecore/kcrash.cpp +++ b/kdecore/kcrash.cpp @@ -466,7 +466,9 @@ static int openSocket() sock_file[strlen(sock_file)-1] = 0; strncat(sock_file, "/socket-", MAX_SOCK_FILE - strlen(sock_file)); - if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0) + if( getenv("XAUTHLOCALHOSTNAME")) + strncat(sock_file, getenv("XAUTHLOCALHOSTNAME"), MAX_SOCK_FILE - strlen(sock_file) - 1); + else if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0) { perror("Warning: Could not determine hostname: "); return -1; diff --git a/kdecore/kdesktopfile.cpp b/kdecore/kdesktopfile.cpp index c8a98e973..09a3f1e9b 100644 --- a/kdecore/kdesktopfile.cpp +++ b/kdecore/kdesktopfile.cpp @@ -34,6 +34,8 @@ #include "kapplication.h" #include "kstandarddirs.h" #include "kmountpoint.h" +#include "kcatalogue.h" +#include "klocale.h" #include "kdesktopfile.h" #include "kdesktopfile.moc" @@ -145,6 +147,27 @@ bool KDesktopFile::isAuthorizedDesktopFile(const TQString& path) return false; } +TQString KDesktopFile::translatedEntry(const char* key) const +{ + if (hasTranslatedKey(key)) + return readEntry(key); + + if (hasKey(key)) { + TQString value = readEntryUntranslated(key); + TQString fName = fileName(); + fName = fName.mid(fName.findRev('/')+1); + TQString po_lookup_key = TQString::fromLatin1(key) + "(" + fName + "): " + value; + TQString po_value = KGlobal::locale()->translate(po_lookup_key.utf8().data()); + + if (po_value == po_lookup_key) + return value; + + return po_value; + } + + return TQString::null; +} + TQString KDesktopFile::readType() const { return readEntry("Type"); @@ -157,17 +180,17 @@ TQString KDesktopFile::readIcon() const TQString KDesktopFile::readName() const { - return readEntry("Name"); + return translatedEntry("Name"); } TQString KDesktopFile::readComment() const { - return readEntry("Comment"); + return translatedEntry("Comment"); } TQString KDesktopFile::readGenericName() const { - return readEntry("GenericName"); + return translatedEntry("GenericName"); } TQString KDesktopFile::readPath() const @@ -342,5 +365,3 @@ KDesktopFile* KDesktopFile::copyTo(const TQString &file) const config->setDesktopGroup(); return config; } - - diff --git a/kdecore/kdesktopfile.h b/kdecore/kdesktopfile.h index dcbe3ea1d..bb705b696 100644 --- a/kdecore/kdesktopfile.h +++ b/kdecore/kdesktopfile.h @@ -236,6 +236,8 @@ private: private: + TQString translatedEntry(const char*) const; + // copy-construction and assignment are not allowed KDesktopFile( const KDesktopFile& ); KDesktopFile& operator= ( const KDesktopFile& ); diff --git a/kdecore/klocale.cpp b/kdecore/klocale.cpp index f83ec6be1..91fe1c2a1 100644 --- a/kdecore/klocale.cpp +++ b/kdecore/klocale.cpp @@ -408,7 +408,11 @@ TQString KLocale::catalogueFileName(const TQString & language, .arg( language ) .arg( catalog.name() ); - return locate( "locale", path ); + TQString fileName = locate( "locale", path ); + if (fileName.isEmpty()) + fileName = locate( "locale-bundle", path ); + + return fileName; } bool KLocale::setLanguage(const TQString & language) @@ -507,6 +511,9 @@ bool KLocale::isApplicationTranslatedInto( const TQString & language) // kdDebug() << "isApplicationTranslatedInto: filename " << sFileName << endl; TQString sAbsFileName = locate( "locale", sFileName ); + if (sAbsFileName.isEmpty()) + sAbsFileName = locate( "locale-bundle", sFileName ); + // kdDebug() << "isApplicationTranslatedInto: absname " << sAbsFileName << endl; return ! sAbsFileName.isEmpty(); } diff --git a/kdecore/kstandarddirs.cpp b/kdecore/kstandarddirs.cpp index 2568dc7df..800c0e191 100644 --- a/kdecore/kstandarddirs.cpp +++ b/kdecore/kstandarddirs.cpp @@ -94,13 +94,13 @@ KStandardDirsSingleton* KStandardDirsSingleton::self() { return s_self; } -static const char* const types[] = {"html", "icon", "apps", "sound", - "data", "locale", "services", "mime", +static const char* const types[] = {"html", "html-bundle", "icon", "apps", "sound", + "data", "locale", "locale-bundle", "services", "mime", "servicetypes", "config", "exe", "wallpaper", "lib", "pixmap", "templates", "module", "qtplugins", "xdgdata-apps", "xdgdata-dirs", "xdgconf-menu", - "xdgdata-icon", "xdgdata-pixmap", + "xdgdata-icon", "xdgdata-pixmap", "xdgconf-autostart", "kcfg", "emoticons", 0 }; static int tokenize( TQStringList& token, const TQString& str, @@ -719,7 +719,10 @@ void KStandardDirs::createSpecialResource(const char *type) { char hostname[256]; hostname[0] = 0; - gethostname(hostname, 255); + if( getenv("XAUTHLOCALHOSTNAME")) + strlcpy(hostname, getenv("XAUTHLOCALHOSTNAME"), 255 ); + else + gethostname(hostname, 255); TQString dir = TQString("%1%2-%3").arg(localkdedir()).arg(type).arg(hostname); char link[1024]; link[1023] = 0; @@ -1024,6 +1027,8 @@ static int tokenize( TQStringList& tokens, const TQString& str, TQString KStandardDirs::kde_default(const char *type) { if (!strcmp(type, "data")) return "share/apps/"; + if (!strcmp(type, "html-bundle")) + return "share/doc-bundle/HTML/"; if (!strcmp(type, "html")) return "share/doc/kde/HTML/"; if (!strcmp(type, "icon")) @@ -1036,6 +1041,8 @@ TQString KStandardDirs::kde_default(const char *type) { return "share/applnk/"; if (!strcmp(type, "sound")) return "share/sounds/"; + if (!strcmp(type, "locale-bundle")) + return "share/locale-bundle/"; if (!strcmp(type, "locale")) return "share/locale/"; if (!strcmp(type, "services")) @@ -1068,6 +1075,8 @@ TQString KStandardDirs::kde_default(const char *type) { return "desktop-directories/"; if (!strcmp(type, "xdgconf-menu")) return "menus/"; + if (!strcmp(type, "xdgconf-autostart")) + return "autostart/"; if (!strcmp(type, "kcfg")) return "share/config.kcfg"; if (!strcmp(type, "emoticons")) diff --git a/kdecore/kwinmodule.cpp b/kdecore/kwinmodule.cpp index 7e4d4f773..f113570db 100644 --- a/kdecore/kwinmodule.cpp +++ b/kdecore/kwinmodule.cpp @@ -436,7 +436,9 @@ TQRect KWinModule::workArea( const TQValueList& exclude, int desktop ) cons if ( strut.bottom > 0 ) r.setBottom( r.bottom() - (int) strut.bottom ); - a = a.intersect(r); + TQRect tmp; + tmp = a.intersect(r); + a = tmp; } return a; } diff --git a/kded/Makefile.am b/kded/Makefile.am index 8437919f0..d8ebac458 100644 --- a/kded/Makefile.am +++ b/kded/Makefile.am @@ -36,12 +36,16 @@ kbuildsycoca_la_SOURCES = kbuildsycoca.cpp kbuildservicetypefactory.cpp \ kctimefactory.cpp \ vfolder_menu.cpp -bin_PROGRAMS = kdontchangethehostname kde-menu +bin_PROGRAMS = kdontchangethehostname kde-menu kmimelist kdontchangethehostname_LDFLAGS = $(all_libraries) $(KDE_RPATH) kdontchangethehostname_LDADD = $(LIB_KDECORE) kdontchangethehostname_SOURCES = khostname.cpp +kmimelist_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kmimelist_LDADD = ../kio/libkio.la +kmimelist_SOURCES = kmimelist.cpp + kde_menu_LDFLAGS = $(all_libraries) $(KDE_RPATH) kde_menu_LDADD = $(LIB_KIO) kde_menu_SOURCES = kde-menu.cpp diff --git a/kded/khostname.cpp b/kded/khostname.cpp index e38c88f53..0fa837a2c 100644 --- a/kded/khostname.cpp +++ b/kded/khostname.cpp @@ -111,7 +111,8 @@ static QCStringList split(const TQCString &str) void KHostName::changeX() { - TQString cmd = "xauth list"; + const char* xauthlocalhostname = getenv("XAUTHLOCALHOSTNAME"); + TQString cmd = "xauth -n list"; FILE *xFile = popen(TQFile::encodeName(cmd), "r"); if (!xFile) { @@ -123,6 +124,7 @@ void KHostName::changeX() char buf[1024+1]; while (!feof(xFile)) { + buf[1024]='\0'; TQCString line = fgets(buf, 1024, xFile); if (line.length()) line.truncate(line.length()-1); // Strip LF. @@ -157,12 +159,17 @@ void KHostName::changeX() TQCString newNetId = newName+netId.mid(i); TQCString oldNetId = netId.left(i); - if(oldNetId != oldName) - continue; + if(oldNetId != oldName + && (!xauthlocalhostname || strcmp(xauthlocalhostname, oldNetId.data()) != 0)) + continue; - cmd = "xauth remove "+KProcess::quote(netId); - system(TQFile::encodeName(cmd)); - cmd = "xauth add "; + // don't nuke the xauth when XAUTHLOCALHOSTNAME points to it + if (!xauthlocalhostname || oldNetId != xauthlocalhostname) + { + cmd = "xauth -n remove "+KProcess::quote(netId); + system(TQFile::encodeName(cmd)); + } + cmd = "xauth -n add "; cmd += KProcess::quote(newNetId); cmd += " "; cmd += KProcess::quote(authName); @@ -276,7 +283,10 @@ void KHostName::changeDcop() } } - // Remove old entries + // Remove old entries, but only if XAUTHLOCALHOSTNAME doesn't point + // to it + char* xauthlocalhostname = getenv("XAUTHLOCALHOSTNAME"); + if (!xauthlocalhostname || !oldNetId.contains(xauthlocalhostname)) { TQString cmd = "iceauth remove "+KProcess::quote("netid="+oldNetId); system(TQFile::encodeName(cmd)); @@ -368,9 +378,7 @@ int main(int argc, char **argv) KHostName hn; - if(!getenv("XAUTHLOCALHOSTNAME")) - hn.changeX(); - + hn.changeX(); hn.changeDcop(); hn.changeStdDirs("socket"); hn.changeStdDirs("tmp"); diff --git a/kded/kmimelist.cpp b/kded/kmimelist.cpp new file mode 100644 index 000000000..a08484bc6 --- /dev/null +++ b/kded/kmimelist.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +int main(int argc, char *argv[]) +{ + KApplication k(argc,argv,"blurb",false); + + KMimeType::List mtl = KMimeType::allMimeTypes( ); + assert( mtl.count() ); + qDebug( "Found %d mime types.", mtl.count() ); + TQValueListIterator it(mtl.begin()); + KServiceTypeProfile::OfferList ol; + + for (; it != mtl.end(); ++it) + { + { + // Application + printf( "APP:%s:", (*it)->name().latin1() ); + ol = KServiceTypeProfile::offers((*it)->name(), "Application"); + TQValueListIterator it2(ol.begin()); + for (; it2 != ol.end(); ++it2) { + if ((*it2).allowAsDefault()) + printf( " %s", (*it2).service()->desktopEntryPath().ascii() ); + + } + printf( "\n" ); + } + + { + // Embedded + printf( "PART:%s:", (*it)->name().latin1() ); + ol = KServiceTypeProfile::offers((*it)->name(), "KParts/ReadOnlyPart"); + TQValueListIterator it2(ol.begin()); + for (; it2 != ol.end(); ++it2) { + if ((*it2).allowAsDefault()) + printf( " %s", (*it2).service()->desktopEntryPath().ascii() ); + + } + printf( "\n" ); + } + } +} + diff --git a/kdefx/kstyle.cpp b/kdefx/kstyle.cpp index 531775dbf..dc5216125 100644 --- a/kdefx/kstyle.cpp +++ b/kdefx/kstyle.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -79,7 +80,7 @@ namespace TQWidget* w1; TQWidget* w2; }; - typedef TQMap ShadowMap; + typedef TQMap ShadowMap; static ShadowMap *_shadowMap = 0; TQSingleCleanupHandler cleanupShadowMap; ShadowMap &shadowMap() { @@ -113,8 +114,13 @@ namespace const double shadow_strip[4] = { 0.565, 0.675, 0.835, 0.945 }; -} + static bool useDropShadow(TQWidget* w) + { + return w && w->metaObject() && + w->metaObject()->findProperty("KStyleMenuDropShadow") != -1; + } +} namespace { @@ -128,12 +134,12 @@ class TransparencyHandler : public QObject protected: void blendToColor(const TQColor &col); - void blendToPixmap(const TQColorGroup &cg, const TQPopupMenu* p); + void blendToPixmap(const TQColorGroup &cg, const TQWidget* p); #ifdef HAVE_XRENDER - void XRenderBlendToPixmap(const TQPopupMenu* p); + void XRenderBlendToPixmap(const TQWidget* p); #endif - void createShadowWindows(const TQPopupMenu* p); - void removeShadowWindows(const TQPopupMenu* p); + void createShadowWindows(const TQWidget* p); + void removeShadowWindows(const TQWidget* p); void rightShadow(TQImage& dst); void bottomShadow(TQImage& dst); private: @@ -256,8 +262,16 @@ void KStyle::polish( TQWidget* widget ) widget->installEventFilter(this); } } -} + if (widget->isTopLevel()) + { + if (!d->menuHandler && useDropShadow(widget)) + d->menuHandler = new TransparencyHandler(this, Disabled, 1.0, false); + if (d->menuHandler && useDropShadow(widget)) + widget->installEventFilter(d->menuHandler); + } +} + void KStyle::unPolish( TQWidget* widget ) { @@ -267,8 +281,10 @@ void KStyle::unPolish( TQWidget* widget ) TQFrame::Shape shape = frame->frameShape(); if (shape == TQFrame::ToolBarPanel || shape == TQFrame::MenuBarPanel) widget->removeEventFilter(this); - } + } } + if (widget->isTopLevel() && d->menuHandler && useDropShadow(widget)) + widget->removeEventFilter(d->menuHandler); } @@ -2016,7 +2032,7 @@ void TransparencyHandler::bottomShadow(TQImage& dst) } // Create a shadow of thickness 4. -void TransparencyHandler::createShadowWindows(const TQPopupMenu* p) +void TransparencyHandler::createShadowWindows(const TQWidget* p) { #ifdef Q_WS_X11 int x2 = p->x()+p->width(); @@ -2063,7 +2079,7 @@ void TransparencyHandler::createShadowWindows(const TQPopupMenu* p) #endif } -void TransparencyHandler::removeShadowWindows(const TQPopupMenu* p) +void TransparencyHandler::removeShadowWindows(const TQWidget* p) { #ifdef Q_WS_X11 ShadowMap::iterator it = shadowMap().find(p); @@ -2089,7 +2105,7 @@ bool TransparencyHandler::eventFilter( TQObject* object, TQEvent* event ) // Copyright (C) 2000 Daniel M. Duley // Added 'fake' menu shadows <04-Jul-2002> -- Karol - TQPopupMenu* p = (TQPopupMenu*)object; + TQWidget* p = (TQWidget*)object; TQEvent::Type et = event->type(); if (et == TQEvent::Show) @@ -2128,13 +2144,23 @@ bool TransparencyHandler::eventFilter( TQObject* object, TQEvent* event ) // * shadows after duplicate show events. // * TODO : determine real cause for duplicate events // * till 20021005 - if (dropShadow && p->width() > 16 && p->height() > 16 && !shadowMap().contains( p )) + if ((dropShadow || useDropShadow(p)) + && p->width() > 16 && p->height() > 16 && !shadowMap().contains( p )) createShadowWindows(p); } + else if (et == TQEvent::Resize && p->isShown() && p->isTopLevel()) + { + // Handle drop shadow + if (dropShadow || useDropShadow(p)) + { + removeShadowWindows(p); + createShadowWindows(p); + } + } else if (et == TQEvent::Hide) { // Handle drop shadow - if (dropShadow) + if (dropShadow || useDropShadow(p)) removeShadowWindows(p); // Handle translucency @@ -2159,7 +2185,7 @@ void TransparencyHandler::blendToColor(const TQColor &col) } -void TransparencyHandler::blendToPixmap(const TQColorGroup &cg, const TQPopupMenu* p) +void TransparencyHandler::blendToPixmap(const TQColorGroup &cg, const TQWidget* p) { if (opacity < 0.0 || opacity > 1.0) return; @@ -2172,7 +2198,10 @@ void TransparencyHandler::blendToPixmap(const TQColorGroup &cg, const TQPopupMen return; // Allow styles to define the blend pixmap - allows for some interesting effects. - kstyle->renderMenuBlendPixmap( blendPix, cg, p ); + if (::qt_cast(p)) + kstyle->renderMenuBlendPixmap( blendPix, cg, ::qt_cast(p) ); + else + blendPix.fill(cg.button()); // Just tint as the default behavior TQImage blendImg = blendPix.convertToImage(); TQImage backImg = pix.convertToImage(); @@ -2185,13 +2214,17 @@ void TransparencyHandler::blendToPixmap(const TQColorGroup &cg, const TQPopupMen // Here we go, use XRender in all its glory. // NOTE: This is actually a bit slower than the above routines // on non-accelerated displays. -- Karol. -void TransparencyHandler::XRenderBlendToPixmap(const TQPopupMenu* p) +void TransparencyHandler::XRenderBlendToPixmap(const TQWidget* p) { KPixmap renderPix; renderPix.resize( pix.width(), pix.height() ); // Allow styles to define the blend pixmap - allows for some interesting effects. - kstyle->renderMenuBlendPixmap( renderPix, p->colorGroup(), p ); + if (::qt_cast(p)) + kstyle->renderMenuBlendPixmap( renderPix, p->colorGroup(), + ::qt_cast(p) ); + else + renderPix.fill(p->colorGroup().button()); // Just tint as the default behavior Display* dpy = qt_xdisplay(); Pixmap alphaPixmap; @@ -2229,6 +2262,14 @@ void TransparencyHandler::XRenderBlendToPixmap(const TQPopupMenu* p) void KStyle::virtual_hook( int, void* ) { /*BASE::virtual_hook( id, data );*/ } +// HACK for gtk-qt-engine + +KDE_EXPORT extern "C" +void kde_kstyle_set_scrollbar_type_windows( void* style ) +{ + ((KStyle*)style)->setScrollBarType( KStyle::WindowsStyleScrollBar ); +} + // vim: set noet ts=4 sw=4: // kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; diff --git a/kdemm/0.png b/kdemm/0.png new file mode 100644 index 0000000000000000000000000000000000000000..0c65b5cc8587dcb1cd25c679fa1806326e431b5c GIT binary patch literal 21253 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 + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License version 2 as published by the Free Software Foundation. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +INCLUDES = -I$(top_srcdir)/kdecore -I$(top_srcdir)/kio $(all_includes) + +SUBDIRS = . test + +lib_LTLIBRARIES = libkdemm.la + +kdemmincludedir = $(includedir)/kdemm +kdemminclude_HEADERS = backend.h factory.h player.h channel.h videoplayer.h mixeriface.h simpleplayer.h + +libkdemm_la_SOURCES = backend.cpp factory.cpp player.cpp channel.cpp videoplayer.cpp mixeriface.skel simpleplayer.cpp factory.skel + +libkdemm_la_LDFLAGS = $(QT_LDFLAGS) $(KDE_RPATH) $(KDE_MT_LDFLAGS) $(USER_LDFLAGS) -version-info 0:0:0 -no-undefined +libkdemm_la_LIBADD = ../kdecore/libkdecore.la ../kio/libkio.la + +factory_DCOPIDLNG = true + +METASOURCES = AUTO + +kde_servicetypes_DATA = mmbackendinfo.desktop + +SRCDOC_DEST=$(kde_htmldir)/en/kdelibs/kdemm + +DOXYGEN_REFERENCES = dcop kdecore kio + +include ../admin/Doxyfile.am + diff --git a/kdemm/TODO b/kdemm/TODO new file mode 100644 index 000000000..457bc6f0a --- /dev/null +++ b/kdemm/TODO @@ -0,0 +1,27 @@ +Factory +- get a list of available backends +- be able to choose the one you want to use, overriding the KTrader information +- test on the fly switching + +Audio +- function to retrieve the PCM data that currently is played (could be done in + the Channel as well as in the Player) + +Video +- either a new interface or somehow integrate into the player + If it's a new interface we have to copy most of the Player class, which I + wouldn't like to see. + +two wrapper APIs: +- for notifications + play file and don't care + automatically use the notification channel if present +- for simple players + open file and provide seek and volume + +Mixer abstraction +- provide access to the hardware mixer and/or the mixer of the backend +- provide access to software volume controls (like the Channels) + +Record Interface +- simple PCM recording API diff --git a/kdemm/backend.cpp b/kdemm/backend.cpp new file mode 100644 index 000000000..715fc52de --- /dev/null +++ b/kdemm/backend.cpp @@ -0,0 +1,78 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "backend.h" +#include "player.h" + +#include + +#include +#include +#include + +namespace KDE +{ +namespace Multimedia +{ + +class Backend::Private +{ + public: + Channel * channel; +}; + +Backend::Backend( TQObject * parent, const char * name ) + : TQObject( parent, name ) + , d( 0 ) +{ +} + +Backend::~Backend() +{ + delete d; +} + +bool Backend::playSoundEvent( const KURL & url ) +{ + if( ! d ) + { + d = new Private; + + TQString ctype = "notifications"; + if( availableChannels( Channel::Output ).contains( ctype ) < 1 ) + ctype = "default"; + d->channel = createChannel( KGlobal::instance()->aboutData()->programName(), ctype, Channel::Output ); + } + + Player * player = createPlayer(); + player->setOutputChannel( d->channel ); + connect( player, TQT_SIGNAL( finished() ), player, TQT_SLOT( deleteLater() ) ); + + if( player->load( url ) ) + if( player->play() ) + return true; + delete player; + return false; +} + +}} // namespaces + +#include "backend.moc" + +// vim: sw=4 ts=4 noet diff --git a/kdemm/backend.h b/kdemm/backend.h new file mode 100644 index 000000000..2f626c4f4 --- /dev/null +++ b/kdemm/backend.h @@ -0,0 +1,86 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef BACKEND_H +#define BACKEND_H + +#include +#include +#include +#include + +namespace KDE +{ +namespace Multimedia +{ + class Player; + class VideoPlayer; + /** + * \brief Base class for all KDE Multimedia Backends + * + * This class provides the interface for Multimedia Backends. Use it to get + * a pointer to a new Player or VideoWidget. There are some reserved + * functions for later extension of the class. + * + * \author Matthias Kretz + * \since 4.0 + */ + class Backend : public QObject + { + Q_OBJECT + public: + /** + * default TQObject constructor + */ + Backend( TQObject * parent = 0, const char * name = 0 ); + virtual ~Backend(); + + /** + * @return A new instance to a Player from the Backend. + */ + virtual Player * createPlayer() = 0; + + /** + * Create a new VideoPlayer object. The Backend does not have to + * implement this function. + * + * @return A new instance to a VideoPlayer from the Backend. Or 0 if + * the Backend does not support the VideoPlayer interface. + */ + virtual VideoPlayer * createVideoPlayer() { return 0; } + + virtual bool playSoundEvent(const KURL & url); + + virtual Channel * createChannel( const TQString & title, const TQString & channeltype, + Channel::Direction direction ) = 0; + + virtual TQStringList availableChannels( Channel::Direction direction ) const = 0; + + virtual TQStringList playableMimeTypes() const = 0; + + private: + RESERVE_VIRTUAL_10 + + class Private; + Private * d; + }; +}} // namespaces + +// vim: sw=4 ts=4 noet tw=80 +#endif // BACKEND_H diff --git a/kdemm/channel.cpp b/kdemm/channel.cpp new file mode 100644 index 000000000..fa1d662f9 --- /dev/null +++ b/kdemm/channel.cpp @@ -0,0 +1,69 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Scott Wheeler + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "channel.h" + +#include + +namespace KDE +{ +namespace Multimedia +{ + +class Channel::Private +{ + public: + TQString channelName; + TQString channelType; + Direction direction; +}; + +Channel::Channel( const TQString & channelName, const TQString & type, Direction direction, + TQObject * parent, const char * name ) + : TQObject( parent, name ) +{ + d = new Private; + d->channelName = channelName; + d->channelType = type; + d->direction = direction; +} + +Channel::~Channel() +{ + delete d; +} + +TQString Channel::channelName() const +{ + return d->channelName; +} + +TQString Channel::channelType() const +{ + return d->channelType; +} + +Channel::Direction Channel::direction() const +{ + return d->direction; +} + +}} +#include "channel.moc" +// vim: sw=4 ts=4 noet diff --git a/kdemm/channel.h b/kdemm/channel.h new file mode 100644 index 000000000..6e47febac --- /dev/null +++ b/kdemm/channel.h @@ -0,0 +1,96 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef KDEMM_CHANNEL_H +#define KDEMM_CHANNEL_H + +#include + +class TQString; + +namespace KDE +{ +namespace Multimedia +{ + /** + * \short Abstraction of different inputs or outputs + * + * \author Matthias Kretz + * \since 4.0 + */ + class KDE_EXPORT Channel : public TQObject, virtual public MixerIface + { + Q_OBJECT + public: + virtual ~Channel(); + + enum Direction + { + Input, + Output + }; + + /** + * The user visible name of the channel + */ + virtual TQString channelName() const; + + /** + * The (internal) channel type. + */ + virtual TQString channelType() const; + + /** + * Whether it is an Input or Output channel + */ + virtual Direction direction() const; + + /** + * If the channel has a volume control this function returns \p true + */ + virtual bool hasVolumeControl() const = 0; + + /** + * The current volume of the channel + */ + virtual float volume() const = 0; + + /** + * Set the volume for the channel + * + * \returns Returns \p true if the volume has been successfully set + */ + virtual bool setVolume( float volume ) = 0; + + protected: + /** + * You can not instantiate channels yourself, use the Factory to + * create them. + */ + Channel( const TQString & channelName, const TQString & type, Direction direction, + TQObject * parent = 0, const char * name = 0 ); + + private: + class Private; + Private * d; + }; +}} // namespaces +// vim: sw=4 ts=4 noet tw=80 + +#endif // KDEMM_CHANNEL_H diff --git a/kdemm/factory.cpp b/kdemm/factory.cpp new file mode 100644 index 000000000..1994b8e5e --- /dev/null +++ b/kdemm/factory.cpp @@ -0,0 +1,296 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "factory.h" +#include "backend.h" +#include "player.h" +#include "videoplayer.h" +#include "channel.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace KDE +{ +namespace Multimedia +{ +class Factory::Private +{ + public: + Private() + : backend( 0 ) + { + createBackend(); + } + + void createBackend() + { + KTrader::OfferList offers = KTrader::self()->query( "KDEMultimediaBackend", "Type == 'Service'" ); + KTrader::OfferListIterator it = offers.begin(); + KTrader::OfferListIterator end = offers.end(); + TQStringList errormsg; + for( ; it != end; ++it ) + { + KService::Ptr ptr = *it; + KLibFactory * factory = KLibLoader::self()->factory( TQFile::encodeName( ptr->library() ) ); + if( factory ) + { + backend = ( Backend* )factory->create( 0, "Multimedia Backend", "KDE::Multimedia::Backend" ); + if( 0 == backend ) + { + TQString e = i18n( "create method returned 0" ); + errormsg.append( e ); + kdDebug( 600 ) << "Error getting backend from factory for " << + ptr->name() << ":\n" << e << endl; + } + else + { + service = ptr; + kdDebug( 600 ) << "using backend: " << ptr->name() << endl; + break; + } + } + else + { + TQString e = KLibLoader::self()->lastErrorMessage(); + errormsg.append( e ); + kdDebug( 600 ) << "Error getting factory for " << ptr->name() << + ":\n" << e << endl; + } + } +#if 0 + if( 0 == backend ) + { + if( offers.size() == 0 ) + KMessageBox::error( 0, i18n( "Unable to find a Multimedia Backend" ) ); + else + { + TQString details = ""; + TQStringList::Iterator eit = errormsg.begin(); + TQStringList::Iterator eend = errormsg.end(); + KTrader::OfferListIterator oit = offers.begin(); + KTrader::OfferListIterator oend = offers.end(); + for( ; eit != eend || oit != oend; ++eit, ++oit ) + details += TQString( "" ) + .arg( ( *oit )->name() ).arg( *eit ); + details += "
%1%2
"; + + KMessageBox::detailedError( 0, + i18n( "Unable to use any of the available Multimedia Backends" ), details ); + } + } +#endif + } + + Backend * backend; + KService::Ptr service; + + TQValueList objects; +}; + +Factory * Factory::m_self = 0; + +Factory * Factory::self() +{ + if( ! m_self ) + m_self = new Factory(); + return m_self; +} + +Factory::Factory() + : DCOPObject( "KDEMMFactory" ) + , d( new Private ) +{ + connectDCOPSignal( 0, 0, "kdemmBackendChanged()", "kdemmBackendChanged()", false); +} + +Factory::~Factory() +{ + delete d; +} + +void Factory::kdemmBackendChanged() +{ + if( d->backend ) + { + // wouw, if we want to switch on the fly we have to exchange the + // (Video)Player and Channel classes without the API user noticing. That + // would mean to implement a kind of smartwrapper: + // Player: Interface that accesses Player_skel which is abstract and is + // implemented by the Backend. The API user would only get access to the + // Player class and if you want to switch the backend you can delete the + // Player_skel object and put another implementation in its place. + // + // Now we tell the kdemm using app to help us. With the first signal we + // tell 'em to delete all the (Video)Players and Channels and with the + // second to recreate them all again. + emit deleteYourObjects(); + if( d->objects.size() > 0 ) + { + kdWarning( 600 ) << "we were asked to change the backend but the application did\n" + "not free all references to objects created by the factory. Therefor we can not\n" + "change the backend without crashing. Now we have to wait for a restart to make\n" + "backendswitching possible." << endl; + // in case there were objects deleted give 'em a chance to recreate + // them now + emit recreateObjects(); + return; + } + delete d->backend; + d->backend = 0; + } + d->createBackend(); + emit recreateObjects(); +} + +void Factory::objectDestroyed( TQObject * obj ) +{ + kdDebug( 600 ) << k_funcinfo << obj << endl; + void * p = ( void* )obj; + d->objects.remove( p ); +} + +Player * Factory::createPlayer() +{ + if( d->backend ) + { + Player * p = d->backend->createPlayer(); + connect( p, TQT_SIGNAL( destroyed( TQObject* ) ), TQT_SLOT( objectDestroyed( TQObject* ) ) ); + d->objects.append( p ); + return p; + } + else + return 0; +} + +VideoPlayer * Factory::createVideoPlayer() +{ + if( d->backend ) + { + VideoPlayer * vp = d->backend->createVideoPlayer(); + connect( vp, TQT_SIGNAL( destroyed( TQObject* ) ), TQT_SLOT( objectDestroyed( TQObject* ) ) ); + d->objects.append( vp ); + return vp; + } + else + return 0; +} + +bool Factory::playSoundEvent(const KURL & url) +{ + if( d->backend ) + return d->backend->playSoundEvent(url); + else + return false; +} + +Channel * Factory::createChannel( const TQString & title, const TQString & channeltype, + Channel::Direction direction ) +{ + if( d->backend ) + { + Channel * c = d->backend->createChannel( title, channeltype, direction ); + connect( c, TQT_SIGNAL( destroyed( TQObject* ) ), TQT_SLOT( objectDestroyed( TQObject* ) ) ); + d->objects.append( c ); + return c; + } + else + return 0; +} + +TQStringList Factory::availableChannels( Channel::Direction direction ) const +{ + if( d->backend ) + return d->backend->availableChannels( direction ); + else + return TQStringList(); +} + +TQStringList Factory::playableMimeTypes() const +{ + if( d->backend ) + return d->backend->playableMimeTypes(); + else + return TQStringList(); +} + +bool Factory::isMimeTypePlayable( const TQString & type ) const +{ + if( d->backend ) + { + KMimeType::Ptr mimetype = KMimeType::mimeType( type ); + TQStringList mimetypes = playableMimeTypes(); + for( TQStringList::ConstIterator i=mimetypes.begin(); i!=mimetypes.end(); i++ ) + if( mimetype->is( *i ) ) + return true; + } + return false; +} + +TQString Factory::backendName() const +{ + if( d->service ) + return d->service->name(); + else + return TQString::null; +} + +TQString Factory::backendComment() const +{ + if( d->service ) + return d->service->comment(); + else + return TQString::null; +} + +TQString Factory::backendVersion() const +{ + if( d->service ) + return d->service->property( "X-KDE-MMBackendInfo-Version" ).toString(); + else + return TQString::null; +} + +TQString Factory::backendIcon() const +{ + if( d->service ) + return d->service->icon(); + else + return TQString::null; +} + +TQString Factory::backendWebsite() const +{ + if( d->service ) + return d->service->property( "X-KDE-MMBackendInfo-Website" ).toString(); + else + return TQString::null; +} + +}} //namespaces + +#include "factory.moc" + +// vim: sw=4 ts=4 noet diff --git a/kdemm/factory.h b/kdemm/factory.h new file mode 100644 index 000000000..c9ed2e3da --- /dev/null +++ b/kdemm/factory.h @@ -0,0 +1,222 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef KDEMM_FACTORY_H +#define KDEMM_FACTORY_H + +#include +#include +#include +#include +#include + +namespace KDE +{ +/** + * \brief The KDE Multimedia classes + * + * In this Namespace you find a few classes to access Multimedia functions like + * playing audio files and simple video playing. Those classes are not dependent + * on any specific framework (like they were in pre KDE4 times) but rather use + * exchangeable backends to do the "dirty" work. + * + * If you want to write a new backend you also find the necessary classes in + * this namespace. + * + * \since 4.0 + */ +namespace Multimedia +{ +class Backend; +class Player; +class VideoPlayer; + +/** + * \brief Factory to access the preferred Backend. + * + * This class is your entry point to KDE Multimedia usage. It provides the + * necessary objects for playing audio and video. + * + * For simple access to just playing an audio file see SimplePlayer. + * + * \remarks + * Extensions to the existing functionality can either be added by using the + * reserved virtual functions in Backend or by adding a new interface e.g. + * BackendV2 and creating a BackendV2 instance when the Backend instance is + * created. + * + * \author Matthias Kretz + * \since 4.0 + */ +class KDE_EXPORT Factory : public TQObject, public DCOPObject +{ + Q_OBJECT + public: + /** + * Returns a pointer to the factory. + * Use this function to get an instance of KLibLoader. + * + * @return a pointer to the loader. If no loader exists until now then + * one is created + */ + static Factory * self(); + + /** + * Create a new Player. + * + * You need to call it like this: + * \code + * Factory::self()->createPlayer(); + * \endcode + * + * @return a pointer to the Player the backend provides + */ + Player * createPlayer(); + + /** + * Create a new VideoPlayer object. The Backend does not have to + * implement this functionality so you have to check that it didn't + * return 0. + * + * @return A new instance to a VideoPlayer from the Backend. Or 0 if + * the Backend does not support the VideoPlayer interface. + */ + VideoPlayer * createVideoPlayer(); + + /** + * Play the specified sound-file with no further control. Returns + * immediatly. + * + * @return if the file was be succesfully issued, but is not a + * garantie for succes of playing the entire file. + */ + bool playSoundEvent(const KURL & url); + + /** + * Creates a new Channel object that you can use for a Player object to + * play to. + * + * \param title A user visible title for the Channel. Most of the time + * you will use the application's name. + * + * \param channeltype \ref availableChannels() returns a list of strings + * where you can choose one to be used. Most of the time you will use + * the "default" channel. But the Backend could also provide special + * purpose channels like one for notifications, one for previews and one + * for media playback. + * + * \param direction Whether it's a Channel::Input or Channel::Output + * that you're requesting. + * + * \returns Returns a Channel object or 0 if your request couldn't be + * fullfilled. + */ + Channel * createChannel( const TQString & title, + const TQString & channeltype = TQString::fromLatin1( "default" ), + Channel::Direction direction = Channel::Output ); + + /** + * Returns the names of the channels that can be used to play or record, + * depending on the value of \p direction. + * + * \param direction If set to Channel::Output you will get the names of + * all output channels, if set to Channel::Input you get the same for + * the input channels. + */ + TQStringList availableChannels( Channel::Direction direction = Channel::Output ) const; + + /** + * Returns the mimetypes that can be played. + */ + TQStringList playableMimeTypes() const; + + /** + * Checks whether a certain mimetype is playable. + */ + bool isMimeTypePlayable( const TQString & mimetype ) const; + + /** + * Get the name of the Backend. It's the name from the .desktop file. + */ + TQString backendName() const; + + /** + * Get the comment of the Backend. It's the comment from the .desktop file. + */ + TQString backendComment() const; + + /** + * Get the version of the Backend. It's the version from the .desktop file. + * + * The version is especially interesting if there are several versions + * available for binary incompatible versions of the backend's media + * framework. + */ + TQString backendVersion() const; + + /** + * Get the icon (name) of the Backend. It's the icon from the .desktop file. + */ + TQString backendIcon() const; + + /** + * Get the website of the Backend. It's the website from the .desktop file. + */ + TQString backendWebsite() const; + + signals: + /** + * This signal is emitted when the user changes the backend. You then + * have to free all your references to Player or Channel objects. + */ + void deleteYourObjects(); + + /** + * After you got a deleteYourObjects() signal the backend is changed + * internally. Then you will receive this signal, and only then should + * you reconstruct all your objects again. This time they will + * internally use the new backend. + */ + void recreateObjects(); + + protected: + Factory(); + ~Factory(); + + private slots: + void objectDestroyed( TQObject * ); + + private: + static Factory * m_self; + class Private; + Private * d; + + K_DCOP + k_dcop: + /** + * \internal + * This is called via DCOP when the user changes the KDEMM Backend. + */ + void kdemmBackendChanged(); + +}; +}} // namespaces + +#endif // BACKENDFACTORY_H +// vim: sw=4 ts=4 tw=80 noet diff --git a/kdemm/mixeriface.h b/kdemm/mixeriface.h new file mode 100644 index 000000000..8ee9fc96b --- /dev/null +++ b/kdemm/mixeriface.h @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef MIXERIFACE_H +#define MIXERIFACE_H + +#include +#include + +class KDE_EXPORT MixerIface : public DCOPObject +{ + K_DCOP + k_dcop: + virtual TQString channelName() const = 0; + virtual TQString channelType() const = 0; + virtual bool hasVolumeControl() const = 0; + virtual float volume() const = 0; + virtual bool setVolume( float volume ) = 0; + + protected: + MixerIface() : DCOPObject( "MixerIface" ) {} +}; + +#endif // MIXERIFACE_H +// vim: sw=4 ts=4 noet tw=80 diff --git a/kdemm/mmbackendinfo.desktop b/kdemm/mmbackendinfo.desktop new file mode 100644 index 000000000..001a48c58 --- /dev/null +++ b/kdemm/mmbackendinfo.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=ServiceType +X-KDE-ServiceType=KDEMultimediaBackend +Name=KDE Multimedia Backend + +[PropertyDef::X-KDE-MMBackendInfo-Version] +Type=QString + +[PropertyDef::X-KDE-MMBackendInfo-Website] +Type=QString + +#[PropertyDef::X-KDE-MMBackendInfo-Supports] +#Type=QStringList +# +#[PropertyDef::X-KDE-MMBackendInfo-Depends] +#Type=QStringList +# +#[PropertyDef::X-KDE-MMBackendInfo-License] +#Type=QString +# +#[PropertyDef::X-KDE-MMBackendInfo-Author] +#Type=QString +# +#[PropertyDef::X-KDE-MMBackendInfo-Email] +#Type=QString +# +#[PropertyDef::X-KDE-MMBackendInfo-Name] +#Type=QString +# diff --git a/kdemm/player.cpp b/kdemm/player.cpp new file mode 100644 index 000000000..b16c942fc --- /dev/null +++ b/kdemm/player.cpp @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "player.h" + +namespace KDE { +namespace Multimedia { +Player::Player( TQObject * parent, const char * name ) + : TQObject( parent, name ) + , m_state( Player::NoMedia ) +{ +} + +Player::~Player() +{ +} + +Player::State Player::state() const +{ + return m_state; +} + +void Player::setState( Player::State newstate ) +{ + if( m_state != newstate ) + { + Player::State oldstate = m_state; + m_state = newstate; + emit stateChanged( newstate, oldstate ); + } +} + +}} // namespaces + +#include "player.moc" + +// vim: sw=4 ts=4 noet diff --git a/kdemm/player.h b/kdemm/player.h new file mode 100644 index 000000000..b753073a7 --- /dev/null +++ b/kdemm/player.h @@ -0,0 +1,259 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef PLAYOBJECT_H +#define PLAYOBJECT_H + +#include +#include + +class KURL; + +namespace KDE +{ +namespace Multimedia +{ + class Channel; + /** + * \short Interface for accessing media playback functions + * + * \author Matthias Kretz + * \since 4.0 + */ + class Player : public QObject + { + Q_OBJECT + public: + /** + * The state the playobject is in at the moment + */ + enum State + { + /** + * The load method wasn't called or didn't succeed. If the + * object is in this state the next thing you have to do is to + * call load. + */ + NoMedia, + /** + * After calling load it might take a while before the Player is + * ready to play(). Normally this doesn't happen for local + * files, but can happen for remote files where the asynchronous + * mimetype detection can take a while. + */ + Loading, + /** + * The Player has a valid media file loaded and is ready for + * playing. + */ + Stopped, + /** + * The Player is playing a media file. + */ + Playing, + /** + * The Player is waiting for data to be able to continue + * playing. + */ + Buffering, + /** + * The Player is paused currently. + */ + Paused + }; + + virtual ~Player(); + + /** + * Set the output device the Player should use. + * + * @return Returns \p true on success. If it returns \p false the + * output device wasn't changed. This could happen if you pass a + * Channel with Channel::direction() == Channel::Input. + */ + virtual bool setOutputChannel( Channel * device ) = 0; + + /** + * Get the current state. + */ + virtual State state() const; + + /** + * Check whether the Player supports to control the volume. + * + * @returns Return \p true if the Player can change its volume. + */ + virtual bool hasVolumeControl() const = 0; + + /** + * Get the current volume. + * + * @returns the current volume + */ + virtual float volume() const = 0; + + /** + * Get the total time (in milliseconds) of the file currently being played. + */ + virtual long totalTime() const = 0; + + /** + * Get the remaining time (in milliseconds) of the file currently being played. + */ + virtual long remainingTime() const = 0; + + /** + * Get the current time (in milliseconds) of the file currently being played. + */ + virtual long currentTime() const = 0; + + /** + * If the current media may be seeked returns true. + * + * @returns whether the current media may be seeked. + */ + virtual bool seekable() const = 0; + + /** + * Return the time interval in milliseconds between two ticks. + */ + virtual long tickInterval() const = 0; + + public slots: + /** + * Load a media file. If another media is currently loaded it is + * stopped and unloaded. + * + * @param url The location of the media file + * + * @returns If the call was successfull (the media file was found + * and can be read and decoded) returns \p true + */ + virtual bool load( const KURL & url ) = 0; + + /** + * Play the media file. + */ + virtual bool play() = 0; + + /** + * Pause a playing media. If it was paused before nothing changes. + */ + virtual bool pause() = 0; + + /** + * Stop a playback. + */ + virtual bool stop() = 0; + + /** + * Set the volume of the playback + * + * @param volume 0.0 is complete silence and 1.0 is full volume. + * Higher values than 1.0 are possible but might + * result in distortion of the sound. + * + * @returns returns \p true if the call was successfull, returns \p + * false if the volume change didn't work and therefor didn't + * change. + */ + virtual bool setVolume( float volume ) = 0; + + /** + * Seek to the time indicated. + * + * @param time The time in milliseconds where to continue playing. + * + * @returns whether the seek was successfull. + */ + virtual bool seek( long time ) = 0; + + /** + * Change the interval the tick signal is emitted. If you set \p ms + * to 0 the signal gets disabled. + * + * \param ms tick interval in milliseconds + * + * @returns Returns \p true if the tick intervall was changed. + */ + virtual bool setTickInterval( long ms ) = 0; + + signals: + /** + * Emitted when the file has finished playing on its own. + * I.e. it is not emitted if you call stop(), pause() or + * load(), but only on end-of-file or a critical error. + */ + void finished(); + + //XXX do we want a aboutToFinish() signal? + + /** + * Emitted when the state of the Player has changed. + * In case you're not interested in the old state you can also + * connect to a slot that only has one State argument. + * + * @param newstate The state the Player is in now + * @param oldstate The state the Player was in before + */ + void stateChanged( KDE::Multimedia::Player::State newstate, KDE::Multimedia::Player::State oldstate ); + + /** + * This signal gets emitted every tickInterval milliseconds. + * + * \param time The position of the media file in milliseconds. + * + * \see setTickInterval, tickInterval + */ + void tick( long time ); + + /** + * This signal is emitted as soon as the length of the media file is + * known or has changed. + * + * @param length The length of the media file in milliseconds. + */ + void length( long length ); + + protected: + /** + * You can not instantiate players yourself, use the Factory to + * create them. + */ + Player( TQObject * parent, const char * name ); + + /** + * Call this in your reimplementation for state changes. + * It emits the stateChanged signal if the state has really changed. + * + * @param newstate The new state of the Player. If \p newstate is + * equal to the old state() the call will be ignored. + */ + void setState( State newstate ); + + private: + class Private; + Private * d; + + State m_state; + }; +}} //namespaces + +// vim: sw=4 ts=4 tw=80 noet +#endif // PLAYOBJECT_H diff --git a/kdemm/reqspec.html b/kdemm/reqspec.html new file mode 100644 index 000000000..a07860c79 --- /dev/null +++ b/kdemm/reqspec.html @@ -0,0 +1,533 @@ +Requirements & Specification +

Requirements & Specification

+ +
+

1. Problem Statement

+ +

KDE needs to integrate better into existing Multimedia solutions for Linux.

+ +

Currently many KDE apps use aRts as media framework and soundserver, while other apps use mplayer, xine and/or gstreamer to do what they need. Sometimes this results in mutually exclusive usage of the soundcard and generally it's an unneeded overhead.

+ +

KDE Developers should be able to "natively" use multimedia functions in the KDE API. The KDE Mulitmedia API (short kdemm) should provide an easy to use and Qt/KDE API like aproach to multimedia functionality.

+ +

Nowadays when a KDE developer wants to add multimedia functionality to his program he can either choose one framework to do it, write all the media code himself (nobody does this, and probably nobody should) or use an abstraction to be able to use more than one framework. Most of the time the development models (object model, programming language) are not the same as a KDE developer is used to. And very often the API of the framework used is more complicated than the task he wants to accomplish. The kdemm API should be useable without any knowledge about media frameworks and how these things work internally.

+ +

The API does not aim to be a general Linux solution to multimedia. It aims to be the solution for all KDE developers.

+ +

KDE should ease the use of pro audio tools on Linux and provide the necessary configuration and integration so that for pro audio tools the DE of choice is KDE.

+ +
+

2. Requirements

+ +
+

2.1. Actors

+ +
+

Actor: Admin

+ + + + + + + +
DescriptionAdministrates a KDE installation. Most users are their own admins...
Initiated User Tasksconfigure Multimedia sytems
Initiated Use CasesConfigure kdemm

+ +
+

Actor: KDE Developer

+ + + + + + + +
DescriptionThe average KDE Developer who might need some multimedia functionality, like playing a media object, for his program.
Initiated User Tasksadd media playback to his program
Initiated Use Casesadd media playback functionality to a program, initialize kdemm

+ +
+

Actor: KDE Multimedia Developer

+ + + + + + + +
DescriptionKDE Developer writing a media application like a jukebox or full-featured media player.
Initiated User TasksDevelop Media Player
Initiated Use Casesmedia playback, manage video window, modify audio path, play media object, load media object, create reusable audio path, crossfading

+ +
+

Actor: KDE User

+ + + + + + + +
DescriptionUser of the K Desktop Environment.
Initiated User Taskschange audio output volume
Initiated Use Caseschange audio volume

+ +
+

2.2. User Tasks

+ +

Actor Admin initilizes the following user tasks

+ +
+

User Task: configure Multimedia sytems

+ + + + + +
DescriptionHis aim is to make all multimedia applications work happily side by side.

He has several options to select, like the audio driver backend to use OSS or ALSA, whether he wants to have a soundserver running and what application uses what audio output. He can set the video output method, like e.g. xv or SDL or plain X.

This has to be done so that applications don't block each other and sometimes to make them interact with one another (like you can do with soundservers).

It is important to have it correctly configured else the user will have problems whenever he uses multimedia programs.

This configuration has to be done once until a new program is installed that is incompatible with the setup. Ideally this task has to be done once and never again.
Realized in Use CasesConfigure kdemm

+
+
+ +

Actor KDE Developer initilizes the following user tasks

+ +
+

User Task: add media playback to his program

+ + + + + +
DescriptionThe average KDE Developer might want to add media playback functionality to his program, meaning that his program will be able to play a sound file or even video file.

He can select from a lot of choices how to achieve his goal.
- write the relevant audio/video code himself
- select and use a media framework like gstreamer or aRts
- use existing code from kdelibs (might not be enough for what he's trying to do)

A lot of programs nowadays need to have multimedia functionality.

It's important that KDE developers can add multimedia functionality to their programs.
Realized in Use Casesadd media playback functionality to a program

+
+
+ +

Actor KDE Multimedia Developer initilizes the following user tasks

+ +
+

User Task: Develop Media Player

+ + + + + +
DescriptionAim: The developers aim is to write a full featured media player like mplayer, xine, all the frontends for the former, windoze media player :-P, realplayer, you name it.

Options: He can choose from a variety of available code, like NMM, gstreamer, aRts to a certain extend, mplayer or (lib)xine to provide for his multimedia needs. He can choose how to output the audio and video as far as the former choice gives him options. There are constraints on what media formats he can play or what he can do with the data depending on his choice of media backend/framework.

Reason: A modern desktop environment needs to have a modern media player with all the latest bells and whistles.

Priority: It is important that KDE has "native" media players.

Info-In:
- knowledge about what other media players can do
- features asked for by users (e.g. EQ is a must have for some users)
Realized in Use Casesmedia playback, manage video window, modify audio path, play media object, load media object, create reusable audio path, crossfading

+
+
+ +

Actor KDE User initilizes the following user tasks

+ +
+

User Task: change audio output volume

+ + + + + +
DescriptionThe user wants to change the volume of some program because it is too loud compared to the sound of another program.

This is a tricky task for the user if the program itself doesn't allow to attenuate its sound. If it does he can use the volume control of the program. If it doesn't he has to use a soundserver that allows him to use a volume control for this one specific program only.

It is rather important to have this functionality as it can become pretty anoying if you e.g. want to get notifications when people are chatting with you while you're listening to classical music. The notifications really need attenuation.

It might happen a few times a day that a user needs this functionality.
Realized in Use Caseschange audio volume

+ +
+

2.3. Domain Data

+ +

When I say "sent" in the diagram below I'm actually talking about the data flow in the underlying media framework. kdemm only creates connections so that the data flow is done by the framework.

+ + + +
+

2.4. Domain Constraints

+ + + +
Network transparencyIt must be possible to run the desktop over the network, not only the GUI, but also audio output. If it is possible, the system should try to play the audio on the computer where the GUI is shown by default.

+ +
+

2.5. Quality Constraints

+ +
+

2.6. Interaction Data

+ +
+

3. Specification

+ +

When reading the use cases keep in mind that the user often is the Application Developer. So the actor steps are steps the application written by the developer does.

+ +
+

3.1. Use Cases

+ +
+

Use Case: add media playback functionality to a program

+ + + + + + + + + + + + + + + + + + + + + + + +
Initiating ActorKDE Developer
Preconditionsthe kdemm API has been initialized without errors (see use case "initialize kdemm")
FlowStepsActorSystem
Tell kdemm what media object you want to play back.
Check whether the media object is one of the known types that the backend can decode. If it cannot be decoded error out.
Prepare for being asked to play back the media object, to seek in it or other operations.
a) Ask for meta information of the current media object. (e.g. length, bitrate, etc.) --> go on with step 5

or

b) asking kdemm to play the media object --> go on with step 7
read out meta information, either using the media backend if it supports this function, or using other external libs
display meta information on the GUI while asking kdemm to play the media object
Tell media backend to start playing.
Wait for notification that the media object has finished playing. Remove display of meta info and clean up all used datastructures.

+ +
+

Use Case: change audio volume

+ + + + + + + + + + + + + + + + + +
Initiating ActorKDE User
Preconditionsa kdemm using program (let's call it A) is running with its volume being x
FlowStepsActorSystem
open KDE Mixer Window
look for all available volume controls and show them (with informative description what control does what) to the user
locate the control for A and move the fader down
decrease the output volume of A
PostconditionsA is running with output volume y < x

+ +
+

Use Case: Configure kdemm

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Initiating ActorAdmin
PreconditionsKDE is installed and the backends the Admin wants to use are installed.
FlowStepsActorSystem
open kdemm admin tool
kdemm looks for available options (media frameworks and their output options) and presents these options in a view (see glossary). One framework is already selected as KDE default and kdemm does a good guess (based on audio driver) on what should be the default audio output option.
For the selected media framework there might be more options that need to be shown.
Also if the audio output choice has configuration options they get shown. (e.g. jackd has a lot of options that are more or less important to the admin)
select another media framework
Change audio output options according to what the framework supports.

Provide access to the configuration options of the just selected media framework.
take a look at media framework config options
show media framework config options (e.g. NMM might show some network related options here)
tweak a few settings
write config out so that programs using kdemm behave like configured
change default audio output
Set the media framework to use the specified audio output.

Provide access to the configuration options of the newly selected audio output.
save settings and close admin tool
write out all configuration and tell all kdemm programs to use the new configuration

+ +
+

Use Case: create reusable audio path

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Initiating ActorKDE Multimedia Developer
Preconditionsthe kdemm API has been initialized without errors (see use case "initialize kdemm")
FlowStepsActorSystem
Ask kdemm for an "audio path"
Create a representation for the flow graph audio normally takes from the media object to the output and return an interface to it.
Ask for a list of effects that can be inserted into the audio path.
Look what effects the media framework supports and return a list that describes those effects (1. information for the end-user, 2. info that a computer program can recognize and identify, 3. unique identifier for the effect)
Create list of effects and let the user decide what effects he wants. Tell kdemm to insert effects into the audio path.
Prepare an audio path with the selected effects for later use.
optional steps:
The user decides to configure the parameters of one effect.
Return a Widget showing the parameters the effect has so that the user can configure the effects.
Ask kdemm to save the state of the current audio path to a configuration object.
Look at current audio path, the contained effects and the parameters of the effects. Write those values in the configuration object.
load media
Tell kdemm to use the above audio path for the media object.
Change flow graph of media framework to use the selected audio path.
play

+ +
+

Use Case: crossfading

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Initiating ActorKDE Multimedia Developer
Preconditionsthe kdemm API has been initialized without errors (see use case "initialize kdemm")
FlowStepsActorSystem
Ask kdemm to send a notification 3 seconds before a media object finishes playing.
tell "notification system" from the media framework if it has one, else start a timer that checks for the end of playback to send the notification
take from playlist
Insert a "Fader" effect into the audio path of the media object
Change flow graph of the media framework to include the "Fader" effect.
play
take from playlist
Insert a "Fader" effect into the audio path of the media object
Change flow graph of the media framework to include the "Fader" effect.
When notification arrives tell the Fader of media object 1 to fade out in 3 seconds. Tell the Fader of media object 2 to fade in in 3 seconds and start playing media object 2.

+ +
+

Use Case: initialize kdemm

+ + + + + + + + + + + + + +
Initiating ActorKDE Developer
PreconditionsKDE and all its libraries are installed correctly.
FlowStepsActorSystem
ask kdemm to initialize
Look at config set by the admin tool and accordingly load the media framework code and initialize an audio output that is at this point already available for the kdemm mixer API.
Postconditionskdemm is initialized and ready for action

+ +
+

Use Case: load media object

+ + + + + + + + + + + + + +
Initiating ActorKDE Multimedia Developer
Preconditionsthe kdemm API has been initialized without errors (see use case "initialize kdemm")
FlowStepsActorSystem
Tell kdemm to load a media object.
Prepare media framework for working with the media object (play, seek, etc.).
PostconditionsMedia object is ready for use.

+ +
+

Use Case: manage video window

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Initiating ActorKDE Multimedia Developer
Preconditionsthe kdemm API has been initialized without errors (see use case "initialize kdemm")
FlowStepsActorSystem
Ask kdemm for a dedicated video widget.
Create widget and keep reference to it for so that all video output at some later point can be displayed in this widget.
optional step - can be inserted at any point from here on:
Resize video widget (might also be fullscreen display).
Adjust for new video output size: video needs to be scaled to fit into the widget.
Ask for available video ratio options.
Ask media framework for available video ratio options. If the media framework doesn't provide any options
Mark the currently used option.
Select one of the ratio options.
Change video ratio.

+ +
+

Use Case: media playback

+ + + + + + + + + + + + + + + + + + + +
Initiating ActorKDE Multimedia Developer
Preconditionsthe kdemm API has been initialized without errors (see use case "initialize kdemm")
FlowStepsActorSystem
Tell kdemm to send a notification whenever a media object is about to finish playing.
tell "notification system" from the media framework if it has one, else start a timer that checks for the end of playback to send the notification
Take first media object from the playlist
play
When notified that the playback is about to end
When notified that the playback has finished

+ +
+

Use Case: modify audio path

+ + + + + + + + + + + + + + + + + + + + + + + +
Initiating ActorKDE Multimedia Developer
Preconditionsthe kdemm API has been initialized without errors (see use case "initialize kdemm")
FlowStepsActorSystem
load
Ask for "audio path" of the media object.
Prepare for changing the flow graph of the audio going from the media object to the default output. Return a reference to an object that can be used to modify the path the audio takes.
Ask for a list of effects that can be inserted into the audio path.
Look what effects the media framework supports and return a list that describes those effects (1. information for the end-user, 2. info that a computer program can recognize and identify, 3. unique identifier for the effect)
Select an effect and tell kdemm to insert it into the audio path (the position can also be specified)
Change audio flow graph of the media framework to include the selected effect at the specified position.
play with effect

+ +
+

Use Case: play media object

+ + + + + + + + + + + +
Initiating ActorKDE Multimedia Developer
Preconditionsa media object has been loaded (include use case "load media object")
FlowStepsActorSystem
Ask kdemm to play the media.
Instruct the media framework to play.

+ +
+

3.2. Features

+ +
+

Feature: create media object

+ + + + + + + + + +
DescriptionCheck whether the media data can be accessed and decoded. If not return an error indicating the problem.
Use the underlying media framework to create a flow graph for reading and decoding the media data. (If the media framework does not support reading data from a remote location an extension has to be created that can use KIO to read the data.)
Create an object to represent the media data.
InputsURL to the media data
Outputsmedia object
Used in Use Casesload media object

+ +
+

Feature: init notifications

+ + + + + + + +
Descriptiontwo cases:
if( The media framework can produce the notification in question ) {
Initialize notifications from media framework and prepare to forward them.
} else {
Use timer to send ticks and check for end of media playback.
}
Inputs- time before media playback ends
- time between ticks
- what notifications to enable/disable (choices are: ticks, aboutToEnd, end)
Used in Use Casesmedia playback, crossfading

+ +
+

3.3. Global Functional Constraints

+ +
+

3.4. Quality Constraints on Use Cases

+ +
+

3.5. Quality Constraints on Features

+ +
+

3.6. Architectural Constraints

+ +
+

3.7. User Interface Constraints

+ +
+

4. Examples

+ +
+

4.1. Actor Instances

+ +
+

4.2. Scenarios

+ +
+

5. Glossary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
admin options view
The kdemm admin tool shows a selection for the media framework and the audio output. Both define an order in which the kdemm applications try to use the framework/audio output. +Further options can be reached in another view for the media framework and the audio output.
 
ALSA
Advanced Linux Sound Architecture: new audio interface for Linux
 
API
Application Programmer Interface: The interface that the Application Programmer uses to interact with the kdemm framework.
 
aRts
analog Realtime Synthesizer: the current soundserver and "media framework" of KDE
 
BC
binary compatibility: If the ABI (application binary interface) is changed in an incompatible way (http://developer.kde.org/documentation/other/binarycompatibility.html) the programs using the library will crash.
 
DE
Desktop Environment
 
desktop
The GUIs of today all work with the metaphor of a "desktop" on your screen. The elements on screen are the objects that you can handle and work with.
 
EQ
equalizer: audio filter to boost and attenuate selected frequencies of the signal
 
flow graph
In media frameworks the flow of media data is modeled as a flow graph. There are nodes representing data sources, data processors and data sinks.
 
gstreamer
GStreamer is a library that allows the construction of graphs of media-handling components, ranging from simple Ogg/Vorbis playback to complex audio (mixing) and video (non-linear editing) processing.
 
GUI
Graphical User Interface
 
kdemm
short for KDE Multimedia - it's the name of the library/framework the API discussed in here is going to have.
 
media object
A media object is a representation for the media data. This could be an audio or video file or stream.
 
mplayer
media player that is often used as backend for other media player GUIs
 
NMM
NETWORK-INTEGRATED MULTIMEDIA MIDDLEWARE: a network-integrated multimedia infrastructure for Linux (as well as other operating systems)
 
OSS
Open Sound System: audio interface for UNIX systems
 
SDL
Simple DirectMedia Layer: multimedia library designed to provide low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer
 
tick
While playing a media file a common task is to show the current position in the file. A tick is a signal every second or so to notify about the progress.
 
URL
Uniform Resource Locator (http://en.wikipedia.org/wiki/Uniform_Resource_Locator)
 
user
As long as I talk about API usage the user equals the AP in API.
 
X
X Window system: The client server architecture used for most UNIX GUI programs.
 
xine
xine is a free multimedia player. It plays back CDs, DVDs, and VCDs. It also decodes multimedia files like AVI, MOV, WMV, and MP3 from local disk drives, and displays multimedia streamed over the Internet. It interprets many of the most common multimedia formats available - and some of the most uncommon formats, too.
 
xv
X Video: extension to X for hardware accelerated video display

+ diff --git a/kdemm/simpleplayer.cpp b/kdemm/simpleplayer.cpp new file mode 100644 index 000000000..71e0edb18 --- /dev/null +++ b/kdemm/simpleplayer.cpp @@ -0,0 +1,198 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "simpleplayer.h" +#include +#include +#include +#include + +namespace KDE +{ +namespace Multimedia +{ + +class SimplePlayer::Private +{ + public: + Channel * channel; + Player * player; + + KURL url; + Player::State state; + float channelvolume; + long time; + TQString title; + TQString type; +}; + +SimplePlayer::SimplePlayer( TQObject * parent, const char * name ) + : TQObject( parent, name ) + , d( new Private ) +{ + connect( Factory::self(), TQT_SIGNAL( deleteYourObjects() ), TQT_SLOT( deleteYourObjects() ) ); + connect( Factory::self(), TQT_SIGNAL( recreateObjects() ), TQT_SLOT( recreateObjects() ) ); + d->channel = Factory::self()->createChannel( KGlobal::instance()->aboutData()->programName() ); + d->player = Factory::self()->createPlayer(); + if ( d->player ) { + d->player->setOutputChannel( d->channel ); + connect( d->player, TQT_SIGNAL( stateChanged( KDE::Multimedia::Player::State, KDE::Multimedia::Player::State ) ), + TQT_SLOT( stateChanged( KDE::Multimedia::Player::State, KDE::Multimedia::Player::State ) ) ); + connect( d->player, TQT_SIGNAL( finished() ), TQT_SIGNAL( finished() ) ); + }; +} + +SimplePlayer::~SimplePlayer() +{ + delete d->player; + delete d->channel; +} + +void SimplePlayer::play( const KURL & url ) +{ + if( ! d->player ) + return; + if( isPaused() && url == d->url ) + { + d->player->play(); + return; + } + if( ! d->player->load( url ) ) + return; + d->url = url; + if( d->player->state() == Player::Stopped ) + d->player->play(); +} + +void SimplePlayer::pause() +{ + if( ! d->player ) + return; + d->player->pause(); +} + +void SimplePlayer::stop() +{ + if( ! d->player ) + return; + d->player->stop(); +} + +long SimplePlayer::totalTime() const +{ + if( ! d->player ) + return 0; + return d->player->totalTime(); +} + +long SimplePlayer::currentTime() const +{ + if( ! d->player ) + return 0; + return d->player->currentTime(); +} + +void SimplePlayer::seek( long ms ) +{ + if( ! d->player ) + return; + d->player->seek( ms ); +} + +float SimplePlayer::volume() const +{ + if( ! d->player ) + return 0; + return d->channel->volume(); +} + +void SimplePlayer::setVolume( float v ) +{ + if( ! d->player ) + return; + d->channel->setVolume( v ); +} + +bool SimplePlayer::isPlaying() const +{ + if( ! d->player ) + return false; + return ( d->player->state() == Player::Playing ); +} + +bool SimplePlayer::isPaused() const +{ + if( ! d->player ) + return false; + return ( d->player->state() == Player::Paused ); +} + +void SimplePlayer::stateChanged( Player::State ns, Player::State os ) +{ + if( ! d->player ) + return; + if( os == Player::Loading && ns == Player::Stopped ) + d->player->play(); +} + +void SimplePlayer::deleteYourObjects() +{ + d->state = d->player->state(); + d->channelvolume = d->channel->volume(); + d->time = d->player->currentTime(); + d->title = d->channel->channelName(); + d->type = d->channel->channelType(); + + if( d->player ) + d->player->stop(); + + delete d->player; + delete d->channel; + d->player = 0; + d->channel = 0; +} + +void SimplePlayer::recreateObjects() +{ + d->channel = Factory::self()->createChannel( d->title, d->type ); + d->channel->setVolume( d->channelvolume ); + + d->player = Factory::self()->createPlayer(); + if( ! d->player ) + return; + + d->player->setOutputChannel( d->channel ); + + if( d->state != Player::NoMedia ) + if( ! d->player->load( d->url ) ) + return; + if( d->state == Player::Playing || d->state == Player::Paused || d->state == Player::Buffering ) + { + d->player->play(); + d->player->seek( d->time ); + if( d->state == Player::Paused ) + d->player->pause(); + } +} + +}} // namespaces + +#include "simpleplayer.moc" + +// vim: sw=4 ts=4 noet diff --git a/kdemm/simpleplayer.h b/kdemm/simpleplayer.h new file mode 100644 index 000000000..f9575d21f --- /dev/null +++ b/kdemm/simpleplayer.h @@ -0,0 +1,72 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef SIMPLEPLAYER_H +#define SIMPLEPLAYER_H + +#include +#include +#include +#include + +class KURL; + +namespace KDE +{ +namespace Multimedia +{ + +class KDE_EXPORT SimplePlayer : public TQObject +{ + Q_OBJECT + public: + SimplePlayer( TQObject * parent = 0, const char * name = 0 ); + ~SimplePlayer(); + + void play( const KURL & url ); + void pause(); + void stop(); + + long totalTime() const; + long currentTime() const; + void seek( long ms ); + + float volume() const; + void setVolume( float volume ); + + bool isPlaying() const; + bool isPaused() const; + + signals: + void finished(); + + private slots: + void stateChanged( KDE::Multimedia::Player::State, KDE::Multimedia::Player::State ); + void deleteYourObjects(); + void recreateObjects(); + + private: + class Private; + Private * d; +}; + +}} // namespaces + +#endif // SIMPLEPLAYER_H +// vim: sw=4 ts=4 noet tw=80 diff --git a/kdemm/test/Makefile.am b/kdemm/test/Makefile.am new file mode 100644 index 000000000..d488da365 --- /dev/null +++ b/kdemm/test/Makefile.am @@ -0,0 +1,30 @@ +# This file is part of the KDE libraries +# Copyright (C) 2004 Matthias Kretz + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License version 2 as published by the Free Software Foundation. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +INCLUDES = -I$(top_srcdir)/kdemm $(all_includes) + +METASOURCES = AUTO + +check_PROGRAMS = guitest statetest + +guitest_SOURCES = main.cpp testwidget.cpp +guitest_LDFLAGS = $(KDE_RPATH) $(all_libraries) +guitest_LDADD = $(LIB_KPARTS) $(top_builddir)/kdemm/libkdemm.la + +statetest_SOURCES = statetest.cpp +statetest_LDFLAGS = $(KDE_RPATH) $(all_libraries) +statetest_LDADD = $(top_builddir)/kdemm/libkdemm.la diff --git a/kdemm/test/main.cpp b/kdemm/test/main.cpp new file mode 100644 index 000000000..eb42dc1ec --- /dev/null +++ b/kdemm/test/main.cpp @@ -0,0 +1,38 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include +#include "testwidget.h" + +int main( int argc, char ** argv ) +{ + KAboutData about( "kdemmtest", "KDE Multimedia Test", + "0.1", "Testprogram", + KAboutData::License_LGPL, 0 ); + about.addAuthor( "Matthias Kretz", 0, "kretz@kde.org" ); + KCmdLineArgs::init( argc, argv, &about ); + KApplication app; + TestWidget foo; + app.setMainWidget( &foo ); + return app.exec(); +} + +// vim: sw=4 ts=4 noet diff --git a/kdemm/test/statetest.cpp b/kdemm/test/statetest.cpp new file mode 100644 index 000000000..48ae444ee --- /dev/null +++ b/kdemm/test/statetest.cpp @@ -0,0 +1,299 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "statetest.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace KDE::Multimedia; + +kdbgstream& operator<<( kdbgstream & stream, const Player::State state ) +{ + switch( state ) + { + case Player::NoMedia: + stream << "NoMedia"; + break; + case Player::Loading: + stream << "Loading"; + break; + case Player::Stopped: + stream << "Stopped"; + break; + case Player::Playing: + stream << "Playing"; + break; + case Player::Buffering: + stream << "Buffering"; + break; + case Player::Paused: + stream << "Paused"; + break; + } + + return stream; +} + +void StateTester::run( const KURL & url ) +{ + /* + check for correct states: + + - after construction: + NoMedia + + - load() + NoMedia -> NoMedia, (only? for remote files: Loading -> NoMedia, Stopped ), Stopped + + - play() + Stopped, Paused -> Playing (except: Stopped -> Stopped) + + - when playing: + Playing -> Buffering -> Playing + + - pause() + Playing -> Paused + + - stop() + Playing, Paused -> Stopped + */ + + f = Factory::self(); + kdDebug() << "using backend: " << f->backendName() << + "\n Comment: " << f->backendComment() << + "\n Version: " << f->backendVersion() << endl; + + c = f->createChannel( "teststates" ); + p = f->createPlayer(); + p->setOutputChannel( c ); + connect( p, TQT_SIGNAL( stateChanged( KDE::Multimedia::Player::State, KDE::Multimedia::Player::State ) ), + TQT_SLOT( stateChanged( KDE::Multimedia::Player::State, KDE::Multimedia::Player::State ) ) ); + connect( p, TQT_SIGNAL( finished() ), kapp, TQT_SLOT( quit() ) ); + + if( p->state() != Player::NoMedia ) + kdDebug() << p->state() << " should be NoMedia" << endl; + + kdDebug() << "loading " << url << endl; + + if( ! p->load( url ) ) + kdDebug() << "load failed" << endl; + if( p->state() == Player::Loading ) + kdDebug() << "wait until Player finished Loading" << endl; + else if( p->state() == Player::Stopped ) + testplaying(); + else if( p->state() == Player::NoMedia ) + { + kdDebug() << "could not load media. exiting." << endl; + exit( 0 ); + } +} + +void StateTester::stateChanged( Player::State newstate, Player::State oldstate ) +{ + kdDebug() << "stateChanged( new = " << newstate << ", old = " << oldstate << " )" << endl; + switch( oldstate ) + { + case Player::NoMedia: + switch( newstate ) + { + case Player::NoMedia: + case Player::Loading: + case Player::Stopped: + return; + default: + break; + } + case Player::Loading: + switch( newstate ) + { + case Player::NoMedia: + return; + case Player::Stopped: + testplaying(); + return; + default: + break; + } + case Player::Stopped: + switch( newstate ) + { + case Player::Playing: + case Player::Stopped: + return; + default: + break; + } + case Player::Playing: + switch( newstate ) + { + case Player::Buffering: + //testbuffering(); + case Player::Paused: + case Player::Stopped: + return; + default: + break; + } + case Player::Buffering: + switch( newstate ) + { + case Player::Playing: + case Player::Stopped: + case Player::Paused: + return; + default: + break; + } + case Player::Paused: + switch( newstate ) + { + case Player::Playing: + case Player::Stopped: + case Player::Buffering: + return; + default: + break; + } + } + + wrongStateChange(); +} + +void StateTester::testplaying() +{ + if( ! p->play() ) + kdDebug() << "play failed" << endl; + if( p->state() == Player::Stopped ) + { + kdDebug() << "could not play media. exiting." << endl; + exit( 0 ); + } + else if( p->state() == Player::Playing ) + { + if( ! p->pause() ) + { + kdDebug() << "pause failed" << endl; + if( p->state() != Player::Playing ) + wrongStateChange(); + } + else + { + if( p->state() != Player::Paused ) + wrongStateChange(); + if( ! p->play() ) + { + kdDebug() << "play failed" << endl; + if( p->state() != Player::Paused ) + wrongStateChange(); + kdError() << "what now? play shouldn't fail here" << endl; + exit( 1 ); + } + if( p->state() != Player::Playing ) + wrongStateChange(); + } + // it's playing now + if( ! p->stop() ) + { + kdDebug() << "stop failed" << endl; + if( p->state() != Player::Playing ) + wrongStateChange(); + } + else + { + if( p->state() != Player::Stopped ) + wrongStateChange(); + if( ! p->play() ) + { + kdDebug() << "play failed" << endl; + if( p->state() != Player::Stopped ) + wrongStateChange(); + kdError() << "play shouldn't fail after it worked before. exiting." << endl; + exit( 1 ); + } + } + // it's playing again + if( ! p->pause() ) + { + kdDebug() << "pause failed. exiting." << endl; + exit( 1 ); + } + if( p->state() != Player::Paused ) + wrongStateChange(); + if( ! p->stop() ) + { + kdDebug() << "stop failed" << endl; + if( p->state() != Player::Paused ) + wrongStateChange(); + exit( 1 ); + } + if( p->state() != Player::Stopped ) + wrongStateChange(); + // do further checking, calling load again + kdDebug() << "success! playing the last 1/5 of the file now and quit on the finished signal" << endl; + p->play(); + p->seek( p->totalTime() * 4 / 5 ); + } +} + +void StateTester::wrongStateChange() +{ + kdError() << "wrong state change in backend!" << endl; + exit( 1 ); +} + +static const KCmdLineOptions options[] = +{ + { "+url", I18N_NOOP( "media file to play" ), 0 }, + KCmdLineLastOption // End of options. +}; + +int main( int argc, char ** argv ) +{ + KAboutData about( "kdemmtest", "KDE Multimedia Test", + "0.1", "Testprogram", + KAboutData::License_LGPL, 0 ); + about.addAuthor( "Matthias Kretz", 0, "kretz@kde.org" ); + KCmdLineArgs::init( argc, argv, &about ); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; // we need it for KTrader + + StateTester tester; + if( KCmdLineArgs::parsedArgs()->count() > 0 ) + tester.run( KCmdLineArgs::parsedArgs()->url( 0 ) ); + else + { + KCmdLineArgs::usage(); + exit( 2 ); + } + + return app.exec(); +} + +#include "statetest.moc" + +// vim: sw=4 ts=4 noet diff --git a/kdemm/test/statetest.h b/kdemm/test/statetest.h new file mode 100644 index 000000000..cf912ac56 --- /dev/null +++ b/kdemm/test/statetest.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef STATETEST_H +#define STATETEST_H + +#include + +#include + +class KURL; + +namespace KDE +{ + namespace Multimedia + { + class Factory; + class Channel; + } +} + +class StateTester : public QObject +{ + Q_OBJECT + public: + void run( const KURL & ); + + private slots: + void stateChanged( KDE::Multimedia::Player::State, KDE::Multimedia::Player::State ); + + private: + void testplaying(); + void wrongStateChange(); + + KDE::Multimedia::Factory * f; + KDE::Multimedia::Channel * c; + KDE::Multimedia::Player * p; +}; + +#endif // STATETEST_H +// vim: sw=4 ts=4 noet diff --git a/kdemm/test/testwidget.cpp b/kdemm/test/testwidget.cpp new file mode 100644 index 000000000..a9a579cd1 --- /dev/null +++ b/kdemm/test/testwidget.cpp @@ -0,0 +1,194 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "testwidget.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace KDE::Multimedia; + +TestWidget::TestWidget() + : TQWidget( 0, 0 ) + , m_ticking( false ) +{ + c = Factory::self()->createChannel( i18n( "KDE Multimedia testprogram" ) ); + if( ! c ) + kdFatal() << "couldn't create outputdevice" << endl; + po = Factory::self()->createPlayer(); + if( ! po ) + kdFatal() << "couldn't create Player" << endl; + + if( ! po->setOutputChannel( c ) ) + kdFatal() << "PlayObject::setOutputChannel failed" << endl; + + if( ! po->state() == Player::NoMedia ) + kdFatal() << "PlayObject in wrong state 1" << endl; + + ( new TQHBoxLayout( this ) )->setAutoAdd( true ); + + m_v1slider = new TQSlider( this ); + m_v1slider->setRange( 0, 150 ); + m_v1slider->setValue( ( int )( 100 * po->volume() ) ); + connect( m_v1slider, TQT_SIGNAL( valueChanged( int ) ), TQT_SLOT( v1changed( int ) ) ); + + m_v2slider = new TQSlider( this ); + m_v2slider->setRange( 0, 150 ); + m_v2slider->setValue( ( int )( 100 * c->volume() ) ); + connect( m_v2slider, TQT_SIGNAL( valueChanged( int ) ), TQT_SLOT( v2changed( int ) ) ); + + TQFrame * frame = new TQFrame( this ); + ( new TQVBoxLayout( frame ) )->setAutoAdd( true ); + + m_seekslider = new TQSlider( frame ); + m_seekslider->setOrientation( Qt::Horizontal ); + connect( m_seekslider, TQT_SIGNAL( valueChanged( int ) ), TQT_SLOT( seek( int ) ) ); + po->setTickInterval( 100 ); + connect( po, TQT_SIGNAL( tick( long ) ), TQT_SLOT( tick( long ) ) ); + connect( po, TQT_SIGNAL( length( long ) ), TQT_SLOT( length( long ) ) ); + + m_statelabel = new TQLabel( frame ); + connect( po, TQT_SIGNAL( stateChanged( KDE::Multimedia::Player::State, KDE::Multimedia::Player::State ) ), + TQT_SLOT( stateChanged( KDE::Multimedia::Player::State ) ) ); + stateChanged( po->state() ); + + TQPushButton * pause = new TQPushButton( frame ); + pause->setText( "pause" ); + connect( pause, TQT_SIGNAL( clicked() ), po, TQT_SLOT( pause() ) ); + + TQPushButton * play = new TQPushButton( frame ); + play->setText( "play" ); + connect( play, TQT_SIGNAL( clicked() ), po, TQT_SLOT( play() ) ); + + TQPushButton * stop = new TQPushButton( frame ); + stop->setText( "stop" ); + connect( stop, TQT_SIGNAL( clicked() ), po, TQT_SLOT( stop() ) ); + + KLineEdit * file = new KLineEdit( frame ); + file->setCompletionObject( new KURLCompletion( KURLCompletion::FileCompletion ) ); + connect( file, TQT_SIGNAL( returnPressed( const TQString & ) ), TQT_SLOT( loadFile( const TQString & ) ) ); + + TQFrame * frame2 = new TQFrame( this ); + ( new TQVBoxLayout( frame2 ) )->setAutoAdd( true ); + + m_volumelabel1 = new TQLabel( frame2 ); + m_volumelabel1->setText( TQString::number( po->volume() ) ); + + m_volumelabel2 = new TQLabel( frame2 ); + m_volumelabel2->setText( TQString::number( c->volume() ) ); + + m_totaltime = new TQLabel( frame2 ); + m_totaltime->setText( TQString::number( po->totalTime() ) ); + + m_currenttime = new TQLabel( frame2 ); + m_currenttime->setText( TQString::number( po->currentTime() ) ); + + m_remainingtime = new TQLabel( frame2 ); + m_remainingtime->setText( TQString::number( po->remainingTime() ) ); + + show(); + + if( ! po->load( KURL( "/home/mkretz/Musik/qt23.mp3" ) ) ) + kdFatal() << "PlayObject::load failed" << endl; + + connect( po, TQT_SIGNAL( finished() ), qApp, TQT_SLOT( quit() ) ); + +} + +void TestWidget::v1changed( int v ) +{ + if( ! po->setVolume( ( ( float )v ) / 100 ) ) + kdDebug() << "setVolume1 failed" << endl; + m_volumelabel1->setText( TQString::number( po->volume() ) ); +} + +void TestWidget::v2changed( int v ) +{ + if( ! c->setVolume( ( ( float )v ) / 100 ) ) + kdDebug() << "setVolume2 failed" << endl; + m_volumelabel2->setText( TQString::number( c->volume() ) ); +} + +void TestWidget::tick( long t ) +{ + m_ticking = true; + m_seekslider->setValue( t ); + m_currenttime->setText( TQString::number( po->currentTime() ) ); + m_remainingtime->setText( TQString::number( po->remainingTime() ) ); + m_ticking = false; +} + +void TestWidget::stateChanged( Player::State newstate ) +{ + switch( newstate ) + { + case Player::NoMedia: + m_statelabel->setText( "NoMedia" ); + break; + case Player::Loading: + m_statelabel->setText( "Loading" ); + break; + case Player::Stopped: + m_statelabel->setText( "Stopped" ); + break; + case Player::Paused: + m_statelabel->setText( "Paused" ); + break; + case Player::Buffering: + m_statelabel->setText( "Buffering" ); + break; + case Player::Playing: + m_statelabel->setText( "Playing" ); + break; + } +} + +void TestWidget::seek( int ms ) +{ + if( ! m_ticking ) + po->seek( ms ); +} + +void TestWidget::length( long ms ) +{ + m_seekslider->setRange( 0, ms ); + m_totaltime->setText( TQString::number( po->totalTime() ) ); + m_currenttime->setText( TQString::number( po->currentTime() ) ); + m_remainingtime->setText( TQString::number( po->remainingTime() ) ); +} + +void TestWidget::loadFile( const TQString & file ) +{ + po->load( KURL( file ) ); +} + +#include "testwidget.moc" +// vim: sw=4 ts=4 noet diff --git a/kdemm/test/testwidget.h b/kdemm/test/testwidget.h new file mode 100644 index 000000000..7a9d0df72 --- /dev/null +++ b/kdemm/test/testwidget.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef TESTWIDGET_H +#define TESTWIDGET_H + +#include + +#include + +class TQSlider; +class TQLabel; +class TQString; + +namespace KDE +{ + namespace Multimedia + { + class Channel; + } +} + +using namespace KDE::Multimedia; + +class TestWidget : public QWidget +{ + Q_OBJECT + public: + TestWidget(); + private slots: + void v1changed( int ); + void v2changed( int ); + void tick( long ); + void stateChanged( KDE::Multimedia::Player::State ); + void seek( int ); + void length( long ); + void loadFile( const TQString & ); + + private: + TQSlider *m_v1slider, *m_v2slider, *m_seekslider; + TQLabel *m_statelabel, *m_volumelabel1, *m_volumelabel2, *m_totaltime, *m_currenttime, *m_remainingtime; + Channel * c; + Player * po; + bool m_ticking; +}; + +#endif // TESTWIDGET_H +// vim: sw=4 ts=4 noet diff --git a/kdemm/videoplayer.cpp b/kdemm/videoplayer.cpp new file mode 100644 index 000000000..e11705204 --- /dev/null +++ b/kdemm/videoplayer.cpp @@ -0,0 +1,34 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "videoplayer.h" + +namespace KDE +{ +namespace Multimedia +{ +VideoPlayer::VideoPlayer( TQObject * parent, const char * name ) + : Player( parent, name ) +{ +} +}} // namespaces + +#include "videoplayer.moc" + +// vim: sw=4 ts=4 noet diff --git a/kdemm/videoplayer.h b/kdemm/videoplayer.h new file mode 100644 index 000000000..3bf26eba0 --- /dev/null +++ b/kdemm/videoplayer.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef VIDEOPLAYER_H +#define VIDEOPLAYER_H + +#include "player.h" + +namespace KDE +{ +namespace Multimedia +{ + +class VideoPlayer : public Player +{ + Q_OBJECT + public: + /** + * Create a widget that will show the video. + * + * \param parent The parent to use for the video widget + * \param name The TQObject name for the widget + * + * \returns Returns the video widget. + */ + virtual TQWidget * createVideoWidget( TQWidget * parent, const char * name ) = 0; + + protected: + /** + * You can not instantiate players yourself, use the Factory to + * create them. + */ + VideoPlayer( TQObject * parent, const char * name ); +}; + +}} //namespaces +#endif // VIDEOPLAYER_H +// vim: sw=4 ts=4 tw=80 noet diff --git a/kdemm/volumecontrold/Makefile.am b/kdemm/volumecontrold/Makefile.am new file mode 100644 index 000000000..b7c7ca729 --- /dev/null +++ b/kdemm/volumecontrold/Makefile.am @@ -0,0 +1,31 @@ +# This file is part of the KDE project +# Copyright (C) 2004 Matthias Kretz + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License version 2 as published by the Free Software Foundation. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +INCLUDES = -I$(top_srcdir) $(all_includes) + +kde_module_LTLIBRARIES = kded_volumecontrold.la + +kded_volumecontrold_la_LDFLAGS = $(all_libraries) -module -avoid-version +kded_volumecontrold_la_LIBADD = ../libkdemm.la +kded_volumecontrold_la_SOURCES = volumecontrold.cpp volumecontrold.skel + +METASOURCES = AUTO + +noinst_HEADERS = volumecontrold.h + +services_DATA = volumecontrold.desktop +servicesdir = $(kde_servicesdir)/kded diff --git a/kdemm/volumecontrold/volumecontrold.cpp b/kdemm/volumecontrold/volumecontrold.cpp new file mode 100644 index 000000000..41fbc40fd --- /dev/null +++ b/kdemm/volumecontrold/volumecontrold.cpp @@ -0,0 +1,47 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "volumecontrold.h" + +#include +#include + +extern "C" +{ + KDEDModule * create_volumecontrold( const TQCString &name ) + { + return new VolumeControlD( name ); + } +} + +class VolumeControlD::Private +{ + public: +}; + +VolumeControlD::VolumeControlD( const TQCString & name ) + : KDEDModule( name ) +{ + connect( KApplication::dcopClient(), TQT_SIGNAL( applicationRemoved( const TQCString & ) ), + TQT_SLOT( applicationRemoved( const TQCString& ) ) ); +} + +#include "volumecontrold.moc" + +// vim: sw=4 ts=4 noet diff --git a/kdemm/volumecontrold/volumecontrold.desktop b/kdemm/volumecontrold/volumecontrold.desktop new file mode 100644 index 000000000..8c0bde15f --- /dev/null +++ b/kdemm/volumecontrold/volumecontrold.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +ServiceTypes=KDEDModule + +X-KDE-ModuleType=Library +X-KDE-Library=volumecontrold +X-KDE-FactoryName=volumecontrold +X-KDE-Kded-autoload=false +X-KDE-Kded-load-on-demand=true + +Name=Volume Control +Comment=volume control daemon module for KDED diff --git a/kdemm/volumecontrold/volumecontrold.h b/kdemm/volumecontrold/volumecontrold.h new file mode 100644 index 000000000..709323c47 --- /dev/null +++ b/kdemm/volumecontrold/volumecontrold.h @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef VOLUMECONTROLD_H +#define VOLUMECONTROLD_H + +#include +#include + +class VolumeControlD : public KDEDModule +{ + Q_OBJECT + K_DCOP + public: + VolumeControlD(const TQCString &name); + virtual ~VolumeControlD(); + + k_dcop: + // think of some better API here... + TQStringList availableChannels() const; + TQStringList openConnectionsToChannel( const TQString & channel ) const; + float volume( const TQString & connection, const TQString & channel ) const; + void setVolume( const TQString & connection, const TQString & channel, float volume ); + + private slots: + /** + * When an app with a registered Channel goes down we want to notice... + */ + void applicationRemoved( const TQCString & ); + + private: + class Private; + Private * d; +}; + +// vim: sw=4 ts=4 tw=80 noet +#endif // VOLUMECONTROLD_H diff --git a/kdeprint/cups/cupsdconf2/cupsddialog.cpp b/kdeprint/cups/cupsdconf2/cupsddialog.cpp index 77f35e1e3..8023aeef7 100644 --- a/kdeprint/cups/cupsdconf2/cupsddialog.cpp +++ b/kdeprint/cups/cupsdconf2/cupsddialog.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include diff --git a/kdeprint/cups/kmcupsmanager.cpp b/kdeprint/cups/kmcupsmanager.cpp index 63513ba55..2d923a371 100644 --- a/kdeprint/cups/kmcupsmanager.cpp +++ b/kdeprint/cups/kmcupsmanager.cpp @@ -85,7 +85,7 @@ static int trials = 5; // change LANG variable so that CUPS is always using // english language: translation may only come from the PPD // itself, or from KDE. - setenv("LANG", "en", 1); + setenv("LANG", "en_US.UTF-8", 1); } KMCupsManager::~KMCupsManager() diff --git a/kdeprint/kmvirtualmanager.cpp b/kdeprint/kmvirtualmanager.cpp index a6e263faf..268da3eac 100644 --- a/kdeprint/kmvirtualmanager.cpp +++ b/kdeprint/kmvirtualmanager.cpp @@ -179,7 +179,7 @@ void KMVirtualManager::setAsDefault(KMPrinter *p, const TQString& name, TQWidget void KMVirtualManager::refresh() { - QFileInfo fi(TQDir::homeDirPath() + TQFile::decodeName("/.lpoptions")); + QFileInfo fi(TQDir::homeDirPath() + TQFile::decodeName("/.cups/.lpoptions")); QFileInfo fi2(TQFile::decodeName("/etc/cups/lpoptions")); // if root, then only use global file: trick -> use twice the same file @@ -301,7 +301,13 @@ void KMVirtualManager::triggerSave() filename = TQFile::decodeName("/etc/cups/lpoptions"); } else - filename = TQDir::homeDirPath() + TQFile::decodeName("/.lpoptions"); + { + TQDir cupsDir(TQDir::home().absPath()+"/.cups"); + if (!cupsDir.exists()) + cupsDir.mkdir(TQDir::home().absPath()+"/.cups"); + filename = TQDir::homeDirPath() + TQFile::decodeName("/.cups/lpoptions"); + } + if (!filename.isEmpty()) { saveFile(filename); diff --git a/kdeprint/kprintpreview.cpp b/kdeprint/kprintpreview.cpp index ab669ea6e..5ad8a428f 100644 --- a/kdeprint/kprintpreview.cpp +++ b/kdeprint/kprintpreview.cpp @@ -108,6 +108,9 @@ static KLibFactory* componentFactory() { kdDebug(500) << "kdeprint: querying trader for 'application/postscript' service" << endl; KLibFactory *factory(0); + factory = KLibLoader::self()->factory("libkghostviewpart"); + if( factory ) + return factory; KTrader::OfferList offers = KTrader::self()->query(TQString::fromLatin1("application/postscript"), TQString::fromLatin1("KParts/ReadOnlyPart"), TQString::null, TQString::null); for (KTrader::OfferList::ConstIterator it = offers.begin(); it != offers.end(); ++it) { diff --git a/kdeprint/management/smbview.cpp b/kdeprint/management/smbview.cpp index 127a42e76..08f8487f7 100644 --- a/kdeprint/management/smbview.cpp +++ b/kdeprint/management/smbview.cpp @@ -184,16 +184,26 @@ void SmbView::setOpen(TQListViewItem *item, bool on) } else if (item->depth() == 1) { // opening server + char *krb5ccname = getenv ("KRB5CCNAME"); m_current = item; - *m_proc << "smbclient -N -L "; - *m_proc << KProcess::quote(item->text(0)); - *m_proc << " -W "; - *m_proc << KProcess::quote(item->parent()->text(0)); - if (m_login != TQString::null) - { + if (krb5ccname) + { + *m_proc << "smbclient -k -N -L "; + } + else + { + *m_proc << "smbclient -N -L "; + } + *m_proc << KProcess::quote (item->text (0)); + *m_proc << " -W "; + *m_proc << KProcess::quote (item->parent ()-> + text (0)); + if (!krb5ccname) + { *m_proc << " -A "; - *m_proc << KProcess::quote(m_passwdFile->name()); - } + *m_proc << KProcess:: + quote (m_passwdFile->name ()); + } startProcess(ShareListing); } } diff --git a/kdesu/defaults.h b/kdesu/defaults.h index 7610985c2..668cdd02f 100644 --- a/kdesu/defaults.h +++ b/kdesu/defaults.h @@ -13,8 +13,9 @@ #ifndef __Defaults_h_included__ #define __Defaults_h_included__ +/*const int defTimeout = 120*60;*/ const int defTimeout = 120*60; const int defEchoMode = 0; -const int defKeep = false; +const int defKeep = true; #endif diff --git a/kdeui/Makefile.am b/kdeui/Makefile.am index 4d60c273b..e5ef761be 100644 --- a/kdeui/Makefile.am +++ b/kdeui/Makefile.am @@ -20,7 +20,7 @@ SUBDIRS = . tests about kdetrayproxy -INCLUDES= -I$(top_srcdir)/kdefx -I$(top_srcdir)/interfaces $(all_includes) +INCLUDES= -I/usr/include/freetype2/ -I$(top_srcdir)/kdefx -I$(top_srcdir)/interfaces $(all_includes) # For the future: examine if condensing the tons of *_LDFLAGS variables # into $(all_libraries) isn't better diff --git a/kdeui/kaction.cpp b/kdeui/kaction.cpp index ffc957658..233fd1039 100644 --- a/kdeui/kaction.cpp +++ b/kdeui/kaction.cpp @@ -42,6 +42,17 @@ #include #include +#include +#include FT_FREETYPE_H +#include +#include +#include +#include +#include +#include + +#include + /** * How it works. * KActionCollection is an organizing container for KActions. diff --git a/kdeui/kactionclasses.cpp b/kdeui/kactionclasses.cpp index 1fa7223cb..113ef2ea0 100644 --- a/kdeui/kactionclasses.cpp +++ b/kdeui/kactionclasses.cpp @@ -27,6 +27,9 @@ #include "kactionclasses.h" #include +#include +#include FT_FREETYPE_H +#include #include #include @@ -35,6 +38,7 @@ #include #include #include +#include #include #include @@ -1498,7 +1502,24 @@ void KFontAction::setFont( const TQString &family ) return; } } - kdDebug(129) << "Font not found " << family.lower() << endl; + + // nothing matched yet, try a fontconfig reverse lookup and + // check again to solve an alias + FcPattern *pattern = NULL; + FcConfig *config = NULL; + TQString realFamily; + TQRegExp regExp("[-:]"); + pattern = FcNameParse( (unsigned char*) family.ascii() ); + FcDefaultSubstitute(pattern); + FcConfigSubstitute (config, pattern, FcMatchPattern); + pattern = FcFontMatch(NULL, pattern, NULL); + realFamily = (char*)FcNameUnparse(pattern); + realFamily.remove(realFamily.find(regExp), realFamily.length()); + + if ( !realFamily.isEmpty() && realFamily != family ) + setFont( realFamily ); + else + kdDebug(129) << "Font not found " << family.lower() << endl; } int KFontAction::plug( TQWidget *w, int index ) diff --git a/kdeui/kcombobox.cpp b/kdeui/kcombobox.cpp index 31f14ed0f..c3cc4c402 100644 --- a/kdeui/kcombobox.cpp +++ b/kdeui/kcombobox.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -343,10 +344,22 @@ void KComboBox::lineEditDeleted() // ********************************************************************* // ********************************************************************* +class KHistoryCombo::KHistoryComboPrivate +{ +public: + KHistoryComboPrivate() : bHistoryEditorEnabled(false) + { + } + ~KHistoryComboPrivate() + { + } + + bool bHistoryEditorEnabled; +}; // we are always read-write KHistoryCombo::KHistoryCombo( TQWidget *parent, const char *name ) - : KComboBox( true, parent, name ), d(0) + : KComboBox( true, parent, name ), d(new KHistoryComboPrivate) { init( true ); // using completion } @@ -354,7 +367,7 @@ KHistoryCombo::KHistoryCombo( TQWidget *parent, const char *name ) // we are always read-write KHistoryCombo::KHistoryCombo( bool useCompletion, TQWidget *parent, const char *name ) - : KComboBox( true, parent, name ), d(0) + : KComboBox( true, parent, name ), d(new KHistoryComboPrivate) { init( useCompletion ); } @@ -441,6 +454,10 @@ void KHistoryCombo::addContextMenuItems( TQPopupMenu* menu ) if ( menu ) { menu->insertSeparator(); + if (d->bHistoryEditorEnabled) { + int idedit = menu->insertItem( SmallIconSet("edit"), i18n("&Edit History..."), this, TQT_SLOT( slotEdit()) ); + menu->setItemEnabled(idedit, count()); + } int id = menu->insertItem( SmallIconSet("history_clear"), i18n("Clear &History"), this, TQT_SLOT( slotClear())); if (!count()) menu->setItemEnabled(id, false); @@ -677,10 +694,104 @@ void KHistoryCombo::slotClear() emit cleared(); } +void KHistoryCombo::slotEdit() +{ + KHistoryComboEditor dlg( historyItems(), this ); + connect( &dlg, TQT_SIGNAL( removeFromHistory(const TQString&) ), TQT_SLOT( slotRemoveFromHistory(const TQString&)) ); + dlg.exec(); +} + +void KHistoryCombo::slotRemoveFromHistory(const TQString &entry) +{ + removeFromHistory(entry); + emit removed(entry); +} + +void KHistoryCombo::setHistoryEditorEnabled( bool enable ) +{ + d->bHistoryEditorEnabled = enable; +} + +bool KHistoryCombo::isHistoryEditorEnabled() const +{ + return d->bHistoryEditorEnabled; +} + void KComboBox::virtual_hook( int id, void* data ) { KCompletionBase::virtual_hook( id, data ); } void KHistoryCombo::virtual_hook( int id, void* data ) { KComboBox::virtual_hook( id, data ); } +void KHistoryComboEditor::virtual_hook( int id, void* data ) +{ KDialogBase::virtual_hook( id, data ); } + +KHistoryComboEditor::KHistoryComboEditor( const TQStringList& entries, TQWidget *parent ) +: KDialogBase( parent, "khistorycomboeditor", true, i18n( "History Editor" ), + KDialogBase::Close | KDialogBase::User1, KDialogBase::User1, true, + KGuiItem( i18n( "&Delete Entry" ), "editdelete") ), d(0) +{ + TQVBox* box = new TQVBox( this ); + box->setSpacing( KDialog::spacingHint() ); + setMainWidget( box ); + + new TQLabel( i18n( "This dialog allows you to delete unwanted history items." ), box ); + + // Add searchline + TQHBox* searchbox = new TQHBox( box ); + searchbox->setSpacing( KDialog::spacingHint() ); + + TQToolButton *clearSearch = new TQToolButton(searchbox); + clearSearch->setTextLabel(i18n("Clear Search"), true); + clearSearch->setIconSet(SmallIconSet(TQApplication::reverseLayout() ? "clear_left" : "locationbar_erase")); + TQLabel* slbl = new TQLabel(i18n("&Search:"), searchbox); + KListViewSearchLine* listViewSearch = new KListViewSearchLine(searchbox); + slbl->setBuddy(listViewSearch); + connect(clearSearch, TQT_SIGNAL(pressed()), listViewSearch, TQT_SLOT(clear())); + + // Add ListView + m_pListView = new KListView( box ); + listViewSearch->setListView(m_pListView); + m_pListView->setAllColumnsShowFocus(true); + m_pListView->header()->hide(); + m_pListView->addColumn(""); + m_pListView->setRenameable( 0 ); + + box->setStretchFactor( m_pListView, 1 ); + + TQStringList newlist = entries; + for ( TQStringList::Iterator it = newlist.begin(); it != newlist.end(); ++it ) { + new TQListViewItem( m_pListView, *it ); + } + + m_pListView->setMinimumSize( m_pListView->sizeHint() ); + + connect( m_pListView, TQT_SIGNAL( selectionChanged( TQListViewItem * ) ), + this, TQT_SLOT( slotSelectionChanged( TQListViewItem * ) ) ); + + enableButton( KDialogBase::User1, false ); + + resize( sizeHint() ); +} + +KHistoryComboEditor::~KHistoryComboEditor() +{ +} + +void KHistoryComboEditor::slotUser1() // Delete button +{ + TQListViewItem *item = m_pListView->selectedItem(); + + if ( item ) { + emit removeFromHistory( item->text(0) ); + m_pListView->takeItem( item ); + enableButton( KDialogBase::User1, false ); + } +} + +void KHistoryComboEditor::slotSelectionChanged( TQListViewItem * item ) +{ + enableButton( KDialogBase::User1, item ); +} + #include "kcombobox.moc" diff --git a/kdeui/kcombobox.h b/kdeui/kcombobox.h index 4a9c30b14..d7d07e4b6 100644 --- a/kdeui/kcombobox.h +++ b/kdeui/kcombobox.h @@ -24,8 +24,15 @@ #include #include +#include +#include +#include +#include +#include #include +#include +#include class TQListBoxItem; class TQPopupMenu; @@ -669,6 +676,12 @@ public: */ void reset() { slotReset(); } + /** + * When enabling it you have to connect to "removed" signal and save changes + */ + void setHistoryEditorEnabled( bool enable ); + bool isHistoryEditorEnabled() const; + public slots: /** * Adds an item to the end of the history list and to the completion list. @@ -702,6 +715,8 @@ signals: */ void cleared(); + void removed( const TQString& item ); + protected: /** * Handling key-events, the shortcuts to rotate the items. @@ -740,11 +755,18 @@ private slots: */ void slotClear(); + /** + * Called from the popupmenu, + */ + void slotEdit(); + /** * Appends our own context menu entry. */ void addContextMenuItems( TQPopupMenu* ); + void slotRemoveFromHistory( const TQString & ); + private: void init( bool useCompletion ); void rotateUp(); @@ -774,6 +796,30 @@ private: KHistoryComboPrivate* const d; }; +class KDEUI_EXPORT KHistoryComboEditor : public KDialogBase +{ + Q_OBJECT + +public: + KHistoryComboEditor( const TQStringList& entries, TQWidget *parent = 0L ); + ~KHistoryComboEditor(); + +signals: + void removeFromHistory( const TQString& ); + +protected slots: + virtual void slotUser1(); // User1 is "Delete Entry" button + void slotSelectionChanged( TQListViewItem * item ); + +protected: + virtual void virtual_hook( int id, void* data ); + +private: + KListView *m_pListView; + + class KHistoryComboEditorPrivate; + KHistoryComboEditorPrivate* const d; +}; #endif diff --git a/kdeui/kfontcombo.cpp b/kdeui/kfontcombo.cpp index 4752a0bd1..ea123c38a 100644 --- a/kdeui/kfontcombo.cpp +++ b/kdeui/kfontcombo.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,18 @@ #include "kfontcombo.h" #include "kfontcombo.moc" +#include +#include FT_FREETYPE_H +#include +#include +#include +#include +#include +#include + +#include + + struct KFontComboPrivate { KFontComboPrivate() @@ -227,6 +240,22 @@ void KFontCombo::setCurrentFont(const TQString &family) return; } } + + // nothing matched yet, try a fontconfig reverse lookup and + // check again to solve an alias + FcPattern *pattern = NULL; + FcConfig *config = NULL; + TQString realFamily; + TQRegExp regExp("[-:]"); + pattern = FcNameParse( (unsigned char*) family.ascii() ); + FcDefaultSubstitute(pattern); + FcConfigSubstitute (config, pattern, FcMatchPattern); + pattern = FcFontMatch(NULL, pattern, NULL); + realFamily = (char*)FcNameUnparse(pattern); + realFamily.remove(realFamily.find(regExp), realFamily.length()); + + if ( !realFamily.isEmpty() && realFamily != family ) + setCurrentFont( realFamily ); } void KFontCombo::slotModified( int ) diff --git a/kdeui/kjanuswidget.cpp b/kdeui/kjanuswidget.cpp index f8647fb26..7f34dc5c1 100644 --- a/kdeui/kjanuswidget.cpp +++ b/kdeui/kjanuswidget.cpp @@ -49,16 +49,19 @@ class KJanusWidget::IconListItem : public QListBoxItem { public: IconListItem( TQListBox *listbox, const TQPixmap &pixmap, - const TQString &text ); + const TQString &text ); virtual int height( const TQListBox *lb ) const; virtual int width( const TQListBox *lb ) const; int expandMinimumWidth( int width ); + void highlight( bool erase ); protected: const TQPixmap &defaultPixmap(); void paint( TQPainter *painter ); - + private: + void paintContents( TQPainter *painter ); + TQPixmap mPixmap; int mMinimumWidth; }; @@ -141,6 +144,8 @@ KJanusWidget::KJanusWidget( TQWidget *parent, const char *name, int face ) mIconList->verticalScrollBar()->installEventFilter( this ); connect( mIconList, TQT_SIGNAL(selectionChanged()), TQT_SLOT(slotShowPage())); + connect( mIconList, TQT_SIGNAL(onItem(TQListBoxItem *)), TQT_SLOT(slotOnItem(TQListBoxItem *))); + hbox->addSpacing( KDialog::marginHint() ); page = new TQFrame( this ); hbox->addWidget( page, 10 ); @@ -259,7 +264,7 @@ void KJanusWidget::slotReopen( TQListViewItem * item ) } TQFrame *KJanusWidget::addPage( const TQString &itemName, const TQString &header, - const TQPixmap &pixmap ) + const TQPixmap &pixmap ) { TQStringList items; items << itemName; @@ -269,8 +274,8 @@ TQFrame *KJanusWidget::addPage( const TQString &itemName, const TQString &header TQVBox *KJanusWidget::addVBoxPage( const TQStringList &items, - const TQString &header, - const TQPixmap &pixmap ) + const TQString &header, + const TQPixmap &pixmap ) { if( !mValid ) { @@ -721,6 +726,12 @@ void KJanusWidget::slotItemClicked(TQListViewItem *it) it->setOpen(!it->isOpen()); } +// hack because qt does not support Q_OBJECT in nested classes +void KJanusWidget::slotOnItem(TQListBoxItem *qitem) +{ + mIconList->slotOnItem( qitem ); +} + void KJanusWidget::setFocus() { if( !mValid ) { return; } @@ -929,11 +940,11 @@ bool KJanusWidget::eventFilter( TQObject *o, TQEvent *e ) KJanusWidget::IconListBox::IconListBox( TQWidget *parent, const char *name, WFlags f ) :KListBox( parent, name, f ), mShowAll(false), mHeightValid(false), - mWidthValid(false) + mWidthValid(false), + mOldItem(0) { } - void KJanusWidget::IconListBox::updateMinimumHeight() { if( mShowAll && !mHeightValid ) @@ -995,6 +1006,45 @@ void KJanusWidget::IconListBox::setShowAll( bool showAll ) } +void KJanusWidget::IconListBox::leaveEvent( TQEvent *ev ) +{ + KListBox::leaveEvent( ev ); + + if ( mOldItem && !mOldItem->isSelected() ) + { + ((KJanusWidget::IconListItem *) mOldItem)->highlight( true ); + mOldItem = 0; + } +} + +// hack because qt does not support Q_OBJECT in nested classes +void KJanusWidget::IconListBox::slotOnItem(TQListBoxItem *qitem) +{ + KListBox::slotOnItem( qitem ); + + if ( qitem == mOldItem ) + { + return; + } + + if ( mOldItem && !mOldItem->isSelected() ) + { + ((KJanusWidget::IconListItem *) mOldItem)->highlight( true ); + } + + KJanusWidget::IconListItem *item = dynamic_cast< KJanusWidget::IconListItem * >( qitem ); + if ( item && !item->isSelected() ) + { + item->highlight( false ); + mOldItem = item; + } + else + { + mOldItem = 0; + } +} + + KJanusWidget::IconListItem::IconListItem( TQListBox *listbox, const TQPixmap &pixmap, const TQString &text ) @@ -1006,6 +1056,7 @@ KJanusWidget::IconListItem::IconListItem( TQListBox *listbox, const TQPixmap &pi mPixmap = defaultPixmap(); } setText( text ); + setCustomHighlighting( true ); mMinimumWidth = 0; } @@ -1017,6 +1068,36 @@ int KJanusWidget::IconListItem::expandMinimumWidth( int width ) } +void KJanusWidget::IconListItem::highlight( bool erase ) +{ + TQRect r = listBox()->itemRect( this ); + r.addCoords( 1, 1, -1, -1 ); + + TQPainter p( listBox()->viewport() ); + p.setClipRegion( r ); + + const TQColorGroup &cg = listBox()->colorGroup(); + if ( erase ) + { + p.setPen( cg.base() ); + p.setBrush( cg.base() ); + p.drawRect( r ); + } + else + { + p.setBrush( cg.highlight().light( 120 ) ); + p.drawRect( r ); + + p.setPen( cg.highlight().dark( 140 ) ); + p.drawRect( r ); + } + + p.setPen( cg.foreground() ); + p.translate( r.x() - 1, r.y() - 1 ); + paintContents( &p ); +} + + const TQPixmap &KJanusWidget::IconListItem::defaultPixmap() { static TQPixmap *pix=0; @@ -1043,16 +1124,35 @@ const TQPixmap &KJanusWidget::IconListItem::defaultPixmap() void KJanusWidget::IconListItem::paint( TQPainter *painter ) +{ + TQRect itemPaintRegion( listBox()->itemRect( this ) ); + TQRect r( 1, 1, itemPaintRegion.width() - 2, itemPaintRegion.height() - 2); + + if ( isSelected() ) + { + painter->eraseRect( r ); + + painter->save(); + painter->setPen( listBox()->colorGroup().highlight().dark( 160 ) ); + painter->drawRect( r ); + painter->restore(); + } + + paintContents( painter ); +} + + +void KJanusWidget::IconListItem::paintContents( TQPainter *painter ) { TQFontMetrics fm = painter->fontMetrics(); int ht = fm.boundingRect( 0, 0, 0, 0, Qt::AlignCenter, text() ).height(); int wp = mPixmap.width(); int hp = mPixmap.height(); + painter->drawPixmap( (mMinimumWidth - wp) / 2, 5, mPixmap ); - painter->drawPixmap( (mMinimumWidth-wp)/2, 5, mPixmap ); if( !text().isEmpty() ) { - painter->drawText( 0, hp+7, mMinimumWidth, ht, Qt::AlignCenter, text() ); + painter->drawText( 1, hp + 7, mMinimumWidth - 2, ht, Qt::AlignCenter, text() ); } } @@ -1082,6 +1182,7 @@ int KJanusWidget::IconListItem::width( const TQListBox *lb ) const void KJanusWidget::virtual_hook( int, void* ) { /*BASE::virtual_hook( id, data );*/ } + // TODO: In TreeList, if the last child of a node is removed, and there is no corrsponding widget for that node, allow the caller to // delete the node. void KJanusWidget::removePage( TQWidget *page ) @@ -1129,6 +1230,7 @@ void KJanusWidget::removePage( TQWidget *page ) } } + TQString KJanusWidget::pageTitle(int index) const { if (!d || !d->mIntToTitle.contains(index)) @@ -1137,6 +1239,7 @@ TQString KJanusWidget::pageTitle(int index) const return d->mIntToTitle[index]; } + TQWidget *KJanusWidget::pageWidget(int index) const { if (!d || !d->mIntToPage.contains(index)) diff --git a/kdeui/kjanuswidget.h b/kdeui/kjanuswidget.h index 29bfd0462..cc22dc334 100644 --- a/kdeui/kjanuswidget.h +++ b/kdeui/kjanuswidget.h @@ -71,6 +71,8 @@ class KDEUI_EXPORT KJanusWidget : public QWidget private: class IconListBox : public KListBox { + friend class KJanusWidget; + public: IconListBox( TQWidget *parent=0, const char *name=0, WFlags f=0 ); void updateMinimumHeight(); @@ -79,10 +81,15 @@ class KDEUI_EXPORT KJanusWidget : public QWidget void invalidateWidth(); void setShowAll( bool showAll ); + protected: + void slotOnItem( TQListBoxItem *item ); + virtual void leaveEvent( TQEvent * ); + private: bool mShowAll; bool mHeightValid; bool mWidthValid; + TQListBoxItem *mOldItem; }; public: @@ -558,6 +565,8 @@ class KDEUI_EXPORT KJanusWidget : public QWidget private slots: bool slotShowPage(); void slotFontChanged(); + + void slotOnItem(TQListBoxItem *item); void slotItemClicked(TQListViewItem *it); void pageGone(TQObject *obj); // signal from the added page's "destroyed" signal void slotReopen(TQListViewItem *item); diff --git a/kdeui/kpassdlg.cpp b/kdeui/kpassdlg.cpp index 8a2d54989..11db31eb5 100644 --- a/kdeui/kpassdlg.cpp +++ b/kdeui/kpassdlg.cpp @@ -49,6 +49,8 @@ #include "kpassdlg.h" +#include "../kdesu/defaults.h" + /* * Password line editor. */ @@ -337,7 +339,8 @@ void KPasswordDialog::init() KConfig* const cfg = KGlobal::config(); const KConfigGroupSaver saver(cfg, "Passwords"); - if (m_Keep && cfg->readBoolEntry("Keep", false)) + bool def = ( qstrcmp( qAppName(), "kdesu" ) == 0 ? defKeep : false ); + if (m_Keep && cfg->readBoolEntry("Keep", def)) ++m_Keep; m_pMain = new TQWidget(this); diff --git a/kdeui/ksconfig.cpp b/kdeui/ksconfig.cpp index ec5460ed7..a8849e8fe 100644 --- a/kdeui/ksconfig.cpp +++ b/kdeui/ksconfig.cpp @@ -187,7 +187,7 @@ KSpellConfig::readGlobalSettings() setRunTogether ( kc->readNumEntry("KSpell_RunTogether", 0) ); setDictionary ( kc->readEntry("KSpell_Dictionary") ); setDictFromList ( kc->readNumEntry("KSpell_DictFromList", false) ); - setEncoding ( kc->readNumEntry ("KSpell_Encoding", KS_E_ASCII) ); + setEncoding ( kc->readNumEntry ("KSpell_Encoding", KS_E_UTF8) ); setClient ( kc->readNumEntry ("KSpell_Client", KS_CLIENT_ISPELL) ); return true; diff --git a/kdeui/ksyntaxhighlighter.cpp b/kdeui/ksyntaxhighlighter.cpp index c70cd672e..a5485841f 100644 --- a/kdeui/ksyntaxhighlighter.cpp +++ b/kdeui/ksyntaxhighlighter.cpp @@ -551,7 +551,7 @@ TQString KDictSpellingHighlighter::spellKey() key += '/'; key += TQString::number( config->readNumEntry( "KSpell_DictFromList", false )); key += '/'; - key += TQString::number( config->readNumEntry( "KSpell_Encoding", KS_E_ASCII )); + key += TQString::number( config->readNumEntry( "KSpell_Encoding", KS_E_UTF8 )); key += '/'; key += TQString::number( config->readNumEntry( "KSpell_Client", KS_CLIENT_ISPELL )); return key; diff --git a/kdeui/ktip.cpp b/kdeui/ktip.cpp index 778d5e33b..ae619a173 100644 --- a/kdeui/ktip.cpp +++ b/kdeui/ktip.cpp @@ -367,24 +367,47 @@ void KTipDialog::showMultiTip(TQWidget *parent, const TQStringList &tipFiles, bo mInstance->raise(); } +static TQString fixTip(TQString tip) +{ + TQRegExp iconRegExp(""); + iconRegExp.setMinimal(true); + if (iconRegExp.search(tip)>-1) { + TQString iconName = iconRegExp.cap(1); + if (!iconName.isEmpty()) + if (KGlobal::dirs()->findResource("icon", iconName).isEmpty()) + tip.replace("crystalsvg","hicolor"); + } + + return tip; +} + void KTipDialog::prevTip() { mDatabase->prevTip(); - mTipText->setText(TQString::fromLatin1( + TQString currentTip = TQString::fromLatin1( "%3") .arg(mTextColor.name()) .arg(mBaseColor.name()) - .arg(i18n(mDatabase->tip().utf8()))); + .arg(i18n(mDatabase->tip().utf8())); + + + currentTip = fixTip(currentTip); + mTipText->setText(currentTip); mTipText->setContentsPos(0, 0); } void KTipDialog::nextTip() { mDatabase->nextTip(); - mTipText->setText(TQString::fromLatin1("%3") + TQString currentTip = TQString::fromLatin1( + "%3") .arg(mTextColor.name()) .arg(mBaseColor.name()) - .arg(i18n(mDatabase->tip().utf8()))); + .arg(i18n(mDatabase->tip().utf8())); + + + currentTip = fixTip(currentTip); + mTipText->setText(currentTip); mTipText->setContentsPos(0, 0); } diff --git a/kdeui/qxembed.cpp b/kdeui/qxembed.cpp index 6403f9d8a..7ec1db3a2 100644 --- a/kdeui/qxembed.cpp +++ b/kdeui/qxembed.cpp @@ -314,8 +314,8 @@ bool QXEmbedAppFilter::eventFilter( TQObject *o, TQEvent * e) if ( qApp->focusWidget() == o && ((QPublicWidget*)qApp->focusWidget()->topLevelWidget())->topData()->embedded ) { TQFocusEvent* fe = (TQFocusEvent*) e; - if ( obeyFocus || fe->reason() == TQFocusEvent::Mouse || - fe->reason() == TQFocusEvent::Shortcut ) { + if ( obeyFocus || fe->reason() != TQFocusEvent::ActiveWindow /*|| fe->reason() == TQFocusEvent::Mouse || + fe->reason() == TQFocusEvent::Shortcut*/ ) { // L0614: A widget in the embedded client was just given the Qt focus. // Variable `obeyFocus' suggests that this is the result of mouse // activity in the client. The XEMBED_REQUEST_FOCUS message causes @@ -478,8 +478,11 @@ static int qxembed_x11_event_filter( XEvent* e) switch ( detail ) { case XEMBED_FOCUS_CURRENT: // L0683: Set focus on saved focus widget - if ( focusCurrent ) + if ( focusCurrent ) { focusCurrent->setFocus(); + if( QXEmbed* emb = dynamic_cast< QXEmbed* >( focusCurrent )) + emb->updateEmbeddedFocus( true ); + } else if ( !w->topLevelWidget()->focusWidget() ) w->topLevelWidget()->setFocus(); break; @@ -511,6 +514,8 @@ static int qxembed_x11_event_filter( XEvent* e) // We first record what the focus widget was // and clear the Qt focus. if ( w->topLevelWidget()->focusWidget() ) { + if( QXEmbed* emb = dynamic_cast< QXEmbed* >( w->topLevelWidget()->focusWidget())) + emb->updateEmbeddedFocus( false ); focusMap->insert( w->topLevelWidget(), new TQGuardedPtr(w->topLevelWidget()->focusWidget() ) ); w->topLevelWidget()->focusWidget()->clearFocus(); @@ -919,6 +924,17 @@ void QXEmbed::focusOutEvent( TQFocusEvent * ){ } +// When QXEmbed has TQt focus and gets/loses X focus, make sure the client knows +// about the state of the focus. +void QXEmbed::updateEmbeddedFocus( bool hasfocus ){ + if (!window || d->xplain) + return; + if( hasfocus ) + sendXEmbedMessage( window, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); + else + sendXEmbedMessage( window, XEMBED_FOCUS_OUT); +} + // L1600: Helper for QXEmbed::embed() // Check whether a window is in withdrawn state. static bool wstate_withdrawn( WId winid ) @@ -1161,6 +1177,8 @@ bool QXEmbed::x11Event( XEvent* e) // L2085: The client asks for the focus. case XEMBED_REQUEST_FOCUS: if( ((QPublicWidget*)topLevelWidget())->topData()->embedded ) { + focusMap->remove( topLevelWidget() ); + focusMap->insert( topLevelWidget(), new TQGuardedPtr( this )); WId window = ((QPublicWidget*)topLevelWidget())->topData()->parentWinId; sendXEmbedMessage( window, XEMBED_REQUEST_FOCUS ); } else { diff --git a/kdeui/qxembed.h b/kdeui/qxembed.h index 38aabbd77..3b345554f 100644 --- a/kdeui/qxembed.h +++ b/kdeui/qxembed.h @@ -191,6 +191,11 @@ public: void enterWhatsThisMode(); // temporary, fix in Qt (Matthias, Mon Jul 17 15:20:55 CEST 2000 ) virtual void reparent( TQWidget * parent, WFlags f, const TQPoint & p, bool showIt = false ); + /** + * @internal + */ + void updateEmbeddedFocus( bool hasfocus ); + signals: /** * This signal is emitted when the embedded window has been lost (destroyed or reparented away) diff --git a/kdoctools/kio_help.cpp b/kdoctools/kio_help.cpp index 615b01b7e..292c4de13 100644 --- a/kdoctools/kio_help.cpp +++ b/kdoctools/kio_help.cpp @@ -42,7 +42,7 @@ TQString HelpProtocol::langLookup(const TQString& fname) TQStringList search; // assemble the local search paths - const TQStringList localDoc = KGlobal::dirs()->resourceDirs("html"); + const TQStringList localDoc = KGlobal::dirs()->resourceDirs("html") + KGlobal::dirs()->resourceDirs("html-bundle"); TQStringList langs = KGlobal::locale()->languageList(); langs.append( "en" ); diff --git a/khtml/ecma/xmlhttprequest.cpp b/khtml/ecma/xmlhttprequest.cpp index d3ebebaec..b20515de7 100644 --- a/khtml/ecma/xmlhttprequest.cpp +++ b/khtml/ecma/xmlhttprequest.cpp @@ -342,16 +342,16 @@ void XMLHttpRequest::send(const TQString& _body) { aborted = false; - if (method == "post") { - TQString protocol = url.protocol().lower(); + const TQString protocol = url.protocol().lower(); + // Abandon the request when the protocol is other than "http", + // instead of blindly doing a KIO::get on other protocols like file:/. + if (!protocol.startsWith("http") && !protocol.startsWith("webdav")) + { + abort(); + return; + } - // Abondon the request when the protocol is other than "http", - // instead of blindly changing it to a "get" request. - if (!protocol.startsWith("http") && !protocol.startsWith("webdav")) - { - abort(); - return; - } + if (method == "post") { // FIXME: determine post encoding correctly by looking in headers // for charset. @@ -763,11 +763,11 @@ Value XMLHttpRequestProtoFunc::tryCall(ExecState *exec, Object &thisObj, const L if (obj.isValid() && obj.inherits(&DOMDocument::info)) { DOM::Node docNode = static_cast(obj.imp())->toNode(); DOM::DocumentImpl *doc = static_cast(docNode.handle()); - + try { body = doc->toString().string(); // FIXME: also need to set content type, including encoding! - + } catch(DOM::DOMException& e) { Object err = Error::create(exec, GeneralError, "Exception serializing document"); exec->setException(err); diff --git a/khtml/khtmlview.cpp b/khtml/khtmlview.cpp index 7f800a670..850d8e9cd 100644 --- a/khtml/khtmlview.cpp +++ b/khtml/khtmlview.cpp @@ -151,6 +151,9 @@ public: KHTMLViewPrivate() : underMouse( 0 ), underMouseNonShared( 0 ), visibleWidgets( 107 ) +#ifndef NO_SMOOTH_SCROLL_HACK + , dx(0), dy(0), ddx(0), ddy(0), rdx(0), rdy(0), scrolling(false) +#endif { #ifndef KHTML_NO_CARET m_caretViewContext = 0; @@ -396,6 +399,17 @@ public: short m_mouseScroll_byY; TQTimer *m_mouseScrollTimer; TQWidget *m_mouseScrollIndicator; +#ifndef NO_SMOOTH_SCROLL_HACK + TQTimer timer2; + int dx; + int dy; + // Step size * 16 and residual to avoid huge difference between 1px/step and 2px/step + int ddx; + int ddy; + int rdx; + int rdy; + bool scrolling; +#endif }; #ifndef QT_NO_TOOLTIP @@ -504,6 +518,11 @@ KHTMLView::KHTMLView( KHTMLPart *part, TQWidget *parent, const char *name) init(); viewport()->show(); +#ifndef NO_SMOOTH_SCROLL_HACK +#define timer timer2 + connect(&d->timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(scrollTick())); +#undef timer +#endif } KHTMLView::~KHTMLView() @@ -1544,7 +1563,7 @@ void KHTMLView::keyPressEvent( TQKeyEvent *_ke ) case Key_Down: case Key_J: if (!d->scrollTimerId || d->scrollSuspended) - scrollBy( 0, 10 ); + scrollBy( 0, 10 * _ke->count() ); if (d->scrollTimerId) d->newScrollTimer(this, 0); break; @@ -1559,7 +1578,7 @@ void KHTMLView::keyPressEvent( TQKeyEvent *_ke ) case Key_Up: case Key_K: if (!d->scrollTimerId || d->scrollSuspended) - scrollBy( 0, -10 ); + scrollBy( 0, -10 * _ke->count()); if (d->scrollTimerId) d->newScrollTimer(this, 0); break; @@ -1572,14 +1591,14 @@ void KHTMLView::keyPressEvent( TQKeyEvent *_ke ) case Key_Right: case Key_L: if (!d->scrollTimerId || d->scrollSuspended) - scrollBy( 10, 0 ); + scrollBy( 10 * _ke->count(), 0 ); if (d->scrollTimerId) d->newScrollTimer(this, 0); break; case Key_Left: case Key_H: if (!d->scrollTimerId || d->scrollSuspended) - scrollBy( -10, 0 ); + scrollBy( -10 * _ke->count(), 0 ); if (d->scrollTimerId) d->newScrollTimer(this, 0); break; @@ -1712,8 +1731,16 @@ void KHTMLView::keyReleaseEvent(TQKeyEvent *_ke) d->scrollSuspendPreActivate = false; if( _ke->key() == Key_Shift && d->scrollSuspendPreActivate && _ke->state() == Qt::ShiftButton && !(KApplication::keyboardMouseState() & Qt::ShiftButton)) + { if (d->scrollTimerId) - d->scrollSuspended = !d->scrollSuspended; + { + d->scrollSuspended = !d->scrollSuspended; +#ifndef NO_SMOOTH_SCROLL_HACK + if( d->scrollSuspended ) + stopScrolling(); +#endif + } + } if (d->accessKeysEnabled) { @@ -3061,6 +3088,16 @@ void KHTMLView::addFormCompletionItem(const TQString &name, const TQString &valu d->formCompletions->writeEntry(name, items); } +void KHTMLView::removeFormCompletionItem(const TQString &name, const TQString &value) +{ + if (!m_part->settings()->isFormCompletionEnabled()) + return; + + TQStringList items = formCompletionItems(name); + if (items.remove(value)) + d->formCompletions->writeEntry(name, items); +} + void KHTMLView::addNonPasswordStorableSite(const TQString& host) { if (!d->formCompletions) { @@ -3259,7 +3296,11 @@ void KHTMLView::viewportWheelEvent(TQWheelEvent* e) else { d->scrollBarMoved = true; +#ifndef NO_SMOOTH_SCROLL_HACK + scrollViewWheelEvent( e ); +#else TQScrollView::viewportWheelEvent( e ); +#endif TQMouseEvent *tempEvent = new TQMouseEvent( TQEvent::MouseMove, TQPoint(-1,-1), TQPoint(-1,-1), Qt::NoButton, e->state() ); emit viewportMouseMoveEvent ( tempEvent ); @@ -4462,4 +4503,117 @@ void KHTMLView::moveCaretToLineEnd() #endif // KHTML_NO_CARET +#ifndef NO_SMOOTH_SCROLL_HACK +#define timer timer2 + +// All scrolls must be completed within 240ms of last keypress +static const int SCROLL_TIME = 240; +// Each step is 20 ms == 50 frames/second +static const int SCROLL_TICK = 20; + +void KHTMLView::scrollBy(int dx, int dy) +{ + KConfigGroup cfg( KGlobal::config(), "KDE" ); + if( !cfg.readBoolEntry( "SmoothScrolling", true )) { + TQScrollView::scrollBy( dx, dy ); + return; + } + // scrolling destination + int full_dx = d->dx + dx; + int full_dy = d->dy + dy; + + // scrolling speed + int ddx = 0; + int ddy = 0; + + int steps = SCROLL_TIME/SCROLL_TICK; + + ddx = (full_dx*16)/steps; + ddy = (full_dy*16)/steps; + + // don't go under 1px/step + if (ddx > 0 && ddx < 16) ddx = 16; + if (ddy > 0 && ddy < 16) ddy = 16; + if (ddx < 0 && ddx > -16) ddx = -16; + if (ddy < 0 && ddy > -16) ddy = -16; + + d->dx = full_dx; + d->dy = full_dy; + d->ddx = ddx; + d->ddy = ddy; + + if (!d->scrolling) { + scrollTick(); + startScrolling(); + } +} + +void KHTMLView::scrollTick() { + if (d->dx == 0 && d->dy == 0) { + stopScrolling(); + return; + } + + int tddx = d->ddx + d->rdx; + int tddy = d->ddy + d->rdy; + + int ddx = tddx / 16; + int ddy = tddy / 16; + d->rdx = tddx % 16; + d->rdy = tddy % 16; + + if (d->dx > 0 && ddx > d->dx) ddx = d->dx; + else + if (d->dx < 0 && ddx < d->dx) ddx = d->dx; + + if (d->dy > 0 && ddy > d->dy) ddy = d->dy; + else + if (d->dy < 0 && ddy < d->dy) ddy = d->dy; + + d->dx -= ddx; + d->dy -= ddy; + +// TQScrollView::setContentsPos( contentsX() + ddx, contentsY() + ddy); + kapp->syncX(); + TQScrollView::scrollBy(ddx, ddy); +// Unaccelerated X can get seriously overloaded by scrolling and for some reason +// will send KeyPress events only infrequently. This should help to reduce +// the load. + kapp->syncX(); +} + +void KHTMLView::startScrolling() +{ + d->scrolling = true; + d->timer.start(SCROLL_TICK, false); +} + +void KHTMLView::stopScrolling() +{ + d->timer.stop(); + d->dx = d->dy = 0; + d->scrolling = false; +} + +// Overloaded from TQScrollView and TQScrollBar +void KHTMLView::scrollViewWheelEvent( TQWheelEvent *e ) +{ + int pageStep = verticalScrollBar()->pageStep(); + int lineStep = verticalScrollBar()->lineStep(); + int step = QMIN( TQApplication::wheelScrollLines()*lineStep, pageStep ); + if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) + step = pageStep; + + if(e->orientation() == Horizontal) + scrollBy(-((e->delta()*step)/120), 0); + else if(e->orientation() == Vertical) + scrollBy(0,-((e->delta()*step)/120)); + + e->accept(); +} + +#undef timer + +#endif // NO_SMOOTH_SCROLL_HACK + #undef DEBUG_CARETMODE diff --git a/khtml/khtmlview.h b/khtml/khtmlview.h index 1efb347a6..a79eff17f 100644 --- a/khtml/khtmlview.h +++ b/khtml/khtmlview.h @@ -181,6 +181,11 @@ signals: void hideAccessKeys(); void repaintAccessKeys(); void findAheadActive( bool ); +//#define NO_SMOOTH_SCROLL_HACK +#ifndef NO_SMOOTH_SCROLL_HACK +public slots: + void scrollBy(int dx, int dy); +#endif protected: void clear(); @@ -211,9 +216,23 @@ protected: void contentsContextMenuEvent ( TQContextMenuEvent *_ce ); void doAutoScroll(); void timerEvent ( TQTimerEvent * ); + +#ifndef NO_SMOOTH_SCROLL_HACK + void startScrolling(); + void stopScrolling(); +#ifndef QT_NO_WHEELEVENT + void scrollViewWheelEvent( TQWheelEvent* e ); +#endif +#endif + protected slots: void slotPaletteChanged(); void slotScrollBarMoved(); +#ifndef NO_SMOOTH_SCROLL_HACK + void scrollTick(); +#else + void scrollTick() {}; // moc cannot handle #if +#endif private slots: void tripleClickTimeout(); @@ -295,6 +314,7 @@ private: TQStringList formCompletionItems(const TQString &name) const; void clearCompletionHistory(const TQString& name); void addFormCompletionItem(const TQString &name, const TQString &value); + void removeFormCompletionItem(const TQString &name, const TQString &value); void addNonPasswordStorableSite( const TQString& host ); bool nonPasswordStorableSite( const TQString& host ) const; diff --git a/khtml/rendering/render_form.cpp b/khtml/rendering/render_form.cpp index 403295a8f..58a9a8d33 100644 --- a/khtml/rendering/render_form.cpp +++ b/khtml/rendering/render_form.cpp @@ -385,7 +385,9 @@ TQPopupMenu *LineEditWidget::createPopupMenu() if (m_input->autoComplete()) { popup->insertSeparator(); - int id = popup->insertItem( SmallIconSet("history_clear"), i18n("Clear &History"), ClearHistory ); + int id = popup->insertItem( SmallIconSet("edit"), i18n("&Edit History..."), EditHistory ); + popup->setItemEnabled( id, (compObj() && !compObj()->isEmpty()) ); + id = popup->insertItem( SmallIconSet("history_clear"), i18n("Clear &History"), ClearHistory ); popup->setItemEnabled( id, (compObj() && !compObj()->isEmpty()) ); } @@ -409,11 +411,25 @@ void LineEditWidget::extendedMenuActivated( int id) m_view->clearCompletionHistory(m_input->name().string()); if (compObj()) compObj()->clear(); + case EditHistory: + { + KHistoryComboEditor dlg( compObj() ? compObj()->items() : TQStringList(), this ); + connect( &dlg, TQT_SIGNAL( removeFromHistory(const TQString&) ), TQT_SLOT( slotRemoveFromHistory(const TQString&)) ); + dlg.exec(); + } default: break; } } +void LineEditWidget::slotRemoveFromHistory(const TQString &entry) +{ + m_view->removeFormCompletionItem(m_input->name().string(), entry); + if (compObj()) + compObj()->removeItem(entry); +} + + bool LineEditWidget::event( TQEvent *e ) { if (KLineEdit::event(e)) diff --git a/khtml/rendering/render_form.h b/khtml/rendering/render_form.h index 33a31a910..c414e079c 100644 --- a/khtml/rendering/render_form.h +++ b/khtml/rendering/render_form.h @@ -272,10 +272,12 @@ private slots: void spellCheckerMisspelling( const TQString &text, const TQStringList &, unsigned int pos); void spellCheckerCorrected( const TQString &, const TQString &, unsigned int ); void spellCheckerFinished(); + void slotRemoveFromHistory( const TQString & ); private: enum LineEditMenuID { - ClearHistory + ClearHistory, + EditHistory }; DOM::HTMLInputElementImpl* m_input; KHTMLView* m_view; diff --git a/kinit/autostart.cpp b/kinit/autostart.cpp index 41caf99f7..63622e737 100644 --- a/kinit/autostart.cpp +++ b/kinit/autostart.cpp @@ -114,7 +114,9 @@ static bool startCondition(const TQString &condition) void AutoStart::loadAutoStartList() { - TQStringList files = KGlobal::dirs()->findAllResources("autostart", "*.desktop", false, true); + TQStringList files = KGlobal::dirs()->findAllResources("xdgconf-autostart", "*.desktop", false, true); + TQStringList kdefiles = KGlobal::dirs()->findAllResources("autostart", "*.desktop", false, true); + files += kdefiles; for(TQStringList::ConstIterator it = files.begin(); it != files.end(); @@ -139,6 +141,17 @@ AutoStart::loadAutoStartList() continue; } + if (config.hasKey("OnlyShowIn")) + { + if (!config.readListEntry("OnlyShowIn", ';').contains("KDE")) + continue; + } + if (config.hasKey("NotShowIn")) + { + if (config.readListEntry("NotShowIn", ';').contains("KDE")) + continue; + } + AutoStartItem *item = new AutoStartItem; item->name = extractName(*it); item->service = *it; diff --git a/kinit/lnusertemp.c b/kinit/lnusertemp.c index 867f1fb95..fdbf8ae0b 100644 --- a/kinit/lnusertemp.c +++ b/kinit/lnusertemp.c @@ -39,7 +39,7 @@ int check_tmp_dir(const char *tmp_dir); int create_link(const char *file, const char *tmp_dir); -int build_link(const char *tmp_prefix, const char *kde_prefix); +int build_link(const char *tmp_prefix, const char *kde_prefix, int kdehostname); int check_tmp_dir(const char *tmp_dir) { @@ -92,7 +92,7 @@ int create_link(const char *file, const char *tmp_dir) #ifndef PATH_MAX #define PATH_MAX 4096 #endif -int build_link(const char *tmp_prefix, const char *kde_prefix) +int build_link(const char *tmp_prefix, const char *kde_prefix, int kdehostname) { struct passwd *pw_ent; char kde_tmp_dir[PATH_MAX+1]; @@ -160,10 +160,21 @@ int build_link(const char *tmp_prefix, const char *kde_prefix) } strncat(kde_tmp_dir, kde_prefix, PATH_MAX - strlen(kde_tmp_dir)); - if (gethostname(kde_tmp_dir+strlen(kde_tmp_dir), PATH_MAX - strlen(kde_tmp_dir) - 1) != 0) + + if( kdehostname ) + { + if( getenv("XAUTHLOCALHOSTNAME")) + strncat(kde_tmp_dir+strlen(kde_tmp_dir), getenv("XAUTHLOCALHOSTNAME"), PATH_MAX - strlen(kde_tmp_dir) - 1); + else + return 0; + } + else { - perror("Aborting. Could not determine hostname: "); - exit(255); + if (gethostname(kde_tmp_dir+strlen(kde_tmp_dir), PATH_MAX - strlen(kde_tmp_dir) - 1) != 0) + { + perror("Could not determine hostname: "); + return 1; + } } kde_tmp_dir[sizeof(kde_tmp_dir)-1] = '\0'; @@ -269,7 +280,9 @@ int main(int argc, char **argv) kde_prefix = "/cache-"; } - res = build_link(tmp_prefix, kde_prefix); + res = build_link(tmp_prefix, kde_prefix, 1); + if( build_link(tmp_prefix, kde_prefix, 0)) + res = 1; free(tmp_prefix); diff --git a/kinit/wrapper.c b/kinit/wrapper.c index 27d208376..7e08d49ca 100644 --- a/kinit/wrapper.c +++ b/kinit/wrapper.c @@ -164,7 +164,9 @@ static int openSocket() sock_file[strlen(sock_file)-1] = 0; strncat(sock_file, "/socket-", MAX_SOCK_FILE - strlen(sock_file)); - if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0) + if( getenv("XAUTHLOCALHOSTNAME")) + strncat(sock_file, getenv("XAUTHLOCALHOSTNAME"), MAX_SOCK_FILE - strlen(sock_file) - 1); + else if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0) { perror("Warning: Could not determine hostname: "); return -1; diff --git a/kio/kfile/kfiledialog.cpp b/kio/kfile/kfiledialog.cpp index f206a4887..0c9364943 100644 --- a/kio/kfile/kfiledialog.cpp +++ b/kio/kfile/kfiledialog.cpp @@ -1165,6 +1165,12 @@ void KFileDialog::urlEntered(const KURL& url) d->pathCombo->setURL( url ); } + if (url.protocol()=="beagle" && url.path()=="/") { + d->pathCombo->setEditText("beagle:/<"+i18n("search term")+">"); + d->pathCombo->lineEdit()->setSelection(8,255); + d->pathCombo->setFocus(); + } + locationEdit->blockSignals( true ); locationEdit->setCurrentItem( 0 ); if ( d->keepLocation ) diff --git a/kio/kfile/kfilesharedlg.cpp b/kio/kfile/kfilesharedlg.cpp index 788edee51..329bd19dc 100644 --- a/kio/kfile/kfilesharedlg.cpp +++ b/kio/kfile/kfilesharedlg.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ public: KProcess *m_configProc; bool m_bAllShared; bool m_bAllUnshared; + bool m_bAllReadOnly; }; KFileSharePropsPlugin::KFileSharePropsPlugin( KPropertiesDialog *_props ) @@ -98,8 +100,10 @@ void KFileSharePropsPlugin::init() delete m_widget; m_rbShare = 0L; m_rbUnShare = 0L; + m_rbSharerw = 0L; m_widget = new TQWidget( d->m_vBox ); TQVBoxLayout * vbox = new TQVBoxLayout( m_widget ); + //TQHBoxLayout * hbox = new TQHBoxLayout( vbox ); switch ( KFileShare::authorization() ) { case KFileShare::Authorized: @@ -110,18 +114,29 @@ void KFileSharePropsPlugin::init() home += '/'; bool ok = true; KFileItemList items = properties->items(); - // We have 3 possibilities: all shared, all unshared, or mixed. + // We have 3 possibilities: all shared, all unshared (ro,rw), or mixed. d->m_bAllShared = true; d->m_bAllUnshared = true; + d->m_bAllReadOnly = true; KFileItemListIterator it( items ); for ( ; it.current() && ok; ++it ) { TQString path = (*it)->url().path(); + // 0 => not shared + // 1 => shared read only + // 3 => shared writeable + int dirStatus = KFileShare::isDirectoryShared( path ); if ( !path.startsWith( home ) ) ok = false; - if ( KFileShare::isDirectoryShared( path ) ) + if ( dirStatus == 1 ) { d->m_bAllUnshared = false; - else - d->m_bAllShared = false; + } + else if ( dirStatus == 3 ) { + d->m_bAllUnshared = false; + d->m_bAllReadOnly = false; + } + else { + d->m_bAllReadOnly = false; + } } if ( !ok ) { @@ -141,16 +156,31 @@ void KFileSharePropsPlugin::init() vbox->addWidget( m_rbUnShare, 0 ); rbGroup->insert( m_rbUnShare ); - m_rbShare = new TQRadioButton( i18n("Shared"), m_widget ); + m_rbShare = new TQRadioButton( i18n("Shared - read only for others"), m_widget ); connect( m_rbShare, TQT_SIGNAL( toggled(bool) ), TQT_SIGNAL( changed() ) ); vbox->addWidget( m_rbShare, 0 ); rbGroup->insert( m_rbShare ); + m_rbSharerw = new TQRadioButton( i18n("Shared - writeable for others"), m_widget ); + connect( m_rbSharerw, TQT_SIGNAL( toggled(bool) ), TQT_SIGNAL( changed() ) ); + vbox->addWidget( m_rbSharerw, 0 ); + rbGroup->insert( m_rbSharerw ); + + //TQLabel *testlabel1 = new TQLabel(i18n("Enter Samba Share Name here"),m_widget); + //m_leSmbShareName = new TQLineEdit(m_widget); + //m_leSmbShareName->setMaxLength(12); + + //hbox->addWidget( testlabel1, 0 ); + //hbox->addWidget( m_leSmbShareName ); + //vbox->addLayout( hbox ); + // Activate depending on status if ( d->m_bAllShared ) - m_rbShare->setChecked(true); + m_rbSharerw->setChecked(true); if ( d->m_bAllUnshared ) m_rbUnShare->setChecked(true); + if ( d->m_bAllReadOnly ) + m_rbShare->setChecked(true); // Some help text TQLabel *label = new TQLabel( i18n("Sharing this folder makes it available under Linux/UNIX (NFS) and Windows (Samba).") , m_widget ); @@ -167,6 +197,9 @@ void KFileSharePropsPlugin::init() vbox->addWidget( m_pbConfig, 0, Qt::AlignHCenter ); vbox->addStretch( 10 ); + + if( !KFileShare::sambaActive() && !KFileShare::nfsActive()) + m_widget->setEnabled( false ); } } break; @@ -228,7 +261,7 @@ void KFileSharePropsPlugin::slotConfigureFileSharingDone() void KFileSharePropsPlugin::applyChanges() { kdDebug() << "KFileSharePropsPlugin::applyChanges" << endl; - if ( m_rbShare && m_rbUnShare ) + if ( m_rbShare && m_rbUnShare && m_rbSharerw ) { bool share = m_rbShare->isChecked(); @@ -242,7 +275,7 @@ void KFileSharePropsPlugin::applyChanges() bool ok = true; for ( ; it.current() && ok; ++it ) { TQString path = (*it)->url().path(); - ok = setShared( path, share ); + ok = SuSEsetShared( path, share, m_rbSharerw->isChecked() ); if (!ok) { if (share) KMessageBox::detailedError(properties, @@ -269,8 +302,14 @@ void KFileSharePropsPlugin::applyChanges() bool KFileSharePropsPlugin::setShared( const TQString& path, bool shared ) { - kdDebug() << "KFileSharePropsPlugin::setShared " << path << "," << shared << endl; - return KFileShare::setShared( path, shared ); + return SuSEsetShared( path, shared, true ); +} + +bool KFileSharePropsPlugin::SuSEsetShared( const TQString& path, bool shared, bool readonly ) +{ + kdDebug() << "KFileSharePropsPlugin::setShared " << path << "," + << shared << readonly << endl; + return KFileShare::SuSEsetShared( path, shared, readonly ); } TQWidget* KFileSharePropsPlugin::page() const diff --git a/kio/kfile/kfilesharedlg.h b/kio/kfile/kfilesharedlg.h index 25fbc7f67..54876f12a 100644 --- a/kio/kfile/kfilesharedlg.h +++ b/kio/kfile/kfilesharedlg.h @@ -55,10 +55,13 @@ protected slots: private: void init(); bool setShared( const TQString&path, bool shared ); + bool SuSEsetShared( const TQString&path, bool shared, bool readonly ); TQWidget *m_widget; TQRadioButton *m_rbShare; + TQRadioButton *m_rbSharerw; TQRadioButton *m_rbUnShare; + //TQLineEdit *m_leSmbShareName; TQPushButton *m_pbConfig; class Private; Private *d; diff --git a/kio/kfile/kurlbar.cpp b/kio/kfile/kurlbar.cpp index 5c2b167c0..08dd36656 100644 --- a/kio/kfile/kurlbar.cpp +++ b/kio/kfile/kurlbar.cpp @@ -605,8 +605,19 @@ void KURLBar::readItem( int i, KConfig *config, bool applicationLocal ) if ( !url.isValid() || !KProtocolInfo::isKnownProtocol( url )) return; // nothing we could do. + TQString description = config->readEntry( TQString("Description_") + number ); + + if (description.isEmpty() && url.protocol()=="beagle") { + KIO::UDSEntry uds; + const KURL kurl("beagle:?beagled-status"); + if (!KIO::NetAccess::stat(kurl, uds)) + return; + + description = i18n("Desktop Search"); + } + insertItem( url, - config->readEntry( TQString("Description_") + number ), + description, applicationLocal, config->readEntry( TQString("Icon_") + number ), static_cast( diff --git a/kio/kio/kdirwatch.cpp b/kio/kio/kdirwatch.cpp index 08ab25b89..38cd8f866 100644 --- a/kio/kio/kdirwatch.cpp +++ b/kio/kio/kdirwatch.cpp @@ -725,7 +725,8 @@ bool KDirWatchPrivate::useINotify( Entry* e ) bool KDirWatchPrivate::useStat(Entry* e) { - if (KIO::probably_slow_mounted(e->path)) + if ( e->path.startsWith("/media/") || (e->path == "/media") + || (KIO::probably_slow_mounted(e->path)) ) useFreq(e, m_nfsPollInterval); else useFreq(e, m_PollInterval); diff --git a/kio/kio/kfileshare.cpp b/kio/kio/kfileshare.cpp index 6b9bf361d..1f6dca285 100644 --- a/kio/kio/kfileshare.cpp +++ b/kio/kio/kfileshare.cpp @@ -20,6 +20,7 @@ #include "kfileshare.h" #include #include +#include #include #include #include @@ -35,8 +36,10 @@ #include KFileShare::Authorization KFileShare::s_authorization = NotInitialized; -TQStringList* KFileShare::s_shareList = 0L; -static KStaticDeleter sdShareList; +//TQStringList* KFileShare::s_shareList = 0L; +//static KStaticDeleter sdShareList; +TQMap* KFileShare::s_shareMap = 0L; +static KStaticDeleter > sdShareMap; KFileShare::ShareMode KFileShare::s_shareMode; bool KFileShare::s_sambaEnabled; @@ -165,10 +168,10 @@ bool KFileShare::nfsEnabled() { void KFileShare::readShareList() { KFileSharePrivate::self(); - if ( !s_shareList ) - sdShareList.setObject( s_shareList, new TQStringList ); + if ( !s_shareMap ) + sdShareMap.setObject( s_shareMap, new TQMap ); else - s_shareList->clear(); + s_shareMap->clear(); // /usr/sbin on Mandrake, $PATH allows flexibility for other distributions TQString exe = findExe( "filesharelist" ); @@ -186,29 +189,45 @@ void KFileShare::readShareList() // Reading code shamelessly stolen from khostname.cpp ;) TQString line; + TQString options; + TQString path; int length; + TQRegExp rx_line("([^\\s]+)\\s+(.*)"); do { length = proc.readln(line, true); if ( length > 0 ) { if ( line[length-1] != '/' ) line += '/'; - s_shareList->append(line); + if( rx_line.search( line ) != -1 ) { + options = rx_line.cap(1); + path = rx_line.cap(2); + (*s_shareMap)[path] = options; + } kdDebug(7000) << "Shared dir:" << line << endl; } } while (length > -1); } -bool KFileShare::isDirectoryShared( const TQString& _path ) +int KFileShare::isDirectoryShared( const TQString& _path ) { - if ( ! s_shareList ) + int ret(0); + + if ( ! s_shareMap ) readShareList(); TQString path( _path ); if ( path[path.length()-1] != '/' ) path += '/'; - return s_shareList && s_shareList->contains( path ); + //return s_shareList && s_shareList->contains( path ); + if( (*s_shareMap).contains(path) && !((*s_shareMap)[path].isEmpty()) ) { + ret+=1; + if( (*s_shareMap)[path].find("readwrite") != -1 ) + ret+=2; + } + + return ret; } KFileShare::Authorization KFileShare::authorization() @@ -230,18 +249,31 @@ TQString KFileShare::findExe( const char* exeName ) } bool KFileShare::setShared( const TQString& path, bool shared ) +{ + return SuSEsetShared( path, shared, false ); +} + +bool KFileShare::SuSEsetShared( const TQString& path, bool shared, bool rw ) { if (! KFileShare::sharingEnabled() || KFileShare::shareMode() == Advanced) return false; - kdDebug(7000) << "KFileShare::setShared " << path << "," << shared << endl; TQString exe = KFileShare::findExe( "fileshareset" ); if (exe.isEmpty()) return false; - + + // we want to share, so we kick it first - just to be sure KProcess proc; proc << exe; + proc << "--remove"; + proc << path; + proc.start( KProcess::Block ); + proc.clearArguments(); + + proc << exe; + if( rw ) + proc << "--rw"; if ( shared ) proc << "--add"; else @@ -291,4 +323,24 @@ bool KFileShare::setShared( const TQString& path, bool shared ) return ok; } +bool KFileShare::sambaActive() +{ + // rcsmb is not executable by users, try ourselves + int status = system( "/sbin/checkproc -p /var/run/samba/smbd.pid /usr/sbin/smbd" ); + return status != -1 && WIFEXITED( status ) && WEXITSTATUS( status ) == 0; +} + +bool KFileShare::nfsActive() +{ + // rcnfsserver is not executable by users, try ourselves + int status = system( "/sbin/checkproc /usr/sbin/rpc.mountd" ); + if( status != -1 && WIFEXITED( status ) && WEXITSTATUS( status ) == 0 ) + { + status = system( "/sbin/checkproc -n nfsd" ); + if( status != -1 && WIFEXITED( status ) && WEXITSTATUS( status ) == 0 ) + return true; + } + return false; +} + #include "kfileshare.moc" diff --git a/kio/kio/kfileshare.h b/kio/kio/kfileshare.h index 6753e055a..6fddebd65 100644 --- a/kio/kio/kfileshare.h +++ b/kio/kio/kfileshare.h @@ -67,7 +67,7 @@ public: /** * Call this to know if a directory is currently shared */ - static bool isDirectoryShared( const TQString& path ); + static int isDirectoryShared( const TQString& path ); enum Authorization { NotInitialized, ErrorNotFound, Authorized, UserNotAllowed }; /** @@ -85,7 +85,12 @@ public: * @returns whether the perl script was successful */ static bool setShared( const TQString& path, bool shared ); - + + /* + * SuSE only enhancement for now + */ + static bool SuSEsetShared( const TQString& path, bool shared, bool ro ); + /** * The used share mode. * Simple means that the simple sharing dialog is used and @@ -131,10 +136,23 @@ public: * Returns whether NFS is enabled */ static bool nfsEnabled(); + + /** + * Returns whether Samba is active (service is running) + * @internal + */ + static bool sambaActive(); + + /** + * Returns whether NFS is active (service is running) + * @internal + */ + static bool nfsActive(); private: static Authorization s_authorization; - static TQStringList* s_shareList; +// static TQStringList* s_shareList; + static TQMap* s_shareMap; static ShareMode s_shareMode; static bool s_sambaEnabled; static bool s_nfsEnabled; diff --git a/kio/kio/kmimetype.cpp b/kio/kio/kmimetype.cpp index af4243a53..a698e7495 100644 --- a/kio/kio/kmimetype.cpp +++ b/kio/kio/kmimetype.cpp @@ -677,9 +677,8 @@ TQString KFolderType::comment( const KURL& _url, bool _is_local ) const KURL u( _url ); u.addPath( ".directory" ); - KSimpleConfig cfg( u.path(), true ); - cfg.setDesktopGroup(); - TQString comment = cfg.readEntry( "Comment" ); + KDesktopFile cfg( u.path(), true ); + TQString comment = cfg.readComment(); if ( comment.isEmpty() ) return KMimeType::comment( _url, _is_local ); @@ -772,9 +771,8 @@ TQString KDEDesktopMimeType::comment( const KURL& _url, bool _is_local ) const if ( !_is_local ) return KMimeType::comment( _url, _is_local ); - KSimpleConfig cfg( _url.path(), true ); - cfg.setDesktopGroup(); - TQString comment = cfg.readEntry( "Comment" ); + KDesktopFile cfg( _url.path(), true ); + TQString comment = cfg.readComment(); if ( comment.isEmpty() ) return KMimeType::comment( _url, _is_local ); diff --git a/kio/kio/kremoteencoding.cpp b/kio/kio/kremoteencoding.cpp index ebd7fda74..632eeb8b2 100644 --- a/kio/kio/kremoteencoding.cpp +++ b/kio/kio/kremoteencoding.cpp @@ -53,7 +53,7 @@ TQCString KRemoteEncoding::encode(const TQString& name) const TQCString result = codec->fromUnicode(name); if (codec->toUnicode(result) != name) return name.latin1(); - + return result; } @@ -80,11 +80,13 @@ void KRemoteEncoding::setEncoding(const char *name) if (name) codec = TQTextCodec::codecForName(name); + else + codec = TQTextCodec::codecForMib( 106 ); // fallback to UTF-8 if (codec == 0L) codec = TQTextCodec::codecForMib(1); - kdDebug() << k_funcinfo << "setting encoding " << codec->name() + kdDebug() << k_funcinfo << "setting encoding " << codec->name() << " for name=" << name << endl; } diff --git a/kio/kio/kservice.cpp b/kio/kio/kservice.cpp index 2122010b2..299d91e90 100644 --- a/kio/kio/kservice.cpp +++ b/kio/kio/kservice.cpp @@ -115,7 +115,7 @@ KService::init( KDesktopFile *config ) return; } - m_strName = config->readEntry( "Name" ); + m_strName = config->readName(); entryMap.remove("Name"); if ( m_strName.isEmpty() ) { @@ -135,7 +135,7 @@ KService::init( KDesktopFile *config ) m_strName = m_strName.left(i); } - m_strType = config->readEntry( "Type" ); + m_strType = config->readType(); entryMap.remove("Type"); if ( m_strType.isEmpty() ) { @@ -224,11 +224,11 @@ KService::init( KDesktopFile *config ) entryMap.remove("Terminal"); m_strTerminalOptions = config->readEntry( "TerminalOptions" ); // should be a property IMHO entryMap.remove("TerminalOptions"); - m_strPath = config->readPathEntry( "Path" ); + m_strPath = config->readPath(); entryMap.remove("Path"); - m_strComment = config->readEntry( "Comment" ); + m_strComment = config->readComment(); entryMap.remove("Comment"); - m_strGenName = config->readEntry( "GenericName" ); + m_strGenName = config->readGenericName(); if (kde4application) { m_strGenName += " [KDE4]"; } @@ -275,7 +275,10 @@ KService::init( KDesktopFile *config ) m_bAllowAsDefault = config->readBoolEntry( "AllowDefault", true ); entryMap.remove("AllowDefault"); - m_initialPreference = config->readNumEntry( "InitialPreference", 1 ); + m_initialPreference = config->readNumEntry( "X-KDE-InitialPreference", 1 ); + entryMap.remove("X-KDE-InitialPreference"); + if ( m_initialPreference == 1 ) + m_initialPreference = config->readNumEntry( "InitialPreference", 1 ); entryMap.remove("InitialPreference"); // Store all additional entries in the property map. diff --git a/kio/kio/kservicegroup.cpp b/kio/kio/kservicegroup.cpp index 1531dc796..be093fc17 100644 --- a/kio/kio/kservicegroup.cpp +++ b/kio/kio/kservicegroup.cpp @@ -64,13 +64,11 @@ KServiceGroup::KServiceGroup( const TQString &configFile, const TQString & _relp d->directoryEntryPath = cfg; - KConfig config( cfg, true, false, "apps" ); + KDesktopFile config( cfg, true, "apps" ); - config.setDesktopGroup(); - - m_strCaption = config.readEntry( "Name" ); - m_strIcon = config.readEntry( "Icon" ); - m_strComment = config.readEntry( "Comment" ); + m_strCaption = config.readName(); + m_strIcon = config.readIcon(); + m_strComment = config.readComment(); m_bDeleted = config.readBoolEntry( "Hidden", false ); d->m_bNoDisplay = config.readBoolEntry( "NoDisplay", false ); TQStringList tmpList; diff --git a/kio/kio/netaccess.cpp b/kio/kio/netaccess.cpp index 1c54afa74..5189b93d6 100644 --- a/kio/kio/netaccess.cpp +++ b/kio/kio/netaccess.cpp @@ -337,7 +337,7 @@ bool NetAccess::statInternal( const KURL & url, int details, bool source, TQWidget* window ) { bJobOK = true; // success unless further error occurs - KIO::StatJob * job = KIO::stat( url, !url.isLocalFile() ); + KIO::StatJob * job = KIO::stat( url, !url.isLocalFile() && !url.url().startsWith("beagle:?") ); job->setWindow (window); job->setDetails( details ); job->setSide( source ); diff --git a/kio/kssl/kopenssl.cc b/kio/kssl/kopenssl.cc index 9252bd223..ababf37a0 100644 --- a/kio/kssl/kopenssl.cc +++ b/kio/kssl/kopenssl.cc @@ -96,9 +96,14 @@ static int (*K_BIO_write) (BIO *b, const void *data, int len) = 0L; static int (*K_PEM_ASN1_write_bio) (int (*)(),const char *,BIO *,char *, const EVP_CIPHER *,unsigned char *,int , pem_password_cb *, void *) = 0L; +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +static int (*K_ASN1_item_i2d_fp)(ASN1_ITEM *,FILE *,unsigned char *) = 0L; +static ASN1_ITEM *K_NETSCAPE_X509_it = 0L; +#else static ASN1_METHOD* (*K_X509_asn1_meth) (void) = 0L; static int (*K_ASN1_i2d_fp)(int (*)(),FILE *,unsigned char *) = 0L; static int (*K_i2d_ASN1_HEADER)(ASN1_HEADER *, unsigned char **) = 0L; +#endif static int (*K_X509_print_fp) (FILE *, X509*) = 0L; static int (*K_i2d_PKCS12) (PKCS12*, unsigned char**) = 0L; static int (*K_i2d_PKCS12_fp) (FILE *, PKCS12*) = 0L; @@ -404,9 +409,14 @@ KConfig *cfg; K_BIO_ctrl = (long (*) (BIO *,int,long,void *)) _cryptoLib->symbol("BIO_ctrl"); K_BIO_write = (int (*) (BIO *b, const void *data, int len)) _cryptoLib->symbol("BIO_write"); K_PEM_ASN1_write_bio = (int (*)(int (*)(), const char *,BIO*, char*, const EVP_CIPHER *, unsigned char *, int, pem_password_cb *, void *)) _cryptoLib->symbol("PEM_ASN1_write_bio"); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + K_ASN1_item_i2d_fp = (int (*)(ASN1_ITEM *, FILE*, unsigned char *)) _cryptoLib->symbol("ASN1_item_i2d_fp"); + K_NETSCAPE_X509_it = (ASN1_ITEM *) _cryptoLib->symbol("NETSCAPE_X509_it"); +#else K_X509_asn1_meth = (ASN1_METHOD* (*)(void)) _cryptoLib->symbol("X509_asn1_meth"); K_ASN1_i2d_fp = (int (*)(int (*)(), FILE*, unsigned char *)) _cryptoLib->symbol("ASN1_i2d_fp"); K_i2d_ASN1_HEADER = (int (*)(ASN1_HEADER *, unsigned char **)) _cryptoLib->symbol("i2d_ASN1_HEADER"); +#endif K_X509_print_fp = (int (*)(FILE*, X509*)) _cryptoLib->symbol("X509_print_fp"); K_i2d_PKCS12 = (int (*)(PKCS12*, unsigned char**)) _cryptoLib->symbol("i2d_PKCS12"); K_i2d_PKCS12_fp = (int (*)(FILE *, PKCS12*)) _cryptoLib->symbol("i2d_PKCS12_fp"); @@ -568,7 +578,7 @@ KConfig *cfg; K_SSL_set_session = (int (*)(SSL*,SSL_SESSION*)) _sslLib->symbol("SSL_set_session"); K_d2i_SSL_SESSION = (SSL_SESSION* (*)(SSL_SESSION**,unsigned char**, long)) _sslLib->symbol("d2i_SSL_SESSION"); K_i2d_SSL_SESSION = (int (*)(SSL_SESSION*,unsigned char**)) _sslLib->symbol("i2d_SSL_SESSION"); - K_SSL_get_ciphers = (STACK *(*)(const SSL*)) _sslLib->symbol("SSL_get_ciphers"); + K_SSL_get_ciphers = (STACK_OF(SSL_CIPHER) *(*)(const SSL*)) _sslLib->symbol("SSL_get_ciphers"); #endif @@ -956,7 +966,13 @@ int KOpenSSLProxy::PEM_write_bio_X509(BIO *bp, X509 *x) { else return -1; } - +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +int KOpenSSLProxy::ASN1_i2d_fp(FILE *out,unsigned char *x) { + if (K_ASN1_item_i2d_fp && K_NETSCAPE_X509_it) + return (K_ASN1_item_i2d_fp)(K_NETSCAPE_X509_it, out, x); + else return -1; +} +#else ASN1_METHOD *KOpenSSLProxy::X509_asn1_meth(void) { if (K_X509_asn1_meth) return (K_X509_asn1_meth)(); else return 0L; @@ -968,7 +984,7 @@ int KOpenSSLProxy::ASN1_i2d_fp(FILE *out,unsigned char *x) { return (K_ASN1_i2d_fp)((int (*)())K_i2d_ASN1_HEADER, out, x); else return -1; } - +#endif int KOpenSSLProxy::X509_print(FILE *fp, X509 *x) { if (K_X509_print_fp) return (K_X509_print_fp)(fp, x); diff --git a/kio/kssl/kopenssl.h b/kio/kssl/kopenssl.h index 68d6733cd..e4f6de0e8 100644 --- a/kio/kssl/kopenssl.h +++ b/kio/kssl/kopenssl.h @@ -48,6 +48,9 @@ class KOpenSSLProxyPrivate; #include #include #undef crypt +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#define STACK _STACK +#endif #endif #include @@ -446,12 +449,12 @@ public: */ int PEM_write_bio_X509(BIO *bp, X509 *x); - +#if OPENSSL_VERSION_NUMBER < 0x10000000L /* * X509_asn1_meth - used for netscape output */ ASN1_METHOD *X509_asn1_meth(); - +#endif /* * ASN1_i2d_fp - used for netscape output @@ -531,6 +534,9 @@ public: */ void sk_free(STACK *s); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + void sk_free(void *s) { return sk_free(reinterpret_cast(s)); } +#endif /* * Number of elements in the stack @@ -543,6 +549,9 @@ public: */ char *sk_value(STACK *s, int n); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + char *sk_value(void *s, int n) { return sk_value(reinterpret_cast(s), n); } +#endif /* * Create a new stack @@ -555,6 +564,9 @@ public: */ int sk_push(STACK *s, char *d); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + int sk_push(void *s, void *d) { return sk_push(reinterpret_cast(s), reinterpret_cast(d)); } +#endif /* * Duplicate the stack diff --git a/kio/kssl/ksmimecrypto.cc b/kio/kssl/ksmimecrypto.cc index c96f9ec6d..1a9e37e60 100644 --- a/kio/kssl/ksmimecrypto.cc +++ b/kio/kssl/ksmimecrypto.cc @@ -87,7 +87,7 @@ KSMIMECryptoPrivate::KSMIMECryptoPrivate(KOpenSSLProxy *kossl): kossl(kossl) { STACK_OF(X509) *KSMIMECryptoPrivate::certsToX509(TQPtrList &certs) { - STACK_OF(X509) *x509 = sk_new(NULL); + STACK_OF(X509) *x509 = reinterpret_cast(sk_new(NULL)); KSSLCertificate *cert = certs.first(); while(cert) { sk_X509_push(x509, cert->getCert()); diff --git a/kio/kssl/ksslcertificate.cc b/kio/kssl/ksslcertificate.cc index a76b235d7..73a8451ca 100644 --- a/kio/kssl/ksslcertificate.cc +++ b/kio/kssl/ksslcertificate.cc @@ -1003,17 +1003,31 @@ return qba; TQByteArray KSSLCertificate::toNetscape() { TQByteArray qba; #ifdef KSSL_HAVE_SSL -ASN1_HEADER ah; -ASN1_OCTET_STRING os; -KTempFile ktf; +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + NETSCAPE_X509 nx; + ASN1_OCTET_STRING hdr; +#else + ASN1_HEADER ah; + ASN1_OCTET_STRING os; +#endif + KTempFile ktf; - os.data = (unsigned char *)NETSCAPE_CERT_HDR; - os.length = strlen(NETSCAPE_CERT_HDR); - ah.header = &os; - ah.data = (char *)getCert(); - ah.meth = d->kossl->X509_asn1_meth(); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + hdr.data = (unsigned char *)NETSCAPE_CERT_HDR; + hdr.length = strlen(NETSCAPE_CERT_HDR); + nx.header = &hdr; + nx.cert = getCert(); - d->kossl->ASN1_i2d_fp(ktf.fstream(),(unsigned char *)&ah); + d->kossl->ASN1_i2d_fp(ktf.fstream(),(unsigned char *)&nx); +#else + os.data = (unsigned char *)NETSCAPE_CERT_HDR; + os.length = strlen(NETSCAPE_CERT_HDR); + ah.header = &os; + ah.data = (char *)getCert(); + ah.meth = d->kossl->X509_asn1_meth(); + + d->kossl->ASN1_i2d_fp(ktf.fstream(),(unsigned char *)&ah); +#endif ktf.close(); diff --git a/kio/misc/kwalletd/kwalletd.cpp b/kio/misc/kwalletd/kwalletd.cpp index fd39f8487..069575710 100644 --- a/kio/misc/kwalletd/kwalletd.cpp +++ b/kio/misc/kwalletd/kwalletd.cpp @@ -355,6 +355,44 @@ int KWalletD::doTransactionOpen(const TQCString& appid, const TQString& wallet, return rc; } +int KWalletD::tryOpen(const TQString& wallet, const TQCString& password) +{ + if (isOpen(wallet)) + return 0; + + if (_tryOpenBlocked.isActive()) { + kdDebug() << "tryOpen is active.." << endl; + return -1; + } + + if (!KWallet::Backend::exists(wallet)) + return -2; + + KWallet::Backend *b = new KWallet::Backend(wallet, false /*isPath*/); + int rc = b->open(TQByteArray().duplicate(password, strlen(password))); + if (rc == 0) { + _wallets.insert(rc = generateHandle(), b); + _passwords[wallet] = password; + b->ref(); + _tryOpenBlocked.stop(); + TQByteArray data; + TQDataStream ds(data, IO_WriteOnly); + ds << wallet; + emitDCOPSignal("walletOpened(TQString)", data); + } + else { + delete b; + // make sure that we're not bombed with a dictionary attack + _tryOpenBlocked.start (30 * 1000, true /*single shot*/); + if (++_failed > 5) { + _failed = 0; + TQTimer::singleShot(0, this, TQT_SLOT(notifyFailures())); + } + + rc = -1; + } + return rc; +} int KWalletD::internalOpen(const TQCString& appid, const TQString& wallet, bool isPath, WId w, bool modal) { int rc = -1; diff --git a/kio/misc/kwalletd/kwalletd.h b/kio/misc/kwalletd/kwalletd.h index b426e7d5a..2aea371ba 100644 --- a/kio/misc/kwalletd/kwalletd.h +++ b/kio/misc/kwalletd/kwalletd.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "kwalletbackend.h" @@ -51,8 +52,10 @@ class KWalletD : public KDEDModule { // Open and unlock the wallet virtual int open(const TQString& wallet, uint wId); - + // Open and unlock the wallet + virtual int tryOpen(const TQString& wallet, const TQCString& passwd); // Open and unlock the wallet with this path + virtual int openPath(const TQString& path, uint wId); // Asynchronous open - must give the object to return the handle @@ -186,6 +189,7 @@ class KWalletD : public KDEDModule { int _idleTime; TQMap _implicitAllowMap, _implicitDenyMap; KTimeout *_timeouts; + TQTimer _tryOpenBlocked; TQPtrList _transactions; TQGuardedPtr< TQWidget > activeDialog; diff --git a/kioslave/file/Makefile.am b/kioslave/file/Makefile.am index 7cafcb339..0f26c723a 100644 --- a/kioslave/file/Makefile.am +++ b/kioslave/file/Makefile.am @@ -10,7 +10,7 @@ kde_module_LTLIBRARIES = kio_file.la kio_file_la_SOURCES = file.cc kio_file_la_LIBADD = $(LIB_KIO) $(LIB_QT) $(LIB_KDECORE) $(ACL_LIBS) -kio_file_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kio_file_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) $(top_builddir)/dcop/libDCOP.la noinst_HEADERS = file.h fileinclude_HEADERS = file.h diff --git a/kioslave/file/file.cc b/kioslave/file/file.cc index 1fde27a81..441d03b98 100644 --- a/kioslave/file/file.cc +++ b/kioslave/file/file.cc @@ -69,6 +69,7 @@ #include #include +#include #include #include #include @@ -1617,6 +1618,31 @@ void FileProtocol::unmount( const TQString& _point ) #endif /* HAVE_VOLMGT */ err = testLogFile( tmp ); + + if (err.contains("fstab") || err.contains("root")) { + TQString olderr; + err = TQString::null; + + DCOPRef d("kded", "mediamanager"); + d.setDCOPClient ( dcopClient() ); + DCOPReply reply = d.call("properties", _point); + TQString udi; + + if ( reply.isValid() ) { + TQStringList list = reply; + if (list.size()) + udi = list[0]; + } + + if (!udi.isEmpty()) + reply = d.call("unmount", udi); + + if (udi.isEmpty() || !reply.isValid()) + err = olderr; + else if (reply.isValid()) + reply.get(err); + } + if ( err.isEmpty() ) finished(); else diff --git a/kioslave/ftp/ftp.cc b/kioslave/ftp/ftp.cc index ffc324c39..9e4fc21d9 100644 --- a/kioslave/ftp/ftp.cc +++ b/kioslave/ftp/ftp.cc @@ -1275,6 +1275,16 @@ bool Ftp::ftpRename( const TQString & src, const TQString & dst, bool overwrite return false; } + // Must check if dst already exists, RNFR+RNTO overwrites by default (#127793). + if (ftpFileExists(dst)) { + error(ERR_FILE_ALREADY_EXIST, dst); + return false; + } + if (ftpFolder(dst, false)) { + error(ERR_DIR_ALREADY_EXIST, dst); + return false; + } + int pos = src.findRev("/"); if( !ftpFolder(src.left(pos+1), false) ) return false; @@ -2406,6 +2416,19 @@ bool Ftp::ftpSize( const TQString & path, char mode ) return true; } +bool Ftp::ftpFileExists(const TQString& path) +{ + TQCString buf; + buf = "SIZE "; + buf += remoteEncoding()->encode(path); + if( !ftpSendCmd( buf ) || (m_iRespType != 2) ) + return false; + + // skip leading "213 " (response code) + const char* psz = ftpResponse(4); + return psz != 0; +} + // Today the differences between ASCII and BINARY are limited to // CR or CR/LF line terminators. Many servers ignore ASCII (like // win2003 -or- vsftp with default config). In the early days of diff --git a/kioslave/ftp/ftp.h b/kioslave/ftp/ftp.h index b2ce56492..44f0bdc71 100644 --- a/kioslave/ftp/ftp.h +++ b/kioslave/ftp/ftp.h @@ -347,6 +347,11 @@ private: */ bool ftpSize( const TQString & path, char mode ); + /** + * Set the current working directory, but only if not yet current + */ + bool ftpFileExists(const TQString& path); + /** * Set the current working directory, but only if not yet current */ diff --git a/kresources/kresources.desktop b/kresources/kresources.desktop index 6c7739bdc..ec30b3885 100644 --- a/kresources/kresources.desktop +++ b/kresources/kresources.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Exec=kcmshell kresources -Icon=date +Icon=about_kde Type=Application Terminal=false diff --git a/mimetypes/application/ogg.desktop b/mimetypes/application/ogg.desktop index bf3f8bf10..be94dfaa7 100644 --- a/mimetypes/application/ogg.desktop +++ b/mimetypes/application/ogg.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Type=MimeType MimeType=application/ogg -Icon=multimedia +Icon=sound Patterns=*.ogg;*.OGG; # Previous name of this mimetype X-KDE-IsAlso=application/x-ogg diff --git a/mimetypes/application/vnd.oasis.opendocument.formula.desktop b/mimetypes/application/vnd.oasis.opendocument.formula.desktop index 3f3bb95eb..4f61a49f4 100644 --- a/mimetypes/application/vnd.oasis.opendocument.formula.desktop +++ b/mimetypes/application/vnd.oasis.opendocument.formula.desktop @@ -2,7 +2,7 @@ Type=MimeType MimeType=application/vnd.oasis.opendocument.formula Patterns=*.odf;*.ODF -Icon=formula +Icon=kformula_kfo Comment=OASIS OpenDocument Formula Comment[af]=OASIS Oop Dokument formule Comment[be]=Формула OASIS OpenDocument diff --git a/mimetypes/application/vnd.sun.xml.base.desktop b/mimetypes/application/vnd.sun.xml.base.desktop index 4b4aa7425..581456933 100644 --- a/mimetypes/application/vnd.sun.xml.base.desktop +++ b/mimetypes/application/vnd.sun.xml.base.desktop @@ -67,7 +67,7 @@ Comment[vi]=Cơ sở dữ liệu OpenOffice.org Comment[zh_CN]=OpenOffice.org 数据库 Comment[zh_HK]=OpenOffice.org 資料庫 Comment[zh_TW]=OpenOffice.org 資料庫 -Icon=spreadsheet +Icon=database Type=MimeType Patterns=*.odb;*.ODB; MimeType=application/vnd.sun.xml.base diff --git a/mimetypes/application/x-bittorrent.desktop b/mimetypes/application/x-bittorrent.desktop index 9981ef65b..4c166a114 100644 --- a/mimetypes/application/x-bittorrent.desktop +++ b/mimetypes/application/x-bittorrent.desktop @@ -1,4 +1,5 @@ [Desktop Entry] +Icon=torrent Comment=BitTorrent Download Comment[af]=BitTorrent Aflaai Comment[ar]=ملف تنزيل BitTorrent diff --git a/mimetypes/application/x-msdos-program.desktop b/mimetypes/application/x-msdos-program.desktop index 508cc737e..5d380bf9a 100644 --- a/mimetypes/application/x-msdos-program.desktop +++ b/mimetypes/application/x-msdos-program.desktop @@ -85,5 +85,5 @@ MimeType=application/x-msdos-program X-KDE-IsAlso=application/x-executable [Property::X-KDE-NativeExtension] Type=QString -Value=.exe +Value=.exe;.EXE;.ExE;.EXe;.eXE;.exE diff --git a/mimetypes/application/x-ogg.desktop b/mimetypes/application/x-ogg.desktop index c91a8400f..e96d9af3c 100644 --- a/mimetypes/application/x-ogg.desktop +++ b/mimetypes/application/x-ogg.desktop @@ -4,7 +4,7 @@ # application/ogg "inherits" from it, so that apps associated with x-ogg can open ogg files. Type=MimeType MimeType=application/x-ogg -Icon=multimedia +Icon=sound Comment=Ogg Multimedia Comment[ar]=وسائط Ogg متعددة Comment[az]=Ogg Multimediya diff --git a/mimetypes/application/x-rpm.desktop b/mimetypes/application/x-rpm.desktop index 38af4822f..399c97a1f 100644 --- a/mimetypes/application/x-rpm.desktop +++ b/mimetypes/application/x-rpm.desktop @@ -1,4 +1,5 @@ [Desktop Entry] +X-KDE-AutoEmbed=true Type=MimeType MimeType=application/x-rpm Icon=rpm diff --git a/mimetypes/text/rtf.desktop b/mimetypes/text/rtf.desktop index 93582979a..0fa173e8c 100644 --- a/mimetypes/text/rtf.desktop +++ b/mimetypes/text/rtf.desktop @@ -77,7 +77,7 @@ Comment[wa]=Documint RTF Comment[zh_CN]=RTF 文档 Comment[zh_HK]=RTF 文件 Comment[zh_TW]=RTF 文件 -Icon=wordprocessing +Icon=rtf Type=MimeType Patterns=*.rtf;*.RTF; [Property::X-KDE-text] diff --git a/pics/crystalsvg/cr128-mime-database.png b/pics/crystalsvg/cr128-mime-database.png new file mode 100644 index 0000000000000000000000000000000000000000..54a9f648463e90a517e3becacf601521dd76ea9f GIT binary patch literal 6866 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 literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr128-mime-drawing.png b/pics/crystalsvg/cr128-mime-drawing.png new file mode 100644 index 0000000000000000000000000000000000000000..daebcc5d64c04f00554eba4471d01bb614f79b0a GIT binary patch literal 6595 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 BhT#AJ literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr128-mime-presentation.png b/pics/crystalsvg/cr128-mime-presentation.png new file mode 100644 index 0000000000000000000000000000000000000000..c66f732c9080d4235b18f96fe6f98dc7d5ea426b GIT binary patch literal 5779 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 Rl@b5|002ovPDHLkV1hcJEWQ8$ literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr128-mime-rtf.png b/pics/crystalsvg/cr128-mime-rtf.png new file mode 100644 index 0000000000000000000000000000000000000000..fd77cc495708c54815d49798bad9d9a3e17dd5cc GIT binary patch literal 6096 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 literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr16-mime-rtf.png b/pics/crystalsvg/cr16-mime-rtf.png new file mode 100644 index 0000000000000000000000000000000000000000..d2bed0b59eb5c93753507275f69777b01c29efd0 GIT binary patch literal 750 zcmVuNNbL=r(7+E8n3D?#!U^44!41o{p7l8;dO724+}R-c?97Dtu>uzIniryx#ot zpnKw)AqZX-uMmJRL;{46+gj*=fU(Rq^}r@26RwwIvMl524^KGi_X!~YAR=5O)oP8^ z)m19xO0Yv`2+IEce{An;1JLUo;hdw@8pAtJWFr97D_3Wx(Vzg>^O?u1p16_aMkB#_ z&&JjkQxgqFY6-i0hm>MVE14*&N_ZGru&R~BZ=fO4?gXK{WSTi4SV zi{!nE2(6l@7M~@!*{bs6FMEu}C#?N(!2ORKOg24(Qpoucf-NqH2;((ns_vOyS)n{S z$>FP?nY-e6{M|ZVJ-osEwHR=8@kP;sgYyV(-DnZdEP%0$e|VRM{zM}QEYDd!sOIQM z{pp0{ubs}miOiUDj%VM0ORsnM-q?CaC)8^RQDgv^nwa3rM_*%&?aKB}XFsyh7=ZWg z!nt20#zd&st{uWxRd)Z~y)-~I;9PcUkB&BoBU{QjFa`nQxb%KxzLaEH%15{F6k8f_ z&hdKV)o*W&@uI~QV=zWgjqu*%e1`X)c6;mBFP84DSV1r%h%r=>eW6jpN(051^aNg1Gv|rx+^z%=KetUe<9{}m4>=oiNXE*=e gIR0#L`SyAL|K>0wh~1fw4FCWD07*qoM6N<$f)7t+kpKVy literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr22-mime-database.png b/pics/crystalsvg/cr22-mime-database.png new file mode 100644 index 0000000000000000000000000000000000000000..a9f512c41cb79993cafe11fcaca770b3426532be GIT binary patch literal 964 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 KMNUMnLSTYrv9`JZ literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr22-mime-rtf.png b/pics/crystalsvg/cr22-mime-rtf.png new file mode 100644 index 0000000000000000000000000000000000000000..6a5c95b98a803399c91a02e73e8059be7d15758b GIT binary patch literal 996 zcmVX1^@s6HR9gx00004b3#c}2nYxW zdo_nsx7I^hU0bPs_xt8(XoSeBcqF>+(PO_Nvi<9HqR0CMFC{E~ z-m1C)+9bm!Bhe!ZkxBl8`hXo8h^(Nr09Y5`i@9Meg0*ozHp$9D;!E_-*IGa)Rx)p6 zT_Io{T!4w3+bziYsqUp;W-ecAcOd;`tu=k$g{wo)H{r!>heEg<;{^Y@Wg~@!d@|>o z_#=zx)u(*@@ih0p-RAD~To(#Z3pE=_#A1(#SOnaZApQilRRgKV{%jt{Dbd!Jm8~|e z>OUJ-fpp3mgQc(k{# zgwf6dV+_U^UROi}$8lnYHrt14T&?b4j6G)i$>RINc$o~)>Ej--Hs(xVi>1|S^=&s8 z!YIUO;~sH%{e69$=qTj!r7NV;>E!Kq7XT~#vje7r;dZE=V>D$JE$Kx#Y4w z0IgQD8&DX9l*^^%`5OzDJ8KVh#puY`X;ny4i}yrPM7dn5>^Jr=^-jA$+<#~8uy~;( zirrVcR4Okv_V@QL-no6RkKnGZRjP%F$-T2;I}!wev$|GEHJgp%o!j^H;Qaq1y@$O{ SBEV7r0000zS&Dk?ApDO^Bs)6ZI8jLAd)@toK!!sABGTuB5bOwniVeYIQx15204E=r zU@GH1c0mwrE&{`7&!9yW1A4K@}t=oM6-4Fcz_cEqwVwyd5 z&+FyS%*+YO&y^_@4*$CV>h(GwfAk5qWpm`n5zak%4iUlkeYDnSt?_-It*tFSyZAX1 z6Di($=S>{P8Dm+-=RmDi_olYat z^NiLS+qMDdbUG|8Em5sjBi&fLeZD!w8A&o!-)prRwOTDQo=T+vXt&#lZ0`dif?=pw zyCIq3;-R~DA&udSNbE5i=)`+q2te-~jDu=f7P(vwfJUQ1x7)?{{hkri#I|iFCnr%# zQLR?#bUOW5$JkX0kT3`0ZE7^;z@*=nH- z0}&yg&y&yRNhA`n3mS$&Hk)O7db(%UP}J*n?$>)4-ktUZCFWikpnacHr%v+6!k^S? zH8PnDr%#{8aU6mmhze6OnIw@&AR;U;FH@;hXf~V7%+BC@9)YhxWLrWHLJ%eFZU~lT zbM}d|T)Vc2>$(6eEG$Ixwryh=2Axi4+c9i38W;(KW79{m9Ghe+g%RU9GhBSsaXXMX zz~MrH*WY-JFE4$?o!fV?EGyb^m~dfhc3l^@<1#xt!&B$aVcRy2E2W*<6lXK2Nb!qSf+WOsXl{8%Z?#f+p-S)-jpMBxos1S{-Shi(TDwZe}OGB;KCkQl#k-#() z(KT&-eQl%-5n*+8Rb03I#awy*Z3vsu9tCTc5x}%eOv@yhiV+%NOGK!ytx3DpzBE^! ze>LQrdur{^_P(Unt5urK=J#{u`4@-zW~dO?bROhuCvN%m>N<@^th*Q@PT`n6M1PD^WjVw~(h>U8u)W*p}C P00000NkvXXu0mjfJ)*R8 literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr32-mime-drawing.png b/pics/crystalsvg/cr32-mime-drawing.png new file mode 100644 index 0000000000000000000000000000000000000000..b35b4d559ee86c127b9e8ad604c598f3af59276d GIT binary patch literal 1543 zcmV+i2Kf1jP)Qyk1rDe}LZ~N%#6N%laYGyte*thp z91ud{!T~rS@k^-USE0}}rj64UNGOi&#J^(i+UtFfgMY0P$95|u#v1SLym|AT_vX#a zuK4ns&r^MU{Urc_pov7|-!*{wgM(wqB?fq_AVe>5D8hVp}_LB2-zl1XgK zB3>*q;PbIP7^G)nf~&g5%uo{*BcSd5Ha=G)J|=n5P_6I2?F z5Di{m|C<}vSGfGs&jfk{+;`tF+UZlIEDNO+vdWH>I8suU6320Hq~!diOB|UPBf1i8 zvbl??6r`3rkj-S7dgvH_zn|gZApk=2=UE#$Q~{Hcauv|cdB@?e=_&4?p5nlP14LI= zJMKW`3y_WoG>tTs?bc=44B6Jr7Nhzx)f{&COllWu-`*IKkQJoH^arrcrUL#(R@2m+@z^l-AdA z90zFhKBW|Tsf0fefG!qNS&aZxB@0CLxgG!g$)^l^d$8Bm8mZ^XhN1O!{`u;wj&|x? z^~3ch+5T6)dJQd|rd%lO0xwI+P(F|L);rvZu6DHL4wF{jjt*?(a+KEAT7uV#(6QJC zk(XbkV47XoR0yTHPrzPmCX@J!MT+a|dlsRxlt?L={_QG1yzwR|)zNO{5Z3P693Fc+ zk6tWcr&9GpS3iWVY*0$k1C*B|B$Ati!=a96(Wrs9OGE!qm<=KDN!b>>CifmZNGg@Y zvhuF46j5DcXQ+dc9PJnqO9x zQe0n&D643_>#Bt9^LOCs3nae#p1{)5t|H{78>6H2&CH3iiR;=y1`@G`J`>?q#XU*pL5SWD-WQfzE&D9bWGT9{va0cy=i zbXUMeFFb{J;2_br-@(4RgkCU_UJusbAmwupGyeQ@JUBH)Hk)nb8`&O%^@9!bSi}eJRT2$Kmc8@bir&3TggO%d_Mp6!u;Y>Zn;qd-#ZIh zM_V^^g6{tAqMeqKi6qQq+p138HEfP>^`2WJpB>MHQkTfrO~k4}iosfN#JfkEjnI zA@P8?sHhb}Eus{al(e~&G%2MdaV}2cBu(ttUi%CW_HLZSYr6%hjCA(w&Y3gwpP6&! zoK-(wxwNuxUwsxpq1Z-h>sc~Z!lh(}v1@#5Y@1&HV`X(YVl_543w4KDC+F7@K#M}7 z5CK7+$61T@i7C=XfL(a|CmI5hF0f z0AjHinRJGpo*qOBIaaDrOGE?_@xMLtSl=681*=&}qo2lqMNOb_Akj;q`CpdcaD30R*_y)aq*L7J~Sn%i6*Vhw?L}+Pk zsiwX<0K+h7Yiq-C9F$V5Opdes-4|r%rm2g*##5)?!nSQ18X9~+9?+0&R|lXd95g%g z=#f$i5y3DF)@P=u`|w>_lL@qGA%FeCt)Xl5e)KWf9NQzyAa0BZmOk*x2w}CX)d`Yh678MK~2B0EJW>d;2UGf4#!N{nH$F ze&gAj-}7?kVQkx`xw+YwbsUFmHj7BGUhlZJym0Kg3;1%%-)f<7nAbn~jHRi+S)QHc z^HY6%jgllkR1@IF;1CbiA8`HpAjwqf;dFQodd34QRvAieB>DNLt8At>IrHWl zj0}%*@xmo~dXLl9-Noi+ip9lvvD&FJy&`1WB$MmT)-I^g_?__y?u<|1=CZtc>J^lN zp59~Fw#n}|hPZd{9?$jl@^tqzgzONg!Vij10-(8Zb5Jsw?58D_Fc^Z?np`f&?XlbZ zaeEvm;t+i)%0@DYXaS{YfAS!Ctr;5`V{~MUj>AXTNUi%jr4+%`^ps>W>GLOF9QZ&D zkKRmlc62ptrc)I?*IJ{sCZEq2S}sps7YdVM7?_5MVHR#kt7|KC9PVT@liC45n3

  • 69KYTl9XPpD);|q{U8}+80E{BS!ooc9#rQ~n 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 literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr48-mime-presentation.png b/pics/crystalsvg/cr48-mime-presentation.png new file mode 100644 index 0000000000000000000000000000000000000000..f2a50c9334ed176cdcbf67104c515de2c059176e GIT binary patch literal 2192 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 Z_+Qg-5U)vmb^-tZ002ovPDHLkV1jjfF9!es literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr64-mime-drawing.png b/pics/crystalsvg/cr64-mime-drawing.png new file mode 100644 index 0000000000000000000000000000000000000000..451395f9cdb505d5442f087b2c31f81275980cba GIT binary patch literal 3151 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 literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr64-mime-presentation.png b/pics/crystalsvg/cr64-mime-presentation.png new file mode 100644 index 0000000000000000000000000000000000000000..d00656fe4ecc600bec3d7103a5f3d18abc5c2cd5 GIT binary patch literal 2755 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 literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/cr64-mime-rtf.png b/pics/crystalsvg/cr64-mime-rtf.png new file mode 100644 index 0000000000000000000000000000000000000000..29ced93d1fb83eb3473e777cdfda09655dfca523 GIT binary patch literal 3035 zcmV<13ncW3P)-b#TCbYbKh&PAF&hs$is0=oCkq~l=4s;K^L}k3q?f&38WTOsX{>2hiLnys8WR} zAKD^5RH~Hn0aX;f(0-soNJ(ia&^E0gB8>tGao+aYj_t(u`n9inryuUFz3;tuH=97y zzjQt8nKLtI&OI~d%$d1j|6krb3#hmd9@1(wR4MnG`fVqd-fLTEz=qC_)wH*_m#8BU zg-rmWAXDVl5&0#{e?&(?dLE zmuh+$vSKM9SGuZ7>$mM9+F~(CDN7ONwJij+S_+PIRs;ZWaX!Iwe>{pKK_Zq@DZxP? zrN9BC6iCYC<)vlvmfOpunfgvanae|d8zuAdcv$(JmzOi9CLIp{xP4h5iVwm6FG61J z0=Vim;HnqE|EIvR6LkU8 z$CoJdIX;cBEUU)=LKceYopn@Xk8^~x+tDNC;Os_Kb9e``P%h6>W70Mcoj z#iaxRL$GUmKOY}I$2)&N$j<9FasAefW$R7P&J(Z z^MtP5(1ET>-gxT+?z?LjmZ>k79X&BhcV`ophdvBV5?@-Ny|oE|sp)w-+nZ6;%IkZ^ zk*jSA=nCjwHJ}2v^mU*s4zIj^fQRqdO-EZZskF`EW9NAE>$i~m_Tz~ZmZ>v4x5Uud zNeo@XG*pr)o72;??Ap4PL&u`rv||Ih2104@6i{jqfFmVRO4J;c-`?LzDmlROFTIUv zXlz>7!JZrYiWXU2Q8;mKf=z2X_-JU9NABBAY(CEHd;%eDJ{pSh#hvTXbp?>1NKzhn zs#yS9jT$gEHP7nyCiI-eZDUUmfM=e#gJ8hOll<6F^OlkfBK|y|$;nC&-bZF$C%5yp9%k{x_RctlQmT($^hiW^R##N26G}B$0@t zDRvR9>2Zy!(Gdd6ci?*kdv56F_xq0V&|QOAriKyBu;HY!w8|{qxVeLKW7GWggJC|u zbqxzkDRvF6p-^L_@Wx+(1_%!i7>er%)$*v=y!+ufwr*(S)qO{?(@B~P2$*U{0M*;L ze@`Eai8LR6GS02rSC^zQy}er33M`)&Rq-CP<%T9}(tr!Ian6j-bMI|yF?5yd`&;?d ztB3f>H+Ry~q$3^Mlj(bP{|WBg-NR#F8>F0{;AG=QLX`9rvpF?;f|f@W%fhxDI)W-| zuj|a@NCtYEc;NQ+JpJ4EQ3OakUDd_@^9Q5Yw#^S7*h(cQ$worDI$t%e0TQc%ofQIv z3PQ;}9TZ5ZBBew+ATxWZJ9n+&jwH?VXUpqlEe6b~c`I&H0Gcd;Wq}odKmYZYJt$e>KP=^w-cf2d9|J75{qa4P3q#9`}WfknFg7XNL6xUe>2a2dkf$B z%~9@u<^#@7EV29AR(|oV&A59T?+dTGtO26rxU!{M&bX?dHDt|TRsaeJ-5s>h1ws{A zTlb)?S`V0@1_29jfFz1OLvZj8lH(tf8a{+n1yTh^1yT~3l03ZU8V;SD<;8uYv<6gO z`u;YW4S$0NP_hEZy)RM%b*upnXbQrRpbF4H>)nF7YCR~epmY?}GQntu^b|(-cEr+o z%cI;7vT5yMaY4r8 zan}NxRsj-(QKce)dr~6;9r>JPgt?H&5x``90$Aadk`mC0DZN0tI%_)!1k!O3GZQ$e zG+5)VK$ZoR`R!gTa(_4y;^&dE8D!iB8-y*vcH3QyuPE^Fg-alSk{7DnB!4sHv6=mc z7f|g&Kl4=PsUIVA1~&|>0^s1VfPZ*!>I-ml|B7;#8dqsgu&M&M1pAJ}nVhM7rLI17 z($wW{nvPtABM1EVr5Glx0+C%?%D~qYXlrk$y{(Ne+;O*WhAuaz0bSGR?dhe`xC+c* zWoW9(SMRx(^W)>iml8jP=;-Vu7%Yq#=ELU{0K+s`-?yP2zDnZ?$un;+;N1!|09U4K z>1zsHNi<49eEFERW3xCv-yl{R1H%kpn&q9df;FJB4kVLFPMx_lw*GRVr@NPHx{4!A z8TdOJGz^0c8#dO>)a67VP<(Wr3twx1K%j^#cb8B~NivyrjaNRr5GDV2|4FCQBom3{ z4=^-MBc6zpPP^WvuPKm7B{_5Uvo!^(Am(cdn1;#5jhh<9OjV4HMd|74rEf!)hs*_A zxstlM<@4qj<~e-i!)(@*zBfZx+Ef~*g<%*Zk}JDJk^Nps$sb7`uR?&7Bre9m9sHx= zP~>%Adx1nU!Rb$l69L%kFvD11n@*S6nPnZ21QNNqQAfFt;>dEbhNfB zx{l*;eCSwq@K1$9k-gb6pH@I3Jq8;af*QJ((qZVtC)o>|PlrR17jnye?FACa&z2RC zQZjU6h=qj(fM>&@$a94>zIKC#X>skq=1a(80jj*=;pjQ>ZaAD3XlGbOaOwWF$)FGZ^3qu%nnvbn5joq?DXJdzzVhKJ8`arz>_o8eI8K`-@u9gUUf)+c>^sKnh!beO3N zQvh#=Ly>#Dwpr8Rs5h1_AEMC_CMPEV{uK^ILVoJ{4vJoK)Tu9;LI{G*EtiJV20;-DGc(gnO-%xf1B2mE zBw0_J8o;U3LrhMdzXU8c7EAG^Y%FgI*c=W;;`O$v0sCUH7_nHa-nK3Wx^X`~x-}e% dT#^Hd{{qZJa<=0@?^OT*002ovPDHLkV1laxw*~+J literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/crsc-mime-database.svgz b/pics/crystalsvg/crsc-mime-database.svgz new file mode 100644 index 0000000000000000000000000000000000000000..579447bcf6a2fed41839b51eb44def6f7242545a GIT binary patch literal 6573 zcmV;e8B*pSiwFp&IcP)x17mV?V=ZlIZDlQFVRT_)VRL0Jb9QF{~`{IG#F*!1MNT)1C&?mNCadjcLJ*l;oyg-8`C~FgS+ZF_Wbv-} zICd$s+5AKbEqP=)5i!P0Fr83328!|Kd9{t6#;eVD&ZE%4(4!P%Jb`;B%4SqIPYYl} zWn=QYZtcMOd-!*J;K;*WU+3^-f>z0P^7)s~lg(&PKE`OaojK(k9`h>p#$y#Nlg)h; zC!1p(`SXV#H_6leI@^w)XZO$=gAvaEf;3MJXy)l6v30(`n%u3I$>g_mxs2k;=VbF` zyS|@1ra*!2S3A}{rnBwc`zx;RpYq>#NqT#?buOf{_gBDYLfF2#1LJOvV8)11za`Es z&i*<}z9tL*+1WatMw?<2pVDO-t^PO4wv#kouQro7dxkl&7{k1{U6)GOb@FMvp3l=b z8PB3^WD7+W79W84-7J}J>_fX=*k3Ha0k;4RNtFFPi)JZIfE|(3fq1=GBr)(IT0BP2 zo2w%Ml;c0jgg6um#cqJZ2U=+C_IUvB3 z2~>=oYxx+!6Hu498cq7uhC{WHaBb>X+q;S1?fq5zD_RuUn7zM}NEv1bITbvA22=@T z2*#40u0FlLq7+an8CA+wUNySRQZZ66NMDOF#mC7XZasP#t+22IDh*f5jM* z@W)SVe1fZ#AYiT};&}C$0Ifoj5k}OBmh5OT=ae;Km=Th2%P=AZ>%=g>FO(A8jA2yt zW>{!)@-~w*;`URRoO@9loHhqJ9GLDijmVU?+rDIqaB%3Wk6k*3DqY2b!k>L6ySb;yl%iV}~_=)OkuY zA?8&lDiqs%s?77hf?h+CGXej2`|ElYpBP7m^6N3oW+S_~j*H&d)k_Q|d`XDy%(BCG!L#$R1b=YwQce z04>cC%CW(?yhiRJ4_(k|!eB{yEf6shOLjfR&>v_z+ogqqmG;vSZcrC(1-B`- zpC+{-#z0Vj#tKy{F+MMefhr`4f!hjt40c<+NREa5G**XHM7*ePPjvR}^$~@_bBmSKHQY9J!f3Y$ zp@=9fu~n5^Vs%oKR>2dFtOag-{jOo*qm6J@8uK9SJV*=b9SlFMDNK%mv@&!(N^i4S z*2Yo%5n8`n}F#iWrz8K3b(hoKab@W&*Uf!vyRN#Z2YGsZP3vbb9V*ZQy1-;*DX` z8_iPNPq1`CUJT83hA7yPZ5Pn)0EfmmIJ$wsZHQ*ViR1?~?|x83G2jSHP>O^y(l7_k z6$T0D97J$1a*6g9d(aQ504xP{QYv5`ETEpXJtZ=>pCHO;jS1Qi z!QO=)2cXpVf?j~uEPy)V+4{8O(a;re4xHbt^^|37{85_gTdk~8S_a(Z3Mj1 zbADu&6Uz5JYuS0rbmRxYl;xv>&)&w+9?J z=B&foCa+{S$In9LjFEYkah6*)HnSV80^bb6 zjA`NXjR_=`aZ_w9-wfEnLThL1N$w^^(g@Yw#UJLIdO5!B_o)S7LnTr|OYScYh}q-D zhYdqiD#B`G!TxyU@U>|`Igkg~1Ovm{XcJ6ZzJ+6-8LX2I!uwcQo?&bd$@_G=YD14U zLBw7^UX6paageq;0(sCbM_{{OxK_-F(-jKeB-gC}R!ZQZyQ{H|*^TtT8D`h@pK_Rw z?3yB2%PP&2F_AW(!1;Bg83UpTQ-a&jtO5pe)s_{R6`Yze;cm~_g^>~FnhNa=J5?Ze z6e$I#i?nf&HpF+~z^xwSO-#`9Ag}1HgdP~;WKh-gwnQ85?H^~5!+hs2$M?P&-?dT! z6dg!=%S$PRX23muO$`VOqVyXV=n@Gjj3NHeb8at#6!C_hfkA?qaQAx$Y2zSmkl~~u z!yBzydk7d~M4rt-kQA_vSIO9JEjsY?bf0p>DAzXcLm-8*?jl@3MB9utcaQyZZi#3G zE6!ypWqFucN0^iX?QhSS=~3QBoEh7Td+!g3}o zlfv>VEbBb6cu|&nx;w=3;WnubXVZkOsj%f0w$j2DooD>LF5BC#s$ZGy4VMDpWy8y{ z{hUM>Z6nrw4N__)u=&#p^wt>y)%>P*GE*sYfo>$n#24tN`&59$#~2hUQ-~3)&1(tt z;WzK*0aTacr6_clj9kkxyPvGE1xO%jj5dBw5YTePMPUnBQz}n~16gF!! zWQcJEb)Mi#kg$%=rnwh`toyUv->NZZrf@);LfoFYR?znvS&zQ-l~kEF4$=l>%;7j0 zA~~hf?kE@9IFX3J$!lCTTIW#LuiVF@eaL)wN3fJ3AIhW;WZZXlNJ~@UA}#F}=+V<- zinZU>Vls9pYdetL-Mz26!uMXvOz$(Q`&?eHtEHshPRs`@$*|vQ;x=&IpPE|jfF_iWg%8%^PS60*4HX)3fuI}^YN>a&7 ztP5q}g;Mo;bhS<{wed4G%?1Eb{LST-8Z9oES_-8PmPZKO@3uGi^)t&q5g}OU7y;;0 z|7{F4+~-|)Ed;pFx@CU9@|F_%Nom7_%whev(BU80XC9tbyz7p+5bIxW!%;u(vmZao zPoKivuQ>&&`TiZP!qbWkb|8<@w9E4PkFcNg2mPbFU&G}O?SD3kQtBfM&n!AvTC@St z-G%$FzL&g?_x%;F;l}V#N;;H&R^e90F&St@p`|&2(Jsf8&56=R`k6pL%9#*O7vmsp z9Hgyw`C()f2WaJI5fFqK$A;L*r6gL}#5W-fReXS`CA6o}`OKEE|H3a|z~<$)*s4&U zy&phm%}HOj@^&Yr&9(gubTYWD{;X+IiBm0+!kUYcgH|zVoP!Q?|2abj>T+zoI7k}@ zX=`yRnP%pIQ_iprCt?KJR8x_E6QEQF0jg*OR4V$f?*gagic0G@gp$a91_CLq+w~SY zP$T~v2%=P;Jrb!}kSZ{6i6VZGjKM{)sT^;_PLP6u!AUVPQKF55wDp3NNG?x;RAB{z zlyb~>2iF-P^)P}|G|IQ3@*?<@ZSWZ}`xy$KsvSOgY(GQcQ*}MEtIWbi{^~Qk4xo_G zzMBi}aiCn{5X0Kn*7eBf-m&DvX-k@e9)hn7pr#G=lNa-i3|zrw6?+CHP)KOM;)O5?wS_jiz2LT+{VoVhB<7CK zJ3qA^hFoH*3gKQ2i%%f--^MVe6qJYLxUI*hJd9GYMktc9RepU_6y?R988)fHo3onk zsa2rpyYH!m+Jk$wn$;f10jT!eQk%cc0G4b<2B zTJZtGa#Ha|y9J~gAZit=9*R9Xj3hLS4M8NPI*zLi*9IUWqC1Efm!js4Q{6!%KoSXz z3m~HQ#TM2w^+3e(*Ifu~3LvuXV}|)4AZir6_tGE+1yKV@L_2H~Rcr?AY_G1MwjPpa zCgy5dZAf4X(A5wiaDfZkr`$yzA zJq#)2ZEAulH(jLpyIevqv-*}$)M$a+5=G&4Nv{_tc?-ePlEB+U++ zkewIZyCiev8RoV25KdkQ zCpv_aBE0;GSLLoA@*Sih?v8EJ5@r~_*-a*dY5DE&_ng?(!>O}l? z!Tcn8Dv^c)guinN%pAjnCg;B8}?t7V?!LG z^hz9iH<=~#P5y1Oo~8Hj?`9P(liAladE7m?*=Fl639w~%O836FdAeBGYxXbRU$e}a zM#p~MN4H5HVFbEeh;e^oy3S@vcDN<;KkwE)2x&7osNe>ZqxEncNL#o=hC}x^chPM9 z2&^dH{C&M%o}!{Ba6IKAP6;YS>E83slA=Z(x5=GuK4cjT=Xeo4C(xz*e}~dHck9R7 z%(7;iJtV~^?Va&-x_%1Zo~`4Dr7ihkho}3eB84s9?dv>RY)ZvErYpEPeoSZEJNr)I z5<~U`cS(ACx3wEIO$7zC6Hr-k)3Qh%78&oB$u^or+vtRi!+H(`u%LN?AeycppiRd>JJ8MI8#`;F?T2&;eV=T;-v0Gzxd1R9Z+OJp=lkST z=&sl-*{mP37=V4Z-QM3!Cif570Ogen$koUAtI*^*bAeubI5T&`D}oJ0F(H2wdrYS2a4|19jwk<2?z8pmA>O9zmEWhH z1%FOKjHc6vV8L1Pe-9}<(W&JBTyKFP!!|a_(yuw+?T~GTZST)_QI>pMq+iNy?pJR9 z>(hGXmYd^lkF!HEiz9U2O%5U{|8;v3Rh|ex%DF`}O+f4YWN(g2cW<-x!~GH(uz&m@ z>(f0V(u-`5Y$xCco<>1LZ%nhedL;TCaWwo%wdB zsBgZTM$=jTdUN}8C-iT~;ioMbT6+Uf+`89(Tc@iVKunUoT}IiLB(udP`|sn;Hp;f% zm1R2fekZH_?|qqhQHvD*cf$|YW)WzqEQ_8!N_%yesC0u5*G}F1nr_l*y0Aa;uZ!gE zEZy7#gKyH6ZTQ>u*Cd-S){lq#M|%f`@jpfs3CXCW)})e@X)Yuk{p4Kc3c*4M3j&|~!u zsQNhnI~lgOZV9*cp!$<#w+)0Uo~-jy8A(}uSSqitr=Mka*i<_eH_6>;3cMF1a_6CjKNhrBJdee z42CW+Z^!Zm4}@y?-D$>=!xMFqk|roJ;8b~?dw zFNUvT({ECQS|1k!?l;IJNeN(G~%Pa~p{RuX$x^Vpr{_JmN0Y;X_n zuED&~^qLq%sDLFNND{b87^lh}W66ks8D@)wi-Iv?uC1(+3QK0IF(8Rx)o24w6mTt_ z2A!^b8d*=c#Pa{!yO!OyVIX+#R~YcAK%$adE+0V9OTVN^j5I*dL(w3=-&yKK%A~EN zMh~vwi?x<0i90)74vBSinU$oBqerx*76IC*lg)5MOy~EmSxJhX8il8nM~{}77PkI% zzU9{R;vmOe+B|5Da#;t}x-#R>cD(G}y?j6?v6U6EXE8!(LII`ganzJl^4|R z(4_n6>K5HkH)_%Sbdwg{lLDl52+M*TJzo?%{ZG<;Metou_l*|Z=Ox5h2W6u1*9q~D zjREh$f6yKoRFGAe7&r8vK|k>d^e2@3CD6kL^a}&sV|*m3TOPeqena{>q{n}QbXFVE zkCA@&<*6pPlow25P5*rJw0yKnFTW!Hzr_qR21WT4Zoc@}^V9Rs?=KPrNe3X$J1Itv zY{eXivEolsL z)~Zq%q2-Ijl9qZ`f?7v_Q1UhmE?Sr zALX8%Y6GPrHvFeJk*)_$X5JXq-9)1*OW%*{Z5C5w!hAd{NLy6XmnF=}v+t9W_m78z zc|0*IzMOh|kHPEZYCPFzI0#W4Fyf0K?8yci!gH{KubC84&)DQuWG%-2=i=14fr=W8 zV4LC6QiDGVfs6BfJ*l{^Kmd+KAo(@9i|n1viwFp9HE2Wt17mV?V=ZlIZDlQFa$$FAZf7oYc4q+OU0HMEHnx7BU!m)~ zq-!h)`=VZ^Qr*ea+{#^QlGJ@nQ50n}t;IvhZ9BjI9#9)Vf)qE~nU0mNNF<2m;KSK) z4*2eGkE`YAOOkEV&HDWnLHKHvtmDl*UEjRF`sL>z#`=wI@%CxGiyp`8?KjS&(81876k|Mrd*_nPN=+Y^K!(cB zB7lilRg&z~mS(V_Slqxo*`)N_2yYuKBQb+k&hcTt>d zPi^GSAAd}f$Gc6o8$ZqOpf?61oc{&OJT@q^NSBFi^ZnK2cC$(*zon~H6i+@S+t0hr z-Q*#K66kKdC*4Cj-`&2y;`;6}|9zXJH@7?ILOOqc1#~8a9hy55Zh8VUMvVGBac*(; z*Lm_KS^Ce;H}Ndm7Q6VEuF`1zYn1IKX}no)Cvo-!YhpQub#b#P)vzDr<9M@Jq;WEy zXVC+!CS+0Z0m{CcCyT9pWfcL-I29v*_nwm`sR zVd%^5X$c*$7sv5txyh#A#2CNLe{W#I)7{gQyuCU<03F>XKwRux%jpYG*p~R!Xwtl` z1*HwzA{A}PQ(NyQ{+Me?^hDoZDUPt#9Me?b%qou=Wm(xCoj5<^AN@*#TK)=-KG!CD}B&tN)&8QK!9upDX)H-$5_rY@1u z{)(1GGG_0uBvOVMLe3R0o`C%cWC+Ic4DrR|`zuO;oe{3iSc-#X<6zk%#DC7B^%lg- z3VI($%j8>v7?VnoYm5M=Sdlj!mW4k+O0hhzI4k<5w^;(B`kMeKE`sVr+iIS`7?SYE ziN_NtK|owdgvS#Ya0*5kQ65jQWijWJH6oZ1l5k5fA_eP2Fn=tR65NbnRP-iTXmRp3 zi!4eoeBg^3WJO5R5K9W{13lQi6+Fn>OzHT zTj*%{UqP;cp2P(Fc2JcC`gEx!(Dsz4g`K1V1k0C9G+LN z&OzNRaTiAmmW1#XuDv6!E|RHco^pECk+Wd3j0wO`ORl8{RWM`-r9$|&P^d7?C6J<0 zaM}`$0i3U*U6ww6i;RD)WLI1?vY<730ZcV}q7UieH{1=Jc#1H>Dm&q{8aMRQe7d;5}dq>wNrpeXvg@-X6H_S%1KjS%sozCF>|x7UXi3ePQ864!9s)G?#I5ke7BSYj(Gxy0%$ zD6N1e99a$A`10NQ(6tecrLhQ>T?EU5cn8H#YYK~FAgK&PkJ6iLURD_DIds^`U$G$& z-aPO&8Y2UoztQz`n|zoiWsOOK3b(hoKacOW&&mHfC=auikV8o zsgApbWP0vrZQy1-;f-O_8_iPNPq23KT$@#8G}kGjphvbfpxp@$&2Mma1C!ej&4d%l zk7(Zipon6?5vZUP31y^V4xB3l*b&Z-jCkC4&uHbaRVz7&;GpCZ?HPM84ygbv1#wcU zU=ggKp0qtVGPa)}%4m%W+7QE<+fTT5>R{NX`+(X+VcJ3(gyY<7hGBQ^LsM^plN_4k zGDcp5Ns}%j$tXlQq23zSAdn2x{u(A4)-d&!urY`lfJHy#F63eXN^`XlaE=Nvrbbb2 z2;~`{&B?rBa~(I*I$)dl>TzL@1|U)9lrJ;6jnUUdgc$2#CH}6WHi!_+3mp^)7P#z*=G^`F)SRJ4sP+Zgy;9&#HsHgbF%-~||Qw1FAHq9HN zFEM;h8=~)JcF_y77Y1K92h0$&+Z2Cox19Dpln7Jd_fQi=l(X7S3M$vc@1aIov%ap( zk02hjssO+MmNUTHafkTT(-*^+zJRZ8++&E@YrSnK2(FZPp{*@y7I8T|q8=wF_XhCf zcDvL>iv{VpqXn&XMMRlO*i;(kA+|^pk#r4Chqh{*{=r0x1@BHJY`Sg&l%Y=qt=xk? ztyJRrM_YTHLm`8X*ZFcX1T@b>v}xA-Jhq>U&G#py1~t*)0S@G zz0VBnq$Be_RhCB>8$|Lkov+%hM;jnwub-~Q!Lo6%Z1oQ0QM#Of?SA1}F(b}bD14J# zv;L`+KtngHv5we{zO zDKg7AHDkg}&)IJyBg{1w+M9N&fbS?$+MF(yje})FbQccX>OtN_1-%UNiqT3Kfgw%= zRn2HiWy7QW<4kgx?)>HG-Z$dAmMegu18#3=DW%X1sK>9V0cJsze&YgNA|Zt_#GiW3 z?PcI1-qbTtNH7y_zIU)}94s3oIB7`mMpkPN24jrKqd5qY0_=E|jBRSsk)G%Klp{vD zws9W!_Lm!IdmxC7AM{i%8wbklauvU6-6-T*zc98c8{x0;OJQT)n;}Dt%c%1JSAv9fd^U}}7{u<+a(}DFoSDJ_Z3=OF z=2}7CYh*q8(ic+YvT?9%fX5tx_;$89_?e~yE}uW z2>DPXeIVk#vqM^%3KwZ9TcF2CPa)R+P>aFXp{VUZba(f$>I&a`DKfp!tR7-{y`h$Z zenZj0p)leeW3AYqDaAnd8LJH(^#@uEFAc?^h5}Z5476ecuM}U;9%E%SV{NmAQ8Uy-yj+PZd4YAI2)t0JUXP*H@ufC?rl!#Vz>2@R*ixg#GlrHz z?SsV;0`W9pyAP(yv*HESV2ebz1V`;}8l=trdu4-$vK$&Vky)UP=Osd@g6R^esE20M_)XxhcR{v+g*ey@LU z^EF)h(EewmD5bt*;gv-P3yU_uy1P*S)pN<~Xy0Gp8g2{^g``967X@zRJ|+XHD6}+Z zFj{k5*_bG8q#p?cML83~8DbnP8wbl)yZkURiUYLrvj_;njAKLW&ZQ(;*}yj;3{`x9 zT}$Xsqw|??~nrbTYZvuqsAV3w3fJ#Mw z`z~-=uBg<0Lnw*tXCRQ$x~#X*fg1VWKoF(!;*m(zj8uVwOBC@p$rxM&oyze>>=`L2 z7@QP$CQ4=FVA*;`N+g$OMyjv^MoKy6`-AI@ka`$KDjLPxQ28wQlx^@CG5Z+`pQ;@` zd2ByJ;Zt=zva3wOM*ivxSqD%_DDUQ(avUg^I7G1at#v&zy7yl4@w6q)K@P!J22j(6 z`oW8NB7+x1nD3C-&}wB0JM$b=SrA^rVP2QxxUZUYIti!pT`pb7~a``tCWk(0VXet6A%D9Dr)il-m4l24J!oA(-zd z8XM4>1z^Z`rrLN3k-zK50}+vkX~hTFmXnG%$`+7nfL*Il^HA-@X(XX(YzQJT({Ws_ zxHbS05#2$=xD+*SoazoD0h~x+TmTWZFSf9nsRtsKzwSa{Qvi{5A2ZAc0a2sPdoK)P zP!KiXM6^SisA6*>rWe^f085x2XxL+;EZR?{W#f%<5}GQ6mGnC5pm!Nv{_t zd4^!gB=C}mJKH~(;;zC0T;X7^a9~(Ch|G&Od&clRUzIKIXqZ5H@#P(jfKFbaz*lJd zo@wx$!&kM(C&KW%>>~%X$qLo8LQnOS+UFhqh1u`2pB2!9DwL)Q4a-xN!({{0;K}b!A&~e!zHva6m#hcp)6<5Dtp);wN5}x_ZcWkcOx`wn0mnVfbc~ObFBR z>!I%@wyTE|VHIpQ4@x3ZTk3*9lkCsKcJ-h#Ex>kxxEvW_lVxp)7&oc}-Irp#uV8rw z0tfy~mZ#~Ye1A2Tr|IN(_-ZUq)5-7f)mWaUli%Ur#qwnJZ}qeXy$$HPWK1eW?3=T> z#_p+qxy4BlXM{WX4Y|cBkRjF~>6@ITvWsBZJiA{RW(av!-z6}#+P6Oe83#DT;zULv zfU#OKG9?&&<3SI=LAxrdMc5I3WT3I1!|6ALX*pHj_O4*TK9TROR)jY zLpV_kOvU`TNlsCt zdJc+dFnJF57%gV2JxO$`92q z(&f@#vw!j7njPmXI?eK3bd%)!t59yg!Fsqc+hp@3JKmD{pLgr91#9D1sNe>ZlkRky z6kEAd42SM-Z=?C<0jOBK`TJ(GI`0XeC)%DG3uYPPT9=-&LKOHs#_XJlc)od`WkBii zGI~m2O!@zg)o*V%4>y^m%`Ur7ici`*>-C863we z$KZo(0sb#72ZAFCFe@_)n>_dbDzb4?AzV$ZaSIV-DgX*$!?$$_-~ckhbBMm0O28^P_|NF`{cQBpE&+Cs<=x$1ck7sxE6Gci z+OyT}$ME4Wgx}KDDvHtSew)U((Q=96-}6TgrOr=66U#JC*4xWu$(v2Tz*1hVHtTIp zp~JJ=$>0C?#|cGvQX)=1EGJrHa+q3os(&o@z?tns`KYhKj$*?>ezi z`OoZrnM~K4_3t1w-|i*#^qW~Uo9AzLw?Fqxe@c!&ZOzczDL`@OUi)p6uBU*QBzwDx zvd>9ot4$8y$JOM|uijUXM z!~BwN(^DqSu?dD68EtZ>yqlP?B1WQc|Y5 zkaYB;bD1jyb|4l8t}^>7b^|}WtM;@zt`wAI95D?g_~EKO&(qrBf)fQdxL2{$-tQw% z8MJ`l)Nz-cbuU1Vl{+BnhqDP<~uF}%>6Ilkrt z4l6;l2V`h2ed>GT%GbrOj~G+@t31aAy7)g6fZ20I991jdc6}_O7P4ZW{>R`zws(>N@1^@(V}{ z6z(y9qOl*`06_wzO80^lqDb!S>@G!9<6g|=1i==@BP4Ei zo47gC#OYcW?Ap>8lIM+@+g43pI6ip){0#l&`uf-Ds%;196h9t%OndY(e9fr4kC{F* z{9+#K;}oC%OsrF!ZV8^~m5PvIV4;CWkv@6>I9db6gD7dMt&(mU5-#NeCw__lwI;l@ z2#H!ETqC0}jwEqX>E2K@yI@(Ou32sTtwy0!rF02q*SvUx7GtZZDM@h@rOgqzh7PG4xbKC z+1qm-I9u|(F#qvv&)yQ!eS2eGYi%aYO$_e|v=2%S|8QDoL=CW)@aB*LjSH*t(=^aGf3yT?;2hAJ z5<(a;^stc@kT|nw&c4j+gvZG8C42L`d3cVRWoj$#$vKg18bc_V{V9Bq%X4g+sgM|| zTGr+6Soxu`%gEZk0=__h5wWy#CM+dP#)TK!sZ=A?_}a4dSkN+hUI_*?1D_y?Q{zqx zPHti0zB}P(sruLNh;1`1F&Lh4iGVY5}mX>@h7?;up zRJiRP5v0tRK{!?Cy9czyLi-IEbjxD0PzS8>NVS}fA#HiihHm}Yu&w9g-5a_+XB^|P zKYQc$`H24FM?6t%)+D&}jcBeBy&Eyah$Uu2JS+yMNOzi%j`koY3tIb zt|7R12<7qj1R!&@b)+Te!#Hs(cHM1}n4=C?CR8kim52|sM1=kU!^Fja45jCK8;bB( z87P2N$vr7V;s{FlQf8%ItA${U z-y6yl6d6TMGlq$+UdRo14`{;;!P?}%ztq{t(NULsp7ZN|3oAV1LTzPh69C4!XLj2XXd zGC;3(r|5e)`vT$Y<%_$$RrX^{V?6um*}6jCyRfZU9&s*Gd_>&)F~AopE9RX8iQa#B zbNA-mZ*LjbeDN28eZdcKM6VXO@GAuJ_#@YmJK$@}6XIV{!N0gjQ;xvY>Oi4sx54QG z@}u-}YkV%ZdR6?*lI#9jeTre{Frag`@WjWooANTQ1W~s(fIR+ac_0XC;R&+kBHu?} z%MqFy21mAyjfa~*Kc5o2Z<^xCo2IMqU4$Oz38x-B*>q#sY{8FW&!2laZn7|DmQ99l zh5bE!L53SwR?GgkcZkn#4+pt@{Uu%*zCL_9y!~n{>AtIcJyc7qd<$5RQtINK3ktnX z8dSStYY9l$3HD!4mu?O0yaI%?ZFD(ThCP)6|GVCQc>nR;?N{!|-TcC@5pfa2cyvGC z?9!$ys)+?$O|Hgrg?bYu5m8ida&Q0gV&?jonb+-j$Yx%0JJZiU{0n7V>Wb9QF{>|I%N8#$7G zpI^b1U)nKb5%(b<6CLX9iP`YXZcKN_K5mqXMXAOVsV!2LS}mY>&IdY_MexZP~-FQ?PT$H%0oiOH(G znZ6xOCQx$y<>n8g(FmGdt}kcVhl@J@{X@BM`DWQPU*vcBa{*xnF~3gA ze7$-ov#RrSB8TZt09YlW*|Jnkr46DA*p=6#c^Ud_9Uw@jeM|JiINoJdwS5H$cXgJWv zGQG>!_i2`|+csSJCUKcR-LJ~cMo!Dqqw_E zv*}Oy`txRWKYc79M{>WcME6+CHn$%xgt>om>D#=xx!rh#V)o$z} zbr~qN1|c?ifuGIu0+>MJBwH<3<>ecx@bk`*R-pG{^L$C)U36uD-*pZ$CLYa+8A?D~ z>|!(>-r6<6&ydy<&Z*K&%2=}sw7aueFBgl}Y&7(kQ+~0R)lA~2$LcY?)e1j@dTSY1 zN*NpPtvIS#$9OiNSsGVq*V_AOh^-@`D5c~_K(VqCA>S}0nbi0%E;r(tmarqxxI9X1 z(dLJW4I0yj#D=MK(Gy!^hJcMlobLhi5FMq*l=s%agt@BQ@yP=mBH|0zYtgn?V1-2>6d2WCOnous?Hea|>8326ejo|sOej7k_-PX`QxR##6P_5w zVQzUl7_CFmA9R+m1e_ZaqE%^dU6?=NVRs{WvBNYmRC8+%qBUY&V{^K~&(?I)6nz)N z8z=T`G%JIjXzi4~8k*H!G&6RFf@AuPYB!?XfoP_JD$$_1O0a|xz!4u$SR%DmmV0pC zF@ZI3_U4GEial$zXI63Mpi(5%QqR$W*#P>XG=ODmI_Xs~k5;f>v}ammf}beL*d7;j zAcl>=PrP>KVK|ig3wjepCi9hms_+tm~cdT`NIrR=kAU%OaN+gU27P{nGCX`qk^2h`@EMCay1_?_PWjmJ+ z1|pEmue15SWe^{M^lStYa?NLaU~~NS5J>kUP?7JZ-oXewjYSPpz236YBGFQBS!t$l z=xnhFY@Z81VzO+YMbdi~_y)sIWo97!w0b@Gkpu8MDd0a}w?nZvG|bo@9fo?2y&D|{ zMu*-S9pE4^vh5*2VGG0P%;}eKORk5(LFu#6ms>Go1JS4KtD(;x0N-f~FfXCqq4+Z= zai|i)WXPeR3Dw?cYaLW!Xvm>SjN#`lWnRJbVDt_Eyg=j(@b;EN!s^+p;maJr*J;+{ zCA2pHUt6nHl z*;6DbtT|qivF2V>l2i^56xF8>Zbn3E^!7=B!-IQZRngt4jzziqPyzJI3tabh-yW~H zXt!Tbaomf}&lm!PzNRtpH`~99F^TLoZ1CqI-Q+(^6iJwonvN&+578Zn{vQ$bM4$>22X2DocZQlAoSBX^8S{kZe2qc3Q2~^L)At78U7O1|TH;d+t3Y-oU z_@Jou34`Jtx6`oKd4XU2X$XGeUs(8-jHwXDS zhrhQt=y!#SV|NeEQwa=5E;$!shy9AfAP$*0yy8#{Q2T8K&h`#gn3P{o;4fk1;&vu( zlj8O(ZtDWG_)SF~%-u1OAA3ORHGG=5Hx>81;$B+ZqYIqB?<@9Wqw4Q0_JN^5JZyM9 zu^+PN^4J~ga~)EK7TEsF8uX)22<(8 zfTVtO1?-25d?F!|T1CSbngj>bNV@e(Q$O#1QGUZtySI^dcN!jttCoOQra2W`3*#ER zeW7gldK2S`rPepp?tm*zBrbdoJ$q4g{{0kRwsybTm$vbZ?b(+if2=B6Z_3r@9C}#o zuhrGhi^T$I_?Oi*>?gi3UMKIawt57CZ9n|FTHSSfDV?6tRvWNVRukEKi1q`#Pok9WKYS=lko07c zKIhOU_tjLtzFj@ulqj2dx@d*d7R5)C>+97Mu1Y`j9?w?U1K$2Hd8o8lzwqzn!212( zMq7C-mhf;=UnF33Pb77Q=4JuizG$?a;|WJ!PaX9`=SDQg>fYbwn{<|L(heOBr8!LU zzMRc3|Nhg@?QU3>UH?m~4(Lvr`9F0$Knu}HG7 zu4tXBD+x_3iY#BQ4~xY$TYQ1Be0R56uAPMHvg_%e|LfBfrtoP`IIdgXKIgPnt*`1& z+Z=e>KC1DsWA}Lp%3B}oSnSr6zvuVmYW9%f{S;xJ&aC*S0t{(!{Sd9V%>VbHfD$Lw z{I}Hx1oE=X=4J84$sJX;ep!BhxlPOb$3^kE&+~cpy42Nb=GW_RSL3XGudp*Z@28DP za;clnR0TG?wR4N~I){1h6FwaEp5BzJhxrGm20?J)63)1*BgtV8Z=e2 zh(SojnPRxZs~7}$5QvRFd1L|Murx$kpmKbV*g_(pVyGsxhP+%P+&~UdQ*kkhLd8-* zIBkpwD)!p@Itsi&1cYFouhuzVfF7K8V5-mDU+1L;C~ER!-VYZo0xL20*Jlw&4^ZcX zBoG=9u7&DnT9w63u}l{zHP<<=jFmyx%Cau`kI85`)Sx@B^ z^_!~vC6)WGJ7LN!H&h7Tw+m9z{v74vWEh>+7%n4^adDF*+y*z1wU0}8{Xt@wmfi|< z_ZVS;FN+iFT|!iu9F_>C1Tmww`q7W0wysfIY(z#BJ|jjFttrgK{YD8ILwV=d~an;Ch zZR7|q6Ow{4J%(RgS;4I<4L*?ONK;&E@O!R8DhSUA;n6rr2vgwxL}^!1D&rt_BB1Sn zW)!1D2c-ilm*8&%IcdUxWVdKSYDwzS(nt;c2C7oj@TzUfn59Uwni(C?Si&j3VP2K) zQ61-;QSE4oi<-s-&R{?jR0>mLIihI4c6P`6u1dQ9nBi4 zDw@UH%{-bbjce{qhdBXuT2vLXx@nE~oS3R<$yr_4Xe%qKu$p6GJPk`hgmq^uiPONh z0FTm81>>+1kyfJeIIJZ~P>*^Qr7{Vlms5l#s>gEaXyD2Nbetl=1F94WR4uu*mCE6- z%4syC(uYQbcI8tMZn7>dm|)U4SZag}(kMK%!U0%;&g#+~mEmqmkQ0Z5C31__4IXIQ zDi|Ri6&)`?y;28gS37fpFsMLNU@YG35T#vV2mq#n7%(pk%rBD+NeZ9{u@>4Uz=qUV zm_wwcJahFaYQ!P|m1HI|S@i;{{@-4Ey~n+LlV^FJr>_y0U*qkp|1|ETo0<^o)WGzC zp|<7%e2Hqr)>MFp1f#{Pu#tn+mK2HA;~;BSK@1j@s)Mk2EeH&WJLBHvKUY9eA%NkQ zm8IaGfd%ABbEnpp%7H43kuE9(Ditt4AXS2~hC4scR}DFypuytU#OpDOzvT`guE!JFQqaWz0%ZFRJdv8l$t zME>ZVi_oVc5U5!Ela)i&`BvxXs+w&HsHRpD0gH%B8*!?1@tJU{Q}|*Uh1d|I7h^Qh zVRbP zUL!cJqyQD{CvBI>)L`lfo}hD4KO^wFj*~m86hhQ%7M2j} zqJDV9>V*-ZaL(yyZXE+`nb5+|$?=z#)UVN}b!~T43WHI(<*2Sknn;aHwSLVtQ>F1dZXIlJGerg4PZbR&VA@yG~ehDlX`t;JEh|N;J=4{n416Q zTL+wnDHH97sHz9;6^ov>$@}4*J3Bel*`S;5$joQJ9SI0uvTJLre1`ES01k-e3Qk8>$hB*JwuDdohwIWZQLQke3yjSqP4d;SB$mAZHtk0t6^h| z5p0*z_@6w0$m+C3cS~W>It?`lTUUhlJL{$qYZDD6!671$b<XRceH{v`Gv8A%s>! zLz+^&c&$xCelTQIcNrKK6kP?NM+;7DS3h$b7Io|R-&atf9AmOeRcwE133b1&IzrX$ zsxn^5gtm>f605iYf*(5qshidvNz zd?QD+DuIYxvrZ#*vQtR&yy3Af-4lOE*d=XE&yx08i?>_S#{afYu!eOlk#z@MT;kwp zr~vYWyQL(Ej}eI+#cvYAC0a3Eb-X6Hd&hOP=`JAZLc>Q5cQ7;*j4li(D|fWs5q1i0 z;4&tJl&X=7cBkS(@KlBWueB@5Z5W1u_r9Vp2t-oU z0u;UUnokr?`u`_%hEfYhsL>pJ3aliQG$CgOifr*I<0HH`emq@V2U8a9OZWT!=YWo& z>rXCY9zd7RfNq}#bNVw!J_06`8p|e0T~S>wuWPKsS@(Zuy*r67#{yOU{l{vawd_P0S#9R3C%1^q>k{q zhBa!#hY74riC`JV2+{Q?c&D)Ir8wEDz^(~>RW)BndR-g-hTVA5(R0hr_uHqm?To5) zq*0L1tRtW!W@PP3(@gA?5OTi##f{pQU0!Et(F~ScqawLm3PRXNeJ9?n zt!ywzA?a^?nf*X394^(l&tXK)qyqfHFg3_+yon)YwZ|ccrFopgA-1+IlE=#%Z;g6? N`37yHD&5+6000uloI?Nr literal 0 HcmV?d00001 diff --git a/pics/crystalsvg/crsc-mime-rtf.svgz b/pics/crystalsvg/crsc-mime-rtf.svgz new file mode 100644 index 0000000000000000000000000000000000000000..923cf5623caed3bbdd3d7c2c2f224d5cb673e116 GIT binary patch literal 5269 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