mpviewplugins/mpdetailsviewplugin/src/mpdetailssharedialog.cpp
changeset 47 4cc1412daed0
parent 45 612c4815aebe
child 51 560ce2306a17
equal deleted inserted replaced
45:612c4815aebe 47:4cc1412daed0
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: Implementation for share player.
       
    15 *
       
    16 */
       
    17 
       
    18 #ifdef SHARE_FUNC_ENABLED
       
    19 
       
    20 #include "mpdetailssharedialog.h"
       
    21 #include "mpsharedata.h"
       
    22 #include "mpsongdata.h"
       
    23 #include "mptrace.h"
       
    24 #include <QObject>
       
    25 #include <QGraphicsWebView>
       
    26 #include <QGraphicsScene>
       
    27 #include <QWebPage>
       
    28 #include <QWebFrame>
       
    29 #include <QNetworkAccessManager>
       
    30 #include <QNetworkDiskCache>
       
    31 #include <QNetworkReply>
       
    32 #include <QDesktopServices>
       
    33 #include <QNetworkProxyFactory>
       
    34 #include <QTimer>
       
    35 #include <hbmessagebox.h>
       
    36 #include <QFile>
       
    37 #include <QTextStream>
       
    38 #include <qsysteminfo.h>
       
    39 #include <hbmainwindow.h>
       
    40 #include <hbstyleloader.h>
       
    41 #include <hbprogressdialog.h>
       
    42 
       
    43 QTM_USE_NAMESPACE
       
    44 
       
    45 // SHARE_INDEX_FILE defines where the index.html file is loaded from.
       
    46 #define SHARE_INDEX_URL "qrc:///shareview/index.html"
       
    47 
       
    48 #ifdef Q_OS_SYMBIAN
       
    49 // Symbian target.
       
    50 #ifdef SHARE_PLAYER_RND
       
    51 // For R&D testing, index.html may be loaded from E: drive.
       
    52 // User must manually place index.html in the correct location.
       
    53 // If the RND file does not exist, then SHARE_INDEX_URL will be used.
       
    54 #define RND_SHARE_INDEX_URL "file:///f:/index.html"
       
    55 #define RND_SHARE_INDEX_FILE "f:\\index.html"
       
    56 #define RND_OVI_LOGIN_FILE "f:\\ovicredentials.txt"
       
    57 #endif
       
    58 #else
       
    59 // Assume Windows target.
       
    60 #define RND_SHARE_INDEX_URL "file:///c:/temp/index.html"
       
    61 #define RND_SHARE_INDEX_FILE "c:\\temp\\index.html"
       
    62 #define RND_OVI_LOGIN_FILE "c:\\temp\\ovicredentials.txt"
       
    63 
       
    64 #endif
       
    65 
       
    66 // Default language in case QSystemInfo does not work.
       
    67 #define DEFAULT_LANGUAGE "en-US"
       
    68 
       
    69 // Default error message.
       
    70 #define ERROR_MESSAGE "An error occured. Sharing is not currently available"
       
    71 
       
    72 
       
    73 /*!
       
    74  MpNetworkAccessManager allows local caching of publishing player files
       
    75  in order to minimize network traffic.
       
    76  The files will be cached to the private directory of the application,
       
    77  i.e. in the music player's case, this is C:\Private\10207C62\Cache.
       
    78  */
       
    79 class MpNetworkAccessManager : public QNetworkAccessManager
       
    80 {
       
    81 public:
       
    82     MpNetworkAccessManager( QObject* parent = 0 )
       
    83         : QNetworkAccessManager( parent )
       
    84     {
       
    85         proxyFactory()->setUseSystemConfiguration( true );
       
    86         QNetworkDiskCache* diskCache = new QNetworkDiskCache( this );
       
    87         QString location = QDesktopServices::storageLocation( QDesktopServices::CacheLocation );
       
    88         diskCache->setCacheDirectory( location );
       
    89         setCache( diskCache );
       
    90     }
       
    91 
       
    92 private:
       
    93     QNetworkReply* createRequest( Operation op,
       
    94                                   const QNetworkRequest &request,
       
    95                                   QIODevice* outgoingData = 0 )
       
    96     {
       
    97         TX_ENTRY
       
    98         TX_LOG_ARGS( "share: createRequest URL=" << request.url().toString() )
       
    99 
       
   100         QVariant val = request.attribute( QNetworkRequest::CacheLoadControlAttribute );
       
   101 
       
   102          // Change the cache load control attrbute!
       
   103         QNetworkRequest req = request;
       
   104         req.setAttribute( QNetworkRequest::CacheLoadControlAttribute,
       
   105                           QVariant( QNetworkRequest::PreferCache ) );
       
   106         QNetworkReply* result = QNetworkAccessManager::createRequest( op, req, outgoingData );
       
   107         TX_EXIT
       
   108         return result;
       
   109     }
       
   110 };
       
   111 
       
   112 /*!
       
   113  MpShareWebView derives from QGraphicsWebView in order to override it's
       
   114  contextMenuEvent method to prevent the background context menu from
       
   115  being displayed when user makes long click in the web view.
       
   116  */
       
   117 class MpShareWebView : public QGraphicsWebView
       
   118 {
       
   119 public:
       
   120     MpShareWebView( QGraphicsItem * parent = 0 )
       
   121         : QGraphicsWebView( parent )
       
   122     {
       
   123         settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true );
       
   124         settings()->setAttribute( QWebSettings::LocalStorageDatabaseEnabled, true );
       
   125         settings()->enablePersistentStorage();
       
   126     }
       
   127 
       
   128 protected:
       
   129     void contextMenuEvent( QGraphicsSceneContextMenuEvent* /*ev*/ ) // Override QGraphicsWebView::contextMenuEvent
       
   130     {
       
   131         // Fix to prevent "Stop" and "Reload" buttons in page background.
       
   132         // Do not respond to the contextMenuEvent.
       
   133     }
       
   134 };
       
   135 
       
   136 
       
   137 /*!
       
   138  Constructor.
       
   139  */
       
   140 MpDetailsShareDialog::MpDetailsShareDialog()
       
   141     : mShareWebView( 0 ),
       
   142       mShareNetAccMan( 0 ),
       
   143       mProgressbar ( 0 ),
       
   144       mIsInitialized( false )
       
   145 {
       
   146     // DeleteOnClose attribute prevents crash when user presses Cancel
       
   147     // before publishing player is fully loaded.
       
   148     setAttribute( Qt::WA_DeleteOnClose, true );
       
   149 }
       
   150 
       
   151 /*!
       
   152  Initialize the share dialog.
       
   153  When fully initialized we set our mIsInitialized flag to true.
       
   154  Our isInitialized() method can be called to determine whether
       
   155  initialization was successful.
       
   156  */
       
   157 void MpDetailsShareDialog::initialize( MpSongData* aSongData, const QString& aUnknownTr )
       
   158 {
       
   159     TX_ENTRY
       
   160     if ( !initUser() )
       
   161     {
       
   162         emit closeShareDialog();
       
   163         return;
       
   164     }
       
   165     initShareData( aSongData, aUnknownTr );
       
   166     initLanguage();
       
   167     initNetworkAccessManager();
       
   168     initWebView();
       
   169     initSignalSlots();
       
   170 
       
   171     setDismissPolicy( HbDialog::NoDismiss );
       
   172 
       
   173     // No timeout needed for the dialog.
       
   174     setTimeout( HbPopup::NoTimeout );
       
   175 
       
   176 #ifdef SHARE_PLAYER_RND
       
   177     // Test whether the RND file exists.
       
   178     QFile file( RND_SHARE_INDEX_FILE );
       
   179     if ( file.exists() )
       
   180     {
       
   181         // Load the RND URL from the specified location to the web view.
       
   182         TX_LOG_ARGS( "share: Use RND index.html file " << RND_SHARE_INDEX_FILE )
       
   183         mShareWebView->load( QUrl( RND_SHARE_INDEX_URL ) );
       
   184     }
       
   185     else
       
   186 #endif
       
   187     {
       
   188         // Load the production URL from the application resources to the web view.
       
   189         TX_LOG_ARGS( "share: Use QRC index.html file " << SHARE_INDEX_URL )
       
   190         mShareWebView->load( QUrl( SHARE_INDEX_URL ) );
       
   191     }
       
   192 
       
   193     // Flag that the dialog is now fully initialized.
       
   194     mIsInitialized = true;
       
   195 
       
   196     // Show progress dialog in .3 second if loading not finished.
       
   197     QTimer::singleShot(300, this, SLOT(showProgressDialog()));
       
   198 
       
   199     TX_EXIT
       
   200 }
       
   201 
       
   202 /*!
       
   203  Destructor.
       
   204  */
       
   205 MpDetailsShareDialog::~MpDetailsShareDialog()
       
   206 {
       
   207     TX_ENTRY
       
   208     if ( mShareData.songData() )
       
   209     {
       
   210         // Ensure that we remove the temporary album art file when we close the dialog.
       
   211         // TODO this should be removed when base64 issue is solved.
       
   212         TX_LOG_ARGS( "share: remove album art file" )
       
   213         mShareData.songData()->removeAlbumArtFile();
       
   214     }
       
   215     logoutPlayer();
       
   216     // Probably mShareNetAccMan should not be deleted but qt documentation
       
   217     // does not indicate whether QWebPage takes ownership of the object or not.
       
   218     // See http://doc.qt.nokia.com/4.6/qwebpage.html
       
   219     //delete mShareNetAccMan;
       
   220     TX_EXIT
       
   221 }
       
   222 
       
   223 /*!
       
   224  Initialize share data.
       
   225  */
       
   226 void MpDetailsShareDialog::initShareData( MpSongData* aSongData, const QString& aUnknownTr )
       
   227 {
       
   228     TX_ENTRY
       
   229     // Set information for the share data.
       
   230     mShareData.setOwner( this );
       
   231     mShareData.setSongData( aSongData );
       
   232     mShareData.setErrorMessage( tr( ERROR_MESSAGE ) );
       
   233     mShareData.setUnknownTr( aUnknownTr );
       
   234     TX_EXIT
       
   235 }
       
   236 
       
   237 /*!
       
   238  Initialize language.
       
   239  Language string is formatted like "en-US", where "en" is the ISO-639-1 language code,
       
   240  and "US" is the ISO-3166-1 country code.
       
   241  We use the QT Mobility API (systeminfo) to obtain the settings from the device.
       
   242  In the event that we cannot construct the QSystemInfo variable then we will fallback
       
   243  to some DEFAULT_LANGUAGE setting.
       
   244  */
       
   245 void MpDetailsShareDialog::initLanguage()
       
   246 {
       
   247     TX_ENTRY
       
   248     // Set language string, example "en-US".
       
   249     QString language;
       
   250     QSystemInfo* sysInfo = new QSystemInfo( this );
       
   251     if ( sysInfo )
       
   252     {
       
   253         language += sysInfo->currentLanguage(); // ISO-639-1 language code.
       
   254         language += "-";
       
   255         language += sysInfo->currentCountryCode(); // ISO-3166-1 country code.
       
   256         delete sysInfo;
       
   257     }
       
   258     else
       
   259     {
       
   260         // Fallback to the default language.
       
   261         language = DEFAULT_LANGUAGE;
       
   262     }
       
   263     TX_LOG_ARGS( "share: language '" << language << "'" )
       
   264     mShareData.setLanguage( language );
       
   265     TX_EXIT
       
   266 }
       
   267 
       
   268 /*!
       
   269  Initialize network access manager.
       
   270  */
       
   271 void MpDetailsShareDialog::initNetworkAccessManager()
       
   272 {
       
   273     TX_ENTRY
       
   274     // Make our own network access manager to allow file retrieval from local cache,
       
   275     // since configuration for the default network access manager seems to be
       
   276     // to always redownload from network.
       
   277     if ( !mShareNetAccMan )
       
   278     {
       
   279         TX_LOG_ARGS( "share: construct network access manager" )
       
   280         mShareNetAccMan = new MpNetworkAccessManager( this );
       
   281     }
       
   282     TX_EXIT
       
   283 }
       
   284 
       
   285 /*!
       
   286  Initialize the web view.
       
   287  */
       
   288 void MpDetailsShareDialog::initWebView()
       
   289 {
       
   290     TX_ENTRY
       
   291     if ( !mShareWebView )
       
   292     {
       
   293         TX_LOG_ARGS( "share: construct share web view" )
       
   294         mShareWebView = new MpShareWebView( this );
       
   295         mShareWebView->page()->setNetworkAccessManager( mShareNetAccMan );
       
   296         mShareWebView->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
       
   297         mShareWebView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded);
       
   298         setContentWidget( mShareWebView );
       
   299         addContext();
       
   300         mProgressbar = new HbProgressDialog(HbProgressDialog::WaitDialog);
       
   301         mProgressbar->setText(tr("Loading"));
       
   302     }
       
   303     TX_EXIT
       
   304 }
       
   305 
       
   306 /*!
       
   307  Initialize signals and slots.
       
   308  */
       
   309 void MpDetailsShareDialog::initSignalSlots()
       
   310 {
       
   311     TX_ENTRY
       
   312     // Connect various signals to slots for networking.
       
   313     connect( mShareNetAccMan, SIGNAL( sslErrors( QNetworkReply*, const QList< QSslError >& ) ),
       
   314              this, SLOT( handleRequestSSLErrors( QNetworkReply*, const QList< QSslError >& ) ) );
       
   315     connect( mShareNetAccMan, SIGNAL( finished( QNetworkReply* ) ), this, SLOT( handleRequestFinished( QNetworkReply* ) ) );
       
   316 
       
   317     // Connect various signals to slots for interface to webview.
       
   318     connect( mShareWebView, SIGNAL( loadFinished( bool ) ), SLOT( onIndexLoad( bool ) ) );
       
   319     connect( mShareWebView->page()->mainFrame(), SIGNAL( javaScriptWindowObjectCleared() ), this, SLOT( addContext() ) );
       
   320     connect( mShareWebView->page(), SIGNAL( windowCloseRequested() ), this, SIGNAL( closeShareDialog() ) );
       
   321     connect( mProgressbar, SIGNAL(cancelled()), this, SIGNAL( closeShareDialog() ) );
       
   322     TX_EXIT
       
   323 }
       
   324 
       
   325 /*!
       
   326  initUser is used as temporary solution until Single Sign On is implemented in platform.
       
   327  */
       
   328 bool MpDetailsShareDialog::initUser()
       
   329 {
       
   330     TX_ENTRY
       
   331     bool result = false;
       
   332 #ifdef SHARE_PLAYER_RND
       
   333     // ovicredentials.txt is used as temporary solution until Single Sign On is implemented in platform.
       
   334     QFile file( RND_OVI_LOGIN_FILE );
       
   335     if ( !file.open( QFile::ReadOnly ) )
       
   336     {
       
   337         // There must be e:ovicredentials.txt in the filesystem but don't show error dialog
       
   338         // otherwise it will appear as soon as detailsview is created.
       
   339         result = false;
       
   340     }
       
   341     else
       
   342     {
       
   343         QTextStream stream ( &file );
       
   344         QString strCred = stream.readLine( 0 );
       
   345         file.close();
       
   346         QStringList slCred = strCred.split( ":" );
       
   347         if ( slCred.length() > 1 )
       
   348         {
       
   349             mShareData.setUsername( slCred[ 0 ] );
       
   350             mShareData.setPassword( slCred[ 1 ] );
       
   351             result = true;
       
   352         }
       
   353         else
       
   354         {
       
   355             errorHandler( "share", QString( RND_OVI_LOGIN_FILE ) + " username:password expected" );
       
   356         }
       
   357     }
       
   358     TX_LOG_ARGS( "share: credentials " << mShareData.username() << " / " << mShareData.password() )
       
   359 #else
       
   360     // TODO: Single Sign On stuff.
       
   361 #endif // SHARE_PLAYER_RND
       
   362     TX_EXIT
       
   363     return result;
       
   364 }
       
   365 
       
   366 /*!
       
   367  Returns true if the dialog has been fully initialized.
       
   368   */
       
   369 bool MpDetailsShareDialog::isInitialized() const
       
   370 {
       
   371     return mIsInitialized;
       
   372 }
       
   373 
       
   374 /*!
       
   375  Attempt to cache the publishing player files from internet
       
   376  to improve user experience for first-time use.
       
   377  If the files are already in the cache and have not expired,
       
   378  then this should not do anything.
       
   379  */
       
   380 void MpDetailsShareDialog::cachePublishingPlayerFiles()
       
   381 {
       
   382     TX_ENTRY
       
   383     // We need the network access manager, so make sure it is initialized.
       
   384     if ( !mShareNetAccMan )
       
   385     {
       
   386         initNetworkAccessManager();
       
   387     }
       
   388     // Attempt to get the required publishing player files from the net in advance.
       
   389     // We don't listen to any signal that the download succeeded or failed
       
   390     // since we will let it silently fail at this stage.
       
   391     // These URLs are also included in index.html resource, and the two must
       
   392     // be kept the same.
       
   393     mShareNetAccMan->get( QNetworkRequest( QUrl(
       
   394             "http://hf.ci.wipsl.com/PleiXXPTsup/noheva-be/css/ovi.player.share.ui.css" ) ) );
       
   395     mShareNetAccMan->get( QNetworkRequest( QUrl(
       
   396             "http://hf.ci.wipsl.com/PleiXXPTsup/noheva-be/js/publishplayer.js" ) ) );
       
   397     TX_EXIT
       
   398 }
       
   399 
       
   400 /*!
       
   401  Release resources from share player.
       
   402  */
       
   403 void MpDetailsShareDialog::logoutPlayer()
       
   404 {
       
   405     TX_ENTRY
       
   406     if ( mShareWebView )
       
   407     {
       
   408         mShareWebView->page()->mainFrame()->evaluateJavaScript( "music.teardown();" );
       
   409     }
       
   410     TX_EXIT
       
   411 }
       
   412 
       
   413 /*!
       
   414  Adds the shared data context to the javascript of the loaded page.
       
   415  */
       
   416 void MpDetailsShareDialog::addContext()
       
   417 {
       
   418     TX_ENTRY
       
   419     if ( mShareWebView )
       
   420     {
       
   421         mShareWebView->page()->mainFrame()->addToJavaScriptWindowObject( "context", &mShareData );
       
   422     }
       
   423     TX_EXIT
       
   424 }
       
   425 
       
   426 /*!
       
   427  Updates the shared data context in the javascript of the loaded page.
       
   428  */
       
   429 void MpDetailsShareDialog::updateSharedData()
       
   430 {
       
   431     TX_ENTRY
       
   432     if ( mShareWebView )
       
   433     {
       
   434         // We don't need to call updateContextArea when the track URL has been updated.
       
   435         //mShareWebView->page()->mainFrame()->evaluateJavaScript( "music.updateContextArea();" );
       
   436         mShareWebView->page()->mainFrame()->evaluateJavaScript( "music.updateMetadata();" );
       
   437     }
       
   438     TX_EXIT
       
   439 }
       
   440 
       
   441 /*!
       
   442  Slot to call when index.html loading completes.
       
   443  */
       
   444 void MpDetailsShareDialog::onIndexLoad( bool aOk )
       
   445 {
       
   446     TX_ENTRY
       
   447     if ( !aOk )
       
   448     {
       
   449         // Close the popup window, failed to load index.html.
       
   450         // This is pretty serious and most likely unrecoverable error.
       
   451         // Only thing we can do really is to close the share player
       
   452         // dialog - TODO do we need to show any error message to user?
       
   453         TX_LOG_ARGS( "share: failed to load index.html" )
       
   454         emit closeShareDialog();
       
   455     }
       
   456     TX_EXIT
       
   457 }
       
   458 
       
   459 /*!
       
   460  Slot to call for debug output.
       
   461  */
       
   462 void MpDetailsShareDialog::debugJs( QString s )
       
   463 {
       
   464     TX_ENTRY
       
   465     Q_UNUSED(s);
       
   466 
       
   467     TX_EXIT
       
   468 }
       
   469 
       
   470 /*!
       
   471  Slot to call for displaying an error message to the user.
       
   472  */
       
   473 void MpDetailsShareDialog::errorHandler( QString aError, QString aMessage )
       
   474 {
       
   475     TX_ENTRY
       
   476     // If error argument ends with "_SUCCESS", then this should be an info message.
       
   477     // If error argument ends with "_FAILED" or something else, then this should be a warning message.
       
   478     TX_LOG_ARGS( "share: errorHandler: " << aError << ": " << aMessage )
       
   479     if ( aError.endsWith( "_SUCCESS" ) )
       
   480     {
       
   481         // TODO this method seems to be deprecated?
       
   482         HbMessageBox::information( tr( "%1" ).arg( aError ) + ": " + tr( "%1" ).arg( aMessage ) ); // For week16 hbwidgets
       
   483     }
       
   484     else
       
   485     {
       
   486         // TODO this method seems to be deprecated?
       
   487         HbMessageBox::warning( tr( "%1" ).arg( aError ) + ": " + tr( "%1" ).arg( aMessage ) ); // For week16 hbwidgets
       
   488         // HbMessageBox::launchWarningMessageBox( tr( "%1" ).arg( aError ) + ": " + tr( "%1" ).arg( message ) ); // For week12 hbwidgets
       
   489     }
       
   490     TX_EXIT
       
   491 }
       
   492 
       
   493 /*!
       
   494  Slot to call to clear the web view cache.
       
   495  */
       
   496 void MpDetailsShareDialog::clearCache()
       
   497 {
       
   498     TX_ENTRY
       
   499     QAbstractNetworkCache* cache = mShareNetAccMan ? mShareNetAccMan->cache() : NULL;
       
   500     if ( cache )
       
   501     {
       
   502         TX_LOG_ARGS( "share: clearCache: clearing cache" )
       
   503         cache->clear();
       
   504 #ifdef SHARE_PLAYER_RND
       
   505         errorHandler( "Cache", "Cleared cache!" );
       
   506 #endif
       
   507     }
       
   508     else
       
   509     {
       
   510         TX_LOG_ARGS( "share: clearCache: unable to clear cache" )
       
   511 #ifdef SHARE_PLAYER_RND
       
   512         errorHandler( "Cache", "Could not clear cache!" );
       
   513 #endif
       
   514     }
       
   515     TX_EXIT
       
   516 }
       
   517 
       
   518 /*!
       
   519  Slot to show the publishing window after html elements are created in javascript.
       
   520  */
       
   521 void MpDetailsShareDialog::showWindow()
       
   522 {
       
   523     TX_ENTRY
       
   524     if (mProgressbar)
       
   525         mProgressbar->close();
       
   526     show();
       
   527     TX_EXIT
       
   528 }
       
   529 
       
   530 /*!
       
   531  Slot to show progress dialog if the publishing window is not loaded in .3 sec.
       
   532  */
       
   533 void MpDetailsShareDialog::showProgressDialog()
       
   534 {
       
   535     TX_ENTRY
       
   536     if (!isVisible())
       
   537         mProgressbar->show();
       
   538     TX_EXIT
       
   539 }
       
   540 
       
   541 /*!
       
   542  Slot to SSL errors in network request. We will ignore any errors.
       
   543  */
       
   544 void MpDetailsShareDialog::handleRequestSSLErrors( QNetworkReply* aReply, const QList< QSslError >& aErrors )
       
   545 {
       
   546     TX_ENTRY
       
   547     aReply->ignoreSslErrors();
       
   548     for( int i = 0; i < aErrors.count(); i++ )
       
   549     {
       
   550         TX_LOG_ARGS( "SSL error " << aErrors.at( i ).errorString() );
       
   551     }
       
   552     TX_EXIT
       
   553 }
       
   554 
       
   555 /*!
       
   556  Slot to handle network request completion.
       
   557  */
       
   558 void MpDetailsShareDialog::handleRequestFinished( QNetworkReply* aReply )
       
   559 {
       
   560     TX_ENTRY
       
   561     if ( aReply->error() != QNetworkReply::NoError )
       
   562     {
       
   563         TX_LOG_ARGS( "Network request error " << aReply->error() << aReply->errorString() );
       
   564         // TODO what to do now?
       
   565     }
       
   566     TX_EXIT
       
   567 }
       
   568 
       
   569 #endif // SHARE_FUNC_ENABLED