1 /* This file is part of Clementine.
2    Copyright 2010, David Sansome <me@davidsansome.com>
3 
4    Clementine is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    Clementine is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with Clementine.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #ifndef PLAYLISTVIEW_H
19 #define PLAYLISTVIEW_H
20 
21 #include <memory>
22 
23 #include <QBasicTimer>
24 #include <QProxyStyle>
25 #include <QTreeView>
26 
27 #include "playlist.h"
28 
29 class QCommonStyle;
30 
31 class Application;
32 class DynamicPlaylistControls;
33 class LibraryBackend;
34 class PlaylistHeader;
35 class RadioLoadingIndicator;
36 class RatingItemDelegate;
37 class QTimeLine;
38 
39 // This proxy style works around a bug/feature introduced in Qt 4.7's QGtkStyle
40 // that uses Gtk to paint row backgrounds, ignoring any custom brush or palette
41 // the caller set in the QStyleOption.  That breaks our currently playing track
42 // animation, which relies on the background painted by Qt to be transparent.
43 // This proxy style uses QCommonStyle to paint the affected elements.
44 // This class is used by the global search view as well.
45 class PlaylistProxyStyle : public QProxyStyle {
46  public:
47   PlaylistProxyStyle();
48   void drawControl(ControlElement element, const QStyleOption* option,
49                    QPainter* painter, const QWidget* widget) const;
50   void drawPrimitive(PrimitiveElement element, const QStyleOption* option,
51                      QPainter* painter, const QWidget* widget) const;
52 
53  private:
54   std::unique_ptr<QCommonStyle> common_style_;
55 };
56 
57 class PlaylistView : public QTreeView {
58   Q_OBJECT
59  public:
60   ~PlaylistView();
61 
62   enum BackgroundImageType { Default, None, Custom, AlbumCover };
63 
64   PlaylistView(QWidget* parent = nullptr);
65 
66   static const int kStateVersion;
67   // Constants for settings: are persistent, values should not be changed
68   static const char* kSettingBackgroundImageType;
69   static const char* kSettingBackgroundImageFilename;
70 
71   static const int kDefaultBlurRadius;
72   static const int kDefaultOpacityLevel;
73 
74   static ColumnAlignmentMap DefaultColumnAlignment();
75 
76   void SetApplication(Application* app);
77   void SetItemDelegates(LibraryBackend* backend);
78   void SetPlaylist(Playlist* playlist);
79   void RemoveSelected(bool deleting_from_disk);
80 
SetReadOnlySettings(bool read_only)81   void SetReadOnlySettings(bool read_only) { read_only_settings_ = read_only; }
82 
playlist()83   Playlist* playlist() const { return playlist_; }
background_image_type()84   BackgroundImageType background_image_type() const {
85     return background_image_type_;
86   }
87   Qt::Alignment column_alignment(int section) const;
88 
89   // QTreeView
90   void drawTree(QPainter* painter, const QRegion& region) const;
91   void drawRow(QPainter* painter, const QStyleOptionViewItem& option,
92                const QModelIndex& index) const;
93   void setModel(QAbstractItemModel* model);
94 
95  public slots:
96   void ReloadSettings();
97   void StopGlowing();
98   void StartGlowing();
99   void JumpToCurrentlyPlayingTrack();
100   void JumpToLastPlayedTrack();
101   void closeEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint);
102   void DynamicModeChanged(bool dynamic);
103   void SetColumnAlignment(int section, Qt::Alignment alignment);
104 
105   void CopyCurrentSongToClipboard() const;
106   void CurrentSongChanged(const Song& new_song, const QString& uri,
107                           const QImage& cover_art);
108   void PlayerStopped();
109 
110 signals:
111   void PlayItem(const QModelIndex& index);
112   void PlayPause();
113   void RightClicked(const QPoint& global_pos, const QModelIndex& index);
114   void SeekForward();
115   void SeekBackward();
116   void FocusOnFilterSignal(QKeyEvent* event);
117   void BackgroundPropertyChanged();
118   void ColumnAlignmentChanged(const ColumnAlignmentMap& alignment);
119 
120  protected:
121   // QWidget
122   void keyPressEvent(QKeyEvent* event);
123   void contextMenuEvent(QContextMenuEvent* e);
124   void hideEvent(QHideEvent* event);
125   void showEvent(QShowEvent* event);
126   void timerEvent(QTimerEvent* event);
127   void mouseMoveEvent(QMouseEvent* event);
128   void mousePressEvent(QMouseEvent* event);
129   void leaveEvent(QEvent*);
130   void paintEvent(QPaintEvent* event);
131   void dragMoveEvent(QDragMoveEvent* event);
132   void dragEnterEvent(QDragEnterEvent* event);
133   void dragLeaveEvent(QDragLeaveEvent* event);
134   void dropEvent(QDropEvent* event);
135   void resizeEvent(QResizeEvent* event);
136   bool eventFilter(QObject* object, QEvent* event);
137   void focusInEvent(QFocusEvent* event);
138 
139   // QAbstractScrollArea
140   void scrollContentsBy(int dx, int dy);
141 
142   // QAbstractItemView
143   void rowsInserted(const QModelIndex& parent, int start, int end);
144 
145  private slots:
146   void LoadGeometry();
147   void LoadRatingLockStatus();
148   void DirtyGeometry();
149   void DirtySettings();
150   void SaveGeometry(QSettings* settings);
151   void SetRatingLockStatus(bool state);
152   void GlowIntensityChanged();
153   void InhibitAutoscrollTimeout();
154   void MaybeAutoscroll();
155   void InvalidateCachedCurrentPixmap();
156   void PlaylistDestroyed();
157 
158   void SaveSettings(QSettings* s);
159   void StretchChanged(bool stretch);
160 
161   void RatingHoverIn(const QModelIndex& index, const QPoint& pos);
162   void RatingHoverOut();
163 
164   void FadePreviousBackgroundImage(qreal value);
165 
166  private:
167   void ReloadBarPixmaps();
168   QList<QPixmap> LoadBarPixmap(const QString& filename);
169   void UpdateCachedCurrentRowPixmap(QStyleOptionViewItem option,
170                                     const QModelIndex& index);
171 
set_background_image_type(BackgroundImageType bg)172   void set_background_image_type(BackgroundImageType bg) {
173     background_image_type_ = bg;
174     emit BackgroundPropertyChanged();
175   }
176   // Save image as the background_image_ after applying some modifications
177   // (opacity, ...).
178   // Should be used instead of modifying background_image_ directly
179   void set_background_image(const QImage& image);
180 
181  private:
182   static const int kGlowIntensitySteps;
183   static const int kAutoscrollGraceTimeout;
184   static const int kDropIndicatorWidth;
185   static const int kDropIndicatorGradientWidth;
186 
187   QList<int> GetEditableColumns();
188   QModelIndex NextEditableIndex(const QModelIndex& current);
189   QModelIndex PrevEditableIndex(const QModelIndex& current);
190 
191   void RepositionDynamicControls();
192 
193   Application* app_;
194   PlaylistProxyStyle* style_;
195   Playlist* playlist_;
196   PlaylistHeader* header_;
197   bool setting_initial_header_layout_;
198   bool upgrading_from_qheaderview_;
199   bool read_only_settings_;
200   int upgrading_from_version_;
201   bool header_loaded_;
202 
203   bool background_initialized_;
204   BackgroundImageType background_image_type_;
205   // Used if background image is a filemane
206   QString background_image_filename_;
207   // Stores the background image to be displayed. As we want this image to be
208   // particular (in terms of format, opacity), you should probably use
209   // set_background_image_type instead of modifying background_image_ directly
210   QImage background_image_;
211   int blur_radius_;
212   int opacity_level_;
213 
214   QImage current_song_cover_art_;
215   QPixmap cached_scaled_background_image_;
216 
217   // For fading when image change
218   QPixmap previous_background_image_;
219   qreal previous_background_image_opacity_;
220   QTimeLine* fade_animation_;
221 
222   // To know if we should redraw the background or not
223   int last_height_;
224   int last_width_;
225   bool force_background_redraw_;
226 
227   bool glow_enabled_;
228   bool currently_glowing_;
229   QBasicTimer glow_timer_;
230   int glow_intensity_step_;
231   QModelIndex last_current_item_;
232   QRect last_glow_rect_;
233 
234   RatingItemDelegate* rating_delegate_;
235 
236   QTimer* inhibit_autoscroll_timer_;
237   bool inhibit_autoscroll_;
238   bool currently_autoscrolling_;
239 
240   int row_height_;  // Used to invalidate the currenttrack_bar pixmaps
241   QList<QPixmap> currenttrack_bar_left_;
242   QList<QPixmap> currenttrack_bar_mid_;
243   QList<QPixmap> currenttrack_bar_right_;
244   QPixmap currenttrack_play_;
245   QPixmap currenttrack_pause_;
246 
247   QRegion current_paint_region_;
248   QPixmap cached_current_row_;
249   QRect cached_current_row_rect_;
250   int cached_current_row_row_;
251 
252   QPixmap cached_tree_;
253   int drop_indicator_row_;
254   bool drag_over_;
255 
256   bool dirty_geometry_;
257   bool dirty_settings_;
258 
259   bool ratings_locked_;  // To store Ratings section lock status
260 
261   DynamicPlaylistControls* dynamic_controls_;
262 
263   ColumnAlignmentMap column_alignment_;
264 };
265 
266 #endif  // PLAYLISTVIEW_H
267