// (c) 2004 Christian Muehlhaeuser <chris@chris.de>
// (c) 2005 Reigo Reinmets <xatax@hot.ee>
// (c) 2005 Mark Kretschmann <markey@web.de>
// (c) 2006 Peter C. Ndikuwera <pndiku@gmail.com>
// (c) 2006 Alexandre Pereira de Oliveira <aleprj@gmail.com>
// (c) 2006 Maximilian Kossick <maximilian.kossick@googlemail.com>
// License: GNU General Public License V2
# define DEBUG_PREFIX "ContextBrowser"
# include "amarok.h"
# include "amarokconfig.h"
# include "app.h"
# include "browserToolBar.h"
# include "debug.h"
# include "clicklineedit.h"
# include "collectiondb.h"
# include "collectionbrowser.h"
# include "colorgenerator.h"
# include "contextbrowser.h"
# include "coverfetcher.h"
# include "covermanager.h"
# include "cuefile.h"
# include "enginecontroller.h"
# include "htmlview.h"
# include "lastfm.h"
# include "mediabrowser.h"
# include "metabundle.h"
# include "mountpointmanager.h"
# include "playlist.h" //appendMedia()
# include "podcastbundle.h"
# include "qstringx.h"
# include "scriptmanager.h"
# include "starmanager.h"
# include "statusbar.h"
# include "tagdialog.h"
# include "threadmanager.h"
# include <tqbuffer.h>
# include <tqdatetime.h>
# include <tqdeepcopy.h>
# include <tqdom.h>
# include <tqimage.h>
# include <tqregexp.h>
# include <tqtextstream.h> // External CSS reading
# include <tqvbox.h> //wiki tab
# include <tqhbox.h>
# include <tqlayout.h>
# include <tqlineedit.h>
# include <tqlistview.h>
# include <tqtimer.h>
# include <tqtooltip.h>
# include <kaction.h>
# include <kapplication.h> //kapp
# include <kcalendarsystem.h> // for Amarok::verboseTimeSince()
# include <kconfig.h> // suggested/related/favorite box visibility
# include <kdialog.h>
# include <kfiledialog.h>
# include <kglobal.h>
# include <kiconloader.h>
# include <kio/job.h>
# include <kio/jobclasses.h>
# include <kmdcodec.h> // for data: URLs
# include <kmessagebox.h>
# include <kpopupmenu.h>
# include <kstandarddirs.h>
# include <ktextedit.h>
# include <ktoolbarbutton.h>
# include <unistd.h> //usleep()
namespace Amarok
{
TQString escapeHTML ( const TQString & s )
{
return TQString ( s ) . replace ( " & " , " & ; " ).replace( " < " , " & lt ; " ).replace( " > " , " & gt ; " );
// .replace( "%", "%25" ) has to be the first(!) one, otherwise we would do things like converting spaces into %20 and then convert them into %25%20
}
TQString escapeHTMLAttr ( const TQString & s )
{
return TQString ( s ) . replace ( " % " , " %25 " ) . replace ( " ' " , " %27 " ) . replace ( " \" " , " %22 " ) . replace ( " # " , " %23 " ) . replace ( " ? " , " %3F " ) ;
}
TQString unescapeHTMLAttr ( const TQString & s )
{
return TQString ( s ) . replace ( " %3F " , " ? " ) . replace ( " %23 " , " # " ) . replace ( " %22 " , " \" " ) . replace ( " %27 " , " ' " ) . replace ( " %25 " , " % " ) ;
}
TQString verboseTimeSince ( const TQDateTime & datetime )
{
const TQDateTime now = TQDateTime : : currentDateTime ( ) ;
const int datediff = datetime . daysTo ( now ) ;
if ( datediff > = 6 * 7 /*six weeks*/ ) { // return absolute month/year
const KCalendarSystem * cal = KGlobal : : locale ( ) - > calendar ( ) ;
const TQDate date = datetime . date ( ) ;
return i18n ( " monthname year " , " %1 %2 " ) . arg ( cal - > monthName ( date ) , cal - > yearString ( date , false ) ) ;
}
//TODO "last week" = maybe within 7 days, but prolly before last sunday
if ( datediff > = 7 ) // return difference in weeks
return i18n ( " One week ago " , " %n weeks ago " , ( datediff + 3 ) / 7 ) ;
if ( datediff = = - 1 )
return i18n ( " Tomorrow " ) ;
const int timediff = datetime . secsTo ( now ) ;
if ( timediff > = 24 * 60 * 60 /*24 hours*/ ) // return difference in days
return datediff = = 1 ?
i18n ( " Yesterday " ) :
i18n ( " One day ago " , " %n days ago " , ( timediff + 12 * 60 * 60 ) / ( 24 * 60 * 60 ) ) ;
if ( timediff > = 90 * 60 /*90 minutes*/ ) // return difference in hours
return i18n ( " One hour ago " , " %n hours ago " , ( timediff + 30 * 60 ) / ( 60 * 60 ) ) ;
//TODO are we too specific here? Be more fuzzy? ie, use units of 5 minutes, or "Recently"
if ( timediff > = 0 ) // return difference in minutes
return timediff / 60 ?
i18n ( " One minute ago " , " %n minutes ago " , ( timediff + 30 ) / 60 ) :
i18n ( " Within the last minute " ) ;
return i18n ( " The future " ) ;
}
TQString verboseTimeSince ( uint time_t )
{
if ( ! time_t )
return i18n ( " Never " ) ;
TQDateTime dt ;
dt . setTime_t ( time_t ) ;
return verboseTimeSince ( dt ) ;
}
extern KConfig * config ( const TQString & ) ;
/**
* Function that must be used when separating contextBrowser escaped urls
* detail can contain track / discnumber
*/
void albumArtistTrackFromUrl ( TQString url , TQString & artist , TQString & album , TQString & detail )
{
if ( ! url . contains ( " @@@ " ) ) return ;
//KHTML removes the trailing space!
if ( url . endsWith ( " @@@ " ) )
url + = ' ' ;
const TQStringList list = TQStringList : : split ( " @@@ " , url , true ) ;
int size = list . count ( ) ;
Q_ASSERT ( size > 0 ) ;
artist = size > 0 ? unescapeHTMLAttr ( list [ 0 ] ) : " " ;
album = size > 1 ? unescapeHTMLAttr ( list [ 1 ] ) : " " ;
detail = size > 2 ? unescapeHTMLAttr ( list [ 2 ] ) : " " ;
}
}
using Amarok : : QStringx ;
using Amarok : : escapeHTML ;
using Amarok : : escapeHTMLAttr ;
using Amarok : : unescapeHTMLAttr ;
static
TQString albumImageTooltip ( const TQString & albumImage , int size )
{
if ( albumImage = = CollectionDB : : instance ( ) - > notAvailCover ( false , size ) )
return escapeHTMLAttr ( i18n ( " Click to fetch cover from amazon.%1, right-click for menu. " ) . arg ( CoverManager : : amazonTld ( ) ) ) ;
return escapeHTMLAttr ( i18n ( " Click for information from Amazon, right-click for menu. " ) ) ;
}
ContextBrowser * ContextBrowser : : s_instance = 0 ;
TQString ContextBrowser : : s_wikiLocale = " en " ;
ContextBrowser : : ContextBrowser ( const char * name )
: KTabWidget ( 0 , name )
, EngineObserver ( EngineController : : instance ( ) )
, m_dirtyCurrentTrackPage ( true )
, m_dirtyLyricsPage ( true )
, m_dirtyWikiPage ( true )
, m_emptyDB ( CollectionDB : : instance ( ) - > isEmpty ( ) )
, m_wikiBackPopup ( new KPopupMenu ( this ) )
, m_wikiForwardPopup ( new KPopupMenu ( this ) )
, m_wikiJob ( NULL )
, m_wikiConfigDialog ( NULL )
, m_relatedOpen ( true )
, m_suggestionsOpen ( true )
, m_favoritesOpen ( true )
, m_labelsOpen ( true )
, m_showFreshPodcasts ( true )
, m_showFavoriteAlbums ( true )
, m_showNewestAlbums ( true )
, m_browseArtists ( false )
, m_browseLabels ( false )
, m_cuefile ( NULL )
{
s_instance = this ;
s_wikiLocale = AmarokConfig : : wikipediaLocale ( ) ;
m_contextTab = new TQVBox ( this , " context_tab " ) ;
m_currentTrackPage = new HTMLView ( m_contextTab , " current_track_page " , true /* DNDEnabled */ ,
true /*JScriptEnabled*/ ) ;
m_lyricsTab = new TQVBox ( this , " lyrics_tab " ) ;
m_lyricsToolBar = new Browser : : ToolBar ( m_lyricsTab ) ;
m_lyricsToolBar - > setIconText ( KToolBar : : IconTextRight , false ) ;
m_lyricsToolBar - > insertButton ( Amarok : : icon ( " refresh " ) , LYRICS_REFRESH , true , i18n ( " Refresh " ) ) ;
m_lyricsToolBar - > insertButton ( Amarok : : icon ( " add_lyrics " ) , LYRICS_ADD , true , i18n ( " Add " ) ) ;
m_lyricsToolBar - > insertButton ( Amarok : : icon ( " edit " ) , LYRICS_EDIT , true , i18n ( " Edit " ) ) ;
m_lyricsToolBar - > setToggle ( LYRICS_EDIT , true ) ;
m_lyricsToolBar - > insertButton ( Amarok : : icon ( " search " ) , LYRICS_SEARCH , true , i18n ( " Search " ) ) ;
m_lyricsToolBar - > setIconText ( KToolBar : : IconOnly , false ) ;
m_lyricsToolBar - > insertButton ( Amarok : : icon ( " external " ) , LYRICS_BROWSER , true , i18n ( " Open in external browser " ) ) ;
{ //Search text inside lyrics. Code inspired/copied from playlistwindow.cpp
m_lyricsTextBar = new KToolBar ( m_lyricsTab , " NotMainToolBar " ) ;
m_lyricsTextBar - > hide ( ) ;
m_lyricsTextBarShowed = false ;
m_lyricsTextBar - > setIconSize ( 22 , false ) ; //looks more sensible
m_lyricsTextBar - > setFlat ( true ) ; //removes the ugly frame
m_lyricsTextBar - > setMovingEnabled ( false ) ; //removes the ugly frame
m_lyricsTextBar - > boxLayout ( ) - > addStretch ( ) ;
TQWidget * button = new KToolBarButton ( " locationbar_erase " , 1 , m_lyricsTextBar ) ;
TQLabel * filter_label = new TQLabel ( i18n ( " S&earch: " ) + ' ' , m_lyricsTextBar ) ;
m_lyricsSearchText = new ClickLineEdit ( i18n ( " Search in lyrics " ) , m_lyricsTextBar ) ;
filter_label - > setBuddy ( m_lyricsSearchText ) ;
m_lyricsTextBar - > setStretchableWidget ( m_lyricsSearchText ) ;
m_lyricsSearchText - > setFrame ( TQFrame : : Sunken ) ;
m_lyricsSearchText - > installEventFilter ( this ) ; //we intercept keyEvents
connect ( button , TQT_SIGNAL ( clicked ( ) ) , m_lyricsSearchText , TQT_SLOT ( clear ( ) ) ) ;
TQToolTip : : add ( button , i18n ( " Clear search " ) ) ;
TQString filtertip = i18n ( " Enter text to search for. Press enter to advance to the next match. " ) ;
TQToolTip : : add ( m_lyricsSearchText , filtertip ) ;
connect ( button , TQT_SIGNAL ( clicked ( ) ) , m_lyricsSearchText , TQT_SLOT ( clear ( ) ) ) ;
connect ( m_lyricsSearchText , TQT_SIGNAL ( textChanged ( const TQString & ) ) , this , TQT_SLOT ( lyricsSearchText ( const TQString & ) ) ) ;
connect ( m_lyricsSearchText , TQT_SIGNAL ( returnPressed ( ) ) , this , ( TQT_SLOT ( lyricsSearchTextNext ( ) ) ) ) ;
Amarok : : actionCollection ( ) - > setAutoConnectShortcuts ( true ) ;
new KAction ( i18n ( " Search text in lyrics " ) , KShortcut ( " / " ) , TQT_TQOBJECT ( this ) , TQT_SLOT ( lyricsSearchTextShow ( ) ) , Amarok : : actionCollection ( ) , " search_text_lyric " ) ;
Amarok : : actionCollection ( ) - > setAutoConnectShortcuts ( false ) ;
}
m_lyricsPage = new HTMLView ( m_lyricsTab , " lyrics_page " , true /* DNDEnabled */ , false /* JScriptEnabled*/ ) ;
m_lyricsTextEdit = new KTextEdit ( m_lyricsTab , " lyrics_text_edit " ) ;
m_lyricsTextEdit - > setTextFormat ( TQt : : PlainText ) ;
m_lyricsTextEdit - > hide ( ) ;
m_wikiTab = new TQVBox ( this , " wiki_tab " ) ;
m_wikiToolBar = new Browser : : ToolBar ( m_wikiTab ) ;
m_wikiToolBar - > insertButton ( " back " , WIKI_BACK , false , i18n ( " Back " ) ) ;
m_wikiToolBar - > insertButton ( " forward " , WIKI_FORWARD , false , i18n ( " Forward " ) ) ;
m_wikiToolBar - > insertLineSeparator ( ) ;
m_wikiToolBar - > insertButton ( Amarok : : icon ( " artist " ) , WIKI_ARTIST , false , i18n ( " Artist Page " ) ) ;
m_wikiToolBar - > insertButton ( Amarok : : icon ( " album " ) , WIKI_ALBUM , false , i18n ( " Album Page " ) ) ;
m_wikiToolBar - > insertButton ( Amarok : : icon ( " track " ) , WIKI_TITLE , false , i18n ( " Title Page " ) ) ;
m_wikiToolBar - > insertLineSeparator ( ) ;
m_wikiToolBar - > insertButton ( Amarok : : icon ( " external " ) , WIKI_BROWSER , true , i18n ( " Open in external browser " ) ) ;
m_wikiToolBar - > insertButton ( Amarok : : icon ( " change_language " ) , WIKI_CONFIG , true , i18n ( " Change Locale " ) ) ;
m_wikiToolBar - > setDelayedPopup ( WIKI_BACK , m_wikiBackPopup ) ;
m_wikiToolBar - > setDelayedPopup ( WIKI_FORWARD , m_wikiForwardPopup ) ;
m_wikiPage = new HTMLView ( m_wikiTab , " wiki_page " , true /* DNDEnabled */ , false /* JScriptEnabled */ ) ;
m_cuefile = CueFile : : instance ( ) ;
connect ( m_cuefile , TQT_SIGNAL ( metaData ( const MetaBundle & ) ) ,
EngineController : : instance ( ) , TQT_SLOT ( currentTrackMetaDataChanged ( const MetaBundle & ) ) ) ;
connect ( m_cuefile , TQT_SIGNAL ( newCuePoint ( long , long , long ) ) ,
Scrobbler : : instance ( ) , TQT_SLOT ( subTrack ( long , long , long ) ) ) ;
addTab ( m_contextTab , SmallIconSet ( Amarok : : icon ( " music " ) ) , i18n ( " Music " ) ) ;
addTab ( m_lyricsTab , SmallIconSet ( Amarok : : icon ( " lyrics " ) ) , i18n ( " Lyrics " ) ) ;
addTab ( m_wikiTab , SmallIconSet ( Amarok : : icon ( " artist " ) ) , i18n ( " Artist " ) ) ;
setTabEnabled ( m_lyricsTab , false ) ;
setTabEnabled ( m_wikiTab , false ) ;
m_showRelated = Amarok : : config ( " ContextBrowser " ) - > readBoolEntry ( " ShowRelated " , true ) ;
m_showSuggested = Amarok : : config ( " ContextBrowser " ) - > readBoolEntry ( " ShowSuggested " , true ) ;
m_showFaves = Amarok : : config ( " ContextBrowser " ) - > readBoolEntry ( " ShowFaves " , true ) ;
m_showLabels = Amarok : : config ( " ContextBrowser " ) - > readBoolEntry ( " ShowLabels " , true ) ;
m_showFreshPodcasts = Amarok : : config ( " ContextBrowser " ) - > readBoolEntry ( " ShowFreshPodcasts " , true ) ;
m_showNewestAlbums = Amarok : : config ( " ContextBrowser " ) - > readBoolEntry ( " ShowNewestAlbums " , true ) ;
m_showFavoriteAlbums = Amarok : : config ( " ContextBrowser " ) - > readBoolEntry ( " ShowFavoriteAlbums " , true ) ;
// Delete folder with the cached coverimage shadow pixmaps
KIO : : del ( KURL : : fromPathOrURL ( Amarok : : saveLocation ( " covershadow-cache/ " ) ) , false , false ) ;
connect ( this , TQT_SIGNAL ( currentChanged ( TQWidget * ) ) , TQT_SLOT ( tabChanged ( TQWidget * ) ) ) ;
connect ( m_currentTrackPage - > browserExtension ( ) , TQT_SIGNAL ( openURLRequest ( const KURL & , const KParts : : URLArgs & ) ) ,
this , TQT_SLOT ( openURLRequest ( const KURL & ) ) ) ;
connect ( m_lyricsPage - > browserExtension ( ) , TQT_SIGNAL ( openURLRequest ( const KURL & , const KParts : : URLArgs & ) ) ,
this , TQT_SLOT ( openURLRequest ( const KURL & ) ) ) ;
connect ( m_wikiPage - > browserExtension ( ) , TQT_SIGNAL ( openURLRequest ( const KURL & , const KParts : : URLArgs & ) ) ,
this , TQT_SLOT ( openURLRequest ( const KURL & ) ) ) ;
connect ( m_currentTrackPage , TQT_SIGNAL ( popupMenu ( const TQString & , const TQPoint & ) ) ,
this , TQT_SLOT ( slotContextMenu ( const TQString & , const TQPoint & ) ) ) ;
connect ( m_lyricsPage , TQT_SIGNAL ( popupMenu ( const TQString & , const TQPoint & ) ) ,
this , TQT_SLOT ( slotContextMenu ( const TQString & , const TQPoint & ) ) ) ;
connect ( m_wikiPage , TQT_SIGNAL ( popupMenu ( const TQString & , const TQPoint & ) ) ,
this , TQT_SLOT ( slotContextMenu ( const TQString & , const TQPoint & ) ) ) ;
connect ( m_lyricsToolBar - > getButton ( LYRICS_ADD ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( lyricsAdd ( ) ) ) ;
connect ( m_lyricsToolBar - > getButton ( LYRICS_EDIT ) , TQT_SIGNAL ( toggled ( int ) ) , TQT_SLOT ( lyricsEditToggle ( ) ) ) ;
connect ( m_lyricsToolBar - > getButton ( LYRICS_SEARCH ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( lyricsSearch ( ) ) ) ;
connect ( m_lyricsToolBar - > getButton ( LYRICS_REFRESH ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( lyricsRefresh ( ) ) ) ;
connect ( m_lyricsToolBar - > getButton ( LYRICS_BROWSER ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( lyricsExternalPage ( ) ) ) ;
connect ( m_wikiToolBar - > getButton ( WIKI_BACK ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( wikiHistoryBack ( ) ) ) ;
connect ( m_wikiToolBar - > getButton ( WIKI_FORWARD ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( wikiHistoryForward ( ) ) ) ;
connect ( m_wikiToolBar - > getButton ( WIKI_ARTIST ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( wikiArtistPage ( ) ) ) ;
connect ( m_wikiToolBar - > getButton ( WIKI_ALBUM ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( wikiAlbumPage ( ) ) ) ;
connect ( m_wikiToolBar - > getButton ( WIKI_TITLE ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( wikiTitlePage ( ) ) ) ;
connect ( m_wikiToolBar - > getButton ( WIKI_BROWSER ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( wikiExternalPage ( ) ) ) ;
connect ( m_wikiToolBar - > getButton ( WIKI_CONFIG ) , TQT_SIGNAL ( clicked ( int ) ) , TQT_SLOT ( wikiConfig ( ) ) ) ;
connect ( m_wikiBackPopup , TQT_SIGNAL ( activated ( int ) ) , TQT_SLOT ( wikiBackPopupActivated ( int ) ) ) ;
connect ( m_wikiForwardPopup , TQT_SIGNAL ( activated ( int ) ) , TQT_SLOT ( wikiForwardPopupActivated ( int ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( scanStarted ( ) ) , TQT_SLOT ( collectionScanStarted ( ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( scanDone ( bool ) ) , TQT_SLOT ( collectionScanDone ( bool ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( databaseEngineChanged ( ) ) , TQT_SLOT ( renderView ( ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( coverFetched ( const TQString & , const TQString & ) ) ,
this , TQT_SLOT ( coverFetched ( const TQString & , const TQString & ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( coverChanged ( const TQString & , const TQString & ) ) ,
this , TQT_SLOT ( coverRemoved ( const TQString & , const TQString & ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( similarArtistsFetched ( const TQString & ) ) ,
this , TQT_SLOT ( similarArtistsFetched ( const TQString & ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( tagsChanged ( const MetaBundle & ) ) ,
this , TQT_SLOT ( tagsChanged ( const MetaBundle & ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( tagsChanged ( const TQString & , const TQString & ) ) ,
this , TQT_SLOT ( tagsChanged ( const TQString & , const TQString & ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( ratingChanged ( const TQString & , int ) ) ,
this , TQT_SLOT ( ratingOrScoreOrLabelsChanged ( const TQString & ) ) ) ;
connect ( StarManager : : instance ( ) , TQT_SIGNAL ( ratingsColorsChanged ( ) ) ,
this , TQT_SLOT ( ratingOrScoreOrLabelsChanged ( const TQString & ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( scoreChanged ( const TQString & , float ) ) ,
this , TQT_SLOT ( ratingOrScoreOrLabelsChanged ( const TQString & ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( labelsChanged ( const TQString & ) ) ,
this , TQT_SLOT ( ratingOrScoreOrLabelsChanged ( const TQString & ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( imageFetched ( const TQString & ) ) ,
this , TQT_SLOT ( imageFetched ( const TQString & ) ) ) ;
connect ( App : : instance ( ) , TQT_SIGNAL ( useScores ( bool ) ) ,
this , TQT_SLOT ( refreshCurrentTrackPage ( ) ) ) ;
connect ( App : : instance ( ) , TQT_SIGNAL ( useRatings ( bool ) ) ,
this , TQT_SLOT ( refreshCurrentTrackPage ( ) ) ) ;
connect ( MountPointManager : : instance ( ) , TQT_SIGNAL ( mediumConnected ( int ) ) ,
this , TQT_SLOT ( renderView ( ) ) ) ;
connect ( MountPointManager : : instance ( ) , TQT_SIGNAL ( mediumRemoved ( int ) ) ,
this , TQT_SLOT ( renderView ( ) ) ) ;
showContext ( KURL ( " current://track " ) ) ;
// setMinimumHeight( AmarokConfig::coverPreviewSize() + (fontMetrics().height()+2)*5 + tabBar()->height() );
}
ContextBrowser : : ~ ContextBrowser ( )
{
DEBUG_BLOCK
ThreadManager : : instance ( ) - > abortAllJobsNamed ( " CurrentTrackJob " ) ;
// Ensure the KHTMLPart dies before its KHTMLView dies,
// because KHTMLPart's dtoring relies on its KHTMLView still being alive
// (see bug 130494).
delete m_currentTrackPage ;
delete m_lyricsPage ;
delete m_wikiPage ;
m_cuefile - > clear ( ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////////
void ContextBrowser : : setFont ( const TQFont & newFont )
{
TQWidget : : setFont ( newFont ) ;
reloadStyleSheet ( ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC SLOTS
//////////////////////////////////////////////////////////////////////////////////////////
void ContextBrowser : : openURLRequest ( const KURL & url )
{
TQString artist , album , track ;
Amarok : : albumArtistTrackFromUrl ( url . path ( ) , artist , album , track ) ;
// All http links should be loaded inside wikipedia tab, as that is the only tab that should contain them.
// Streams should use stream:// protocol.
if ( url . protocol ( ) = = " http " )
{
if ( url . hasHTMLRef ( ) )
{
KURL base = url ;
base . setRef ( TQString ( ) ) ;
// Wikipedia also has links to otherpages with Anchors, so we have to check if it's for the current one
if ( m_wikiCurrentUrl = = base . url ( ) ) {
m_wikiPage - > gotoAnchor ( url . htmlRef ( ) ) ;
return ;
}
}
// new page
m_dirtyWikiPage = true ;
m_wikiCurrentEntry = TQString ( ) ;
showWikipedia ( url . url ( ) ) ;
}
else if ( url . protocol ( ) = = " show " )
{
if ( url . path ( ) . contains ( " suggestLyric- " ) )
{
TQString _url = url . url ( ) . mid ( url . url ( ) . find ( TQString ( " - " ) ) + 1 ) ;
debug ( ) < < " Clicked lyrics URL: " < < _url < < endl ;
m_dirtyLyricsPage = true ;
showLyrics ( _url ) ;
}
else if ( url . path ( ) = = " collectionSetup " )
{
CollectionView : : instance ( ) - > setupDirs ( ) ;
}
else if ( url . path ( ) = = " scriptmanager " )
{
ScriptManager : : instance ( ) - > show ( ) ;
ScriptManager : : instance ( ) - > raise ( ) ;
}
else if ( url . path ( ) = = " editLabels " )
{
showLabelsDialog ( ) ;
}
// Konqueror sidebar needs these
if ( url . path ( ) = = " context " ) { m_dirtyCurrentTrackPage = true ; showContext ( KURL ( " current://track " ) ) ; saveHtmlData ( ) ; }
if ( url . path ( ) = = " wiki " ) { m_dirtyWikiPage = true ; showWikipedia ( ) ; saveHtmlData ( ) ; }
if ( url . path ( ) = = " lyrics " ) { m_dirtyLyricsPage = true ; m_wikiJob = false ; showLyrics ( ) ; saveHtmlData ( ) ; }
}
else if ( url . protocol ( ) = = " runscript " )
{
ScriptManager : : instance ( ) - > runScript ( url . path ( ) ) ;
}
// When left-clicking on cover image, open browser with amazon site
else if ( url . protocol ( ) = = " fetchcover " )
{
TQString albumPath = CollectionDB : : instance ( ) - > albumImage ( artist , album , false , 0 ) ;
if ( albumPath = = CollectionDB : : instance ( ) - > notAvailCover ( false , 0 ) )
{
CollectionDB : : instance ( ) - > fetchCover ( this , artist , album , false ) ;
return ;
}
TQImage img ( albumPath ) ;
const TQString amazonUrl = img . text ( " amazon-url " ) ;
if ( amazonUrl . isEmpty ( ) )
KMessageBox : : information ( this , i18n ( " <p>There is no product information available for this image.<p>Right-click on image for menu. " ) ) ;
else
Amarok : : invokeBrowser ( amazonUrl ) ;
}
/* open konqueror with musicbrainz search result for artist-album */
else if ( url . protocol ( ) = = " musicbrainz " )
{
const TQString url = " http://www.musicbrainz.org/taglookup.html?artist=%1&album=%2&track=%3 " ;
Amarok : : invokeBrowser ( url . arg ( KURL : : encode_string_no_slash ( artist , 106 /*utf-8*/ ) ,
KURL : : encode_string_no_slash ( album , 106 /*utf-8*/ ) ,
KURL : : encode_string_no_slash ( track , 106 /*utf-8*/ ) ) ) ;
}
else if ( url . protocol ( ) = = " externalurl " )
Amarok : : invokeBrowser ( url . url ( ) . replace ( TQRegExp ( " ^externalurl: " ) , " http: " ) ) ;
else if ( url . protocol ( ) = = " lastfm " )
{
LastFm : : WebService * lfm = LastFm : : Controller : : instance ( ) - > getService ( ) ;
if ( url . path ( ) = = " skip " ) lfm - > skip ( ) ;
else if ( url . path ( ) = = " love " ) lfm - > love ( ) ;
else if ( url . path ( ) = = " ban " ) lfm - > ban ( ) ;
}
else if ( url . protocol ( ) = = " togglebox " )
{
if ( url . path ( ) = = " ra " ) m_relatedOpen ^ = true ;
else if ( url . path ( ) = = " ss " ) m_suggestionsOpen ^ = true ;
else if ( url . path ( ) = = " ft " ) m_favoritesOpen ^ = true ;
else if ( url . path ( ) = = " sl " ) m_labelsOpen ^ = true ;
}
else if ( url . protocol ( ) = = " seek " )
{
EngineController : : instance ( ) - > seek ( url . path ( ) . toLong ( ) ) ;
}
// browse albums of a related artist. Don't do this if we are viewing Home tab
else if ( url . protocol ( ) = = " artist " | | url . protocol ( ) = = " current " | | url . protocol ( ) = = " showlabel " )
{
if ( EngineController : : engine ( ) - > loaded ( ) ) // song must be active
showContext ( url ) ;
}
else if ( url . protocol ( ) = = " artistback " )
{
contextHistoryBack ( ) ;
}
else if ( url . protocol ( ) = = " wikipedia " )
{
m_dirtyWikiPage = true ;
TQString entry = unescapeHTMLAttr ( url . path ( ) ) ;
showWikipediaEntry ( entry ) ;
}
else if ( url . protocol ( ) = = " ggartist " )
{
const TQString url2 = TQString ( " http://www.google.com/musicsearch?q=%1&res=artist " )
. arg ( KURL : : encode_string_no_slash ( unescapeHTMLAttr ( url . path ( ) ) . replace ( " " , " + " ) , 106 /*utf-8*/ ) ) ;
Amarok : : invokeBrowser ( url2 ) ;
}
else if ( url . protocol ( ) = = " file " )
{
Playlist : : instance ( ) - > insertMedia ( url , Playlist : : DefaultOptions ) ;
}
else if ( url . protocol ( ) = = " stream " )
{
Playlist : : instance ( ) - > insertMedia ( KURL : : fromPathOrURL ( url . url ( ) . replace ( TQRegExp ( " ^stream: " ) , " http: " ) ) , Playlist : : DefaultOptions ) ;
}
else if ( url . protocol ( ) = = " compilationdisc " | | url . protocol ( ) = = " albumdisc " )
{
Playlist : : instance ( ) - > insertMedia ( expandURL ( url ) , Playlist : : DefaultOptions ) ;
}
else
HTMLView : : openURLRequest ( url ) ;
}
void ContextBrowser : : collectionScanStarted ( )
{
m_emptyDB = CollectionDB : : instance ( ) - > isEmpty ( ) ;
if ( m_emptyDB & & currentPage ( ) = = m_contextTab )
showCurrentTrack ( ) ;
}
void ContextBrowser : : collectionScanDone ( bool changed )
{
if ( CollectionDB : : instance ( ) - > isEmpty ( ) )
{
m_emptyDB = true ;
if ( currentPage ( ) = = m_contextTab )
showCurrentTrack ( ) ;
}
else if ( m_emptyDB )
{
m_emptyDB = false ;
PlaylistWindow : : self ( ) - > showBrowser ( " CollectionBrowser " ) ;
}
else if ( changed & & currentPage ( ) = = m_contextTab )
{
m_dirtyCurrentTrackPage = true ;
showCurrentTrack ( ) ;
}
}
void ContextBrowser : : renderView ( )
{
m_dirtyCurrentTrackPage = true ;
m_dirtyLyricsPage = true ;
m_dirtyWikiPage = true ;
m_emptyDB = CollectionDB : : instance ( ) - > isEmpty ( ) ;
showCurrentTrack ( ) ;
}
void ContextBrowser : : lyricsChanged ( const TQString & url ) {
if ( url = = EngineController : : instance ( ) - > bundle ( ) . url ( ) . path ( ) ) {
m_dirtyLyricsPage = true ;
if ( currentPage ( ) = = m_lyricsTab )
showLyrics ( ) ;
}
}
void ContextBrowser : : lyricsScriptChanged ( ) {
m_dirtyLyricsPage = true ;
if ( currentPage ( ) = = m_lyricsTab )
showLyrics ( ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// PROTECTED
//////////////////////////////////////////////////////////////////////////////////////////
void ContextBrowser : : engineNewMetaData ( const MetaBundle & bundle , bool trackChanged )
{
bool newMetaData = false ;
m_dirtyCurrentTrackPage = true ;
m_dirtyLyricsPage = true ;
m_wikiJob = 0 ; //New metadata, so let's forget previous wiki-fetching jobs
if ( MetaBundle ( m_currentURL ) . artist ( ) ! = bundle . artist ( ) )
m_dirtyWikiPage = true ;
// Prepend stream metadata history item to list
if ( ! m_metadataHistory . first ( ) . contains ( bundle . prettyTitle ( ) ) )
{
newMetaData = true ;
const TQString timeString = KGlobal : : locale ( ) - > formatTime ( TQTime : : currentTime ( ) ) . replace ( " " , " " ) ; // don't break over lines
m_metadataHistory . prepend ( TQString ( " <td valign='top'> " + timeString + " </td><td align='left'> " + escapeHTML ( bundle . prettyTitle ( ) ) + " </td> " ) ) ;
}
if ( currentPage ( ) = = m_contextTab & & ( bundle . url ( ) ! = m_currentURL | | newMetaData | | ! trackChanged ) )
showCurrentTrack ( ) ;
else if ( currentPage ( ) = = m_lyricsTab )
{
EngineController : : engine ( ) - > isStream ( ) ?
lyricsRefresh ( ) : // can't call showLyrics() because the url hasn't changed
showLyrics ( ) ;
}
else if ( CollectionDB : : instance ( ) - > isEmpty ( ) | | ! CollectionDB : : instance ( ) - > isValid ( ) )
showCurrentTrack ( ) ;
if ( trackChanged )
{
m_cuefile - > clear ( ) ;
if ( bundle . url ( ) . isLocalFile ( ) )
{
/** The cue file that is provided with the media might have different name than the
* media file itself , hence simply cutting the media extension and adding " .cue "
* is not always enough to find the matching cue file . In such cases we have
* to search for all the cue files in the directory and have a look inside them for
* the matching FILE = " " stanza . However the FILE = " " stanza does not always
* point at the corresponding media file ( e . g . it is quite often set to the misleading
* FILE = " audio.wav " WAV ) . Therfore we also have to check blindly if there is a cue
* file having the same name as the media file played , as described above .
*/
// look for the cue file that matches the media file played first
TQString path = bundle . url ( ) . path ( ) ;
TQString cueFile = path . left ( path . findRev ( ' . ' ) ) + " .cue " ;
m_cuefile - > setCueFileName ( cueFile ) ;
if ( m_cuefile - > load ( bundle . length ( ) ) )
debug ( ) < < " [CUEFILE]: " < < cueFile < < " - Shoot blindly, found and loaded. " < < endl ;
// if unlucky, let's have a look inside cue files, if any
else
{
debug ( ) < < " [CUEFILE]: " < < cueFile < < " - Shoot blindly and missed, searching for other cue files. " < < endl ;
bool foundCueFile = false ;
TQDir dir ( bundle . directory ( ) ) ;
dir . setFilter ( TQDir : : Files ) ;
dir . setNameFilter ( " *.cue *.CUE " ) ;
TQStringList cueFilesList = dir . entryList ( ) ;
if ( ! cueFilesList . empty ( ) )
for ( TQStringList : : Iterator it = cueFilesList . begin ( ) ; it ! = cueFilesList . end ( ) & & ! foundCueFile ; + + it )
{
TQFile file ( dir . filePath ( * it ) ) ;
if ( file . open ( IO_ReadOnly ) )
{
debug ( ) < < " [CUEFILE]: " < < * it < < " - Opened, looking for the matching FILE stanza. " < < endl ;
TQTextStream stream ( & file ) ;
TQString line ;
while ( ! stream . atEnd ( ) & & ! foundCueFile )
{
line = stream . readLine ( ) . simplifyWhiteSpace ( ) ;
if ( line . startsWith ( " file " , false ) )
{
line = line . mid ( 5 ) . remove ( ' " ' ) ;
if ( line . contains ( bundle . filename ( ) , false ) )
{
cueFile = dir . filePath ( * it ) ;
foundCueFile = true ;
m_cuefile - > setCueFileName ( cueFile ) ;
if ( m_cuefile - > load ( bundle . length ( ) ) )
debug ( ) < < " [CUEFILE]: " < < cueFile < < " - Looked inside cue files, found and loaded proper one " < < endl ;
}
}
}
file . close ( ) ;
}
}
if ( ! foundCueFile )
debug ( ) < < " [CUEFILE]: - Didn't find any matching cue file. " < < endl ;
}
}
}
}
void ContextBrowser : : engineStateChanged ( Engine : : State state , Engine : : State oldState )
{
DEBUG_BLOCK
if ( state ! = Engine : : Paused /*pause*/ & & oldState ! = Engine : : Paused /*resume*/
| | state = = Engine : : Empty )
{
// Pause shouldn't clear everything (but stop should, even when paused)
m_dirtyCurrentTrackPage = true ;
m_dirtyLyricsPage = true ;
m_wikiJob = 0 ; //let's forget previous wiki-fetching jobs
}
switch ( state )
{
case Engine : : Empty :
m_metadataHistory . clear ( ) ;
if ( currentPage ( ) = = m_contextTab | | currentPage ( ) = = m_lyricsTab )
{
showCurrentTrack ( ) ;
}
blockSignals ( true ) ;
setTabEnabled ( m_lyricsTab , false ) ;
if ( currentPage ( ) ! = m_wikiTab ) {
setTabEnabled ( m_wikiTab , false ) ;
m_dirtyWikiPage = true ;
}
else // current tab is wikitab, disable some buttons.
{
m_wikiToolBar - > setItemEnabled ( WIKI_ARTIST , false ) ;
m_wikiToolBar - > setItemEnabled ( WIKI_ALBUM , false ) ;
m_wikiToolBar - > setItemEnabled ( WIKI_TITLE , false ) ;
}
blockSignals ( false ) ;
break ;
case Engine : : Playing :
if ( oldState ! = Engine : : Paused )
m_metadataHistory . clear ( ) ;
blockSignals ( true ) ;
setTabEnabled ( m_lyricsTab , true ) ;
setTabEnabled ( m_wikiTab , true ) ;
m_wikiToolBar - > setItemEnabled ( WIKI_ARTIST , true ) ;
m_wikiToolBar - > setItemEnabled ( WIKI_ALBUM , true ) ;
m_wikiToolBar - > setItemEnabled ( WIKI_TITLE , true ) ;
blockSignals ( false ) ;
break ;
default :
;
}
}
void ContextBrowser : : saveHtmlData ( )
{
TQFile exportedDocument ( Amarok : : saveLocation ( ) + " contextbrowser.html " ) ;
if ( ! exportedDocument . open ( IO_WriteOnly ) )
warning ( ) < < " Failed to open file " < < exportedDocument . name ( )
< < " write-only " < < endl ;
else {
TQTextStream stream ( & exportedDocument ) ;
stream . setEncoding ( TQTextStream : : UnicodeUTF8 ) ;
stream < < m_HTMLSource // the pure html data..
. replace ( " <html> " ,
TQString ( " <html><head><style type= \" text/css \" > "
" %1</style></head> " )
. arg ( HTMLView : : loadStyleSheet ( ) ) ) ; // and the
// stylesheet
// code
exportedDocument . close ( ) ;
}
}
void ContextBrowser : : paletteChange ( const TQPalette & /* pal */ )
{
// KTabWidget::paletteChange( pal );
HTMLView : : paletteChange ( ) ;
reloadStyleSheet ( ) ;
}
void ContextBrowser : : reloadStyleSheet ( )
{
m_currentTrackPage - > setUserStyleSheet ( HTMLView : : loadStyleSheet ( ) ) ;
m_lyricsPage - > setUserStyleSheet ( HTMLView : : loadStyleSheet ( ) ) ;
m_wikiPage - > setUserStyleSheet ( HTMLView : : loadStyleSheet ( ) ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// PROTECTED SLOTS
//////////////////////////////////////////////////////////////////////////////////////////
//parts of this function from ktabwidget.cpp, copyright (C) 2003 Zack Rusin and Stephan Binner
//fucking setCurrentTab() isn't virtual so we have to override this instead =(
void ContextBrowser : : wheelDelta ( int delta )
{
if ( count ( ) < 2 | | delta = = 0 )
return ;
int index = currentPageIndex ( ) , start = index ;
do
{
if ( delta < 0 )
index = ( index + 1 ) % count ( ) ;
else
{
index = index - 1 ;
if ( index < 0 )
index = count ( ) - 1 ;
}
if ( index = = start ) // full circle, none enabled
return ;
} while ( ! isTabEnabled ( page ( index ) ) ) ;
setCurrentPage ( index ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE SLOTS
//////////////////////////////////////////////////////////////////////////////////////////
void ContextBrowser : : tabChanged ( TQWidget * page )
{
DEBUG_FUNC_INFO
setFocusProxy ( page ) ; //so focus is given to a sensible widget when the tab is opened
if ( page = = m_contextTab )
showCurrentTrack ( ) ;
else if ( page = = m_lyricsTab )
showLyrics ( ) ;
else if ( page = = m_wikiTab )
showWikipedia ( ) ;
}
void ContextBrowser : : slotContextMenu ( const TQString & urlString , const TQPoint & point )
{
enum { APPEND , ASNEXT , MAKE , MEDIA_DEVICE , INFO , TITLE , RELATED , SUGGEST , FAVES , FRESHPODCASTS , NEWALBUMS , FAVALBUMS , LABELS } ;
debug ( ) < < " url string: " < < urlString < < endl ;
if ( urlString . startsWith ( " musicbrainz " ) | |
urlString . startsWith ( " externalurl " ) | |
urlString . startsWith ( " show:suggest " ) | |
urlString . startsWith ( " http " ) | |
urlString . startsWith ( " wikipedia " ) | |
urlString . startsWith ( " seek " ) | |
urlString . startsWith ( " ggartist " ) | |
urlString . startsWith ( " artistback " ) | |
urlString . startsWith ( " current " ) | |
urlString . startsWith ( " lastfm " ) | |
urlString . startsWith ( " showlabel " ) | |
urlString . startsWith ( " show:editLabels " ) | |
currentPage ( ) ! = m_contextTab )
return ;
KURL url ( urlString ) ;
KPopupMenu menu ;
KURL : : List urls ( url ) ;
TQString artist , album , track ; // track unused here
Amarok : : albumArtistTrackFromUrl ( url . path ( ) , artist , album , track ) ;
if ( urlString . isEmpty ( ) )
{
debug ( ) < < " url string empty. loaded? " < < EngineController : : engine ( ) - > loaded ( ) < < endl ;
if ( EngineController : : engine ( ) - > loaded ( ) )
{
menu . setCheckable ( true ) ;
menu . insertItem ( i18n ( " Show Labels " ) , LABELS ) ;
menu . insertItem ( i18n ( " Show Related Artists " ) , RELATED ) ;
menu . insertItem ( i18n ( " Show Suggested Songs " ) , SUGGEST ) ;
menu . insertItem ( i18n ( " Show Favorite Tracks " ) , FAVES ) ;
menu . setItemChecked ( RELATED , m_showRelated ) ;
menu . setItemChecked ( SUGGEST , m_showSuggested ) ;