1 /***************************************************************************
2  *   Copyright (C) 2015-2021 by Ilya Kotov                                 *
3  *   forkotov02@ya.ru                                                      *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20 
21 #include <QSettings>
22 #include <QPainter>
23 #include <QApplication>
24 #include <qmmp/qmmp.h>
25 #include <qmmpui/playlistmanager.h>
26 #include "listwidgetdrawer.h"
27 
28 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
29 #define horizontalAdvance width
30 #endif
31 
32 // |= number=|=row1=|=row2=|=extra= duration=|
33 
ListWidgetDrawer()34 ListWidgetDrawer::ListWidgetDrawer()
35 {
36     m_header_model = PlayListManager::instance()->headerModel();
37     readSettings();
38 }
39 
~ListWidgetDrawer()40 ListWidgetDrawer::~ListWidgetDrawer()
41 {
42     if(m_metrics)
43         delete m_metrics;
44     if(m_extra_metrics)
45         delete m_extra_metrics;
46     if(m_bold_metrics)
47         delete m_bold_metrics;
48 }
49 
readSettings()50 void ListWidgetDrawer::readSettings()
51 {
52     QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
53     settings.beginGroup("Simple");
54     m_show_anchor = settings.value("pl_show_anchor", false).toBool();
55     m_show_number = settings.value("pl_show_numbers", true).toBool();
56     m_show_lengths = settings.value("pl_show_lengths", true).toBool();
57     m_align_numbres = settings.value("pl_align_numbers", false).toBool();
58     m_show_splitters = settings.value("pl_show_splitters", true).toBool();
59     m_font = qApp->font("QAbstractItemView");
60 
61     if(!settings.value("use_system_fonts", true).toBool())
62         m_font.fromString(settings.value("pl_font", m_font.toString()).toString());
63 
64     m_extra_font = m_font;
65     m_extra_font.setPointSize(m_font.pointSize() - 1);
66     m_use_system_colors = settings.value("pl_system_colors", true).toBool();
67     loadSystemColors();
68 
69     if(!m_use_system_colors)
70     {
71         m_normal_bg.setNamedColor(settings.value("pl_bg1_color", m_normal_bg.name()).toString());
72         m_alternate.setNamedColor(settings.value("pl_bg2_color", m_alternate.name()).toString());
73         m_selected_bg.setNamedColor(settings.value("pl_highlight_color", m_selected_bg.name()).toString());
74         m_normal.setNamedColor(settings.value("pl_normal_text_color", m_normal.name()).toString());
75         m_current.setNamedColor(settings.value("pl_current_text_color",m_current.name()).toString());
76         m_highlighted.setNamedColor(settings.value("pl_hl_text_color",m_highlighted.name()).toString());
77         m_splitter.setNamedColor(settings.value("pl_splitter_color", m_splitter).toString());
78         m_group_text.setNamedColor(settings.value("pl_group_text", m_group_text.name()).toString());
79         if(settings.value("pl_override_group_bg", false).toBool())
80         {
81             m_group_bg.setNamedColor(settings.value("pl_group_bg", m_normal_bg.name()).toString());
82             m_group_alt_bg = m_group_bg;
83         }
84         else
85         {
86             m_group_bg = m_normal_bg;
87             m_group_alt_bg = m_alternate;
88         }
89         if(settings.value("pl_override_current_bg", false).toBool())
90         {
91             m_current_bg.setNamedColor(settings.value("pl_current_bg_color", m_normal_bg.name()).toString());
92             m_current_alt_bg = m_current_bg;
93         }
94         else
95         {
96             m_current_bg = m_normal_bg;
97             m_current_alt_bg = m_alternate;
98         }
99     }
100 
101     if (m_update)
102     {
103         delete m_metrics;
104         delete m_extra_metrics;
105         delete m_bold_metrics;
106     }
107     m_update = true;
108     m_metrics = new QFontMetrics(m_font);
109     m_extra_metrics = new QFontMetrics(m_extra_font);
110     m_font.setBold(true);
111     m_bold_metrics = new QFontMetrics(m_font);
112     m_font.setBold(false);
113     m_padding = m_metrics->horizontalAdvance("9")/2;
114     m_row_height = m_metrics->lineSpacing() + 1;
115 }
116 
loadSystemColors()117 void ListWidgetDrawer::loadSystemColors()
118 {
119     m_normal = qApp->palette().color(QPalette::Text);
120     m_alternate = qApp->palette().color(QPalette::AlternateBase);
121     m_current = qApp->palette().color(QPalette::Text);
122     m_highlighted = qApp->palette().color(QPalette::HighlightedText);
123     m_normal_bg = qApp->palette().color(QPalette::Base);
124     m_selected_bg = qApp->palette().color(QPalette::Highlight);
125     m_splitter = m_normal;
126     m_group_bg = m_normal_bg;
127     m_group_alt_bg = m_alternate,
128     m_group_text = m_normal;
129     m_current_bg = m_normal_bg;
130     m_current_alt_bg = m_alternate;
131 }
132 
rowHeight() const133 int ListWidgetDrawer::rowHeight() const
134 {
135     return m_row_height;
136 }
137 
numberWidth() const138 int ListWidgetDrawer::numberWidth() const
139 {
140     return m_number_width;
141 }
142 
calculateNumberWidth(int count)143 void ListWidgetDrawer::calculateNumberWidth(int count)
144 {
145     //song numbers width
146     if(m_show_number && m_align_numbres && count)
147         m_number_width = m_bold_metrics->horizontalAdvance("9") * QString::number(count).size();
148     else
149         m_number_width = 0;
150 }
151 
setSingleColumnMode(int enabled)152 void ListWidgetDrawer::setSingleColumnMode(int enabled)
153 {
154     m_single_column = enabled;
155 }
156 
prepareRow(ListWidgetRow * row)157 void ListWidgetDrawer::prepareRow(ListWidgetRow *row)
158 {
159     if(m_number_width && m_single_column)
160         row->numberColumnWidth = m_number_width + 2 * m_padding;
161     else
162         row->numberColumnWidth = 0;
163 
164     if(row->flags & ListWidgetRow::GROUP)
165     {
166         row->titles[0] = m_metrics->elidedText (row->titles[0], Qt::ElideRight,
167                                             row->rect.width() - m_number_width - 12 - 70);
168         return;
169     }
170 
171     QFontMetrics *metrics = (row->flags & ListWidgetRow::CURRENT) ? m_bold_metrics : m_metrics;
172 
173     if(row->titles.count() == 1)
174     {
175         if(m_show_number && !m_align_numbres)
176             row->titles[0].prepend(QString("%1").arg(row->number)+". ");
177 
178         if((m_show_lengths && !row->length.isEmpty()) || !row->extraString.isEmpty())
179             row->lengthColumnWidth = m_padding;
180         else
181             row->lengthColumnWidth = 0;
182 
183         if(m_show_lengths && !row->length.isEmpty())
184             row->lengthColumnWidth += metrics->horizontalAdvance(row->length) + m_padding;
185 
186         if(!row->extraString.isEmpty())
187             row->lengthColumnWidth += m_extra_metrics->horizontalAdvance(row->extraString) + m_padding;
188     }
189 
190     //elide title
191     int visible_width = row->rect.width() - row->lengthColumnWidth - row->numberColumnWidth;
192 
193     if(row->titles.count() == 1 && !row->lengthColumnWidth)
194     {
195         row->titles[0] = metrics->elidedText (row->titles[0], Qt::ElideRight, visible_width - 2 * m_padding);
196         return;
197     }
198     else if(row->titles.count() == 1)
199     {
200         row->titles[0] = metrics->elidedText (row->titles[0], Qt::ElideRight, visible_width - m_padding);
201         return;
202     }
203 
204     for(int i = 0; i < row->titles.count(); ++i)
205     {
206         int size = row->sizes[i];
207         if(i == row->trackStateColumn && !row->extraString.isEmpty())
208         {
209             int text_size = qMax(0, size - 3 * m_padding - m_extra_metrics->horizontalAdvance(row->extraString));
210             row->titles[i] = metrics->elidedText (row->titles[i], Qt::ElideRight, text_size);
211             row->extraString = m_extra_metrics->elidedText(row->extraString, Qt::ElideRight,
212                                                            size - 3 * m_padding - metrics->horizontalAdvance(row->titles[i]));
213         }
214         else
215         {
216             row->titles[i] = metrics->elidedText (row->titles[i], Qt::ElideRight, size - 2 * m_padding);
217         }
218     }
219 }
220 
fillBackground(QPainter * painter,int width,int height)221 void ListWidgetDrawer::fillBackground(QPainter *painter, int width, int height)
222 {
223     painter->setBrush(m_normal_bg);
224     painter->setPen(m_normal_bg);
225     painter->drawRect(0,0,width,height);
226 }
227 
drawBackground(QPainter * painter,ListWidgetRow * row,int index)228 void ListWidgetDrawer::drawBackground(QPainter *painter, ListWidgetRow *row, int index)
229 {
230     if(row->flags & ListWidgetRow::SELECTED)
231     {
232         painter->setBrush(m_selected_bg);
233     }
234     else if(row->flags & ListWidgetRow::GROUP)
235     {
236         if(index % 2)
237         {
238             painter->setBrush(QBrush(m_group_alt_bg));
239             painter->setPen(m_group_alt_bg);
240         }
241         else
242         {
243             painter->setBrush(QBrush(m_group_bg));
244             painter->setPen(m_group_bg);
245         }
246     }
247     else if(row->flags & ListWidgetRow::CURRENT)
248     {
249         if(index % 2)
250         {
251             painter->setBrush(QBrush(m_current_alt_bg));
252             painter->setPen(m_current_alt_bg);
253         }
254         else
255         {
256             painter->setBrush(QBrush(m_current_bg));
257             painter->setPen(m_current_bg);
258         }
259     }
260     else
261     {
262         if(index % 2)
263         {
264             painter->setBrush(QBrush(m_alternate));
265             painter->setPen(m_alternate);
266         }
267         else
268         {
269             painter->setBrush(QBrush(m_normal_bg));
270             painter->setPen(m_normal_bg);
271         }
272     }
273 
274     if(m_show_anchor && (row->flags & ListWidgetRow::ANCHOR))
275     {
276         painter->setPen(m_normal);
277     }
278     else if(row->flags & ListWidgetRow::SELECTED)
279     {
280         painter->setPen(m_selected_bg);
281     }
282     painter->drawRect(row->rect);
283 }
284 
drawSeparator(QPainter * painter,ListWidgetRow * row,bool rtl)285 void ListWidgetDrawer::drawSeparator(QPainter *painter, ListWidgetRow *row, bool rtl)
286 {
287     int sx = 50 + row->numberColumnWidth;
288     int sy =  row->rect.y() + m_metrics->overlinePos() - 1;
289 
290     painter->setFont(m_font);
291     painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : m_group_text);
292 
293     if(rtl)
294         sx = row->rect.right() - sx - m_metrics->horizontalAdvance(row->titles[0]);
295     else
296         sx += row->rect.x();
297 
298     painter->drawText(sx, sy, row->titles[0]);
299 
300     sy -= m_metrics->lineSpacing()/2 - 2;
301 
302     if(rtl)
303     {
304         painter->drawLine(row->rect.x() + 5, sy, sx - 5, sy);
305         painter->drawLine(sx + m_metrics->horizontalAdvance(row->titles[0]) + 5, sy,
306                 row->rect.right() - row->numberColumnWidth - m_padding, sy);
307         if(m_show_splitters && row->numberColumnWidth)
308         {
309             painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : m_splitter);
310             painter->drawLine(row->rect.right() - row->numberColumnWidth, row->rect.top(),
311                               row->rect.right() - row->numberColumnWidth, row->rect.bottom() + 1);
312         }
313     }
314     else
315     {
316         painter->drawLine(sx - 45, sy, sx - 5, sy);
317         painter->drawLine(sx + m_metrics->horizontalAdvance(row->titles[0]) + 5, sy,
318                 row->rect.width(), sy);
319         if(m_show_splitters && row->numberColumnWidth)
320         {
321             painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : m_splitter);
322             painter->drawLine(row->rect.left() + row->numberColumnWidth, row->rect.top(),
323                               row->rect.left() + row->numberColumnWidth, row->rect.bottom() + 1);
324         }
325     }
326 }
327 
drawTrack(QPainter * painter,ListWidgetRow * row,bool rtl)328 void ListWidgetDrawer::drawTrack(QPainter *painter, ListWidgetRow *row, bool rtl)
329 {
330     int sy = row->rect.y() + m_metrics->overlinePos() - 1;
331     int sx = rtl ? row->rect.right() : row->rect.x();
332     int title_x = 0, extra_x = 0;
333     bool draw_extra = false;
334 
335     painter->setFont(m_font);
336     QFontMetrics *metrics = nullptr;
337     QColor textColor = m_normal;
338 
339     if(row->flags & ListWidgetRow::CURRENT)
340     {
341         m_font.setBold(true);
342         painter->setFont(m_font);
343         m_font.setBold(false);
344         metrics = m_bold_metrics;
345         textColor = m_current;
346     }
347     else
348         metrics = m_metrics;
349 
350     painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : textColor);
351 
352     if(rtl)
353     {
354         //|=duration=extra=|=  col1=|=number =|
355         if(row->titles.count() == 1)
356         {
357             if(row->numberColumnWidth)
358             {
359                 sx -= row->numberColumnWidth;
360                 QString number = QString("%1").arg(row->number);
361                 painter->drawText(sx + m_padding, sy, number);
362                 if(m_show_splitters)
363                 {
364                     painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : m_splitter);
365                     painter->drawLine(sx, row->rect.top(), sx, row->rect.bottom() + 1);
366                     painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : textColor);
367                 }
368             }
369 
370             sx -= metrics->horizontalAdvance(row->titles[0]);
371             painter->drawText(sx - m_padding, sy, row->titles[0]);
372             sx = row->rect.x() + m_padding;
373 
374             if(m_show_lengths && !row->length.isEmpty())
375             {
376                 painter->drawText(sx, sy, row->length);
377                 sx += metrics->horizontalAdvance(row->length);
378                 sx += m_padding;
379             }
380 
381             if(!row->extraString.isEmpty())
382             {
383                 painter->setFont(m_extra_font);
384                 painter->drawText(sx, sy, row->extraString);
385             }
386         }
387         else //|=extra col1=|=  col2=|
388         {
389             for(int i = 0; i < row->sizes.count(); i++)
390             {
391                 painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : textColor);
392                 draw_extra = (i == row->trackStateColumn && !row->extraString.isEmpty());
393 
394                 if(row->alignment[i] == ListWidgetRow::ALIGN_LEFT)
395                 {
396                     title_x = sx - row->sizes[i] + m_padding;
397                     extra_x = draw_extra ? sx - m_padding - m_extra_metrics->horizontalAdvance(row->extraString) : 0;
398                 }
399                 else if(row->alignment[i] == ListWidgetRow::ALIGN_RIGHT)
400                 {
401                     title_x = sx - m_padding - metrics->horizontalAdvance(row->titles[i]);
402                     extra_x = draw_extra ? sx - row->sizes[i] + m_padding : 0;
403                 }
404                 else
405                 {
406                     title_x = sx - row->sizes[i] / 2 - metrics->horizontalAdvance(row->titles[i]) / 2 +
407                             (draw_extra ? (m_extra_metrics->horizontalAdvance(row->extraString) + m_padding) / 2 : 0);
408                     extra_x = draw_extra ? title_x - metrics->horizontalAdvance(row->extraString) - m_padding : 0;
409                 }
410 
411                 painter->drawText(title_x, sy, row->titles[i]);
412 
413                 if(draw_extra)
414                 {
415                     QFont prev_font = painter->font();
416                     painter->setFont(m_extra_font);
417                     painter->drawText(extra_x, sy, row->extraString);
418                     painter->setFont(prev_font);
419                 }
420 
421                 sx -= row->sizes[i];
422 
423                 if(m_show_splitters && (!row->autoResize || i < row->sizes.count() - 1)) //do not draw last vertical line
424                 {
425                     painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : m_splitter);
426                     painter->drawLine(sx - 1, row->rect.top(), sx - 1, row->rect.bottom() + 1);
427                 }
428             }
429         }
430     }
431     else
432     {
433         //|= number=|=col  =|=extra=duration=|
434         if(row->titles.count() == 1)
435         {
436             if(row->numberColumnWidth)
437             {
438                 sx += row->numberColumnWidth;
439                 QString number = QString("%1").arg(row->number);
440                 painter->drawText(sx - m_padding - metrics->horizontalAdvance(number), sy, number);
441                 if(m_show_splitters)
442                 {
443                     painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : m_splitter);
444                     painter->drawLine(sx, row->rect.top(), sx, row->rect.bottom() + 1);
445                     painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : textColor);
446                 }
447             }
448 
449             painter->drawText(sx + m_padding, sy, row->titles[0]);
450             sx = row->rect.right() - m_padding;
451 
452             if(m_show_lengths && !row->length.isEmpty())
453             {
454                 sx -= metrics->horizontalAdvance(row->length);
455                 painter->drawText(sx, sy, row->length);
456                 sx -= m_padding;
457             }
458 
459             if(!row->extraString.isEmpty())
460             {
461                 sx -= m_extra_metrics->horizontalAdvance(row->extraString);
462                 painter->setFont(m_extra_font);
463                 painter->drawText(sx, sy, row->extraString);
464             }
465         }
466         else //|=col1  extra=|=col2  =|
467         {
468             for(int i = 0; i < row->sizes.count(); i++)
469             {
470                 painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : textColor);
471                 draw_extra = (i == row->trackStateColumn && !row->extraString.isEmpty());
472 
473                 if(row->alignment[i] == ListWidgetRow::ALIGN_LEFT)
474                 {
475                     title_x = sx + m_padding;
476                     extra_x = draw_extra ? sx + row->sizes[i] - m_padding - m_extra_metrics->horizontalAdvance(row->extraString) : 0;
477                 }
478                 else if(row->alignment[i] == ListWidgetRow::ALIGN_RIGHT)
479                 {
480                     title_x = sx + row->sizes[i] - m_padding - metrics->horizontalAdvance(row->titles[i]);
481                     extra_x = draw_extra ? sx + m_padding : 0;
482                 }
483                 else
484                 {
485                     title_x = sx + row->sizes[i] / 2 - metrics->horizontalAdvance(row->titles[i]) / 2 -
486                             (draw_extra ? (m_extra_metrics->horizontalAdvance(row->extraString) + m_padding) / 2 : 0);
487                     extra_x = draw_extra ? title_x + metrics->horizontalAdvance(row->titles[i]) + m_padding : 0;
488                 }
489 
490                 painter->drawText(title_x, sy, row->titles[i]);
491 
492                 if(draw_extra)
493                 {
494                     QFont prev_font = painter->font();
495                     painter->setFont(m_extra_font);
496                     painter->drawText(extra_x, sy, row->extraString);
497                     painter->setFont(prev_font);
498                 }
499 
500                 sx += row->sizes[i];
501 
502                 if(m_show_splitters && (!row->autoResize || i < row->sizes.count() - 1)) //do not draw last vertical line
503                 {
504                     painter->setPen((row->flags & ListWidgetRow::SELECTED) ? m_highlighted : m_splitter);
505                     painter->drawLine(sx - 1, row->rect.top(), sx - 1, row->rect.bottom() + 1);
506                 }
507             }
508         }
509     }
510 }
511 
drawDropLine(QPainter * painter,int row_number,int width,int header_height)512 void ListWidgetDrawer::drawDropLine(QPainter *painter, int row_number, int width, int header_height)
513 {
514     painter->setPen(m_current);
515     painter->drawLine (5, header_height + row_number * m_row_height,
516                        width - 5 , header_height + row_number * m_row_height);
517 }
518