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:
Robert Griebl
2021-03-07 02:43:23 +01:00
parent fc2ca4b8e7
commit a806d615d8
45 changed files with 2109 additions and 2463 deletions
+31 -31
View File
@@ -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
View File
@@ -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" ;\">&nbsp;" %
item.colorName() % u"&nbsp;</font></b>&nbsp;&nbsp;";
lot.colorName() % u"&nbsp;</font></b>&nbsp;&nbsp;";
}
QString s = tr("Added %1").arg(HumanReadableTimeDelta::toString(now, when)) %
u":&nbsp;&nbsp;<b>" % QString::number(item.quantity()) % u"</b>&nbsp;&nbsp;" % cs %
item.itemName() % u" <i>[" + item.itemId() % u"]</i>";
u":&nbsp;&nbsp;<b>" % QString::number(lot.quantity()) % u"</b>&nbsp;&nbsp;" % 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
View File
@@ -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;
};
+6 -1
View File
@@ -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();
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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 &copy);
~InvItem();
InvItem &operator=(const InvItem &copy);
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 *)
-2
View File
@@ -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
View File
@@ -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 &copy)
{
*this = copy;
}
BrickLink::InvItem &BrickLink::InvItem::operator=(const InvItem &copy)
{
if (this == &copy)
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)
+13 -20
View File
@@ -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));
+2 -4
View File
@@ -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;
-364
View File
@@ -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"
-122
View File
@@ -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 &copy) = default;
InvMatchList &operator=(const InvMatchList &copy) = 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
+17 -19
View File
@@ -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());
+1 -3
View File
@@ -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;
+8 -8
View File
@@ -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);
+1 -1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+67 -48
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+21 -20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"));
}
+93 -6
View File
@@ -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"));
}
+7 -2
View File
@@ -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
View File
@@ -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 &copy)
{
*this = copy;
}
Lot &Lot::operator=(const Lot &copy)
{
if (this == &copy)
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();
}
Executable
+178
View File
@@ -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 &copy);
~Lot();
Lot &operator=(const Lot &copy);
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;
+37 -36
View File
@@ -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 -16
View File
@@ -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)
+4 -4
View File
@@ -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()),
+1 -1
View File
@@ -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");
+14 -14
View File
@@ -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
+4 -4
View File
@@ -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
View File
@@ -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())
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
-2
View File
@@ -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
View File
@@ -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;
+2 -2
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+14 -16
View File
@@ -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;