diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/MdiWindow.h b/MdiWindow.h new file mode 100644 index 0000000..76d7f1b --- /dev/null +++ b/MdiWindow.h @@ -0,0 +1,174 @@ +#ifndef MDI_WINDOW_H +#define MDI_WINDOW_H + + +#include +#include + + +class MdiChild; +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QSignalMapper; +class Ui_MainWindow; + + +class MdiChild : public QTextEdit +{ + Q_OBJECT +public: + MdiChild(); + ~MdiChild(); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + + void markdownSelection(const char* preText, const char* postText); + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + +signals: + void closing(); + void documentContentsChanged(); + +private slots: + void documentWasModified(); + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled; +}; + + +/* +class MdiOptions +{ +public: + void serialize(); + void deserialize(); +}; +*/ + + +class MdiWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MdiWindow(QWidget *parent = 0); + void init(Ui_MainWindow* ui); + + bool openFile(const QString &fileName); +signals: + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +public slots: + +private slots: + + // File menu actions + void newFile(); + void open(); + void save(); + void saveAs(); + + void updatePreview(); + + // Edit menu actions +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void options(); + void saveAll(); + void selectAll(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + + // Help + void support(); + void website(); + + // Window menu actions + void cascadeSubWindows(); + void tileSubWindows(); + void tabSubWindows(); + + void updateActions(); + MdiChild *createMdiChild(); + void switchLayoutDirection(); + void setActiveSubWindow(QWidget *window); + +private: + void readSettings(); + void writeSettings(); + MdiChild *activeMdiChild(); + QMdiSubWindow *findMdiChild(const QString &fileName); + + QMdiArea *mdiArea; + QSignalMapper *windowMapper; + Ui_MainWindow *appWindow; +}; + + +#endif // MDI_WINDOW_H diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/MdiWindow.h b/MdiWindow.h new file mode 100644 index 0000000..76d7f1b --- /dev/null +++ b/MdiWindow.h @@ -0,0 +1,174 @@ +#ifndef MDI_WINDOW_H +#define MDI_WINDOW_H + + +#include +#include + + +class MdiChild; +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QSignalMapper; +class Ui_MainWindow; + + +class MdiChild : public QTextEdit +{ + Q_OBJECT +public: + MdiChild(); + ~MdiChild(); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + + void markdownSelection(const char* preText, const char* postText); + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + +signals: + void closing(); + void documentContentsChanged(); + +private slots: + void documentWasModified(); + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled; +}; + + +/* +class MdiOptions +{ +public: + void serialize(); + void deserialize(); +}; +*/ + + +class MdiWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MdiWindow(QWidget *parent = 0); + void init(Ui_MainWindow* ui); + + bool openFile(const QString &fileName); +signals: + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +public slots: + +private slots: + + // File menu actions + void newFile(); + void open(); + void save(); + void saveAs(); + + void updatePreview(); + + // Edit menu actions +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void options(); + void saveAll(); + void selectAll(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + + // Help + void support(); + void website(); + + // Window menu actions + void cascadeSubWindows(); + void tileSubWindows(); + void tabSubWindows(); + + void updateActions(); + MdiChild *createMdiChild(); + void switchLayoutDirection(); + void setActiveSubWindow(QWidget *window); + +private: + void readSettings(); + void writeSettings(); + MdiChild *activeMdiChild(); + QMdiSubWindow *findMdiChild(const QString &fileName); + + QMdiArea *mdiArea; + QSignalMapper *windowMapper; + Ui_MainWindow *appWindow; +}; + + +#endif // MDI_WINDOW_H diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index 7680de9..f4e410c 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -1,224 +1,236 @@ - -QT += core gui widgets - -CONFIG -= flat - -TARGET = MakePDF - -DEFINES += \ - GUI_APP=1 - -RESOURCES += \ - ../Resources/resources.qrc - -FORMS += \ - ../Resources/forms/About.ui \ - ../Resources/forms/NewProjectWizard.ui \ - ../Resources/forms/ExtensibleObjectModelUI.ui \ - ../Resources/forms/SendBugReport.ui - -INCLUDEPATH += \ - ../Framework \ - ../3rdParty/hoedown \ - ../3rdParty/libharu/include \ - ../3rdParty/TinyXML++ - -HEADERS += \ - ../Framework/EventLog.h \ - ../Framework/Expected.h \ - ../Framework/Final.h \ - ../Framework/Finally.h \ - ../Framework/GenericTable.h \ - ../Framework/GenericTableUI.h \ - ../Framework/NonCopyable.h \ - ../Framework/Utilities.h - -HEADERS += \ - ../DocOutput.h \ - ../DocProject.h \ - ../DocStyle.h \ - ../DocTemplate.h \ - ../DocVisitor.h \ - ../DocSVG.h - -SOURCES += \ - ../Framework/Tests.cpp \ - ../Framework/Utilities.cpp \ - ../Framework/EventLog.cpp - -SOURCES += \ - ../DocOutput.cpp \ - ../DocProject.cpp \ - ../DocStyle.cpp \ - ../DocTemplate.cpp \ - ../DocVisitor.cpp \ - ../DocSVG.cpp \ - ../main.cpp - -SOURCES += \ - ../3rdParty/hoedown/autolink.c \ - ../3rdParty/hoedown/buffer.c \ - ../3rdParty/hoedown/document.c \ - ../3rdParty/hoedown/escape.c \ - ../3rdParty/hoedown/html.c \ - ../3rdParty/hoedown/html_blocks.c \ - ../3rdParty/hoedown/stack.c - -SOURCES += \ - ../3rdParty/libharu/src/hpdf_3dmeasure.c \ - ../3rdParty/libharu/src/hpdf_annotation.c \ - ../3rdParty/libharu/src/hpdf_array.c \ - ../3rdParty/libharu/src/hpdf_binary.c \ - ../3rdParty/libharu/src/hpdf_boolean.c \ - ../3rdParty/libharu/src/hpdf_catalog.c \ - ../3rdParty/libharu/src/hpdf_destination.c \ - ../3rdParty/libharu/src/hpdf_dict.c \ - ../3rdParty/libharu/src/hpdf_doc.c \ - ../3rdParty/libharu/src/hpdf_doc_png.c \ - ../3rdParty/libharu/src/hpdf_encoder.c \ - ../3rdParty/libharu/src/hpdf_encoder_utf.c \ - ../3rdParty/libharu/src/hpdf_encrypt.c \ - ../3rdParty/libharu/src/hpdf_encryptdict.c \ - ../3rdParty/libharu/src/hpdf_error.c \ - ../3rdParty/libharu/src/hpdf_exdata.c \ - ../3rdParty/libharu/src/hpdf_ext_gstate.c \ - ../3rdParty/libharu/src/hpdf_font.c \ - ../3rdParty/libharu/src/hpdf_fontdef.c \ - ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ - ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ - ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ - ../3rdParty/libharu/src/hpdf_font_cid.c \ - ../3rdParty/libharu/src/hpdf_font_tt.c \ - ../3rdParty/libharu/src/hpdf_font_type1.c \ - ../3rdParty/libharu/src/hpdf_gstate.c \ - ../3rdParty/libharu/src/hpdf_image.c \ - ../3rdParty/libharu/src/hpdf_image_png.c \ - ../3rdParty/libharu/src/hpdf_info.c \ - ../3rdParty/libharu/src/hpdf_list.c \ - ../3rdParty/libharu/src/hpdf_mmgr.c \ - ../3rdParty/libharu/src/hpdf_name.c \ - ../3rdParty/libharu/src/hpdf_namedict.c \ - ../3rdParty/libharu/src/hpdf_null.c \ - ../3rdParty/libharu/src/hpdf_number.c \ - ../3rdParty/libharu/src/hpdf_objects.c \ - ../3rdParty/libharu/src/hpdf_outline.c \ - ../3rdParty/libharu/src/hpdf_pages.c \ - ../3rdParty/libharu/src/hpdf_page_label.c \ - ../3rdParty/libharu/src/hpdf_page_operator.c \ - ../3rdParty/libharu/src/hpdf_pdfa.c \ - ../3rdParty/libharu/src/hpdf_real.c \ - ../3rdParty/libharu/src/hpdf_streams.c \ - ../3rdParty/libharu/src/hpdf_string.c \ - ../3rdParty/libharu/src/hpdf_u3d.c \ - ../3rdParty/libharu/src/hpdf_utils.c \ - ../3rdParty/libharu/src/hpdf_xref.c - -SOURCES += \ - ../3rdParty/TinyXML++/ticpp.cpp \ - ../3rdParty/TinyXML++/tinystr.cpp \ - ../3rdParty/TinyXML++/tinyxml.cpp \ - ../3rdParty/TinyXML++/tinyxmlerror.cpp \ - ../3rdParty/TinyXML++/tinyxmlparser.cpp - - -# As a note to what was removed -REMOVED_SOURCES = \ - ../3rdParty/hoedown/html_smartypants.c \ - ../3rdParty/hoedown/version.c \ - ../3rdParty/libharu/src/hpdf_image_ccitt.c - - -# Linux specific settings -unix { - - QMAKE_CXXFLAGS += --std=gnu++11 - # QMAKE_CXXFLAGS -= -Wall - QMAKE_CXXFLAGS_WARN_ON=-w - QMAKE_CFLAGS_WARN_ON=-w - LIBS += -lpng -lz - -} - - -# Windows specific. Note we compile in libpng and zlib statically on Windows -win32 { - - # Supress annoying warnings - QMAKE_CXXFLAGS += /wd"4996;4100" - QMAKE_CXXFLAGS_WARN_ON -= -w34100 - - - # Release mode debugging - QMAKE_CXXFLAGS += /Zi - QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF - - - # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything - Project.target = $${TARGET}.vcxproj - Project.output = $${TARGET}.vcxproj - Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 - Project.depends = '"$${_PRO_FILE_}"' - Project.input = _PRO_FILE_ - QMAKE_EXTRA_COMPILERS += Project - - - # This makes the documentation - MARKDOWN_FILES = \ - test.md \ - test2.md - - Documentation.target = test.pdf - Documentation.output = test.pdf - Documentation.commands = echo ' ' > test.pdf - Documentation.depends = '"$${MARKDOWN_FILES}"' - Documentation.input = MARKDOWN_FILES - QMAKE_EXTRA_COMPILERS += Documentation - - - INCLUDEPATH += \ - ../3rdParty/libpng \ - ../3rdParty/zlib - - - SOURCES += \ - ../3rdParty/libpng/png.c \ - ../3rdParty/libpng/pngerror.c \ - ../3rdParty/libpng/pngget.c \ - ../3rdParty/libpng/pngmem.c \ - ../3rdParty/libpng/pngpread.c \ - ../3rdParty/libpng/pngread.c \ - ../3rdParty/libpng/pngrio.c \ - ../3rdParty/libpng/pngrtran.c \ - ../3rdParty/libpng/pngrutil.c \ - ../3rdParty/libpng/pngset.c \ - ../3rdParty/libpng/pngtrans.c \ - ../3rdParty/libpng/pngwio.c \ - ../3rdParty/libpng/pngwrite.c \ - ../3rdParty/libpng/pngwtran.c \ - ../3rdParty/libpng/pngwutil.c - - SOURCES += \ - ../3rdParty/zlib/inftrees.c \ - ../3rdParty/zlib/trees.c \ - ../3rdParty/zlib/uncompr.c \ - ../3rdParty/zlib/zutil.c \ - ../3rdParty/zlib/adler32.c \ - ../3rdParty/zlib/compress.c \ - ../3rdParty/zlib/crc32.c \ - ../3rdParty/zlib/deflate.c \ - ../3rdParty/zlib/gzclose.c \ - ../3rdParty/zlib/gzlib.c \ - ../3rdParty/zlib/gzread.c \ - ../3rdParty/zlib/gzwrite.c \ - ../3rdParty/zlib/infback.c \ - ../3rdParty/zlib/inffast.c \ - ../3rdParty/zlib/inflate.c - -} - - + +QT += core gui widgets + +CONFIG -= flat + +TARGET = MakePDF + +DEFINES += \ + GUI_APP=1 + +RESOURCES += \ + ../Resources/resources.qrc + +FORMS += \ + ../Resources/forms/About.ui \ + ../Resources/forms/NewProjectWizard.ui \ + ../Resources/forms/ExtensibleObjectModelUI.ui \ + ../Resources/forms/SendBugReport.ui \ + ../Resources/forms/Help.ui \ + ../Resources/forms/CheckUpdates.ui \ + ../Resources/forms/LicenseUpgrade.ui \ + ../Resources/forms/Settings.ui \ + ../Resources/forms/PaletteEditor.ui + +INCLUDEPATH += \ + ../Framework \ + ../3rdParty/hoedown \ + ../3rdParty/libharu/include \ + ../3rdParty/TinyXML++ + +HEADERS += \ + ../Framework/EventLog.h \ + ../Framework/Expected.h \ + ../Framework/Final.h \ + ../Framework/Finally.h \ + ../Framework/GenericTable.h \ + ../Framework/GenericTableUI.h \ + ../Framework/NonCopyable.h \ + ../Framework/Utilities.h \ + ../ui.h \ + ../MdiWindow.h \ + ../ColorPicker.h \ + ../Version.h + +HEADERS += \ + ../DocOutput.h \ + ../DocProject.h \ + ../DocStyle.h \ + ../DocTemplate.h \ + ../DocVisitor.h \ + ../DocSVG.h + +SOURCES += \ + ../Framework/Tests.cpp \ + ../Framework/Utilities.cpp \ + ../Framework/EventLog.cpp \ + ../MdiWindow.cpp \ + ../ColorPicker.cpp \ + ../Version.cpp + +SOURCES += \ + ../DocOutput.cpp \ + ../DocProject.cpp \ + ../DocStyle.cpp \ + ../DocTemplate.cpp \ + ../DocVisitor.cpp \ + ../DocSVG.cpp \ + ../main.cpp + +SOURCES += \ + ../3rdParty/hoedown/autolink.c \ + ../3rdParty/hoedown/buffer.c \ + ../3rdParty/hoedown/document.c \ + ../3rdParty/hoedown/escape.c \ + ../3rdParty/hoedown/html.c \ + ../3rdParty/hoedown/html_blocks.c \ + ../3rdParty/hoedown/stack.c + +SOURCES += \ + ../3rdParty/libharu/src/hpdf_3dmeasure.c \ + ../3rdParty/libharu/src/hpdf_annotation.c \ + ../3rdParty/libharu/src/hpdf_array.c \ + ../3rdParty/libharu/src/hpdf_binary.c \ + ../3rdParty/libharu/src/hpdf_boolean.c \ + ../3rdParty/libharu/src/hpdf_catalog.c \ + ../3rdParty/libharu/src/hpdf_destination.c \ + ../3rdParty/libharu/src/hpdf_dict.c \ + ../3rdParty/libharu/src/hpdf_doc.c \ + ../3rdParty/libharu/src/hpdf_doc_png.c \ + ../3rdParty/libharu/src/hpdf_encoder.c \ + ../3rdParty/libharu/src/hpdf_encoder_utf.c \ + ../3rdParty/libharu/src/hpdf_encrypt.c \ + ../3rdParty/libharu/src/hpdf_encryptdict.c \ + ../3rdParty/libharu/src/hpdf_error.c \ + ../3rdParty/libharu/src/hpdf_exdata.c \ + ../3rdParty/libharu/src/hpdf_ext_gstate.c \ + ../3rdParty/libharu/src/hpdf_font.c \ + ../3rdParty/libharu/src/hpdf_fontdef.c \ + ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ + ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ + ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ + ../3rdParty/libharu/src/hpdf_font_cid.c \ + ../3rdParty/libharu/src/hpdf_font_tt.c \ + ../3rdParty/libharu/src/hpdf_font_type1.c \ + ../3rdParty/libharu/src/hpdf_gstate.c \ + ../3rdParty/libharu/src/hpdf_image.c \ + ../3rdParty/libharu/src/hpdf_image_png.c \ + ../3rdParty/libharu/src/hpdf_info.c \ + ../3rdParty/libharu/src/hpdf_list.c \ + ../3rdParty/libharu/src/hpdf_mmgr.c \ + ../3rdParty/libharu/src/hpdf_name.c \ + ../3rdParty/libharu/src/hpdf_namedict.c \ + ../3rdParty/libharu/src/hpdf_null.c \ + ../3rdParty/libharu/src/hpdf_number.c \ + ../3rdParty/libharu/src/hpdf_objects.c \ + ../3rdParty/libharu/src/hpdf_outline.c \ + ../3rdParty/libharu/src/hpdf_pages.c \ + ../3rdParty/libharu/src/hpdf_page_label.c \ + ../3rdParty/libharu/src/hpdf_page_operator.c \ + ../3rdParty/libharu/src/hpdf_pdfa.c \ + ../3rdParty/libharu/src/hpdf_real.c \ + ../3rdParty/libharu/src/hpdf_streams.c \ + ../3rdParty/libharu/src/hpdf_string.c \ + ../3rdParty/libharu/src/hpdf_u3d.c \ + ../3rdParty/libharu/src/hpdf_utils.c \ + ../3rdParty/libharu/src/hpdf_xref.c + +SOURCES += \ + ../3rdParty/TinyXML++/ticpp.cpp \ + ../3rdParty/TinyXML++/tinystr.cpp \ + ../3rdParty/TinyXML++/tinyxml.cpp \ + ../3rdParty/TinyXML++/tinyxmlerror.cpp \ + ../3rdParty/TinyXML++/tinyxmlparser.cpp + + +# As a note to what was removed +REMOVED_SOURCES = \ + ../3rdParty/hoedown/html_smartypants.c \ + ../3rdParty/hoedown/version.c \ + ../3rdParty/libharu/src/hpdf_image_ccitt.c + + +# Linux specific settings +unix { + + QMAKE_CXXFLAGS += --std=gnu++11 + # QMAKE_CXXFLAGS -= -Wall + QMAKE_CXXFLAGS_WARN_ON=-w + QMAKE_CFLAGS_WARN_ON=-w + LIBS += -lpng -lz + +} + + +# Windows specific. Note we compile in libpng and zlib statically on Windows +win32 { + + # Supress annoying warnings + QMAKE_CXXFLAGS += /wd"4996;4100" + QMAKE_CXXFLAGS_WARN_ON -= -w34100 + + + # Release mode debugging + QMAKE_CXXFLAGS += /Zi + QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF + + + # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything + Project.target = $${TARGET}.vcxproj + Project.output = $${TARGET}.vcxproj + Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 + Project.depends = '"$${_PRO_FILE_}"' + Project.input = _PRO_FILE_ + QMAKE_EXTRA_COMPILERS += Project + + + # This makes the documentation + MARKDOWN_FILES = \ + test.md \ + test2.md + + Documentation.target = test.pdf + Documentation.output = test.pdf + Documentation.commands = echo ' ' > test.pdf + Documentation.depends = '"$${MARKDOWN_FILES}"' + Documentation.input = MARKDOWN_FILES + QMAKE_EXTRA_COMPILERS += Documentation + + + INCLUDEPATH += \ + ../3rdParty/libpng \ + ../3rdParty/zlib + + + SOURCES += \ + ../3rdParty/libpng/png.c \ + ../3rdParty/libpng/pngerror.c \ + ../3rdParty/libpng/pngget.c \ + ../3rdParty/libpng/pngmem.c \ + ../3rdParty/libpng/pngpread.c \ + ../3rdParty/libpng/pngread.c \ + ../3rdParty/libpng/pngrio.c \ + ../3rdParty/libpng/pngrtran.c \ + ../3rdParty/libpng/pngrutil.c \ + ../3rdParty/libpng/pngset.c \ + ../3rdParty/libpng/pngtrans.c \ + ../3rdParty/libpng/pngwio.c \ + ../3rdParty/libpng/pngwrite.c \ + ../3rdParty/libpng/pngwtran.c \ + ../3rdParty/libpng/pngwutil.c + + SOURCES += \ + ../3rdParty/zlib/inftrees.c \ + ../3rdParty/zlib/trees.c \ + ../3rdParty/zlib/uncompr.c \ + ../3rdParty/zlib/zutil.c \ + ../3rdParty/zlib/adler32.c \ + ../3rdParty/zlib/compress.c \ + ../3rdParty/zlib/crc32.c \ + ../3rdParty/zlib/deflate.c \ + ../3rdParty/zlib/gzclose.c \ + ../3rdParty/zlib/gzlib.c \ + ../3rdParty/zlib/gzread.c \ + ../3rdParty/zlib/gzwrite.c \ + ../3rdParty/zlib/infback.c \ + ../3rdParty/zlib/inffast.c \ + ../3rdParty/zlib/inflate.c + +} + + diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/MdiWindow.h b/MdiWindow.h new file mode 100644 index 0000000..76d7f1b --- /dev/null +++ b/MdiWindow.h @@ -0,0 +1,174 @@ +#ifndef MDI_WINDOW_H +#define MDI_WINDOW_H + + +#include +#include + + +class MdiChild; +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QSignalMapper; +class Ui_MainWindow; + + +class MdiChild : public QTextEdit +{ + Q_OBJECT +public: + MdiChild(); + ~MdiChild(); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + + void markdownSelection(const char* preText, const char* postText); + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + +signals: + void closing(); + void documentContentsChanged(); + +private slots: + void documentWasModified(); + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled; +}; + + +/* +class MdiOptions +{ +public: + void serialize(); + void deserialize(); +}; +*/ + + +class MdiWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MdiWindow(QWidget *parent = 0); + void init(Ui_MainWindow* ui); + + bool openFile(const QString &fileName); +signals: + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +public slots: + +private slots: + + // File menu actions + void newFile(); + void open(); + void save(); + void saveAs(); + + void updatePreview(); + + // Edit menu actions +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void options(); + void saveAll(); + void selectAll(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + + // Help + void support(); + void website(); + + // Window menu actions + void cascadeSubWindows(); + void tileSubWindows(); + void tabSubWindows(); + + void updateActions(); + MdiChild *createMdiChild(); + void switchLayoutDirection(); + void setActiveSubWindow(QWidget *window); + +private: + void readSettings(); + void writeSettings(); + MdiChild *activeMdiChild(); + QMdiSubWindow *findMdiChild(const QString &fileName); + + QMdiArea *mdiArea; + QSignalMapper *windowMapper; + Ui_MainWindow *appWindow; +}; + + +#endif // MDI_WINDOW_H diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index 7680de9..f4e410c 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -1,224 +1,236 @@ - -QT += core gui widgets - -CONFIG -= flat - -TARGET = MakePDF - -DEFINES += \ - GUI_APP=1 - -RESOURCES += \ - ../Resources/resources.qrc - -FORMS += \ - ../Resources/forms/About.ui \ - ../Resources/forms/NewProjectWizard.ui \ - ../Resources/forms/ExtensibleObjectModelUI.ui \ - ../Resources/forms/SendBugReport.ui - -INCLUDEPATH += \ - ../Framework \ - ../3rdParty/hoedown \ - ../3rdParty/libharu/include \ - ../3rdParty/TinyXML++ - -HEADERS += \ - ../Framework/EventLog.h \ - ../Framework/Expected.h \ - ../Framework/Final.h \ - ../Framework/Finally.h \ - ../Framework/GenericTable.h \ - ../Framework/GenericTableUI.h \ - ../Framework/NonCopyable.h \ - ../Framework/Utilities.h - -HEADERS += \ - ../DocOutput.h \ - ../DocProject.h \ - ../DocStyle.h \ - ../DocTemplate.h \ - ../DocVisitor.h \ - ../DocSVG.h - -SOURCES += \ - ../Framework/Tests.cpp \ - ../Framework/Utilities.cpp \ - ../Framework/EventLog.cpp - -SOURCES += \ - ../DocOutput.cpp \ - ../DocProject.cpp \ - ../DocStyle.cpp \ - ../DocTemplate.cpp \ - ../DocVisitor.cpp \ - ../DocSVG.cpp \ - ../main.cpp - -SOURCES += \ - ../3rdParty/hoedown/autolink.c \ - ../3rdParty/hoedown/buffer.c \ - ../3rdParty/hoedown/document.c \ - ../3rdParty/hoedown/escape.c \ - ../3rdParty/hoedown/html.c \ - ../3rdParty/hoedown/html_blocks.c \ - ../3rdParty/hoedown/stack.c - -SOURCES += \ - ../3rdParty/libharu/src/hpdf_3dmeasure.c \ - ../3rdParty/libharu/src/hpdf_annotation.c \ - ../3rdParty/libharu/src/hpdf_array.c \ - ../3rdParty/libharu/src/hpdf_binary.c \ - ../3rdParty/libharu/src/hpdf_boolean.c \ - ../3rdParty/libharu/src/hpdf_catalog.c \ - ../3rdParty/libharu/src/hpdf_destination.c \ - ../3rdParty/libharu/src/hpdf_dict.c \ - ../3rdParty/libharu/src/hpdf_doc.c \ - ../3rdParty/libharu/src/hpdf_doc_png.c \ - ../3rdParty/libharu/src/hpdf_encoder.c \ - ../3rdParty/libharu/src/hpdf_encoder_utf.c \ - ../3rdParty/libharu/src/hpdf_encrypt.c \ - ../3rdParty/libharu/src/hpdf_encryptdict.c \ - ../3rdParty/libharu/src/hpdf_error.c \ - ../3rdParty/libharu/src/hpdf_exdata.c \ - ../3rdParty/libharu/src/hpdf_ext_gstate.c \ - ../3rdParty/libharu/src/hpdf_font.c \ - ../3rdParty/libharu/src/hpdf_fontdef.c \ - ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ - ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ - ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ - ../3rdParty/libharu/src/hpdf_font_cid.c \ - ../3rdParty/libharu/src/hpdf_font_tt.c \ - ../3rdParty/libharu/src/hpdf_font_type1.c \ - ../3rdParty/libharu/src/hpdf_gstate.c \ - ../3rdParty/libharu/src/hpdf_image.c \ - ../3rdParty/libharu/src/hpdf_image_png.c \ - ../3rdParty/libharu/src/hpdf_info.c \ - ../3rdParty/libharu/src/hpdf_list.c \ - ../3rdParty/libharu/src/hpdf_mmgr.c \ - ../3rdParty/libharu/src/hpdf_name.c \ - ../3rdParty/libharu/src/hpdf_namedict.c \ - ../3rdParty/libharu/src/hpdf_null.c \ - ../3rdParty/libharu/src/hpdf_number.c \ - ../3rdParty/libharu/src/hpdf_objects.c \ - ../3rdParty/libharu/src/hpdf_outline.c \ - ../3rdParty/libharu/src/hpdf_pages.c \ - ../3rdParty/libharu/src/hpdf_page_label.c \ - ../3rdParty/libharu/src/hpdf_page_operator.c \ - ../3rdParty/libharu/src/hpdf_pdfa.c \ - ../3rdParty/libharu/src/hpdf_real.c \ - ../3rdParty/libharu/src/hpdf_streams.c \ - ../3rdParty/libharu/src/hpdf_string.c \ - ../3rdParty/libharu/src/hpdf_u3d.c \ - ../3rdParty/libharu/src/hpdf_utils.c \ - ../3rdParty/libharu/src/hpdf_xref.c - -SOURCES += \ - ../3rdParty/TinyXML++/ticpp.cpp \ - ../3rdParty/TinyXML++/tinystr.cpp \ - ../3rdParty/TinyXML++/tinyxml.cpp \ - ../3rdParty/TinyXML++/tinyxmlerror.cpp \ - ../3rdParty/TinyXML++/tinyxmlparser.cpp - - -# As a note to what was removed -REMOVED_SOURCES = \ - ../3rdParty/hoedown/html_smartypants.c \ - ../3rdParty/hoedown/version.c \ - ../3rdParty/libharu/src/hpdf_image_ccitt.c - - -# Linux specific settings -unix { - - QMAKE_CXXFLAGS += --std=gnu++11 - # QMAKE_CXXFLAGS -= -Wall - QMAKE_CXXFLAGS_WARN_ON=-w - QMAKE_CFLAGS_WARN_ON=-w - LIBS += -lpng -lz - -} - - -# Windows specific. Note we compile in libpng and zlib statically on Windows -win32 { - - # Supress annoying warnings - QMAKE_CXXFLAGS += /wd"4996;4100" - QMAKE_CXXFLAGS_WARN_ON -= -w34100 - - - # Release mode debugging - QMAKE_CXXFLAGS += /Zi - QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF - - - # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything - Project.target = $${TARGET}.vcxproj - Project.output = $${TARGET}.vcxproj - Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 - Project.depends = '"$${_PRO_FILE_}"' - Project.input = _PRO_FILE_ - QMAKE_EXTRA_COMPILERS += Project - - - # This makes the documentation - MARKDOWN_FILES = \ - test.md \ - test2.md - - Documentation.target = test.pdf - Documentation.output = test.pdf - Documentation.commands = echo ' ' > test.pdf - Documentation.depends = '"$${MARKDOWN_FILES}"' - Documentation.input = MARKDOWN_FILES - QMAKE_EXTRA_COMPILERS += Documentation - - - INCLUDEPATH += \ - ../3rdParty/libpng \ - ../3rdParty/zlib - - - SOURCES += \ - ../3rdParty/libpng/png.c \ - ../3rdParty/libpng/pngerror.c \ - ../3rdParty/libpng/pngget.c \ - ../3rdParty/libpng/pngmem.c \ - ../3rdParty/libpng/pngpread.c \ - ../3rdParty/libpng/pngread.c \ - ../3rdParty/libpng/pngrio.c \ - ../3rdParty/libpng/pngrtran.c \ - ../3rdParty/libpng/pngrutil.c \ - ../3rdParty/libpng/pngset.c \ - ../3rdParty/libpng/pngtrans.c \ - ../3rdParty/libpng/pngwio.c \ - ../3rdParty/libpng/pngwrite.c \ - ../3rdParty/libpng/pngwtran.c \ - ../3rdParty/libpng/pngwutil.c - - SOURCES += \ - ../3rdParty/zlib/inftrees.c \ - ../3rdParty/zlib/trees.c \ - ../3rdParty/zlib/uncompr.c \ - ../3rdParty/zlib/zutil.c \ - ../3rdParty/zlib/adler32.c \ - ../3rdParty/zlib/compress.c \ - ../3rdParty/zlib/crc32.c \ - ../3rdParty/zlib/deflate.c \ - ../3rdParty/zlib/gzclose.c \ - ../3rdParty/zlib/gzlib.c \ - ../3rdParty/zlib/gzread.c \ - ../3rdParty/zlib/gzwrite.c \ - ../3rdParty/zlib/infback.c \ - ../3rdParty/zlib/inffast.c \ - ../3rdParty/zlib/inflate.c - -} - - + +QT += core gui widgets + +CONFIG -= flat + +TARGET = MakePDF + +DEFINES += \ + GUI_APP=1 + +RESOURCES += \ + ../Resources/resources.qrc + +FORMS += \ + ../Resources/forms/About.ui \ + ../Resources/forms/NewProjectWizard.ui \ + ../Resources/forms/ExtensibleObjectModelUI.ui \ + ../Resources/forms/SendBugReport.ui \ + ../Resources/forms/Help.ui \ + ../Resources/forms/CheckUpdates.ui \ + ../Resources/forms/LicenseUpgrade.ui \ + ../Resources/forms/Settings.ui \ + ../Resources/forms/PaletteEditor.ui + +INCLUDEPATH += \ + ../Framework \ + ../3rdParty/hoedown \ + ../3rdParty/libharu/include \ + ../3rdParty/TinyXML++ + +HEADERS += \ + ../Framework/EventLog.h \ + ../Framework/Expected.h \ + ../Framework/Final.h \ + ../Framework/Finally.h \ + ../Framework/GenericTable.h \ + ../Framework/GenericTableUI.h \ + ../Framework/NonCopyable.h \ + ../Framework/Utilities.h \ + ../ui.h \ + ../MdiWindow.h \ + ../ColorPicker.h \ + ../Version.h + +HEADERS += \ + ../DocOutput.h \ + ../DocProject.h \ + ../DocStyle.h \ + ../DocTemplate.h \ + ../DocVisitor.h \ + ../DocSVG.h + +SOURCES += \ + ../Framework/Tests.cpp \ + ../Framework/Utilities.cpp \ + ../Framework/EventLog.cpp \ + ../MdiWindow.cpp \ + ../ColorPicker.cpp \ + ../Version.cpp + +SOURCES += \ + ../DocOutput.cpp \ + ../DocProject.cpp \ + ../DocStyle.cpp \ + ../DocTemplate.cpp \ + ../DocVisitor.cpp \ + ../DocSVG.cpp \ + ../main.cpp + +SOURCES += \ + ../3rdParty/hoedown/autolink.c \ + ../3rdParty/hoedown/buffer.c \ + ../3rdParty/hoedown/document.c \ + ../3rdParty/hoedown/escape.c \ + ../3rdParty/hoedown/html.c \ + ../3rdParty/hoedown/html_blocks.c \ + ../3rdParty/hoedown/stack.c + +SOURCES += \ + ../3rdParty/libharu/src/hpdf_3dmeasure.c \ + ../3rdParty/libharu/src/hpdf_annotation.c \ + ../3rdParty/libharu/src/hpdf_array.c \ + ../3rdParty/libharu/src/hpdf_binary.c \ + ../3rdParty/libharu/src/hpdf_boolean.c \ + ../3rdParty/libharu/src/hpdf_catalog.c \ + ../3rdParty/libharu/src/hpdf_destination.c \ + ../3rdParty/libharu/src/hpdf_dict.c \ + ../3rdParty/libharu/src/hpdf_doc.c \ + ../3rdParty/libharu/src/hpdf_doc_png.c \ + ../3rdParty/libharu/src/hpdf_encoder.c \ + ../3rdParty/libharu/src/hpdf_encoder_utf.c \ + ../3rdParty/libharu/src/hpdf_encrypt.c \ + ../3rdParty/libharu/src/hpdf_encryptdict.c \ + ../3rdParty/libharu/src/hpdf_error.c \ + ../3rdParty/libharu/src/hpdf_exdata.c \ + ../3rdParty/libharu/src/hpdf_ext_gstate.c \ + ../3rdParty/libharu/src/hpdf_font.c \ + ../3rdParty/libharu/src/hpdf_fontdef.c \ + ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ + ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ + ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ + ../3rdParty/libharu/src/hpdf_font_cid.c \ + ../3rdParty/libharu/src/hpdf_font_tt.c \ + ../3rdParty/libharu/src/hpdf_font_type1.c \ + ../3rdParty/libharu/src/hpdf_gstate.c \ + ../3rdParty/libharu/src/hpdf_image.c \ + ../3rdParty/libharu/src/hpdf_image_png.c \ + ../3rdParty/libharu/src/hpdf_info.c \ + ../3rdParty/libharu/src/hpdf_list.c \ + ../3rdParty/libharu/src/hpdf_mmgr.c \ + ../3rdParty/libharu/src/hpdf_name.c \ + ../3rdParty/libharu/src/hpdf_namedict.c \ + ../3rdParty/libharu/src/hpdf_null.c \ + ../3rdParty/libharu/src/hpdf_number.c \ + ../3rdParty/libharu/src/hpdf_objects.c \ + ../3rdParty/libharu/src/hpdf_outline.c \ + ../3rdParty/libharu/src/hpdf_pages.c \ + ../3rdParty/libharu/src/hpdf_page_label.c \ + ../3rdParty/libharu/src/hpdf_page_operator.c \ + ../3rdParty/libharu/src/hpdf_pdfa.c \ + ../3rdParty/libharu/src/hpdf_real.c \ + ../3rdParty/libharu/src/hpdf_streams.c \ + ../3rdParty/libharu/src/hpdf_string.c \ + ../3rdParty/libharu/src/hpdf_u3d.c \ + ../3rdParty/libharu/src/hpdf_utils.c \ + ../3rdParty/libharu/src/hpdf_xref.c + +SOURCES += \ + ../3rdParty/TinyXML++/ticpp.cpp \ + ../3rdParty/TinyXML++/tinystr.cpp \ + ../3rdParty/TinyXML++/tinyxml.cpp \ + ../3rdParty/TinyXML++/tinyxmlerror.cpp \ + ../3rdParty/TinyXML++/tinyxmlparser.cpp + + +# As a note to what was removed +REMOVED_SOURCES = \ + ../3rdParty/hoedown/html_smartypants.c \ + ../3rdParty/hoedown/version.c \ + ../3rdParty/libharu/src/hpdf_image_ccitt.c + + +# Linux specific settings +unix { + + QMAKE_CXXFLAGS += --std=gnu++11 + # QMAKE_CXXFLAGS -= -Wall + QMAKE_CXXFLAGS_WARN_ON=-w + QMAKE_CFLAGS_WARN_ON=-w + LIBS += -lpng -lz + +} + + +# Windows specific. Note we compile in libpng and zlib statically on Windows +win32 { + + # Supress annoying warnings + QMAKE_CXXFLAGS += /wd"4996;4100" + QMAKE_CXXFLAGS_WARN_ON -= -w34100 + + + # Release mode debugging + QMAKE_CXXFLAGS += /Zi + QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF + + + # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything + Project.target = $${TARGET}.vcxproj + Project.output = $${TARGET}.vcxproj + Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 + Project.depends = '"$${_PRO_FILE_}"' + Project.input = _PRO_FILE_ + QMAKE_EXTRA_COMPILERS += Project + + + # This makes the documentation + MARKDOWN_FILES = \ + test.md \ + test2.md + + Documentation.target = test.pdf + Documentation.output = test.pdf + Documentation.commands = echo ' ' > test.pdf + Documentation.depends = '"$${MARKDOWN_FILES}"' + Documentation.input = MARKDOWN_FILES + QMAKE_EXTRA_COMPILERS += Documentation + + + INCLUDEPATH += \ + ../3rdParty/libpng \ + ../3rdParty/zlib + + + SOURCES += \ + ../3rdParty/libpng/png.c \ + ../3rdParty/libpng/pngerror.c \ + ../3rdParty/libpng/pngget.c \ + ../3rdParty/libpng/pngmem.c \ + ../3rdParty/libpng/pngpread.c \ + ../3rdParty/libpng/pngread.c \ + ../3rdParty/libpng/pngrio.c \ + ../3rdParty/libpng/pngrtran.c \ + ../3rdParty/libpng/pngrutil.c \ + ../3rdParty/libpng/pngset.c \ + ../3rdParty/libpng/pngtrans.c \ + ../3rdParty/libpng/pngwio.c \ + ../3rdParty/libpng/pngwrite.c \ + ../3rdParty/libpng/pngwtran.c \ + ../3rdParty/libpng/pngwutil.c + + SOURCES += \ + ../3rdParty/zlib/inftrees.c \ + ../3rdParty/zlib/trees.c \ + ../3rdParty/zlib/uncompr.c \ + ../3rdParty/zlib/zutil.c \ + ../3rdParty/zlib/adler32.c \ + ../3rdParty/zlib/compress.c \ + ../3rdParty/zlib/crc32.c \ + ../3rdParty/zlib/deflate.c \ + ../3rdParty/zlib/gzclose.c \ + ../3rdParty/zlib/gzlib.c \ + ../3rdParty/zlib/gzread.c \ + ../3rdParty/zlib/gzwrite.c \ + ../3rdParty/zlib/infback.c \ + ../3rdParty/zlib/inffast.c \ + ../3rdParty/zlib/inflate.c + +} + + diff --git a/Resources/resources.qrc b/Resources/resources.qrc index 55e4392..d6ef98c 100644 --- a/Resources/resources.qrc +++ b/Resources/resources.qrc @@ -1,44 +1,50 @@ - - icons/window26.png - icons/lifebelt.png - icons/lightbulb3.png - icons/open134.png - icons/question24.png - icons/speech64.png - icons/speech66.png - icons/window30.png - icons/h1.png - icons/h2.png - icons/h3.png - icons/h4.png - icons/h5.png - icons/normal.png - icons/cut20.png - icons/delete30.png - icons/development.png - icons/document28.png - icons/italics.png - icons/floppy13.png - icons/font8.png - icons/floppy15.png - icons/list36.png - icons/link71.png - icons/magnifying glass39.png - icons/magnifying glass40.png - icons/minus25.png - icons/numbered5.png - icons/open131.png - icons/page10.png - icons/paste1.png - icons/pdf24.png - icons/printing28.png - icons/quotation1.png - icons/redo5.png - icons/undo7.png - icons/squares64.png - - - icons/logo.png - + + icons/window26.png + icons/lifebelt.png + icons/lightbulb3.png + icons/open134.png + icons/question24.png + icons/speech64.png + icons/speech66.png + icons/window30.png + icons/h1.png + icons/h2.png + icons/h3.png + icons/h4.png + icons/h5.png + icons/normal.png + icons/cut20.png + icons/delete30.png + icons/development.png + icons/document28.png + icons/italics.png + icons/floppy13.png + icons/font8.png + icons/floppy15.png + icons/list36.png + icons/link71.png + icons/magnifying glass39.png + icons/magnifying glass40.png + icons/minus25.png + icons/numbered5.png + icons/open131.png + icons/page10.png + icons/paste1.png + icons/pdf24.png + icons/printing28.png + icons/quotation1.png + icons/redo5.png + icons/undo7.png + icons/squares64.png + + + images/logo.svg + images/color-wheel-mode-1.png + images/color-wheel-mode-2.png + images/color-wheel-mode-3.png + images/color-wheel-mode-4.png + images/color-wheel-mode-5.png + images/color-wheel.png + diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/MdiWindow.h b/MdiWindow.h new file mode 100644 index 0000000..76d7f1b --- /dev/null +++ b/MdiWindow.h @@ -0,0 +1,174 @@ +#ifndef MDI_WINDOW_H +#define MDI_WINDOW_H + + +#include +#include + + +class MdiChild; +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QSignalMapper; +class Ui_MainWindow; + + +class MdiChild : public QTextEdit +{ + Q_OBJECT +public: + MdiChild(); + ~MdiChild(); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + + void markdownSelection(const char* preText, const char* postText); + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + +signals: + void closing(); + void documentContentsChanged(); + +private slots: + void documentWasModified(); + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled; +}; + + +/* +class MdiOptions +{ +public: + void serialize(); + void deserialize(); +}; +*/ + + +class MdiWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MdiWindow(QWidget *parent = 0); + void init(Ui_MainWindow* ui); + + bool openFile(const QString &fileName); +signals: + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +public slots: + +private slots: + + // File menu actions + void newFile(); + void open(); + void save(); + void saveAs(); + + void updatePreview(); + + // Edit menu actions +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void options(); + void saveAll(); + void selectAll(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + + // Help + void support(); + void website(); + + // Window menu actions + void cascadeSubWindows(); + void tileSubWindows(); + void tabSubWindows(); + + void updateActions(); + MdiChild *createMdiChild(); + void switchLayoutDirection(); + void setActiveSubWindow(QWidget *window); + +private: + void readSettings(); + void writeSettings(); + MdiChild *activeMdiChild(); + QMdiSubWindow *findMdiChild(const QString &fileName); + + QMdiArea *mdiArea; + QSignalMapper *windowMapper; + Ui_MainWindow *appWindow; +}; + + +#endif // MDI_WINDOW_H diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index 7680de9..f4e410c 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -1,224 +1,236 @@ - -QT += core gui widgets - -CONFIG -= flat - -TARGET = MakePDF - -DEFINES += \ - GUI_APP=1 - -RESOURCES += \ - ../Resources/resources.qrc - -FORMS += \ - ../Resources/forms/About.ui \ - ../Resources/forms/NewProjectWizard.ui \ - ../Resources/forms/ExtensibleObjectModelUI.ui \ - ../Resources/forms/SendBugReport.ui - -INCLUDEPATH += \ - ../Framework \ - ../3rdParty/hoedown \ - ../3rdParty/libharu/include \ - ../3rdParty/TinyXML++ - -HEADERS += \ - ../Framework/EventLog.h \ - ../Framework/Expected.h \ - ../Framework/Final.h \ - ../Framework/Finally.h \ - ../Framework/GenericTable.h \ - ../Framework/GenericTableUI.h \ - ../Framework/NonCopyable.h \ - ../Framework/Utilities.h - -HEADERS += \ - ../DocOutput.h \ - ../DocProject.h \ - ../DocStyle.h \ - ../DocTemplate.h \ - ../DocVisitor.h \ - ../DocSVG.h - -SOURCES += \ - ../Framework/Tests.cpp \ - ../Framework/Utilities.cpp \ - ../Framework/EventLog.cpp - -SOURCES += \ - ../DocOutput.cpp \ - ../DocProject.cpp \ - ../DocStyle.cpp \ - ../DocTemplate.cpp \ - ../DocVisitor.cpp \ - ../DocSVG.cpp \ - ../main.cpp - -SOURCES += \ - ../3rdParty/hoedown/autolink.c \ - ../3rdParty/hoedown/buffer.c \ - ../3rdParty/hoedown/document.c \ - ../3rdParty/hoedown/escape.c \ - ../3rdParty/hoedown/html.c \ - ../3rdParty/hoedown/html_blocks.c \ - ../3rdParty/hoedown/stack.c - -SOURCES += \ - ../3rdParty/libharu/src/hpdf_3dmeasure.c \ - ../3rdParty/libharu/src/hpdf_annotation.c \ - ../3rdParty/libharu/src/hpdf_array.c \ - ../3rdParty/libharu/src/hpdf_binary.c \ - ../3rdParty/libharu/src/hpdf_boolean.c \ - ../3rdParty/libharu/src/hpdf_catalog.c \ - ../3rdParty/libharu/src/hpdf_destination.c \ - ../3rdParty/libharu/src/hpdf_dict.c \ - ../3rdParty/libharu/src/hpdf_doc.c \ - ../3rdParty/libharu/src/hpdf_doc_png.c \ - ../3rdParty/libharu/src/hpdf_encoder.c \ - ../3rdParty/libharu/src/hpdf_encoder_utf.c \ - ../3rdParty/libharu/src/hpdf_encrypt.c \ - ../3rdParty/libharu/src/hpdf_encryptdict.c \ - ../3rdParty/libharu/src/hpdf_error.c \ - ../3rdParty/libharu/src/hpdf_exdata.c \ - ../3rdParty/libharu/src/hpdf_ext_gstate.c \ - ../3rdParty/libharu/src/hpdf_font.c \ - ../3rdParty/libharu/src/hpdf_fontdef.c \ - ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ - ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ - ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ - ../3rdParty/libharu/src/hpdf_font_cid.c \ - ../3rdParty/libharu/src/hpdf_font_tt.c \ - ../3rdParty/libharu/src/hpdf_font_type1.c \ - ../3rdParty/libharu/src/hpdf_gstate.c \ - ../3rdParty/libharu/src/hpdf_image.c \ - ../3rdParty/libharu/src/hpdf_image_png.c \ - ../3rdParty/libharu/src/hpdf_info.c \ - ../3rdParty/libharu/src/hpdf_list.c \ - ../3rdParty/libharu/src/hpdf_mmgr.c \ - ../3rdParty/libharu/src/hpdf_name.c \ - ../3rdParty/libharu/src/hpdf_namedict.c \ - ../3rdParty/libharu/src/hpdf_null.c \ - ../3rdParty/libharu/src/hpdf_number.c \ - ../3rdParty/libharu/src/hpdf_objects.c \ - ../3rdParty/libharu/src/hpdf_outline.c \ - ../3rdParty/libharu/src/hpdf_pages.c \ - ../3rdParty/libharu/src/hpdf_page_label.c \ - ../3rdParty/libharu/src/hpdf_page_operator.c \ - ../3rdParty/libharu/src/hpdf_pdfa.c \ - ../3rdParty/libharu/src/hpdf_real.c \ - ../3rdParty/libharu/src/hpdf_streams.c \ - ../3rdParty/libharu/src/hpdf_string.c \ - ../3rdParty/libharu/src/hpdf_u3d.c \ - ../3rdParty/libharu/src/hpdf_utils.c \ - ../3rdParty/libharu/src/hpdf_xref.c - -SOURCES += \ - ../3rdParty/TinyXML++/ticpp.cpp \ - ../3rdParty/TinyXML++/tinystr.cpp \ - ../3rdParty/TinyXML++/tinyxml.cpp \ - ../3rdParty/TinyXML++/tinyxmlerror.cpp \ - ../3rdParty/TinyXML++/tinyxmlparser.cpp - - -# As a note to what was removed -REMOVED_SOURCES = \ - ../3rdParty/hoedown/html_smartypants.c \ - ../3rdParty/hoedown/version.c \ - ../3rdParty/libharu/src/hpdf_image_ccitt.c - - -# Linux specific settings -unix { - - QMAKE_CXXFLAGS += --std=gnu++11 - # QMAKE_CXXFLAGS -= -Wall - QMAKE_CXXFLAGS_WARN_ON=-w - QMAKE_CFLAGS_WARN_ON=-w - LIBS += -lpng -lz - -} - - -# Windows specific. Note we compile in libpng and zlib statically on Windows -win32 { - - # Supress annoying warnings - QMAKE_CXXFLAGS += /wd"4996;4100" - QMAKE_CXXFLAGS_WARN_ON -= -w34100 - - - # Release mode debugging - QMAKE_CXXFLAGS += /Zi - QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF - - - # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything - Project.target = $${TARGET}.vcxproj - Project.output = $${TARGET}.vcxproj - Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 - Project.depends = '"$${_PRO_FILE_}"' - Project.input = _PRO_FILE_ - QMAKE_EXTRA_COMPILERS += Project - - - # This makes the documentation - MARKDOWN_FILES = \ - test.md \ - test2.md - - Documentation.target = test.pdf - Documentation.output = test.pdf - Documentation.commands = echo ' ' > test.pdf - Documentation.depends = '"$${MARKDOWN_FILES}"' - Documentation.input = MARKDOWN_FILES - QMAKE_EXTRA_COMPILERS += Documentation - - - INCLUDEPATH += \ - ../3rdParty/libpng \ - ../3rdParty/zlib - - - SOURCES += \ - ../3rdParty/libpng/png.c \ - ../3rdParty/libpng/pngerror.c \ - ../3rdParty/libpng/pngget.c \ - ../3rdParty/libpng/pngmem.c \ - ../3rdParty/libpng/pngpread.c \ - ../3rdParty/libpng/pngread.c \ - ../3rdParty/libpng/pngrio.c \ - ../3rdParty/libpng/pngrtran.c \ - ../3rdParty/libpng/pngrutil.c \ - ../3rdParty/libpng/pngset.c \ - ../3rdParty/libpng/pngtrans.c \ - ../3rdParty/libpng/pngwio.c \ - ../3rdParty/libpng/pngwrite.c \ - ../3rdParty/libpng/pngwtran.c \ - ../3rdParty/libpng/pngwutil.c - - SOURCES += \ - ../3rdParty/zlib/inftrees.c \ - ../3rdParty/zlib/trees.c \ - ../3rdParty/zlib/uncompr.c \ - ../3rdParty/zlib/zutil.c \ - ../3rdParty/zlib/adler32.c \ - ../3rdParty/zlib/compress.c \ - ../3rdParty/zlib/crc32.c \ - ../3rdParty/zlib/deflate.c \ - ../3rdParty/zlib/gzclose.c \ - ../3rdParty/zlib/gzlib.c \ - ../3rdParty/zlib/gzread.c \ - ../3rdParty/zlib/gzwrite.c \ - ../3rdParty/zlib/infback.c \ - ../3rdParty/zlib/inffast.c \ - ../3rdParty/zlib/inflate.c - -} - - + +QT += core gui widgets + +CONFIG -= flat + +TARGET = MakePDF + +DEFINES += \ + GUI_APP=1 + +RESOURCES += \ + ../Resources/resources.qrc + +FORMS += \ + ../Resources/forms/About.ui \ + ../Resources/forms/NewProjectWizard.ui \ + ../Resources/forms/ExtensibleObjectModelUI.ui \ + ../Resources/forms/SendBugReport.ui \ + ../Resources/forms/Help.ui \ + ../Resources/forms/CheckUpdates.ui \ + ../Resources/forms/LicenseUpgrade.ui \ + ../Resources/forms/Settings.ui \ + ../Resources/forms/PaletteEditor.ui + +INCLUDEPATH += \ + ../Framework \ + ../3rdParty/hoedown \ + ../3rdParty/libharu/include \ + ../3rdParty/TinyXML++ + +HEADERS += \ + ../Framework/EventLog.h \ + ../Framework/Expected.h \ + ../Framework/Final.h \ + ../Framework/Finally.h \ + ../Framework/GenericTable.h \ + ../Framework/GenericTableUI.h \ + ../Framework/NonCopyable.h \ + ../Framework/Utilities.h \ + ../ui.h \ + ../MdiWindow.h \ + ../ColorPicker.h \ + ../Version.h + +HEADERS += \ + ../DocOutput.h \ + ../DocProject.h \ + ../DocStyle.h \ + ../DocTemplate.h \ + ../DocVisitor.h \ + ../DocSVG.h + +SOURCES += \ + ../Framework/Tests.cpp \ + ../Framework/Utilities.cpp \ + ../Framework/EventLog.cpp \ + ../MdiWindow.cpp \ + ../ColorPicker.cpp \ + ../Version.cpp + +SOURCES += \ + ../DocOutput.cpp \ + ../DocProject.cpp \ + ../DocStyle.cpp \ + ../DocTemplate.cpp \ + ../DocVisitor.cpp \ + ../DocSVG.cpp \ + ../main.cpp + +SOURCES += \ + ../3rdParty/hoedown/autolink.c \ + ../3rdParty/hoedown/buffer.c \ + ../3rdParty/hoedown/document.c \ + ../3rdParty/hoedown/escape.c \ + ../3rdParty/hoedown/html.c \ + ../3rdParty/hoedown/html_blocks.c \ + ../3rdParty/hoedown/stack.c + +SOURCES += \ + ../3rdParty/libharu/src/hpdf_3dmeasure.c \ + ../3rdParty/libharu/src/hpdf_annotation.c \ + ../3rdParty/libharu/src/hpdf_array.c \ + ../3rdParty/libharu/src/hpdf_binary.c \ + ../3rdParty/libharu/src/hpdf_boolean.c \ + ../3rdParty/libharu/src/hpdf_catalog.c \ + ../3rdParty/libharu/src/hpdf_destination.c \ + ../3rdParty/libharu/src/hpdf_dict.c \ + ../3rdParty/libharu/src/hpdf_doc.c \ + ../3rdParty/libharu/src/hpdf_doc_png.c \ + ../3rdParty/libharu/src/hpdf_encoder.c \ + ../3rdParty/libharu/src/hpdf_encoder_utf.c \ + ../3rdParty/libharu/src/hpdf_encrypt.c \ + ../3rdParty/libharu/src/hpdf_encryptdict.c \ + ../3rdParty/libharu/src/hpdf_error.c \ + ../3rdParty/libharu/src/hpdf_exdata.c \ + ../3rdParty/libharu/src/hpdf_ext_gstate.c \ + ../3rdParty/libharu/src/hpdf_font.c \ + ../3rdParty/libharu/src/hpdf_fontdef.c \ + ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ + ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ + ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ + ../3rdParty/libharu/src/hpdf_font_cid.c \ + ../3rdParty/libharu/src/hpdf_font_tt.c \ + ../3rdParty/libharu/src/hpdf_font_type1.c \ + ../3rdParty/libharu/src/hpdf_gstate.c \ + ../3rdParty/libharu/src/hpdf_image.c \ + ../3rdParty/libharu/src/hpdf_image_png.c \ + ../3rdParty/libharu/src/hpdf_info.c \ + ../3rdParty/libharu/src/hpdf_list.c \ + ../3rdParty/libharu/src/hpdf_mmgr.c \ + ../3rdParty/libharu/src/hpdf_name.c \ + ../3rdParty/libharu/src/hpdf_namedict.c \ + ../3rdParty/libharu/src/hpdf_null.c \ + ../3rdParty/libharu/src/hpdf_number.c \ + ../3rdParty/libharu/src/hpdf_objects.c \ + ../3rdParty/libharu/src/hpdf_outline.c \ + ../3rdParty/libharu/src/hpdf_pages.c \ + ../3rdParty/libharu/src/hpdf_page_label.c \ + ../3rdParty/libharu/src/hpdf_page_operator.c \ + ../3rdParty/libharu/src/hpdf_pdfa.c \ + ../3rdParty/libharu/src/hpdf_real.c \ + ../3rdParty/libharu/src/hpdf_streams.c \ + ../3rdParty/libharu/src/hpdf_string.c \ + ../3rdParty/libharu/src/hpdf_u3d.c \ + ../3rdParty/libharu/src/hpdf_utils.c \ + ../3rdParty/libharu/src/hpdf_xref.c + +SOURCES += \ + ../3rdParty/TinyXML++/ticpp.cpp \ + ../3rdParty/TinyXML++/tinystr.cpp \ + ../3rdParty/TinyXML++/tinyxml.cpp \ + ../3rdParty/TinyXML++/tinyxmlerror.cpp \ + ../3rdParty/TinyXML++/tinyxmlparser.cpp + + +# As a note to what was removed +REMOVED_SOURCES = \ + ../3rdParty/hoedown/html_smartypants.c \ + ../3rdParty/hoedown/version.c \ + ../3rdParty/libharu/src/hpdf_image_ccitt.c + + +# Linux specific settings +unix { + + QMAKE_CXXFLAGS += --std=gnu++11 + # QMAKE_CXXFLAGS -= -Wall + QMAKE_CXXFLAGS_WARN_ON=-w + QMAKE_CFLAGS_WARN_ON=-w + LIBS += -lpng -lz + +} + + +# Windows specific. Note we compile in libpng and zlib statically on Windows +win32 { + + # Supress annoying warnings + QMAKE_CXXFLAGS += /wd"4996;4100" + QMAKE_CXXFLAGS_WARN_ON -= -w34100 + + + # Release mode debugging + QMAKE_CXXFLAGS += /Zi + QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF + + + # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything + Project.target = $${TARGET}.vcxproj + Project.output = $${TARGET}.vcxproj + Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 + Project.depends = '"$${_PRO_FILE_}"' + Project.input = _PRO_FILE_ + QMAKE_EXTRA_COMPILERS += Project + + + # This makes the documentation + MARKDOWN_FILES = \ + test.md \ + test2.md + + Documentation.target = test.pdf + Documentation.output = test.pdf + Documentation.commands = echo ' ' > test.pdf + Documentation.depends = '"$${MARKDOWN_FILES}"' + Documentation.input = MARKDOWN_FILES + QMAKE_EXTRA_COMPILERS += Documentation + + + INCLUDEPATH += \ + ../3rdParty/libpng \ + ../3rdParty/zlib + + + SOURCES += \ + ../3rdParty/libpng/png.c \ + ../3rdParty/libpng/pngerror.c \ + ../3rdParty/libpng/pngget.c \ + ../3rdParty/libpng/pngmem.c \ + ../3rdParty/libpng/pngpread.c \ + ../3rdParty/libpng/pngread.c \ + ../3rdParty/libpng/pngrio.c \ + ../3rdParty/libpng/pngrtran.c \ + ../3rdParty/libpng/pngrutil.c \ + ../3rdParty/libpng/pngset.c \ + ../3rdParty/libpng/pngtrans.c \ + ../3rdParty/libpng/pngwio.c \ + ../3rdParty/libpng/pngwrite.c \ + ../3rdParty/libpng/pngwtran.c \ + ../3rdParty/libpng/pngwutil.c + + SOURCES += \ + ../3rdParty/zlib/inftrees.c \ + ../3rdParty/zlib/trees.c \ + ../3rdParty/zlib/uncompr.c \ + ../3rdParty/zlib/zutil.c \ + ../3rdParty/zlib/adler32.c \ + ../3rdParty/zlib/compress.c \ + ../3rdParty/zlib/crc32.c \ + ../3rdParty/zlib/deflate.c \ + ../3rdParty/zlib/gzclose.c \ + ../3rdParty/zlib/gzlib.c \ + ../3rdParty/zlib/gzread.c \ + ../3rdParty/zlib/gzwrite.c \ + ../3rdParty/zlib/infback.c \ + ../3rdParty/zlib/inffast.c \ + ../3rdParty/zlib/inflate.c + +} + + diff --git a/Resources/resources.qrc b/Resources/resources.qrc index 55e4392..d6ef98c 100644 --- a/Resources/resources.qrc +++ b/Resources/resources.qrc @@ -1,44 +1,50 @@ - - icons/window26.png - icons/lifebelt.png - icons/lightbulb3.png - icons/open134.png - icons/question24.png - icons/speech64.png - icons/speech66.png - icons/window30.png - icons/h1.png - icons/h2.png - icons/h3.png - icons/h4.png - icons/h5.png - icons/normal.png - icons/cut20.png - icons/delete30.png - icons/development.png - icons/document28.png - icons/italics.png - icons/floppy13.png - icons/font8.png - icons/floppy15.png - icons/list36.png - icons/link71.png - icons/magnifying glass39.png - icons/magnifying glass40.png - icons/minus25.png - icons/numbered5.png - icons/open131.png - icons/page10.png - icons/paste1.png - icons/pdf24.png - icons/printing28.png - icons/quotation1.png - icons/redo5.png - icons/undo7.png - icons/squares64.png - - - icons/logo.png - + + icons/window26.png + icons/lifebelt.png + icons/lightbulb3.png + icons/open134.png + icons/question24.png + icons/speech64.png + icons/speech66.png + icons/window30.png + icons/h1.png + icons/h2.png + icons/h3.png + icons/h4.png + icons/h5.png + icons/normal.png + icons/cut20.png + icons/delete30.png + icons/development.png + icons/document28.png + icons/italics.png + icons/floppy13.png + icons/font8.png + icons/floppy15.png + icons/list36.png + icons/link71.png + icons/magnifying glass39.png + icons/magnifying glass40.png + icons/minus25.png + icons/numbered5.png + icons/open131.png + icons/page10.png + icons/paste1.png + icons/pdf24.png + icons/printing28.png + icons/quotation1.png + icons/redo5.png + icons/undo7.png + icons/squares64.png + + + images/logo.svg + images/color-wheel-mode-1.png + images/color-wheel-mode-2.png + images/color-wheel-mode-3.png + images/color-wheel-mode-4.png + images/color-wheel-mode-5.png + images/color-wheel.png + diff --git a/Util.h b/Util.h index bc6a8a5..1dfc996 100644 --- a/Util.h +++ b/Util.h @@ -1,17 +1,66 @@ #include +#include +#include +#include #ifndef _MSC_VER namespace std { - -template -std::unique_ptr make_unique( Args&& ...args ) -{ - return std::unique_ptr( new T( std::forward(args)... ) ); -} - + template + std::unique_ptr make_unique( Args&& ...args ) + { + return std::unique_ptr( new T( std::forward(args)... ) ); + } }; +inline int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} + #endif + +inline static std::vector split(const std::string &s, char delim) +{ + std::vector elems; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + + +inline static std::string trim(std::string str) +{ + str.erase(0, str.find_first_not_of(" \t\r\n")); + str.erase(str.find_last_not_of(" \t\r\n") + 1); + return str; +} + + +inline static std::string str2lower(const std::string& str) +{ + std::string ret = str; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + + +inline static unsigned int str2col(std::string str) +{ + if (str[0] == '#') + str = &str[1]; + return std::stoul(str, nullptr, 16); +} + + +float static str2float(const std::string& str) +{ + return (float)atof(str.c_str()); +} + diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/MdiWindow.h b/MdiWindow.h new file mode 100644 index 0000000..76d7f1b --- /dev/null +++ b/MdiWindow.h @@ -0,0 +1,174 @@ +#ifndef MDI_WINDOW_H +#define MDI_WINDOW_H + + +#include +#include + + +class MdiChild; +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QSignalMapper; +class Ui_MainWindow; + + +class MdiChild : public QTextEdit +{ + Q_OBJECT +public: + MdiChild(); + ~MdiChild(); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + + void markdownSelection(const char* preText, const char* postText); + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + +signals: + void closing(); + void documentContentsChanged(); + +private slots: + void documentWasModified(); + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled; +}; + + +/* +class MdiOptions +{ +public: + void serialize(); + void deserialize(); +}; +*/ + + +class MdiWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MdiWindow(QWidget *parent = 0); + void init(Ui_MainWindow* ui); + + bool openFile(const QString &fileName); +signals: + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +public slots: + +private slots: + + // File menu actions + void newFile(); + void open(); + void save(); + void saveAs(); + + void updatePreview(); + + // Edit menu actions +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void options(); + void saveAll(); + void selectAll(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + + // Help + void support(); + void website(); + + // Window menu actions + void cascadeSubWindows(); + void tileSubWindows(); + void tabSubWindows(); + + void updateActions(); + MdiChild *createMdiChild(); + void switchLayoutDirection(); + void setActiveSubWindow(QWidget *window); + +private: + void readSettings(); + void writeSettings(); + MdiChild *activeMdiChild(); + QMdiSubWindow *findMdiChild(const QString &fileName); + + QMdiArea *mdiArea; + QSignalMapper *windowMapper; + Ui_MainWindow *appWindow; +}; + + +#endif // MDI_WINDOW_H diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index 7680de9..f4e410c 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -1,224 +1,236 @@ - -QT += core gui widgets - -CONFIG -= flat - -TARGET = MakePDF - -DEFINES += \ - GUI_APP=1 - -RESOURCES += \ - ../Resources/resources.qrc - -FORMS += \ - ../Resources/forms/About.ui \ - ../Resources/forms/NewProjectWizard.ui \ - ../Resources/forms/ExtensibleObjectModelUI.ui \ - ../Resources/forms/SendBugReport.ui - -INCLUDEPATH += \ - ../Framework \ - ../3rdParty/hoedown \ - ../3rdParty/libharu/include \ - ../3rdParty/TinyXML++ - -HEADERS += \ - ../Framework/EventLog.h \ - ../Framework/Expected.h \ - ../Framework/Final.h \ - ../Framework/Finally.h \ - ../Framework/GenericTable.h \ - ../Framework/GenericTableUI.h \ - ../Framework/NonCopyable.h \ - ../Framework/Utilities.h - -HEADERS += \ - ../DocOutput.h \ - ../DocProject.h \ - ../DocStyle.h \ - ../DocTemplate.h \ - ../DocVisitor.h \ - ../DocSVG.h - -SOURCES += \ - ../Framework/Tests.cpp \ - ../Framework/Utilities.cpp \ - ../Framework/EventLog.cpp - -SOURCES += \ - ../DocOutput.cpp \ - ../DocProject.cpp \ - ../DocStyle.cpp \ - ../DocTemplate.cpp \ - ../DocVisitor.cpp \ - ../DocSVG.cpp \ - ../main.cpp - -SOURCES += \ - ../3rdParty/hoedown/autolink.c \ - ../3rdParty/hoedown/buffer.c \ - ../3rdParty/hoedown/document.c \ - ../3rdParty/hoedown/escape.c \ - ../3rdParty/hoedown/html.c \ - ../3rdParty/hoedown/html_blocks.c \ - ../3rdParty/hoedown/stack.c - -SOURCES += \ - ../3rdParty/libharu/src/hpdf_3dmeasure.c \ - ../3rdParty/libharu/src/hpdf_annotation.c \ - ../3rdParty/libharu/src/hpdf_array.c \ - ../3rdParty/libharu/src/hpdf_binary.c \ - ../3rdParty/libharu/src/hpdf_boolean.c \ - ../3rdParty/libharu/src/hpdf_catalog.c \ - ../3rdParty/libharu/src/hpdf_destination.c \ - ../3rdParty/libharu/src/hpdf_dict.c \ - ../3rdParty/libharu/src/hpdf_doc.c \ - ../3rdParty/libharu/src/hpdf_doc_png.c \ - ../3rdParty/libharu/src/hpdf_encoder.c \ - ../3rdParty/libharu/src/hpdf_encoder_utf.c \ - ../3rdParty/libharu/src/hpdf_encrypt.c \ - ../3rdParty/libharu/src/hpdf_encryptdict.c \ - ../3rdParty/libharu/src/hpdf_error.c \ - ../3rdParty/libharu/src/hpdf_exdata.c \ - ../3rdParty/libharu/src/hpdf_ext_gstate.c \ - ../3rdParty/libharu/src/hpdf_font.c \ - ../3rdParty/libharu/src/hpdf_fontdef.c \ - ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ - ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ - ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ - ../3rdParty/libharu/src/hpdf_font_cid.c \ - ../3rdParty/libharu/src/hpdf_font_tt.c \ - ../3rdParty/libharu/src/hpdf_font_type1.c \ - ../3rdParty/libharu/src/hpdf_gstate.c \ - ../3rdParty/libharu/src/hpdf_image.c \ - ../3rdParty/libharu/src/hpdf_image_png.c \ - ../3rdParty/libharu/src/hpdf_info.c \ - ../3rdParty/libharu/src/hpdf_list.c \ - ../3rdParty/libharu/src/hpdf_mmgr.c \ - ../3rdParty/libharu/src/hpdf_name.c \ - ../3rdParty/libharu/src/hpdf_namedict.c \ - ../3rdParty/libharu/src/hpdf_null.c \ - ../3rdParty/libharu/src/hpdf_number.c \ - ../3rdParty/libharu/src/hpdf_objects.c \ - ../3rdParty/libharu/src/hpdf_outline.c \ - ../3rdParty/libharu/src/hpdf_pages.c \ - ../3rdParty/libharu/src/hpdf_page_label.c \ - ../3rdParty/libharu/src/hpdf_page_operator.c \ - ../3rdParty/libharu/src/hpdf_pdfa.c \ - ../3rdParty/libharu/src/hpdf_real.c \ - ../3rdParty/libharu/src/hpdf_streams.c \ - ../3rdParty/libharu/src/hpdf_string.c \ - ../3rdParty/libharu/src/hpdf_u3d.c \ - ../3rdParty/libharu/src/hpdf_utils.c \ - ../3rdParty/libharu/src/hpdf_xref.c - -SOURCES += \ - ../3rdParty/TinyXML++/ticpp.cpp \ - ../3rdParty/TinyXML++/tinystr.cpp \ - ../3rdParty/TinyXML++/tinyxml.cpp \ - ../3rdParty/TinyXML++/tinyxmlerror.cpp \ - ../3rdParty/TinyXML++/tinyxmlparser.cpp - - -# As a note to what was removed -REMOVED_SOURCES = \ - ../3rdParty/hoedown/html_smartypants.c \ - ../3rdParty/hoedown/version.c \ - ../3rdParty/libharu/src/hpdf_image_ccitt.c - - -# Linux specific settings -unix { - - QMAKE_CXXFLAGS += --std=gnu++11 - # QMAKE_CXXFLAGS -= -Wall - QMAKE_CXXFLAGS_WARN_ON=-w - QMAKE_CFLAGS_WARN_ON=-w - LIBS += -lpng -lz - -} - - -# Windows specific. Note we compile in libpng and zlib statically on Windows -win32 { - - # Supress annoying warnings - QMAKE_CXXFLAGS += /wd"4996;4100" - QMAKE_CXXFLAGS_WARN_ON -= -w34100 - - - # Release mode debugging - QMAKE_CXXFLAGS += /Zi - QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF - - - # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything - Project.target = $${TARGET}.vcxproj - Project.output = $${TARGET}.vcxproj - Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 - Project.depends = '"$${_PRO_FILE_}"' - Project.input = _PRO_FILE_ - QMAKE_EXTRA_COMPILERS += Project - - - # This makes the documentation - MARKDOWN_FILES = \ - test.md \ - test2.md - - Documentation.target = test.pdf - Documentation.output = test.pdf - Documentation.commands = echo ' ' > test.pdf - Documentation.depends = '"$${MARKDOWN_FILES}"' - Documentation.input = MARKDOWN_FILES - QMAKE_EXTRA_COMPILERS += Documentation - - - INCLUDEPATH += \ - ../3rdParty/libpng \ - ../3rdParty/zlib - - - SOURCES += \ - ../3rdParty/libpng/png.c \ - ../3rdParty/libpng/pngerror.c \ - ../3rdParty/libpng/pngget.c \ - ../3rdParty/libpng/pngmem.c \ - ../3rdParty/libpng/pngpread.c \ - ../3rdParty/libpng/pngread.c \ - ../3rdParty/libpng/pngrio.c \ - ../3rdParty/libpng/pngrtran.c \ - ../3rdParty/libpng/pngrutil.c \ - ../3rdParty/libpng/pngset.c \ - ../3rdParty/libpng/pngtrans.c \ - ../3rdParty/libpng/pngwio.c \ - ../3rdParty/libpng/pngwrite.c \ - ../3rdParty/libpng/pngwtran.c \ - ../3rdParty/libpng/pngwutil.c - - SOURCES += \ - ../3rdParty/zlib/inftrees.c \ - ../3rdParty/zlib/trees.c \ - ../3rdParty/zlib/uncompr.c \ - ../3rdParty/zlib/zutil.c \ - ../3rdParty/zlib/adler32.c \ - ../3rdParty/zlib/compress.c \ - ../3rdParty/zlib/crc32.c \ - ../3rdParty/zlib/deflate.c \ - ../3rdParty/zlib/gzclose.c \ - ../3rdParty/zlib/gzlib.c \ - ../3rdParty/zlib/gzread.c \ - ../3rdParty/zlib/gzwrite.c \ - ../3rdParty/zlib/infback.c \ - ../3rdParty/zlib/inffast.c \ - ../3rdParty/zlib/inflate.c - -} - - + +QT += core gui widgets + +CONFIG -= flat + +TARGET = MakePDF + +DEFINES += \ + GUI_APP=1 + +RESOURCES += \ + ../Resources/resources.qrc + +FORMS += \ + ../Resources/forms/About.ui \ + ../Resources/forms/NewProjectWizard.ui \ + ../Resources/forms/ExtensibleObjectModelUI.ui \ + ../Resources/forms/SendBugReport.ui \ + ../Resources/forms/Help.ui \ + ../Resources/forms/CheckUpdates.ui \ + ../Resources/forms/LicenseUpgrade.ui \ + ../Resources/forms/Settings.ui \ + ../Resources/forms/PaletteEditor.ui + +INCLUDEPATH += \ + ../Framework \ + ../3rdParty/hoedown \ + ../3rdParty/libharu/include \ + ../3rdParty/TinyXML++ + +HEADERS += \ + ../Framework/EventLog.h \ + ../Framework/Expected.h \ + ../Framework/Final.h \ + ../Framework/Finally.h \ + ../Framework/GenericTable.h \ + ../Framework/GenericTableUI.h \ + ../Framework/NonCopyable.h \ + ../Framework/Utilities.h \ + ../ui.h \ + ../MdiWindow.h \ + ../ColorPicker.h \ + ../Version.h + +HEADERS += \ + ../DocOutput.h \ + ../DocProject.h \ + ../DocStyle.h \ + ../DocTemplate.h \ + ../DocVisitor.h \ + ../DocSVG.h + +SOURCES += \ + ../Framework/Tests.cpp \ + ../Framework/Utilities.cpp \ + ../Framework/EventLog.cpp \ + ../MdiWindow.cpp \ + ../ColorPicker.cpp \ + ../Version.cpp + +SOURCES += \ + ../DocOutput.cpp \ + ../DocProject.cpp \ + ../DocStyle.cpp \ + ../DocTemplate.cpp \ + ../DocVisitor.cpp \ + ../DocSVG.cpp \ + ../main.cpp + +SOURCES += \ + ../3rdParty/hoedown/autolink.c \ + ../3rdParty/hoedown/buffer.c \ + ../3rdParty/hoedown/document.c \ + ../3rdParty/hoedown/escape.c \ + ../3rdParty/hoedown/html.c \ + ../3rdParty/hoedown/html_blocks.c \ + ../3rdParty/hoedown/stack.c + +SOURCES += \ + ../3rdParty/libharu/src/hpdf_3dmeasure.c \ + ../3rdParty/libharu/src/hpdf_annotation.c \ + ../3rdParty/libharu/src/hpdf_array.c \ + ../3rdParty/libharu/src/hpdf_binary.c \ + ../3rdParty/libharu/src/hpdf_boolean.c \ + ../3rdParty/libharu/src/hpdf_catalog.c \ + ../3rdParty/libharu/src/hpdf_destination.c \ + ../3rdParty/libharu/src/hpdf_dict.c \ + ../3rdParty/libharu/src/hpdf_doc.c \ + ../3rdParty/libharu/src/hpdf_doc_png.c \ + ../3rdParty/libharu/src/hpdf_encoder.c \ + ../3rdParty/libharu/src/hpdf_encoder_utf.c \ + ../3rdParty/libharu/src/hpdf_encrypt.c \ + ../3rdParty/libharu/src/hpdf_encryptdict.c \ + ../3rdParty/libharu/src/hpdf_error.c \ + ../3rdParty/libharu/src/hpdf_exdata.c \ + ../3rdParty/libharu/src/hpdf_ext_gstate.c \ + ../3rdParty/libharu/src/hpdf_font.c \ + ../3rdParty/libharu/src/hpdf_fontdef.c \ + ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ + ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ + ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ + ../3rdParty/libharu/src/hpdf_font_cid.c \ + ../3rdParty/libharu/src/hpdf_font_tt.c \ + ../3rdParty/libharu/src/hpdf_font_type1.c \ + ../3rdParty/libharu/src/hpdf_gstate.c \ + ../3rdParty/libharu/src/hpdf_image.c \ + ../3rdParty/libharu/src/hpdf_image_png.c \ + ../3rdParty/libharu/src/hpdf_info.c \ + ../3rdParty/libharu/src/hpdf_list.c \ + ../3rdParty/libharu/src/hpdf_mmgr.c \ + ../3rdParty/libharu/src/hpdf_name.c \ + ../3rdParty/libharu/src/hpdf_namedict.c \ + ../3rdParty/libharu/src/hpdf_null.c \ + ../3rdParty/libharu/src/hpdf_number.c \ + ../3rdParty/libharu/src/hpdf_objects.c \ + ../3rdParty/libharu/src/hpdf_outline.c \ + ../3rdParty/libharu/src/hpdf_pages.c \ + ../3rdParty/libharu/src/hpdf_page_label.c \ + ../3rdParty/libharu/src/hpdf_page_operator.c \ + ../3rdParty/libharu/src/hpdf_pdfa.c \ + ../3rdParty/libharu/src/hpdf_real.c \ + ../3rdParty/libharu/src/hpdf_streams.c \ + ../3rdParty/libharu/src/hpdf_string.c \ + ../3rdParty/libharu/src/hpdf_u3d.c \ + ../3rdParty/libharu/src/hpdf_utils.c \ + ../3rdParty/libharu/src/hpdf_xref.c + +SOURCES += \ + ../3rdParty/TinyXML++/ticpp.cpp \ + ../3rdParty/TinyXML++/tinystr.cpp \ + ../3rdParty/TinyXML++/tinyxml.cpp \ + ../3rdParty/TinyXML++/tinyxmlerror.cpp \ + ../3rdParty/TinyXML++/tinyxmlparser.cpp + + +# As a note to what was removed +REMOVED_SOURCES = \ + ../3rdParty/hoedown/html_smartypants.c \ + ../3rdParty/hoedown/version.c \ + ../3rdParty/libharu/src/hpdf_image_ccitt.c + + +# Linux specific settings +unix { + + QMAKE_CXXFLAGS += --std=gnu++11 + # QMAKE_CXXFLAGS -= -Wall + QMAKE_CXXFLAGS_WARN_ON=-w + QMAKE_CFLAGS_WARN_ON=-w + LIBS += -lpng -lz + +} + + +# Windows specific. Note we compile in libpng and zlib statically on Windows +win32 { + + # Supress annoying warnings + QMAKE_CXXFLAGS += /wd"4996;4100" + QMAKE_CXXFLAGS_WARN_ON -= -w34100 + + + # Release mode debugging + QMAKE_CXXFLAGS += /Zi + QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF + + + # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything + Project.target = $${TARGET}.vcxproj + Project.output = $${TARGET}.vcxproj + Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 + Project.depends = '"$${_PRO_FILE_}"' + Project.input = _PRO_FILE_ + QMAKE_EXTRA_COMPILERS += Project + + + # This makes the documentation + MARKDOWN_FILES = \ + test.md \ + test2.md + + Documentation.target = test.pdf + Documentation.output = test.pdf + Documentation.commands = echo ' ' > test.pdf + Documentation.depends = '"$${MARKDOWN_FILES}"' + Documentation.input = MARKDOWN_FILES + QMAKE_EXTRA_COMPILERS += Documentation + + + INCLUDEPATH += \ + ../3rdParty/libpng \ + ../3rdParty/zlib + + + SOURCES += \ + ../3rdParty/libpng/png.c \ + ../3rdParty/libpng/pngerror.c \ + ../3rdParty/libpng/pngget.c \ + ../3rdParty/libpng/pngmem.c \ + ../3rdParty/libpng/pngpread.c \ + ../3rdParty/libpng/pngread.c \ + ../3rdParty/libpng/pngrio.c \ + ../3rdParty/libpng/pngrtran.c \ + ../3rdParty/libpng/pngrutil.c \ + ../3rdParty/libpng/pngset.c \ + ../3rdParty/libpng/pngtrans.c \ + ../3rdParty/libpng/pngwio.c \ + ../3rdParty/libpng/pngwrite.c \ + ../3rdParty/libpng/pngwtran.c \ + ../3rdParty/libpng/pngwutil.c + + SOURCES += \ + ../3rdParty/zlib/inftrees.c \ + ../3rdParty/zlib/trees.c \ + ../3rdParty/zlib/uncompr.c \ + ../3rdParty/zlib/zutil.c \ + ../3rdParty/zlib/adler32.c \ + ../3rdParty/zlib/compress.c \ + ../3rdParty/zlib/crc32.c \ + ../3rdParty/zlib/deflate.c \ + ../3rdParty/zlib/gzclose.c \ + ../3rdParty/zlib/gzlib.c \ + ../3rdParty/zlib/gzread.c \ + ../3rdParty/zlib/gzwrite.c \ + ../3rdParty/zlib/infback.c \ + ../3rdParty/zlib/inffast.c \ + ../3rdParty/zlib/inflate.c + +} + + diff --git a/Resources/resources.qrc b/Resources/resources.qrc index 55e4392..d6ef98c 100644 --- a/Resources/resources.qrc +++ b/Resources/resources.qrc @@ -1,44 +1,50 @@ - - icons/window26.png - icons/lifebelt.png - icons/lightbulb3.png - icons/open134.png - icons/question24.png - icons/speech64.png - icons/speech66.png - icons/window30.png - icons/h1.png - icons/h2.png - icons/h3.png - icons/h4.png - icons/h5.png - icons/normal.png - icons/cut20.png - icons/delete30.png - icons/development.png - icons/document28.png - icons/italics.png - icons/floppy13.png - icons/font8.png - icons/floppy15.png - icons/list36.png - icons/link71.png - icons/magnifying glass39.png - icons/magnifying glass40.png - icons/minus25.png - icons/numbered5.png - icons/open131.png - icons/page10.png - icons/paste1.png - icons/pdf24.png - icons/printing28.png - icons/quotation1.png - icons/redo5.png - icons/undo7.png - icons/squares64.png - - - icons/logo.png - + + icons/window26.png + icons/lifebelt.png + icons/lightbulb3.png + icons/open134.png + icons/question24.png + icons/speech64.png + icons/speech66.png + icons/window30.png + icons/h1.png + icons/h2.png + icons/h3.png + icons/h4.png + icons/h5.png + icons/normal.png + icons/cut20.png + icons/delete30.png + icons/development.png + icons/document28.png + icons/italics.png + icons/floppy13.png + icons/font8.png + icons/floppy15.png + icons/list36.png + icons/link71.png + icons/magnifying glass39.png + icons/magnifying glass40.png + icons/minus25.png + icons/numbered5.png + icons/open131.png + icons/page10.png + icons/paste1.png + icons/pdf24.png + icons/printing28.png + icons/quotation1.png + icons/redo5.png + icons/undo7.png + icons/squares64.png + + + images/logo.svg + images/color-wheel-mode-1.png + images/color-wheel-mode-2.png + images/color-wheel-mode-3.png + images/color-wheel-mode-4.png + images/color-wheel-mode-5.png + images/color-wheel.png + diff --git a/Util.h b/Util.h index bc6a8a5..1dfc996 100644 --- a/Util.h +++ b/Util.h @@ -1,17 +1,66 @@ #include +#include +#include +#include #ifndef _MSC_VER namespace std { - -template -std::unique_ptr make_unique( Args&& ...args ) -{ - return std::unique_ptr( new T( std::forward(args)... ) ); -} - + template + std::unique_ptr make_unique( Args&& ...args ) + { + return std::unique_ptr( new T( std::forward(args)... ) ); + } }; +inline int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} + #endif + +inline static std::vector split(const std::string &s, char delim) +{ + std::vector elems; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + + +inline static std::string trim(std::string str) +{ + str.erase(0, str.find_first_not_of(" \t\r\n")); + str.erase(str.find_last_not_of(" \t\r\n") + 1); + return str; +} + + +inline static std::string str2lower(const std::string& str) +{ + std::string ret = str; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + + +inline static unsigned int str2col(std::string str) +{ + if (str[0] == '#') + str = &str[1]; + return std::stoul(str, nullptr, 16); +} + + +float static str2float(const std::string& str) +{ + return (float)atof(str.c_str()); +} + diff --git a/Version.cpp b/Version.cpp new file mode 100644 index 0000000..fb913ae --- /dev/null +++ b/Version.cpp @@ -0,0 +1,131 @@ +#include "Version.h" +#include "Util.h" +#include +#include +#include + + +#ifdef __APPLE__ +# include "TargetConditionals.h" +#elif defined(_WIN32) +# include +#else +# include +# include +# include +# include +#endif + + +void getSystemInfo(SystemInfo& a_info) +{ + a_info.m_cpuType = "unknown"; + a_info.m_osType = "unknown"; + a_info.m_osVersion = "unknown"; + +#ifdef _WIN32 + a_info.m_osType = "Windows"; + + // Get RAM size + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + a_info.m_ramSize = status.ullTotalPhys; + + // Get OS version + const OSVERSIONINFO osver = winOsVersion(); + a_info.m_osVersion.reserve(512); + snprintf(a_info.m_osVersion.data(), "%i.%i.%i", osver.dwMajorVersion, osver.dwMinorVersion, osver.dwBuildNumber); + + // Get OS arch + SYSTEM_INFO info; + GetNativeSystemInfo(&info); +# ifdef _WIN64 + //if (info.wProcessorArchitecture == ???) + a_info.m_cpuType = "x86_64"; +# else + a_info.m_cpuType = "x86"; +# endif +#else + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + a_info.m_ramSize = pages * page_size; + struct utsname u; + long ret = uname(&u); + if (ret != -1) + { + a_info.m_cpuType = u.machine; + a_info.m_osVersion = u.sysname + std::string(" ") + u.release; + } +# ifdef __APPLE__ + // Get RAM Size + int mib[2] = { CTL_HW, HW_MEMSIZE }; + size_t len = sizeof(uint64_t); + sysctl(mib, sizeof(mib) / sizeof(mib[0]), &u.ramSize, &len, NULL, 0); +# if TARGET_OS_IPHONE + a_info.m_osType = "iOS"; +# elif TARGET_OS_MAC + const char *macCodeName = ""; + switch (int(MacintoshVersion)) { + case MV_CHEETAH: macCodeName = " Cheetah"; break; + case MV_PUMA: macCodeName = " Puma"; break; + case MV_JAGUAR: macCodeName = " Jaguar"; break; + case MV_PANTHER: macCodeName = " Panther"; break; + case MV_TIGER: macCodeName = " Tiger"; break; + case MV_LEOPARD: macCodeName = " Leopard"; break; + case MV_SNOWLEOPARD: macCodeName = " Snow Leopard"; break; + case MV_LION: macCodeName = " Lion"; break; + case MV_MOUNTAINLION:macCodeName = " Mountain Lion"; break; + case MV_MAVERICKS: macCodeName = " Mavericks"; break; + case MV_YOSEMITE: macCodeName = " Yosemite"; break; + } + a_info.m_osType = "Mac OS X" + std::string(macCodeName); +# endif +# elif __ANDROID__ + a_info.m_osType = "Android"; +# elif __linux + // system("lsb_release -sd"); + FILE* f = fopen("/etc/lsb-release", "rt"); + char dist[64] = { 0 }; + if (f) { + char buf[128]; + while (fgets(buf, 128, f) != 0) + if (sscanf(buf, "DISTRIB_DESCRIPTION=%64c", dist) == 1) + break; + if (dist[0]) { + std::vector strBits = split(dist, '"'); + a_info.m_osType = strBits[(strBits.size()==3)?1:0]; + } else { + a_info.m_osType = "non-specific LSB"; + } + fclose(f); + } else { + a_info.m_osType = "non-LSB"; + } +# else + a_info.m_osType = "Generic UNIX"; +# endif +#endif + + // TODO: improve the CPU detection using libcpuid + + // Make human readable RAM size string + const char* humanUnitStrings[] = { " bytes", " KB", " MB", " GB", " TB" }; + int ramUnit = 0; + uint64_t ramSize = a_info.m_ramSize; + while (ramSize > 1024 && ramUnit < (YQ_ARRAY_SIZE(humanUnitStrings)-1)) + ramUnit++, ramSize /= 1000; + a_info.m_ramSizeStr = std::to_string(ramSize) + humanUnitStrings[ramUnit]; + + // Obtain the screen properties + QScreen *s = QApplication::screens()[0]; + int refreshRate = (s) ? s->refreshRate() : 0; + int depth = (s) ? s->depth() : 0; + a_info.m_resolutionWidth = (s) ? s->geometry().width() : 0; + a_info.m_resolutionHeight = (s) ? s->geometry().height() : 0; + a_info.m_resolutionStr = std::to_string(a_info.m_resolutionWidth) + "x" + + std::to_string(a_info.m_resolutionHeight) + " " + + std::to_string(depth) + "bit " + + "@" + std::to_string(refreshRate) + "Hz"; + ; +} diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/MdiWindow.h b/MdiWindow.h new file mode 100644 index 0000000..76d7f1b --- /dev/null +++ b/MdiWindow.h @@ -0,0 +1,174 @@ +#ifndef MDI_WINDOW_H +#define MDI_WINDOW_H + + +#include +#include + + +class MdiChild; +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QSignalMapper; +class Ui_MainWindow; + + +class MdiChild : public QTextEdit +{ + Q_OBJECT +public: + MdiChild(); + ~MdiChild(); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + + void markdownSelection(const char* preText, const char* postText); + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + +signals: + void closing(); + void documentContentsChanged(); + +private slots: + void documentWasModified(); + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled; +}; + + +/* +class MdiOptions +{ +public: + void serialize(); + void deserialize(); +}; +*/ + + +class MdiWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MdiWindow(QWidget *parent = 0); + void init(Ui_MainWindow* ui); + + bool openFile(const QString &fileName); +signals: + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +public slots: + +private slots: + + // File menu actions + void newFile(); + void open(); + void save(); + void saveAs(); + + void updatePreview(); + + // Edit menu actions +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void options(); + void saveAll(); + void selectAll(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + + // Help + void support(); + void website(); + + // Window menu actions + void cascadeSubWindows(); + void tileSubWindows(); + void tabSubWindows(); + + void updateActions(); + MdiChild *createMdiChild(); + void switchLayoutDirection(); + void setActiveSubWindow(QWidget *window); + +private: + void readSettings(); + void writeSettings(); + MdiChild *activeMdiChild(); + QMdiSubWindow *findMdiChild(const QString &fileName); + + QMdiArea *mdiArea; + QSignalMapper *windowMapper; + Ui_MainWindow *appWindow; +}; + + +#endif // MDI_WINDOW_H diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index 7680de9..f4e410c 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -1,224 +1,236 @@ - -QT += core gui widgets - -CONFIG -= flat - -TARGET = MakePDF - -DEFINES += \ - GUI_APP=1 - -RESOURCES += \ - ../Resources/resources.qrc - -FORMS += \ - ../Resources/forms/About.ui \ - ../Resources/forms/NewProjectWizard.ui \ - ../Resources/forms/ExtensibleObjectModelUI.ui \ - ../Resources/forms/SendBugReport.ui - -INCLUDEPATH += \ - ../Framework \ - ../3rdParty/hoedown \ - ../3rdParty/libharu/include \ - ../3rdParty/TinyXML++ - -HEADERS += \ - ../Framework/EventLog.h \ - ../Framework/Expected.h \ - ../Framework/Final.h \ - ../Framework/Finally.h \ - ../Framework/GenericTable.h \ - ../Framework/GenericTableUI.h \ - ../Framework/NonCopyable.h \ - ../Framework/Utilities.h - -HEADERS += \ - ../DocOutput.h \ - ../DocProject.h \ - ../DocStyle.h \ - ../DocTemplate.h \ - ../DocVisitor.h \ - ../DocSVG.h - -SOURCES += \ - ../Framework/Tests.cpp \ - ../Framework/Utilities.cpp \ - ../Framework/EventLog.cpp - -SOURCES += \ - ../DocOutput.cpp \ - ../DocProject.cpp \ - ../DocStyle.cpp \ - ../DocTemplate.cpp \ - ../DocVisitor.cpp \ - ../DocSVG.cpp \ - ../main.cpp - -SOURCES += \ - ../3rdParty/hoedown/autolink.c \ - ../3rdParty/hoedown/buffer.c \ - ../3rdParty/hoedown/document.c \ - ../3rdParty/hoedown/escape.c \ - ../3rdParty/hoedown/html.c \ - ../3rdParty/hoedown/html_blocks.c \ - ../3rdParty/hoedown/stack.c - -SOURCES += \ - ../3rdParty/libharu/src/hpdf_3dmeasure.c \ - ../3rdParty/libharu/src/hpdf_annotation.c \ - ../3rdParty/libharu/src/hpdf_array.c \ - ../3rdParty/libharu/src/hpdf_binary.c \ - ../3rdParty/libharu/src/hpdf_boolean.c \ - ../3rdParty/libharu/src/hpdf_catalog.c \ - ../3rdParty/libharu/src/hpdf_destination.c \ - ../3rdParty/libharu/src/hpdf_dict.c \ - ../3rdParty/libharu/src/hpdf_doc.c \ - ../3rdParty/libharu/src/hpdf_doc_png.c \ - ../3rdParty/libharu/src/hpdf_encoder.c \ - ../3rdParty/libharu/src/hpdf_encoder_utf.c \ - ../3rdParty/libharu/src/hpdf_encrypt.c \ - ../3rdParty/libharu/src/hpdf_encryptdict.c \ - ../3rdParty/libharu/src/hpdf_error.c \ - ../3rdParty/libharu/src/hpdf_exdata.c \ - ../3rdParty/libharu/src/hpdf_ext_gstate.c \ - ../3rdParty/libharu/src/hpdf_font.c \ - ../3rdParty/libharu/src/hpdf_fontdef.c \ - ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ - ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ - ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ - ../3rdParty/libharu/src/hpdf_font_cid.c \ - ../3rdParty/libharu/src/hpdf_font_tt.c \ - ../3rdParty/libharu/src/hpdf_font_type1.c \ - ../3rdParty/libharu/src/hpdf_gstate.c \ - ../3rdParty/libharu/src/hpdf_image.c \ - ../3rdParty/libharu/src/hpdf_image_png.c \ - ../3rdParty/libharu/src/hpdf_info.c \ - ../3rdParty/libharu/src/hpdf_list.c \ - ../3rdParty/libharu/src/hpdf_mmgr.c \ - ../3rdParty/libharu/src/hpdf_name.c \ - ../3rdParty/libharu/src/hpdf_namedict.c \ - ../3rdParty/libharu/src/hpdf_null.c \ - ../3rdParty/libharu/src/hpdf_number.c \ - ../3rdParty/libharu/src/hpdf_objects.c \ - ../3rdParty/libharu/src/hpdf_outline.c \ - ../3rdParty/libharu/src/hpdf_pages.c \ - ../3rdParty/libharu/src/hpdf_page_label.c \ - ../3rdParty/libharu/src/hpdf_page_operator.c \ - ../3rdParty/libharu/src/hpdf_pdfa.c \ - ../3rdParty/libharu/src/hpdf_real.c \ - ../3rdParty/libharu/src/hpdf_streams.c \ - ../3rdParty/libharu/src/hpdf_string.c \ - ../3rdParty/libharu/src/hpdf_u3d.c \ - ../3rdParty/libharu/src/hpdf_utils.c \ - ../3rdParty/libharu/src/hpdf_xref.c - -SOURCES += \ - ../3rdParty/TinyXML++/ticpp.cpp \ - ../3rdParty/TinyXML++/tinystr.cpp \ - ../3rdParty/TinyXML++/tinyxml.cpp \ - ../3rdParty/TinyXML++/tinyxmlerror.cpp \ - ../3rdParty/TinyXML++/tinyxmlparser.cpp - - -# As a note to what was removed -REMOVED_SOURCES = \ - ../3rdParty/hoedown/html_smartypants.c \ - ../3rdParty/hoedown/version.c \ - ../3rdParty/libharu/src/hpdf_image_ccitt.c - - -# Linux specific settings -unix { - - QMAKE_CXXFLAGS += --std=gnu++11 - # QMAKE_CXXFLAGS -= -Wall - QMAKE_CXXFLAGS_WARN_ON=-w - QMAKE_CFLAGS_WARN_ON=-w - LIBS += -lpng -lz - -} - - -# Windows specific. Note we compile in libpng and zlib statically on Windows -win32 { - - # Supress annoying warnings - QMAKE_CXXFLAGS += /wd"4996;4100" - QMAKE_CXXFLAGS_WARN_ON -= -w34100 - - - # Release mode debugging - QMAKE_CXXFLAGS += /Zi - QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF - - - # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything - Project.target = $${TARGET}.vcxproj - Project.output = $${TARGET}.vcxproj - Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 - Project.depends = '"$${_PRO_FILE_}"' - Project.input = _PRO_FILE_ - QMAKE_EXTRA_COMPILERS += Project - - - # This makes the documentation - MARKDOWN_FILES = \ - test.md \ - test2.md - - Documentation.target = test.pdf - Documentation.output = test.pdf - Documentation.commands = echo ' ' > test.pdf - Documentation.depends = '"$${MARKDOWN_FILES}"' - Documentation.input = MARKDOWN_FILES - QMAKE_EXTRA_COMPILERS += Documentation - - - INCLUDEPATH += \ - ../3rdParty/libpng \ - ../3rdParty/zlib - - - SOURCES += \ - ../3rdParty/libpng/png.c \ - ../3rdParty/libpng/pngerror.c \ - ../3rdParty/libpng/pngget.c \ - ../3rdParty/libpng/pngmem.c \ - ../3rdParty/libpng/pngpread.c \ - ../3rdParty/libpng/pngread.c \ - ../3rdParty/libpng/pngrio.c \ - ../3rdParty/libpng/pngrtran.c \ - ../3rdParty/libpng/pngrutil.c \ - ../3rdParty/libpng/pngset.c \ - ../3rdParty/libpng/pngtrans.c \ - ../3rdParty/libpng/pngwio.c \ - ../3rdParty/libpng/pngwrite.c \ - ../3rdParty/libpng/pngwtran.c \ - ../3rdParty/libpng/pngwutil.c - - SOURCES += \ - ../3rdParty/zlib/inftrees.c \ - ../3rdParty/zlib/trees.c \ - ../3rdParty/zlib/uncompr.c \ - ../3rdParty/zlib/zutil.c \ - ../3rdParty/zlib/adler32.c \ - ../3rdParty/zlib/compress.c \ - ../3rdParty/zlib/crc32.c \ - ../3rdParty/zlib/deflate.c \ - ../3rdParty/zlib/gzclose.c \ - ../3rdParty/zlib/gzlib.c \ - ../3rdParty/zlib/gzread.c \ - ../3rdParty/zlib/gzwrite.c \ - ../3rdParty/zlib/infback.c \ - ../3rdParty/zlib/inffast.c \ - ../3rdParty/zlib/inflate.c - -} - - + +QT += core gui widgets + +CONFIG -= flat + +TARGET = MakePDF + +DEFINES += \ + GUI_APP=1 + +RESOURCES += \ + ../Resources/resources.qrc + +FORMS += \ + ../Resources/forms/About.ui \ + ../Resources/forms/NewProjectWizard.ui \ + ../Resources/forms/ExtensibleObjectModelUI.ui \ + ../Resources/forms/SendBugReport.ui \ + ../Resources/forms/Help.ui \ + ../Resources/forms/CheckUpdates.ui \ + ../Resources/forms/LicenseUpgrade.ui \ + ../Resources/forms/Settings.ui \ + ../Resources/forms/PaletteEditor.ui + +INCLUDEPATH += \ + ../Framework \ + ../3rdParty/hoedown \ + ../3rdParty/libharu/include \ + ../3rdParty/TinyXML++ + +HEADERS += \ + ../Framework/EventLog.h \ + ../Framework/Expected.h \ + ../Framework/Final.h \ + ../Framework/Finally.h \ + ../Framework/GenericTable.h \ + ../Framework/GenericTableUI.h \ + ../Framework/NonCopyable.h \ + ../Framework/Utilities.h \ + ../ui.h \ + ../MdiWindow.h \ + ../ColorPicker.h \ + ../Version.h + +HEADERS += \ + ../DocOutput.h \ + ../DocProject.h \ + ../DocStyle.h \ + ../DocTemplate.h \ + ../DocVisitor.h \ + ../DocSVG.h + +SOURCES += \ + ../Framework/Tests.cpp \ + ../Framework/Utilities.cpp \ + ../Framework/EventLog.cpp \ + ../MdiWindow.cpp \ + ../ColorPicker.cpp \ + ../Version.cpp + +SOURCES += \ + ../DocOutput.cpp \ + ../DocProject.cpp \ + ../DocStyle.cpp \ + ../DocTemplate.cpp \ + ../DocVisitor.cpp \ + ../DocSVG.cpp \ + ../main.cpp + +SOURCES += \ + ../3rdParty/hoedown/autolink.c \ + ../3rdParty/hoedown/buffer.c \ + ../3rdParty/hoedown/document.c \ + ../3rdParty/hoedown/escape.c \ + ../3rdParty/hoedown/html.c \ + ../3rdParty/hoedown/html_blocks.c \ + ../3rdParty/hoedown/stack.c + +SOURCES += \ + ../3rdParty/libharu/src/hpdf_3dmeasure.c \ + ../3rdParty/libharu/src/hpdf_annotation.c \ + ../3rdParty/libharu/src/hpdf_array.c \ + ../3rdParty/libharu/src/hpdf_binary.c \ + ../3rdParty/libharu/src/hpdf_boolean.c \ + ../3rdParty/libharu/src/hpdf_catalog.c \ + ../3rdParty/libharu/src/hpdf_destination.c \ + ../3rdParty/libharu/src/hpdf_dict.c \ + ../3rdParty/libharu/src/hpdf_doc.c \ + ../3rdParty/libharu/src/hpdf_doc_png.c \ + ../3rdParty/libharu/src/hpdf_encoder.c \ + ../3rdParty/libharu/src/hpdf_encoder_utf.c \ + ../3rdParty/libharu/src/hpdf_encrypt.c \ + ../3rdParty/libharu/src/hpdf_encryptdict.c \ + ../3rdParty/libharu/src/hpdf_error.c \ + ../3rdParty/libharu/src/hpdf_exdata.c \ + ../3rdParty/libharu/src/hpdf_ext_gstate.c \ + ../3rdParty/libharu/src/hpdf_font.c \ + ../3rdParty/libharu/src/hpdf_fontdef.c \ + ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ + ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ + ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ + ../3rdParty/libharu/src/hpdf_font_cid.c \ + ../3rdParty/libharu/src/hpdf_font_tt.c \ + ../3rdParty/libharu/src/hpdf_font_type1.c \ + ../3rdParty/libharu/src/hpdf_gstate.c \ + ../3rdParty/libharu/src/hpdf_image.c \ + ../3rdParty/libharu/src/hpdf_image_png.c \ + ../3rdParty/libharu/src/hpdf_info.c \ + ../3rdParty/libharu/src/hpdf_list.c \ + ../3rdParty/libharu/src/hpdf_mmgr.c \ + ../3rdParty/libharu/src/hpdf_name.c \ + ../3rdParty/libharu/src/hpdf_namedict.c \ + ../3rdParty/libharu/src/hpdf_null.c \ + ../3rdParty/libharu/src/hpdf_number.c \ + ../3rdParty/libharu/src/hpdf_objects.c \ + ../3rdParty/libharu/src/hpdf_outline.c \ + ../3rdParty/libharu/src/hpdf_pages.c \ + ../3rdParty/libharu/src/hpdf_page_label.c \ + ../3rdParty/libharu/src/hpdf_page_operator.c \ + ../3rdParty/libharu/src/hpdf_pdfa.c \ + ../3rdParty/libharu/src/hpdf_real.c \ + ../3rdParty/libharu/src/hpdf_streams.c \ + ../3rdParty/libharu/src/hpdf_string.c \ + ../3rdParty/libharu/src/hpdf_u3d.c \ + ../3rdParty/libharu/src/hpdf_utils.c \ + ../3rdParty/libharu/src/hpdf_xref.c + +SOURCES += \ + ../3rdParty/TinyXML++/ticpp.cpp \ + ../3rdParty/TinyXML++/tinystr.cpp \ + ../3rdParty/TinyXML++/tinyxml.cpp \ + ../3rdParty/TinyXML++/tinyxmlerror.cpp \ + ../3rdParty/TinyXML++/tinyxmlparser.cpp + + +# As a note to what was removed +REMOVED_SOURCES = \ + ../3rdParty/hoedown/html_smartypants.c \ + ../3rdParty/hoedown/version.c \ + ../3rdParty/libharu/src/hpdf_image_ccitt.c + + +# Linux specific settings +unix { + + QMAKE_CXXFLAGS += --std=gnu++11 + # QMAKE_CXXFLAGS -= -Wall + QMAKE_CXXFLAGS_WARN_ON=-w + QMAKE_CFLAGS_WARN_ON=-w + LIBS += -lpng -lz + +} + + +# Windows specific. Note we compile in libpng and zlib statically on Windows +win32 { + + # Supress annoying warnings + QMAKE_CXXFLAGS += /wd"4996;4100" + QMAKE_CXXFLAGS_WARN_ON -= -w34100 + + + # Release mode debugging + QMAKE_CXXFLAGS += /Zi + QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF + + + # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything + Project.target = $${TARGET}.vcxproj + Project.output = $${TARGET}.vcxproj + Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 + Project.depends = '"$${_PRO_FILE_}"' + Project.input = _PRO_FILE_ + QMAKE_EXTRA_COMPILERS += Project + + + # This makes the documentation + MARKDOWN_FILES = \ + test.md \ + test2.md + + Documentation.target = test.pdf + Documentation.output = test.pdf + Documentation.commands = echo ' ' > test.pdf + Documentation.depends = '"$${MARKDOWN_FILES}"' + Documentation.input = MARKDOWN_FILES + QMAKE_EXTRA_COMPILERS += Documentation + + + INCLUDEPATH += \ + ../3rdParty/libpng \ + ../3rdParty/zlib + + + SOURCES += \ + ../3rdParty/libpng/png.c \ + ../3rdParty/libpng/pngerror.c \ + ../3rdParty/libpng/pngget.c \ + ../3rdParty/libpng/pngmem.c \ + ../3rdParty/libpng/pngpread.c \ + ../3rdParty/libpng/pngread.c \ + ../3rdParty/libpng/pngrio.c \ + ../3rdParty/libpng/pngrtran.c \ + ../3rdParty/libpng/pngrutil.c \ + ../3rdParty/libpng/pngset.c \ + ../3rdParty/libpng/pngtrans.c \ + ../3rdParty/libpng/pngwio.c \ + ../3rdParty/libpng/pngwrite.c \ + ../3rdParty/libpng/pngwtran.c \ + ../3rdParty/libpng/pngwutil.c + + SOURCES += \ + ../3rdParty/zlib/inftrees.c \ + ../3rdParty/zlib/trees.c \ + ../3rdParty/zlib/uncompr.c \ + ../3rdParty/zlib/zutil.c \ + ../3rdParty/zlib/adler32.c \ + ../3rdParty/zlib/compress.c \ + ../3rdParty/zlib/crc32.c \ + ../3rdParty/zlib/deflate.c \ + ../3rdParty/zlib/gzclose.c \ + ../3rdParty/zlib/gzlib.c \ + ../3rdParty/zlib/gzread.c \ + ../3rdParty/zlib/gzwrite.c \ + ../3rdParty/zlib/infback.c \ + ../3rdParty/zlib/inffast.c \ + ../3rdParty/zlib/inflate.c + +} + + diff --git a/Resources/resources.qrc b/Resources/resources.qrc index 55e4392..d6ef98c 100644 --- a/Resources/resources.qrc +++ b/Resources/resources.qrc @@ -1,44 +1,50 @@ - - icons/window26.png - icons/lifebelt.png - icons/lightbulb3.png - icons/open134.png - icons/question24.png - icons/speech64.png - icons/speech66.png - icons/window30.png - icons/h1.png - icons/h2.png - icons/h3.png - icons/h4.png - icons/h5.png - icons/normal.png - icons/cut20.png - icons/delete30.png - icons/development.png - icons/document28.png - icons/italics.png - icons/floppy13.png - icons/font8.png - icons/floppy15.png - icons/list36.png - icons/link71.png - icons/magnifying glass39.png - icons/magnifying glass40.png - icons/minus25.png - icons/numbered5.png - icons/open131.png - icons/page10.png - icons/paste1.png - icons/pdf24.png - icons/printing28.png - icons/quotation1.png - icons/redo5.png - icons/undo7.png - icons/squares64.png - - - icons/logo.png - + + icons/window26.png + icons/lifebelt.png + icons/lightbulb3.png + icons/open134.png + icons/question24.png + icons/speech64.png + icons/speech66.png + icons/window30.png + icons/h1.png + icons/h2.png + icons/h3.png + icons/h4.png + icons/h5.png + icons/normal.png + icons/cut20.png + icons/delete30.png + icons/development.png + icons/document28.png + icons/italics.png + icons/floppy13.png + icons/font8.png + icons/floppy15.png + icons/list36.png + icons/link71.png + icons/magnifying glass39.png + icons/magnifying glass40.png + icons/minus25.png + icons/numbered5.png + icons/open131.png + icons/page10.png + icons/paste1.png + icons/pdf24.png + icons/printing28.png + icons/quotation1.png + icons/redo5.png + icons/undo7.png + icons/squares64.png + + + images/logo.svg + images/color-wheel-mode-1.png + images/color-wheel-mode-2.png + images/color-wheel-mode-3.png + images/color-wheel-mode-4.png + images/color-wheel-mode-5.png + images/color-wheel.png + diff --git a/Util.h b/Util.h index bc6a8a5..1dfc996 100644 --- a/Util.h +++ b/Util.h @@ -1,17 +1,66 @@ #include +#include +#include +#include #ifndef _MSC_VER namespace std { - -template -std::unique_ptr make_unique( Args&& ...args ) -{ - return std::unique_ptr( new T( std::forward(args)... ) ); -} - + template + std::unique_ptr make_unique( Args&& ...args ) + { + return std::unique_ptr( new T( std::forward(args)... ) ); + } }; +inline int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} + #endif + +inline static std::vector split(const std::string &s, char delim) +{ + std::vector elems; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + + +inline static std::string trim(std::string str) +{ + str.erase(0, str.find_first_not_of(" \t\r\n")); + str.erase(str.find_last_not_of(" \t\r\n") + 1); + return str; +} + + +inline static std::string str2lower(const std::string& str) +{ + std::string ret = str; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + + +inline static unsigned int str2col(std::string str) +{ + if (str[0] == '#') + str = &str[1]; + return std::stoul(str, nullptr, 16); +} + + +float static str2float(const std::string& str) +{ + return (float)atof(str.c_str()); +} + diff --git a/Version.cpp b/Version.cpp new file mode 100644 index 0000000..fb913ae --- /dev/null +++ b/Version.cpp @@ -0,0 +1,131 @@ +#include "Version.h" +#include "Util.h" +#include +#include +#include + + +#ifdef __APPLE__ +# include "TargetConditionals.h" +#elif defined(_WIN32) +# include +#else +# include +# include +# include +# include +#endif + + +void getSystemInfo(SystemInfo& a_info) +{ + a_info.m_cpuType = "unknown"; + a_info.m_osType = "unknown"; + a_info.m_osVersion = "unknown"; + +#ifdef _WIN32 + a_info.m_osType = "Windows"; + + // Get RAM size + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + a_info.m_ramSize = status.ullTotalPhys; + + // Get OS version + const OSVERSIONINFO osver = winOsVersion(); + a_info.m_osVersion.reserve(512); + snprintf(a_info.m_osVersion.data(), "%i.%i.%i", osver.dwMajorVersion, osver.dwMinorVersion, osver.dwBuildNumber); + + // Get OS arch + SYSTEM_INFO info; + GetNativeSystemInfo(&info); +# ifdef _WIN64 + //if (info.wProcessorArchitecture == ???) + a_info.m_cpuType = "x86_64"; +# else + a_info.m_cpuType = "x86"; +# endif +#else + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + a_info.m_ramSize = pages * page_size; + struct utsname u; + long ret = uname(&u); + if (ret != -1) + { + a_info.m_cpuType = u.machine; + a_info.m_osVersion = u.sysname + std::string(" ") + u.release; + } +# ifdef __APPLE__ + // Get RAM Size + int mib[2] = { CTL_HW, HW_MEMSIZE }; + size_t len = sizeof(uint64_t); + sysctl(mib, sizeof(mib) / sizeof(mib[0]), &u.ramSize, &len, NULL, 0); +# if TARGET_OS_IPHONE + a_info.m_osType = "iOS"; +# elif TARGET_OS_MAC + const char *macCodeName = ""; + switch (int(MacintoshVersion)) { + case MV_CHEETAH: macCodeName = " Cheetah"; break; + case MV_PUMA: macCodeName = " Puma"; break; + case MV_JAGUAR: macCodeName = " Jaguar"; break; + case MV_PANTHER: macCodeName = " Panther"; break; + case MV_TIGER: macCodeName = " Tiger"; break; + case MV_LEOPARD: macCodeName = " Leopard"; break; + case MV_SNOWLEOPARD: macCodeName = " Snow Leopard"; break; + case MV_LION: macCodeName = " Lion"; break; + case MV_MOUNTAINLION:macCodeName = " Mountain Lion"; break; + case MV_MAVERICKS: macCodeName = " Mavericks"; break; + case MV_YOSEMITE: macCodeName = " Yosemite"; break; + } + a_info.m_osType = "Mac OS X" + std::string(macCodeName); +# endif +# elif __ANDROID__ + a_info.m_osType = "Android"; +# elif __linux + // system("lsb_release -sd"); + FILE* f = fopen("/etc/lsb-release", "rt"); + char dist[64] = { 0 }; + if (f) { + char buf[128]; + while (fgets(buf, 128, f) != 0) + if (sscanf(buf, "DISTRIB_DESCRIPTION=%64c", dist) == 1) + break; + if (dist[0]) { + std::vector strBits = split(dist, '"'); + a_info.m_osType = strBits[(strBits.size()==3)?1:0]; + } else { + a_info.m_osType = "non-specific LSB"; + } + fclose(f); + } else { + a_info.m_osType = "non-LSB"; + } +# else + a_info.m_osType = "Generic UNIX"; +# endif +#endif + + // TODO: improve the CPU detection using libcpuid + + // Make human readable RAM size string + const char* humanUnitStrings[] = { " bytes", " KB", " MB", " GB", " TB" }; + int ramUnit = 0; + uint64_t ramSize = a_info.m_ramSize; + while (ramSize > 1024 && ramUnit < (YQ_ARRAY_SIZE(humanUnitStrings)-1)) + ramUnit++, ramSize /= 1000; + a_info.m_ramSizeStr = std::to_string(ramSize) + humanUnitStrings[ramUnit]; + + // Obtain the screen properties + QScreen *s = QApplication::screens()[0]; + int refreshRate = (s) ? s->refreshRate() : 0; + int depth = (s) ? s->depth() : 0; + a_info.m_resolutionWidth = (s) ? s->geometry().width() : 0; + a_info.m_resolutionHeight = (s) ? s->geometry().height() : 0; + a_info.m_resolutionStr = std::to_string(a_info.m_resolutionWidth) + "x" + + std::to_string(a_info.m_resolutionHeight) + " " + + std::to_string(depth) + "bit " + + "@" + std::to_string(refreshRate) + "Hz"; + ; +} diff --git a/Version.h b/Version.h new file mode 100644 index 0000000..5dd615c --- /dev/null +++ b/Version.h @@ -0,0 +1,32 @@ +#ifndef VERSION_H +#define VERSION_H + + +#include "Utilities.h" +#include +#include + + +#define APP_VERSION 1.00 +#define APP_BUILD 0001 +#define APP_VER_STR YQ_STRINGIZE(APP_VERSION) "." YQ_STRINGIZE(APP_BUILD) +#define APP_VER_STR_LONG "Version " YQ_STRINGIZE(APP_VERSION) ", Build" YQ_STRINGIZE(APP_BUILD) + + +struct SystemInfo +{ + std::string m_cpuType; + std::string m_osType; + std::string m_osVersion; + std::string m_ramSizeStr; // Human friendly string, eg: "4 GB" + std::string m_resolutionStr; + uint64_t m_ramSize; + uint32_t m_resolutionWidth; + uint32_t m_resolutionHeight; +}; + + +void getSystemInfo(SystemInfo& a_info); + + +#endif // VERSION_H diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/MdiWindow.h b/MdiWindow.h new file mode 100644 index 0000000..76d7f1b --- /dev/null +++ b/MdiWindow.h @@ -0,0 +1,174 @@ +#ifndef MDI_WINDOW_H +#define MDI_WINDOW_H + + +#include +#include + + +class MdiChild; +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QSignalMapper; +class Ui_MainWindow; + + +class MdiChild : public QTextEdit +{ + Q_OBJECT +public: + MdiChild(); + ~MdiChild(); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + + void markdownSelection(const char* preText, const char* postText); + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + +signals: + void closing(); + void documentContentsChanged(); + +private slots: + void documentWasModified(); + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled; +}; + + +/* +class MdiOptions +{ +public: + void serialize(); + void deserialize(); +}; +*/ + + +class MdiWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MdiWindow(QWidget *parent = 0); + void init(Ui_MainWindow* ui); + + bool openFile(const QString &fileName); +signals: + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +public slots: + +private slots: + + // File menu actions + void newFile(); + void open(); + void save(); + void saveAs(); + + void updatePreview(); + + // Edit menu actions +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void options(); + void saveAll(); + void selectAll(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + + // Help + void support(); + void website(); + + // Window menu actions + void cascadeSubWindows(); + void tileSubWindows(); + void tabSubWindows(); + + void updateActions(); + MdiChild *createMdiChild(); + void switchLayoutDirection(); + void setActiveSubWindow(QWidget *window); + +private: + void readSettings(); + void writeSettings(); + MdiChild *activeMdiChild(); + QMdiSubWindow *findMdiChild(const QString &fileName); + + QMdiArea *mdiArea; + QSignalMapper *windowMapper; + Ui_MainWindow *appWindow; +}; + + +#endif // MDI_WINDOW_H diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index 7680de9..f4e410c 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -1,224 +1,236 @@ - -QT += core gui widgets - -CONFIG -= flat - -TARGET = MakePDF - -DEFINES += \ - GUI_APP=1 - -RESOURCES += \ - ../Resources/resources.qrc - -FORMS += \ - ../Resources/forms/About.ui \ - ../Resources/forms/NewProjectWizard.ui \ - ../Resources/forms/ExtensibleObjectModelUI.ui \ - ../Resources/forms/SendBugReport.ui - -INCLUDEPATH += \ - ../Framework \ - ../3rdParty/hoedown \ - ../3rdParty/libharu/include \ - ../3rdParty/TinyXML++ - -HEADERS += \ - ../Framework/EventLog.h \ - ../Framework/Expected.h \ - ../Framework/Final.h \ - ../Framework/Finally.h \ - ../Framework/GenericTable.h \ - ../Framework/GenericTableUI.h \ - ../Framework/NonCopyable.h \ - ../Framework/Utilities.h - -HEADERS += \ - ../DocOutput.h \ - ../DocProject.h \ - ../DocStyle.h \ - ../DocTemplate.h \ - ../DocVisitor.h \ - ../DocSVG.h - -SOURCES += \ - ../Framework/Tests.cpp \ - ../Framework/Utilities.cpp \ - ../Framework/EventLog.cpp - -SOURCES += \ - ../DocOutput.cpp \ - ../DocProject.cpp \ - ../DocStyle.cpp \ - ../DocTemplate.cpp \ - ../DocVisitor.cpp \ - ../DocSVG.cpp \ - ../main.cpp - -SOURCES += \ - ../3rdParty/hoedown/autolink.c \ - ../3rdParty/hoedown/buffer.c \ - ../3rdParty/hoedown/document.c \ - ../3rdParty/hoedown/escape.c \ - ../3rdParty/hoedown/html.c \ - ../3rdParty/hoedown/html_blocks.c \ - ../3rdParty/hoedown/stack.c - -SOURCES += \ - ../3rdParty/libharu/src/hpdf_3dmeasure.c \ - ../3rdParty/libharu/src/hpdf_annotation.c \ - ../3rdParty/libharu/src/hpdf_array.c \ - ../3rdParty/libharu/src/hpdf_binary.c \ - ../3rdParty/libharu/src/hpdf_boolean.c \ - ../3rdParty/libharu/src/hpdf_catalog.c \ - ../3rdParty/libharu/src/hpdf_destination.c \ - ../3rdParty/libharu/src/hpdf_dict.c \ - ../3rdParty/libharu/src/hpdf_doc.c \ - ../3rdParty/libharu/src/hpdf_doc_png.c \ - ../3rdParty/libharu/src/hpdf_encoder.c \ - ../3rdParty/libharu/src/hpdf_encoder_utf.c \ - ../3rdParty/libharu/src/hpdf_encrypt.c \ - ../3rdParty/libharu/src/hpdf_encryptdict.c \ - ../3rdParty/libharu/src/hpdf_error.c \ - ../3rdParty/libharu/src/hpdf_exdata.c \ - ../3rdParty/libharu/src/hpdf_ext_gstate.c \ - ../3rdParty/libharu/src/hpdf_font.c \ - ../3rdParty/libharu/src/hpdf_fontdef.c \ - ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ - ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ - ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ - ../3rdParty/libharu/src/hpdf_font_cid.c \ - ../3rdParty/libharu/src/hpdf_font_tt.c \ - ../3rdParty/libharu/src/hpdf_font_type1.c \ - ../3rdParty/libharu/src/hpdf_gstate.c \ - ../3rdParty/libharu/src/hpdf_image.c \ - ../3rdParty/libharu/src/hpdf_image_png.c \ - ../3rdParty/libharu/src/hpdf_info.c \ - ../3rdParty/libharu/src/hpdf_list.c \ - ../3rdParty/libharu/src/hpdf_mmgr.c \ - ../3rdParty/libharu/src/hpdf_name.c \ - ../3rdParty/libharu/src/hpdf_namedict.c \ - ../3rdParty/libharu/src/hpdf_null.c \ - ../3rdParty/libharu/src/hpdf_number.c \ - ../3rdParty/libharu/src/hpdf_objects.c \ - ../3rdParty/libharu/src/hpdf_outline.c \ - ../3rdParty/libharu/src/hpdf_pages.c \ - ../3rdParty/libharu/src/hpdf_page_label.c \ - ../3rdParty/libharu/src/hpdf_page_operator.c \ - ../3rdParty/libharu/src/hpdf_pdfa.c \ - ../3rdParty/libharu/src/hpdf_real.c \ - ../3rdParty/libharu/src/hpdf_streams.c \ - ../3rdParty/libharu/src/hpdf_string.c \ - ../3rdParty/libharu/src/hpdf_u3d.c \ - ../3rdParty/libharu/src/hpdf_utils.c \ - ../3rdParty/libharu/src/hpdf_xref.c - -SOURCES += \ - ../3rdParty/TinyXML++/ticpp.cpp \ - ../3rdParty/TinyXML++/tinystr.cpp \ - ../3rdParty/TinyXML++/tinyxml.cpp \ - ../3rdParty/TinyXML++/tinyxmlerror.cpp \ - ../3rdParty/TinyXML++/tinyxmlparser.cpp - - -# As a note to what was removed -REMOVED_SOURCES = \ - ../3rdParty/hoedown/html_smartypants.c \ - ../3rdParty/hoedown/version.c \ - ../3rdParty/libharu/src/hpdf_image_ccitt.c - - -# Linux specific settings -unix { - - QMAKE_CXXFLAGS += --std=gnu++11 - # QMAKE_CXXFLAGS -= -Wall - QMAKE_CXXFLAGS_WARN_ON=-w - QMAKE_CFLAGS_WARN_ON=-w - LIBS += -lpng -lz - -} - - -# Windows specific. Note we compile in libpng and zlib statically on Windows -win32 { - - # Supress annoying warnings - QMAKE_CXXFLAGS += /wd"4996;4100" - QMAKE_CXXFLAGS_WARN_ON -= -w34100 - - - # Release mode debugging - QMAKE_CXXFLAGS += /Zi - QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF - - - # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything - Project.target = $${TARGET}.vcxproj - Project.output = $${TARGET}.vcxproj - Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 - Project.depends = '"$${_PRO_FILE_}"' - Project.input = _PRO_FILE_ - QMAKE_EXTRA_COMPILERS += Project - - - # This makes the documentation - MARKDOWN_FILES = \ - test.md \ - test2.md - - Documentation.target = test.pdf - Documentation.output = test.pdf - Documentation.commands = echo ' ' > test.pdf - Documentation.depends = '"$${MARKDOWN_FILES}"' - Documentation.input = MARKDOWN_FILES - QMAKE_EXTRA_COMPILERS += Documentation - - - INCLUDEPATH += \ - ../3rdParty/libpng \ - ../3rdParty/zlib - - - SOURCES += \ - ../3rdParty/libpng/png.c \ - ../3rdParty/libpng/pngerror.c \ - ../3rdParty/libpng/pngget.c \ - ../3rdParty/libpng/pngmem.c \ - ../3rdParty/libpng/pngpread.c \ - ../3rdParty/libpng/pngread.c \ - ../3rdParty/libpng/pngrio.c \ - ../3rdParty/libpng/pngrtran.c \ - ../3rdParty/libpng/pngrutil.c \ - ../3rdParty/libpng/pngset.c \ - ../3rdParty/libpng/pngtrans.c \ - ../3rdParty/libpng/pngwio.c \ - ../3rdParty/libpng/pngwrite.c \ - ../3rdParty/libpng/pngwtran.c \ - ../3rdParty/libpng/pngwutil.c - - SOURCES += \ - ../3rdParty/zlib/inftrees.c \ - ../3rdParty/zlib/trees.c \ - ../3rdParty/zlib/uncompr.c \ - ../3rdParty/zlib/zutil.c \ - ../3rdParty/zlib/adler32.c \ - ../3rdParty/zlib/compress.c \ - ../3rdParty/zlib/crc32.c \ - ../3rdParty/zlib/deflate.c \ - ../3rdParty/zlib/gzclose.c \ - ../3rdParty/zlib/gzlib.c \ - ../3rdParty/zlib/gzread.c \ - ../3rdParty/zlib/gzwrite.c \ - ../3rdParty/zlib/infback.c \ - ../3rdParty/zlib/inffast.c \ - ../3rdParty/zlib/inflate.c - -} - - + +QT += core gui widgets + +CONFIG -= flat + +TARGET = MakePDF + +DEFINES += \ + GUI_APP=1 + +RESOURCES += \ + ../Resources/resources.qrc + +FORMS += \ + ../Resources/forms/About.ui \ + ../Resources/forms/NewProjectWizard.ui \ + ../Resources/forms/ExtensibleObjectModelUI.ui \ + ../Resources/forms/SendBugReport.ui \ + ../Resources/forms/Help.ui \ + ../Resources/forms/CheckUpdates.ui \ + ../Resources/forms/LicenseUpgrade.ui \ + ../Resources/forms/Settings.ui \ + ../Resources/forms/PaletteEditor.ui + +INCLUDEPATH += \ + ../Framework \ + ../3rdParty/hoedown \ + ../3rdParty/libharu/include \ + ../3rdParty/TinyXML++ + +HEADERS += \ + ../Framework/EventLog.h \ + ../Framework/Expected.h \ + ../Framework/Final.h \ + ../Framework/Finally.h \ + ../Framework/GenericTable.h \ + ../Framework/GenericTableUI.h \ + ../Framework/NonCopyable.h \ + ../Framework/Utilities.h \ + ../ui.h \ + ../MdiWindow.h \ + ../ColorPicker.h \ + ../Version.h + +HEADERS += \ + ../DocOutput.h \ + ../DocProject.h \ + ../DocStyle.h \ + ../DocTemplate.h \ + ../DocVisitor.h \ + ../DocSVG.h + +SOURCES += \ + ../Framework/Tests.cpp \ + ../Framework/Utilities.cpp \ + ../Framework/EventLog.cpp \ + ../MdiWindow.cpp \ + ../ColorPicker.cpp \ + ../Version.cpp + +SOURCES += \ + ../DocOutput.cpp \ + ../DocProject.cpp \ + ../DocStyle.cpp \ + ../DocTemplate.cpp \ + ../DocVisitor.cpp \ + ../DocSVG.cpp \ + ../main.cpp + +SOURCES += \ + ../3rdParty/hoedown/autolink.c \ + ../3rdParty/hoedown/buffer.c \ + ../3rdParty/hoedown/document.c \ + ../3rdParty/hoedown/escape.c \ + ../3rdParty/hoedown/html.c \ + ../3rdParty/hoedown/html_blocks.c \ + ../3rdParty/hoedown/stack.c + +SOURCES += \ + ../3rdParty/libharu/src/hpdf_3dmeasure.c \ + ../3rdParty/libharu/src/hpdf_annotation.c \ + ../3rdParty/libharu/src/hpdf_array.c \ + ../3rdParty/libharu/src/hpdf_binary.c \ + ../3rdParty/libharu/src/hpdf_boolean.c \ + ../3rdParty/libharu/src/hpdf_catalog.c \ + ../3rdParty/libharu/src/hpdf_destination.c \ + ../3rdParty/libharu/src/hpdf_dict.c \ + ../3rdParty/libharu/src/hpdf_doc.c \ + ../3rdParty/libharu/src/hpdf_doc_png.c \ + ../3rdParty/libharu/src/hpdf_encoder.c \ + ../3rdParty/libharu/src/hpdf_encoder_utf.c \ + ../3rdParty/libharu/src/hpdf_encrypt.c \ + ../3rdParty/libharu/src/hpdf_encryptdict.c \ + ../3rdParty/libharu/src/hpdf_error.c \ + ../3rdParty/libharu/src/hpdf_exdata.c \ + ../3rdParty/libharu/src/hpdf_ext_gstate.c \ + ../3rdParty/libharu/src/hpdf_font.c \ + ../3rdParty/libharu/src/hpdf_fontdef.c \ + ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ + ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ + ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ + ../3rdParty/libharu/src/hpdf_font_cid.c \ + ../3rdParty/libharu/src/hpdf_font_tt.c \ + ../3rdParty/libharu/src/hpdf_font_type1.c \ + ../3rdParty/libharu/src/hpdf_gstate.c \ + ../3rdParty/libharu/src/hpdf_image.c \ + ../3rdParty/libharu/src/hpdf_image_png.c \ + ../3rdParty/libharu/src/hpdf_info.c \ + ../3rdParty/libharu/src/hpdf_list.c \ + ../3rdParty/libharu/src/hpdf_mmgr.c \ + ../3rdParty/libharu/src/hpdf_name.c \ + ../3rdParty/libharu/src/hpdf_namedict.c \ + ../3rdParty/libharu/src/hpdf_null.c \ + ../3rdParty/libharu/src/hpdf_number.c \ + ../3rdParty/libharu/src/hpdf_objects.c \ + ../3rdParty/libharu/src/hpdf_outline.c \ + ../3rdParty/libharu/src/hpdf_pages.c \ + ../3rdParty/libharu/src/hpdf_page_label.c \ + ../3rdParty/libharu/src/hpdf_page_operator.c \ + ../3rdParty/libharu/src/hpdf_pdfa.c \ + ../3rdParty/libharu/src/hpdf_real.c \ + ../3rdParty/libharu/src/hpdf_streams.c \ + ../3rdParty/libharu/src/hpdf_string.c \ + ../3rdParty/libharu/src/hpdf_u3d.c \ + ../3rdParty/libharu/src/hpdf_utils.c \ + ../3rdParty/libharu/src/hpdf_xref.c + +SOURCES += \ + ../3rdParty/TinyXML++/ticpp.cpp \ + ../3rdParty/TinyXML++/tinystr.cpp \ + ../3rdParty/TinyXML++/tinyxml.cpp \ + ../3rdParty/TinyXML++/tinyxmlerror.cpp \ + ../3rdParty/TinyXML++/tinyxmlparser.cpp + + +# As a note to what was removed +REMOVED_SOURCES = \ + ../3rdParty/hoedown/html_smartypants.c \ + ../3rdParty/hoedown/version.c \ + ../3rdParty/libharu/src/hpdf_image_ccitt.c + + +# Linux specific settings +unix { + + QMAKE_CXXFLAGS += --std=gnu++11 + # QMAKE_CXXFLAGS -= -Wall + QMAKE_CXXFLAGS_WARN_ON=-w + QMAKE_CFLAGS_WARN_ON=-w + LIBS += -lpng -lz + +} + + +# Windows specific. Note we compile in libpng and zlib statically on Windows +win32 { + + # Supress annoying warnings + QMAKE_CXXFLAGS += /wd"4996;4100" + QMAKE_CXXFLAGS_WARN_ON -= -w34100 + + + # Release mode debugging + QMAKE_CXXFLAGS += /Zi + QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF + + + # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything + Project.target = $${TARGET}.vcxproj + Project.output = $${TARGET}.vcxproj + Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 + Project.depends = '"$${_PRO_FILE_}"' + Project.input = _PRO_FILE_ + QMAKE_EXTRA_COMPILERS += Project + + + # This makes the documentation + MARKDOWN_FILES = \ + test.md \ + test2.md + + Documentation.target = test.pdf + Documentation.output = test.pdf + Documentation.commands = echo ' ' > test.pdf + Documentation.depends = '"$${MARKDOWN_FILES}"' + Documentation.input = MARKDOWN_FILES + QMAKE_EXTRA_COMPILERS += Documentation + + + INCLUDEPATH += \ + ../3rdParty/libpng \ + ../3rdParty/zlib + + + SOURCES += \ + ../3rdParty/libpng/png.c \ + ../3rdParty/libpng/pngerror.c \ + ../3rdParty/libpng/pngget.c \ + ../3rdParty/libpng/pngmem.c \ + ../3rdParty/libpng/pngpread.c \ + ../3rdParty/libpng/pngread.c \ + ../3rdParty/libpng/pngrio.c \ + ../3rdParty/libpng/pngrtran.c \ + ../3rdParty/libpng/pngrutil.c \ + ../3rdParty/libpng/pngset.c \ + ../3rdParty/libpng/pngtrans.c \ + ../3rdParty/libpng/pngwio.c \ + ../3rdParty/libpng/pngwrite.c \ + ../3rdParty/libpng/pngwtran.c \ + ../3rdParty/libpng/pngwutil.c + + SOURCES += \ + ../3rdParty/zlib/inftrees.c \ + ../3rdParty/zlib/trees.c \ + ../3rdParty/zlib/uncompr.c \ + ../3rdParty/zlib/zutil.c \ + ../3rdParty/zlib/adler32.c \ + ../3rdParty/zlib/compress.c \ + ../3rdParty/zlib/crc32.c \ + ../3rdParty/zlib/deflate.c \ + ../3rdParty/zlib/gzclose.c \ + ../3rdParty/zlib/gzlib.c \ + ../3rdParty/zlib/gzread.c \ + ../3rdParty/zlib/gzwrite.c \ + ../3rdParty/zlib/infback.c \ + ../3rdParty/zlib/inffast.c \ + ../3rdParty/zlib/inflate.c + +} + + diff --git a/Resources/resources.qrc b/Resources/resources.qrc index 55e4392..d6ef98c 100644 --- a/Resources/resources.qrc +++ b/Resources/resources.qrc @@ -1,44 +1,50 @@ - - icons/window26.png - icons/lifebelt.png - icons/lightbulb3.png - icons/open134.png - icons/question24.png - icons/speech64.png - icons/speech66.png - icons/window30.png - icons/h1.png - icons/h2.png - icons/h3.png - icons/h4.png - icons/h5.png - icons/normal.png - icons/cut20.png - icons/delete30.png - icons/development.png - icons/document28.png - icons/italics.png - icons/floppy13.png - icons/font8.png - icons/floppy15.png - icons/list36.png - icons/link71.png - icons/magnifying glass39.png - icons/magnifying glass40.png - icons/minus25.png - icons/numbered5.png - icons/open131.png - icons/page10.png - icons/paste1.png - icons/pdf24.png - icons/printing28.png - icons/quotation1.png - icons/redo5.png - icons/undo7.png - icons/squares64.png - - - icons/logo.png - + + icons/window26.png + icons/lifebelt.png + icons/lightbulb3.png + icons/open134.png + icons/question24.png + icons/speech64.png + icons/speech66.png + icons/window30.png + icons/h1.png + icons/h2.png + icons/h3.png + icons/h4.png + icons/h5.png + icons/normal.png + icons/cut20.png + icons/delete30.png + icons/development.png + icons/document28.png + icons/italics.png + icons/floppy13.png + icons/font8.png + icons/floppy15.png + icons/list36.png + icons/link71.png + icons/magnifying glass39.png + icons/magnifying glass40.png + icons/minus25.png + icons/numbered5.png + icons/open131.png + icons/page10.png + icons/paste1.png + icons/pdf24.png + icons/printing28.png + icons/quotation1.png + icons/redo5.png + icons/undo7.png + icons/squares64.png + + + images/logo.svg + images/color-wheel-mode-1.png + images/color-wheel-mode-2.png + images/color-wheel-mode-3.png + images/color-wheel-mode-4.png + images/color-wheel-mode-5.png + images/color-wheel.png + diff --git a/Util.h b/Util.h index bc6a8a5..1dfc996 100644 --- a/Util.h +++ b/Util.h @@ -1,17 +1,66 @@ #include +#include +#include +#include #ifndef _MSC_VER namespace std { - -template -std::unique_ptr make_unique( Args&& ...args ) -{ - return std::unique_ptr( new T( std::forward(args)... ) ); -} - + template + std::unique_ptr make_unique( Args&& ...args ) + { + return std::unique_ptr( new T( std::forward(args)... ) ); + } }; +inline int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} + #endif + +inline static std::vector split(const std::string &s, char delim) +{ + std::vector elems; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + + +inline static std::string trim(std::string str) +{ + str.erase(0, str.find_first_not_of(" \t\r\n")); + str.erase(str.find_last_not_of(" \t\r\n") + 1); + return str; +} + + +inline static std::string str2lower(const std::string& str) +{ + std::string ret = str; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + + +inline static unsigned int str2col(std::string str) +{ + if (str[0] == '#') + str = &str[1]; + return std::stoul(str, nullptr, 16); +} + + +float static str2float(const std::string& str) +{ + return (float)atof(str.c_str()); +} + diff --git a/Version.cpp b/Version.cpp new file mode 100644 index 0000000..fb913ae --- /dev/null +++ b/Version.cpp @@ -0,0 +1,131 @@ +#include "Version.h" +#include "Util.h" +#include +#include +#include + + +#ifdef __APPLE__ +# include "TargetConditionals.h" +#elif defined(_WIN32) +# include +#else +# include +# include +# include +# include +#endif + + +void getSystemInfo(SystemInfo& a_info) +{ + a_info.m_cpuType = "unknown"; + a_info.m_osType = "unknown"; + a_info.m_osVersion = "unknown"; + +#ifdef _WIN32 + a_info.m_osType = "Windows"; + + // Get RAM size + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + a_info.m_ramSize = status.ullTotalPhys; + + // Get OS version + const OSVERSIONINFO osver = winOsVersion(); + a_info.m_osVersion.reserve(512); + snprintf(a_info.m_osVersion.data(), "%i.%i.%i", osver.dwMajorVersion, osver.dwMinorVersion, osver.dwBuildNumber); + + // Get OS arch + SYSTEM_INFO info; + GetNativeSystemInfo(&info); +# ifdef _WIN64 + //if (info.wProcessorArchitecture == ???) + a_info.m_cpuType = "x86_64"; +# else + a_info.m_cpuType = "x86"; +# endif +#else + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + a_info.m_ramSize = pages * page_size; + struct utsname u; + long ret = uname(&u); + if (ret != -1) + { + a_info.m_cpuType = u.machine; + a_info.m_osVersion = u.sysname + std::string(" ") + u.release; + } +# ifdef __APPLE__ + // Get RAM Size + int mib[2] = { CTL_HW, HW_MEMSIZE }; + size_t len = sizeof(uint64_t); + sysctl(mib, sizeof(mib) / sizeof(mib[0]), &u.ramSize, &len, NULL, 0); +# if TARGET_OS_IPHONE + a_info.m_osType = "iOS"; +# elif TARGET_OS_MAC + const char *macCodeName = ""; + switch (int(MacintoshVersion)) { + case MV_CHEETAH: macCodeName = " Cheetah"; break; + case MV_PUMA: macCodeName = " Puma"; break; + case MV_JAGUAR: macCodeName = " Jaguar"; break; + case MV_PANTHER: macCodeName = " Panther"; break; + case MV_TIGER: macCodeName = " Tiger"; break; + case MV_LEOPARD: macCodeName = " Leopard"; break; + case MV_SNOWLEOPARD: macCodeName = " Snow Leopard"; break; + case MV_LION: macCodeName = " Lion"; break; + case MV_MOUNTAINLION:macCodeName = " Mountain Lion"; break; + case MV_MAVERICKS: macCodeName = " Mavericks"; break; + case MV_YOSEMITE: macCodeName = " Yosemite"; break; + } + a_info.m_osType = "Mac OS X" + std::string(macCodeName); +# endif +# elif __ANDROID__ + a_info.m_osType = "Android"; +# elif __linux + // system("lsb_release -sd"); + FILE* f = fopen("/etc/lsb-release", "rt"); + char dist[64] = { 0 }; + if (f) { + char buf[128]; + while (fgets(buf, 128, f) != 0) + if (sscanf(buf, "DISTRIB_DESCRIPTION=%64c", dist) == 1) + break; + if (dist[0]) { + std::vector strBits = split(dist, '"'); + a_info.m_osType = strBits[(strBits.size()==3)?1:0]; + } else { + a_info.m_osType = "non-specific LSB"; + } + fclose(f); + } else { + a_info.m_osType = "non-LSB"; + } +# else + a_info.m_osType = "Generic UNIX"; +# endif +#endif + + // TODO: improve the CPU detection using libcpuid + + // Make human readable RAM size string + const char* humanUnitStrings[] = { " bytes", " KB", " MB", " GB", " TB" }; + int ramUnit = 0; + uint64_t ramSize = a_info.m_ramSize; + while (ramSize > 1024 && ramUnit < (YQ_ARRAY_SIZE(humanUnitStrings)-1)) + ramUnit++, ramSize /= 1000; + a_info.m_ramSizeStr = std::to_string(ramSize) + humanUnitStrings[ramUnit]; + + // Obtain the screen properties + QScreen *s = QApplication::screens()[0]; + int refreshRate = (s) ? s->refreshRate() : 0; + int depth = (s) ? s->depth() : 0; + a_info.m_resolutionWidth = (s) ? s->geometry().width() : 0; + a_info.m_resolutionHeight = (s) ? s->geometry().height() : 0; + a_info.m_resolutionStr = std::to_string(a_info.m_resolutionWidth) + "x" + + std::to_string(a_info.m_resolutionHeight) + " " + + std::to_string(depth) + "bit " + + "@" + std::to_string(refreshRate) + "Hz"; + ; +} diff --git a/Version.h b/Version.h new file mode 100644 index 0000000..5dd615c --- /dev/null +++ b/Version.h @@ -0,0 +1,32 @@ +#ifndef VERSION_H +#define VERSION_H + + +#include "Utilities.h" +#include +#include + + +#define APP_VERSION 1.00 +#define APP_BUILD 0001 +#define APP_VER_STR YQ_STRINGIZE(APP_VERSION) "." YQ_STRINGIZE(APP_BUILD) +#define APP_VER_STR_LONG "Version " YQ_STRINGIZE(APP_VERSION) ", Build" YQ_STRINGIZE(APP_BUILD) + + +struct SystemInfo +{ + std::string m_cpuType; + std::string m_osType; + std::string m_osVersion; + std::string m_ramSizeStr; // Human friendly string, eg: "4 GB" + std::string m_resolutionStr; + uint64_t m_ramSize; + uint32_t m_resolutionWidth; + uint32_t m_resolutionHeight; +}; + + +void getSystemInfo(SystemInfo& a_info); + + +#endif // VERSION_H diff --git a/main.cpp b/main.cpp index c5d5163..a4a0bd4 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,7 @@ #include "DocTemplate.h" #include "DocOutput.h" #include "DocSVG.h" +#include "Util.h" //#include "document.h" #include "html.h" @@ -19,15 +20,9 @@ #ifndef _WIN32 # include -# define __cdecl +//# define __cdecl #else extern "C" void __stdcall Sleep(unsigned int); -//# include -#endif - - -#ifndef _MSC_VER // _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode); #endif @@ -119,7 +114,7 @@ } -void SVGTest(const char* a_fileName, DocOutputDevice* outputDoc) +void SVGTest(const char* a_fileName, double scale, DocOutputDevice* outputDoc) { hoedown_buffer* inputBuffer = ReadInWholeFile(a_fileName); if (!inputBuffer) @@ -127,7 +122,7 @@ // SVG xml parse TiXmlDocument parser; parser.Parse((char*)inputBuffer->data); - DocSVG visitor; + DocSVG visitor(scale); parser.Accept(&visitor); visitor.DumpOperations(); visitor.WriteTo(outputDoc); @@ -147,7 +142,8 @@ DocVisitor visitor(outputDoc, &style, &templ); parser.Accept(&visitor); - SVGTest("test/test.svg", outputDoc); + //SVGTest("test/triangle.svg", 0.02, outputDoc); + //SVGTest("test/ArcTest.svg", 1.0, outputDoc); } @@ -184,33 +180,56 @@ int main(int argc, char* argv[]) { + DocOutputDevice outputDoc; + Context context = { + "Test Markdown to PDF", + "test/test.md", + "test/test.pdf", + "test/test.html", + &outputDoc + }; + ProcessConversionContext(&context); + +/* + // Qt MDI Example does this: + + Q_INIT_RESOURCE(mdi); + QApplication app(argc, argv); + QCoreApplication::setApplicationVersion(QT_VERSION_STR); + QCommandLineParser parser; + parser.setApplicationDescription("Qt MDI Example"); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument("file", "The file to open."); + parser.process(app); + + MainWindow mainWin; + foreach (const QString &fileName, parser.positionalArguments()) + mainWin.openFile(fileName); + mainWin.show(); + return app.exec(); +*/ + +#ifdef GUI_APP + QApplication app(argc, argv); + UiContext ui; + ui.setupUi(); + //YQ_LOG_DEBUG("This is a test message"); + //runTests(); + app.exec(); +#endif + + +/* fprintf(stderr, "HERE!\n\n\n"); QApplication app2(argc, argv); QLabel l(0); l.setText("Hello!"); l.show(); app2.exec(); +*/ - DocOutputDevice outputDoc; - Context context = { - "Test Markdown to PDF", - "test/test.md", - "test/test.pdf", - "test/test.html", - &outputDoc - }; - ProcessConversionContext(&context); - -#ifdef GUI_APP - QApplication app(argc, argv); - UiContext ui; - ui.setupUi(); - YQ_LOG_DEBUG("This is a test message"); - runTests(); - app.exec(); -#endif - return 0; } diff --git a/ColorPicker.cpp b/ColorPicker.cpp new file mode 100644 index 0000000..3278ae3 --- /dev/null +++ b/ColorPicker.cpp @@ -0,0 +1,400 @@ +#include +#include "ColorPicker.h" + + +static const int modeHands[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + + +static const int modeHandAngles[6][4] = { + { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, + { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } +}; + + +static const char* modeNames[6] = { + "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" +}; + + +const char* ColorScheme::modeName() +{ + if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) + return "Triadic"; + if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) + return "Split-Complementary"; + return modeNames[m_mode]; +} + + +int ColorScheme::hueCount() +{ + return modeHands[m_mode]; +} + + +void ColorScheme::setHue(int a_index, float a_val) +{ + if (a_index == 0) + m_primaryHue = unsigned(a_val * 255.0) & 0xff; + else if (a_index == 1 && m_mode != 2) + m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; + else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) + m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; + else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) + m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; + else + m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; +} + + +float denormalizedHue(ColorScheme a_scheme, int a_index) +{ + if (a_index == 0) + return float(a_scheme.m_primaryHue) / 255.0; + if (a_index == 1 && a_scheme.m_mode != 2) + return 0.5 + float(a_scheme.m_primaryHue) / 255.0; + if ((a_index == 2 && a_scheme.m_mode != 2) || (a_scheme.m_mode == 2 && a_index == 1)) + return float((int)a_scheme.m_primaryHue + a_scheme.m_secondaryHueDelta) / 255.0; + if (a_scheme.m_mode == 3 || (a_scheme.m_mode == 2 && a_index == 2)) + return float((int)a_scheme.m_primaryHue - (int)a_scheme.m_secondaryHueDelta) / 255.0; + return 0.5 + float((int)a_scheme.m_primaryHue + (int)a_scheme.m_secondaryHueDelta) / 255.0; +} + + +float ColorScheme::hue(int a_index) +{ + float h = denormalizedHue(*this, a_index); + while (h < 0.0) h += 1.0; + while (h > 1.0) h -= 1.0; + return h; +} + + +// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() +uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) +{ + uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); + h1 = hue == 36000 ? 0 : hue / qreal(6000.); + s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); + v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); + const qreal h = h1; + const int i = int(h); + const qreal f = h - i; + const qreal v2 = v * USHRT_MAX; + uint8_t components[4] = { + qRound(v2 * (qreal(1.0) - s)) >> 8, + qRound(v2) >> 8, + qRound(v2 * (qreal(1.0) - (s * f))) >> 8, + qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 + }; + const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; + return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; +} + + +bool m_wheelCached = false; +QImage colorWheelCache; + + +void makeColorWheel(int siz, unsigned bgColor) +{ + const float twoPi = 2 * 3.142; + const int cen = (siz / 2); + const int rad = cen - 20; + const int iter = siz * 10; + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + double dSat = 1.0 / rad; + for (int i = 0; i < iter; i++) + { + double hue = double(i) / iter; + double c = ::cos(hue * twoPi); + double s = ::sin(hue * twoPi); + double x = cen + rad * c; + double y = cen + rad * s; + double sat = 1.0; + for (int w = rad-30; w < rad; w++) + { + bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); + x -= c; + y -= s; + //sat -= dSat; + } + } + // Smoothing technique where we scale it up a fraction and then back down again + img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_wheelCached = true; +} + + +static const int siz = 250; +static const int cen = (siz / 2); +static const int rad = (siz / 2) - 20; +static const int iter = siz * 10; +static const int dotSiz = 18;// 16; +static const int dotAlpha = 96;// 64; + +static const int titleHeight = 35; + +static const int schemeWidth = 80; +static const int wheelWidth = siz * 2;// 600; + +static const int paletteWidth = 150; +static const int paletteHeight = 350; + +static const int xOff = schemeWidth; +static const int yOff = titleHeight; + + +class ModeButton : public QPushButton +{ +public: + ModeButton(QWidget* a_parent, int a_mode) + : QPushButton(a_parent) + , m_mode(a_mode) + { + setAutoExclusive(true); + setCheckable(true); + setFlat(true); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + setMinimumSize(45,45); + } + void paintEvent(QPaintEvent*) + { + QPainter p(this); + QPixmap pix1("../color-dial-deselect.png"); + QPixmap pix2("../color-dial-select.png"); + QPixmap pixHand1("../color-pointer-deselect.png"); + QPixmap pixHand2("../color-pointer-select.png"); + bool checked = isChecked(); + int xoff = (width() - 55) / 2; + int yoff = (height() - 55) / 2; + p.drawPixmap(xoff, yoff, (checked) ? pix2 : pix1); + QPixmap hands = (checked) ? pixHand2 : pixHand1; + for (int j = 0; j < modeHands[m_mode]; j++) + { + QMatrix m; + m = m.scale(0.5, 0.5); + m = m.rotate(modeHandAngles[m_mode][j]); + QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); + QSize s = newPix.size() - hands.size() / 2; + s = s / 2; + p.drawPixmap(xoff - s.width(), yoff - s.height(), newPix); + } + } +private: + int m_mode; +}; + + +SchemeSelection::SchemeSelection(QWidget* a_parent) : QWidget(a_parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + QSignalMapper* signalMapper = new QSignalMapper(this); + connect(signalMapper, SIGNAL(mapped(int)), a_parent, SLOT(setMode(int))); + for (int i = 0; i < ColorScheme::modes; i++) { + modeButtons[i] = new ModeButton(this, i); + connect(modeButtons[i], SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(modeButtons[i], i); + vbox->addWidget(modeButtons[i]); + } + modeButtons[0]->setChecked(true); + setLayout(vbox); + vbox->setMargin(0); + vbox->setSpacing(0); +} + + + +// lineedit to name the scheme +// okay/cancel buttons +// styled titlebar + + +const unsigned bgColor1 = 0x444444; +const unsigned bgColor2 = 0x888888; +const int lineHeight = 50; + +// Properties of a scheme: +// mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) +// saturation (8-bits), stretch (5-bits) +ColorPicker::ColorPicker(QWidget* parent) : QDialog(parent), scheme(this) +{ + setModal(true); + palette.m_mode = 0; + palette.m_primaryHue = 0; + palette.m_secondaryHueDelta = 10; + palette.m_saturation = 0; + palette.m_stretch = 0; + scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); + setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight + lineHeight); + m_hueMovingIdx = -1; + + QPushButton* okyBut = new QPushButton("Okay", this); + QPushButton* canBut = new QPushButton("Cancel", this); + okyBut->setGeometry(width() - 120, height() - 90, 100, 30); + canBut->setGeometry(width() - 120, height() - 50, 100, 30); + QLabel* nameLab = new QLabel("Palette's name: ", this); + nameLab->setGeometry(10, height() - 50, 100, 30); + QLineEdit* name = new QLineEdit(this); + name->setGeometry(120, height() - 50, width() - 120 - 140, 30); +} + + +ColorPicker::~ColorPicker() +{ +} + + +void ColorPicker::setMode(int a_mode) +{ + palette.m_mode = a_mode; + update(); +} + + +void ColorPicker::mousePressEvent(QMouseEvent* me) +{ + for (int h = palette.hueCount() - 1; h >= 0; h--) + { + int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); + QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); + if (r.contains(me->pos())) + { + m_hueMovingIdx = h; + return; + } + } +} + + +void ColorPicker::mouseReleaseEvent(QMouseEvent*) +{ + m_hueMovingIdx = -1; +} + + +void ColorPicker::mouseMoveEvent(QMouseEvent* me) +{ + if (m_hueMovingIdx != -1) + { + int x = me->pos().x() - (cen + xOff); + int y = me->pos().y() - (cen + yOff); + palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); + update(); + } +} + + +void ColorPicker::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.fillRect(0, 0, width(), height(), QColor(bgColor1)); + + QFont f("Segoe UI", 13, 200); + p.setFont(f); + p.setPen(Qt::white); + //p.drawText(3 + schemeWidth, 15, "Color Wheel"); + p.drawText(3 + schemeWidth + wheelWidth, 15 + titleHeight, "Palette Preview"); + + + if (!m_wheelCached) + makeColorWheel(siz, bgColor2); + p.drawImage(xOff, yOff, colorWheelCache); + + f.setPixelSize(32); + p.setFont(f); + p.drawText(3 + schemeWidth, 25, QString(palette.modeName()) + " Color Scheme"); + + for (int h = 0; h < palette.hueCount(); h++) + { + int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); + int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); + if (h == 0) { + p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); + p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); + } + else { + p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); + p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); + } + p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); + } + + QImage img(siz, siz, QImage::Format_ARGB32); + img.fill(0xFF000000 | bgColor2); + uint32_t* bits = (uint32_t*)img.bits(); + int pitch = img.bytesPerLine() / sizeof(uint32_t); + float hue = palette.hue(0); // Get the primary color + for (int j = 0; j < (siz - 120); j++) + { + for (int i = 0; i < (siz - 40); i++) + { + bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); + } + } + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 10; i++) + { + float t = i / float(9); + for (int x = 0; x < 16; x++) + { + // shades, tones and tints (black,grey,white) + bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades + bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones + bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints + } + } + } + p.drawImage(schemeWidth + siz, titleHeight, img); + + + for (int h = 0; h < palette.hueCount(); h++) + { + QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); + p.setPen(QPen(Qt::white)); + p.setBrush(c); + p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 16 + 20 + 60 * h, 50, 50); + + QFont f("Segoe UI", 12, 175); + p.setFont(f); + QString x = QVariant(c.toRgb()).toString(); + p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 16 + 20 + 60 * h + 16, x); + } + + int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; + for (int i = 0; i < 4; i++) + { + int idx = colIdxForMode[palette.m_mode][i]; + float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; + float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; + float baseTone = 0.15; + float hue = palette.hue(idx); + QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); + + int unitSiz = (((siz*2) - 30) / 16) * 4; + int unitW = unitSiz - 4; + int x1 = schemeWidth + 20 + unitSiz * i; + int x2 = x1 + unitW; + + while (idx == colIdxForMode[palette.m_mode][i + 1]) { + x2 += unitSiz; + i++; + } + + int w = x2 - x1; + p.fillRect(x1, titleHeight + siz + 10, w, 50, c); + for (int s = 0; s < 4; s++) + p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); + + } +} diff --git a/ColorPicker.h b/ColorPicker.h index c45c75a..4203c40 100644 --- a/ColorPicker.h +++ b/ColorPicker.h @@ -1,393 +1,59 @@ -#include +#ifndef COLOR_PICKER_H +#define COLOR_PICKER_H -// Values from this come up the same as doing: QColor::fromHsvF(hue, sat, 1.0).rgba() -uint32_t rgbFromHsvF(qreal h1, qreal s, qreal v, qreal a) -{ - uint16_t hue = h1 == qreal(-1.0) ? USHRT_MAX : qRound(h1 * 36000); - h1 = hue == 36000 ? 0 : hue / qreal(6000.); - s = uint16_t(qRound(s * USHRT_MAX)) / qreal(USHRT_MAX); - v = uint16_t(qRound(v * USHRT_MAX)) / qreal(USHRT_MAX); - const qreal h = h1; - const int i = int(h); - const qreal f = h - i; - const qreal v2 = v * USHRT_MAX; - uint8_t components[4] = { - qRound(v2 * (qreal(1.0) - s)) >> 8, - qRound(v2) >> 8, - qRound(v2 * (qreal(1.0) - (s * f))) >> 8, - qRound(v2 * (qreal(1.0) - (s * (qreal(1.0) - f)))) >> 8 - }; - const int indexTable[10] = { 1, 2, 0, 0, 3, 1, 1, 2, 0, 0 }; - return 0xff000000 | (components[indexTable[i]] << 16) | (components[indexTable[i + 4]] << 8) | components[indexTable[i + 2]]; -} - -bool m_wheelCached = false; -QImage colorWheelCache; - -void makeColorWheel(int siz) -{ - const float twoPi = 2 * 3.142; - const int cen = (siz / 2); - const int rad = cen - 20; - const int iter = siz * 10; - - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - double dSat = 1.0 / rad; - for (int i = 0; i < iter; i++) - { - double hue = double(i) / iter; - double c = ::cos(hue * twoPi); - double s = ::sin(hue * twoPi); - double x = cen + rad * c; - double y = cen + rad * s; - double sat = 1.0; - for (int w = rad-30; w < rad; w++) - { - bits[int(y)*pitch + int(x)] = rgbFromHsvF(hue, sat, 1.0, 1.0); - x -= c; - y -= s; - //sat -= dSat; - } - } - // Smoothing technique where we scale it up a fraction and then back down again - img = img.scaled(img.size() + QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - colorWheelCache = img.scaled(img.size() - QSize(6, 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_wheelCached = true; -} - - -const int modeHands[8] = { 1, 2, 3, 4, 4, 4, 4, 4 }; -const int modeHandAngles[6][4] = { { 0 }, { 0, 180 }, { 0, 180 - 30, 180 + 30 }, - { 0, 360 - 30, 30, 180 }, /* { 0, 180 - 30, 180, 180 + 30 }, */ { 360 - 45, 45, 180 - 45, 180 + 45 } }; -const char* modeNames[6] = { - "Monochromatic", "Complementary", "Analogous", "Accented Analogous", "Tetradic" -}; +#include +class QPushButton; struct ColorScheme { - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - unsigned m_primaryHue : 8; - unsigned m_secondaryHueDelta : 8; - unsigned m_saturation : 8; - unsigned m_stretch : 5; - unsigned m_mode : 3; + static const int modes = 5; - const char* modeName() - { - if (m_mode == 2 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255 - 64)) - return "Triadic"; - if (m_mode == 3 && m_secondaryHueDelta > 64 && m_secondaryHueDelta < (255-64)) - return "Split-Complementary"; - return modeNames[m_mode]; - } - int hueCount() - { - return modeHands[m_mode]; - } - void setHue(int a_index, float a_val) - { - if (a_index == 0) - m_primaryHue = unsigned(a_val * 255.0) & 0xff; - else if (a_index == 1 && m_mode != 2) - m_primaryHue = unsigned((a_val - 0.5) * 255.0) & 0xff; - else if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - m_secondaryHueDelta = (unsigned(a_val * 255.0) - m_primaryHue) & 0xff; - else if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - m_secondaryHueDelta = (m_primaryHue - unsigned(a_val * 255.0)) & 0xff; - else - m_secondaryHueDelta = (unsigned((a_val - 0.5) * 255.0) - m_primaryHue) & 0xff; - } - float denormalizedHue(int a_index) - { - if (a_index == 0) - return float(m_primaryHue) / 255.0; - if (a_index == 1 && m_mode != 2) - return 0.5 + float(m_primaryHue) / 255.0; - if ((a_index == 2 && m_mode != 2) || (m_mode == 2 && a_index == 1)) - return float((int)m_primaryHue + m_secondaryHueDelta) / 255.0; - if (m_mode == 3 || (m_mode == 2 && a_index == 2)) - return float((int)m_primaryHue - (int)m_secondaryHueDelta) / 255.0; - return 0.5 + float((int)m_primaryHue + (int)m_secondaryHueDelta) / 255.0; - } - float hue(int a_index) - { - float h = denormalizedHue(a_index); - while (h < 0.0) h += 1.0; - while (h > 1.0) h -= 1.0; - return h; - } + // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) + // saturation (8-bits), stretch (5-bits) + unsigned m_primaryHue : 8; + unsigned m_secondaryHueDelta : 8; + unsigned m_saturation : 8; + unsigned m_stretch : 5; + unsigned m_mode : 3; + + const char* modeName(); + int hueCount(); + + void setHue(int a_index, float a_val); + float hue(int a_index); }; - - -static_assert(sizeof(ColorScheme) == 4, "bad size"); +static_assert(sizeof(ColorScheme) == 4, "Not the size we expected"); class SchemeSelection : public QWidget { public: - const int titleHeight = 20; - const int modes = 5; - - ColorScheme* scheme; - SchemeSelection(QWidget* a_parent, ColorScheme* a_scheme) : QWidget(a_parent), scheme(a_scheme) {} - - void mousePressEvent(QMouseEvent* me) - { - for (int i = 0; i < modes; i++) - { - QRect r(16, titleHeight + 10 + i * 55, 50, 50); - if (r.contains(me->pos())) - { - scheme->m_mode = i; - parentWidget()->update(); - return; - } - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - p.drawText(3 + 0, 15, "Scheme"); - - QPixmap pix1("../color-dial-deselect.png"); - QPixmap pix2("../color-dial-select.png"); - QPixmap pixHand1("../color-pointer-deselect.png"); - QPixmap pixHand2("../color-pointer-select.png"); - p.fillRect(0, titleHeight, width(), height() - titleHeight, QColor(0x333333)); - for (int i = 0; i < modes; i++) - { - p.drawPixmap(16, titleHeight + 10 + i * 55, (i == scheme->m_mode) ? pix2 : pix1); - QPixmap hands = (i == scheme->m_mode) ? pixHand2 : pixHand1; - - for (int j = 0; j < modeHands[i]; j++) - { - QMatrix m; - m = m.scale(0.5, 0.5); - m = m.rotate(modeHandAngles[i][j]); - QPixmap newPix = hands.transformed(m, Qt::SmoothTransformation); - QSize s = newPix.size() - hands.size() / 2; - s = s / 2; - p.drawPixmap(16 - s.width(), titleHeight + 10 - s.height() + i * 55, newPix); - //pix.transformed(m, Qt::SmoothTransformation); - } - } - } + SchemeSelection(QWidget* a_parent); +private: + QPushButton* modeButtons[ColorScheme::modes]; }; - -//#include "c:\Users\John\Desktop\Euclideon\model-iterator.cpp" - - - -// lineedit to name the scheme -// okay/cancel buttons -// styled titlebar - -class ColorPicker : public QWidget +class ColorPicker : public QDialog { + Q_OBJECT public: - const float twoPi = 2 * acos(-1); - const int siz = 250; - const int cen = (siz / 2); - const int rad = (siz / 2) - 20; - const int iter = siz * 10; - const int dotSiz = 18;// 16; - const int dotAlpha = 96;// 64; - - const int titleHeight = 20; - - const int schemeWidth = 80; - const int wheelWidth = siz * 2;// 600; - - const int paletteWidth = 150; - const int paletteHeight = 350; - - const int xOff = schemeWidth; - const int yOff = titleHeight; - //const int modes = 6; - - int m_hueMovingIdx; - - - ColorScheme palette; - SchemeSelection scheme; - - - // Properties of a scheme: - // mode (3-bits), primary hue (8-bits), angle / hue-delta (8-bits) - // saturation (8-bits), stretch (5-bits) - - ColorPicker(QWidget* parent = 0) : QWidget(parent), scheme(this, &palette) - { - palette.m_mode = 0; - palette.m_primaryHue = 0; - palette.m_secondaryHueDelta = 10; - palette.m_saturation = 0; - palette.m_stretch = 0; - scheme.setGeometry(0, 0, schemeWidth, paletteHeight + titleHeight); - - setFixedSize(schemeWidth + wheelWidth + paletteWidth, paletteHeight + titleHeight); - m_hueMovingIdx = -1; - } - ~ColorPicker() - { - } - - void mousePressEvent(QMouseEvent* me) - { - for (int h = palette.hueCount() - 1; h >= 0; h--) - { - int x = xOff + cen + (rad - dotSiz - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad - dotSiz - 5) * ::sin(palette.hue(h) * twoPi); - QRect r(x - dotSiz / 2, y - dotSiz / 2, x + dotSiz / 2, y + dotSiz / 2); - if (r.contains(me->pos())) - { - m_hueMovingIdx = h; - return; - } - } - } - - void mouseReleaseEvent(QMouseEvent*) - { - m_hueMovingIdx = -1; - } - - void mouseMoveEvent(QMouseEvent* me) - { - if (m_hueMovingIdx != -1) - { - int x = me->pos().x() - (cen + xOff); - int y = me->pos().y() - (cen + yOff); - palette.setHue(m_hueMovingIdx, ::atan2(y, x) / twoPi); -update(); - } - } - - void paintEvent(QPaintEvent*) - { - QPainter p(this); - - p.fillRect(0, 0, width(), titleHeight, QColor(0x444444)); - p.fillRect(schemeWidth, 0, wheelWidth, titleHeight, QColor(0x333333)); - - QFont f("Segoe UI", 13, 200); - p.setFont(f); - p.setPen(Qt::white); - //p.drawText(3 + schemeWidth, 15, "Color Wheel"); - p.drawText(3 + schemeWidth + wheelWidth, 15, "Palette Preview"); - - - if (!m_wheelCached) - makeColorWheel(siz); - p.drawImage(xOff, yOff, colorWheelCache); - //p.drawText(3 + schemeWidth, titleHeight + 15, QString(modeNames[m_mode]) + " Colors"); - p.drawText(3 + schemeWidth, 15, QString(palette.modeName()) + " Colors"); - - for (int h = 0; h < palette.hueCount(); h++) - { - int x = xOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::cos(palette.hue(h) * twoPi); - int y = yOff + cen + (rad /*- dotSiz */ - 15 - 5) * ::sin(palette.hue(h) * twoPi); - if (h == 0) { - p.setPen(QPen(QColor(255, 255, 255, dotAlpha + 20), 2)); - p.setBrush(QColor(192, 192, 192, dotAlpha + 20)); - } - else { - p.setPen(QPen(QColor(0, 0, 0, dotAlpha - 10), 2)); - p.setBrush(QColor(0, 0, 0, dotAlpha - 10)); - } - p.drawEllipse(x - dotSiz / 2, y - dotSiz / 2, dotSiz, dotSiz); - } - - - //p.fillRect(schemeWidth + siz, titleHeight, siz, paletteHeight, QColor(0x533333)); - QImage img(siz, siz, QImage::Format_ARGB32); - img.fill(0xFF444444); - uint32_t* bits = (uint32_t*)img.bits(); - int pitch = img.bytesPerLine() / sizeof(uint32_t); - float hue = palette.hue(0); // Get the primary color - for (int j = 0; j < (siz - 120); j++) - { - for (int i = 0; i < (siz - 40); i++) - { - bits[int(j + 20)*pitch + int(i + 20)] = rgbFromHsvF(hue, j / float(siz - 120), i / float(siz - 40), 1.0); - } - } - - for (int j = 0; j < 16; j++) - { - for (int i = 0; i < 10; i++) - { - float t = i / float(9); - for (int x = 0; x < 16; x++) - { - // shades, tones and tints (black,grey,white) - bits[int(j + siz - 80 + 10)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0, 1.0 - t, 1.0); // shades - bits[int(j + siz - 80 + 30)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0 - (t*0.6), 1.0); // tones - bits[int(j + siz - 80 + 50)*pitch + int(i * 20 + x + 25)] = rgbFromHsvF(hue, 1.0 - t, 1.0, 1.0); // tints - } - } - } - p.drawImage(schemeWidth + siz, titleHeight, img); - - - p.fillRect(schemeWidth + wheelWidth, titleHeight, paletteWidth, paletteHeight, QColor(0x333333)); - for (int h = 0; h < palette.hueCount(); h++) - { - QColor c = QColor::fromHsvF(palette.hue(h), 1.0, 1.0); - p.setPen(QPen(Qt::white)); - p.setBrush(c); - // p.drawRect(schemeWidth + siz + 20 + 60 * (h % 2), titleHeight + 20 + 60 * (h / 2), 50, 50); - p.drawRect(schemeWidth + wheelWidth + 20, titleHeight + 20 + 60 * h, 50, 50); - - QFont f("Segoe UI", 12, 175); - p.setFont(f); - QString x = QVariant(c.toRgb()).toString(); - //QString y = QString("%1,%2,%3,%4").arg(c.cyan()).arg(c.magenta()).arg(c.yellow()).arg(c.black()); - p.drawText(schemeWidth + wheelWidth + 20 + 60, titleHeight + 20 + 60 * h + 16, x);// "#FF123456"); - //p.drawText(schemeWidth + siz + 20 + 60, titleHeight + 20 + 60 * h + 16 + 25, y);// "10,20,40,10"); - } - - int colIdxForMode[5][5] = { { 0, 0, 0, 0, -1 }, { 0, 0, 0, 1, -1 }, { 0, 0, 1, 2, -1 }, { 0, 2, 3, 1, -1 }, { 0, 2, 3, 1, -1 } }; - for (int i = 0; i < 4; i++) - { - int idx = colIdxForMode[palette.m_mode][i]; - float sats[4] = { 1.0 - 0.65, 1.0 - 0.35, 1.0, 1.0 }; - float vals[4] = { 1.0, 1.0, 1.0 - 0.35, 1.0 - 0.65 }; - float baseTone = 0.15; - float hue = palette.hue(idx); - QColor c = QColor::fromHsvF(hue, 1.0 - baseTone, 1.0 - (baseTone*0.6)); - - int unitSiz = (((siz*2) - 30) / 16) * 4; - int unitW = unitSiz - 4; - int x1 = schemeWidth + 20 + unitSiz * i; - int x2 = x1 + unitW; - - while (idx == colIdxForMode[palette.m_mode][i + 1]) { - x2 += unitSiz; - i++; - } - - int w = x2 - x1; - p.fillRect(x1, titleHeight + siz + 10, w, 50, c); - for (int s = 0; s < 4; s++) - p.fillRect(x1 + (w / 4) * s, titleHeight + siz + 10 + 50, w / 4, 25, rgbFromHsvF(hue, sats[s], vals[s], 1.0)); - - } - - } + ColorPicker(QWidget* parent = 0); + ~ColorPicker(); + void mousePressEvent(QMouseEvent* me); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent* me); + void paintEvent(QPaintEvent*); +public slots: + void setMode(int); +private: + int m_hueMovingIdx; + ColorScheme palette; + SchemeSelection scheme; + const float twoPi = 2 * acos(-1); }; + +#endif // COLOR_PICKER_H diff --git a/DocSVG.cpp b/DocSVG.cpp index 76c80cd..f2e28ed 100644 --- a/DocSVG.cpp +++ b/DocSVG.cpp @@ -1,4 +1,5 @@ #include "DocSVG.h" +#include "Util.h" const char SVGOperation::PathOperationChar[] = @@ -15,7 +16,7 @@ }; -DocSVG::DocSVG() +DocSVG::DocSVG(double a_scale) : m_scale(a_scale) { } @@ -67,21 +68,70 @@ } +DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str) +{ + SVGStyle style = { 0, 0, 0.0 }; + std::vector list = split(a_str, ';'); + for (int i = 0; i < list.size(); ++i) + { + std::vector kvp = split(list[i], ':'); + std::string s = kvp[0]; + if (s == "fill") style.fillColor = parseColor(kvp[1].c_str()); + else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str()); + else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]); + } + return style; +} + + +uint32_t DocSVG::parseColor(const char* a_str) +{ + std::string s = a_str; + if (s == "red") return 0xffff0000; + else if (s == "blue") return 0xff0000ff; + else if (s == "green") return 0xff00ff00; + else if (s == "yellow") return 0xffffff00; + else if (s == "purple") return 0xffe00fe0; + else if (s == "cyan") return 0xff00ffff; + else if (s == "magenta") return 0xffff00ff; + else if (s == "black") return 0xff000000; + else if (s == "white") return 0xffffffff; + else if (s == "none") return 0; + return str2col(s); +} + + /// Visit an element. bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { + +// Inkscape: +// style="fill:#729fcf;stroke:none" +// Spec: +// fill="yellow" stroke="blue" stroke-width="5" + + SVGPath path; + if (strcmp(element.ValueTStr().c_str(), "path") == 0) { const TiXmlAttribute* attrib = firstAttribute; while ( attrib ) { if (strcmp(attrib->Name(), "d") == 0) { - ParsePath(attrib->Value()); - break; - } else { - attrib = attrib->Next(); + path.operations = ParsePath(attrib->Value()); + } else if (strcmp(attrib->Name(), "style") == 0) { + path.style = parseStyle(attrib->Value()); + } else if (strcmp(attrib->Name(), "fill") == 0) { + path.style.fillColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke") == 0) { + path.style.strokeColor = parseColor(attrib->Value()); + } else if (strcmp(attrib->Name(), "stroke-width") == 0) { + path.style.strokeWidth = str2float(attrib->Value()); } + attrib = attrib->Next(); } } + paths.push_back(path); + return true; } diff --git a/DocSVG.h b/DocSVG.h index 08494bf..d8e20e2 100644 --- a/DocSVG.h +++ b/DocSVG.h @@ -64,10 +64,16 @@ fprintf(stderr, "\n"); } - void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp) { + void scale(double a_scale) { + for (int i = 0; i < PathOperationArgs[m_type]; i++) + m_values[i] *= a_scale; + } + + void output(HPDF_Page page, float curPos[2], bool fill, PathOperation prevOp, float pathStartPos[2]) { float pageHeight = HPDF_Page_GetHeight(page); float x = m_values[0]; float y = m_values[1]; + fprintf(stderr, "Output: OP: -%c- ARGS: %f %f \n", PathOperationChar[m_type], x, y); switch (m_type) { case MoveToAbs: curPos[0] = x; curPos[1] = y; @@ -121,11 +127,16 @@ // HPDF_REAL ray, HPDF_REAL ang1, HPDF_REAL ang2); break; case MoveToRel: - curPos[0] += x; curPos[1] += y; + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed + curPos[0] = x; curPos[1] = y; + } else { + curPos[0] += x; curPos[1] += y; + } + //curPos[0] += x; curPos[1] += y; HPDF_Page_MoveTo(page, curPos[0], pageHeight - curPos[1]); break; case LineToRel: - if (prevOp == BeginPathOperation) { + if (prevOp == BeginPathOperation) { // TODO: Perhaps should check this is really needed curPos[0] = x; curPos[1] = y; } else { curPos[0] += x; curPos[1] += y; @@ -168,8 +179,9 @@ break; case ClosePath: case AltClosePath: - default: // TODO: actually this means to draw a line back to the first point in the path + curPos[0] = pathStartPos[0]; curPos[1] = pathStartPos[1]; + //HPDF_Page_LineTo(page, curPos[0], pageHeight - curPos[1]); /* if (fill) HPDF_Page_FillStroke(page); @@ -177,6 +189,8 @@ HPDF_Page_Stroke(page); */ break; + default: + break; } } @@ -188,7 +202,7 @@ class DocSVG : public TiXmlVisitor { public: - DocSVG(); + DocSVG(double scale); virtual ~DocSVG(); /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& doc ); @@ -212,8 +226,9 @@ void DumpOperations() { - for (int i = 0; i < operations.size(); i++) - operations[i].dump(); + for (int p = 0; p < paths.size(); ++p) + for (int i = 0; i < paths[p].operations.size(); i++) + paths[p].operations[i].dump(); } void WriteTo(DocOutputDevice* outputDoc) @@ -221,36 +236,88 @@ DocOutputPage* outputPage = outputDoc->newPage(); HPDF_Page page = outputPage->page(); float curPos[2] = { 10.0, 10.0 }; - - //fprintf(stderr, "doc wxh: %f x %f\n", HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)); - - HPDF_Page_EndText(page); - - HPDF_Page_SetLineWidth(page, 2.0); - HPDF_Page_SetRGBStroke(page, 0.85f, 0.5f, 0.5f); // TODO: set this via API - //HPDF_Page_SetRGBFill(page, 0.95f, 0.95f, 0.95f); - HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); /* HPDF_Page_Rectangle(page, x1, pageHeight - y1 - h, w, h); HPDF_Page_Circle(page, x, pageHeight - y, radius); */ + HPDF_Page_EndText(page); - SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; - for (int i = 0; i < operations.size(); i++) { - operations[i].output(page, curPos, false, prevOp); - prevOp = operations[i].m_type; + for (int p = 0; p < paths.size(); ++p) + { + uint32_t fCol = paths[p].style.fillColor; + uint32_t sCol = paths[p].style.strokeColor; + std::vector operations = paths[p].operations; + + HPDF_Page_SetLineWidth(page, paths[p].style.strokeWidth);// * m_scale); + + //if ( fCol ) + HPDF_Page_SetRGBFill(page, ((fCol >> 16) & 0xff) / 255.0, + ((fCol >> 8) & 0xff) / 255.0, ((fCol >> 0) & 0xff) / 255.0); + //if ( sCol ) + HPDF_Page_SetRGBStroke(page, ((sCol >> 16) & 0xff) / 255.0, + ((sCol >> 8) & 0xff) / 255.0, ((sCol >> 0) & 0xff) / 255.0); + + HPDF_Page_MoveTo(page, 0.0, HPDF_Page_GetHeight(page)); + + float pathStartPos[2]; + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + SVGOperation::PathOperation prevOp = SVGOperation::BadOperation; + for (int i = 0; i < operations.size(); i++) { + SVGOperation op = operations[i]; + op.scale(m_scale); + op.output(page, curPos, false, prevOp, pathStartPos); + if (prevOp == SVGOperation::BeginPathOperation) { + pathStartPos[0] = curPos[0]; + pathStartPos[1] = curPos[1]; + } + prevOp = op.m_type; + } + + if (fCol) + HPDF_Page_ClosePathFillStroke(page); + else + HPDF_Page_Stroke(page); +/* +HPDF_Page_Stroke (HPDF_Page page) +HPDF_Page_ClosePathStroke (HPDF_Page page) + +HPDF_Page_Fill (HPDF_Page page) +HPDF_Page_Eofill (HPDF_Page page) // Even-Odd Fill + +HPDF_Page_FillStroke (HPDF_Page page) +HPDF_Page_EofillStroke (HPDF_Page page) + +HPDF_Page_ClosePathFillStroke (HPDF_Page page) +HPDF_Page_ClosePathEofillStroke (HPDF_Page page) + +HPDF_Page_EndPath (HPDF_Page page) +*/ + } - HPDF_Page_Stroke(page); // Need to close HPDF_Page_BeginText(page); delete outputPage; } private: - void ParsePath(const char* a_pathData) + struct SVGStyle { + uint32_t fillColor; + uint32_t strokeColor; + float strokeWidth; + }; + struct SVGPath { + std::vector operations; + SVGStyle style; + }; + + SVGStyle parseStyle(const char* a_str); + uint32_t parseColor(const char* a_str); + std::vector ParsePath(const char* a_pathData) { + std::vector operations; SVGOperation currentOp; currentOp.m_type = SVGOperation::BeginPathOperation; operations.push_back(currentOp); @@ -266,6 +333,12 @@ val++; if (val == currentOp.getArgCount()) { operations.push_back(currentOp); + // The first position in a move to op is where to go to, but after that it + // is where to draw a line to + if ( currentOp.m_type == SVGOperation::MoveToAbs ) + currentOp.m_type = SVGOperation::LineToAbs; + if ( currentOp.m_type == SVGOperation::MoveToRel ) + currentOp.m_type = SVGOperation::LineToRel; val = 0; } pathPtr = end; @@ -290,9 +363,11 @@ } currentOp.m_type = SVGOperation::EndPathOperation; operations.push_back(currentOp); + return operations; } - std::vector operations; + std::vector paths; + double m_scale; }; diff --git a/DocTemplate.cpp b/DocTemplate.cpp index 1b8a750..7defbcc 100644 --- a/DocTemplate.cpp +++ b/DocTemplate.cpp @@ -1,9 +1,7 @@ #include #include -#include -#include -#include #include "DocTemplate.h" +#include "Util.h" std::string DocTemplate::m_currentFile = ""; @@ -142,42 +140,6 @@ } -std::vector split(const std::string &s, char delim) -{ - std::vector elems; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - - -std::string trim(std::string str) -{ - str.erase(0, str.find_first_not_of(" \t\r\n")); - str.erase(str.find_last_not_of(" \t\r\n") + 1); - return str; -} - - -std::string str2lower(const std::string& str) -{ - std::string ret = str; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - - -unsigned int str2col(std::string str) -{ - if (str[0] == '#') - str = &str[1]; - return std::stoul(str, nullptr, 16); -} - - std::vector str2pointList(const std::string& str) { std::vector ret; @@ -206,12 +168,6 @@ } -float str2float(const std::string& str) -{ - return (float)atof(str.c_str()); -} - - PenStyle str2style(const std::string& str) { Choose(str) @@ -282,15 +238,6 @@ } -#ifndef _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) -{ - *a_file = fopen(a_fileName, a_mode); - return (*a_file != nullptr); -} -#endif - - void DocTemplate::ReadTemplateFile(const char* a_fileName) { m_items.clear(); @@ -459,7 +406,7 @@ page.drawText(x, y, m_items[i].m_text.c_str()); } } - page.drawImage("test/logo.png", 10, 10, 100, 100); + page.drawImage("test/letterhead.png", 0, 0, page.width(), page.height()); } diff --git a/DocVisitor.cpp b/DocVisitor.cpp index dcf88ff..5691cbb 100644 --- a/DocVisitor.cpp +++ b/DocVisitor.cpp @@ -5,7 +5,7 @@ const float lineSpacing = 1.5f; const float pageLeftMargin = 60.0f; const float pageRightMargin = 60.0f; -const float pageTopMargin = 60.0f; +const float pageTopMargin = 120.0f; const float pageBottomMargin = 60.0f; diff --git a/Framework/GenericTableUI.h b/Framework/GenericTableUI.h index 34bcb07..c4cd323 100644 --- a/Framework/GenericTableUI.h +++ b/Framework/GenericTableUI.h @@ -160,7 +160,7 @@ m_view->scrollToBottom(); } - static void __cdecl updateCallback(void *a_data) + static void updateCallback(void *a_data) { GenericTableUI* obj = (GenericTableUI*)a_data; obj->reload(); diff --git a/Framework/Utilities.cpp b/Framework/Utilities.cpp index 65b023b..8dbccf9 100644 --- a/Framework/Utilities.cpp +++ b/Framework/Utilities.cpp @@ -49,8 +49,8 @@ { if (BT_IsDebug) { - yqConsolePrintString(a_location); - yqConsolePrintString(a_message); + yqConsolePrintString(a_location); + yqConsolePrintString(a_message); yqConsolePrintString("\n"); } } @@ -82,44 +82,48 @@ } -void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...) +void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_messageLogger) - g_currentModule.m_messageLogger->logMessage(a_logLevel, a_location, message); + g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message); free(message); #endif va_end(args); } -void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...) +void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...) { va_list args; va_start(args, a_format); + std::string loc = a_location; + loc += a_func; #ifdef _WIN32 size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1); char* message = (char*)_alloca(sizeRequired); _vsnprintf(message, sizeRequired, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); _freea(message); #else char* message = 0; vasprintf(&message, a_format, args); if (g_currentModule.m_errorHandler) - g_currentModule.m_errorHandler->handleError(a_result, a_location, message); + g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message); free(message); #endif va_end(args); diff --git a/Framework/Utilities.h b/Framework/Utilities.h index 7572b21..cff9fcc 100644 --- a/Framework/Utilities.h +++ b/Framework/Utilities.h @@ -35,13 +35,13 @@ #ifdef __MINGW32__ // Avoid callbacks # define YQ_CALLBACK __cdecl -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #elif defined(_WIN32) # define YQ_CALLBACK __cdecl # define YQ_FUNCTION_NAME __FUNCTION__ #else # define YQ_CALLBACK -# define YQ_FUNCTION_NAME YQ_STRINGIZE(__func__) +# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__ #endif // Helper marcos for doing a variable number of null checks @@ -55,20 +55,20 @@ #define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) // File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off -#define YQ_LOCATION(a_msg) __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " a_msg YQ_FUNCTION_NAME "(): " +#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): " // Macros to log messages for debug builds. // For release builds these will not output anything and no code or data will be generate (except for error logging) -#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION("debug: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION("info: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION("warning: "), a_fmt, ##__VA_ARGS__)) -#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION("error: "), a_fmt, ##__VA_ARGS__) +#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__)) +#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__) // Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned. // In debug and release, on errors, the code always calls the installed or default error handling routine enum { YQ_VERBOSE_DEBUGGING = 0 }; #define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0)) -#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION(""), __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) +#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result) #define YQ_API_ENTER() YQ_API_VERBOSE("Enter") #define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__)) @@ -136,8 +136,8 @@ // Redirects to the installed logger/error/memory handler or a platform default -extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_format, ...); -extern void yqHandleError(yqResult a_result, const char *a_location, const char *a_format, ...); +extern void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...); +extern void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...); extern void* yqAllocateMemory(size_t a_size); extern void* yqReallocateMemory(void* a_ptr, size_t a_newSize); extern void yqFreeMemory(void* a_ptr); diff --git a/MdiWindow.cpp b/MdiWindow.cpp new file mode 100644 index 0000000..2713c0d --- /dev/null +++ b/MdiWindow.cpp @@ -0,0 +1,697 @@ +#include +#include "Utilities.h" +#include "MdiWindow.h" +#include "ui_ExtensibleObjectModelUI.h" + + +MdiChild::MdiChild() +{ + setAttribute(Qt::WA_DeleteOnClose); + isUntitled = true; + connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged())); +} + +MdiChild::~MdiChild() +{ + closing(); +} + +void MdiChild::newFile() +{ + static int sequenceNumber = 1; + isUntitled = true; + curFile = tr("document%1.txt").arg(sequenceNumber++); + setWindowTitle(curFile + "[*]"); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); +} + +bool MdiChild::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream in(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + setPlainText(in.readAll()); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); + return true; +} + +bool MdiChild::save() +{ + return (isUntitled) ? saveAs() : saveFile(curFile); +} + +bool MdiChild::saveAs() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); + if (fileName.isEmpty()) + return false; + return saveFile(fileName); +} + +bool MdiChild::saveFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("MDI"), + tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString())); + return false; + } + QTextStream out(&file); + QApplication::setOverrideCursor(Qt::WaitCursor); + out << toPlainText(); + QApplication::restoreOverrideCursor(); + + setCurrentFile(fileName); + return true; +} + +QString MdiChild::userFriendlyCurrentFile() +{ + return strippedName(curFile); +} + +void MdiChild::closeEvent(QCloseEvent *event) +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} + +void MdiChild::documentWasModified() +{ + setWindowModified(document()->isModified()); +} + +bool MdiChild::maybeSave() +{ + if (document()->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("MDI"), + tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(userFriendlyCurrentFile()), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) + return save(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void MdiChild::setCurrentFile(const QString &fileName) +{ + curFile = QFileInfo(fileName).canonicalFilePath(); + isUntitled = false; + document()->setModified(false); + setWindowModified(false); + setWindowTitle(userFriendlyCurrentFile() + "[*]"); +} + +QString MdiChild::strippedName(const QString &fullFileName) +{ + return QFileInfo(fullFileName).fileName(); +} + + + +void MdiChild::markdownSelection(const char* preText, const char* postText) +{ + QTextCursor cur = textCursor(); + cur.beginEditBlock(); + QString selectedText = cur.selection().toPlainText(); + cur.removeSelectedText(); + cur.insertText(preText + selectedText + postText); + cur.endEditBlock(); +} + + + +void MdiChild::bold() +{ + markdownSelection("**", "**"); +} +void MdiChild::italic() +{ + markdownSelection("*", "*"); +} +void MdiChild::quote() +{ + markdownSelection("`", "`"); +} +void MdiChild::code() +{ + //markdownSelection("*", "*"); + // Need to add indentation of text with 4 spaces each line +} +void MdiChild::heading1() +{ + markdownSelection("# ", " #"); +} +void MdiChild::heading2() +{ + markdownSelection("## ", " ##"); +} +void MdiChild::heading3() +{ + markdownSelection("### ", " ###"); +} +void MdiChild::heading4() +{ + markdownSelection("#### ", " ####"); +} +void MdiChild::hyperlink() +{ +} +void MdiChild::image() +{ +} +void MdiChild::unorderedList() +{ +} +void MdiChild::orderedList() +{ +} +void MdiChild::horizontalRule() +{ +} +void MdiChild::timestamp() +{ +} +void MdiChild::pageNumber() +{ +} +void MdiChild::pageCount() +{ +} +void MdiChild::del() +{ + //QTextEdit::cursor().pos() +} +void MdiChild::undo() +{ +} +void MdiChild::redo() +{ +} +void MdiChild::find() +{ +} +void MdiChild::findNext() +{ +} +void MdiChild::findPrevious() +{ +} +void MdiChild::goToLine() +{ +} + + + + + + +MdiWindow::MdiWindow(QWidget *parent) : + QMainWindow(parent), appWindow(nullptr) +{ +} + + +void MdiWindow::init(Ui_MainWindow* ui) +{ + appWindow = ui; + mdiArea = ui->mdiArea;//new QMdiArea; + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions())); + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview())); + + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + + QObject::connect(appWindow->actionNew, SIGNAL(triggered(bool)), this, SLOT(newFile())); + QObject::connect(appWindow->actionOpen, SIGNAL(triggered(bool)), this, SLOT(open())); + QObject::connect(appWindow->actionSave, SIGNAL(triggered(bool)), this, SLOT(save())); + QObject::connect(appWindow->actionSave_As, SIGNAL(triggered(bool)), this, SLOT(saveAs())); + QObject::connect(appWindow->actionSwitchRTL, SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection())); +#ifndef QT_NO_CLIPBOARD + QObject::connect(appWindow->actionCut, SIGNAL(triggered(bool)), this, SLOT(cut())); + QObject::connect(appWindow->actionCopy, SIGNAL(triggered(bool)), this, SLOT(copy())); + QObject::connect(appWindow->actionPaste, SIGNAL(triggered(bool)), this, SLOT(paste())); +#endif + QObject::connect(appWindow->actionBold, SIGNAL(triggered(bool)), this, SLOT(bold())); + QObject::connect(appWindow->actionItalic, SIGNAL(triggered(bool)), this, SLOT(italic())); + QObject::connect(appWindow->actionQuote, SIGNAL(triggered(bool)), this, SLOT(quote())); + QObject::connect(appWindow->actionCode, SIGNAL(triggered(bool)), this, SLOT(code())); + QObject::connect(appWindow->actionHeading_1, SIGNAL(triggered(bool)), this, SLOT(heading1())); + QObject::connect(appWindow->actionHeading_2, SIGNAL(triggered(bool)), this, SLOT(heading2())); + QObject::connect(appWindow->actionHeading_3, SIGNAL(triggered(bool)), this, SLOT(heading3())); + QObject::connect(appWindow->actionHeading_4, SIGNAL(triggered(bool)), this, SLOT(heading4())); + QObject::connect(appWindow->actionHyperlink, SIGNAL(triggered(bool)), this, SLOT(hyperlink())); + QObject::connect(appWindow->actionImage, SIGNAL(triggered(bool)), this, SLOT(image())); + QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList())); + QObject::connect(appWindow->actionOrdered_List, SIGNAL(triggered(bool)), this, SLOT(orderedList())); + QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule())); + QObject::connect(appWindow->actionTimestamp, SIGNAL(triggered(bool)), this, SLOT(timestamp())); + QObject::connect(appWindow->actionPage_Number, SIGNAL(triggered(bool)), this, SLOT(pageNumber())); + QObject::connect(appWindow->actionPage_Count, SIGNAL(triggered(bool)), this, SLOT(pageCount())); + QObject::connect(appWindow->actionOptions, SIGNAL(triggered(bool)), this, SLOT(options())); + QObject::connect(appWindow->actionSave_All, SIGNAL(triggered(bool)), this, SLOT(saveAll())); + QObject::connect(appWindow->actionSelect_All, SIGNAL(triggered(bool)), this, SLOT(selectAll())); + QObject::connect(appWindow->actionDelete, SIGNAL(triggered(bool)), this, SLOT(del())); + QObject::connect(appWindow->actionUndo, SIGNAL(triggered(bool)), this, SLOT(undo())); + QObject::connect(appWindow->actionRedo, SIGNAL(triggered(bool)), this, SLOT(redo())); + QObject::connect(appWindow->actionFind, SIGNAL(triggered(bool)), this, SLOT(find())); + QObject::connect(appWindow->actionFind_Next, SIGNAL(triggered(bool)), this, SLOT(findNext())); + QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious())); + QObject::connect(appWindow->actionGo_to_Line, SIGNAL(triggered(bool)), this, SLOT(goToLine())); + QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows())); + QObject::connect(appWindow->action_Windowed, SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows())); + QObject::connect(appWindow->actionTabbed, SIGNAL(triggered(bool)), this, SLOT(tabSubWindows())); + QObject::connect(appWindow->actionClose, SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow())); + QObject::connect(appWindow->actionClose_All, SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows())); + QObject::connect(appWindow->actionNext, SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow())); + QObject::connect(appWindow->actionPrevious, SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow())); + + QObject::connect(appWindow->actionSupport, SIGNAL(triggered(bool)), this, SLOT(support())); + QObject::connect(appWindow->actionWebsite, SIGNAL(triggered(bool)), this, SLOT(website())); + + statusBar()->showMessage(tr("Ready")); + updateActions(); + readSettings(); + setUnifiedTitleAndToolBarOnMac(true); +#if QT_VERSION >= 0x050000 + menuBar()->setNativeMenuBar(false); +#endif + tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock); + tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock); + appWindow->projectDock->raise(); + appWindow->eventLogDock->hide(); + appWindow->consoleDock->hide(); +} + + +void MdiWindow::closeEvent(QCloseEvent *event) +{ + YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)"); + mdiArea->closeAllSubWindows(); + if (mdiArea->currentSubWindow()) { + YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent"); + event->ignore(); + } else { + writeSettings(); + YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent"); + event->accept(); + } +} + + +void MdiWindow::newFile() +{ + YQ_LOG_DEBUG("newFile"); + MdiChild *child = createMdiChild(); + child->newFile(); + child->show(); +} + + +void MdiWindow::open() +{ + YQ_LOG_DEBUG("open"); + QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) { + QMdiSubWindow *existing = findMdiChild(fileName); + if (existing) { + mdiArea->setActiveSubWindow(existing); + return; + } + + if (openFile(fileName)) + statusBar()->showMessage(tr("File loaded"), 2000); + } +} + + +bool MdiWindow::openFile(const QString &fileName) +{ + YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data()); + MdiChild *child = createMdiChild(); + const bool succeeded = child->loadFile(fileName); + if (succeeded) + child->show(); + else + child->close(); + return succeeded; +} + + +void MdiWindow::save() +{ + YQ_LOG_DEBUG("save"); + if (activeMdiChild() && activeMdiChild()->save()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +void MdiWindow::saveAs() +{ + YQ_LOG_DEBUG("saveAs"); + if (activeMdiChild() && activeMdiChild()->saveAs()) + statusBar()->showMessage(tr("File saved"), 2000); +} + + +#ifndef QT_NO_CLIPBOARD +void MdiWindow::cut() +{ + if (activeMdiChild()) + activeMdiChild()->cut(); +} + + +void MdiWindow::copy() +{ + if (activeMdiChild()) + activeMdiChild()->copy(); +} + + +void MdiWindow::paste() +{ + if (activeMdiChild()) + activeMdiChild()->paste(); +} +#endif + + +void MdiWindow::support() +{ + QDesktopServices::openUrl(QUrl("http://stackoverflow.org/")); +} + + +void MdiWindow::website() +{ + QDesktopServices::openUrl(QUrl("http://slashdot.org/")); +} + + +void MdiWindow::bold() +{ + if (activeMdiChild()) activeMdiChild()->bold(); +} +void MdiWindow::italic() +{ + if (activeMdiChild()) activeMdiChild()->italic(); +} +void MdiWindow::quote() +{ + if (activeMdiChild()) activeMdiChild()->quote(); +} +void MdiWindow::code() +{ + if (activeMdiChild()) activeMdiChild()->code(); +} +void MdiWindow::heading1() +{ + if (activeMdiChild()) activeMdiChild()->heading1(); +} +void MdiWindow::heading2() +{ + if (activeMdiChild()) activeMdiChild()->heading2(); +} +void MdiWindow::heading3() +{ + if (activeMdiChild()) activeMdiChild()->heading3(); +} +void MdiWindow::heading4() +{ + if (activeMdiChild()) activeMdiChild()->heading4(); +} +void MdiWindow::hyperlink() +{ + if (activeMdiChild()) activeMdiChild()->hyperlink(); +} +void MdiWindow::image() +{ + if (activeMdiChild()) activeMdiChild()->image(); +} +void MdiWindow::unorderedList() +{ + if (activeMdiChild()) activeMdiChild()->unorderedList(); +} +void MdiWindow::orderedList() +{ + if (activeMdiChild()) activeMdiChild()->orderedList(); +} +void MdiWindow::horizontalRule() +{ + if (activeMdiChild()) activeMdiChild()->horizontalRule(); +} +void MdiWindow::timestamp() +{ + if (activeMdiChild()) activeMdiChild()->timestamp(); +} +void MdiWindow::pageNumber() +{ + if (activeMdiChild()) activeMdiChild()->pageNumber(); +} +void MdiWindow::pageCount() +{ + if (activeMdiChild()) activeMdiChild()->pageCount(); +} +void MdiWindow::options() +{ +} +void MdiWindow::saveAll() +{ +} +void MdiWindow::selectAll() +{ + if (activeMdiChild()) activeMdiChild()->selectAll(); +} +void MdiWindow::del() +{ + if (activeMdiChild()) activeMdiChild()->del(); +} +void MdiWindow::undo() +{ + if (activeMdiChild()) activeMdiChild()->undo(); +} +void MdiWindow::redo() +{ + if (activeMdiChild()) activeMdiChild()->redo(); +} +void MdiWindow::find() +{ + if (activeMdiChild()) activeMdiChild()->find(); +} +void MdiWindow::findNext() +{ + if (activeMdiChild()) activeMdiChild()->findNext(); +} +void MdiWindow::findPrevious() +{ + if (activeMdiChild()) activeMdiChild()->findPrevious(); +} +void MdiWindow::goToLine() +{ + if (activeMdiChild()) activeMdiChild()->goToLine(); +} + + +#include "html.h" +#include "document.h" + +void MdiWindow::updatePreview() +{ + MdiChild* child = activeMdiChild(); + if (child) + { + QByteArray text = child->toPlainText().toLatin1(); + hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML); + hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0); + hoedown_buffer *ob = hoedown_buffer_new(1024); + hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64); + hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size()); + hoedown_document_free(document); + hoedown_html_renderer_free(renderer); + appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size))); + hoedown_buffer_free(ob); + } +} + + +void MdiWindow::updateActions() +{ + bool hasMdiChild = (activeMdiChild() != 0); + bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection()); + + appWindow->actionSave->setEnabled(hasMdiChild); + appWindow->actionSave_As->setEnabled(hasMdiChild); + appWindow->actionSave_All->setEnabled(hasMdiChild); + appWindow->actionClose->setEnabled(hasMdiChild); +#ifndef QT_NO_CLIPBOARD + appWindow->actionCut->setEnabled(hasSelection); + appWindow->actionCopy->setEnabled(hasSelection); + appWindow->actionPaste->setEnabled(hasMdiChild); +#endif + appWindow->actionSelect_All->setEnabled(hasMdiChild); + appWindow->actionDelete->setEnabled(hasMdiChild); + appWindow->actionUndo->setEnabled(hasMdiChild); + appWindow->actionRedo->setEnabled(hasMdiChild); + appWindow->actionFind->setEnabled(hasMdiChild); + appWindow->actionFind_Next->setEnabled(hasMdiChild); + appWindow->actionFind_Previous->setEnabled(hasMdiChild); + appWindow->actionGo_to_Line->setEnabled(hasMdiChild); + appWindow->actionBold->setEnabled(hasMdiChild); + appWindow->actionItalic->setEnabled(hasMdiChild); + appWindow->actionQuote->setEnabled(hasMdiChild); + appWindow->actionCode->setEnabled(hasMdiChild); + appWindow->actionHeading_1->setEnabled(hasMdiChild); + appWindow->actionHeading_2->setEnabled(hasMdiChild); + appWindow->actionHeading_3->setEnabled(hasMdiChild); + appWindow->actionHeading_4->setEnabled(hasMdiChild); + appWindow->actionHyperlink->setEnabled(hasMdiChild); + appWindow->actionImage->setEnabled(hasMdiChild); + appWindow->actionUnordered_List->setEnabled(hasMdiChild); + appWindow->actionOrdered_List->setEnabled(hasMdiChild); + appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild); + appWindow->actionTimestamp->setEnabled(hasMdiChild); + appWindow->actionPage_Number->setEnabled(hasMdiChild); + appWindow->actionPage_Count->setEnabled(hasMdiChild); + appWindow->actionClose_All->setEnabled(hasMdiChild); + appWindow->actionNext->setEnabled(hasMdiChild); + appWindow->actionPrevious->setEnabled(hasMdiChild); + appWindow->action_Tile_Windows->setEnabled(hasMdiChild); + appWindow->action_Windowed->setEnabled(hasMdiChild); + + //appWindow->actionTabbed->setEnabled(hasMdiChild); + //separatorAct->setVisible(hasMdiChild); + + appWindow->menuView->clear(); + appWindow->menuView->addAction(appWindow->action_Tile_Windows); + appWindow->menuView->addAction(appWindow->action_Windowed); + appWindow->menuView->addAction(appWindow->actionTabbed); + appWindow->menuView->addSeparator(); + appWindow->menuView->addAction(appWindow->actionNext); + appWindow->menuView->addAction(appWindow->actionPrevious); + + QList windows = mdiArea->subWindowList(); + if (!windows.isEmpty()) + appWindow->menuView->addSeparator(); + + for (int i = 0; i < windows.size(); ++i) { + MdiChild *child = qobject_cast(windows.at(i)->widget()); + QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile()); + if (i < 9) + text = "&" + text; + QAction *action = appWindow->menuView->addAction(text); + action->setCheckable(true); + action->setChecked(child == activeMdiChild()); + connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + } +} + + +MdiChild *MdiWindow::createMdiChild() +{ + MdiChild *child = new MdiChild; + mdiArea->addSubWindow(child); + connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview())); + connect(child, SIGNAL(closing()), this, SLOT(updateActions())); +#ifndef QT_NO_CLIPBOARD + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut, SLOT(setEnabled(bool))); + connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool))); +#endif + return child; +} + + +void MdiWindow::cascadeSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->cascadeSubWindows(); +} + + +void MdiWindow::tileSubWindows() +{ + mdiArea->setViewMode(QMdiArea::SubWindowView); + mdiArea->tileSubWindows(); +} + + +void MdiWindow::tabSubWindows() +{ + mdiArea->setViewMode(QMdiArea::TabbedView); +} + + +void MdiWindow::readSettings() +{ + YQ_LOG_DEBUG("Reading settings"); + QSettings settings("QtProject", "MDI Example"); + QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); + QSize size = settings.value("size", QSize(400, 400)).toSize(); + move(pos); + resize(size); +} + + +void MdiWindow::writeSettings() +{ + YQ_LOG_DEBUG("Saving settings"); + QSettings settings("QtProject", "MDI Example"); + settings.setValue("pos", pos()); + settings.setValue("size", size()); +} + + +MdiChild *MdiWindow::activeMdiChild() +{ + if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) + return qobject_cast(activeSubWindow->widget()); + return 0; +} + + +QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName) +{ + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + foreach (QMdiSubWindow *window, mdiArea->subWindowList()) { + MdiChild *mdiChild = qobject_cast(window->widget()); + if (mdiChild && mdiChild->currentFile() == canonicalFilePath) + return window; + } + return 0; +} + + +void MdiWindow::switchLayoutDirection() +{ + if (layoutDirection() == Qt::LeftToRight) + qApp->setLayoutDirection(Qt::RightToLeft); + else + qApp->setLayoutDirection(Qt::LeftToRight); +} + + +void MdiWindow::setActiveSubWindow(QWidget *window) +{ + if (!window) + return; + mdiArea->setActiveSubWindow(qobject_cast(window)); +} diff --git a/MdiWindow.h b/MdiWindow.h new file mode 100644 index 0000000..76d7f1b --- /dev/null +++ b/MdiWindow.h @@ -0,0 +1,174 @@ +#ifndef MDI_WINDOW_H +#define MDI_WINDOW_H + + +#include +#include + + +class MdiChild; +class QAction; +class QMenu; +class QMdiArea; +class QMdiSubWindow; +class QSignalMapper; +class Ui_MainWindow; + + +class MdiChild : public QTextEdit +{ + Q_OBJECT +public: + MdiChild(); + ~MdiChild(); + + void newFile(); + bool loadFile(const QString &fileName); + bool save(); + bool saveAs(); + bool saveFile(const QString &fileName); + QString userFriendlyCurrentFile(); + QString currentFile() { return curFile; } + + void markdownSelection(const char* preText, const char* postText); + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + +signals: + void closing(); + void documentContentsChanged(); + +private slots: + void documentWasModified(); + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +private: + bool maybeSave(); + void setCurrentFile(const QString &fileName); + QString strippedName(const QString &fullFileName); + + QString curFile; + bool isUntitled; +}; + + +/* +class MdiOptions +{ +public: + void serialize(); + void deserialize(); +}; +*/ + + +class MdiWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MdiWindow(QWidget *parent = 0); + void init(Ui_MainWindow* ui); + + bool openFile(const QString &fileName); +signals: + +protected: + void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; + +public slots: + +private slots: + + // File menu actions + void newFile(); + void open(); + void save(); + void saveAs(); + + void updatePreview(); + + // Edit menu actions +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + + void bold(); + void italic(); + void quote(); + void code(); + void heading1(); + void heading2(); + void heading3(); + void heading4(); + void hyperlink(); + void image(); + void unorderedList(); + void orderedList(); + void horizontalRule(); + void timestamp(); + void pageNumber(); + void pageCount(); + void options(); + void saveAll(); + void selectAll(); + void del(); + void undo(); + void redo(); + void find(); + void findNext(); + void findPrevious(); + void goToLine(); + + // Help + void support(); + void website(); + + // Window menu actions + void cascadeSubWindows(); + void tileSubWindows(); + void tabSubWindows(); + + void updateActions(); + MdiChild *createMdiChild(); + void switchLayoutDirection(); + void setActiveSubWindow(QWidget *window); + +private: + void readSettings(); + void writeSettings(); + MdiChild *activeMdiChild(); + QMdiSubWindow *findMdiChild(const QString &fileName); + + QMdiArea *mdiArea; + QSignalMapper *windowMapper; + Ui_MainWindow *appWindow; +}; + + +#endif // MDI_WINDOW_H diff --git a/QtProject/MakePDF.pro b/QtProject/MakePDF.pro index 7680de9..f4e410c 100644 --- a/QtProject/MakePDF.pro +++ b/QtProject/MakePDF.pro @@ -1,224 +1,236 @@ - -QT += core gui widgets - -CONFIG -= flat - -TARGET = MakePDF - -DEFINES += \ - GUI_APP=1 - -RESOURCES += \ - ../Resources/resources.qrc - -FORMS += \ - ../Resources/forms/About.ui \ - ../Resources/forms/NewProjectWizard.ui \ - ../Resources/forms/ExtensibleObjectModelUI.ui \ - ../Resources/forms/SendBugReport.ui - -INCLUDEPATH += \ - ../Framework \ - ../3rdParty/hoedown \ - ../3rdParty/libharu/include \ - ../3rdParty/TinyXML++ - -HEADERS += \ - ../Framework/EventLog.h \ - ../Framework/Expected.h \ - ../Framework/Final.h \ - ../Framework/Finally.h \ - ../Framework/GenericTable.h \ - ../Framework/GenericTableUI.h \ - ../Framework/NonCopyable.h \ - ../Framework/Utilities.h - -HEADERS += \ - ../DocOutput.h \ - ../DocProject.h \ - ../DocStyle.h \ - ../DocTemplate.h \ - ../DocVisitor.h \ - ../DocSVG.h - -SOURCES += \ - ../Framework/Tests.cpp \ - ../Framework/Utilities.cpp \ - ../Framework/EventLog.cpp - -SOURCES += \ - ../DocOutput.cpp \ - ../DocProject.cpp \ - ../DocStyle.cpp \ - ../DocTemplate.cpp \ - ../DocVisitor.cpp \ - ../DocSVG.cpp \ - ../main.cpp - -SOURCES += \ - ../3rdParty/hoedown/autolink.c \ - ../3rdParty/hoedown/buffer.c \ - ../3rdParty/hoedown/document.c \ - ../3rdParty/hoedown/escape.c \ - ../3rdParty/hoedown/html.c \ - ../3rdParty/hoedown/html_blocks.c \ - ../3rdParty/hoedown/stack.c - -SOURCES += \ - ../3rdParty/libharu/src/hpdf_3dmeasure.c \ - ../3rdParty/libharu/src/hpdf_annotation.c \ - ../3rdParty/libharu/src/hpdf_array.c \ - ../3rdParty/libharu/src/hpdf_binary.c \ - ../3rdParty/libharu/src/hpdf_boolean.c \ - ../3rdParty/libharu/src/hpdf_catalog.c \ - ../3rdParty/libharu/src/hpdf_destination.c \ - ../3rdParty/libharu/src/hpdf_dict.c \ - ../3rdParty/libharu/src/hpdf_doc.c \ - ../3rdParty/libharu/src/hpdf_doc_png.c \ - ../3rdParty/libharu/src/hpdf_encoder.c \ - ../3rdParty/libharu/src/hpdf_encoder_utf.c \ - ../3rdParty/libharu/src/hpdf_encrypt.c \ - ../3rdParty/libharu/src/hpdf_encryptdict.c \ - ../3rdParty/libharu/src/hpdf_error.c \ - ../3rdParty/libharu/src/hpdf_exdata.c \ - ../3rdParty/libharu/src/hpdf_ext_gstate.c \ - ../3rdParty/libharu/src/hpdf_font.c \ - ../3rdParty/libharu/src/hpdf_fontdef.c \ - ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ - ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ - ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ - ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ - ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ - ../3rdParty/libharu/src/hpdf_font_cid.c \ - ../3rdParty/libharu/src/hpdf_font_tt.c \ - ../3rdParty/libharu/src/hpdf_font_type1.c \ - ../3rdParty/libharu/src/hpdf_gstate.c \ - ../3rdParty/libharu/src/hpdf_image.c \ - ../3rdParty/libharu/src/hpdf_image_png.c \ - ../3rdParty/libharu/src/hpdf_info.c \ - ../3rdParty/libharu/src/hpdf_list.c \ - ../3rdParty/libharu/src/hpdf_mmgr.c \ - ../3rdParty/libharu/src/hpdf_name.c \ - ../3rdParty/libharu/src/hpdf_namedict.c \ - ../3rdParty/libharu/src/hpdf_null.c \ - ../3rdParty/libharu/src/hpdf_number.c \ - ../3rdParty/libharu/src/hpdf_objects.c \ - ../3rdParty/libharu/src/hpdf_outline.c \ - ../3rdParty/libharu/src/hpdf_pages.c \ - ../3rdParty/libharu/src/hpdf_page_label.c \ - ../3rdParty/libharu/src/hpdf_page_operator.c \ - ../3rdParty/libharu/src/hpdf_pdfa.c \ - ../3rdParty/libharu/src/hpdf_real.c \ - ../3rdParty/libharu/src/hpdf_streams.c \ - ../3rdParty/libharu/src/hpdf_string.c \ - ../3rdParty/libharu/src/hpdf_u3d.c \ - ../3rdParty/libharu/src/hpdf_utils.c \ - ../3rdParty/libharu/src/hpdf_xref.c - -SOURCES += \ - ../3rdParty/TinyXML++/ticpp.cpp \ - ../3rdParty/TinyXML++/tinystr.cpp \ - ../3rdParty/TinyXML++/tinyxml.cpp \ - ../3rdParty/TinyXML++/tinyxmlerror.cpp \ - ../3rdParty/TinyXML++/tinyxmlparser.cpp - - -# As a note to what was removed -REMOVED_SOURCES = \ - ../3rdParty/hoedown/html_smartypants.c \ - ../3rdParty/hoedown/version.c \ - ../3rdParty/libharu/src/hpdf_image_ccitt.c - - -# Linux specific settings -unix { - - QMAKE_CXXFLAGS += --std=gnu++11 - # QMAKE_CXXFLAGS -= -Wall - QMAKE_CXXFLAGS_WARN_ON=-w - QMAKE_CFLAGS_WARN_ON=-w - LIBS += -lpng -lz - -} - - -# Windows specific. Note we compile in libpng and zlib statically on Windows -win32 { - - # Supress annoying warnings - QMAKE_CXXFLAGS += /wd"4996;4100" - QMAKE_CXXFLAGS_WARN_ON -= -w34100 - - - # Release mode debugging - QMAKE_CXXFLAGS += /Zi - QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF - - - # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything - Project.target = $${TARGET}.vcxproj - Project.output = $${TARGET}.vcxproj - Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 - Project.depends = '"$${_PRO_FILE_}"' - Project.input = _PRO_FILE_ - QMAKE_EXTRA_COMPILERS += Project - - - # This makes the documentation - MARKDOWN_FILES = \ - test.md \ - test2.md - - Documentation.target = test.pdf - Documentation.output = test.pdf - Documentation.commands = echo ' ' > test.pdf - Documentation.depends = '"$${MARKDOWN_FILES}"' - Documentation.input = MARKDOWN_FILES - QMAKE_EXTRA_COMPILERS += Documentation - - - INCLUDEPATH += \ - ../3rdParty/libpng \ - ../3rdParty/zlib - - - SOURCES += \ - ../3rdParty/libpng/png.c \ - ../3rdParty/libpng/pngerror.c \ - ../3rdParty/libpng/pngget.c \ - ../3rdParty/libpng/pngmem.c \ - ../3rdParty/libpng/pngpread.c \ - ../3rdParty/libpng/pngread.c \ - ../3rdParty/libpng/pngrio.c \ - ../3rdParty/libpng/pngrtran.c \ - ../3rdParty/libpng/pngrutil.c \ - ../3rdParty/libpng/pngset.c \ - ../3rdParty/libpng/pngtrans.c \ - ../3rdParty/libpng/pngwio.c \ - ../3rdParty/libpng/pngwrite.c \ - ../3rdParty/libpng/pngwtran.c \ - ../3rdParty/libpng/pngwutil.c - - SOURCES += \ - ../3rdParty/zlib/inftrees.c \ - ../3rdParty/zlib/trees.c \ - ../3rdParty/zlib/uncompr.c \ - ../3rdParty/zlib/zutil.c \ - ../3rdParty/zlib/adler32.c \ - ../3rdParty/zlib/compress.c \ - ../3rdParty/zlib/crc32.c \ - ../3rdParty/zlib/deflate.c \ - ../3rdParty/zlib/gzclose.c \ - ../3rdParty/zlib/gzlib.c \ - ../3rdParty/zlib/gzread.c \ - ../3rdParty/zlib/gzwrite.c \ - ../3rdParty/zlib/infback.c \ - ../3rdParty/zlib/inffast.c \ - ../3rdParty/zlib/inflate.c - -} - - + +QT += core gui widgets + +CONFIG -= flat + +TARGET = MakePDF + +DEFINES += \ + GUI_APP=1 + +RESOURCES += \ + ../Resources/resources.qrc + +FORMS += \ + ../Resources/forms/About.ui \ + ../Resources/forms/NewProjectWizard.ui \ + ../Resources/forms/ExtensibleObjectModelUI.ui \ + ../Resources/forms/SendBugReport.ui \ + ../Resources/forms/Help.ui \ + ../Resources/forms/CheckUpdates.ui \ + ../Resources/forms/LicenseUpgrade.ui \ + ../Resources/forms/Settings.ui \ + ../Resources/forms/PaletteEditor.ui + +INCLUDEPATH += \ + ../Framework \ + ../3rdParty/hoedown \ + ../3rdParty/libharu/include \ + ../3rdParty/TinyXML++ + +HEADERS += \ + ../Framework/EventLog.h \ + ../Framework/Expected.h \ + ../Framework/Final.h \ + ../Framework/Finally.h \ + ../Framework/GenericTable.h \ + ../Framework/GenericTableUI.h \ + ../Framework/NonCopyable.h \ + ../Framework/Utilities.h \ + ../ui.h \ + ../MdiWindow.h \ + ../ColorPicker.h \ + ../Version.h + +HEADERS += \ + ../DocOutput.h \ + ../DocProject.h \ + ../DocStyle.h \ + ../DocTemplate.h \ + ../DocVisitor.h \ + ../DocSVG.h + +SOURCES += \ + ../Framework/Tests.cpp \ + ../Framework/Utilities.cpp \ + ../Framework/EventLog.cpp \ + ../MdiWindow.cpp \ + ../ColorPicker.cpp \ + ../Version.cpp + +SOURCES += \ + ../DocOutput.cpp \ + ../DocProject.cpp \ + ../DocStyle.cpp \ + ../DocTemplate.cpp \ + ../DocVisitor.cpp \ + ../DocSVG.cpp \ + ../main.cpp + +SOURCES += \ + ../3rdParty/hoedown/autolink.c \ + ../3rdParty/hoedown/buffer.c \ + ../3rdParty/hoedown/document.c \ + ../3rdParty/hoedown/escape.c \ + ../3rdParty/hoedown/html.c \ + ../3rdParty/hoedown/html_blocks.c \ + ../3rdParty/hoedown/stack.c + +SOURCES += \ + ../3rdParty/libharu/src/hpdf_3dmeasure.c \ + ../3rdParty/libharu/src/hpdf_annotation.c \ + ../3rdParty/libharu/src/hpdf_array.c \ + ../3rdParty/libharu/src/hpdf_binary.c \ + ../3rdParty/libharu/src/hpdf_boolean.c \ + ../3rdParty/libharu/src/hpdf_catalog.c \ + ../3rdParty/libharu/src/hpdf_destination.c \ + ../3rdParty/libharu/src/hpdf_dict.c \ + ../3rdParty/libharu/src/hpdf_doc.c \ + ../3rdParty/libharu/src/hpdf_doc_png.c \ + ../3rdParty/libharu/src/hpdf_encoder.c \ + ../3rdParty/libharu/src/hpdf_encoder_utf.c \ + ../3rdParty/libharu/src/hpdf_encrypt.c \ + ../3rdParty/libharu/src/hpdf_encryptdict.c \ + ../3rdParty/libharu/src/hpdf_error.c \ + ../3rdParty/libharu/src/hpdf_exdata.c \ + ../3rdParty/libharu/src/hpdf_ext_gstate.c \ + ../3rdParty/libharu/src/hpdf_font.c \ + ../3rdParty/libharu/src/hpdf_fontdef.c \ + ../3rdParty/libharu/src/hpdf_fontdef_base14.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cid.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cns.c \ + ../3rdParty/libharu/src/hpdf_fontdef_cnt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_jp.c \ + ../3rdParty/libharu/src/hpdf_fontdef_kr.c \ + ../3rdParty/libharu/src/hpdf_fontdef_tt.c \ + ../3rdParty/libharu/src/hpdf_fontdef_type1.c \ + ../3rdParty/libharu/src/hpdf_font_cid.c \ + ../3rdParty/libharu/src/hpdf_font_tt.c \ + ../3rdParty/libharu/src/hpdf_font_type1.c \ + ../3rdParty/libharu/src/hpdf_gstate.c \ + ../3rdParty/libharu/src/hpdf_image.c \ + ../3rdParty/libharu/src/hpdf_image_png.c \ + ../3rdParty/libharu/src/hpdf_info.c \ + ../3rdParty/libharu/src/hpdf_list.c \ + ../3rdParty/libharu/src/hpdf_mmgr.c \ + ../3rdParty/libharu/src/hpdf_name.c \ + ../3rdParty/libharu/src/hpdf_namedict.c \ + ../3rdParty/libharu/src/hpdf_null.c \ + ../3rdParty/libharu/src/hpdf_number.c \ + ../3rdParty/libharu/src/hpdf_objects.c \ + ../3rdParty/libharu/src/hpdf_outline.c \ + ../3rdParty/libharu/src/hpdf_pages.c \ + ../3rdParty/libharu/src/hpdf_page_label.c \ + ../3rdParty/libharu/src/hpdf_page_operator.c \ + ../3rdParty/libharu/src/hpdf_pdfa.c \ + ../3rdParty/libharu/src/hpdf_real.c \ + ../3rdParty/libharu/src/hpdf_streams.c \ + ../3rdParty/libharu/src/hpdf_string.c \ + ../3rdParty/libharu/src/hpdf_u3d.c \ + ../3rdParty/libharu/src/hpdf_utils.c \ + ../3rdParty/libharu/src/hpdf_xref.c + +SOURCES += \ + ../3rdParty/TinyXML++/ticpp.cpp \ + ../3rdParty/TinyXML++/tinystr.cpp \ + ../3rdParty/TinyXML++/tinyxml.cpp \ + ../3rdParty/TinyXML++/tinyxmlerror.cpp \ + ../3rdParty/TinyXML++/tinyxmlparser.cpp + + +# As a note to what was removed +REMOVED_SOURCES = \ + ../3rdParty/hoedown/html_smartypants.c \ + ../3rdParty/hoedown/version.c \ + ../3rdParty/libharu/src/hpdf_image_ccitt.c + + +# Linux specific settings +unix { + + QMAKE_CXXFLAGS += --std=gnu++11 + # QMAKE_CXXFLAGS -= -Wall + QMAKE_CXXFLAGS_WARN_ON=-w + QMAKE_CFLAGS_WARN_ON=-w + LIBS += -lpng -lz + +} + + +# Windows specific. Note we compile in libpng and zlib statically on Windows +win32 { + + # Supress annoying warnings + QMAKE_CXXFLAGS += /wd"4996;4100" + QMAKE_CXXFLAGS_WARN_ON -= -w34100 + + + # Release mode debugging + QMAKE_CXXFLAGS += /Zi + QMAKE_LFLAGS += /DEBUG /OPT:REF /OPT:ICF + + + # This makes the .vcxproj depend on the .pro file, so editing the .pro file will update everything + Project.target = $${TARGET}.vcxproj + Project.output = $${TARGET}.vcxproj + Project.commands = c:\qt\qt5.4.1\5.4\msvc2013_64_opengl\bin\qmake -tp vc '"$${_PRO_FILE_}"' -spec win32-msvc2013 + Project.depends = '"$${_PRO_FILE_}"' + Project.input = _PRO_FILE_ + QMAKE_EXTRA_COMPILERS += Project + + + # This makes the documentation + MARKDOWN_FILES = \ + test.md \ + test2.md + + Documentation.target = test.pdf + Documentation.output = test.pdf + Documentation.commands = echo ' ' > test.pdf + Documentation.depends = '"$${MARKDOWN_FILES}"' + Documentation.input = MARKDOWN_FILES + QMAKE_EXTRA_COMPILERS += Documentation + + + INCLUDEPATH += \ + ../3rdParty/libpng \ + ../3rdParty/zlib + + + SOURCES += \ + ../3rdParty/libpng/png.c \ + ../3rdParty/libpng/pngerror.c \ + ../3rdParty/libpng/pngget.c \ + ../3rdParty/libpng/pngmem.c \ + ../3rdParty/libpng/pngpread.c \ + ../3rdParty/libpng/pngread.c \ + ../3rdParty/libpng/pngrio.c \ + ../3rdParty/libpng/pngrtran.c \ + ../3rdParty/libpng/pngrutil.c \ + ../3rdParty/libpng/pngset.c \ + ../3rdParty/libpng/pngtrans.c \ + ../3rdParty/libpng/pngwio.c \ + ../3rdParty/libpng/pngwrite.c \ + ../3rdParty/libpng/pngwtran.c \ + ../3rdParty/libpng/pngwutil.c + + SOURCES += \ + ../3rdParty/zlib/inftrees.c \ + ../3rdParty/zlib/trees.c \ + ../3rdParty/zlib/uncompr.c \ + ../3rdParty/zlib/zutil.c \ + ../3rdParty/zlib/adler32.c \ + ../3rdParty/zlib/compress.c \ + ../3rdParty/zlib/crc32.c \ + ../3rdParty/zlib/deflate.c \ + ../3rdParty/zlib/gzclose.c \ + ../3rdParty/zlib/gzlib.c \ + ../3rdParty/zlib/gzread.c \ + ../3rdParty/zlib/gzwrite.c \ + ../3rdParty/zlib/infback.c \ + ../3rdParty/zlib/inffast.c \ + ../3rdParty/zlib/inflate.c + +} + + diff --git a/Resources/resources.qrc b/Resources/resources.qrc index 55e4392..d6ef98c 100644 --- a/Resources/resources.qrc +++ b/Resources/resources.qrc @@ -1,44 +1,50 @@ - - icons/window26.png - icons/lifebelt.png - icons/lightbulb3.png - icons/open134.png - icons/question24.png - icons/speech64.png - icons/speech66.png - icons/window30.png - icons/h1.png - icons/h2.png - icons/h3.png - icons/h4.png - icons/h5.png - icons/normal.png - icons/cut20.png - icons/delete30.png - icons/development.png - icons/document28.png - icons/italics.png - icons/floppy13.png - icons/font8.png - icons/floppy15.png - icons/list36.png - icons/link71.png - icons/magnifying glass39.png - icons/magnifying glass40.png - icons/minus25.png - icons/numbered5.png - icons/open131.png - icons/page10.png - icons/paste1.png - icons/pdf24.png - icons/printing28.png - icons/quotation1.png - icons/redo5.png - icons/undo7.png - icons/squares64.png - - - icons/logo.png - + + icons/window26.png + icons/lifebelt.png + icons/lightbulb3.png + icons/open134.png + icons/question24.png + icons/speech64.png + icons/speech66.png + icons/window30.png + icons/h1.png + icons/h2.png + icons/h3.png + icons/h4.png + icons/h5.png + icons/normal.png + icons/cut20.png + icons/delete30.png + icons/development.png + icons/document28.png + icons/italics.png + icons/floppy13.png + icons/font8.png + icons/floppy15.png + icons/list36.png + icons/link71.png + icons/magnifying glass39.png + icons/magnifying glass40.png + icons/minus25.png + icons/numbered5.png + icons/open131.png + icons/page10.png + icons/paste1.png + icons/pdf24.png + icons/printing28.png + icons/quotation1.png + icons/redo5.png + icons/undo7.png + icons/squares64.png + + + images/logo.svg + images/color-wheel-mode-1.png + images/color-wheel-mode-2.png + images/color-wheel-mode-3.png + images/color-wheel-mode-4.png + images/color-wheel-mode-5.png + images/color-wheel.png + diff --git a/Util.h b/Util.h index bc6a8a5..1dfc996 100644 --- a/Util.h +++ b/Util.h @@ -1,17 +1,66 @@ #include +#include +#include +#include #ifndef _MSC_VER namespace std { - -template -std::unique_ptr make_unique( Args&& ...args ) -{ - return std::unique_ptr( new T( std::forward(args)... ) ); -} - + template + std::unique_ptr make_unique( Args&& ...args ) + { + return std::unique_ptr( new T( std::forward(args)... ) ); + } }; +inline int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode) +{ + *a_file = fopen(a_fileName, a_mode); + return (*a_file != nullptr); +} + #endif + +inline static std::vector split(const std::string &s, char delim) +{ + std::vector elems; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + + +inline static std::string trim(std::string str) +{ + str.erase(0, str.find_first_not_of(" \t\r\n")); + str.erase(str.find_last_not_of(" \t\r\n") + 1); + return str; +} + + +inline static std::string str2lower(const std::string& str) +{ + std::string ret = str; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + + +inline static unsigned int str2col(std::string str) +{ + if (str[0] == '#') + str = &str[1]; + return std::stoul(str, nullptr, 16); +} + + +float static str2float(const std::string& str) +{ + return (float)atof(str.c_str()); +} + diff --git a/Version.cpp b/Version.cpp new file mode 100644 index 0000000..fb913ae --- /dev/null +++ b/Version.cpp @@ -0,0 +1,131 @@ +#include "Version.h" +#include "Util.h" +#include +#include +#include + + +#ifdef __APPLE__ +# include "TargetConditionals.h" +#elif defined(_WIN32) +# include +#else +# include +# include +# include +# include +#endif + + +void getSystemInfo(SystemInfo& a_info) +{ + a_info.m_cpuType = "unknown"; + a_info.m_osType = "unknown"; + a_info.m_osVersion = "unknown"; + +#ifdef _WIN32 + a_info.m_osType = "Windows"; + + // Get RAM size + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + a_info.m_ramSize = status.ullTotalPhys; + + // Get OS version + const OSVERSIONINFO osver = winOsVersion(); + a_info.m_osVersion.reserve(512); + snprintf(a_info.m_osVersion.data(), "%i.%i.%i", osver.dwMajorVersion, osver.dwMinorVersion, osver.dwBuildNumber); + + // Get OS arch + SYSTEM_INFO info; + GetNativeSystemInfo(&info); +# ifdef _WIN64 + //if (info.wProcessorArchitecture == ???) + a_info.m_cpuType = "x86_64"; +# else + a_info.m_cpuType = "x86"; +# endif +#else + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + a_info.m_ramSize = pages * page_size; + struct utsname u; + long ret = uname(&u); + if (ret != -1) + { + a_info.m_cpuType = u.machine; + a_info.m_osVersion = u.sysname + std::string(" ") + u.release; + } +# ifdef __APPLE__ + // Get RAM Size + int mib[2] = { CTL_HW, HW_MEMSIZE }; + size_t len = sizeof(uint64_t); + sysctl(mib, sizeof(mib) / sizeof(mib[0]), &u.ramSize, &len, NULL, 0); +# if TARGET_OS_IPHONE + a_info.m_osType = "iOS"; +# elif TARGET_OS_MAC + const char *macCodeName = ""; + switch (int(MacintoshVersion)) { + case MV_CHEETAH: macCodeName = " Cheetah"; break; + case MV_PUMA: macCodeName = " Puma"; break; + case MV_JAGUAR: macCodeName = " Jaguar"; break; + case MV_PANTHER: macCodeName = " Panther"; break; + case MV_TIGER: macCodeName = " Tiger"; break; + case MV_LEOPARD: macCodeName = " Leopard"; break; + case MV_SNOWLEOPARD: macCodeName = " Snow Leopard"; break; + case MV_LION: macCodeName = " Lion"; break; + case MV_MOUNTAINLION:macCodeName = " Mountain Lion"; break; + case MV_MAVERICKS: macCodeName = " Mavericks"; break; + case MV_YOSEMITE: macCodeName = " Yosemite"; break; + } + a_info.m_osType = "Mac OS X" + std::string(macCodeName); +# endif +# elif __ANDROID__ + a_info.m_osType = "Android"; +# elif __linux + // system("lsb_release -sd"); + FILE* f = fopen("/etc/lsb-release", "rt"); + char dist[64] = { 0 }; + if (f) { + char buf[128]; + while (fgets(buf, 128, f) != 0) + if (sscanf(buf, "DISTRIB_DESCRIPTION=%64c", dist) == 1) + break; + if (dist[0]) { + std::vector strBits = split(dist, '"'); + a_info.m_osType = strBits[(strBits.size()==3)?1:0]; + } else { + a_info.m_osType = "non-specific LSB"; + } + fclose(f); + } else { + a_info.m_osType = "non-LSB"; + } +# else + a_info.m_osType = "Generic UNIX"; +# endif +#endif + + // TODO: improve the CPU detection using libcpuid + + // Make human readable RAM size string + const char* humanUnitStrings[] = { " bytes", " KB", " MB", " GB", " TB" }; + int ramUnit = 0; + uint64_t ramSize = a_info.m_ramSize; + while (ramSize > 1024 && ramUnit < (YQ_ARRAY_SIZE(humanUnitStrings)-1)) + ramUnit++, ramSize /= 1000; + a_info.m_ramSizeStr = std::to_string(ramSize) + humanUnitStrings[ramUnit]; + + // Obtain the screen properties + QScreen *s = QApplication::screens()[0]; + int refreshRate = (s) ? s->refreshRate() : 0; + int depth = (s) ? s->depth() : 0; + a_info.m_resolutionWidth = (s) ? s->geometry().width() : 0; + a_info.m_resolutionHeight = (s) ? s->geometry().height() : 0; + a_info.m_resolutionStr = std::to_string(a_info.m_resolutionWidth) + "x" + + std::to_string(a_info.m_resolutionHeight) + " " + + std::to_string(depth) + "bit " + + "@" + std::to_string(refreshRate) + "Hz"; + ; +} diff --git a/Version.h b/Version.h new file mode 100644 index 0000000..5dd615c --- /dev/null +++ b/Version.h @@ -0,0 +1,32 @@ +#ifndef VERSION_H +#define VERSION_H + + +#include "Utilities.h" +#include +#include + + +#define APP_VERSION 1.00 +#define APP_BUILD 0001 +#define APP_VER_STR YQ_STRINGIZE(APP_VERSION) "." YQ_STRINGIZE(APP_BUILD) +#define APP_VER_STR_LONG "Version " YQ_STRINGIZE(APP_VERSION) ", Build" YQ_STRINGIZE(APP_BUILD) + + +struct SystemInfo +{ + std::string m_cpuType; + std::string m_osType; + std::string m_osVersion; + std::string m_ramSizeStr; // Human friendly string, eg: "4 GB" + std::string m_resolutionStr; + uint64_t m_ramSize; + uint32_t m_resolutionWidth; + uint32_t m_resolutionHeight; +}; + + +void getSystemInfo(SystemInfo& a_info); + + +#endif // VERSION_H diff --git a/main.cpp b/main.cpp index c5d5163..a4a0bd4 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,7 @@ #include "DocTemplate.h" #include "DocOutput.h" #include "DocSVG.h" +#include "Util.h" //#include "document.h" #include "html.h" @@ -19,15 +20,9 @@ #ifndef _WIN32 # include -# define __cdecl +//# define __cdecl #else extern "C" void __stdcall Sleep(unsigned int); -//# include -#endif - - -#ifndef _MSC_VER // _WIN32 -int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode); #endif @@ -119,7 +114,7 @@ } -void SVGTest(const char* a_fileName, DocOutputDevice* outputDoc) +void SVGTest(const char* a_fileName, double scale, DocOutputDevice* outputDoc) { hoedown_buffer* inputBuffer = ReadInWholeFile(a_fileName); if (!inputBuffer) @@ -127,7 +122,7 @@ // SVG xml parse TiXmlDocument parser; parser.Parse((char*)inputBuffer->data); - DocSVG visitor; + DocSVG visitor(scale); parser.Accept(&visitor); visitor.DumpOperations(); visitor.WriteTo(outputDoc); @@ -147,7 +142,8 @@ DocVisitor visitor(outputDoc, &style, &templ); parser.Accept(&visitor); - SVGTest("test/test.svg", outputDoc); + //SVGTest("test/triangle.svg", 0.02, outputDoc); + //SVGTest("test/ArcTest.svg", 1.0, outputDoc); } @@ -184,33 +180,56 @@ int main(int argc, char* argv[]) { + DocOutputDevice outputDoc; + Context context = { + "Test Markdown to PDF", + "test/test.md", + "test/test.pdf", + "test/test.html", + &outputDoc + }; + ProcessConversionContext(&context); + +/* + // Qt MDI Example does this: + + Q_INIT_RESOURCE(mdi); + QApplication app(argc, argv); + QCoreApplication::setApplicationVersion(QT_VERSION_STR); + QCommandLineParser parser; + parser.setApplicationDescription("Qt MDI Example"); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument("file", "The file to open."); + parser.process(app); + + MainWindow mainWin; + foreach (const QString &fileName, parser.positionalArguments()) + mainWin.openFile(fileName); + mainWin.show(); + return app.exec(); +*/ + +#ifdef GUI_APP + QApplication app(argc, argv); + UiContext ui; + ui.setupUi(); + //YQ_LOG_DEBUG("This is a test message"); + //runTests(); + app.exec(); +#endif + + +/* fprintf(stderr, "HERE!\n\n\n"); QApplication app2(argc, argv); QLabel l(0); l.setText("Hello!"); l.show(); app2.exec(); +*/ - DocOutputDevice outputDoc; - Context context = { - "Test Markdown to PDF", - "test/test.md", - "test/test.pdf", - "test/test.html", - &outputDoc - }; - ProcessConversionContext(&context); - -#ifdef GUI_APP - QApplication app(argc, argv); - UiContext ui; - ui.setupUi(); - YQ_LOG_DEBUG("This is a test message"); - runTests(); - app.exec(); -#endif - return 0; } diff --git a/ui.h b/ui.h index 6398374..c27187f 100644 --- a/ui.h +++ b/ui.h @@ -1,8 +1,19 @@ #include "ui_About.h" +#include "ui_CheckUpdates.h" #include "ui_ExtensibleObjectModelUI.h" +#include "ui_Help.h" +#include "ui_LicenseUpgrade.h" +#include "ui_NewProjectWizard.h" +#include "ui_PaletteEditor.h" +#include "ui_SendBugReport.h" +#include "ui_Settings.h" + #include "EventLog.h" -#include "GenericTableUI.h" #include "ColorPicker.h" +#include "GenericTableUI.h" +#include "MdiWindow.h" +#include "Version.h" +#include class UiLogger : public Logger @@ -23,39 +34,147 @@ struct UiContext { - Ui_MainWindow appWindow; - QMainWindow mainWindow; - Ui_About about; - QDialog aboutDialog; + QDialog aboutDialog; - EventLog log; - UiLogger logger; - GenericTableUI logView; + Ui_CheckUpdates checkUpdates; + QDialog checkUpdatesDialog; - ColorPicker picker; + Ui_MainWindow appWindow; + MdiWindow mainWindow; + + Ui_Help help; + QDialog helpDialog; + + Ui_LicenseUpgrade licenseUpgrade; + QDialog licenseUpgradeDialog; + + Ui_NewProjectWizard newProject; + QDialog newProjectDialog; + + Ui_PaletteEditor paletteEditor; + QDialog paletteEditorDialog; + + Ui_SendBugReport sendBugReport; + QDialog sendBugReportDialog; + + Ui_Settings settings; + QDialog settingsDialog; + + ColorPicker colorPicker; + + EventLog log; + GenericTableUI logView; + UiLogger logger; void setupUi() { - logger.init(&log); - g_currentModule.m_messageLogger = &logger; + logger.init(&log); + g_currentModule.m_messageLogger = &logger; + YQ_LOG_DEBUG("Initializing"); - appWindow.setupUi(&mainWindow); -#if QT_VERSION >= 0x050000 - mainWindow.menuBar()->setNativeMenuBar(false); -#endif + about.setupUi(&aboutDialog); + checkUpdates.setupUi(&checkUpdatesDialog); + appWindow.setupUi(&mainWindow); + help.setupUi(&helpDialog); + licenseUpgrade.setupUi(&licenseUpgradeDialog); + newProject.setupUi(&newProjectDialog); + paletteEditor.setupUi(&paletteEditorDialog); + sendBugReport.setupUi(&sendBugReportDialog); + settings.setupUi(&settingsDialog); - about.setupUi(&aboutDialog); - aboutDialog.setFixedSize(430, 300); + // Fill-in details + SystemInfo sysInfo; + getSystemInfo(sysInfo); + sendBugReport.ver->setText(APP_VER_STR); + sendBugReport.plat->setText(sysInfo.m_osType.c_str()); + sendBugReport.platVer->setText(sysInfo.m_osVersion.c_str()); + sendBugReport.cpu->setText(sysInfo.m_cpuType.c_str()); + sendBugReport.dateTime->setText(QDateTime::currentDateTime().toString()); + sendBugReport.ram->setText(sysInfo.m_ramSizeStr.c_str()); + sendBugReport.res->setText(sysInfo.m_resolutionStr.c_str()); - QObject::connect(appWindow.actionAbout, SIGNAL(triggered(bool)), &aboutDialog, SLOT(exec())); + // Setup standard keyboard short cuts + appWindow.actionNew->setShortcuts(QKeySequence::New); + appWindow.actionOpen->setShortcuts(QKeySequence::Open); + appWindow.actionSave->setShortcuts(QKeySequence::Save); + appWindow.actionSave_As->setShortcuts(QKeySequence::SaveAs); + appWindow.actionExit->setShortcuts(QKeySequence::Quit); + appWindow.actionCut->setShortcuts(QKeySequence::Cut); + appWindow.actionCopy->setShortcuts(QKeySequence::Copy); + appWindow.actionPaste->setShortcuts(QKeySequence::Paste); + appWindow.actionHelp->setShortcuts(QKeySequence::HelpContents); + appWindow.actionDelete->setShortcuts(QKeySequence::Delete); + appWindow.actionClose->setShortcuts(QKeySequence::Close); + appWindow.actionUndo->setShortcuts(QKeySequence::Undo); + appWindow.actionRedo->setShortcuts(QKeySequence::Redo); + appWindow.actionOptions->setShortcuts(QKeySequence::Preferences); + appWindow.actionBold->setShortcuts(QKeySequence::Bold); + appWindow.actionItalic->setShortcuts(QKeySequence::Italic); + appWindow.actionFind->setShortcuts(QKeySequence::Find); + appWindow.actionFind_Next->setShortcuts(QKeySequence::FindNext); + appWindow.actionFind_Previous->setShortcuts(QKeySequence::FindPrevious); + appWindow.actionSelect_All->setShortcuts(QKeySequence::SelectAll); + appWindow.actionNext->setShortcuts(QKeySequence::NextChild); + appWindow.actionPrevious->setShortcuts(QKeySequence::PreviousChild); - mainWindow.show(); - picker.show(); - - log.LogMessage("blah", "hooking up ui"); - logView.init(&log, appWindow.eventLog); - log.LogMessage("blah", "starting"); + QObject::connect(appWindow.actionExit, SIGNAL(triggered(bool)), qApp, SLOT(closeAllWindows())); + QObject::connect(appWindow.actionAbout, SIGNAL(triggered(bool)), &aboutDialog, SLOT(exec())); + QObject::connect(appWindow.actionCheck_for_Updates, SIGNAL(triggered(bool)), &checkUpdatesDialog, SLOT(exec())); + QObject::connect(appWindow.actionHelp, SIGNAL(triggered(bool)), &helpDialog, SLOT(exec())); + QObject::connect(appWindow.actionUpgrade, SIGNAL(triggered(bool)), &licenseUpgradeDialog, SLOT(exec())); + QObject::connect(appWindow.actionNew_Project, SIGNAL(triggered(bool)), &newProjectDialog, SLOT(exec())); + //QObject::connect(appWindow.actionChoose_Palette, SIGNAL(triggered(bool)), &colorPicker, SLOT(exec())); + QObject::connect(appWindow.actionChoose_Palette, SIGNAL(triggered(bool)), &paletteEditorDialog, SLOT(exec())); + QObject::connect(appWindow.actionReport_a_bug, SIGNAL(triggered(bool)), &sendBugReportDialog, SLOT(exec())); + QObject::connect(appWindow.actionOptions, SIGNAL(triggered(bool)), &settingsDialog, SLOT(exec())); + + logView.init(&log, appWindow.eventLog); + mainWindow.init(&appWindow); + mainWindow.show(); + + YQ_LOG_DEBUG("Started"); + + /* + + // Actions require handling: + + actionSave_All; + actionSelect_All; + actionDelete; + actionUndo; + actionRedo; + actionFind; + actionFind_Next; + actionFind_Previous; + actionGo_to_Line; + + actionCode; + + actionHyperlink; + actionImage; + actionUnordered_List; + actionOrdered_List; + actionHorizontal_Rule; + actionTimestamp; + actionPage_Number; + actionPage_Count; + actionOptions; + + // QKeySequences that might be useful actions to add: + + WhatsThis, + ZoomIn, + ZoomOut, + Print, + Refresh, + AddTab, + Underline, + Back, + Forward, + Replace, + + */ } };