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