File indexing completed on 2024-05-19 04:48:41

0001 /****************************************************************************************
0002  * Copyright (c) 2007-2010 Bart Cerneels <bart.cerneels@kde.org>                        *
0003  * Copyright (c) 2007-2008 Nikolaj Hald Nielsen <nhn@kde.org>                           *
0004  * Copyright (c) 2007 Henry de Valence <hdevalence@gmail.com>                           *
0005  *                                                                                      *
0006  * This program is free software; you can redistribute it and/or modify it under        *
0007  * the terms of the GNU General Public License as published by the Free Software        *
0008  * Foundation; either version 2 of the License, or (at your option) any later           *
0009  * version.                                                                             *
0010  *                                                                                      *
0011  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0013  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0014  *                                                                                      *
0015  * You should have received a copy of the GNU General Public License along with         *
0016  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0017  ****************************************************************************************/
0018 
0019 #define DEBUG_PREFIX "PodcastCategory"
0020 
0021 #include "PodcastCategory.h"
0022 
0023 #include "amarokconfig.h"
0024 #include "amarokurls/AmarokUrl.h"
0025 #include "App.h"
0026 #include "browsers/InfoProxy.h"
0027 #include "core/support/Debug.h"
0028 #include "core/meta/support/MetaUtility.h"
0029 #include "PaletteHandler.h"
0030 #include "PodcastModel.h"
0031 #include "PlaylistBrowserView.h"
0032 #include "widgets/PrettyTreeRoles.h"
0033 
0034 #include <QModelIndexList>
0035 #include <QTextBrowser>
0036 
0037 #include <QAction>
0038 #include <QIcon>
0039 #include <QStandardPaths>
0040 #include <KUrlRequesterDialog>
0041 
0042 #include <KLocalizedString>
0043 #include <KToolBar>
0044 
0045 namespace The
0046 {
0047     PlaylistBrowserNS::PodcastCategory* podcastCategory()
0048     {
0049         return PlaylistBrowserNS::PodcastCategory::instance();
0050     }
0051 }
0052 
0053 using namespace PlaylistBrowserNS;
0054 
0055 QString PodcastCategory::s_configGroup( QStringLiteral("Podcast View") );
0056 
0057 PodcastCategory* PodcastCategory::s_instance = nullptr;
0058 
0059 PodcastCategory*
0060 PodcastCategory::instance()
0061 {
0062     return s_instance ? s_instance : new PodcastCategory( nullptr );
0063 }
0064 
0065 void
0066 PodcastCategory::destroy()
0067 {
0068     if( s_instance )
0069     {
0070         delete s_instance;
0071         s_instance = nullptr;
0072     }
0073 }
0074 
0075 PodcastCategory::PodcastCategory( QWidget *parent )
0076     : PlaylistBrowserCategory( Playlists::PodcastChannelPlaylist,
0077                                QStringLiteral("podcasts"),
0078                                s_configGroup,
0079                                The::podcastModel(),
0080                                parent )
0081 {
0082     setPrettyName( i18n( "Podcasts" ) );
0083     setShortDescription( i18n( "List of podcast subscriptions and episodes" ) );
0084     setIcon( QIcon::fromTheme( QStringLiteral("podcast-amarok") ) );
0085 
0086     setLongDescription( i18n( "Manage your podcast subscriptions and browse individual episodes. "
0087                               "Downloading episodes to the disk is also done here, or you can tell "
0088                               "Amarok to do this automatically." ) );
0089 
0090     setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, QStringLiteral("amarok/images/hover_info_podcasts.png") ) );
0091 
0092     // set background
0093     if( AmarokConfig::showBrowserBackgroundImage() )
0094         setBackgroundImage( imagePath() );
0095 
0096     QAction *addPodcastAction = new QAction( QIcon::fromTheme( QStringLiteral("list-add-amarok") ), i18n("&Add Podcast"),
0097                                              m_toolBar );
0098     addPodcastAction->setPriority( QAction::NormalPriority );
0099     m_toolBar->insertAction( m_separator, addPodcastAction );
0100     connect( addPodcastAction, &QAction::triggered, The::podcastModel(), &PodcastModel::addPodcast );
0101 
0102     QAction *updateAllAction = new QAction( QIcon::fromTheme(QStringLiteral("view-refresh-amarok")), QString(), m_toolBar );
0103     updateAllAction->setToolTip( i18n("&Update All") );
0104     updateAllAction->setPriority( QAction::LowPriority );
0105     m_toolBar->insertAction( m_separator, updateAllAction );
0106     connect( updateAllAction, &QAction::triggered,
0107              The::podcastModel(), &PodcastModel::refreshPodcasts );
0108 
0109 
0110     QAction *importOpmlAction = new QAction( QIcon::fromTheme(QStringLiteral("document-import"))
0111                                              , i18n( "Import OPML File" )
0112                                              , m_toolBar
0113                                          );
0114     importOpmlAction->setToolTip( i18n( "Import OPML File" ) );
0115     importOpmlAction->setPriority( QAction::LowPriority );
0116     m_toolBar->addAction( importOpmlAction );
0117     connect( importOpmlAction, &QAction::triggered, this, &PodcastCategory::slotImportOpml );
0118 
0119     PlaylistBrowserView *view = static_cast<PlaylistBrowserView*>( playlistView() );
0120     connect( view, &PlaylistBrowserView::currentItemChanged, this, &PodcastCategory::showInfo );
0121 
0122     //transparency
0123 //    QPalette p = m_podcastTreeView->palette();
0124 //    QColor c = p.color( QPalette::Base );
0125 //    c.setAlpha( 0 );
0126 //    p.setColor( QPalette::Base, c );
0127 //
0128 //    c = p.color( QPalette::AlternateBase );
0129 //    c.setAlpha( 77 );
0130 //    p.setColor( QPalette::AlternateBase, c );
0131 //
0132 //    m_podcastTreeView->setPalette( p );
0133 //
0134 //    QSizePolicy sizePolicy1(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
0135 //    sizePolicy1.setHorizontalStretch(0);
0136 //    sizePolicy1.setVerticalStretch(0);
0137 //    sizePolicy1.setHeightForWidth(m_podcastTreeView->sizePolicy().hasHeightForWidth());
0138 //    m_podcastTreeView->setSizePolicy(sizePolicy1);
0139 }
0140 
0141 PodcastCategory::~PodcastCategory()
0142 {
0143 }
0144 
0145 void
0146 PodcastCategory::showInfo( const QModelIndex &index )
0147 {
0148     if( !index.isValid() )
0149         return;
0150 
0151     const int row = index.row();
0152     QString description;
0153     QString title( index.data( Qt::DisplayRole ).toString() );
0154     QString subtitle( index.sibling( row, SubtitleColumn ).data( Qt::DisplayRole ).toString() );
0155     QUrl imageUrl( qvariant_cast<QUrl>(
0156         index.sibling( row, ImageColumn ).data( Qt::DisplayRole )
0157     ) );
0158     QString author( index.sibling( row, AuthorColumn ).data( Qt::DisplayRole ).toString() );
0159     QStringList keywords( qvariant_cast<QStringList>(
0160         index.sibling( row, KeywordsColumn ).data( Qt::DisplayRole )
0161     ) );
0162     bool isEpisode = index.sibling( row, IsEpisodeColumn ).data( Qt::DisplayRole ).toBool();
0163     QString authorAndPubDate;
0164     
0165     if( !author.isEmpty() )
0166     {
0167         authorAndPubDate = QStringLiteral( "<b>%1</b> %2 " )
0168             .arg( i18n( "By" ),
0169                   author.toHtmlEscaped() );
0170     }
0171 
0172     if( !subtitle.isEmpty() )
0173     {
0174         description += QStringLiteral( "<h1 class=\"subtitle\">%1</h1>" )
0175             .arg( subtitle.toHtmlEscaped() );
0176     }
0177 
0178     if( !imageUrl.isEmpty() )
0179     {
0180         description += QString( "<p style=\"float:right;\"><img src=\"%1\" onclick=\""
0181             "if (this.style.width=='150px') {"
0182                 "this.style.width='auto';"
0183                 "this.style.marginLeft='0em';"
0184                 "this.style.cursor='-webkit-zoom-out';"
0185                 "this.parentNode.style.float='inherit';"
0186                 "this.parentNode.style.textAlign='center';"
0187             "} else {"
0188                 "this.style.width='150px';"
0189                 "this.style.marginLeft='1em';"
0190                 "this.style.cursor='-webkit-zoom-in';"
0191                 "this.parentNode.style.float='right';"
0192                 "this.parentNode.style.textAlign='inherit';"
0193             "}\""
0194             " style=\"width: 150px; margin-left: 1em;"
0195             " margin-right: 0em; cursor: -webkit-zoom-in;\""
0196             "/></p>" )
0197             .arg( imageUrl.url().toHtmlEscaped() );
0198     }
0199 
0200     if( isEpisode )
0201     {
0202         QDateTime pubDate( index.sibling( row, DateColumn ).data( Qt::DisplayRole ).toDateTime() );
0203         
0204         if( pubDate.isValid() )
0205         {
0206             authorAndPubDate += QStringLiteral( "<b>%1</b> %2" )
0207                 .arg( i18nc( "Podcast published on date", "On" ),
0208                       QLocale().toString( pubDate, QLocale::ShortFormat ) );
0209         }
0210     }
0211 
0212     if( !authorAndPubDate.isEmpty() )
0213     {
0214         description += QStringLiteral( "<p>%1</p>" )
0215             .arg( authorAndPubDate );
0216     }
0217 
0218     if( isEpisode )
0219     {
0220         int fileSize = index.sibling( row, FilesizeColumn ).data( Qt::DisplayRole ).toInt();
0221 
0222         if( fileSize != 0 )
0223         {
0224             description += QStringLiteral( "<p><b>%1</b> %2</p>" )
0225                 .arg( i18n( "File Size:" ),
0226                       Meta::prettyFilesize( fileSize ) );
0227         }
0228 
0229     }
0230     else
0231     {
0232         QDate subsDate( index.sibling( row, DateColumn ).data( Qt::DisplayRole ).toDate() );
0233         
0234         if( subsDate.isValid() )
0235         {
0236             description += QStringLiteral( "<p><b>%1</b> %2</p>" )
0237                 .arg( i18n( "Subscription Date:" ),
0238                       QLocale().toString( subsDate, QLocale::ShortFormat ) );
0239         }
0240     }
0241 
0242     if( !keywords.isEmpty() )
0243     {
0244         description += QStringLiteral( "<p><b>%1</b> %2</p>" )
0245             .arg( i18n( "Keywords:" ),
0246                   keywords.join( QStringLiteral(", ") ).toHtmlEscaped() );
0247     }
0248 
0249     description += index.data( PrettyTreeRoles::ByLineRole ).toString();
0250 
0251     description = QString(
0252         "<html>"
0253         "    <head>"
0254         "        <title>%1</title>"
0255         "        <style type=\"text/css\">"
0256         "body {color: %3;}"
0257         "::selection {background-color: %4;}"
0258         "h1 {text-align:center; font-size: 1.2em;}"
0259         "h1.subtitle {text-align:center; font-size: 1em; font-weight: normal;}"
0260         "        </style>"
0261         "    </head>"
0262         "    <body>"
0263         "        <h1>%1</h1>"
0264         "        %2"
0265         "    </body>"
0266         "</html>")
0267         .arg( title.toHtmlEscaped(),
0268               description,
0269               pApp->palette().brush( QPalette::Text ).color().name(),
0270               The::paletteHandler()->highlightColor().name() );
0271     
0272     QVariantMap map;
0273     map[QStringLiteral("service_name")] = title;
0274     map[QStringLiteral("main_info")] = description;
0275     The::infoProxy()->setInfo( map );
0276 }
0277 
0278 void
0279 PodcastCategory::slotImportOpml()
0280 {
0281     AmarokUrl( QStringLiteral("amarok://service-podcastdirectory/addOpml") ).run();
0282 }
0283