mirror of
https://github.com/rgriebl/brickstore.git
synced 2026-05-25 10:28:29 -05:00
Finally resolved the BL::InvItem vs. Doc::Item mess...
... by introducing a new Lot class and removing InvItem from the BrickLink API completely.
This commit is contained in:
@@ -9,14 +9,14 @@ Script {
|
||||
|
||||
PrintingScriptTemplate {
|
||||
text: "Standard Print Template"
|
||||
printFunction: function(job, doc, items) {
|
||||
printJob(job, doc, items)
|
||||
printFunction: function(job, doc, lots) {
|
||||
printJob(job, doc, lots)
|
||||
}
|
||||
}
|
||||
|
||||
function printJob(job, doc, items)
|
||||
function printJob(job, doc, lots)
|
||||
{
|
||||
if (!items.length)
|
||||
if (!lots.length)
|
||||
return
|
||||
|
||||
let ps = {}
|
||||
@@ -29,12 +29,12 @@ Script {
|
||||
ps.pos = 0
|
||||
|
||||
let pagecount = 0
|
||||
let itemh = 15
|
||||
let rowh = 15
|
||||
let pageh = 10
|
||||
let reporth = 7.5
|
||||
let listh = 7.5
|
||||
let items_left = items.length
|
||||
let itemflip = false
|
||||
let lots_left = lots.length
|
||||
let alternate = false
|
||||
let rfooter = false
|
||||
let page
|
||||
|
||||
@@ -45,7 +45,7 @@ Script {
|
||||
jobstat.items = 0
|
||||
jobstat.total = 0
|
||||
|
||||
while (items_left || !rfooter) {
|
||||
while (lots_left || !rfooter) {
|
||||
page = job.addPage()
|
||||
|
||||
ps.pos = 0
|
||||
@@ -58,36 +58,36 @@ Script {
|
||||
if (job.pageCount == 1)
|
||||
reportHeader(page, ps)
|
||||
|
||||
if (items_left)
|
||||
if (lots_left)
|
||||
listHeader(page, ps)
|
||||
|
||||
let items_on_page = false
|
||||
let lots_on_page = false
|
||||
|
||||
while (items_left) {
|
||||
if (ps.pos >(ps.h - itemh - listh - pageh))
|
||||
while (lots_left) {
|
||||
if (ps.pos >(ps.h - rowh - listh - pageh))
|
||||
break
|
||||
|
||||
let item = items [items.length - items_left]
|
||||
listItem(page, ps, item, itemflip)
|
||||
let lot = lots [lots.length - lots_left]
|
||||
listLot(page, ps, lot, alternate)
|
||||
|
||||
pagestat.lots++
|
||||
pagestat.items += item.quantity
|
||||
pagestat.total += item.total
|
||||
pagestat.items += lot.quantity
|
||||
pagestat.total += lot.total
|
||||
|
||||
jobstat.lots++
|
||||
jobstat.items += item.quantity
|
||||
jobstat.total += item.total
|
||||
jobstat.items += lot.quantity
|
||||
jobstat.total += lot.total
|
||||
|
||||
itemflip = itemflip ? false : true
|
||||
items_left--
|
||||
items_on_page = true
|
||||
alternate = !alternate
|
||||
lots_left--
|
||||
lots_on_page = true
|
||||
}
|
||||
|
||||
if (items_on_page) {
|
||||
if (lots_on_page) {
|
||||
listFooter(page, ps, pagestat)
|
||||
}
|
||||
|
||||
if (!items_left && !rfooter && (ps.pos <= (ps.h - reporth - pageh))) {
|
||||
if (!lots_left && !rfooter && (ps.pos <= (ps.h - reporth - pageh))) {
|
||||
reportFooter(page, ps, jobstat)
|
||||
rfooter = true
|
||||
}
|
||||
@@ -221,35 +221,35 @@ Script {
|
||||
ps.pos += h
|
||||
}
|
||||
|
||||
function listItem(page, ps, item, odd)
|
||||
function listLot(page, ps, lot, alternate)
|
||||
{
|
||||
let y = ps.y + ps.pos
|
||||
let h = 15
|
||||
|
||||
page.backgroundColor = odd ? "#dddddd" : "white"
|
||||
page.backgroundColor = alternate ? "#dddddd" : "white"
|
||||
page.color = page.backgroundColor
|
||||
page.drawRect(ps.x, y, ps.w, h)
|
||||
|
||||
page.color = "black"
|
||||
page.font = Qt.font({ family: "Arial", pointSize: 10 })
|
||||
|
||||
page.drawImage(ps.x + 2, y, xs(ps.w, 15), h, item.image)
|
||||
page.drawImage(ps.x + 2, y, xs(ps.w, 15), h, lot.image)
|
||||
|
||||
page.drawText(ps.x + xs(ps.w, 20), y, xs(ps.w, 15), h,
|
||||
Page.AlignHCenter | Page.AlignVCenter,
|
||||
item.condition == BrickLink.Used ? "Used" : "New")
|
||||
lot.condition == BrickLink.Used ? "Used" : "New")
|
||||
page.drawText(ps.x + xs(ps.w, 35), y, xs(ps.w, 85), h,
|
||||
Page.AlignLeft | Page.AlignVCenter | Page.TextWordWrap,
|
||||
item.color.name + " " + item.name + " [" + item.id + "]")
|
||||
lot.color.name + " " + lot.name + " [" + lot.id + "]")
|
||||
page.drawText(ps.x + xs(ps.w, 120), y, xs(ps.w, 10), h,
|
||||
Page.AlignRight | Page.AlignVCenter,
|
||||
item.quantity)
|
||||
lot.quantity)
|
||||
page.drawText(ps.x + xs(ps.w, 130), y, xs(ps.w, 19), h,
|
||||
Page.AlignRight | Page.AlignVCenter,
|
||||
BrickStore.toCurrencyString(item.price, ps.ccode))
|
||||
BrickStore.toCurrencyString(lot.price, ps.ccode))
|
||||
page.drawText(ps.x + xs(ps.w, 150), y, xs(ps.w, 19), h,
|
||||
Page.AlignRight | Page.AlignVCenter,
|
||||
BrickStore.toCurrencyString(item.total, ps.ccode))
|
||||
BrickStore.toCurrencyString(lot.total, ps.ccode))
|
||||
|
||||
ps.pos += h
|
||||
}
|
||||
|
||||
+22
-22
@@ -514,20 +514,20 @@ void AddItemDialog::updateHistoryText()
|
||||
}
|
||||
}
|
||||
|
||||
QString AddItemDialog::historyTextFor(const QDateTime &when, const BrickLink::InvItem &item)
|
||||
QString AddItemDialog::historyTextFor(const QDateTime &when, const Lot &lot)
|
||||
{
|
||||
auto now = QDateTime::currentDateTime();
|
||||
QString cs;
|
||||
if (item.color() && item.color()->id()) {
|
||||
QColor color = item.color()->color();
|
||||
cs = u"<b><font color=\"" % Utility::contrastColor(color, 1.).name() %
|
||||
if (lot.color() && lot.color()->id()) {
|
||||
QColor color = lot.color()->color();
|
||||
cs = u"<b><font color=\"" % Utility::textColor(color).name() %
|
||||
"\" style=\"background-color: " % color.name() % u" ;\"> " %
|
||||
item.colorName() % u" </font></b> ";
|
||||
lot.colorName() % u" </font></b> ";
|
||||
}
|
||||
|
||||
QString s = tr("Added %1").arg(HumanReadableTimeDelta::toString(now, when)) %
|
||||
u": <b>" % QString::number(item.quantity()) % u"</b> " % cs %
|
||||
item.itemName() % u" <i>[" + item.itemId() % u"]</i>";
|
||||
u": <b>" % QString::number(lot.quantity()) % u"</b> " % cs %
|
||||
lot.itemName() % u" <i>[" + lot.itemId() % u"]</i>";
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -655,33 +655,33 @@ void AddItemDialog::addClicked()
|
||||
else
|
||||
color = BrickLink::core()->color(0);
|
||||
|
||||
auto *ii = new BrickLink::InvItem(color, item);
|
||||
auto *lot = new Lot(color, item);
|
||||
|
||||
ii->setQuantity(w_qty->text().toInt());
|
||||
ii->setPrice(Currency::fromString(w_price->text()));
|
||||
ii->setCost(Currency::fromString(w_cost->text()));
|
||||
ii->setBulkQuantity(w_bulk->text().toInt());
|
||||
ii->setCondition(static_cast <BrickLink::Condition>(m_condition->checkedId()));
|
||||
if (ii->itemType() && ii->itemType()->hasSubConditions())
|
||||
ii->setSubCondition(static_cast<BrickLink::SubCondition>(1 + w_subcondition->currentIndex()));
|
||||
ii->setRemarks(w_remarks->text());
|
||||
ii->setComments(w_comments->text());
|
||||
lot->setQuantity(w_qty->text().toInt());
|
||||
lot->setPrice(Currency::fromString(w_price->text()));
|
||||
lot->setCost(Currency::fromString(w_cost->text()));
|
||||
lot->setBulkQuantity(w_bulk->text().toInt());
|
||||
lot->setCondition(static_cast <BrickLink::Condition>(m_condition->checkedId()));
|
||||
if (lot->itemType() && lot->itemType()->hasSubConditions())
|
||||
lot->setSubCondition(static_cast<BrickLink::SubCondition>(1 + w_subcondition->currentIndex()));
|
||||
lot->setRemarks(w_remarks->text());
|
||||
lot->setComments(w_comments->text());
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!w_tier_price [i]->isEnabled())
|
||||
break;
|
||||
|
||||
ii->setTierQuantity(i, w_tier_qty [i]->text().toInt());
|
||||
ii->setTierPrice(i, tierPriceValue(i));
|
||||
lot->setTierQuantity(i, w_tier_qty [i]->text().toInt());
|
||||
lot->setTierPrice(i, tierPriceValue(i));
|
||||
}
|
||||
|
||||
m_addHistory.emplace_back(qMakePair(QDateTime::currentDateTime(), *ii));
|
||||
m_addHistory.emplace_back(qMakePair(QDateTime::currentDateTime(), *lot));
|
||||
while (m_addHistory.size() > 6)
|
||||
m_addHistory.pop_front();
|
||||
updateHistoryText();
|
||||
|
||||
m_window->addItems({ ii }, w_merge->isChecked() ? Window::AddItemMode::ConsolidateWithExisting
|
||||
: Window::AddItemMode::AddAsNew);
|
||||
m_window->addLots({ lot }, w_merge->isChecked() ? Window::AddLotMode::ConsolidateWithExisting
|
||||
: Window::AddLotMode::AddAsNew);
|
||||
}
|
||||
|
||||
#include "moc_additemdialog.cpp"
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ private slots:
|
||||
private:
|
||||
double tierPriceValue(int i);
|
||||
void updateHistoryText();
|
||||
static QString historyTextFor(const QDateTime &when, const BrickLink::InvItem &item);
|
||||
static QString historyTextFor(const QDateTime &when, const Lot &lot);
|
||||
|
||||
QByteArray saveState() const;
|
||||
bool restoreState(const QByteArray &ba);
|
||||
@@ -85,5 +85,5 @@ private:
|
||||
QToolButton *w_toggles[3];
|
||||
|
||||
QTimer *m_historyTimer;
|
||||
std::list<QPair<QDateTime, const BrickLink::InvItem>> m_addHistory;
|
||||
std::list<QPair<QDateTime, const Lot>> m_addHistory;
|
||||
};
|
||||
|
||||
@@ -147,10 +147,15 @@ void AppearsInWidget::setItem(const BrickLink::Item *item, const BrickLink::Colo
|
||||
delete old_model;
|
||||
}
|
||||
|
||||
void AppearsInWidget::setItems(const BrickLink::InvItemList &list)
|
||||
void AppearsInWidget::setItems(const LotList &lots)
|
||||
{
|
||||
QAbstractItemModel *old_model = model();
|
||||
|
||||
QVector<QPair<const BrickLink::Item *, const BrickLink::Color *>> list;
|
||||
list.reserve(lots.size());
|
||||
for (const auto &lot : lots)
|
||||
list.append({ lot->item(), lot->color() });
|
||||
|
||||
setModel(new BrickLink::AppearsInModel(list, this));
|
||||
resizeColumns();
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include "bricklinkfwd.h"
|
||||
#include "lot.h"
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QAction)
|
||||
class AppearsInWidgetPrivate;
|
||||
@@ -30,7 +31,7 @@ public:
|
||||
~AppearsInWidget() override;
|
||||
|
||||
void setItem(const BrickLink::Item *item, const BrickLink::Color *color = nullptr);
|
||||
void setItems(const BrickLink::InvItemList &list);
|
||||
void setItems(const LotList &lots);
|
||||
|
||||
QSize minimumSizeHint() const override;
|
||||
QSize sizeHint() const override;
|
||||
|
||||
+44
-32
@@ -991,16 +991,19 @@ Item *Core::readItemFromDatabase(QDataStream &dataStream, DatabaseVersion)
|
||||
dataStream >> consists;
|
||||
|
||||
if (consists) {
|
||||
auto *ptr = new quint64 [consists + 1];
|
||||
item->m_consists_of = ptr;
|
||||
item->m_consists_of.resize(consists);
|
||||
union {
|
||||
quint64 ui64;
|
||||
Item::ConsistsOf co;
|
||||
} u;
|
||||
|
||||
*ptr++ = consists;
|
||||
|
||||
for (quint32 i = 0; i < consists; i++)
|
||||
dataStream >> *ptr++;
|
||||
for (quint32 i = 0; i < consists; ++i) {
|
||||
dataStream >> u.ui64;
|
||||
item->m_consists_of[i] = u.co;
|
||||
}
|
||||
}
|
||||
else
|
||||
item->m_consists_of = nullptr;
|
||||
item->m_consists_of.clear();
|
||||
|
||||
quint32 known_colors_count;
|
||||
dataStream >> known_colors_count;
|
||||
@@ -1036,13 +1039,17 @@ void Core::writeItemToDatabase(const Item *item, QDataStream &dataStream, Databa
|
||||
else
|
||||
dataStream << quint32(0);
|
||||
|
||||
if (item->m_consists_of && item->m_consists_of [0]) {
|
||||
dataStream << quint32(item->m_consists_of [0]);
|
||||
if (!item->m_consists_of.empty()) {
|
||||
dataStream << quint32(item->m_consists_of.size());
|
||||
union {
|
||||
quint64 ui64;
|
||||
Item::ConsistsOf co;
|
||||
} u;
|
||||
|
||||
quint64 *ptr = item->m_consists_of + 1;
|
||||
|
||||
for (quint32 i = 0; i < quint32(item->m_consists_of [0]); i++)
|
||||
dataStream << *ptr++;
|
||||
for (quint32 i = 0; i < quint32(item->m_consists_of.size()); ++i) {
|
||||
u.co = item->m_consists_of.at(i);
|
||||
dataStream << u.ui64;
|
||||
}
|
||||
}
|
||||
else
|
||||
dataStream << quint32(0);
|
||||
@@ -1077,21 +1084,20 @@ void Core::writePCCToDatabase(const PartColorCode *pcc,
|
||||
}
|
||||
|
||||
|
||||
bool Core::applyChangeLogToItem(InvItem *item)
|
||||
bool Core::applyChangeLog(const Item *&item, const Color *&color, Incomplete *inc)
|
||||
{
|
||||
const InvItem::Incomplete *incpl = item->isIncomplete();
|
||||
if (!incpl)
|
||||
if (!inc)
|
||||
return false;
|
||||
|
||||
const Item *fixed_item = item->item();
|
||||
const Color *fixed_color = item->color();
|
||||
const Item *fixed_item = item;
|
||||
const Color *fixed_color = color;
|
||||
|
||||
QString itemtypeid = incpl->m_itemtype_id;
|
||||
QString itemid = incpl->m_item_id;
|
||||
QString colorid = incpl->m_color_name;
|
||||
QString itemtypeid = inc->m_itemtype_id;
|
||||
QString itemid = inc->m_item_id;
|
||||
QString colorid = inc->m_color_name;
|
||||
|
||||
if (itemtypeid.isEmpty() && !incpl->m_itemtype_name.isEmpty())
|
||||
itemtypeid = incpl->m_itemtype_name.at(0).toUpper();
|
||||
if (itemtypeid.isEmpty() && !inc->m_itemtype_name.isEmpty())
|
||||
itemtypeid = inc->m_itemtype_name.at(0).toUpper();
|
||||
|
||||
for (int i = int(m_changelog.size()) - 1; i >= 0 && !(fixed_color && fixed_item); --i) {
|
||||
const ChangeLogEntry &cl = ChangeLogEntry(m_changelog.at(size_t(i)));
|
||||
@@ -1124,16 +1130,12 @@ bool Core::applyChangeLogToItem(InvItem *item)
|
||||
}
|
||||
}
|
||||
|
||||
if (fixed_item && !item->item())
|
||||
item->setItem(fixed_item);
|
||||
if (fixed_color && !item->color())
|
||||
item->setColor(fixed_color);
|
||||
if (fixed_item && !item)
|
||||
item = fixed_item;
|
||||
if (fixed_color && !color)
|
||||
color = fixed_color;
|
||||
|
||||
if (fixed_item && fixed_color) {
|
||||
item->setIncomplete(nullptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return (fixed_item && fixed_color);
|
||||
}
|
||||
|
||||
qreal Core::itemImageScaleFactor() const
|
||||
@@ -1179,6 +1181,16 @@ const QVector<const Color *> Item::knownColors() const
|
||||
return result;
|
||||
}
|
||||
|
||||
const Item *Item::ConsistsOf::item() const
|
||||
{
|
||||
return BrickLink::core()->items().at(m_index);
|
||||
}
|
||||
|
||||
const Color *Item::ConsistsOf::color() const
|
||||
{
|
||||
return BrickLink::core()->color(m_color);
|
||||
}
|
||||
|
||||
} // namespace BrickLink
|
||||
|
||||
#include "moc_bricklink.cpp"
|
||||
|
||||
+56
-212
@@ -26,7 +26,6 @@
|
||||
#include <QMap>
|
||||
#include <QPair>
|
||||
#include <QUrl>
|
||||
#include <QMimeData>
|
||||
#include <QAbstractListModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QMutex>
|
||||
@@ -191,7 +190,42 @@ public:
|
||||
~Item();
|
||||
|
||||
AppearsIn appearsIn(const Color *color = nullptr) const;
|
||||
InvItemList consistsOf() const;
|
||||
|
||||
class ConsistsOf {
|
||||
public:
|
||||
const Item *item() const;
|
||||
const Color *color() const;
|
||||
int quantity() const { return m_qty; }
|
||||
bool isExtra() const { return m_extra; }
|
||||
bool isAlternate() const { return m_isalt; }
|
||||
int alternateId() const { return m_altid; }
|
||||
bool isCounterPart() const { return m_cpart; }
|
||||
|
||||
private:
|
||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
quint64 m_qty : 12;
|
||||
quint64 m_index : 20;
|
||||
quint64 m_color : 12;
|
||||
quint64 m_extra : 1;
|
||||
quint64 m_isalt : 1;
|
||||
quint64 m_altid : 6;
|
||||
quint64 m_cpart : 1;
|
||||
quint64 m_reserved : 11;
|
||||
#else
|
||||
quint64 m_reserved : 11;
|
||||
quint64 m_cpart : 1;
|
||||
quint64 m_altid : 6;
|
||||
quint64 m_isalt : 1;
|
||||
quint64 m_extra : 1;
|
||||
quint64 m_color : 12;
|
||||
quint64 m_index : 20;
|
||||
quint64 m_qty : 12;
|
||||
#endif
|
||||
|
||||
friend class TextImport;
|
||||
};
|
||||
|
||||
const QVector<ConsistsOf> &consistsOf() const;
|
||||
|
||||
uint index() const { return m_index; } // only for internal use (picture/priceguide hashes)
|
||||
|
||||
@@ -210,14 +244,14 @@ private:
|
||||
QVector<uint> m_known_colors;
|
||||
|
||||
mutable quint32 * m_appears_in = nullptr;
|
||||
mutable quint64 * m_consists_of = nullptr;
|
||||
QVector<ConsistsOf> m_consists_of;
|
||||
|
||||
private:
|
||||
Item() = default;
|
||||
Q_DISABLE_COPY(Item)
|
||||
|
||||
void setAppearsIn(const AppearsIn &hash) const;
|
||||
void setConsistsOf(const InvItemList &items) const;
|
||||
void setConsistsOf(const QVector<ConsistsOf> &items);
|
||||
|
||||
struct appears_in_record {
|
||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
@@ -229,28 +263,6 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
struct consists_of_record {
|
||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
quint64 m_qty : 12;
|
||||
quint64 m_index : 20;
|
||||
quint64 m_color : 12;
|
||||
quint64 m_extra : 1;
|
||||
quint64 m_isalt : 1;
|
||||
quint64 m_altid : 6;
|
||||
quint64 m_cpart : 1;
|
||||
quint64 m_reserved : 11;
|
||||
#else
|
||||
quint64 m_reserved : 11;
|
||||
quint64 m_cpart : 1;
|
||||
quint64 m_altid : 6;
|
||||
quint64 m_isalt : 1;
|
||||
quint64 m_extra : 1;
|
||||
quint64 m_color : 12;
|
||||
quint64 m_index : 20;
|
||||
quint64 m_qty : 12;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int compare(const Item **a, const Item **b);
|
||||
static bool lowerBound(const Item *item, const std::pair<char, QString> &ids);
|
||||
|
||||
@@ -324,189 +336,6 @@ private:
|
||||
friend class PictureLoaderJob;
|
||||
};
|
||||
|
||||
class InvItem
|
||||
{
|
||||
public:
|
||||
InvItem(const Color *color = nullptr, const Item *item = nullptr);
|
||||
InvItem(const InvItem ©);
|
||||
~InvItem();
|
||||
|
||||
InvItem &operator=(const InvItem ©);
|
||||
bool operator==(const InvItem &cmp) const;
|
||||
bool operator!=(const InvItem &cmp) const;
|
||||
|
||||
const Item *item() const { return m_item; }
|
||||
void setItem(const Item *i) { m_item = i; }
|
||||
const Category *category() const { return m_item ? m_item->category() : nullptr; }
|
||||
const ItemType *itemType() const { return m_item ? m_item->itemType() : nullptr; }
|
||||
const Color *color() const { return m_color; }
|
||||
void setColor(const Color *c) { m_color = c; }
|
||||
|
||||
QString itemId() const { return m_item ? m_item->id()
|
||||
: (m_incomplete ? m_incomplete->m_item_id
|
||||
: QString()); }
|
||||
QString itemName() const { return m_item ? m_item->name()
|
||||
: (m_incomplete ? m_incomplete->m_item_name
|
||||
: QString()); }
|
||||
QString colorId() const { return m_color ? QString::number(m_color->id())
|
||||
: (m_incomplete ? m_incomplete->m_color_id
|
||||
: QString()); }
|
||||
QString colorName() const { return m_color ? m_color->name()
|
||||
: (m_incomplete ? m_incomplete->m_color_name
|
||||
: QString()); }
|
||||
QString categoryId() const { return category() ? QString::number(category()->id())
|
||||
: (m_incomplete ? m_incomplete->m_category_id
|
||||
: QString()); }
|
||||
QString categoryName() const { return category() ? category()->name()
|
||||
: (m_incomplete ? m_incomplete->m_category_name
|
||||
: QString()); }
|
||||
QString itemTypeId() const { return itemType() ? QString(QChar(itemType()->id()))
|
||||
: (m_incomplete ? m_incomplete->m_itemtype_id
|
||||
: QString()); }
|
||||
QString itemTypeName() const { return itemType() ? itemType()->name()
|
||||
: (m_incomplete ? m_incomplete->m_itemtype_name
|
||||
: QString()); }
|
||||
int itemYearReleased() const { return m_item ? m_item->yearReleased() : 0; }
|
||||
|
||||
Status status() const { return m_status; }
|
||||
void setStatus(Status s) { m_status = s; }
|
||||
Condition condition() const { return m_condition; }
|
||||
void setCondition(Condition c) { m_condition = c; }
|
||||
SubCondition subCondition() const { return m_scondition; }
|
||||
void setSubCondition(SubCondition c) { m_scondition = c; }
|
||||
QString comments() const { return m_comments; }
|
||||
void setComments(const QString &n) { m_comments = n; }
|
||||
QString remarks() const { return m_remarks; }
|
||||
void setRemarks(const QString &r) { m_remarks = r; }
|
||||
|
||||
int quantity() const { return m_quantity; }
|
||||
void setQuantity(int q) { m_quantity = q; }
|
||||
int bulkQuantity() const { return m_bulk_quantity; }
|
||||
void setBulkQuantity(int q) { m_bulk_quantity = qMax(1, q); }
|
||||
int tierQuantity(int i) const { return m_tier_quantity [qBound(0, i, 2)]; }
|
||||
void setTierQuantity(int i, int q) { m_tier_quantity [qBound(0, i, 2)] = q; }
|
||||
double price() const { return m_price; }
|
||||
void setPrice(double p) { m_price = p; }
|
||||
double tierPrice(int i) const { return m_tier_price[qBound(0, i, 2)]; }
|
||||
void setTierPrice(int i, double p) { m_tier_price[qBound(0, i, 2)] = p; }
|
||||
|
||||
int sale() const { return m_sale; }
|
||||
void setSale(int s) { m_sale = qMax(-99, qMin(100, s)); }
|
||||
double total() const { return m_price * m_quantity; }
|
||||
void setCost(double c) { m_cost = c; }
|
||||
double cost() const { return m_cost; }
|
||||
|
||||
uint lotId() const { return m_lot_id; }
|
||||
void setLotId(uint lid) { m_lot_id = lid; }
|
||||
|
||||
bool retain() const { return m_retain; }
|
||||
void setRetain(bool r) { m_retain = r; }
|
||||
Stockroom stockroom() const { return m_stockroom; }
|
||||
void setStockroom(Stockroom sr) { m_stockroom = sr; }
|
||||
|
||||
bool hasCustomWeight() const { return (m_weight > 0); }
|
||||
double weight() const { return hasCustomWeight() ? m_weight : (m_item ? m_item->weight() : 0); }
|
||||
double totalWeight() const { return weight() * quantity(); }
|
||||
void setWeight(double w) { m_weight = (w <= 0) ? 0 : w; }
|
||||
void setTotalWeight(double w) { m_weight = (w <= 0) ? 0 : (w / (m_quantity ? qAbs(m_quantity) : 1)); }
|
||||
|
||||
QString reserved() const { return m_reserved; }
|
||||
void setReserved(const QString &r) { m_reserved = r; }
|
||||
|
||||
bool alternate() const { return m_alternate; }
|
||||
void setAlternate(bool a) { m_alternate = a; }
|
||||
uint alternateId() const { return m_alt_id; }
|
||||
void setAlternateId(uint aid) { m_alt_id = aid; }
|
||||
|
||||
bool counterPart() const { return m_cpart; }
|
||||
void setCounterPart(bool b) { m_cpart = b; }
|
||||
|
||||
// needed for the copy/merge template code -- std::bind doesn't work there
|
||||
int tierQuantity0() const { return tierQuantity(0); }
|
||||
int tierQuantity1() const { return tierQuantity(1); }
|
||||
int tierQuantity2() const { return tierQuantity(2); }
|
||||
void setTierQuantity0(int q) { setTierQuantity(0, q); }
|
||||
void setTierQuantity1(int q) { setTierQuantity(1, q); }
|
||||
void setTierQuantity2(int q) { setTierQuantity(2, q); }
|
||||
double tierPrice0() const { return tierPrice(0); }
|
||||
double tierPrice1() const { return tierPrice(1); }
|
||||
double tierPrice2() const { return tierPrice(2); }
|
||||
void setTierPrice0(double p) { setTierPrice(0, p); }
|
||||
void setTierPrice1(double p) { setTierPrice(1, p); }
|
||||
void setTierPrice2(double p) { setTierPrice(2, p); }
|
||||
|
||||
struct Incomplete {
|
||||
QString m_item_id;
|
||||
QString m_item_name;
|
||||
QString m_itemtype_id;
|
||||
QString m_itemtype_name;
|
||||
QString m_color_id;
|
||||
QString m_color_name;
|
||||
QString m_category_id;
|
||||
QString m_category_name;
|
||||
|
||||
bool operator==(const Incomplete &other) const; //TODO: = default in C++20
|
||||
};
|
||||
|
||||
Incomplete *isIncomplete() const { return m_incomplete.data(); }
|
||||
void setIncomplete(Incomplete *inc) { m_incomplete.reset(inc); }
|
||||
|
||||
bool mergeFrom(const InvItem &merge, bool useCostQtyAg = false);
|
||||
|
||||
void save(QDataStream &ds) const;
|
||||
static InvItem *restore(QDataStream &ds);
|
||||
|
||||
private:
|
||||
const Item * m_item;
|
||||
const Color * m_color;
|
||||
|
||||
QScopedPointer<Incomplete> m_incomplete;
|
||||
|
||||
Status m_status : 3;
|
||||
Condition m_condition : 2;
|
||||
SubCondition m_scondition: 3;
|
||||
bool m_retain : 1;
|
||||
Stockroom m_stockroom : 5;
|
||||
bool m_alternate : 1;
|
||||
uint m_alt_id : 6;
|
||||
bool m_cpart : 1;
|
||||
|
||||
uint m_lot_id = 0;
|
||||
QString m_reserved;
|
||||
|
||||
QString m_comments;
|
||||
QString m_remarks;
|
||||
|
||||
int m_quantity = 0;
|
||||
int m_bulk_quantity = 1;
|
||||
int m_tier_quantity[3] = { 0, 0, 0 };
|
||||
int m_sale = 0;
|
||||
|
||||
double m_price = 0;
|
||||
double m_cost = 0;
|
||||
double m_tier_price[3] = { 0, 0, 0 };
|
||||
|
||||
double m_weight = 0;
|
||||
|
||||
friend class Core;
|
||||
};
|
||||
|
||||
class InvItemMimeData : public QMimeData
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InvItemMimeData(const InvItemList &items);
|
||||
|
||||
QStringList formats() const override;
|
||||
bool hasFormat(const QString &mimeType) const override;
|
||||
|
||||
void setItems(const InvItemList &items);
|
||||
static InvItemList items(const QMimeData *md);
|
||||
|
||||
private:
|
||||
static const char *s_mimetype;
|
||||
};
|
||||
|
||||
class Order
|
||||
{
|
||||
public:
|
||||
@@ -767,11 +596,27 @@ private:
|
||||
std::vector<QByteArray> m_changelog;
|
||||
std::vector<const PartColorCode *> m_pccs;
|
||||
QHash<const Item *, AppearsIn> m_appears_in_hash;
|
||||
QHash<const Item *, InvItemList> m_consists_of_hash;
|
||||
QHash<const Item *, QVector<Item::ConsistsOf>> m_consists_of_hash;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class Incomplete
|
||||
{
|
||||
public:
|
||||
QString m_item_id;
|
||||
QString m_item_name;
|
||||
QString m_itemtype_id;
|
||||
QString m_itemtype_name;
|
||||
QString m_color_id;
|
||||
QString m_color_name;
|
||||
QString m_category_id;
|
||||
QString m_category_name;
|
||||
|
||||
bool operator==(const Incomplete &other) const; //TODO: = default in C++20
|
||||
};
|
||||
|
||||
|
||||
class Core : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -830,7 +675,7 @@ public:
|
||||
QString ldrawDataPath() const;
|
||||
void setLDrawDataPath(const QString &ldrawDataPath);
|
||||
|
||||
bool applyChangeLogToItem(BrickLink::InvItem *item);
|
||||
bool applyChangeLog(const Item *&item, const Color *&color, Incomplete *inc);
|
||||
|
||||
bool onlineStatus() const;
|
||||
|
||||
@@ -951,7 +796,6 @@ Q_DECLARE_METATYPE(const BrickLink::Color *)
|
||||
Q_DECLARE_METATYPE(const BrickLink::Category *)
|
||||
Q_DECLARE_METATYPE(const BrickLink::ItemType *)
|
||||
Q_DECLARE_METATYPE(const BrickLink::Item *)
|
||||
Q_DECLARE_METATYPE(const BrickLink::InvItem *)
|
||||
Q_DECLARE_METATYPE(const BrickLink::AppearsInItem *)
|
||||
Q_DECLARE_METATYPE(const BrickLink::Order *)
|
||||
Q_DECLARE_METATYPE(const BrickLink::Cart *)
|
||||
|
||||
@@ -6,7 +6,6 @@ DEPENDPATH += $$RELPWD
|
||||
HEADERS += \
|
||||
$$PWD/bricklink.h \
|
||||
$$PWD/bricklinkfwd.h \
|
||||
$$PWD/bricklink_setmatch.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/bricklink.cpp \
|
||||
@@ -14,7 +13,6 @@ SOURCES += \
|
||||
$$PWD/bricklink_textimport.cpp \
|
||||
$$PWD/bricklink_priceguide.cpp \
|
||||
$$PWD/bricklink_picture.cpp \
|
||||
$$PWD/bricklink_setmatch.cpp \
|
||||
|
||||
!backend-only {
|
||||
|
||||
|
||||
Regular → Executable
+5
-358
@@ -58,7 +58,6 @@ QSize BrickLink::ItemType::rawPictureSize() const
|
||||
BrickLink::Item::~Item()
|
||||
{
|
||||
delete [] m_appears_in;
|
||||
delete [] m_consists_of;
|
||||
}
|
||||
|
||||
bool BrickLink::Item::lowerBound(const Item *item, const std::pair<char, QString> &ids)
|
||||
@@ -147,234 +146,17 @@ BrickLink::AppearsIn BrickLink::Item::appearsIn(const Color *only_color) const
|
||||
return map;
|
||||
}
|
||||
|
||||
void BrickLink::Item::setConsistsOf(const InvItemList &items) const
|
||||
void BrickLink::Item::setConsistsOf(const QVector<BrickLink::Item::ConsistsOf> &items)
|
||||
{
|
||||
delete [] m_consists_of;
|
||||
m_consists_of = items;
|
||||
|
||||
_qwords_for_consists += (items.count() + 1);
|
||||
|
||||
auto *ptr = new quint64 [size_t(items.count()) + 1];
|
||||
m_consists_of = ptr;
|
||||
|
||||
*ptr++ = quint32(items.count()); // how many entries
|
||||
|
||||
for (const InvItem *item : items) {
|
||||
auto *entry = reinterpret_cast <consists_of_record *>(ptr);
|
||||
|
||||
if (item->item() && item->color() && item->quantity()) {
|
||||
entry->m_qty = uint(item->quantity());
|
||||
entry->m_index = item->item()->m_index;
|
||||
entry->m_color = item->color()->id();
|
||||
entry->m_extra = (item->status() == BrickLink::Status::Extra) ? 1 : 0;
|
||||
entry->m_isalt = item->alternate();
|
||||
entry->m_altid = item->alternateId();
|
||||
entry->m_cpart = item->counterPart();
|
||||
entry->m_reserved = 0;
|
||||
ptr++;
|
||||
}
|
||||
else
|
||||
m_consists_of [0]--;
|
||||
}
|
||||
}
|
||||
|
||||
BrickLink::InvItemList BrickLink::Item::consistsOf() const
|
||||
const QVector<BrickLink::Item::ConsistsOf> &BrickLink::Item::consistsOf() const
|
||||
{
|
||||
InvItemList list;
|
||||
|
||||
const BrickLink::Item * const *items = BrickLink::core()->items().data();
|
||||
auto count = BrickLink::core()->items().size();
|
||||
|
||||
if (m_consists_of) {
|
||||
const quint64 *ptr = m_consists_of + 1;
|
||||
|
||||
for (uint i = 0; i < uint(m_consists_of[0]); i++) {
|
||||
const auto *entry = reinterpret_cast <const consists_of_record *>(ptr);
|
||||
ptr++;
|
||||
|
||||
const BrickLink::Color *color = BrickLink::core()->color(entry->m_color);
|
||||
const BrickLink::Item *item = (entry->m_index < count) ? items [entry->m_index] : nullptr;
|
||||
|
||||
if (color && item) {
|
||||
auto *ii = new InvItem(color, item);
|
||||
ii->setQuantity(entry->m_qty);
|
||||
if (entry->m_extra)
|
||||
ii->setStatus(BrickLink::Status::Extra);
|
||||
ii->setAlternate(entry->m_isalt);
|
||||
ii->setAlternateId(entry->m_altid);
|
||||
ii->setCounterPart(entry->m_cpart);
|
||||
|
||||
list.append(ii);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
return m_consists_of;
|
||||
}
|
||||
|
||||
|
||||
BrickLink::InvItem::InvItem(const Color *color, const Item *item)
|
||||
{
|
||||
m_item = item;
|
||||
m_color = color;
|
||||
|
||||
//TODO: replace with member initializers when switching to c++20
|
||||
m_status = Status::Include;
|
||||
m_condition = Condition::New;
|
||||
m_scondition = SubCondition::None;
|
||||
m_retain = false;
|
||||
m_stockroom = Stockroom::None;
|
||||
m_alternate = false;
|
||||
m_alt_id = 0;
|
||||
m_cpart = false;
|
||||
}
|
||||
|
||||
BrickLink::InvItem::InvItem(const BrickLink::InvItem ©)
|
||||
{
|
||||
*this = copy;
|
||||
}
|
||||
|
||||
BrickLink::InvItem &BrickLink::InvItem::operator=(const InvItem ©)
|
||||
{
|
||||
if (this == ©)
|
||||
return *this;
|
||||
|
||||
m_item = copy.m_item;
|
||||
m_color = copy.m_color;
|
||||
|
||||
m_incomplete.reset(copy.m_incomplete ? new Incomplete(*copy.m_incomplete.get()) : nullptr);
|
||||
|
||||
m_status = copy.m_status;
|
||||
m_condition = copy.m_condition;
|
||||
m_scondition = copy.m_scondition;
|
||||
m_retain = copy.m_retain;
|
||||
m_stockroom = copy.m_stockroom;
|
||||
m_alternate = copy.m_alternate;
|
||||
m_alt_id = copy.m_alt_id;
|
||||
m_cpart = copy.m_cpart;
|
||||
m_lot_id = copy.m_lot_id;
|
||||
m_reserved = copy.m_reserved;
|
||||
m_comments = copy.m_comments;
|
||||
m_remarks = copy.m_remarks;
|
||||
m_quantity = copy.m_quantity;
|
||||
m_bulk_quantity = copy.m_bulk_quantity;
|
||||
m_tier_quantity[0] = copy.m_tier_quantity[0];
|
||||
m_tier_quantity[1] = copy.m_tier_quantity[1];
|
||||
m_tier_quantity[2] = copy.m_tier_quantity[2];
|
||||
m_sale = copy.m_sale;
|
||||
m_price = copy.m_price;
|
||||
m_cost = copy.m_cost;
|
||||
m_tier_price[0] = copy.m_tier_price[0];
|
||||
m_tier_price[1] = copy.m_tier_price[1];
|
||||
m_tier_price[2] = copy.m_tier_price[2];
|
||||
m_weight = copy.m_weight;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool BrickLink::InvItem::operator!=(const InvItem &cmp) const
|
||||
{
|
||||
return !operator==(cmp);
|
||||
}
|
||||
|
||||
bool BrickLink::InvItem::operator==(const InvItem &cmp) const
|
||||
{
|
||||
return (!m_incomplete && !cmp.m_incomplete)
|
||||
&& (m_item == cmp.m_item)
|
||||
&& (m_color == cmp.m_color)
|
||||
&& (m_status == cmp.m_status)
|
||||
&& (m_condition == cmp.m_condition)
|
||||
&& (m_scondition == cmp.m_scondition)
|
||||
&& (m_retain == cmp.m_retain)
|
||||
&& (m_stockroom == cmp.m_stockroom)
|
||||
&& (m_lot_id == cmp.m_lot_id)
|
||||
&& (m_reserved == cmp.m_reserved)
|
||||
&& (m_comments == cmp.m_comments)
|
||||
&& (m_remarks == cmp.m_remarks)
|
||||
&& (m_quantity == cmp.m_quantity)
|
||||
&& (m_bulk_quantity == cmp.m_bulk_quantity)
|
||||
&& (m_tier_quantity[0] == cmp.m_tier_quantity[0])
|
||||
&& (m_tier_quantity[1] == cmp.m_tier_quantity[1])
|
||||
&& (m_tier_quantity[2] == cmp.m_tier_quantity[2])
|
||||
&& (m_sale == cmp.m_sale)
|
||||
&& qFuzzyCompare(m_price, cmp.m_price)
|
||||
&& qFuzzyCompare(m_cost, cmp.m_cost)
|
||||
&& qFuzzyCompare(m_tier_price[0], cmp.m_tier_price[0])
|
||||
&& qFuzzyCompare(m_tier_price[1], cmp.m_tier_price[1])
|
||||
&& qFuzzyCompare(m_tier_price[2], cmp.m_tier_price[2])
|
||||
&& qFuzzyCompare(m_weight, cmp.m_weight);
|
||||
}
|
||||
|
||||
BrickLink::InvItem::~InvItem()
|
||||
{ }
|
||||
|
||||
bool BrickLink::InvItem::mergeFrom(const InvItem &from, bool useCostQtyAg)
|
||||
{
|
||||
if ((&from == this) ||
|
||||
(from.isIncomplete() || isIncomplete()) ||
|
||||
(from.item() != item()) ||
|
||||
(from.color() != color()) ||
|
||||
(from.condition() != condition()) ||
|
||||
(from.subCondition() != subCondition()))
|
||||
return false;
|
||||
|
||||
if (useCostQtyAg) {
|
||||
setCost((cost() * quantity() + from.cost() * from.quantity()) / (quantity() + from.quantity()));
|
||||
} else if (!qFuzzyIsNull(from.cost()) && qFuzzyIsNull(cost())) {
|
||||
setCost(from.cost());
|
||||
}
|
||||
setQuantity(quantity() + from.quantity());
|
||||
|
||||
if (!qFuzzyIsNull(from.price()) && qFuzzyIsNull(price()))
|
||||
setPrice(from.price());
|
||||
if ((from.bulkQuantity() != 1) && (bulkQuantity() == 1))
|
||||
setBulkQuantity(from.bulkQuantity());
|
||||
if ((from.sale()) && !sale())
|
||||
setSale(from.sale());
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!qFuzzyIsNull(from.tierPrice(i)) && qFuzzyIsNull(tierPrice(i)))
|
||||
setTierPrice(i, from.tierPrice(i));
|
||||
if (from.tierQuantity(i) && !tierQuantity(i))
|
||||
setTierQuantity(i, from.tierQuantity(i));
|
||||
}
|
||||
|
||||
if (!from.remarks().isEmpty() && !remarks().isEmpty() && (from.remarks() != remarks())) {
|
||||
QRegularExpression fromRe { u"\\b" % QRegularExpression::escape(from.remarks()) % u"\\b" };
|
||||
|
||||
if (!fromRe.match(remarks()).hasMatch()) {
|
||||
QRegularExpression thisRe { u"\\b" % QRegularExpression::escape(remarks()) % u"\\b" };
|
||||
|
||||
if (thisRe.match(from.remarks()).hasMatch())
|
||||
setRemarks(from.remarks());
|
||||
else
|
||||
setRemarks(remarks() % u" " % from.remarks());
|
||||
}
|
||||
} else if (!from.remarks().isEmpty()) {
|
||||
setRemarks(from.remarks());
|
||||
}
|
||||
|
||||
if (!from.comments().isEmpty() && !comments().isEmpty() && (from.comments() != comments())) {
|
||||
QRegularExpression fromRe { u"\\b" % QRegularExpression::escape(from.comments()) % u"\\b" };
|
||||
|
||||
if (!fromRe.match(comments()).hasMatch()) {
|
||||
QRegularExpression thisRe { u"\\b" % QRegularExpression::escape(comments()) % u"\\b" };
|
||||
|
||||
if (thisRe.match(from.comments()).hasMatch())
|
||||
setComments(from.comments());
|
||||
else
|
||||
setComments(comments() % u" " % from.comments());
|
||||
}
|
||||
} else if (!from.comments().isEmpty()) {
|
||||
setComments(from.comments());
|
||||
}
|
||||
|
||||
if (!from.reserved().isEmpty() && reserved().isEmpty())
|
||||
setReserved(from.reserved());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BrickLink::InvItem::Incomplete::operator==(const BrickLink::InvItem::Incomplete &other) const
|
||||
bool BrickLink::Incomplete::operator==(const BrickLink::Incomplete &other) const
|
||||
{
|
||||
return m_item_id == other.m_item_id
|
||||
&& m_item_name == other.m_item_name
|
||||
@@ -386,141 +168,6 @@ bool BrickLink::InvItem::Incomplete::operator==(const BrickLink::InvItem::Incomp
|
||||
&& m_category_name == other.m_category_name;
|
||||
}
|
||||
|
||||
void BrickLink::InvItem::save(QDataStream &ds) const
|
||||
{
|
||||
ds << QByteArray("II") << qint32(2)
|
||||
<< itemId()
|
||||
<< qint8(itemType() ? itemType()->id() : char(-1))
|
||||
<< uint(color() ? color()->id() : uint(0xffffffff))
|
||||
<< qint8(m_status) << qint8(m_condition) << qint8(m_scondition) << qint8(m_retain ? 1 : 0)
|
||||
<< qint8(m_stockroom) << m_lot_id << m_reserved << m_comments << m_remarks
|
||||
<< m_quantity << m_bulk_quantity
|
||||
<< m_tier_quantity[0] << m_tier_quantity[1] << m_tier_quantity[2]
|
||||
<< m_sale << m_price << m_cost
|
||||
<< m_tier_price[0] << m_tier_price[1] << m_tier_price[2]
|
||||
<< m_weight;
|
||||
}
|
||||
|
||||
BrickLink::InvItem *BrickLink::InvItem::restore(QDataStream &ds)
|
||||
{
|
||||
QScopedPointer<InvItem> ii;
|
||||
|
||||
QByteArray tag;
|
||||
qint32 version;
|
||||
ds >> tag >> version;
|
||||
if ((ds.status() != QDataStream::Ok) || (tag != "II") || (version != 2))
|
||||
return nullptr;
|
||||
|
||||
QString itemid;
|
||||
uint colorid = 0;
|
||||
qint8 itemtypeid = 0;
|
||||
|
||||
ds >> itemid >> itemtypeid >> colorid;
|
||||
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return nullptr;
|
||||
|
||||
auto item = BrickLink::core()->item(itemtypeid, itemid);
|
||||
auto color = BrickLink::core()->color(colorid);
|
||||
|
||||
ii.reset(new BrickLink::InvItem(color, item));
|
||||
|
||||
if (!item || !color) {
|
||||
ii->m_incomplete.reset(new BrickLink::InvItem::Incomplete);
|
||||
if (!item) {
|
||||
ii->m_incomplete->m_item_id = itemid;
|
||||
ii->m_incomplete->m_itemtype_id = QLatin1Char(itemtypeid);
|
||||
}
|
||||
if (!color)
|
||||
ii->m_incomplete->m_color_name = QString::number(colorid);
|
||||
|
||||
BrickLink::core()->applyChangeLogToItem(ii.get());
|
||||
}
|
||||
|
||||
// alternate, cpart and altid are left out on purpose!
|
||||
|
||||
qint8 status = 0, cond = 0, scond = 0, retain = 0, stockroom = 0;
|
||||
ds >> status >> cond >> scond >> retain >> stockroom
|
||||
>> ii->m_lot_id >> ii->m_reserved >> ii->m_comments >> ii->m_remarks
|
||||
>> ii->m_quantity >> ii->m_bulk_quantity
|
||||
>> ii->m_tier_quantity[0] >> ii->m_tier_quantity[1] >> ii->m_tier_quantity[2]
|
||||
>> ii->m_sale >> ii->m_price >> ii->m_cost
|
||||
>> ii->m_tier_price[0] >> ii->m_tier_price[1] >> ii->m_tier_price[2]
|
||||
>> ii->m_weight;
|
||||
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return nullptr;
|
||||
|
||||
ii->m_status = static_cast<BrickLink::Status>(status);
|
||||
ii->m_condition = static_cast<BrickLink::Condition>(cond);
|
||||
ii->m_scondition = static_cast<BrickLink::SubCondition>(scond);
|
||||
ii->m_retain = (retain);
|
||||
ii->m_stockroom = static_cast<BrickLink::Stockroom>(stockroom);
|
||||
|
||||
return ii.take();
|
||||
}
|
||||
|
||||
const char *BrickLink::InvItemMimeData::s_mimetype = "application/x-bricklink-invitems";
|
||||
|
||||
BrickLink::InvItemMimeData::InvItemMimeData(const InvItemList &items)
|
||||
: QMimeData()
|
||||
{
|
||||
setItems(items);
|
||||
}
|
||||
|
||||
void BrickLink::InvItemMimeData::setItems(const InvItemList &items)
|
||||
{
|
||||
QByteArray data;
|
||||
QString text;
|
||||
|
||||
QDataStream ds(&data, QIODevice::WriteOnly);
|
||||
|
||||
ds << quint32(items.count());
|
||||
for (const InvItem *ii : items) {
|
||||
ii->save(ds);
|
||||
if (!text.isEmpty())
|
||||
text.append("\n");
|
||||
text.append(ii->itemId());
|
||||
}
|
||||
setText(text);
|
||||
setData(s_mimetype, data);
|
||||
}
|
||||
|
||||
BrickLink::InvItemList BrickLink::InvItemMimeData::items(const QMimeData *md)
|
||||
{
|
||||
InvItemList items;
|
||||
|
||||
if (md) {
|
||||
QByteArray data = md->data(s_mimetype);
|
||||
QDataStream ds(data);
|
||||
|
||||
if (!data.isEmpty()) {
|
||||
quint32 count = 0;
|
||||
ds >> count;
|
||||
|
||||
for (; count && !ds.atEnd(); count--) {
|
||||
if (auto item = BrickLink::InvItem::restore(ds))
|
||||
items.append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
QStringList BrickLink::InvItemMimeData::formats() const
|
||||
{
|
||||
static QStringList sl;
|
||||
|
||||
if (sl.isEmpty())
|
||||
sl << s_mimetype << "text/plain";
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
bool BrickLink::InvItemMimeData::hasFormat(const QString &mimeType) const
|
||||
{
|
||||
return mimeType.compare(s_mimetype) || mimeType.compare("text/plain");
|
||||
}
|
||||
|
||||
BrickLink::Order::Order(const QString &id, OrderType type)
|
||||
: m_id(id)
|
||||
|
||||
@@ -715,15 +715,14 @@ bool BrickLink::ItemModel::filterAccepts(const void *pointer) const
|
||||
}
|
||||
for (const auto &c : m_filter_consistsOf) {
|
||||
bool found = false;
|
||||
const auto containslist = item->consistsOf();
|
||||
const auto &containslist = item->consistsOf();
|
||||
for (const auto &ci : containslist) {
|
||||
if ((ci->item() == c.second.first)
|
||||
&& (!c.second.second || (ci->color() == c.second.second))) {
|
||||
if ((ci.item() == c.second.first)
|
||||
&& (!c.second.second || (ci.color() == c.second.second))) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
qDeleteAll(containslist);
|
||||
match = match && (found == !c.first); // found xor negate
|
||||
}
|
||||
|
||||
@@ -737,22 +736,15 @@ bool BrickLink::ItemModel::filterAccepts(const void *pointer) const
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BrickLink::InternalAppearsInModel::InternalAppearsInModel(const Item *item, const Color *color, QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
BrickLink::InternalAppearsInModel::InternalAppearsInModel(const Item *item, const Color *color,
|
||||
QObject *parent)
|
||||
: InternalAppearsInModel({ { item, color } }, parent)
|
||||
{
|
||||
InvItemList list;
|
||||
InvItem invitem(color, item);
|
||||
list.append(&invitem);
|
||||
init(list);
|
||||
}
|
||||
|
||||
BrickLink::InternalAppearsInModel::InternalAppearsInModel(const InvItemList &list, QObject *parent)
|
||||
BrickLink::InternalAppearsInModel::InternalAppearsInModel(const QVector<QPair<const Item *,
|
||||
const Color *>> &list, QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
init(list);
|
||||
}
|
||||
|
||||
void BrickLink::InternalAppearsInModel::init(const InvItemList &list)
|
||||
{
|
||||
MODELTEST_ATTACH(this)
|
||||
|
||||
@@ -760,11 +752,11 @@ void BrickLink::InternalAppearsInModel::init(const InvItemList &list)
|
||||
bool first_item = true;
|
||||
bool single_item = (list.count() == 1);
|
||||
|
||||
for (const InvItem *invitem : list) {
|
||||
if (!invitem->item())
|
||||
for (const auto &p : list) {
|
||||
if (!p.first)
|
||||
continue;
|
||||
|
||||
const auto appearsvec = invitem->item()->appearsIn(invitem->color());
|
||||
const auto appearsvec = p.first->appearsIn(p.second);
|
||||
for (const AppearsInColor &vec : appearsvec) {
|
||||
for (const AppearsInItem &item : vec) {
|
||||
if (single_item) {
|
||||
@@ -860,7 +852,8 @@ QVariant BrickLink::InternalAppearsInModel::headerData(int section, Qt::Orientat
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
BrickLink::AppearsInModel::AppearsInModel(const BrickLink::InvItemList &list, QObject *parent)
|
||||
BrickLink::AppearsInModel::AppearsInModel(const QVector<QPair<const Item *, const Color *>> &list,
|
||||
QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
setSourceModel(new InternalAppearsInModel(list, this));
|
||||
|
||||
@@ -197,11 +197,9 @@ public:
|
||||
QModelIndex index(const AppearsInItem *const_ai) const;
|
||||
|
||||
protected:
|
||||
InternalAppearsInModel(const BrickLink::InvItemList &list, QObject *parent);
|
||||
InternalAppearsInModel(const QVector<QPair<const Item *, const Color *> > &list, QObject *parent);
|
||||
InternalAppearsInModel(const Item *item, const Color *color, QObject *parent);
|
||||
|
||||
void init(const InvItemList &list);
|
||||
|
||||
AppearsIn m_appearsin;
|
||||
QVector<AppearsInItem *> m_items;
|
||||
|
||||
@@ -212,7 +210,7 @@ class AppearsInModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AppearsInModel(const BrickLink::InvItemList &list, QObject *parent);
|
||||
AppearsInModel(const QVector<QPair<const Item *, const Color *> > &list, QObject *parent);
|
||||
AppearsInModel(const Item *item, const Color *color, QObject *parent);
|
||||
|
||||
using QSortFilterProxyModel::index;
|
||||
|
||||
@@ -1,364 +0,0 @@
|
||||
/* Copyright (C) 2004-2021 Robert Griebl. All rights reserved.
|
||||
**
|
||||
** This file is part of BrickStore.
|
||||
**
|
||||
** This file may be distributed and/or modified under the terms of the GNU
|
||||
** General Public License version 2 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this file.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
** See http://fsf.org/licensing/licenses/gpl.html for GPL licensing information.
|
||||
*/
|
||||
#include <QThread>
|
||||
|
||||
#include "bricklink_setmatch.h"
|
||||
|
||||
/*
|
||||
|
||||
What sets can be built (one at a time)
|
||||
What sets can be built (all at once)
|
||||
|
||||
|
||||
Greedy + Recursive:
|
||||
Constraints are handled in nextPossibleMatch
|
||||
|
||||
Greedy:
|
||||
Preferences are handled when sorting the array ("cost/weight")
|
||||
|
||||
Preferences:
|
||||
* Prefer big/small
|
||||
* Prefer old/new
|
||||
(* Prefer Category/ItemType --> would clutter UI)
|
||||
|
||||
Recursive:
|
||||
No Preferences possible
|
||||
NOT POSSIBLE anymore, because BL has too many items.
|
||||
|
||||
|
||||
Constraints:
|
||||
* part count: QPair<int, int>(min, max) (min, max == -1 -> don't care)
|
||||
* year released: QPair<int, int>(min, max) (min, max == -1 -> don't care)
|
||||
* category: QList<const Category *> (empty -> don't care)
|
||||
bool not_these_categories;
|
||||
* item type: QList<const ItemType *> (empty -> don't care)
|
||||
bool not_these_itemtypes;
|
||||
* price: WOULD BE COOL, BUT WE NEED A PRECALCULATED PRICE/SET DB FIRST!!
|
||||
|
||||
|
||||
--------------------------------------------------
|
||||
|
||||
Match your items against the BrickLink set inventory database:
|
||||
|
||||
Select constraints
|
||||
|
||||
|
||||
|
||||
Select algorithm to use [Greedy v] (i)
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
//QVector<QPair<const BrickLink::Item *, BrickLink::SetMatch::InvMatchList> > BrickLink::SetMatch::s_inventories;
|
||||
|
||||
BrickLink::SetMatch::InvMatchList::InvMatchList(const InvItemList &list)
|
||||
: InvMatchList()
|
||||
{
|
||||
add(list);
|
||||
}
|
||||
|
||||
void BrickLink::SetMatch::InvMatchList::add(const InvItemList &list)
|
||||
{
|
||||
for (const InvItem *ii : list) {
|
||||
if (!ii->isIncomplete() &&
|
||||
(ii->quantity() > 0) &&
|
||||
!ii->counterPart() &&
|
||||
(ii->status() == BrickLink::Status::Include) &&
|
||||
(!ii->alternateId() || !ii->alternate())) {
|
||||
InvMatchItem mi = { ii->item(), ii->color(), ii->quantity() };
|
||||
m_list << mi;
|
||||
|
||||
m_count += mi.qty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BrickLink::SetMatch::InvMatchList::subtract(const InvMatchList &list)
|
||||
{
|
||||
if (list.isEmpty() || list.count() > count())
|
||||
return false;
|
||||
|
||||
// for every item in the given set inventory
|
||||
for (const auto &item : list.m_list) {
|
||||
// we need that many parts in this step
|
||||
int qty_to_match = item.qty;
|
||||
|
||||
// quick check if there are at least those many parts left (regardless of type and color)
|
||||
if (m_count < qty_to_match)
|
||||
return false;
|
||||
|
||||
// for every item in the inventory
|
||||
//foreach (InvMatchItem &mi, m_list) {
|
||||
for (auto &matchItem : m_list) {
|
||||
// check if type and color matches
|
||||
if (item.item == matchItem.item && item.color == matchItem.color) {
|
||||
// can we satisfy this request with a single lot?
|
||||
if (qty_to_match > matchItem.qty) { // no
|
||||
qty_to_match -= matchItem.qty;
|
||||
m_count -= matchItem.qty;
|
||||
matchItem.qty = 0; // only a partial match, try again
|
||||
} else { // yes
|
||||
matchItem.qty -= qty_to_match;
|
||||
m_count -= qty_to_match;
|
||||
qty_to_match = 0;
|
||||
break; // we matched this item completely
|
||||
}
|
||||
}
|
||||
}
|
||||
// we couldn't match one of the items completely -> failure
|
||||
if (qty_to_match > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// we matched all items completely -> success
|
||||
return true;
|
||||
}
|
||||
|
||||
int BrickLink::SetMatch::InvMatchList::count() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
bool BrickLink::SetMatch::InvMatchList::isEmpty() const
|
||||
{
|
||||
return m_count == 0;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BrickLink::SetMatch::SetMatch(QObject *parent)
|
||||
: QObject(parent)
|
||||
{ }
|
||||
|
||||
bool BrickLink::SetMatch::isActive() const
|
||||
{
|
||||
return m_active;
|
||||
}
|
||||
|
||||
void BrickLink::SetMatch::setPartCountConstraint(int _min, int _max)
|
||||
{
|
||||
if (!isActive()) {
|
||||
m_part_min = _min;
|
||||
m_part_max = _max;
|
||||
}
|
||||
}
|
||||
|
||||
void BrickLink::SetMatch::setYearReleasedConstraint(int _min, int _max)
|
||||
{
|
||||
if (!isActive()) {
|
||||
m_year_min = _min;
|
||||
m_year_max = _max;
|
||||
}
|
||||
}
|
||||
|
||||
void BrickLink::SetMatch::setCategoryConstraint(const QVector<const Category *> &list)
|
||||
{
|
||||
if (!isActive())
|
||||
m_categories = list;
|
||||
}
|
||||
|
||||
void BrickLink::SetMatch::setItemTypeConstraint(const QVector<const ItemType *> &list)
|
||||
{
|
||||
if (!isActive())
|
||||
m_itemtypes = list;
|
||||
}
|
||||
|
||||
void BrickLink::SetMatch::setGreedyPreference(GreedyPreference p)
|
||||
{
|
||||
if (!isActive())
|
||||
m_prefer = p;
|
||||
}
|
||||
|
||||
namespace BrickLink {
|
||||
|
||||
class MatchThread : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MatchThread(bool all, BrickLink::SetMatch *sm, const BrickLink::InvItemList &list)
|
||||
: QThread(sm)
|
||||
, m_sm(sm)
|
||||
, m_list(list)
|
||||
, m_all(all)
|
||||
{ }
|
||||
|
||||
void run() override
|
||||
{
|
||||
QVector<const BrickLink::Item *> result;
|
||||
|
||||
if (m_all)
|
||||
result = m_sm->allPossibleSetMatch(m_list);
|
||||
else
|
||||
result = m_sm->maximumPossibleSetMatch(m_list);
|
||||
|
||||
emit m_sm->finished(result);
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
BrickLink::SetMatch *m_sm;
|
||||
const BrickLink::InvItemList m_list;
|
||||
bool m_all;
|
||||
};
|
||||
|
||||
} // namespace BrickLink
|
||||
|
||||
bool BrickLink::SetMatch::startMaximumPossibleSetMatch(const InvItemList &list, Algorithm a)
|
||||
{
|
||||
if (isActive())
|
||||
return false;
|
||||
m_active = true;
|
||||
|
||||
m_algorithm = a;
|
||||
|
||||
auto *mt = new MatchThread(false, this, list);
|
||||
mt->run();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BrickLink::SetMatch::startAllPossibleSetMatch(const InvItemList &list)
|
||||
{
|
||||
if (isActive())
|
||||
return false;
|
||||
m_active = true;
|
||||
|
||||
auto *mt = new MatchThread(true, this, list);
|
||||
mt->run();
|
||||
return true;
|
||||
}
|
||||
|
||||
QVector<const BrickLink::Item *> BrickLink::SetMatch::allPossibleSetMatch(const InvItemList &list)
|
||||
{
|
||||
InvMatchList parts(list);
|
||||
QVector<const Item *> result;
|
||||
int p = 0, pmax = int(m_inventories.count()), pstep = pmax / 100;
|
||||
|
||||
for (auto it = m_inventories.constBegin(); it != m_inventories.constEnd(); ++it) {
|
||||
InvMatchList parts_copy = parts;
|
||||
|
||||
if (parts_copy.subtract(it->second))
|
||||
result << it->first;
|
||||
|
||||
if ((++p % pstep) == 0)
|
||||
emit progress(p, pmax);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
QVector<const BrickLink::Item *> BrickLink::SetMatch::maximumPossibleSetMatch(const InvItemList &list)
|
||||
{
|
||||
create_inventory_list();
|
||||
|
||||
InvMatchList parts(list);
|
||||
QPair<int, QVector<const Item *>> result(list.count(), QVector<const Item *>());
|
||||
|
||||
m_step = 0;
|
||||
|
||||
qWarning("Starting set match using algorithm \'%s\'", m_algorithm == Greedy ? "greedy" : "???");
|
||||
|
||||
// greedy Knapsack algorithm O(n)
|
||||
if (m_algorithm == Greedy) {
|
||||
result = set_match_greedy(parts);
|
||||
}
|
||||
|
||||
qWarning("found a solution with %d parts left", result.first);
|
||||
return result.second;
|
||||
}
|
||||
|
||||
QPair<int, QVector<const BrickLink::Item *>> BrickLink::SetMatch::set_match_greedy(InvMatchList &parts)
|
||||
{
|
||||
QVector<const Item *> result;
|
||||
int p = 0, pmax = int(m_inventories.count()) * 2, pstep = pmax / 100;
|
||||
|
||||
// pass == 0: try to match one of each set
|
||||
// pass == 1: fill up with as many copies as possible
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
// try to get one of each
|
||||
for (auto it = m_inventories.constBegin(); it != m_inventories.constEnd(); ++it) {
|
||||
while (true) {
|
||||
InvMatchList parts_copy = parts;
|
||||
|
||||
// can we take this one?
|
||||
if (!parts_copy.subtract(it->second))
|
||||
break;
|
||||
|
||||
result << it->first;
|
||||
parts = parts_copy;
|
||||
|
||||
if (pass == 0)
|
||||
break; // only one copy this time
|
||||
}
|
||||
if ((++p % pstep) == 0)
|
||||
emit progress(p, pmax);
|
||||
}
|
||||
}
|
||||
emit progress(pmax, pmax);
|
||||
|
||||
return qMakePair(parts.count(), result);
|
||||
}
|
||||
|
||||
|
||||
void BrickLink::SetMatch::create_inventory_list()
|
||||
{
|
||||
clear_inventory_list();
|
||||
const auto &items = core()->items();
|
||||
|
||||
for (auto item : items) {
|
||||
bool ok = true;
|
||||
|
||||
ok = ok && (item->itemType()->hasInventories() && item->hasInventory());
|
||||
ok = ok && ((m_year_min == -1) || (item->yearReleased() >= m_year_min));
|
||||
ok = ok && ((m_year_max == -1) || (item->yearReleased() <= m_year_max));
|
||||
|
||||
ok = ok && (m_itemtypes.isEmpty() || m_itemtypes.contains(item->itemType()));
|
||||
|
||||
ok = ok && (m_categories.isEmpty() || m_categories.contains(item->category()));
|
||||
|
||||
if (ok) {
|
||||
InvMatchList iml(item->consistsOf());
|
||||
ok = ok && !iml.isEmpty();
|
||||
ok = ok && ((m_part_min == -1) || (iml.count() >= m_part_min));
|
||||
ok = ok && ((m_part_max == -1) || (iml.count() <= m_part_max));
|
||||
|
||||
if (ok) {
|
||||
m_inventories.append(qMakePair(item, iml));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_algorithm == Greedy) {
|
||||
qWarning() << "Sorting inventories for greedy matching";
|
||||
std::sort(m_inventories.begin(), m_inventories.end(), [this](const auto &p1, const auto &p2) {
|
||||
return m_prefer == PreferSmallerSets ? p1.second.count() < p2.second.count()
|
||||
: p1.second.count() > p2.second.count();
|
||||
|
||||
});
|
||||
}
|
||||
qWarning("InvMatchList has %d entries", int(m_inventories.count()));
|
||||
}
|
||||
|
||||
|
||||
void BrickLink::SetMatch::clear_inventory_list()
|
||||
{
|
||||
m_inventories.clear();
|
||||
}
|
||||
|
||||
#include "bricklink_setmatch.moc"
|
||||
|
||||
#include "moc_bricklink_setmatch.cpp"
|
||||
@@ -1,122 +0,0 @@
|
||||
/* Copyright (C) 2004-2021 Robert Griebl. All rights reserved.
|
||||
**
|
||||
** This file is part of BrickStore.
|
||||
**
|
||||
** This file may be distributed and/or modified under the terms of the GNU
|
||||
** General Public License version 2 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this file.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
** See http://fsf.org/licensing/licenses/gpl.html for GPL licensing information.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QBitArray>
|
||||
|
||||
#include "bricklink.h"
|
||||
|
||||
namespace BrickLink {
|
||||
|
||||
|
||||
class SetMatch : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
struct InvMatchItem
|
||||
{
|
||||
const Item *item;
|
||||
const Color *color;
|
||||
int qty;
|
||||
};
|
||||
|
||||
class InvMatchList
|
||||
{
|
||||
public:
|
||||
InvMatchList() = default;
|
||||
InvMatchList(const InvItemList &list);
|
||||
InvMatchList(const InvMatchList ©) = default;
|
||||
|
||||
InvMatchList &operator=(const InvMatchList ©) = default;
|
||||
|
||||
void add(const InvItemList &list);
|
||||
//bool subtract(const InvItemList &list);
|
||||
bool subtract(const InvMatchList &list);
|
||||
|
||||
int count() const;
|
||||
bool isEmpty() const;
|
||||
|
||||
friend class SetMatch;
|
||||
|
||||
private:
|
||||
QVector<InvMatchItem> m_list;
|
||||
int m_count = 0;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
enum Algorithm {
|
||||
Greedy
|
||||
};
|
||||
|
||||
SetMatch(QObject *parent = nullptr);
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
bool startAllPossibleSetMatch(const InvItemList &list);
|
||||
bool startMaximumPossibleSetMatch(const InvItemList &list, Algorithm a = Greedy);
|
||||
|
||||
void setPartCountConstraint(int _min, int _max);
|
||||
void setYearReleasedConstraint(int _min, int _max);
|
||||
void setCategoryConstraint(const QVector<const Category *> &list);
|
||||
void setItemTypeConstraint(const QVector<const ItemType *> &list);
|
||||
|
||||
enum GreedyPreference {
|
||||
PreferLargerSets = 0,
|
||||
PreferSmallerSets = 1,
|
||||
};
|
||||
|
||||
void setGreedyPreference(GreedyPreference p);
|
||||
|
||||
signals:
|
||||
void finished(const QVector<const BrickLink::Item *> &);
|
||||
void progress(int, int);
|
||||
|
||||
protected:
|
||||
QVector<const Item *> allPossibleSetMatch(const InvItemList &list);
|
||||
QVector<const Item *> maximumPossibleSetMatch(const InvItemList &list);
|
||||
|
||||
friend class MatchThread;
|
||||
|
||||
private:
|
||||
QPair<int, QVector<const Item *>> set_match_greedy(InvMatchList &parts);
|
||||
|
||||
void clear_inventory_list();
|
||||
void create_inventory_list();
|
||||
|
||||
bool compare_pairs(const QPair<const Item *, InvMatchList> &p1, const QPair<const Item *, InvMatchList> &p2);
|
||||
|
||||
|
||||
private:
|
||||
Algorithm m_algorithm = Greedy;
|
||||
|
||||
// constraints
|
||||
int m_part_min = -1;
|
||||
int m_part_max = -1;
|
||||
int m_year_min = -1;
|
||||
int m_year_max = -1;
|
||||
QVector<const Category *> m_categories;
|
||||
QVector<const ItemType *> m_itemtypes;
|
||||
|
||||
// greedy preferences
|
||||
GreedyPreference m_prefer = PreferLargerSets;
|
||||
|
||||
// state
|
||||
int m_step = 0;
|
||||
QVector<QPair<const Item *, InvMatchList>> m_inventories;
|
||||
bool m_active = false;
|
||||
};
|
||||
|
||||
} //namespace BrickLink
|
||||
@@ -61,10 +61,7 @@ BrickLink::TextImport::TextImport()
|
||||
{ }
|
||||
|
||||
BrickLink::TextImport::~TextImport()
|
||||
{
|
||||
for (auto &&items : m_consists_of_hash)
|
||||
qDeleteAll(items);
|
||||
}
|
||||
{ }
|
||||
|
||||
bool BrickLink::TextImport::import(const QString &path)
|
||||
{
|
||||
@@ -319,11 +316,11 @@ bool BrickLink::TextImport::readInventory(const Item *item)
|
||||
if (!f || !f->isOpen() || (f->fileTime(QFileDevice::FileModificationTime) < item->inventoryUpdated()))
|
||||
return false;
|
||||
|
||||
InvItemList invItems;
|
||||
QVector<Item::ConsistsOf> inventory;
|
||||
|
||||
try {
|
||||
XmlHelpers::ParseXML p(f.take(), "INVENTORY", "ITEM");
|
||||
p.parse([this, &p, &invItems](QDomElement e) {
|
||||
p.parse([this, &p, &inventory](QDomElement e) {
|
||||
char itemTypeId = XmlHelpers::firstCharInString(p.elementText(e, "ITEMTYPE"));
|
||||
const QString itemId = p.elementText(e, "ITEMID");
|
||||
uint colorId = p.elementText(e, "COLOR").toUInt();
|
||||
@@ -339,26 +336,27 @@ bool BrickLink::TextImport::readInventory(const Item *item)
|
||||
if (!item || !color || !qty)
|
||||
throw Exception("Unknown item- or color-id or 0 qty");
|
||||
|
||||
auto *ii = new InvItem(color, item);
|
||||
ii->setQuantity(qty);
|
||||
ii->setStatus(extra ? Status::Extra : Status::Include);
|
||||
ii->setCounterPart(counterPart);
|
||||
ii->setAlternate(alternate);
|
||||
ii->setAlternateId(matchId);
|
||||
Item::ConsistsOf co;
|
||||
co.m_qty = qty;
|
||||
co.m_index = item->index();
|
||||
co.m_color = color->id();
|
||||
co.m_extra = extra;
|
||||
co.m_isalt = alternate;
|
||||
co.m_altid = matchId;
|
||||
co.m_cpart = counterPart;
|
||||
|
||||
invItems << ii;
|
||||
inventory.append(co);
|
||||
});
|
||||
|
||||
for (const InvItem *ii : qAsConst(invItems)) {
|
||||
AppearsInColor &vec = m_appears_in_hash[ii->item()][ii->color()];
|
||||
vec.append(qMakePair(ii->quantity(), item));
|
||||
for (const Item::ConsistsOf &co : qAsConst(inventory)) {
|
||||
AppearsInColor &vec = m_appears_in_hash[co.item()][co.color()];
|
||||
vec.append(qMakePair(co.quantity(), item));
|
||||
}
|
||||
// the hash owns the items now
|
||||
m_consists_of_hash.insert(item, invItems);
|
||||
m_consists_of_hash.insert(item, inventory);
|
||||
return true;
|
||||
|
||||
} catch (const Exception &) {
|
||||
qDeleteAll(invItems);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -523,7 +521,7 @@ void BrickLink::TextImport::exportInventoriesTo(Core * /*bl*/)
|
||||
//QMutexLocker lock(&m_corelock);
|
||||
|
||||
for (auto it = m_consists_of_hash.cbegin(); it != m_consists_of_hash.cend(); ++it)
|
||||
it.key()->setConsistsOf(it.value());
|
||||
const_cast<Item *>(it.key())->setConsistsOf(it.value());
|
||||
|
||||
for (auto it = m_appears_in_hash.cbegin(); it != m_appears_in_hash.cend(); ++it)
|
||||
it.key()->setAppearsIn(it.value());
|
||||
|
||||
@@ -29,12 +29,11 @@ class PartColorCode;
|
||||
|
||||
class Picture;
|
||||
class PriceGuide;
|
||||
class InvItem;
|
||||
class Order;
|
||||
class Cart;
|
||||
class Incomplete;
|
||||
class Core;
|
||||
class TextImport;
|
||||
class InvItemMimeData;
|
||||
|
||||
class ColorModel;
|
||||
class InternalColorModel;
|
||||
@@ -45,7 +44,6 @@ class AppearsInModel;
|
||||
class InternalAppearsInModel;
|
||||
class ItemDelegate;
|
||||
|
||||
typedef QVector<InvItem *> InvItemList;
|
||||
typedef QPair<int, const Item *> AppearsInItem;
|
||||
typedef QVector<AppearsInItem> AppearsInColor;
|
||||
typedef QHash<const Color *, AppearsInColor> AppearsIn;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
|
||||
ConsolidateItemsDialog::ConsolidateItemsDialog(const Window *win,
|
||||
const BrickLink::InvItemList &items,
|
||||
const LotList &lots,
|
||||
int preselectedIndex, Window::Consolidate mode,
|
||||
int current, int total, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
@@ -42,24 +42,24 @@ ConsolidateItemsDialog::ConsolidateItemsDialog(const Window *win,
|
||||
else
|
||||
w_counter->hide();
|
||||
|
||||
bool newItems = (items.count() == 2) && win->document()->items().contains(items.at(0))
|
||||
&& !win->document()->items().contains(items.at(items.count() - 1));
|
||||
bool newLots = (lots.count() == 2) && win->document()->lots().contains(lots.at(0))
|
||||
&& !win->document()->lots().contains(lots.at(lots.count() - 1));
|
||||
|
||||
Q_ASSERT(newItems == (int(mode) >= int(Window::Consolidate::IntoExisting)));
|
||||
Q_ASSERT(newLots == (int(mode) >= int(Window::Consolidate::IntoExisting)));
|
||||
|
||||
for (int i = int(Window::Consolidate::IntoNew); i > int(Window::Consolidate::Not); --i) {
|
||||
w_prefer_remaining->setItemData(i, QVariant::fromValue(static_cast<Window::Consolidate>(i)));
|
||||
|
||||
bool forNewOnly = (i >= int(Window::Consolidate::IntoExisting));
|
||||
if (newItems != forNewOnly)
|
||||
if (newLots != forNewOnly)
|
||||
w_prefer_remaining->removeItem(i);
|
||||
}
|
||||
|
||||
QVector<int> fakeIndexes;
|
||||
for (int i = 0; i < items.size(); ++i)
|
||||
fakeIndexes << win->document()->items().indexOf(items.at(i));
|
||||
for (int i = 0; i < lots.size(); ++i)
|
||||
fakeIndexes << win->document()->lots().indexOf(lots.at(i));
|
||||
|
||||
Document *doc = Document::createTemporary(items, fakeIndexes);
|
||||
Document *doc = Document::createTemporary(lots, fakeIndexes);
|
||||
doc->setParent(this);
|
||||
|
||||
auto *headerView = new HeaderView(Qt::Horizontal, w_list);
|
||||
|
||||
@@ -23,7 +23,7 @@ class ConsolidateItemsDialog : public QDialog, private Ui::ConsolidateItemsDialo
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConsolidateItemsDialog(const Window *win, const BrickLink::InvItemList &items,
|
||||
ConsolidateItemsDialog(const Window *win, const LotList &lots,
|
||||
int preselectedIndex, Window::Consolidate mode, int current, int total,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
|
||||
Regular → Executable
+409
-342
File diff suppressed because it is too large
Load Diff
+67
-48
@@ -17,8 +17,11 @@
|
||||
#include <QPixmap>
|
||||
#include <QUuid>
|
||||
#include <QElapsedTimer>
|
||||
#include <QMimeData>
|
||||
|
||||
#include "bricklink.h"
|
||||
|
||||
#include "bricklinkfwd.h"
|
||||
#include "lot.h"
|
||||
#include "documentio.h"
|
||||
#include "filter.h"
|
||||
|
||||
@@ -78,8 +81,8 @@ public:
|
||||
FilterRole = Qt::UserRole,
|
||||
BaseDisplayRole, // like Qt::DisplayRole, but for differenceBase
|
||||
BaseEditRole, // like Qt::EditRole, but for differenceBase
|
||||
ItemPointerRole,
|
||||
BaseItemPointerRole,
|
||||
LotPointerRole,
|
||||
BaseLotPointerRole,
|
||||
ErrorFlagsRole,
|
||||
DifferenceFlagsRole,
|
||||
|
||||
@@ -96,13 +99,11 @@ public:
|
||||
Q_DECLARE_FLAGS(MergeModes, MergeMode)
|
||||
Q_FLAG(MergeMode)
|
||||
|
||||
typedef BrickLink::InvItem Item;
|
||||
typedef QVector<BrickLink::InvItem *> ItemList;
|
||||
|
||||
class Statistics
|
||||
{
|
||||
public:
|
||||
Statistics(const Document *doc, const ItemList &list, bool ignoreExcluded = false);
|
||||
Statistics(const Document *doc, const LotList &list, bool ignoreExcluded = false);
|
||||
|
||||
int lots() const { return m_lots; }
|
||||
int items() const { return m_items; }
|
||||
@@ -129,8 +130,8 @@ public:
|
||||
};
|
||||
|
||||
// Itemviews API
|
||||
BrickLink::InvItem *item(const QModelIndex &idx) const;
|
||||
QModelIndex index(const BrickLink::InvItem *i, int column = 0) const;
|
||||
Lot *lot(const QModelIndex &idx) const;
|
||||
QModelIndex index(const Lot *i, int column = 0) const;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = { }) const override;
|
||||
|
||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
@@ -140,9 +141,9 @@ public:
|
||||
int role = Qt::DisplayRole) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex&) const override;
|
||||
bool setData(const QModelIndex&, const QVariant&, int) override;
|
||||
QVariant dataForEditRole(const Item *it, Field f) const;
|
||||
QVariant dataForDisplayRole(const BrickLink::InvItem *it, Field f) const;
|
||||
QVariant dataForFilterRole(const Item *it, Field f) const;
|
||||
QVariant dataForEditRole(const Lot *lot, Field f) const;
|
||||
QVariant dataForDisplayRole(const Lot *lot, Field f) const;
|
||||
QVariant dataForFilterRole(const Lot *lot, Field f) const;
|
||||
|
||||
bool isSorted() const;
|
||||
int sortColumn() const;
|
||||
@@ -157,7 +158,7 @@ public:
|
||||
void reSort();
|
||||
void reFilter();
|
||||
|
||||
BrickLink::InvItemList sortItemList(const BrickLink::InvItemList &list) const;
|
||||
LotList sortLotList(const LotList &list) const;
|
||||
|
||||
QString filterToolTip() const;
|
||||
|
||||
@@ -171,7 +172,7 @@ public:
|
||||
Document(const DocumentIO::BsxContents &bsx, bool forceModified = false);
|
||||
virtual ~Document() override;
|
||||
|
||||
static Document *createTemporary(const BrickLink::InvItemList &list,
|
||||
static Document *createTemporary(const LotList &list,
|
||||
const QVector<int> &fakeIndexes = { });
|
||||
|
||||
static const QVector<Document *> &allDocuments();
|
||||
@@ -184,28 +185,28 @@ public:
|
||||
|
||||
bool isModified() const;
|
||||
void unsetModified(); // only for DocumentIO::fileSaveTo
|
||||
QHash<const Item *, Item> differenceBase() const; // only for DocumentIO::fileSaveTo
|
||||
QHash<const Lot *, Lot> differenceBase() const; // only for DocumentIO::fileSaveTo
|
||||
|
||||
const ItemList &items() const;
|
||||
const ItemList &sortedItems() const;
|
||||
const LotList &lots() const;
|
||||
const LotList &sortedLots() const;
|
||||
bool clear();
|
||||
|
||||
void appendItem(Item *item);
|
||||
void appendItems(const ItemList &items);
|
||||
void insertItemsAfter(Item *afterItem, const BrickLink::InvItemList &items);
|
||||
void appendLot(Lot *lot);
|
||||
void appendLots(const LotList &lots);
|
||||
void insertLotsAfter(Lot *afterLot, const LotList &lots);
|
||||
|
||||
void removeItems(const ItemList &items);
|
||||
void removeItem(Item *item);
|
||||
void removeLots(const LotList &lots);
|
||||
void removeLot(Lot *lot);
|
||||
|
||||
void changeItem(Item *item, const Item &value, Document::Field hint = Document::FieldCount);
|
||||
void changeItems(const std::vector<std::pair<Item *, Item>> &changes,
|
||||
void changeLot(Lot *lot, const Lot &value, Document::Field hint = Document::FieldCount);
|
||||
void changeLots(const std::vector<std::pair<Lot *, Lot>> &changes,
|
||||
Document::Field hint = Document::FieldCount);
|
||||
|
||||
Statistics statistics(const ItemList &list, bool ignoreExcluded = false) const;
|
||||
Statistics statistics(const LotList &list, bool ignoreExcluded = false) const;
|
||||
|
||||
void setItemFlagsMask(QPair<quint64, quint64> flagsMask);
|
||||
void setLotFlagsMask(QPair<quint64, quint64> flagsMask);
|
||||
|
||||
QPair<quint64, quint64> itemFlags(const Item *item) const;
|
||||
QPair<quint64, quint64> lotFlags(const Lot *lot) const;
|
||||
|
||||
bool legacyCurrencyCode() const;
|
||||
QString currencyCode() const;
|
||||
@@ -213,8 +214,8 @@ public:
|
||||
|
||||
void setOrder(BrickLink::Order *order);
|
||||
|
||||
static bool canItemsBeMerged(const Item &item1, const Item &item2);
|
||||
static bool mergeItemFields(const Item &from, Item &to, MergeMode defaultMerge,
|
||||
static bool canLotsBeMerged(const Lot &lot1, const Lot &lot2);
|
||||
static bool mergeLotFields(const Lot &from, Lot &to, MergeMode defaultMerge,
|
||||
const QHash<Field, MergeMode> &fieldMerge = { });
|
||||
|
||||
public slots:
|
||||
@@ -228,19 +229,19 @@ public:
|
||||
|
||||
QUndoStack *undoStack() const;
|
||||
|
||||
const Item *differenceBaseItem(const Item *item) const;
|
||||
const Lot *differenceBaseLot(const Lot *lot) const;
|
||||
|
||||
QByteArray saveSortFilterState() const;
|
||||
bool restoreSortFilterState(const QByteArray &ba);
|
||||
|
||||
signals:
|
||||
void itemFlagsChanged(const Document::Item *);
|
||||
void lotFlagsChanged(const Lot *);
|
||||
void statisticsChanged();
|
||||
void fileNameChanged(const QString &);
|
||||
void titleChanged(const QString &);
|
||||
void modificationChanged(bool);
|
||||
void currencyCodeChanged(const QString &ccode);
|
||||
void itemCountChanged(int itemCount);
|
||||
void lotCountChanged(int lotCount);
|
||||
void filterChanged(const QString &filter);
|
||||
void sortOrderChanged(Qt::SortOrder order);
|
||||
void sortColumnChanged(int column);
|
||||
@@ -250,32 +251,32 @@ signals:
|
||||
|
||||
protected:
|
||||
bool event(QEvent *e) override;
|
||||
virtual bool filterAcceptsItem(const BrickLink::InvItem *item) const;
|
||||
virtual bool filterAcceptsLot(const Lot *lot) const;
|
||||
|
||||
private:
|
||||
Document(int dummy);
|
||||
|
||||
void setFakeIndexes(const QVector<int> &fakeIndexes);
|
||||
|
||||
void setItemsDirect(const ItemList &items);
|
||||
void insertItemsDirect(const ItemList &items, QVector<int> &positions, QVector<int> &sortedPositions, QVector<int> &filteredPositions);
|
||||
void removeItemsDirect(const ItemList &items, QVector<int> &positions, QVector<int> &sortedPositions, QVector<int> &filteredPositions);
|
||||
void changeItemsDirect(std::vector<std::pair<Item *, Item> > &changes);
|
||||
void setLotsDirect(const LotList &lots);
|
||||
void insertLotsDirect(const LotList &lots, QVector<int> &positions, QVector<int> &sortedPositions, QVector<int> &filteredPositions);
|
||||
void removeLotsDirect(const LotList &lots, QVector<int> &positions, QVector<int> &sortedPositions, QVector<int> &filteredPositions);
|
||||
void changeLotsDirect(std::vector<std::pair<Lot *, Lot> > &changes);
|
||||
void changeCurrencyDirect(const QString &ccode, qreal crate, double *&prices);
|
||||
void resetDifferenceModeDirect(QHash<const BrickLink::InvItem *, BrickLink::InvItem>
|
||||
void resetDifferenceModeDirect(QHash<const Lot *, Lot>
|
||||
&differenceBase);
|
||||
void filterDirect(const QString &filterString, const QVector<Filter> &filterList, bool &filtered,
|
||||
BrickLink::InvItemList &unfiltered);
|
||||
void sortDirect(int column, Qt::SortOrder order, bool &sorted, BrickLink::InvItemList &unsorted);
|
||||
LotList &unfiltered);
|
||||
void sortDirect(int column, Qt::SortOrder order, bool &sorted, LotList &unsorted);
|
||||
|
||||
void emitDataChanged(const QModelIndex &tl = { }, const QModelIndex &br = { });
|
||||
void emitStatisticsChanged();
|
||||
void updateItemFlags(const Item *item);
|
||||
void setItemFlags(const Item *item, quint64 errors, quint64 updated);
|
||||
void updateLotFlags(const Lot *lot);
|
||||
void setLotFlags(const Lot *lot, quint64 errors, quint64 updated);
|
||||
|
||||
void updateModified();
|
||||
|
||||
int compare(const BrickLink::InvItem *i1, const BrickLink::InvItem *i2, int sortColumn) const;
|
||||
int compare(const Lot *i1, const Lot *i2, int sortColumn) const;
|
||||
void languageChange();
|
||||
|
||||
friend class AddRemoveCmd;
|
||||
@@ -286,13 +287,13 @@ private:
|
||||
friend class ResetDifferenceModeCmd;
|
||||
|
||||
private:
|
||||
QVector<BrickLink::InvItem *> m_items;
|
||||
QVector<BrickLink::InvItem *> m_sortedItems;
|
||||
QVector<BrickLink::InvItem *> m_filteredItems;
|
||||
QVector<Lot *> m_lots;
|
||||
QVector<Lot *> m_sortedLots;
|
||||
QVector<Lot *> m_filteredLots;
|
||||
|
||||
QHash<const Item *, Item> m_differenceBase;
|
||||
QHash<const Lot *, Lot> m_differenceBase;
|
||||
QVector<int> m_fakeIndexes; // for the consolidate dialogs
|
||||
QHash<const Item *, QPair<quint64, quint64>> m_itemFlags;
|
||||
QHash<const Lot *, QPair<quint64, quint64>> m_lotFlags;
|
||||
|
||||
|
||||
int m_sortColumn = -1;
|
||||
@@ -306,7 +307,7 @@ private:
|
||||
bool m_nextSortFilterIsDirect = false;
|
||||
|
||||
QString m_currencycode;
|
||||
QPair<quint64, quint64> m_itemFlagsMask = { 0, 0 };
|
||||
QPair<quint64, quint64> m_lotFlagsMask = { 0, 0 };
|
||||
QString m_filename;
|
||||
QString m_title;
|
||||
|
||||
@@ -323,7 +324,25 @@ private:
|
||||
static QVector<Document *> s_documents;
|
||||
};
|
||||
|
||||
class DocumentLotsMimeData : public QMimeData
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DocumentLotsMimeData(const LotList &lots);
|
||||
|
||||
QStringList formats() const override;
|
||||
bool hasFormat(const QString &mimeType) const override;
|
||||
|
||||
void setLots(const LotList &lots);
|
||||
static LotList lots(const QMimeData *md);
|
||||
|
||||
private:
|
||||
static const char *s_mimetype;
|
||||
};
|
||||
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Document::MergeModes)
|
||||
|
||||
Q_DECLARE_METATYPE(Document *)
|
||||
Q_DECLARE_METATYPE(const Document *)
|
||||
Q_DECLARE_METATYPE(const Lot *)
|
||||
|
||||
+7
-7
@@ -24,7 +24,7 @@ public:
|
||||
|
||||
AddRemoveCmd(Type t, Document *doc, const QVector<int> &positions,
|
||||
const QVector<int> &sortedPositions, const QVector<int> &filteredPositions,
|
||||
const Document::ItemList &items);
|
||||
const LotList &lots);
|
||||
~AddRemoveCmd() override;
|
||||
int id() const override;
|
||||
|
||||
@@ -38,14 +38,14 @@ private:
|
||||
QVector<int> m_positions;
|
||||
QVector<int> m_sortedPositions;
|
||||
QVector<int> m_filteredPositions;
|
||||
Document::ItemList m_items;
|
||||
LotList m_lots;
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
class ChangeCmd : public QUndoCommand
|
||||
{
|
||||
public:
|
||||
ChangeCmd(Document *doc, const std::vector<std::pair<Document::Item *, Document::Item>> &changes,
|
||||
ChangeCmd(Document *doc, const std::vector<std::pair<Lot *, Lot>> &changes,
|
||||
Document::Field hint = Document::FieldCount);
|
||||
int id() const override;
|
||||
bool mergeWith(const QUndoCommand *other) override;
|
||||
@@ -59,7 +59,7 @@ private:
|
||||
Document *m_doc;
|
||||
uint m_loopCount;
|
||||
Document::Field m_hint;
|
||||
std::vector<std::pair<Document::Item *, Document::Item>> m_changes;
|
||||
std::vector<std::pair<Lot *, Lot>> m_changes;
|
||||
|
||||
static QTimer *s_eventLoopCounter;
|
||||
};
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
|
||||
private:
|
||||
Document *m_doc;
|
||||
QHash<const BrickLink::InvItem *, BrickLink::InvItem> m_differenceBase;
|
||||
QHash<const Lot *, Lot> m_differenceBase;
|
||||
};
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ private:
|
||||
Qt::SortOrder m_order;
|
||||
bool m_isSorted = false;
|
||||
|
||||
QVector<BrickLink::InvItem *> m_unsorted;
|
||||
QVector<Lot *> m_unsorted;
|
||||
};
|
||||
|
||||
class FilterCmd : public QUndoCommand
|
||||
@@ -135,5 +135,5 @@ private:
|
||||
QVector<Filter> m_filterList;
|
||||
bool m_isFiltered = false;
|
||||
|
||||
QVector<BrickLink::InvItem *> m_unfiltered;
|
||||
QVector<Lot *> m_unfiltered;
|
||||
};
|
||||
|
||||
+31
-31
@@ -174,8 +174,8 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
|
||||
const auto *item = idx.data(Document::ItemPointerRole).value<const BrickLink::InvItem *>();
|
||||
const auto *base = idx.data(Document::BaseItemPointerRole).value<const BrickLink::InvItem *>();
|
||||
const auto *lot = idx.data(Document::LotPointerRole).value<const Lot *>();
|
||||
const auto *base = idx.data(Document::BaseLotPointerRole).value<const Lot *>();
|
||||
const auto errorFlags = idx.data(Document::ErrorFlagsRole).value<quint64>();
|
||||
const auto differenceFlags = idx.data(Document::DifferenceFlagsRole).value<quint64>();
|
||||
|
||||
@@ -210,8 +210,8 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
}
|
||||
|
||||
bool selected = (option.state & QStyle::State_Selected);
|
||||
bool nocolor = !item->color();
|
||||
bool noitem = !item->item();
|
||||
bool nocolor = !lot->color();
|
||||
bool noitem = !lot->item();
|
||||
|
||||
const QFontMetrics &fm = option.fontMetrics;
|
||||
QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled;
|
||||
@@ -268,25 +268,25 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
if (!selected) {
|
||||
switch (idx.column()) {
|
||||
case Document::ItemType:
|
||||
if (item->itemType())
|
||||
bg = shadeColor(item->itemType()->id(), 0.1);
|
||||
if (lot->itemType())
|
||||
bg = shadeColor(lot->itemType()->id(), 0.1);
|
||||
break;
|
||||
|
||||
case Document::Category:
|
||||
if (item->category())
|
||||
bg = shadeColor(int(item->category()->id()), 0.2);
|
||||
if (lot->category())
|
||||
bg = shadeColor(int(lot->category()->id()), 0.2);
|
||||
break;
|
||||
|
||||
case Document::Quantity:
|
||||
if (item->quantity() <= 0)
|
||||
bg = (item->quantity() == 0) ? QColor::fromRgbF(1, 1, 0, 0.4)
|
||||
if (lot->quantity() <= 0)
|
||||
bg = (lot->quantity() == 0) ? QColor::fromRgbF(1, 1, 0, 0.4)
|
||||
: QColor::fromRgbF(1, 0, 0, 0.4);
|
||||
break;
|
||||
|
||||
case Document::QuantityDiff:
|
||||
if (base && (base->quantity() < item->quantity()))
|
||||
if (base && (base->quantity() < lot->quantity()))
|
||||
bg = QColor::fromRgbF(0, 1, 0, 0.3);
|
||||
else if (base && (base->quantity() > item->quantity()))
|
||||
else if (base && (base->quantity() > lot->quantity()))
|
||||
bg = QColor::fromRgbF(1, 0, 0, 0.3);
|
||||
break;
|
||||
|
||||
@@ -296,9 +296,9 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
break;
|
||||
|
||||
case Document::PriceDiff: {
|
||||
if (base && (base->price() < item->price()))
|
||||
if (base && (base->price() < lot->price()))
|
||||
bg = QColor::fromRgbF(0, 1, 0, 0.3);
|
||||
else if (base && (base->price() > item->price()))
|
||||
else if (base && (base->price() > lot->price()))
|
||||
bg = QColor::fromRgbF(1, 0, 0, 0.3);
|
||||
break;
|
||||
}
|
||||
@@ -307,7 +307,7 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
break;
|
||||
|
||||
case Document::Condition:
|
||||
if (item->condition() != BrickLink::Condition::New) {
|
||||
if (lot->condition() != BrickLink::Condition::New) {
|
||||
bg = fg;
|
||||
bg.setAlphaF(0.3);
|
||||
}
|
||||
@@ -339,12 +339,12 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
int iconSize = std::min(fm.height() * 5 / 4, h * 3 / 4);
|
||||
union { struct { qint32 i1; quint32 i2; } s; quint64 q; } key;
|
||||
key.s.i1 = iconSize;
|
||||
key.s.i2 = quint32(item->status());
|
||||
key.s.i2 = quint32(lot->status());
|
||||
|
||||
QPixmap *pix = s_status_cache[key.q];
|
||||
if (!pix) {
|
||||
QIcon icon;
|
||||
switch (item->status()) {
|
||||
switch (lot->status()) {
|
||||
case BrickLink::Status::Exclude: icon = QIcon::fromTheme("vcs-removed"); break;
|
||||
case BrickLink::Status::Extra : icon = QIcon::fromTheme("vcs-added"); break;
|
||||
default :
|
||||
@@ -357,11 +357,11 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
if (pix)
|
||||
image = pix->toImage();
|
||||
|
||||
uint altid = item->alternateId();
|
||||
bool cp = item->counterPart();
|
||||
uint altid = lot->alternateId();
|
||||
bool cp = lot->counterPart();
|
||||
if (altid || cp) {
|
||||
tag.text = cp ? QLatin1String("CP") : QString::number(altid);
|
||||
tag.bold = (cp || !item->alternate());
|
||||
tag.bold = (cp || !lot->alternate());
|
||||
tag.foreground = fg;
|
||||
if (cp || selected) {
|
||||
tag.background = fg;
|
||||
@@ -373,13 +373,13 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
break;
|
||||
}
|
||||
case Document::Description:
|
||||
if (item->item() && item->item()->hasInventory()) {
|
||||
if (lot->item() && lot->item()->hasInventory()) {
|
||||
tag.text = tr("Inv");
|
||||
tag.foreground = bg;
|
||||
tag.background = fg;
|
||||
tag.background.setAlphaF(0.3);
|
||||
|
||||
if ((option.state & QStyle::State_MouseOver) && item->quantity()) {
|
||||
if ((option.state & QStyle::State_MouseOver) && lot->quantity()) {
|
||||
tag.foreground = option.palette.color(QPalette::HighlightedText);
|
||||
tag.background = option.palette.color(QPalette::Highlight);
|
||||
}
|
||||
@@ -387,7 +387,7 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
break;
|
||||
|
||||
case Document::Picture: {
|
||||
BrickLink::Picture *pic = BrickLink::core()->picture(item->item(), item->color());
|
||||
BrickLink::Picture *pic = BrickLink::core()->picture(lot->item(), lot->color());
|
||||
double dpr = p->device()->devicePixelRatioF();
|
||||
QSize s = option.rect.size() * dpr;
|
||||
|
||||
@@ -401,19 +401,19 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
break;
|
||||
}
|
||||
case Document::Color: {
|
||||
image = BrickLink::core()->colorImage(item->color(), option.decorationSize.width() * 3 / 2,
|
||||
image = BrickLink::core()->colorImage(lot->color(), option.decorationSize.width() * 3 / 2,
|
||||
option.rect.height());
|
||||
break;
|
||||
}
|
||||
case Document::Retain:
|
||||
checkmark = item->retain() ? 1 : -1;
|
||||
checkmark = lot->retain() ? 1 : -1;
|
||||
break;
|
||||
|
||||
case Document::Stockroom:
|
||||
if (item->stockroom() == BrickLink::Stockroom::None)
|
||||
if (lot->stockroom() == BrickLink::Stockroom::None)
|
||||
checkmark = -1;
|
||||
else
|
||||
str = QLatin1Char('A' + char(item->stockroom()) - char(BrickLink::Stockroom::A));
|
||||
str = QLatin1Char('A' + char(lot->stockroom()) - char(BrickLink::Stockroom::A));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -560,7 +560,7 @@ void DocumentDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, co
|
||||
Document::Remarks,
|
||||
Document::Category,
|
||||
Document::ItemType,
|
||||
Document::Reserved
|
||||
Document::Reserved,
|
||||
};
|
||||
|
||||
if (!str.isEmpty()) {
|
||||
@@ -877,7 +877,7 @@ bool DocumentDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view,
|
||||
|
||||
auto f = static_cast<Document::Field>(idx.column());
|
||||
|
||||
const auto *item = idx.data(Document::ItemPointerRole).value<const BrickLink::InvItem *>();
|
||||
const auto *item = idx.data(Document::LotPointerRole).value<const Lot *>();
|
||||
if (!item)
|
||||
return false;
|
||||
|
||||
@@ -995,7 +995,7 @@ QString DocumentDelegate::displayData(const QModelIndex &idx, bool toolTip, bool
|
||||
default : break;
|
||||
}
|
||||
|
||||
const auto *item = idx.data(Document::ItemPointerRole).value<const BrickLink::InvItem *>();
|
||||
const auto *item = idx.data(Document::LotPointerRole).value<const Lot *>();
|
||||
if (item->counterPart())
|
||||
tip = tip % u"<br>(" % tr("Counter part") % u")";
|
||||
else if (item->alternateId())
|
||||
@@ -1012,7 +1012,7 @@ QString DocumentDelegate::displayData(const QModelIndex &idx, bool toolTip, bool
|
||||
? tr("New", "ToolTip Cond>New") : tr("Used", "ToolTip Cond>Used");
|
||||
}
|
||||
|
||||
const auto *item = idx.data(Document::ItemPointerRole).value<const BrickLink::InvItem *>();
|
||||
const auto *item = idx.data(Document::LotPointerRole).value<const Lot *>();
|
||||
if (item && item->itemType() && item->itemType()->hasSubConditions()
|
||||
&& (item->subCondition() != BrickLink::SubCondition::None)) {
|
||||
QString scStr;
|
||||
|
||||
+339
-339
File diff suppressed because it is too large
Load Diff
+21
-20
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include "bricklinkfwd.h"
|
||||
#include "lot.h"
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QFile)
|
||||
QT_FORWARD_DECLARE_CLASS(QIODevice)
|
||||
@@ -32,52 +33,52 @@ public:
|
||||
static Window *open();
|
||||
static Window *open(const QString &name);
|
||||
static Document *importBrickLinkInventory(const BrickLink::Item *preselect = nullptr,
|
||||
int quantity = 1,
|
||||
int multiply = 1,
|
||||
BrickLink::Condition condition = BrickLink::Condition::New,
|
||||
BrickLink::Status extraParts = BrickLink::Status::Extra,
|
||||
bool includeInstructions = false);
|
||||
static Document *importBrickLinkOrder(BrickLink::Order *order, const QByteArray &orderXml);
|
||||
static Document *importBrickLinkOrder(BrickLink::Order *order, const LotList &lots);
|
||||
static Document *importBrickLinkStore();
|
||||
static Document *importBrickLinkCart(BrickLink::Cart *cart, const BrickLink::InvItemList &itemlist);
|
||||
static Document *importBrickLinkCart(BrickLink::Cart *cart, const LotList &lots);
|
||||
static Document *importBrickLinkXML();
|
||||
static Document *importLDrawModel();
|
||||
|
||||
static bool save(Window *win);
|
||||
static bool saveAs(Window *win);
|
||||
static void exportBrickLinkXML(const BrickLink::InvItemList &itemlist);
|
||||
static void exportBrickLinkXMLClipboard(const BrickLink::InvItemList &itemlist);
|
||||
static void exportBrickLinkXML(const LotList &lots);
|
||||
static void exportBrickLinkXMLClipboard(const LotList &itemlist);
|
||||
static void exportBrickLinkUpdateClipboard(const Document *doc,
|
||||
const BrickLink::InvItemList &itemlist);
|
||||
static void exportBrickLinkInvReqClipboard(const BrickLink::InvItemList &itemlist);
|
||||
static void exportBrickLinkWantedListClipboard(const BrickLink::InvItemList &itemlist);
|
||||
const LotList &lots);
|
||||
static void exportBrickLinkInvReqClipboard(const LotList &lots);
|
||||
static void exportBrickLinkWantedListClipboard(const LotList &lots);
|
||||
|
||||
struct BsxContents
|
||||
{
|
||||
BrickLink::InvItemList items;
|
||||
LotList lots;
|
||||
QString currencyCode;
|
||||
|
||||
QHash<const BrickLink::InvItem *, BrickLink::InvItem> differenceModeBase;
|
||||
QHash<const Lot *, Lot> differenceModeBase;
|
||||
|
||||
QByteArray guiColumnLayout;
|
||||
QByteArray guiSortFilterState;
|
||||
|
||||
int invalidItemCount = 0; // parse only
|
||||
int invalidLotsCount = 0; // parse only
|
||||
};
|
||||
|
||||
static QString toBrickLinkXML(const LotList &lots);
|
||||
static BsxContents fromBrickLinkXML(const QByteArray &xml);
|
||||
|
||||
private:
|
||||
static Window *loadFrom(const QString &s);
|
||||
static bool saveTo(Window *win, const QString &s);
|
||||
|
||||
static QString toBrickLinkXML(const BrickLink::InvItemList &itemlist);
|
||||
static BsxContents fromBrickLinkXML(const QByteArray &xml);
|
||||
static bool parseLDrawModel(QFile *f, LotList &lots, int *invalidLots);
|
||||
static bool parseLDrawModelInternal(QFile *f, const QString &modelName,
|
||||
QVector<Lot *> &lots,
|
||||
QHash<QString, QVector<Lot *> > &subCache,
|
||||
QVector<QString> &recursionDetection);
|
||||
|
||||
static bool parseLDrawModel(QFile *f, BrickLink::InvItemList &items, int *invalid_items);
|
||||
static bool parseLDrawModelInternal(QFile *f, const QString &model_name,
|
||||
QVector<BrickLink::InvItem *> &items,
|
||||
QHash<QString, QVector<BrickLink::InvItem *> > &subCache,
|
||||
QVector<QString> &recursion_detection);
|
||||
|
||||
static bool resolveIncomplete(BrickLink::InvItem *item);
|
||||
static bool resolveIncomplete(Lot *lot);
|
||||
|
||||
static BsxContents parseBsxInventory(QIODevice *in);
|
||||
static bool createBsxInventory(QIODevice *out, const BsxContents &bsx);
|
||||
|
||||
+21
-21
@@ -91,7 +91,7 @@ enum {
|
||||
NeedItemMask = 0x000f,
|
||||
|
||||
NeedDocument = 0x0010,
|
||||
NeedItems = 0x0040,
|
||||
NeedLots = 0x0040,
|
||||
NeedNetwork = 0x0100,
|
||||
|
||||
// the upper 16 bits (0xffff0000) are reserved for NeedSelection()
|
||||
@@ -1070,8 +1070,8 @@ void FrameWork::createActions()
|
||||
|
||||
(void) newQAction(this, "document_save");
|
||||
(void) newQAction(this, "document_save_as", NeedDocument);
|
||||
(void) newQAction(this, "document_print", NeedDocument | NeedItems);
|
||||
(void) newQAction(this, "document_print_pdf", NeedDocument | NeedItems);
|
||||
(void) newQAction(this, "document_print", NeedDocument | NeedLots);
|
||||
(void) newQAction(this, "document_print_pdf", NeedDocument | NeedLots);
|
||||
|
||||
m = newQMenu(this, "document_import");
|
||||
m->addAction(newQAction(this, "document_import_bl_inv", 0, false, this, [this]() {
|
||||
@@ -1106,11 +1106,11 @@ void FrameWork::createActions()
|
||||
|
||||
|
||||
m = newQMenu(this, "document_export");
|
||||
m->addAction(newQAction(this, "document_export_bl_xml", NeedDocument | NeedItems));
|
||||
m->addAction(newQAction(this, "document_export_bl_xml_clip", NeedDocument | NeedItems));
|
||||
m->addAction(newQAction(this, "document_export_bl_update_clip", NeedDocument | NeedItems));
|
||||
m->addAction(newQAction(this, "document_export_bl_invreq_clip", NeedDocument | NeedItems));
|
||||
m->addAction(newQAction(this, "document_export_bl_wantedlist_clip", NeedDocument | NeedItems));
|
||||
m->addAction(newQAction(this, "document_export_bl_xml", NeedDocument | NeedLots));
|
||||
m->addAction(newQAction(this, "document_export_bl_xml_clip", NeedDocument | NeedLots));
|
||||
m->addAction(newQAction(this, "document_export_bl_update_clip", NeedDocument | NeedLots));
|
||||
m->addAction(newQAction(this, "document_export_bl_invreq_clip", NeedDocument | NeedLots));
|
||||
m->addAction(newQAction(this, "document_export_bl_wantedlist_clip", NeedDocument | NeedLots));
|
||||
|
||||
(void) newQAction(this, "document_close", NeedDocument);
|
||||
|
||||
@@ -1139,9 +1139,9 @@ void FrameWork::createActions()
|
||||
(void) newQAction(this, "edit_subtractitems", NeedDocument);
|
||||
(void) newQAction(this, "edit_mergeitems", NeedSelection(2));
|
||||
(void) newQAction(this, "edit_partoutitems", NeedInventory | NeedSelection(1) | NeedQuantity);
|
||||
(void) newQAction(this, "edit_copy_fields", NeedDocument | NeedItems);
|
||||
(void) newQAction(this, "edit_select_all", NeedDocument | NeedItems);
|
||||
(void) newQAction(this, "edit_select_none", NeedDocument | NeedItems);
|
||||
(void) newQAction(this, "edit_copy_fields", NeedDocument | NeedLots);
|
||||
(void) newQAction(this, "edit_select_all", NeedDocument | NeedLots);
|
||||
(void) newQAction(this, "edit_select_none", NeedDocument | NeedLots);
|
||||
(void) newQAction(this, "edit_filter_from_selection", NeedSelection(1, 1));
|
||||
(void) newQAction(this, "edit_filter_focus", NeedDocument, false, this, [this]() {
|
||||
if (m_filter)
|
||||
@@ -1510,7 +1510,7 @@ void FrameWork::connectWindow(QWidget *w)
|
||||
this, &FrameWork::titleUpdate);
|
||||
disconnect(doc, &Document::modificationChanged,
|
||||
this, &FrameWork::modificationUpdate);
|
||||
disconnect(m_activeWin.data(), &Window::selectionChanged,
|
||||
disconnect(m_activeWin.data(), &Window::selectedLotsChanged,
|
||||
this, &FrameWork::selectionUpdate);
|
||||
disconnect(m_activeWin.data(), &Window::blockingOperationActiveChanged,
|
||||
this, &FrameWork::blockUpdate);
|
||||
@@ -1540,7 +1540,7 @@ void FrameWork::connectWindow(QWidget *w)
|
||||
this, &FrameWork::titleUpdate);
|
||||
connect(doc, &Document::modificationChanged,
|
||||
this, &FrameWork::modificationUpdate);
|
||||
connect(window, &Window::selectionChanged,
|
||||
connect(window, &Window::selectedLotsChanged,
|
||||
this, &FrameWork::selectionUpdate);
|
||||
connect(window, &Window::blockingOperationActiveChanged,
|
||||
this, &FrameWork::blockUpdate);
|
||||
@@ -1572,7 +1572,7 @@ void FrameWork::connectWindow(QWidget *w)
|
||||
m_add_dialog->close();
|
||||
}
|
||||
|
||||
selectionUpdate(m_activeWin ? m_activeWin->selection() : Document::ItemList());
|
||||
selectionUpdate(m_activeWin ? m_activeWin->selectedLots() : LotList());
|
||||
blockUpdate(m_activeWin ? m_activeWin->isBlockingOperationActive() : false);
|
||||
titleUpdate();
|
||||
modificationUpdate();
|
||||
@@ -1582,9 +1582,9 @@ void FrameWork::connectWindow(QWidget *w)
|
||||
|
||||
void FrameWork::updateActions()
|
||||
{
|
||||
Document::ItemList selection;
|
||||
LotList selection;
|
||||
if (m_activeWin)
|
||||
selection = m_activeWin->selection();
|
||||
selection = m_activeWin->selectedLots();
|
||||
bool isOnline = Application::inst()->isOnline();
|
||||
|
||||
const auto *doc = m_activeWin ? m_activeWin->document() : nullptr;
|
||||
@@ -1599,7 +1599,7 @@ void FrameWork::updateActions()
|
||||
bool b = true;
|
||||
|
||||
if (m_activeWin && m_activeWin->isBlockingOperationActive()
|
||||
&& (flags & (NeedDocument | NeedItems | NeedItemMask))) {
|
||||
&& (flags & (NeedDocument | NeedLots | NeedItemMask))) {
|
||||
b = false;
|
||||
}
|
||||
|
||||
@@ -1610,7 +1610,7 @@ void FrameWork::updateActions()
|
||||
b = b && m_activeWin;
|
||||
|
||||
if (b) {
|
||||
if (flags & NeedItems)
|
||||
if (flags & NeedLots)
|
||||
b = b && (docItemCount > 0);
|
||||
|
||||
quint8 minSelection = (flags >> 24) & 0xff;
|
||||
@@ -1624,7 +1624,7 @@ void FrameWork::updateActions()
|
||||
}
|
||||
|
||||
if (flags & NeedItemMask) {
|
||||
foreach (Document::Item *item, selection) {
|
||||
foreach (Lot *item, selection) {
|
||||
if (flags & NeedLotId)
|
||||
b = b && (item->lotId() != 0);
|
||||
if (flags & NeedInventory)
|
||||
@@ -1642,7 +1642,7 @@ void FrameWork::updateActions()
|
||||
}
|
||||
}
|
||||
|
||||
void FrameWork::selectionUpdate(const Document::ItemList &selection)
|
||||
void FrameWork::selectionUpdate(const LotList &selection)
|
||||
{
|
||||
int cnt = selection.count();
|
||||
int status = -1;
|
||||
@@ -1658,7 +1658,7 @@ void FrameWork::selectionUpdate(const Document::ItemList &selection)
|
||||
retain = selection.front()->retain() ? 1 : 0;
|
||||
stockroom = int(selection.front()->stockroom());
|
||||
|
||||
foreach (Document::Item *item, selection) {
|
||||
foreach (Lot *item, selection) {
|
||||
if ((status >= 0) && (status != int(item->status())))
|
||||
status = -1;
|
||||
if ((condition >= 0) && (condition != int(item->condition())))
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ public:
|
||||
static constexpr double maxPrice = 99999;
|
||||
|
||||
public slots:
|
||||
void selectionUpdate(const Document::ItemList &selection);
|
||||
void selectionUpdate(const LotList &selection);
|
||||
void blockUpdate(bool blocked);
|
||||
void modificationUpdate();
|
||||
void titleUpdate();
|
||||
|
||||
+14
-14
@@ -462,7 +462,7 @@ void ImportCartDialog::downloadFinished(TransferJob *job)
|
||||
QLocale en_US("en_US");
|
||||
|
||||
if (!job->data()->isEmpty() && (job->responseCode() == 200)) {
|
||||
BrickLink::InvItemList items;
|
||||
LotList lots;
|
||||
|
||||
try {
|
||||
int invalidCount = 0;
|
||||
@@ -493,23 +493,23 @@ void ImportCartDialog::downloadFinished(TransferJob *job)
|
||||
if (!item || !color) {
|
||||
++invalidCount;
|
||||
} else {
|
||||
auto *ii = new BrickLink::InvItem(color, item);
|
||||
ii->setCondition(cond);
|
||||
auto *lot = new Lot(color, item);
|
||||
lot->setCondition(cond);
|
||||
|
||||
if (ii->itemType()->hasSubConditions()) {
|
||||
if (lot->itemType()->hasSubConditions()) {
|
||||
QString scond = cartItem["invComplete"].toString();
|
||||
if (scond == QLatin1String("Complete"))
|
||||
ii->setSubCondition(BrickLink::SubCondition::Complete);
|
||||
lot->setSubCondition(BrickLink::SubCondition::Complete);
|
||||
if (scond == QLatin1String("Incomplete"))
|
||||
ii->setSubCondition(BrickLink::SubCondition::Incomplete);
|
||||
lot->setSubCondition(BrickLink::SubCondition::Incomplete);
|
||||
if (scond == QLatin1String("Sealed"))
|
||||
ii->setSubCondition(BrickLink::SubCondition::Sealed);
|
||||
lot->setSubCondition(BrickLink::SubCondition::Sealed);
|
||||
}
|
||||
|
||||
ii->setQuantity(qty);
|
||||
ii->setPrice(price);
|
||||
lot->setQuantity(qty);
|
||||
lot->setPrice(price);
|
||||
|
||||
items << ii;
|
||||
lots << lot;
|
||||
}
|
||||
}
|
||||
if (invalidCount)
|
||||
@@ -520,8 +520,8 @@ void ImportCartDialog::downloadFinished(TransferJob *job)
|
||||
tr("Could not parse the cart data") % u"<br><br>" % e.error());
|
||||
}
|
||||
|
||||
if (!items.isEmpty()) {
|
||||
if (auto doc = DocumentIO::importBrickLinkCart(cart, items))
|
||||
if (!lots.isEmpty()) {
|
||||
if (auto doc = DocumentIO::importBrickLinkCart(cart, lots))
|
||||
FrameWork::inst()->createWindow(doc);
|
||||
}
|
||||
}
|
||||
@@ -573,8 +573,8 @@ void ImportCartDialog::updateStatusLabel()
|
||||
{
|
||||
if (m_currentUpdate.isEmpty()) {
|
||||
w_lastUpdated->setText(tr("Last updated %1").arg(
|
||||
HumanReadableTimeDelta::toString(m_lastUpdated,
|
||||
QDateTime::currentDateTime())));
|
||||
HumanReadableTimeDelta::toString(QDateTime::currentDateTime(),
|
||||
m_lastUpdated)));
|
||||
} else {
|
||||
w_lastUpdated->setText(tr("Currently updating carts"));
|
||||
}
|
||||
|
||||
@@ -507,6 +507,8 @@ void ImportOrderDialog::downloadFinished(TransferJob *job)
|
||||
void ImportOrderDialog::orderDownloadFinished(BrickLink::Order *order, TransferJob *job,
|
||||
const QByteArray &xml)
|
||||
{
|
||||
bool hasCombinedFinished = false;
|
||||
|
||||
for (auto it = m_orderDownloads.begin(); it != m_orderDownloads.end(); ++it) {
|
||||
if (it->m_order != order)
|
||||
continue;
|
||||
@@ -518,16 +520,101 @@ void ImportOrderDialog::orderDownloadFinished(BrickLink::Order *order, TransferJ
|
||||
it->m_xmlData = xml;
|
||||
}
|
||||
if (!it->m_xmlJob && !it->m_addressJob) {
|
||||
if (auto doc = DocumentIO::importBrickLinkOrder(order, it->m_xmlData))
|
||||
FrameWork::inst()->createWindow(doc);
|
||||
m_orderDownloads.erase(it);
|
||||
if (!it->m_combine) {
|
||||
LotList lots = parseOrderXML(it->m_order, it->m_xmlData);
|
||||
if (!lots.isEmpty()) {
|
||||
if (auto doc = DocumentIO::importBrickLinkOrder(order, lots))
|
||||
FrameWork::inst()->createWindow(doc);
|
||||
}
|
||||
m_orderDownloads.erase(it);
|
||||
} else {
|
||||
it->m_finished = true;
|
||||
hasCombinedFinished = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasCombinedFinished) {
|
||||
bool allCombinedFinished = true;
|
||||
int combinedCount = 0;
|
||||
for (auto it = m_orderDownloads.begin(); it != m_orderDownloads.end(); ++it) {
|
||||
if (it->m_combine) {
|
||||
allCombinedFinished = allCombinedFinished && it->m_finished;
|
||||
++combinedCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (allCombinedFinished) {
|
||||
auto *order = new BrickLink::Order(tr("Multiple"), BrickLink::OrderType::Any);
|
||||
LotList lots;
|
||||
|
||||
int orderCount = 0;
|
||||
|
||||
for (auto it = m_orderDownloads.begin(); it != m_orderDownloads.end(); ) {
|
||||
if (it->m_combine) {
|
||||
LotList orderLots = parseOrderXML(it->m_order, it->m_xmlData);
|
||||
if (!orderLots.isEmpty()) {
|
||||
lots.append(orderLots);
|
||||
order->setCurrencyCode(it->m_order->currencyCode());
|
||||
order->setItemCount(order->itemCount() + it->m_order->itemCount());
|
||||
order->setLotCount(order->lotCount() + it->m_order->lotCount());
|
||||
}
|
||||
delete it->m_order;
|
||||
it = m_orderDownloads.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
++orderCount;
|
||||
}
|
||||
|
||||
if (auto doc = DocumentIO::importBrickLinkOrder(order, lots))
|
||||
FrameWork::inst()->createWindow(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LotList ImportOrderDialog::parseOrderXML(BrickLink::Order *order, const QByteArray &orderXml)
|
||||
{
|
||||
// we should really parse the XML ourselves
|
||||
int start = orderXml.indexOf("\n <ITEM>");
|
||||
int end = orderXml.lastIndexOf("</ITEM>");
|
||||
QByteArray xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><INVENTORY>\n" %
|
||||
orderXml.mid(start, end - start + 8) % "</INVENTORY>";
|
||||
|
||||
try {
|
||||
auto result = DocumentIO::fromBrickLinkXML(xml);
|
||||
return result.lots;
|
||||
} catch (const Exception &e) {
|
||||
MessageBox::warning(nullptr, { }, tr("Failed to import order %1").arg(order->id())
|
||||
% u"<br><br>" % e.error());
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
void ImportOrderDialog::importOrders(const QModelIndexList &rows)
|
||||
{
|
||||
bool combine = false;
|
||||
|
||||
if (rows.count() > 1) {
|
||||
QString ccode; // check if all orders have the same currency
|
||||
for (auto idx : rows) {
|
||||
auto order = idx.data(OrderPointerRole).value<const BrickLink::Order *>();
|
||||
if (ccode.isEmpty()) {
|
||||
ccode = order->currencyCode();
|
||||
} else if (ccode != order->currencyCode()) {
|
||||
ccode.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ccode.isEmpty()) {
|
||||
combine = (MessageBox::question(this, { },
|
||||
tr("Do you want to import all selected orders into a single document for multi order picking?"))
|
||||
== QMessageBox::Yes);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto idx : rows) {
|
||||
auto order = idx.data(OrderPointerRole).value<const BrickLink::Order *>();
|
||||
|
||||
@@ -558,7 +645,7 @@ void ImportOrderDialog::importOrders(const QModelIndexList &rows)
|
||||
auto orderCopy = new BrickLink::Order(*order);
|
||||
job->setUserData('o', orderCopy);
|
||||
addressJob->setUserData('a', orderCopy);
|
||||
m_orderDownloads << OrderDownload { orderCopy, job, addressJob };
|
||||
m_orderDownloads << OrderDownload { orderCopy, job, addressJob, combine };
|
||||
|
||||
m_trans->retrieve(job);
|
||||
m_trans->retrieve(addressJob);
|
||||
@@ -586,8 +673,8 @@ void ImportOrderDialog::updateStatusLabel()
|
||||
{
|
||||
if (m_currentUpdate.isEmpty()) {
|
||||
w_lastUpdated->setText(tr("Last updated %1").arg(
|
||||
HumanReadableTimeDelta::toString(m_lastUpdated,
|
||||
QDateTime::currentDateTime())));
|
||||
HumanReadableTimeDelta::toString(QDateTime::currentDateTime(),
|
||||
m_lastUpdated)));
|
||||
} else {
|
||||
w_lastUpdated->setText(tr("Currently updating orders"));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <QDateTime>
|
||||
|
||||
#include "bricklinkfwd.h"
|
||||
#include "lot.h"
|
||||
#include "ui_importorderdialog.h"
|
||||
|
||||
class Transfer;
|
||||
@@ -47,6 +48,7 @@ protected slots:
|
||||
void showOrdersOnBrickLink();
|
||||
|
||||
private:
|
||||
LotList parseOrderXML(BrickLink::Order *order, const QByteArray &orderXML);
|
||||
void orderDownloadFinished(BrickLink::Order *order, TransferJob *job,
|
||||
const QByteArray &xml = { });
|
||||
|
||||
@@ -57,13 +59,16 @@ private:
|
||||
QVector<TransferJob *> m_currentUpdate;
|
||||
struct OrderDownload {
|
||||
OrderDownload() = default;
|
||||
OrderDownload(BrickLink::Order *order, TransferJob *xmlJob, TransferJob *addressJob)
|
||||
: m_order(order), m_xmlJob(xmlJob), m_addressJob(addressJob)
|
||||
OrderDownload(BrickLink::Order *order, TransferJob *xmlJob, TransferJob *addressJob,
|
||||
bool combine)
|
||||
: m_order(order), m_xmlJob(xmlJob), m_addressJob(addressJob), m_combine(combine)
|
||||
{ }
|
||||
BrickLink::Order *m_order;
|
||||
TransferJob *m_xmlJob;
|
||||
TransferJob *m_addressJob;
|
||||
QByteArray m_xmlData;
|
||||
bool m_finished = false;
|
||||
bool m_combine;
|
||||
};
|
||||
QVector<OrderDownload> m_orderDownloads;
|
||||
OrderModel *m_orderModel;
|
||||
|
||||
Executable
+259
@@ -0,0 +1,259 @@
|
||||
/* Copyright (C) 2004-2021 Robert Griebl. All rights reserved.
|
||||
**
|
||||
** This file is part of BrickStore.
|
||||
**
|
||||
** This file may be distributed and/or modified under the terms of the GNU
|
||||
** General Public License version 2 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this file.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
** See http://fsf.org/licensing/licenses/gpl.html for GPL licensing information.
|
||||
*/
|
||||
#include <QRegularExpression>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include "bricklink.h"
|
||||
#include "lot.h"
|
||||
|
||||
|
||||
Lot::Lot(const BrickLink::Color *color, const BrickLink::Item *item)
|
||||
{
|
||||
m_item = item;
|
||||
m_color = color;
|
||||
|
||||
//TODO: replace with member initializers when switching to c++20
|
||||
m_status = BrickLink::Status::Include;
|
||||
m_condition = BrickLink::Condition::New;
|
||||
m_scondition = BrickLink::SubCondition::None;
|
||||
m_retain = false;
|
||||
m_stockroom = BrickLink::Stockroom::None;
|
||||
m_alternate = false;
|
||||
m_alt_id = 0;
|
||||
m_cpart = false;
|
||||
}
|
||||
|
||||
Lot::Lot(const Lot ©)
|
||||
{
|
||||
*this = copy;
|
||||
}
|
||||
|
||||
Lot &Lot::operator=(const Lot ©)
|
||||
{
|
||||
if (this == ©)
|
||||
return *this;
|
||||
|
||||
m_item = copy.m_item;
|
||||
m_color = copy.m_color;
|
||||
|
||||
m_incomplete.reset(copy.m_incomplete ? new BrickLink::Incomplete(*copy.m_incomplete.get())
|
||||
: nullptr);
|
||||
|
||||
m_status = copy.m_status;
|
||||
m_condition = copy.m_condition;
|
||||
m_scondition = copy.m_scondition;
|
||||
m_retain = copy.m_retain;
|
||||
m_stockroom = copy.m_stockroom;
|
||||
m_alternate = copy.m_alternate;
|
||||
m_alt_id = copy.m_alt_id;
|
||||
m_cpart = copy.m_cpart;
|
||||
m_lot_id = copy.m_lot_id;
|
||||
m_reserved = copy.m_reserved;
|
||||
m_comments = copy.m_comments;
|
||||
m_remarks = copy.m_remarks;
|
||||
m_quantity = copy.m_quantity;
|
||||
m_bulk_quantity = copy.m_bulk_quantity;
|
||||
m_tier_quantity[0] = copy.m_tier_quantity[0];
|
||||
m_tier_quantity[1] = copy.m_tier_quantity[1];
|
||||
m_tier_quantity[2] = copy.m_tier_quantity[2];
|
||||
m_sale = copy.m_sale;
|
||||
m_price = copy.m_price;
|
||||
m_cost = copy.m_cost;
|
||||
m_tier_price[0] = copy.m_tier_price[0];
|
||||
m_tier_price[1] = copy.m_tier_price[1];
|
||||
m_tier_price[2] = copy.m_tier_price[2];
|
||||
m_weight = copy.m_weight;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Lot::operator!=(const Lot &cmp) const
|
||||
{
|
||||
return !operator==(cmp);
|
||||
}
|
||||
|
||||
bool Lot::operator==(const Lot &cmp) const
|
||||
{
|
||||
return (!m_incomplete && !cmp.m_incomplete)
|
||||
&& (m_item == cmp.m_item)
|
||||
&& (m_color == cmp.m_color)
|
||||
&& (m_status == cmp.m_status)
|
||||
&& (m_condition == cmp.m_condition)
|
||||
&& (m_scondition == cmp.m_scondition)
|
||||
&& (m_retain == cmp.m_retain)
|
||||
&& (m_stockroom == cmp.m_stockroom)
|
||||
&& (m_lot_id == cmp.m_lot_id)
|
||||
&& (m_reserved == cmp.m_reserved)
|
||||
&& (m_comments == cmp.m_comments)
|
||||
&& (m_remarks == cmp.m_remarks)
|
||||
&& (m_quantity == cmp.m_quantity)
|
||||
&& (m_bulk_quantity == cmp.m_bulk_quantity)
|
||||
&& (m_tier_quantity[0] == cmp.m_tier_quantity[0])
|
||||
&& (m_tier_quantity[1] == cmp.m_tier_quantity[1])
|
||||
&& (m_tier_quantity[2] == cmp.m_tier_quantity[2])
|
||||
&& (m_sale == cmp.m_sale)
|
||||
&& qFuzzyCompare(m_price, cmp.m_price)
|
||||
&& qFuzzyCompare(m_cost, cmp.m_cost)
|
||||
&& qFuzzyCompare(m_tier_price[0], cmp.m_tier_price[0])
|
||||
&& qFuzzyCompare(m_tier_price[1], cmp.m_tier_price[1])
|
||||
&& qFuzzyCompare(m_tier_price[2], cmp.m_tier_price[2])
|
||||
&& qFuzzyCompare(m_weight, cmp.m_weight);
|
||||
}
|
||||
|
||||
Lot::~Lot()
|
||||
{ }
|
||||
|
||||
bool Lot::mergeFrom(const Lot &from, bool useCostQtyAg)
|
||||
{
|
||||
if ((&from == this) ||
|
||||
(from.isIncomplete() || isIncomplete()) ||
|
||||
(from.item() != item()) ||
|
||||
(from.color() != color()) ||
|
||||
(from.condition() != condition()) ||
|
||||
(from.subCondition() != subCondition()))
|
||||
return false;
|
||||
|
||||
if (useCostQtyAg) {
|
||||
setCost((cost() * quantity() + from.cost() * from.quantity()) / (quantity() + from.quantity()));
|
||||
} else if (!qFuzzyIsNull(from.cost()) && qFuzzyIsNull(cost())) {
|
||||
setCost(from.cost());
|
||||
}
|
||||
setQuantity(quantity() + from.quantity());
|
||||
|
||||
if (!qFuzzyIsNull(from.price()) && qFuzzyIsNull(price()))
|
||||
setPrice(from.price());
|
||||
if ((from.bulkQuantity() != 1) && (bulkQuantity() == 1))
|
||||
setBulkQuantity(from.bulkQuantity());
|
||||
if ((from.sale()) && !sale())
|
||||
setSale(from.sale());
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!qFuzzyIsNull(from.tierPrice(i)) && qFuzzyIsNull(tierPrice(i)))
|
||||
setTierPrice(i, from.tierPrice(i));
|
||||
if (from.tierQuantity(i) && !tierQuantity(i))
|
||||
setTierQuantity(i, from.tierQuantity(i));
|
||||
}
|
||||
|
||||
if (!from.remarks().isEmpty() && !remarks().isEmpty() && (from.remarks() != remarks())) {
|
||||
QRegularExpression fromRe { u"\\b" % QRegularExpression::escape(from.remarks()) % u"\\b" };
|
||||
|
||||
if (!fromRe.match(remarks()).hasMatch()) {
|
||||
QRegularExpression thisRe { u"\\b" % QRegularExpression::escape(remarks()) % u"\\b" };
|
||||
|
||||
if (thisRe.match(from.remarks()).hasMatch())
|
||||
setRemarks(from.remarks());
|
||||
else
|
||||
setRemarks(remarks() % u" " % from.remarks());
|
||||
}
|
||||
} else if (!from.remarks().isEmpty()) {
|
||||
setRemarks(from.remarks());
|
||||
}
|
||||
|
||||
if (!from.comments().isEmpty() && !comments().isEmpty() && (from.comments() != comments())) {
|
||||
QRegularExpression fromRe { u"\\b" % QRegularExpression::escape(from.comments()) % u"\\b" };
|
||||
|
||||
if (!fromRe.match(comments()).hasMatch()) {
|
||||
QRegularExpression thisRe { u"\\b" % QRegularExpression::escape(comments()) % u"\\b" };
|
||||
|
||||
if (thisRe.match(from.comments()).hasMatch())
|
||||
setComments(from.comments());
|
||||
else
|
||||
setComments(comments() % u" " % from.comments());
|
||||
}
|
||||
} else if (!from.comments().isEmpty()) {
|
||||
setComments(from.comments());
|
||||
}
|
||||
|
||||
if (!from.reserved().isEmpty() && reserved().isEmpty())
|
||||
setReserved(from.reserved());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Lot::save(QDataStream &ds) const
|
||||
{
|
||||
ds << QByteArray("II") << qint32(2)
|
||||
<< itemId()
|
||||
<< qint8(itemType() ? itemType()->id() : char(-1))
|
||||
<< uint(color() ? color()->id() : uint(0xffffffff))
|
||||
<< qint8(m_status) << qint8(m_condition) << qint8(m_scondition) << qint8(m_retain ? 1 : 0)
|
||||
<< qint8(m_stockroom) << m_lot_id << m_reserved << m_comments << m_remarks
|
||||
<< m_quantity << m_bulk_quantity
|
||||
<< m_tier_quantity[0] << m_tier_quantity[1] << m_tier_quantity[2]
|
||||
<< m_sale << m_price << m_cost
|
||||
<< m_tier_price[0] << m_tier_price[1] << m_tier_price[2]
|
||||
<< m_weight;
|
||||
}
|
||||
|
||||
Lot *Lot::restore(QDataStream &ds)
|
||||
{
|
||||
QScopedPointer<Lot> lot;
|
||||
|
||||
QByteArray tag;
|
||||
qint32 version;
|
||||
ds >> tag >> version;
|
||||
if ((ds.status() != QDataStream::Ok) || (tag != "II") || (version != 2))
|
||||
return nullptr;
|
||||
|
||||
QString itemid;
|
||||
uint colorid = 0;
|
||||
qint8 itemtypeid = 0;
|
||||
|
||||
ds >> itemid >> itemtypeid >> colorid;
|
||||
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return nullptr;
|
||||
|
||||
auto item = BrickLink::core()->item(itemtypeid, itemid);
|
||||
auto color = BrickLink::core()->color(colorid);
|
||||
QScopedPointer<BrickLink::Incomplete> inc;
|
||||
|
||||
if (!item || !color) {
|
||||
inc.reset(new BrickLink::Incomplete);
|
||||
if (!item) {
|
||||
inc->m_item_id = itemid;
|
||||
inc->m_itemtype_id = QLatin1Char(itemtypeid);
|
||||
}
|
||||
if (!color)
|
||||
inc->m_color_name = QString::number(colorid);
|
||||
|
||||
if (BrickLink::core()->applyChangeLog(item, color, inc.get()))
|
||||
inc.reset();
|
||||
}
|
||||
lot.reset(new Lot(color, item));
|
||||
if (inc)
|
||||
lot->setIncomplete(inc.take());
|
||||
|
||||
// alternate, cpart and altid are left out on purpose!
|
||||
|
||||
qint8 status = 0, cond = 0, scond = 0, retain = 0, stockroom = 0;
|
||||
ds >> status >> cond >> scond >> retain >> stockroom
|
||||
>> lot->m_lot_id >> lot->m_reserved >> lot->m_comments >> lot->m_remarks
|
||||
>> lot->m_quantity >> lot->m_bulk_quantity
|
||||
>> lot->m_tier_quantity[0] >> lot->m_tier_quantity[1] >> lot->m_tier_quantity[2]
|
||||
>> lot->m_sale >> lot->m_price >> lot->m_cost
|
||||
>> lot->m_tier_price[0] >> lot->m_tier_price[1] >> lot->m_tier_price[2]
|
||||
>> lot->m_weight;
|
||||
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return nullptr;
|
||||
|
||||
lot->m_status = static_cast<BrickLink::Status>(status);
|
||||
lot->m_condition = static_cast<BrickLink::Condition>(cond);
|
||||
lot->m_scondition = static_cast<BrickLink::SubCondition>(scond);
|
||||
lot->m_retain = (retain);
|
||||
lot->m_stockroom = static_cast<BrickLink::Stockroom>(stockroom);
|
||||
|
||||
return lot.take();
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/* Copyright (C) 2004-2021 Robert Griebl. All rights reserved.
|
||||
**
|
||||
** This file is part of BrickStore.
|
||||
**
|
||||
** This file may be distributed and/or modified under the terms of the GNU
|
||||
** General Public License version 2 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this file.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
** See http://fsf.org/licensing/licenses/gpl.html for GPL licensing information.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QScopedPointer>
|
||||
#include <QColor>
|
||||
|
||||
#include "bricklink.h"
|
||||
|
||||
|
||||
class Lot
|
||||
{
|
||||
public:
|
||||
Lot(const BrickLink::Color *color = nullptr, const BrickLink::Item *item = nullptr);
|
||||
Lot(const Lot ©);
|
||||
~Lot();
|
||||
|
||||
Lot &operator=(const Lot ©);
|
||||
bool operator==(const Lot &cmp) const;
|
||||
bool operator!=(const Lot &cmp) const;
|
||||
|
||||
const BrickLink::Item *item() const { return m_item; }
|
||||
void setItem(const BrickLink::Item *i) { m_item = i; }
|
||||
const BrickLink::Category *category() const { return m_item ? m_item->category() : nullptr; }
|
||||
const BrickLink::ItemType *itemType() const { return m_item ? m_item->itemType() : nullptr; }
|
||||
const BrickLink::Color *color() const { return m_color; }
|
||||
void setColor(const BrickLink::Color *c) { m_color = c; }
|
||||
|
||||
QString itemId() const { return m_item ? m_item->id()
|
||||
: (m_incomplete ? m_incomplete->m_item_id
|
||||
: QString()); }
|
||||
QString itemName() const { return m_item ? m_item->name()
|
||||
: (m_incomplete ? m_incomplete->m_item_name
|
||||
: QString()); }
|
||||
QString colorId() const { return m_color ? QString::number(m_color->id())
|
||||
: (m_incomplete ? m_incomplete->m_color_id
|
||||
: QString()); }
|
||||
QString colorName() const { return m_color ? m_color->name()
|
||||
: (m_incomplete ? m_incomplete->m_color_name
|
||||
: QString()); }
|
||||
QString categoryId() const { return category() ? QString::number(category()->id())
|
||||
: (m_incomplete ? m_incomplete->m_category_id
|
||||
: QString()); }
|
||||
QString categoryName() const { return category() ? category()->name()
|
||||
: (m_incomplete ? m_incomplete->m_category_name
|
||||
: QString()); }
|
||||
QString itemTypeId() const { return itemType() ? QString(QChar(itemType()->id()))
|
||||
: (m_incomplete ? m_incomplete->m_itemtype_id
|
||||
: QString()); }
|
||||
QString itemTypeName() const { return itemType() ? itemType()->name()
|
||||
: (m_incomplete ? m_incomplete->m_itemtype_name
|
||||
: QString()); }
|
||||
int itemYearReleased() const { return m_item ? m_item->yearReleased() : 0; }
|
||||
|
||||
BrickLink::Status status() const { return m_status; }
|
||||
void setStatus(BrickLink::Status s) { m_status = s; }
|
||||
BrickLink::Condition condition() const { return m_condition; }
|
||||
void setCondition(BrickLink::Condition c) { m_condition = c; }
|
||||
BrickLink::SubCondition subCondition() const { return m_scondition; }
|
||||
void setSubCondition(BrickLink::SubCondition c) { m_scondition = c; }
|
||||
QString comments() const { return m_comments; }
|
||||
void setComments(const QString &n) { m_comments = n; }
|
||||
QString remarks() const { return m_remarks; }
|
||||
void setRemarks(const QString &r) { m_remarks = r; }
|
||||
|
||||
int quantity() const { return m_quantity; }
|
||||
void setQuantity(int q) { m_quantity = q; }
|
||||
int bulkQuantity() const { return m_bulk_quantity; }
|
||||
void setBulkQuantity(int q) { m_bulk_quantity = qMax(1, q); }
|
||||
int tierQuantity(int i) const { return m_tier_quantity [qBound(0, i, 2)]; }
|
||||
void setTierQuantity(int i, int q) { m_tier_quantity [qBound(0, i, 2)] = q; }
|
||||
double price() const { return m_price; }
|
||||
void setPrice(double p) { m_price = p; }
|
||||
double tierPrice(int i) const { return m_tier_price[qBound(0, i, 2)]; }
|
||||
void setTierPrice(int i, double p) { m_tier_price[qBound(0, i, 2)] = p; }
|
||||
|
||||
int sale() const { return m_sale; }
|
||||
void setSale(int s) { m_sale = qMax(-99, qMin(100, s)); }
|
||||
double total() const { return m_price * m_quantity; }
|
||||
void setCost(double c) { m_cost = c; }
|
||||
double cost() const { return m_cost; }
|
||||
|
||||
uint lotId() const { return m_lot_id; }
|
||||
void setLotId(uint lid) { m_lot_id = lid; }
|
||||
|
||||
bool retain() const { return m_retain; }
|
||||
void setRetain(bool r) { m_retain = r; }
|
||||
BrickLink::Stockroom stockroom() const { return m_stockroom; }
|
||||
void setStockroom(BrickLink::Stockroom sr) { m_stockroom = sr; }
|
||||
|
||||
bool hasCustomWeight() const { return (m_weight > 0); }
|
||||
double weight() const { return hasCustomWeight() ? m_weight : (m_item ? m_item->weight() : 0); }
|
||||
double totalWeight() const { return weight() * quantity(); }
|
||||
void setWeight(double w) { m_weight = (w <= 0) ? 0 : w; }
|
||||
void setTotalWeight(double w) { m_weight = (w <= 0) ? 0 : (w / (m_quantity ? qAbs(m_quantity) : 1)); }
|
||||
|
||||
QString reserved() const { return m_reserved; }
|
||||
void setReserved(const QString &r) { m_reserved = r; }
|
||||
|
||||
bool alternate() const { return m_alternate; }
|
||||
void setAlternate(bool a) { m_alternate = a; }
|
||||
uint alternateId() const { return m_alt_id; }
|
||||
void setAlternateId(uint aid) { m_alt_id = aid; }
|
||||
|
||||
bool counterPart() const { return m_cpart; }
|
||||
void setCounterPart(bool b) { m_cpart = b; }
|
||||
|
||||
// needed for the copy/merge template code -- std::bind doesn't work there
|
||||
int tierQuantity0() const { return tierQuantity(0); }
|
||||
int tierQuantity1() const { return tierQuantity(1); }
|
||||
int tierQuantity2() const { return tierQuantity(2); }
|
||||
void setTierQuantity0(int q) { setTierQuantity(0, q); }
|
||||
void setTierQuantity1(int q) { setTierQuantity(1, q); }
|
||||
void setTierQuantity2(int q) { setTierQuantity(2, q); }
|
||||
double tierPrice0() const { return tierPrice(0); }
|
||||
double tierPrice1() const { return tierPrice(1); }
|
||||
double tierPrice2() const { return tierPrice(2); }
|
||||
void setTierPrice0(double p) { setTierPrice(0, p); }
|
||||
void setTierPrice1(double p) { setTierPrice(1, p); }
|
||||
void setTierPrice2(double p) { setTierPrice(2, p); }
|
||||
|
||||
BrickLink::Incomplete *isIncomplete() const { return m_incomplete.data(); }
|
||||
void setIncomplete(BrickLink::Incomplete *inc) { m_incomplete.reset(inc); }
|
||||
|
||||
bool mergeFrom(const Lot &merge, bool useCostQtyAg = false);
|
||||
|
||||
void save(QDataStream &ds) const;
|
||||
static Lot *restore(QDataStream &ds);
|
||||
|
||||
private:
|
||||
const BrickLink::Item * m_item;
|
||||
const BrickLink::Color * m_color;
|
||||
|
||||
QScopedPointer<BrickLink::Incomplete> m_incomplete;
|
||||
|
||||
BrickLink::Status m_status : 3;
|
||||
BrickLink::Condition m_condition : 2;
|
||||
BrickLink::SubCondition m_scondition: 3;
|
||||
bool m_retain : 1;
|
||||
BrickLink::Stockroom m_stockroom : 5;
|
||||
bool m_alternate : 1;
|
||||
uint m_alt_id : 6;
|
||||
bool m_cpart : 1;
|
||||
|
||||
uint m_lot_id = 0;
|
||||
QString m_reserved;
|
||||
|
||||
QString m_comments;
|
||||
QString m_remarks;
|
||||
|
||||
int m_quantity = 0;
|
||||
int m_bulk_quantity = 1;
|
||||
int m_tier_quantity[3] = { 0, 0, 0 };
|
||||
int m_sale = 0;
|
||||
|
||||
double m_price = 0;
|
||||
double m_cost = 0;
|
||||
double m_tier_price[3] = { 0, 0, 0 };
|
||||
|
||||
double m_weight = 0;
|
||||
|
||||
friend class Core;
|
||||
};
|
||||
|
||||
typedef QVector<Lot *> LotList;
|
||||
@@ -176,11 +176,12 @@ QVariantList Item::knownColors() const
|
||||
|
||||
QVariantList Item::consistsOf() const
|
||||
{
|
||||
const auto invItems = wrapped->consistsOf();
|
||||
QVariantList result;
|
||||
for (auto invItem : invItems)
|
||||
result << QVariant::fromValue(InvItem { invItem, nullptr });
|
||||
return result;
|
||||
// const auto consists = wrapped->consistsOf();
|
||||
// QVariantList result;
|
||||
// for (auto auto &co : consists)
|
||||
// result << QVariant::fromValue(Lot { nullptr, nullptr });
|
||||
// return result;
|
||||
return { };
|
||||
}
|
||||
|
||||
|
||||
@@ -287,71 +288,71 @@ double PriceGuide::price(::BrickLink::Time time, ::BrickLink::Condition conditio
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
InvItem::InvItem(::BrickLink::InvItem *invItem, Document *document)
|
||||
: WrapperBase(invItem)
|
||||
Lot::Lot(::Lot *lot, Document *document)
|
||||
: WrapperBase(lot)
|
||||
, doc(document)
|
||||
{ }
|
||||
|
||||
QImage InvItem::image() const
|
||||
QImage Lot::image() const
|
||||
{
|
||||
auto pic = ::BrickLink::core()->picture(get()->item(), get()->color(), true);
|
||||
return pic->image();
|
||||
}
|
||||
|
||||
//Item InvItem::item() const
|
||||
//Item Lot::item() const
|
||||
//{
|
||||
// return wrapped->item();
|
||||
//}
|
||||
|
||||
//void InvItem::setItem(Item item)
|
||||
//void Lot::setItem(Item item)
|
||||
//{
|
||||
// set().to().setItem(item.wrappedObject());
|
||||
//}
|
||||
|
||||
//Color InvItem::color() const
|
||||
//Color Lot::color() const
|
||||
//{
|
||||
// return wrapped->color();
|
||||
//}
|
||||
|
||||
//void InvItem::setColor(Color color)
|
||||
//void Lot::setColor(Color color)
|
||||
//{
|
||||
// set().to().setColor(color.wrappedObject());
|
||||
//}
|
||||
|
||||
//int InvItem::quantity() const
|
||||
//int Lot::quantity() const
|
||||
//{
|
||||
// return wrapped->quantity();
|
||||
//}
|
||||
|
||||
//void InvItem::setQuantity(int q)
|
||||
//void Lot::setQuantity(int q)
|
||||
//{
|
||||
// set().to().setQuantity(q);
|
||||
//}
|
||||
|
||||
InvItem::Setter::Setter(InvItem *invItem)
|
||||
: m_invItem((invItem && !invItem->isNull()) ? invItem : nullptr)
|
||||
Lot::Setter::Setter(Lot *lot)
|
||||
: m_lot((lot && !lot->isNull()) ? lot : nullptr)
|
||||
{
|
||||
if (m_invItem)
|
||||
m_to = *m_invItem->wrapped;
|
||||
if (m_lot)
|
||||
m_to = *m_lot->wrapped;
|
||||
}
|
||||
|
||||
::BrickLink::InvItem *InvItem::Setter::to()
|
||||
::Lot *Lot::Setter::to()
|
||||
{
|
||||
return &m_to;
|
||||
}
|
||||
|
||||
InvItem::Setter::~Setter()
|
||||
Lot::Setter::~Setter()
|
||||
{
|
||||
if (m_invItem && (*m_invItem->wrapped != m_to))
|
||||
m_invItem->doc->changeItem(m_invItem, m_to);
|
||||
if (m_lot && (*m_lot->wrapped != m_to))
|
||||
m_lot->doc->changeLot(m_lot, m_to);
|
||||
}
|
||||
|
||||
InvItem::Setter InvItem::set()
|
||||
Lot::Setter Lot::set()
|
||||
{
|
||||
return Setter(this);
|
||||
}
|
||||
|
||||
::BrickLink::InvItem *InvItem::get() const
|
||||
::Lot *Lot::get() const
|
||||
{
|
||||
return wrapped;
|
||||
}
|
||||
@@ -403,13 +404,13 @@ bool Document::isWrapperFor(::Document *doc) const
|
||||
return (d == doc);
|
||||
}
|
||||
|
||||
bool Document::changeItem(InvItem *from, ::BrickLink::InvItem &to)
|
||||
bool Document::changeLot(Lot *from, ::Lot &to)
|
||||
{
|
||||
if (isReadOnly(this))
|
||||
return false;
|
||||
if (this != from->doc)
|
||||
return false;
|
||||
d->changeItem(from->wrapped, to);
|
||||
d->changeLot(from->wrapped, to);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -418,32 +419,32 @@ int Document::count() const
|
||||
return d->rowCount();
|
||||
}
|
||||
|
||||
InvItem Document::invItem(int index)
|
||||
Lot Document::lot(int index)
|
||||
{
|
||||
if (index < 0 || index >= d->rowCount())
|
||||
return InvItem { };
|
||||
return InvItem(d->items().at(index), this);
|
||||
return Lot { };
|
||||
return Lot(d->lots().at(index), this);
|
||||
}
|
||||
|
||||
void Document::deleteInvItem(InvItem ii)
|
||||
void Document::deleteLot(Lot ii)
|
||||
{
|
||||
if (isReadOnly(this))
|
||||
return;
|
||||
|
||||
if (!ii.isNull() && ii.doc == this)
|
||||
d->removeItem(static_cast<::Document::Item *>(ii.wrapped));
|
||||
d->removeLot(static_cast<::Lot *>(ii.wrapped));
|
||||
}
|
||||
|
||||
InvItem Document::addInvItem(Item item, Color color)
|
||||
Lot Document::addLot(Item item, Color color)
|
||||
{
|
||||
if (isReadOnly(this))
|
||||
return InvItem { };
|
||||
return Lot { };
|
||||
|
||||
auto di = new ::Document::Item();
|
||||
auto di = new ::Lot();
|
||||
di->setItem(item.wrappedObject());
|
||||
di->setColor(color.wrappedObject());
|
||||
d->appendItem(di);
|
||||
return InvItem(di, this);
|
||||
d->appendLot(di);
|
||||
return Lot(di, this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "bricklink.h"
|
||||
#include "document.h"
|
||||
#include "lot.h"
|
||||
|
||||
class Window;
|
||||
|
||||
@@ -26,7 +27,7 @@ class Color;
|
||||
class Category;
|
||||
class ItemType;
|
||||
class Item;
|
||||
class InvItem;
|
||||
class Lot;
|
||||
class PriceGuide;
|
||||
class Picture;
|
||||
class Document;
|
||||
@@ -191,7 +192,7 @@ public:
|
||||
|
||||
friend class BrickLink;
|
||||
friend class Document;
|
||||
friend class InvItem;
|
||||
friend class Lot;
|
||||
};
|
||||
|
||||
|
||||
@@ -282,7 +283,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class InvItem : WrapperBase<::BrickLink::InvItem>
|
||||
class Lot : WrapperBase<::Lot>
|
||||
{
|
||||
Q_GADGET
|
||||
Q_PROPERTY(bool isNull READ isNull)
|
||||
@@ -336,7 +337,7 @@ class InvItem : WrapperBase<::BrickLink::InvItem>
|
||||
Q_PROPERTY(QImage image READ image)
|
||||
|
||||
public:
|
||||
InvItem(::BrickLink::InvItem *invItem = nullptr, Document *document = nullptr);
|
||||
Lot(::Lot *lot = nullptr, Document *document = nullptr);
|
||||
|
||||
Item item() const { return get()->item(); }
|
||||
void setItem(Item item) { set().to()->setItem(item.wrappedObject()); }
|
||||
@@ -417,16 +418,16 @@ private:
|
||||
class Setter
|
||||
{
|
||||
public:
|
||||
Setter(InvItem *invItem);
|
||||
::BrickLink::InvItem *to();
|
||||
Setter(Lot *lot);
|
||||
::Lot *to();
|
||||
~Setter();
|
||||
|
||||
private:
|
||||
InvItem *m_invItem;
|
||||
::BrickLink::InvItem m_to;
|
||||
Lot *m_lot;
|
||||
::Lot m_to;
|
||||
};
|
||||
Setter set();
|
||||
::BrickLink::InvItem *get() const;
|
||||
::Lot *get() const;
|
||||
|
||||
Document *doc = nullptr;
|
||||
|
||||
@@ -489,17 +490,17 @@ public:
|
||||
Document(::Document *doc);
|
||||
bool isWrapperFor(::Document *doc) const;
|
||||
|
||||
bool changeItem(InvItem *from, ::BrickLink::InvItem &to);
|
||||
bool changeLot(Lot *from, ::Lot &to);
|
||||
|
||||
int count() const;
|
||||
|
||||
Q_INVOKABLE InvItem invItem(int index);
|
||||
Q_INVOKABLE void deleteInvItem(InvItem ii);
|
||||
Q_INVOKABLE InvItem addInvItem(Item item, Color color);
|
||||
Q_INVOKABLE Lot lot(int index);
|
||||
Q_INVOKABLE void deleteLot(Lot ii);
|
||||
Q_INVOKABLE Lot addLot(Item item, Color color);
|
||||
|
||||
// Q_INVOKABLE InvItem addItem(InvItem invItem, Flags consolidate)
|
||||
// Q_INVOKABLE Lot addItem(Lot lot, Flags consolidate)
|
||||
// {
|
||||
// if (invItem.doc != this) {
|
||||
// if (m_lot.doc != this) {
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
@@ -559,7 +560,7 @@ Q_DECLARE_METATYPE(QmlWrapper::Color)
|
||||
Q_DECLARE_METATYPE(QmlWrapper::Category)
|
||||
Q_DECLARE_METATYPE(QmlWrapper::ItemType)
|
||||
Q_DECLARE_METATYPE(QmlWrapper::Item)
|
||||
Q_DECLARE_METATYPE(QmlWrapper::InvItem)
|
||||
Q_DECLARE_METATYPE(QmlWrapper::Lot)
|
||||
Q_DECLARE_METATYPE(QmlWrapper::Picture)
|
||||
Q_DECLARE_METATYPE(QmlWrapper::PriceGuide)
|
||||
Q_DECLARE_METATYPE(QmlWrapper::Order)
|
||||
|
||||
@@ -166,11 +166,11 @@ void PrintingScriptTemplate::executePrint(QPaintDevice *pd, Window *win, bool se
|
||||
if (!wrappedDoc)
|
||||
throw Exception(tr("Cannot print without a document."));
|
||||
|
||||
const auto items = win->document()->sortItemList(selectionOnly ? win->selection()
|
||||
: win->document()->items());
|
||||
const auto lots = win->document()->sortLotList(selectionOnly ? win->selectedLots()
|
||||
: win->document()->lots());
|
||||
QVariantList itemList;
|
||||
for (auto item : items)
|
||||
itemList << QVariant::fromValue(QmlWrapper::InvItem(item, wrappedDoc));
|
||||
for (auto lot : lots)
|
||||
itemList << QVariant::fromValue(QmlWrapper::Lot(lot, wrappedDoc));
|
||||
|
||||
QQmlEngine *engine = m_script->qmlEngine();
|
||||
QJSValueList args = { engine->toScriptValue(job.data()),
|
||||
|
||||
@@ -92,7 +92,7 @@ bool ScriptManager::initialize(::BrickLink::Core *core)
|
||||
qRegisterMetaType<QmlWrapper::ItemType>("ItemType");
|
||||
qRegisterMetaType<QmlWrapper::Category>("Category");
|
||||
qRegisterMetaType<QmlWrapper::Item>("Item");
|
||||
qRegisterMetaType<QmlWrapper::InvItem>("InvItem");
|
||||
qRegisterMetaType<QmlWrapper::Lot>("Lot");
|
||||
qRegisterMetaType<QmlWrapper::PriceGuide>("PriceGuide");
|
||||
qRegisterMetaType<QmlWrapper::Picture>("Picture");
|
||||
qRegisterMetaType<QmlWrapper::Order>("Order");
|
||||
|
||||
@@ -48,7 +48,7 @@ SelectDocument::SelectDocument(const Document *self, QWidget *parent)
|
||||
layout->addWidget(m_document, 1, 0, 1, 2);
|
||||
layout->addWidget(m_documentList, 2, 1, 1, 1);
|
||||
|
||||
m_itemsFromClipboard = BrickLink::InvItemMimeData::items(QApplication::clipboard()->mimeData());
|
||||
m_lotsFromClipboard = DocumentLotsMimeData::lots(QApplication::clipboard()->mimeData());
|
||||
|
||||
foreach (const Document *doc, Document::allDocuments()) {
|
||||
if (doc != self) {
|
||||
@@ -57,7 +57,7 @@ SelectDocument::SelectDocument(const Document *self, QWidget *parent)
|
||||
}
|
||||
}
|
||||
|
||||
bool hasClip = !m_itemsFromClipboard.isEmpty();
|
||||
bool hasClip = !m_lotsFromClipboard.isEmpty();
|
||||
bool hasDocs = m_documentList->count() > 0;
|
||||
|
||||
m_clipboard->setEnabled(hasClip);
|
||||
@@ -81,21 +81,21 @@ SelectDocument::SelectDocument(const Document *self, QWidget *parent)
|
||||
QMetaObject::invokeMethod(this, emitSelected, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
BrickLink::InvItemList SelectDocument::items() const
|
||||
LotList SelectDocument::lots() const
|
||||
{
|
||||
BrickLink::InvItemList list;
|
||||
LotList list;
|
||||
|
||||
if (m_clipboard->isChecked()) {
|
||||
for (const BrickLink::InvItem *item : m_itemsFromClipboard)
|
||||
list.append(new BrickLink::InvItem(*item));
|
||||
for (const Lot *lot : m_lotsFromClipboard)
|
||||
list.append(new Lot(*lot));
|
||||
} else {
|
||||
if (!m_documentList->selectedItems().isEmpty()) {
|
||||
const auto *doc = m_documentList->selectedItems().constFirst()
|
||||
->data(Qt::UserRole).value<const Document *>();
|
||||
if (doc) {
|
||||
const auto items = doc->items();
|
||||
for (const Document::Item *item : items)
|
||||
list.append(new BrickLink::InvItem(*item));
|
||||
const auto lots = doc->lots();
|
||||
for (const Lot *lot : lots)
|
||||
list.append(new Lot(*lot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ BrickLink::InvItemList SelectDocument::items() const
|
||||
|
||||
SelectDocument::~SelectDocument()
|
||||
{
|
||||
qDeleteAll(m_itemsFromClipboard);
|
||||
qDeleteAll(m_lotsFromClipboard);
|
||||
}
|
||||
|
||||
bool SelectDocument::isDocumentSelected() const
|
||||
@@ -139,9 +139,9 @@ SelectDocumentDialog::SelectDocumentDialog(const Document *self, const QString &
|
||||
m_ok, &QAbstractButton::setEnabled);
|
||||
}
|
||||
|
||||
BrickLink::InvItemList SelectDocumentDialog::items() const
|
||||
LotList SelectDocumentDialog::lots() const
|
||||
{
|
||||
return m_sd->items();
|
||||
return m_sd->lots();
|
||||
}
|
||||
|
||||
|
||||
@@ -389,9 +389,9 @@ SelectCopyMergeDialog::SelectCopyMergeDialog(const Document *self, const QString
|
||||
mpage, &WizardPage::setComplete);
|
||||
}
|
||||
|
||||
Document::ItemList SelectCopyMergeDialog::items() const
|
||||
LotList SelectCopyMergeDialog::lots() const
|
||||
{
|
||||
return m_sd->items();
|
||||
return m_sd->lots();
|
||||
}
|
||||
|
||||
Document::MergeMode SelectCopyMergeDialog::defaultMergeMode() const
|
||||
|
||||
@@ -34,13 +34,13 @@ public:
|
||||
~SelectDocument() override;
|
||||
|
||||
bool isDocumentSelected() const;
|
||||
BrickLink::InvItemList items() const;
|
||||
LotList lots() const;
|
||||
|
||||
signals:
|
||||
void documentSelected(bool valid);
|
||||
|
||||
private:
|
||||
BrickLink::InvItemList m_itemsFromClipboard;
|
||||
LotList m_lotsFromClipboard;
|
||||
|
||||
QRadioButton *m_clipboard;
|
||||
QRadioButton *m_document;
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
SelectDocumentDialog(const Document *self, const QString &headertext,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
BrickLink::InvItemList items() const;
|
||||
LotList lots() const;
|
||||
|
||||
private:
|
||||
SelectDocument *m_sd;
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
SelectCopyMergeDialog(const Document *self, const QString &chooseDocText,
|
||||
const QString &chooseFieldsText, QWidget *parent = nullptr);
|
||||
|
||||
Document::ItemList items() const;
|
||||
LotList lots() const;
|
||||
Document::MergeMode defaultMergeMode() const;
|
||||
QHash<Document::Field, Document::MergeMode> fieldMergeModes() const;
|
||||
|
||||
|
||||
+4
-6
@@ -914,11 +914,11 @@ void SelectItem::showContextMenu(const QPoint &p)
|
||||
if (item && item->itemType() && (item->itemType()->id() == 'M') && item->hasInventory()) {
|
||||
auto minifigParts = item->consistsOf();
|
||||
|
||||
for (const BrickLink::InvItem *part : minifigParts) {
|
||||
if (!part || !part->item())
|
||||
for (const BrickLink::Item::ConsistsOf &part : minifigParts) {
|
||||
auto partItem = part.item();
|
||||
if (!partItem)
|
||||
continue;
|
||||
auto partItem = part->item();
|
||||
auto partColor = part->color();
|
||||
auto partColor = part.color();
|
||||
auto partPicture = BrickLink::core()->picture(partItem, partColor, true);
|
||||
|
||||
|
||||
@@ -947,8 +947,6 @@ void SelectItem::showContextMenu(const QPoint &p)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
qDeleteAll(minifigParts);
|
||||
}
|
||||
|
||||
if (!m.isEmpty())
|
||||
|
||||
@@ -32,6 +32,7 @@ SOURCES += \
|
||||
$$PWD/documentdelegate.cpp \
|
||||
$$PWD/framework.cpp \
|
||||
$$PWD/incdecpricesdialog.cpp \
|
||||
$$PWD/lot.cpp \
|
||||
$$PWD/managecolumnlayoutsdialog.cpp \
|
||||
$$PWD/picturewidget.cpp \
|
||||
$$PWD/priceguidewidget.cpp \
|
||||
@@ -54,6 +55,7 @@ HEADERS += \
|
||||
$$PWD/documentdelegate.h \
|
||||
$$PWD/framework.h \
|
||||
$$PWD/incdecpricesdialog.h \
|
||||
$$PWD/lot.h \
|
||||
$$PWD/managecolumnlayoutsdialog.h \
|
||||
$$PWD/picturewidget.h \
|
||||
$$PWD/priceguidewidget.h \
|
||||
|
||||
+20
-20
@@ -61,21 +61,21 @@ TaskPriceGuideWidget::TaskPriceGuideWidget(QWidget *parent)
|
||||
void TaskPriceGuideWidget::windowUpdate(Window *win)
|
||||
{
|
||||
if (m_win) {
|
||||
disconnect(m_win.data(), &Window::selectionChanged,
|
||||
disconnect(m_win.data(), &Window::selectedLotsChanged,
|
||||
this, &TaskPriceGuideWidget::selectionUpdate);
|
||||
disconnect(m_win->document(), &Document::currencyCodeChanged,
|
||||
this, &TaskPriceGuideWidget::currencyUpdate);
|
||||
}
|
||||
m_win = win;
|
||||
if (m_win) {
|
||||
connect(m_win.data(), &Window::selectionChanged,
|
||||
connect(m_win.data(), &Window::selectedLotsChanged,
|
||||
this, &TaskPriceGuideWidget::selectionUpdate);
|
||||
connect(m_win->document(), &Document::currencyCodeChanged,
|
||||
this, &TaskPriceGuideWidget::currencyUpdate);
|
||||
}
|
||||
|
||||
setCurrencyCode(m_win ? m_win->document()->currencyCode() : Config::inst()->defaultCurrencyCode());
|
||||
selectionUpdate(m_win ? m_win->selection() : Document::ItemList());
|
||||
selectionUpdate(m_win ? m_win->selectedLots() : LotList());
|
||||
}
|
||||
|
||||
void TaskPriceGuideWidget::currencyUpdate(const QString &ccode)
|
||||
@@ -83,7 +83,7 @@ void TaskPriceGuideWidget::currencyUpdate(const QString &ccode)
|
||||
setCurrencyCode(ccode);
|
||||
}
|
||||
|
||||
void TaskPriceGuideWidget::selectionUpdate(const Document::ItemList &list)
|
||||
void TaskPriceGuideWidget::selectionUpdate(const LotList &list)
|
||||
{
|
||||
m_selection = list;
|
||||
m_delayTimer.start();
|
||||
@@ -91,15 +91,15 @@ void TaskPriceGuideWidget::selectionUpdate(const Document::ItemList &list)
|
||||
|
||||
void TaskPriceGuideWidget::setPrice(double p)
|
||||
{
|
||||
if (m_win && (m_win->selection().count() == 1)) {
|
||||
Document::Item *pos = m_win->selection().front();
|
||||
Document::Item item = *pos;
|
||||
if (m_win && (m_win->selectedLots().count() == 1)) {
|
||||
Lot *pos = m_win->selectedLots().front();
|
||||
Lot lot = *pos;
|
||||
|
||||
auto doc = m_win->document();
|
||||
p *= Currency::inst()->rate(doc->currencyCode());
|
||||
item.setPrice(p);
|
||||
lot.setPrice(p);
|
||||
|
||||
doc->changeItem(pos, item);
|
||||
doc->changeLot(pos, lot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ TaskInfoWidget::TaskInfoWidget(QWidget *parent)
|
||||
void TaskInfoWidget::windowUpdate(Window *win)
|
||||
{
|
||||
if (m_win) {
|
||||
disconnect(m_win.data(), &Window::selectionChanged,
|
||||
disconnect(m_win.data(), &Window::selectedLotsChanged,
|
||||
this, &TaskInfoWidget::selectionUpdate);
|
||||
disconnect(m_win->document(), &Document::statisticsChanged,
|
||||
this, &TaskInfoWidget::statisticsUpdate);
|
||||
@@ -197,7 +197,7 @@ void TaskInfoWidget::windowUpdate(Window *win)
|
||||
}
|
||||
m_win = win;
|
||||
if (m_win) {
|
||||
connect(m_win.data(), &Window::selectionChanged,
|
||||
connect(m_win.data(), &Window::selectedLotsChanged,
|
||||
this, &TaskInfoWidget::selectionUpdate);
|
||||
connect(m_win->document(), &Document::statisticsChanged,
|
||||
this, &TaskInfoWidget::statisticsUpdate);
|
||||
@@ -205,12 +205,12 @@ void TaskInfoWidget::windowUpdate(Window *win)
|
||||
this, &TaskInfoWidget::currencyUpdate);
|
||||
}
|
||||
|
||||
selectionUpdate(m_win ? m_win->selection() : Document::ItemList());
|
||||
selectionUpdate(m_win ? m_win->selectedLots() : LotList());
|
||||
}
|
||||
|
||||
void TaskInfoWidget::currencyUpdate()
|
||||
{
|
||||
selectionUpdate(m_win ? m_win->selection() : Document::ItemList());
|
||||
selectionUpdate(m_win ? m_win->selectedLots() : LotList());
|
||||
}
|
||||
|
||||
void TaskInfoWidget::statisticsUpdate()
|
||||
@@ -220,7 +220,7 @@ void TaskInfoWidget::statisticsUpdate()
|
||||
selectionUpdate(m_selection);
|
||||
}
|
||||
|
||||
void TaskInfoWidget::selectionUpdate(const Document::ItemList &list)
|
||||
void TaskInfoWidget::selectionUpdate(const LotList &list)
|
||||
{
|
||||
m_selection = list;
|
||||
m_delayTimer.start();
|
||||
@@ -236,7 +236,7 @@ void TaskInfoWidget::delayedSelectionUpdate()
|
||||
setCurrentWidget(m_pic);
|
||||
} else {
|
||||
Document::Statistics stat(m_win->document(),
|
||||
m_selection.isEmpty() ? m_win->document()->items() : m_selection);
|
||||
m_selection.isEmpty() ? m_win->document()->lots() : m_selection);
|
||||
|
||||
QLocale loc;
|
||||
QString ccode = m_win->document()->currencyCode();
|
||||
@@ -304,7 +304,7 @@ void TaskInfoWidget::languageChange()
|
||||
void TaskInfoWidget::refresh()
|
||||
{
|
||||
if (m_win)
|
||||
selectionUpdate(m_win->selection());
|
||||
selectionUpdate(m_win->selectedLots());
|
||||
}
|
||||
|
||||
void TaskInfoWidget::changeEvent(QEvent *e)
|
||||
@@ -342,19 +342,19 @@ TaskAppearsInWidget::TaskAppearsInWidget(QWidget *parent)
|
||||
void TaskAppearsInWidget::windowUpdate(Window *win)
|
||||
{
|
||||
if (m_win) {
|
||||
disconnect(m_win.data(), &Window::selectionChanged,
|
||||
disconnect(m_win.data(), &Window::selectedLotsChanged,
|
||||
this, &TaskAppearsInWidget::selectionUpdate);
|
||||
}
|
||||
m_win = win;
|
||||
if (m_win) {
|
||||
connect(m_win.data(), &Window::selectionChanged,
|
||||
connect(m_win.data(), &Window::selectedLotsChanged,
|
||||
this, &TaskAppearsInWidget::selectionUpdate);
|
||||
}
|
||||
|
||||
selectionUpdate(m_win ? m_win->selection() : Document::ItemList());
|
||||
selectionUpdate(m_win ? m_win->selectedLots() : LotList());
|
||||
}
|
||||
|
||||
void TaskAppearsInWidget::selectionUpdate(const Document::ItemList &list)
|
||||
void TaskAppearsInWidget::selectionUpdate(const LotList &list)
|
||||
{
|
||||
m_selection = list;
|
||||
m_delayTimer.start();
|
||||
|
||||
+6
-6
@@ -35,7 +35,7 @@ public:
|
||||
|
||||
protected slots:
|
||||
void windowUpdate(Window *win);
|
||||
void selectionUpdate(const Document::ItemList &list);
|
||||
void selectionUpdate(const LotList &list);
|
||||
void currencyUpdate(const QString &ccode);
|
||||
virtual void topLevelChanged(bool);
|
||||
virtual void dockLocationChanged(Qt::DockWidgetArea);
|
||||
@@ -50,7 +50,7 @@ private:
|
||||
QPointer<Window> m_win;
|
||||
QDockWidget *m_dock;
|
||||
QTimer m_delayTimer;
|
||||
Document::ItemList m_selection;
|
||||
LotList m_selection;
|
||||
};
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
|
||||
protected slots:
|
||||
void windowUpdate(Window *win);
|
||||
void selectionUpdate(const Document::ItemList &list);
|
||||
void selectionUpdate(const LotList &list);
|
||||
void statisticsUpdate();
|
||||
void currencyUpdate();
|
||||
|
||||
@@ -85,7 +85,7 @@ private:
|
||||
PictureWidget *m_pic;
|
||||
QPointer<Window> m_win;
|
||||
QTimer m_delayTimer;
|
||||
Document::ItemList m_selection;
|
||||
LotList m_selection;
|
||||
};
|
||||
|
||||
|
||||
@@ -102,10 +102,10 @@ public:
|
||||
|
||||
protected slots:
|
||||
void windowUpdate(Window *win);
|
||||
void selectionUpdate(const Document::ItemList &list);
|
||||
void selectionUpdate(const LotList &list);
|
||||
|
||||
private:
|
||||
QPointer<Window> m_win;
|
||||
QTimer m_delayTimer;
|
||||
Document::ItemList m_selection;
|
||||
LotList m_selection;
|
||||
};
|
||||
|
||||
@@ -160,8 +160,6 @@ void HeaderView::setModel(QAbstractItemModel *m)
|
||||
|
||||
bool HeaderView::restoreLayout(const QByteArray &config)
|
||||
{
|
||||
//TODO: we are missing the lastSortColumn[] history here, as this is kept in DocumentProxyModel
|
||||
|
||||
if (config.isEmpty())
|
||||
return false;
|
||||
|
||||
|
||||
+23
-9
@@ -110,15 +110,6 @@ int Utility::naturalCompare(const QString &name1, const QString &name2)
|
||||
}
|
||||
}
|
||||
|
||||
qreal Utility::colorDifference(const QColor &c1, const QColor &c2)
|
||||
{
|
||||
qreal r1, g1, b1, a1, r2, g2, b2, a2;
|
||||
c1.getRgbF(&r1, &g1, &b1, &a1);
|
||||
c2.getRgbF(&r2, &g2, &b2, &a2);
|
||||
|
||||
return (qAbs(r1-r2) + qAbs(g1-g2) + qAbs(b1-b2)) / qreal(3);
|
||||
}
|
||||
|
||||
QColor Utility::gradientColor(const QColor &c1, const QColor &c2, qreal f)
|
||||
{
|
||||
qreal r1, g1, b1, a1, r2, g2, b2, a2;
|
||||
@@ -131,6 +122,29 @@ QColor Utility::gradientColor(const QColor &c1, const QColor &c2, qreal f)
|
||||
return QColor::fromRgbF(r1 * e + r2 * f, g1 * e + g2 * f, b1 * e + b2 * f, a1 * e + a2 * f);
|
||||
}
|
||||
|
||||
QColor Utility::textColor(const QColor &bg)
|
||||
{
|
||||
// see https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
|
||||
|
||||
auto adjust = [](qreal c) {
|
||||
return (c <= 0.03928) ? c / 12.92 : std::pow(((c + 0.055) / 1.055), 2.4);
|
||||
};
|
||||
|
||||
auto luminance = [](qreal r, qreal g, qreal b) {
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
};
|
||||
|
||||
auto r = adjust(bg.redF());
|
||||
auto g = adjust(bg.greenF());
|
||||
auto b = adjust(bg.blueF());
|
||||
auto l = luminance(r, g, b);
|
||||
|
||||
auto cw = (1. + 0.05) / (l + 0.05); // contrast to white
|
||||
auto cb = (l + 0.05) / (0. + 0.05); // contrast to black
|
||||
|
||||
return (cw > cb) ? Qt::white : Qt::black;
|
||||
}
|
||||
|
||||
QColor Utility::contrastColor(const QColor &c, qreal f)
|
||||
{
|
||||
qreal h, s, l, a;
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace Utility {
|
||||
int naturalCompare(const QString &s1, const QString &s2);
|
||||
|
||||
QColor gradientColor(const QColor &c1, const QColor &c2, qreal f = 0.5);
|
||||
QColor contrastColor(const QColor &c, qreal f = 0.04);
|
||||
qreal colorDifference(const QColor &c1, const QColor &c2);
|
||||
QColor textColor(const QColor &backgroundColor);
|
||||
QColor contrastColor(const QColor &c, qreal f);
|
||||
QColor premultiplyAlpha(const QColor &c);
|
||||
|
||||
void setPopupPos(QWidget *w, const QRect &pos);
|
||||
|
||||
Regular → Executable
+304
-294
File diff suppressed because it is too large
Load Diff
+14
-16
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "bricklinkfwd.h"
|
||||
#include "document.h"
|
||||
#include "lot.h"
|
||||
#include "config.h"
|
||||
#include "currency.h"
|
||||
|
||||
@@ -61,7 +62,7 @@ public:
|
||||
IntoNew = 5
|
||||
};
|
||||
|
||||
enum class AddItemMode {
|
||||
enum class AddLotMode {
|
||||
AddAsNew,
|
||||
ConsolidateWithExisting,
|
||||
ConsolidateInteractive,
|
||||
@@ -71,13 +72,11 @@ public:
|
||||
enum class AutosaveAction { Restore, Delete };
|
||||
static const QVector<Window *> processAutosaves(AutosaveAction action);
|
||||
|
||||
const Document::ItemList &selection() const { return m_selection; }
|
||||
const LotList &selectedLots() const { return m_selectedLots; }
|
||||
|
||||
uint setItems(const BrickLink::InvItemList &items, int multiply = 1);
|
||||
int addItems(const BrickLink::InvItemList &items, AddItemMode addItemMode = AddItemMode::AddAsNew);
|
||||
void deleteItems(const BrickLink::InvItemList &items);
|
||||
int addLots(const LotList &lots, AddLotMode addLotMode = AddLotMode::AddAsNew);
|
||||
|
||||
void consolidateItems(const Document::ItemList &items);
|
||||
void consolidateLots(const LotList &lots);
|
||||
|
||||
enum class ColumnLayoutCommand {
|
||||
BrickStoreDefault,
|
||||
@@ -106,7 +105,7 @@ public:
|
||||
QString blockingOperationTitle() const;
|
||||
void setBlockingOperationTitle(const QString &title);
|
||||
public slots:
|
||||
void setSelection(const Document::ItemList &);
|
||||
void setSelection(const LotList &);
|
||||
|
||||
void on_document_save_triggered();
|
||||
void on_document_save_as_triggered();
|
||||
@@ -196,7 +195,7 @@ public slots:
|
||||
void setStockroom(BrickLink::Stockroom stockroom);
|
||||
|
||||
signals:
|
||||
void selectionChanged(const Document::ItemList &);
|
||||
void selectedLotsChanged(const LotList &);
|
||||
void blockingOperationActiveChanged(bool blocked);
|
||||
void blockingOperationCancelableChanged(bool cancelable);
|
||||
void blockingOperationTitleChanged(const QString &title);
|
||||
@@ -212,7 +211,7 @@ private slots:
|
||||
void ensureLatestVisible();
|
||||
void updateCaption();
|
||||
void updateSelection();
|
||||
void documentItemsChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
void documentDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
|
||||
void contextMenu(const QPoint &pos);
|
||||
void priceGuideUpdated(BrickLink::PriceGuide *);
|
||||
@@ -221,16 +220,15 @@ private slots:
|
||||
void autosave() const;
|
||||
|
||||
private:
|
||||
void applyTo(const Document::ItemList &items,
|
||||
std::function<bool(const Document::Item &, Document::Item &)> callback);
|
||||
void applyTo(const LotList &lots, std::function<bool(const Lot &, Lot &)> callback);
|
||||
|
||||
void cancelPriceGuideUpdates();
|
||||
|
||||
void editCurrentItem(int column);
|
||||
|
||||
Document::ItemList exportCheck() const;
|
||||
LotList exportCheck() const;
|
||||
void resizeColumnsToDefault(bool simpleMode = false);
|
||||
int consolidateItemsHelper(const Document::ItemList &items, Consolidate conMode) const;
|
||||
int consolidateLotsHelper(const LotList &lots, Consolidate conMode) const;
|
||||
void deleteAutosave();
|
||||
|
||||
void moveColumnDirect(int logical, int oldVisual, int newVisual);
|
||||
@@ -240,7 +238,7 @@ private:
|
||||
private:
|
||||
Document * m_doc;
|
||||
QItemSelectionModel *m_selection_model;
|
||||
Document::ItemList m_selection;
|
||||
LotList m_selectedLots;
|
||||
QTimer * m_delayedSelectionUpdate = nullptr;
|
||||
QMenu * m_contextMenu = nullptr;
|
||||
StatusBar * w_statusbar;
|
||||
@@ -258,8 +256,8 @@ private:
|
||||
|
||||
struct SetToPriceGuideData
|
||||
{
|
||||
std::vector<std::pair<Document::Item *, Document::Item>> changes;
|
||||
QMultiHash<BrickLink::PriceGuide *, Document::Item *> priceGuides;
|
||||
std::vector<std::pair<Lot *, Lot>> changes;
|
||||
QMultiHash<BrickLink::PriceGuide *, Lot *> priceGuides;
|
||||
int failCount = 0;
|
||||
int doneCount = 0;
|
||||
int totalCount = 0;
|
||||
|
||||
Reference in New Issue
Block a user