File indexing completed on 2024-04-21 04:32:11

0001 /*
0002  * Copyright (C) 2010-2015 by Stephen Allewell
0003  * steve.allewell@gmail.com
0004  *
0005  * This program is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation; either version 2 of the License, or
0008  * (at your option) any later version.
0009  */
0010 
0011 #include "Palette.h"
0012 
0013 #include <QBitmap>
0014 #include <QMouseEvent>
0015 #include <QPainter>
0016 #include <QToolTip>
0017 
0018 #include <KLocalizedString>
0019 #include <KXMLGUIClient>
0020 
0021 #include "Document.h"
0022 #include "Floss.h"
0023 #include "FlossScheme.h"
0024 #include "SchemeManager.h"
0025 #include "SymbolLibrary.h"
0026 #include "SymbolManager.h"
0027 #include "configuration.h"
0028 
0029 const uchar swapCursor[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x00,
0030                             0x7f, 0xc0, 0x3f, 0xff, 0x3f, 0xe0, 0x3f, 0xff, 0x19, 0xe0, 0x3f, 0xff, 0x08, 0xe0, 0x3f, 0xff, 0x00, 0xe0, 0x3f, 0xff, 0x00, 0xe0,
0031                             0x3f, 0xff, 0x03, 0xf8, 0x3f, 0xff, 0x01, 0xf0, 0x3f, 0xff, 0x00, 0xe0, 0x3f, 0xff, 0x00, 0x40, 0x3f, 0xff, 0x00, 0x00, 0x3f, 0xff,
0032                             0xff, 0x80, 0x3f, 0xff, 0x00, 0x80, 0x3f, 0xff, 0x00, 0x80, 0x3f, 0xff, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80,
0033                             0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10,
0034                             0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
0035 
0036 const uchar swapCursorMask[] = {
0037     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xf0, 0x7f, 0xff,
0038     0xff, 0xf0, 0x7f, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xbf, 0xf0, 0x7f, 0xff, 0x9d, 0xf0, 0x7f, 0xff, 0x87, 0xfc, 0x7f, 0xff, 0x87, 0xfc, 0x7f, 0xff, 0x87, 0xfc,
0039     0x7f, 0xff, 0x83, 0xf8, 0x7f, 0xff, 0x81, 0xf0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xc0, 0x7f, 0xff,
0040     0xff, 0xc0, 0x7f, 0xff, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0,
0041     0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00};
0042 
0043 const uchar replaceCursor[] = {
0044     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x00, 0x7f, 0xc0, 0x3f, 0xff,
0045     0x3f, 0xe0, 0x3f, 0xff, 0x19, 0xe0, 0x3f, 0xff, 0x08, 0xe0, 0x3f, 0xff, 0x00, 0xe0, 0x3f, 0xff, 0x00, 0xe0, 0x3f, 0xff, 0x00, 0xe0, 0x3f, 0xff, 0x00, 0xe0,
0046     0x3f, 0xff, 0x00, 0xe0, 0x3f, 0xff, 0x00, 0xe0, 0x3f, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0x80, 0x3f, 0xff, 0x00, 0x80, 0x3f, 0xff, 0x00, 0x80, 0x3f, 0xff,
0047     0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80,
0048     0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x10, 0x00, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
0049 
0050 const uchar replaceCursorMask[] = {
0051     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xf0, 0x7f, 0xff,
0052     0xff, 0xf0, 0x7f, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xbf, 0xf0, 0x7f, 0xff, 0x9d, 0xf0, 0x7f, 0xff, 0x81, 0xf0, 0x7f, 0xff, 0x81, 0xf0, 0x7f, 0xff, 0x81, 0xf0,
0053     0x7f, 0xff, 0x81, 0xf0, 0x7f, 0xff, 0x81, 0xf0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xc0, 0x7f, 0xff,
0054     0xff, 0xc0, 0x7f, 0xff, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0,
0055     0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00};
0056 
0057 Palette::Palette(QWidget *parent)
0058     : QFrame(parent)
0059     , m_document(nullptr)
0060     , m_showSymbols(Configuration::palette_ShowSymbols())
0061     , m_cols(0)
0062     , m_rows(0)
0063     , m_width(0)
0064     , m_height(0)
0065     , m_flosses(0)
0066     , m_mode(Select)
0067 {
0068     setObjectName(QStringLiteral("Palette#"));
0069 }
0070 
0071 QSize Palette::sizeHint() const
0072 {
0073     return QSize(400, 600);
0074 }
0075 
0076 void Palette::setDocument(Document *document)
0077 {
0078     m_document = document;
0079 }
0080 
0081 Document *Palette::document() const
0082 {
0083     return m_document;
0084 }
0085 
0086 void Palette::showSymbols(bool show)
0087 {
0088     m_showSymbols = show;
0089     update();
0090 }
0091 
0092 void Palette::swapColors()
0093 {
0094     if (m_paletteIndex.count() > 1) { // can't swap if there is less than two colors
0095         m_mode = Swap;
0096         setCursor(QCursor(QBitmap().fromData(QSize(32, 32), swapCursor, QImage::Format_Mono),
0097                           QBitmap().fromData(QSize(32, 32), swapCursorMask, QImage::Format_Mono)));
0098     }
0099 }
0100 
0101 void Palette::replaceColor()
0102 {
0103     if (m_paletteIndex.count() > 1) { // can't replace if there is less than two colors
0104         m_mode = Replace;
0105         setCursor(QCursor(QBitmap().fromData(QSize(32, 32), replaceCursor, QImage::Format_Mono),
0106                           QBitmap().fromData(QSize(32, 32), replaceCursorMask, QImage::Format_Mono)));
0107     }
0108 }
0109 
0110 void Palette::loadSettings()
0111 {
0112     update();
0113 }
0114 
0115 bool Palette::event(QEvent *event)
0116 {
0117     if (event->type() == QEvent::ToolTip) {
0118         QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
0119 
0120         if (m_paletteIndex.count()) {
0121             QPoint local = helpEvent->pos();
0122             int xCell = local.x() / m_width;
0123             int yCell = local.y() / m_height;
0124             int i = yCell * m_cols + xCell;
0125 
0126             if (i < m_paletteIndex.count()) {
0127                 DocumentFloss *documentFloss = m_document->pattern()->palette().flosses()[m_paletteIndex[i]];
0128                 FlossScheme *flossScheme = SchemeManager::scheme(m_document->pattern()->palette().schemeName());
0129                 Floss *floss = flossScheme->find(documentFloss->flossName());
0130                 FlossUsage flossUsage = m_document->pattern()->stitches().flossUsage()[m_paletteIndex[i]];
0131                 QString tip = i18ncp("%1 is the number of stitches of a particular floss, %2 is the floss name and %3 the floss description",
0132                                      "%2 %3\n%1 Stitch",
0133                                      "%2 %3\n%1 Stitches",
0134                                      flossUsage.stitchCount() + flossUsage.backstitchCount,
0135                                      floss->name(),
0136                                      floss->description());
0137                 QToolTip::showText(helpEvent->globalPos(), tip);
0138             } else {
0139                 QToolTip::hideText();
0140                 event->ignore();
0141             }
0142         } else {
0143             QToolTip::showText(helpEvent->globalPos(),
0144                                i18n("There are no flosses in the palette.\nAdd them with the Palette Manager\nwhich will then enable the drawing tools."));
0145         }
0146 
0147         return true;
0148     }
0149 
0150     return QWidget::event(event);
0151 }
0152 
0153 void Palette::paintEvent(QPaintEvent *)
0154 {
0155     if (m_document == nullptr) {
0156         return;
0157     }
0158 
0159     QMap<int, DocumentFloss *> palette = m_document->pattern()->palette().flosses();
0160     m_flosses = palette.count();
0161     m_paletteIndex = m_document->pattern()->palette().sortedFlosses();
0162 
0163     int currentFlossIndex = m_document->pattern()->palette().currentIndex();
0164 
0165     SymbolLibrary *library = SymbolManager::library(m_document->pattern()->palette().symbolLibrary());
0166 
0167     QPainter painter;
0168 
0169     if (m_flosses) {
0170         emit signalStateChanged(QStringLiteral("palette_empty"), KXMLGUIClient::StateReverse);
0171 
0172         m_cols = 5;
0173 
0174         while (true) {
0175             while ((m_width = contentsRect().width() / m_cols) > 40) {
0176                 m_cols++;
0177             }
0178 
0179             m_rows = m_flosses / m_cols;
0180 
0181             if (m_flosses % m_cols) {
0182                 m_rows++;
0183             }
0184 
0185             m_height = std::min(contentsRect().height() / m_rows, m_width);
0186 
0187             if ((m_width - m_height) > m_height) {
0188                 m_cols++;
0189             } else {
0190                 break;
0191             }
0192         }
0193 
0194         QTransform scale = QTransform::fromScale(m_width, m_height);
0195 
0196         painter.begin(this);
0197         painter.setRenderHint(QPainter::Antialiasing, Configuration::palette_SymbolsAntialiased());
0198 
0199         for (int flossIndex = 0; flossIndex < m_flosses; flossIndex++) {
0200             QColor color = palette[m_paletteIndex[flossIndex]]->flossColor();
0201             int x = (flossIndex % m_cols) * m_width;
0202             int y = (flossIndex / m_cols) * m_height;
0203             QRect rect(x, y, m_width, m_height);
0204 
0205             painter.fillRect(rect, color);
0206 
0207             if (currentFlossIndex == -1) {
0208                 m_document->pattern()->palette().setCurrentIndex(m_paletteIndex[flossIndex]);
0209                 currentFlossIndex = m_paletteIndex[flossIndex];
0210             }
0211 
0212             if (m_paletteIndex[flossIndex] == currentFlossIndex) {
0213                 painter.setPen(Qt::black);
0214                 painter.drawLine(rect.topLeft(), rect.topRight());
0215                 painter.drawLine(rect.topLeft(), rect.bottomLeft());
0216                 painter.setPen(Qt::white);
0217                 painter.drawLine(rect.topRight(), rect.bottomRight());
0218                 painter.drawLine(rect.bottomLeft(), rect.bottomRight());
0219             } else {
0220                 painter.setPen(Qt::black);
0221                 painter.drawLine(rect.topRight(), rect.bottomRight());
0222                 painter.drawLine(rect.bottomLeft(), rect.bottomRight());
0223                 painter.setPen(Qt::white);
0224                 painter.drawLine(rect.topLeft(), rect.topRight());
0225                 painter.drawLine(rect.topLeft(), rect.bottomLeft());
0226             }
0227 
0228             if (m_showSymbols) {
0229                 QTransform transform = scale * QTransform::fromTranslate(x, y);
0230                 painter.setTransform(transform);
0231 
0232                 Symbol symbol = library->symbol(palette[m_paletteIndex[flossIndex]]->stitchSymbol());
0233                 QPen pen = symbol.pen();
0234                 QBrush brush = symbol.brush();
0235 
0236                 if (qGray(color.rgb()) < 128) {
0237                     pen.setColor(Qt::white);
0238                     brush.setColor(Qt::white);
0239                 }
0240 
0241                 painter.setPen(pen);
0242                 painter.setBrush(brush);
0243                 painter.drawPath(symbol.path(Stitch::BTHalf));
0244                 painter.setTransform(QTransform());
0245             }
0246         }
0247 
0248         painter.end();
0249     } else {
0250         emit signalStateChanged(QStringLiteral("palette_empty"), KXMLGUIClient::StateNoReverse);
0251     }
0252 }
0253 
0254 void Palette::mousePressEvent(QMouseEvent *event)
0255 {
0256     if ((event->button() & Qt::LeftButton) && m_flosses) {
0257         QPoint p = event->pos();
0258         int x = (p.x() / m_width);
0259         int y = (p.y() / m_height);
0260         int i = y * m_cols + x;
0261 
0262         if (i < m_flosses) {
0263             int selectedIndex = m_paletteIndex[i];
0264             if (m_mode == Swap) {
0265                 int currentIndex = m_document->pattern()->palette().currentIndex();
0266                 emit(swapColors(currentIndex, selectedIndex));
0267                 setCursor(Qt::ArrowCursor);
0268                 m_mode = Select;
0269                 selectedIndex = currentIndex;
0270             }
0271 
0272             if (m_mode == Replace) {
0273                 emit(replaceColor(m_document->pattern()->palette().currentIndex(), selectedIndex));
0274                 setCursor(Qt::ArrowCursor);
0275                 m_mode = Select;
0276             }
0277 
0278             if (m_mode == Select) {
0279                 m_document->pattern()->palette().setCurrentIndex(selectedIndex);
0280                 emit colorSelected(i);
0281             }
0282             update();
0283         }
0284     }
0285 }
0286 
0287 #include "moc_Palette.cpp"