diff -r fdb31ab341af -r a0afa279b8fe mpviewplugins/mpdetailsviewplugin/src/mpdetailsview.cpp --- a/mpviewplugins/mpdetailsviewplugin/src/mpdetailsview.cpp Fri May 14 18:54:37 2010 -0500 +++ b/mpviewplugins/mpdetailsviewplugin/src/mpdetailsview.cpp Fri May 28 19:46:01 2010 -0500 @@ -1,7 +1,7 @@ /* * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). * All rights reserved. -* This component and the accompanying materials are made available +* This component and the accompanying materials are made available * under the terms of "Eclipse Public License v1.0" * which accompanies this distribution, and is available * at the URL "http://www.eclipse.org/legal/epl-v10.html". @@ -26,9 +26,7 @@ #include #include -#include -#include -#include + #include #include @@ -57,7 +55,7 @@ #include "mpdetailssharedialog.h" #endif -const int KUndefined = -1; + const int KRecommendationCount = 2; @@ -68,16 +66,20 @@ : mMpEngine( 0 ), mSongData( 0 ), mActivated( false ), - mWindow( 0 ), mSoftKeyBack( 0 ), - mSongText( NULL ), - mAlbumText( NULL ), - mArtistText( NULL ), - mAlbumArt( NULL ), - mDocumentLoader( NULL ), - mDownloadedAlbumArts( 0 ), + mSongText( 0 ), + mAlbumText( 0 ), + mArtistText( 0 ), + mAlbumArt( 0 ), + mSongDetailsGroupBox(0), + mInspireMeGroupBox(0), + mShareButton(0), + mDocumentLoader( 0 ), + mDetailList(0), + mInspireList(0), + mInspireMeProgressBar(0), mMpTranslator( 0 ), - mCommonTranslator( 0 ), + mCommonTranslator( 0 ), mMpQueryManager( 0 ), mInspireMeQueryOngoing( false ), mInspireMeQueryRendered( false ), @@ -91,28 +93,26 @@ bool widgetsOk = false; HbMainWindow *mainWindow = hbInstance->allMainWindows()[0]; mDocumentLoader = new HbDocumentLoader(); - + TX_LOG_ARGS("Document loader created") if(!mDocumentLoader) { TX_LOG_ARGS( "MpDetailsView() Error: HbDocumentLoader not constructed" ); - return; + return; } TX_LOG_ARGS("Loading :/mpdetailsviewdocml/mpdetailsview.docml") mDocumentLoader->load( QString(":/mpdetailsviewdocml/mpdetailsview.docml"), &widgetsOk ); if(!widgetsOk) { TX_LOG_ARGS( "MpDetailsView() Error: invalid mpdetailsview.docml" ); - return; + return; } - + TX_LOG_ARGS("Loaded :/mpdetailsviewdocml/mpdetailsview.docml") - QGraphicsWidget *widget = 0; - + QGraphicsWidget *widget = 0; + widget = mDocumentLoader->findWidget( QString("content") ); TX_LOG_ARGS( "MpDetailsView() mDocumentLoader->findWidget for : " << (int)(widget) ); - mContainer = qobject_cast(widget); - TX_LOG_ARGS("MpDetailsView() widget found mContainer: " << (int)(mContainer)); - setWidget( mContainer ); + setWidget( qobject_cast(widget) ); widget = mDocumentLoader->findWidget( QString("shareButton") ); mShareButton = qobject_cast(widget); @@ -121,11 +121,11 @@ widget = mDocumentLoader->findWidget( QString("trackTitle") ); mSongText = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mSongText: " << (int)(mSongText) ); - + widget = mDocumentLoader->findWidget( QString("artist") ); mArtistText = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mArtistText: " << (int)(mArtistText) ); - + widget = mDocumentLoader->findWidget( QString("albumName") ); mAlbumText = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mAlbumText: " << (int)(mAlbumText) ); @@ -133,15 +133,15 @@ widget = mDocumentLoader->findWidget( QString("albumArt") ); mAlbumArt = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mAlbumArt: " << (int)(mAlbumArt) ); - + widget = mDocumentLoader->findWidget( QString("songDetails") ); mSongDetailsGroupBox = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mSongDetailsGroupBox: " << (int)(mSongDetailsGroupBox) ); - + widget = mDocumentLoader->findWidget( QString("songDetailsListWidget") ); mDetailList = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mDetailList: " << (int)(mDetailList) ); - + widget = mDocumentLoader->findWidget( QString("inspireMeGroupBox") ); mInspireMeGroupBox = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mInspireMeGroupBox: " << (int)(mInspireMeGroupBox) ); @@ -149,11 +149,20 @@ widget = mDocumentLoader->findWidget( QString("inspireListWidget") ); mInspireList = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mInspireList: " << (int)(mInspireList) ); - + widget = mDocumentLoader->findWidget( QString("inspireMeProgressBar") ); mInspireMeProgressBar = qobject_cast(widget); TX_LOG_ARGS("MpDetailsView() widget found mInspireMeProgressBar: " << (int)(mInspireMeProgressBar) ); - + + // load section HideInspireMe if no ovi store suport + bool loadingSectionOk = false; + mDocumentLoader->load( QString(":/mpdetailsviewdocml/mpdetailsview.docml"), QString( "ShowInspireMe" ), &loadingSectionOk ); + if( loadingSectionOk ) { + TX_LOG_ARGS( "Loading ShowInspireMe section is successful." ); + } else { + TX_LOG_ARGS( "Loading ShowInspireMe section fails." ); + } + TX_EXIT } @@ -162,16 +171,9 @@ */ MpDetailsView::~MpDetailsView() { - TX_ENTRY - if ( mSoftKeyBack ) { - delete mSoftKeyBack; - } - if ( mDocumentLoader ) { - delete mDocumentLoader; - } - if( mThumbnailManager ) { - delete mThumbnailManager; - } + TX_ENTRY + delete mSoftKeyBack; + delete mDocumentLoader; delete mMpTranslator; delete mCommonTranslator; delete mMpQueryManager; @@ -188,8 +190,8 @@ */ void MpDetailsView::initializeView() { - TX_ENTRY - + TX_ENTRY + //Load musicplayer and common translators QString lang = QLocale::system().name(); QString path = QString( "z:/resource/qt/translations/" ); @@ -208,47 +210,31 @@ if ( translatorLoaded ) { qApp->installTranslator( mMpTranslator ); } - - // TODO: Hkn: Remove this once ELCE-844GBS is fixed - mInspireMeProgressBar->hide(); - - mWindow = mainWindow(); - mSoftKeyBack = new HbAction( Hb::BackNaviAction, this ); - + + mSoftKeyBack = new HbAction( Hb::BackNaviAction, this ); + mMpEngine = MpEngineFactory::sharedEngine(); mSongData = mMpEngine->songData(); - - // TODO: Remove this once Orbit bug ELCE-843HFJ is fixed - mInspireList->setVerticalScrollBarPolicy(HbScrollArea::ScrollBarAlwaysOff); - - - - - mThumbnailManager = new ThumbnailManager( this ); - mThumbnailManager->setQualityPreference( ThumbnailManager::OptimizeForQuality ); - mThumbnailManager->setThumbnailSize( ThumbnailManager::ThumbnailSmall ); - QObject::connect( mThumbnailManager, SIGNAL( thumbnailReady( QPixmap , void * , int , int ) ), - this, SLOT( thumbnailReady( QPixmap , void * , int , int ) ) ); - // TODO: Use the album art in provided by 10.1 wk16 platform. Current one is in the binary - mDefaultRecommendationAlbumArt = QPixmap( ":/mpdetailsviewicons/qtg_large_music_album.svg" ); - + // TODO: might need later setupMenu(); - mMpQueryManager = new MpQueryManager(); - + mMpQueryManager = new MpQueryManager(); + connect( mSoftKeyBack, SIGNAL( triggered() ), this, SLOT( back() ) ); - connect( mSongData, SIGNAL( albumArtReady() ), this, SLOT( albumArtChanged() ) ); + connect( mSongData, SIGNAL( albumArtReady() ), this, SLOT( albumArtChanged() ) ); connect( mSongData, SIGNAL( playbackInfoChanged() ), this, SLOT( handlePlaybackInfoChanged() ) ); - connect( mSongData, SIGNAL( songDetailInfoChanged() ), this, SLOT( songDetailInfoChanged() ) ); + connect( mSongData, SIGNAL( songDetailInfoChanged() ), this, SLOT( songDetailInfoChanged() ) ); + connect( mSongDetailsGroupBox, SIGNAL( toggled( bool ) ), this, SLOT( handleDetailsGroupBoxToggled( bool ) ) ); + connect( mInspireMeGroupBox, SIGNAL( toggled( bool ) ), this, SLOT( handleInspireMeGroupBoxToggled( bool ) ) ); connect( mMpQueryManager, SIGNAL( networkError() ), this ,SLOT( handleNetworkError() ) ); - connect( mMpQueryManager, SIGNAL(setAlbumArt(const QString &, const QString &) ), this ,SLOT(setAlbumArtUri(const QString &, const QString & ))); connect( mMpQueryManager, SIGNAL(searchUrlRetrieved(const QString&)), this, SLOT(updateSharedData(const QString&))); + connect( mMpQueryManager, SIGNAL(recommendationAlbumArtsReady()), this, SLOT(RenderInspireMeGroupBox())); -#ifdef SHARE_FUNC_ENABLED +#ifdef SHARE_FUNC_ENABLED connect( mShareButton, SIGNAL( clicked() ), this, SLOT( share() ) ); - + // Preload the share popup - createShareDialog(); + preloadShareDialog(); #endif TX_EXIT } @@ -258,10 +244,10 @@ */ void MpDetailsView::activateView() { - TX_ENTRY + TX_ENTRY setNavigationAction( mSoftKeyBack ); mMpEngine->retrieveSong(); - + mActivated = true; mInspireMeOpen = MpSettingsManager::inspireMe(); mSongDetailsGbOpen = MpSettingsManager::songDetailsGb(); @@ -269,10 +255,6 @@ TX_LOG_ARGS( "SongDetailsGbVal: " << mSongDetailsGbOpen ); mInspireMeGroupBox->setCollapsed(!mInspireMeOpen); mSongDetailsGroupBox->setCollapsed(!mSongDetailsGbOpen); - - connect( mSongDetailsGroupBox, SIGNAL( toggled( bool ) ), this, SLOT( handleDetailsGroupBoxToggled( bool ) ) ); - connect( mInspireMeGroupBox, SIGNAL( toggled( bool ) ), this, SLOT( handleInspireMeGroupBoxToggled( bool ) ) ); - TX_EXIT } @@ -281,25 +263,21 @@ */ void MpDetailsView::deactivateView() { - TX_ENTRY + TX_ENTRY if (mInspireMeGroupBox->isCollapsed() ) { MpSettingsManager::setInspireMe(false); } else { MpSettingsManager::setInspireMe(true); } - + if (mSongDetailsGroupBox->isCollapsed() ) { MpSettingsManager::setSongDetailsGb(false); } else { MpSettingsManager::setSongDetailsGb(true); } - + setNavigationAction( 0 ); - mActivated = false; - - disconnect( mSongDetailsGroupBox, SIGNAL( toggled( bool ) ), this, SLOT( handleDetailsGroupBoxToggled( bool ) ) ); - disconnect( mInspireMeGroupBox, SIGNAL( toggled( bool ) ), this, SLOT( handleInspireMeGroupBoxToggled( bool ) ) ); - + mActivated = false; TX_EXIT } @@ -318,7 +296,7 @@ */ void MpDetailsView::back() { - TX_ENTRY + TX_ENTRY emit command( MpCommon::ActivatePlaybackView ); TX_EXIT } @@ -327,7 +305,7 @@ Slot to handle Album art changed. */ void MpDetailsView::albumArtChanged( ) -{ +{ TX_ENTRY HbIcon icon; mSongData->albumArt( icon ); @@ -343,38 +321,15 @@ TX_ENTRY mInspireMeQueryOngoing = false; mInspireMeQueryRendered = false; - clearInspireMe(); + clearInspireMe(); mInspireMeGroupBox->setCollapsed( true ); TX_EXIT } /*! - Sets recommendation album art -*/ -void MpDetailsView::setAlbumArtUri( const QString &albumArtUri, const QString &albumArtName ) -{ - TX_ENTRY_ARGS( "albumArtUri = " << albumArtUri ) - TX_LOG_ARGS( "albumArtName = " << albumArtName ) - if ( !albumArtUri.isEmpty() && !albumArtName.isEmpty() ) { - int id = mThumbnailManager->getThumbnail( albumArtName, reinterpret_cast( const_cast( &albumArtUri ) ) ); - if ( id == KUndefined ) { - // Request failed. Set default album art. - mMpQueryManager->insertMapItem( albumArtUri, mDefaultRecommendationAlbumArt ); - recommendationAlbumArtReady(); - } - } - else { - // No album art uri. Set default album art. - mMpQueryManager->insertMapItem( albumArtUri, mDefaultRecommendationAlbumArt ); - recommendationAlbumArtReady(); - } - TX_EXIT -} - -/*! Render inspireme groupbox after album arts downloaded */ -void MpDetailsView::RenderInspireMeGroupBox() +void MpDetailsView::RenderInspireMeGroupBox() { TX_ENTRY mInspireMeQueryOngoing = false; @@ -386,13 +341,13 @@ for ( int i = 0; i < KRecommendationCount; i++ ) { // configure the layout properties if(!mInspireList->count()) { - // we havent configured the prototype before + // we havent configured the prototype before HbListViewItem *prototype = mInspireList->listItemPrototype(); prototype->setGraphicsSize( HbListViewItem::LargeIcon ); } // create the item HbListWidgetItem *inspireMeItem = new HbListWidgetItem(); - HbIcon icon( QIcon( mMpQueryManager->recommendationAlbumArtsMap().value(mMpQueryManager->recommendationAlbumArtsLink().at( i ) ) ) ); + HbIcon icon( QIcon( mMpQueryManager->recommendationAlbumArtsMap().value(mMpQueryManager->recommendationAlbumArtsLink().at( i ) ) ) ); inspireMeItem->setIcon( icon ); inspireMeItem->setText( mMpQueryManager->recommendationSongs().at( i ) ); inspireMeItem->setSecondaryText( mMpQueryManager->recommendationArtists().at( i ) ); @@ -402,37 +357,26 @@ else { TX_LOG_ARGS( "There is NO recommendation." ) // we dont have recommendations - // we havent configured the prototype before + // we havent configured the prototype before HbListViewItem *prototype = mInspireList->listItemPrototype(); const int maxTextRowCount = 20; const int minTextRowCount = 1; prototype->setSecondaryTextRowCount(minTextRowCount,maxTextRowCount); - + HbListWidgetItem *inspireMeItem = new HbListWidgetItem(); QString info(tr("There are no recommendations for this track, but you can always discover new music on Ovi")); inspireMeItem->setText( QString( " " ) ); inspireMeItem->setSecondaryText( info ); mInspireList->addItem( inspireMeItem ); } - + TX_EXIT } -void MpDetailsView::recommendationAlbumArtReady() -{ - TX_ENTRY_ARGS( "mAlbumArtsReadyCount = " << mMpQueryManager->albumArtsReadyCount() ) - ( mMpQueryManager->albumArtsReadyCount() )++; - if ( mMpQueryManager->albumArtsReadyCount() == KRecommendationCount ) { - RenderInspireMeGroupBox(); - } - TX_EXIT -} - - bool MpDetailsView::canQueryRecommendations() const { bool result = ( ( !mSongData->album().isEmpty() ) || - ( !mSongData->artist().isEmpty() ) ) && + ( !mSongData->artist().isEmpty() ) ) && !( mInspireMeGroupBox->isCollapsed() ); TX_LOG_ARGS( "Can query recommendations:" << result ); return result; @@ -455,33 +399,37 @@ mMpQueryManager->clearNetworkReplies(); clearInspireMe(); mInspireMeQueryRendered = false; - + + // Clear the song data link until new query has been made. + mSongData->setLink( "" ); + if ( !mSongData->title().isEmpty () ) { mSongText->setPlainText( mSongData->title() ); } else { mSongText->setPlainText( mSongData->fileName() ); } - + if ( !mSongData->album().isEmpty () ) { mAlbumText->setPlainText( mSongData->album() ); } else { - mAlbumText->setPlainText( tr( "Unknown ") ); + mAlbumText->setPlainText( tr( "Unknown") ); } - + if ( !mSongData->artist().isEmpty() ) { mArtistText->setPlainText( mSongData->artist() ); } else { - mArtistText->setPlainText( tr( "Unknown ") ); + mArtistText->setPlainText( tr( "Unknown") ); } - + + if (canQuerySharePlayerLink() ) { + mMpQueryManager->queryLocalMusicStore(mSongData->artist(),mSongData->album(),mSongData->title()); + } + if (canQueryRecommendations()) { // start inspire me area progress bar // TODO: currently, till we get to this callback from MPX the bar not shown // TODO: check if inspireMe is ON, if not, dont show mInspireMeProgressBar->show(); - if (canQuerySharePlayerLink() ) { - mMpQueryManager->queryLocalMusicStore(mSongData->artist(),mSongData->album(),mSongData->title()); - } mMpQueryManager->queryInspireMeItems(mSongData->artist(),mSongData->album(),mSongData->title()); mInspireMeQueryOngoing = true; } @@ -491,15 +439,15 @@ if (!mInspireMeGroupBox->isCollapsed()) RenderInspireMeGroupBox(); } - TX_EXIT + TX_EXIT } void MpDetailsView::clearInspireMe() { - TX_ENTRY + TX_ENTRY mInspireList->clear(); - mMpQueryManager->clearRecommendations(); - TX_EXIT + mMpQueryManager->clearRecommendations(); + TX_EXIT } /*! Slot to handle detail song information @@ -508,15 +456,15 @@ { TX_ENTRY mDetailList->clear(); - + if ( !mSongData->albumTrack().isNull() ) { - HbListWidgetItem *item = new HbListWidgetItem(); + HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Song number" ) ); item->setSecondaryText( mSongData->albumTrack() ); item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->year().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Year" ) ); @@ -524,7 +472,7 @@ item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->genre().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Genre" ) ); @@ -532,7 +480,7 @@ item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->composer().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Composer" ) ); @@ -548,7 +496,7 @@ item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->mimeType().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Format" ) ); @@ -564,7 +512,7 @@ item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->bitRate().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Bitrate" ) ); @@ -572,7 +520,7 @@ item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->sampleRate().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Sampling rate" ) ); @@ -580,7 +528,7 @@ item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->size().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Size" ) ); @@ -588,7 +536,7 @@ item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->modified().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Modified" ) ); @@ -604,7 +552,7 @@ item->setEnabled( false ); mDetailList->addItem( item ); } - + if ( !mSongData->musicURL().isNull() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Web site" ) ); @@ -612,7 +560,7 @@ item->setEnabled( true ); mDetailList->addItem( item ); } - + if ( mSongData->isDrmProtected() ) { HbListWidgetItem *item = new HbListWidgetItem(); item->setText( tr( "Licences" ) ); @@ -624,9 +572,8 @@ TX_EXIT } -void MpDetailsView::handleDetailsGroupBoxToggled(bool state) +void MpDetailsView::handleDetailsGroupBoxToggled(bool /*state*/) { - Q_UNUSED(state); TX_ENTRY if ( !mSongDetailsGroupBox->isCollapsed() ) { mInspireMeGroupBox->setCollapsed( true ); @@ -639,7 +586,7 @@ TX_ENTRY if ( !mInspireMeGroupBox->isCollapsed() ) { TX_LOG_ARGS( "InspireMe is expanded." ) - mSongDetailsGroupBox->setCollapsed( true ); + mSongDetailsGroupBox->setCollapsed( true ); if ( mInspireMeQueryOngoing ) { TX_LOG_ARGS( "Query is ongoing " ) mInspireMeProgressBar->show(); @@ -658,7 +605,7 @@ TX_LOG_ARGS( "InspireMe is NOT rendered yet and CANNOT query for recommendations either. " ) RenderInspireMeGroupBox(); } - } + } } } else { TX_LOG_ARGS( "InspireMe is collapsed." ) @@ -667,30 +614,6 @@ TX_EXIT } -/*! - Slot to handle the recommendation album art -*/ -void MpDetailsView::thumbnailReady( - const QPixmap& pixmap, - void *data, - int /*id*/, - int error ) -{ - TX_ENTRY - // TODO: Hkn: use qobject_cast - QString uri = *( reinterpret_cast( data ) ); - TX_LOG_ARGS( "Uri: " << uri ); - - if ( error == 0 ) { - TX_LOG_ARGS( "album art link: " << uri ); - mMpQueryManager->insertMapItem( uri, pixmap ); - } else { - mMpQueryManager->insertMapItem( uri, mDefaultRecommendationAlbumArt ); - } - recommendationAlbumArtReady(); - TX_EXIT -} - #ifdef SHARE_FUNC_ENABLED /*! Slot to be called when share button is clicked @@ -699,10 +622,6 @@ { TX_ENTRY createShareDialog(); - if ( mSharePopup ) - { - mSharePopup->show(); - } if (canQuerySharePlayerLink() ) { mMpQueryManager->queryLocalMusicStore(mSongData->artist(),mSongData->album(),mSongData->title() ); @@ -712,6 +631,7 @@ /*! Method to create the share dialog on demand. + This will cause the share web view to be created and start loading. */ void MpDetailsView::createShareDialog() { @@ -719,13 +639,31 @@ if ( !mSharePopup ) { mSharePopup = new MpDetailsShareDialog(); - connect( mSharePopup, SIGNAL(closeShareDialog() ), this, SLOT( closeShareDialog() ) ); - mSharePopup->initialize( mSongData ); + } + if ( !mSharePopup->isInitialized() ) + { + connect( mSharePopup, SIGNAL( closeShareDialog() ), this, SLOT( closeShareDialog() ) ); + mSharePopup->initialize( mSongData, tr( "Unknown" ) ); } TX_EXIT } /*! + Method to create the share dialog on demand and preload publishing player files. + This will construct the share dialog but it will still be in "uninitialized" state. + */ +void MpDetailsView::preloadShareDialog() +{ + TX_ENTRY + if ( !mSharePopup ) + { + mSharePopup = new MpDetailsShareDialog(); + } + mSharePopup->cachePublishingPlayerFiles(); + TX_EXIT +} + +/*! Slot to be called when ok/close button in share dialog is pressed. */ void MpDetailsView::closeShareDialog() @@ -733,8 +671,8 @@ TX_ENTRY if ( mSharePopup ) { + // Dialog uses WA_DeleteOnClose so no need to delete it explicitely here, just close it. mSharePopup->close(); - delete mSharePopup; mSharePopup = NULL; } TX_EXIT