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"