mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
GUI: Get size and position of grid item from GridItemInfo
This commit is contained in:
parent
6c2fdec8a8
commit
fb9c232f25
2 changed files with 106 additions and 34 deletions
|
@ -68,7 +68,8 @@ void GridItemWidget::move(int x, int y) {
|
||||||
|
|
||||||
void GridItemWidget::drawWidget() {
|
void GridItemWidget::drawWidget() {
|
||||||
if (_activeEntry->isHeader) {
|
if (_activeEntry->isHeader) {
|
||||||
g_gui.theme()->drawText(Common::Rect(_x, _y, _x + _w, _y + _h), Common::U32String(_activeEntry->title), ThemeEngine::kStateEnabled, Graphics::kTextAlignLeft);
|
g_gui.theme()->drawFoldIndicator(Common::Rect(_x, _y, _x + _h, _y + _h), _grid->groupExpanded(_activeEntry->entryID));
|
||||||
|
g_gui.theme()->drawText(Common::Rect(_x + _h, _y, _x + _w, _y + _h), Common::U32String(_activeEntry->title), ThemeEngine::kStateEnabled, Graphics::kTextAlignLeft);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int thumbHeight = _grid->getThumbnailHeight();
|
int thumbHeight = _grid->getThumbnailHeight();
|
||||||
|
@ -180,25 +181,32 @@ void GridItemWidget::handleMouseMoved(int x, int y, int button) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
|
void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
|
||||||
if (isHighlighted && isVisible()) {
|
if (_activeEntry->isHeader) {
|
||||||
|
_grid->_selectedEntry = nullptr;
|
||||||
|
}
|
||||||
|
else if (isHighlighted && isVisible()) {
|
||||||
_grid->_selectedEntry = _activeEntry;
|
_grid->_selectedEntry = _activeEntry;
|
||||||
sendCommand(kItemClicked, 0);
|
sendCommand(kItemClicked, 0);
|
||||||
// Work in progress
|
// Work in progress
|
||||||
// Since user expected to click on "entry" and not the "widget", we
|
// Since user expected to click on "entry" and not the "widget", we
|
||||||
// must open the tray where the user expects it to be, which might
|
// must open the tray where the user expects it to be, which might
|
||||||
// not be at the new widget location.
|
// not be at the new widget location.
|
||||||
int oldX = getAbsX(), oldY = getAbsY();
|
// TODO: Make a scrollToSelection() function which does this
|
||||||
int offsetY = 0;
|
int offsetY = 0;
|
||||||
if (_y > (_grid->getHeight() - _h - _grid->_trayHeight)) {
|
if (_y > (_grid->getHeight() - _h - _grid->_trayHeight)) {
|
||||||
offsetY = _y - (_grid->getHeight() - _h - _grid->_trayHeight);
|
offsetY = _y - (_grid->getHeight() - _h - _grid->_trayHeight);
|
||||||
sendCommand(kSetPositionCmd, _grid->getScrollPos() + offsetY);
|
sendCommand(kSetPositionCmd, _grid->getScrollPos() + offsetY);
|
||||||
|
_grid->scrollBarRecalc();
|
||||||
_grid->markAsDirty();
|
_grid->markAsDirty();
|
||||||
_grid->draw();
|
_grid->draw();
|
||||||
}
|
}
|
||||||
_grid->openTray(oldX, oldY - offsetY + _h, _activeEntry->entryID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GridItemWidget::handleMouseUp(int x, int y, int button, int clickCount) {
|
||||||
|
_grid->openTrayAtSelected();
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
GridItemTray::GridItemTray(GuiObject *boss, int x, int y, int w, int h, int entryID, GridWidget *grid)
|
GridItemTray::GridItemTray(GuiObject *boss, int x, int y, int w, int h, int entryID, GridWidget *grid)
|
||||||
|
@ -335,6 +343,8 @@ GridWidget::GridWidget(GuiObject *boss, int x, int y, int w, int h)
|
||||||
|
|
||||||
_thumbnailHeight = g_gui.xmlEval()->getVar("Globals.GridItemThumbnail.Height");
|
_thumbnailHeight = g_gui.xmlEval()->getVar("Globals.GridItemThumbnail.Height");
|
||||||
_thumbnailWidth = g_gui.xmlEval()->getVar("Globals.GridItemThumbnail.Width");
|
_thumbnailWidth = g_gui.xmlEval()->getVar("Globals.GridItemThumbnail.Width");
|
||||||
|
_gridHeaderHeight = kLineHeight;
|
||||||
|
_gridHeaderWidth = _thumbnailWidth;
|
||||||
_minGridXSpacing = g_gui.xmlEval()->getVar("Globals.Grid.XSpacing");
|
_minGridXSpacing = g_gui.xmlEval()->getVar("Globals.Grid.XSpacing");
|
||||||
_gridYSpacing = g_gui.xmlEval()->getVar("Globals.Grid.YSpacing");
|
_gridYSpacing = g_gui.xmlEval()->getVar("Globals.Grid.YSpacing");
|
||||||
|
|
||||||
|
@ -366,6 +376,8 @@ GridWidget::GridWidget(GuiObject *boss, const String &name)
|
||||||
|
|
||||||
_thumbnailHeight = g_gui.xmlEval()->getVar("Globals.GridItemThumbnail.Height");
|
_thumbnailHeight = g_gui.xmlEval()->getVar("Globals.GridItemThumbnail.Height");
|
||||||
_thumbnailWidth = g_gui.xmlEval()->getVar("Globals.GridItemThumbnail.Width");
|
_thumbnailWidth = g_gui.xmlEval()->getVar("Globals.GridItemThumbnail.Width");
|
||||||
|
_gridHeaderHeight = kLineHeight;
|
||||||
|
_gridHeaderWidth = _thumbnailWidth;
|
||||||
_minGridXSpacing = g_gui.xmlEval()->getVar("Globals.Grid.XSpacing");
|
_minGridXSpacing = g_gui.xmlEval()->getVar("Globals.Grid.XSpacing");
|
||||||
_gridYSpacing = g_gui.xmlEval()->getVar("Globals.Grid.YSpacing");
|
_gridYSpacing = g_gui.xmlEval()->getVar("Globals.Grid.YSpacing");
|
||||||
|
|
||||||
|
@ -477,10 +489,14 @@ void GridWidget::sortGroups() {
|
||||||
uint groupID = _groupValueIndex[header];
|
uint groupID = _groupValueIndex[header];
|
||||||
|
|
||||||
_sortedEntryList.push_back(GridItemInfo(_groupHeaderPrefix + displayedHeader + _groupHeaderSuffix, groupID));
|
_sortedEntryList.push_back(GridItemInfo(_groupHeaderPrefix + displayedHeader + _groupHeaderSuffix, groupID));
|
||||||
|
_sortedEntryList.back().rect.setHeight(_gridHeaderHeight);
|
||||||
|
_sortedEntryList.back().rect.setWidth(_gridHeaderWidth);
|
||||||
|
|
||||||
if (_groupExpanded[groupID]) {
|
if (_groupExpanded[groupID]) {
|
||||||
for (int *k = _itemsInGroup[groupID].begin(); k != _itemsInGroup[groupID].end(); ++k) {
|
for (int *k = _itemsInGroup[groupID].begin(); k != _itemsInGroup[groupID].end(); ++k) {
|
||||||
_sortedEntryList.push_back(_dataEntryList[*k]);
|
_sortedEntryList.push_back(_dataEntryList[*k]);
|
||||||
|
_sortedEntryList.back().rect.setHeight(_gridItemHeight);
|
||||||
|
_sortedEntryList.back().rect.setWidth(_gridItemWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,7 +509,28 @@ bool GridWidget::calcVisibleEntries() {
|
||||||
|
|
||||||
int nFirstVisibleItem = 0, nItemsOnScreen = 0;
|
int nFirstVisibleItem = 0, nItemsOnScreen = 0;
|
||||||
|
|
||||||
nFirstVisibleItem = _itemsPerRow * (_scrollPos / (_gridItemHeight + _gridYSpacing));
|
// Binary search to find the last element whose y value is less
|
||||||
|
// than _scrollPos, i.e., the last item of the topmost visible row.
|
||||||
|
int start = 0;
|
||||||
|
int end = (int)_sortedEntryList.size() - 1;
|
||||||
|
int mid;
|
||||||
|
int ans = -1;
|
||||||
|
while (start <= end) {
|
||||||
|
mid = start + (end - start) / 2;
|
||||||
|
if (_sortedEntryList[mid].rect.top >= _scrollPos) {
|
||||||
|
end = mid - 1;
|
||||||
|
} else {
|
||||||
|
ans = mid;
|
||||||
|
start = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nFirstVisibleItem = ans;
|
||||||
|
// We want the leftmost item from the topmost visible row, so we traverse backwards
|
||||||
|
while ((nFirstVisibleItem >= 0) &&
|
||||||
|
(_sortedEntryList[nFirstVisibleItem].rect.top == _sortedEntryList[ans].rect.top)) {
|
||||||
|
nFirstVisibleItem--;
|
||||||
|
}
|
||||||
|
nFirstVisibleItem++;
|
||||||
nFirstVisibleItem = (nFirstVisibleItem < 0) ? 0 : nFirstVisibleItem;
|
nFirstVisibleItem = (nFirstVisibleItem < 0) ? 0 : nFirstVisibleItem;
|
||||||
|
|
||||||
nItemsOnScreen = (3 + (_scrollWindowHeight / (_gridItemHeight + _gridYSpacing))) * (_itemsPerRow);
|
nItemsOnScreen = (3 + (_scrollWindowHeight / (_gridItemHeight + _gridYSpacing))) * (_itemsPerRow);
|
||||||
|
@ -503,11 +540,11 @@ bool GridWidget::calcVisibleEntries() {
|
||||||
_itemsOnScreen = nItemsOnScreen;
|
_itemsOnScreen = nItemsOnScreen;
|
||||||
_firstVisibleItem = nFirstVisibleItem;
|
_firstVisibleItem = nFirstVisibleItem;
|
||||||
|
|
||||||
int toRender = MIN(_firstVisibleItem + _itemsOnScreen, (int)_dataEntryList.size());
|
int toRender = MIN(_firstVisibleItem + _itemsOnScreen, (int)_sortedEntryList.size());
|
||||||
|
|
||||||
_visibleEntryList.clear();
|
_visibleEntryList.clear();
|
||||||
for (int ind = _firstVisibleItem; ind < toRender; ++ind) {
|
for (int ind = _firstVisibleItem; ind < toRender; ++ind) {
|
||||||
GridItemInfo *iter = _dataEntryList.begin() + ind;
|
GridItemInfo *iter = _sortedEntryList.begin() + ind;
|
||||||
_visibleEntryList.push_back(iter);
|
_visibleEntryList.push_back(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,11 +630,9 @@ void GridWidget::updateGrid() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridWidget::assignEntriesToItems() {
|
void GridWidget::assignEntriesToItems() {
|
||||||
// Assign entries from _visibleEntryList to each GridItem in _gridItems
|
// Assign entries from _visibleEntries to each GridItem in _gridItems
|
||||||
Common::Array<GridItemInfo *>::iterator eit = _visibleEntryList.begin();
|
Common::Array<GridItemInfo *>::iterator eit = _visibleEntryList.begin();
|
||||||
// Start assigning from the second row as the first row is supposed
|
Common::Array<GridItemWidget *>::iterator it = _gridItems.begin();
|
||||||
// to be offscreen.
|
|
||||||
Common::Array<GridItemWidget *>::iterator it = _gridItems.begin() + _itemsPerRow;
|
|
||||||
|
|
||||||
for (int k = 0; k < _itemsOnScreen; ++k) {
|
for (int k = 0; k < _itemsOnScreen; ++k) {
|
||||||
GridItemWidget *item = *it;
|
GridItemWidget *item = *it;
|
||||||
|
@ -605,6 +640,8 @@ void GridWidget::assignEntriesToItems() {
|
||||||
if (eit != _visibleEntryList.end()) {
|
if (eit != _visibleEntryList.end()) {
|
||||||
// Assign entry and update
|
// Assign entry and update
|
||||||
item->setActiveEntry(*entry);
|
item->setActiveEntry(*entry);
|
||||||
|
item->setPos(entry->rect.left, entry->rect.top - _scrollPos);
|
||||||
|
item->setSize(entry->rect.width(), entry->rect.height());
|
||||||
item->update();
|
item->update();
|
||||||
if (k >= _itemsOnScreen - _itemsPerRow)
|
if (k >= _itemsOnScreen - _itemsPerRow)
|
||||||
item->setVisible(false);
|
item->setVisible(false);
|
||||||
|
@ -613,7 +650,9 @@ void GridWidget::assignEntriesToItems() {
|
||||||
++eit;
|
++eit;
|
||||||
} else {
|
} else {
|
||||||
// If we run out of visible entries to display.
|
// If we run out of visible entries to display.
|
||||||
// e.g., scrolled to the very bottom, we make items invisible.
|
// e.g., scrolled to the very bottom, we make items invisible,
|
||||||
|
// and move them out of view to keep them from registering mouse events.
|
||||||
|
item->setPos(_scrollWindowWidth, _scrollWindowHeight);
|
||||||
item->setVisible(false);
|
item->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,17 +677,6 @@ void GridWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
|
||||||
reloadThumbnails();
|
reloadThumbnails();
|
||||||
}
|
}
|
||||||
|
|
||||||
int row = 0;
|
|
||||||
int col = 0;
|
|
||||||
|
|
||||||
for (Common::Array<GridItemWidget *>::iterator it = _gridItems.begin(); it != _gridItems.end(); ++it) {
|
|
||||||
(*it)->setPos(2 * _minGridXSpacing + col * (_gridItemWidth + _gridXSpacing),
|
|
||||||
_gridYSpacing + (row - 1) * (_gridItemHeight + _gridYSpacing) - (_scrollPos % (_gridItemHeight + _gridYSpacing)));
|
|
||||||
if (++col >= _itemsPerRow) {
|
|
||||||
++row;
|
|
||||||
col = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assignEntriesToItems();
|
assignEntriesToItems();
|
||||||
markAsDirty();
|
markAsDirty();
|
||||||
|
|
||||||
|
@ -677,6 +705,9 @@ void GridWidget::reflowLayout() {
|
||||||
reloadThumbnails();
|
reloadThumbnails();
|
||||||
loadFlagIcons();
|
loadFlagIcons();
|
||||||
}
|
}
|
||||||
|
_gridHeaderHeight = kLineHeight;
|
||||||
|
_gridHeaderWidth = _scrollWindowWidth - _scrollBarWidth - 2 * _gridXSpacing;
|
||||||
|
|
||||||
_minGridXSpacing = g_gui.xmlEval()->getVar("Globals.Grid.XSpacing");
|
_minGridXSpacing = g_gui.xmlEval()->getVar("Globals.Grid.XSpacing");
|
||||||
_gridYSpacing = g_gui.xmlEval()->getVar("Globals.Grid.YSpacing");
|
_gridYSpacing = g_gui.xmlEval()->getVar("Globals.Grid.YSpacing");
|
||||||
|
|
||||||
|
@ -691,29 +722,57 @@ void GridWidget::reflowLayout() {
|
||||||
_itemsPerRow = MAX(((_scrollWindowWidth - (2 * _minGridXSpacing) - _scrollBarWidth) / (_gridItemWidth + _minGridXSpacing)), 1);
|
_itemsPerRow = MAX(((_scrollWindowWidth - (2 * _minGridXSpacing) - _scrollBarWidth) / (_gridItemWidth + _minGridXSpacing)), 1);
|
||||||
_gridXSpacing = MAX(((_scrollWindowWidth - (2 * _minGridXSpacing) - _scrollBarWidth) - (_itemsPerRow * _gridItemWidth)) / _itemsPerRow, _minGridXSpacing);
|
_gridXSpacing = MAX(((_scrollWindowWidth - (2 * _minGridXSpacing) - _scrollBarWidth) - (_itemsPerRow * _gridItemWidth)) / _itemsPerRow, _minGridXSpacing);
|
||||||
|
|
||||||
_rows = ceil(_dataEntryList.size() / (float)_itemsPerRow);
|
int row = 0;
|
||||||
|
int col = 0;
|
||||||
|
Common::Point p(_gridXSpacing, _gridYSpacing);
|
||||||
|
|
||||||
_innerHeight = _trayHeight + _gridYSpacing + _rows * (_gridItemHeight + _gridYSpacing);
|
for (int k = 0; k < (int)_sortedEntryList.size(); ++k) {
|
||||||
_innerWidth = (2 * _minGridXSpacing) + (_itemsPerRow * (_gridItemWidth + _gridXSpacing));
|
if (_sortedEntryList[k].isHeader) {
|
||||||
|
while (col != 0) {
|
||||||
|
if (++col >= _itemsPerRow) {
|
||||||
|
col = 0;
|
||||||
|
++row;
|
||||||
|
p.x = _gridXSpacing;
|
||||||
|
p.y += _sortedEntryList[k - 1].rect.height() + _gridYSpacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_sortedEntryList[k].rect.moveTo(p);
|
||||||
|
++row;
|
||||||
|
p.y += _sortedEntryList[k].rect.height() + _gridYSpacing;
|
||||||
|
} else {
|
||||||
|
_sortedEntryList[k].rect.moveTo(p);
|
||||||
|
if (++col >= _itemsPerRow) {
|
||||||
|
++row;
|
||||||
|
p.y += _sortedEntryList[k].rect.height() + _gridYSpacing;
|
||||||
|
col = 0;
|
||||||
|
p.x = _gridXSpacing;
|
||||||
|
} else {
|
||||||
|
p.x += _sortedEntryList[k].rect.width() + _gridXSpacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warning("Titel: %s \tX: %d Y: %d", _sortedEntryList[k].title.c_str(), _sortedEntryList[k].rect.left, _sortedEntryList[k].rect.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
_rows = row;
|
||||||
|
|
||||||
|
_innerHeight = p.y + _gridItemHeight + _gridYSpacing + _trayHeight;
|
||||||
|
_innerWidth = _gridXSpacing + (_itemsPerRow * (_gridItemWidth + _gridXSpacing));
|
||||||
|
|
||||||
_scrollBar->checkBounds(_scrollBar->_currentPos);
|
_scrollBar->checkBounds(_scrollBar->_currentPos);
|
||||||
_scrollPos = _scrollBar->_currentPos;
|
_scrollPos = _scrollBar->_currentPos;
|
||||||
|
|
||||||
int row = 0;
|
|
||||||
int col = 0;
|
|
||||||
|
|
||||||
_scrollBar->resize(_scrollWindowWidth - _scrollBarWidth, 0, _scrollBarWidth, _scrollWindowHeight, false);
|
_scrollBar->resize(_scrollWindowWidth - _scrollBarWidth, 0, _scrollBarWidth, _scrollWindowHeight, false);
|
||||||
|
|
||||||
if (calcVisibleEntries()) {
|
if (calcVisibleEntries()) {
|
||||||
reloadThumbnails();
|
reloadThumbnails();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row = 0;
|
||||||
|
col = 0;
|
||||||
|
|
||||||
for (int k = 0; k < _itemsOnScreen; ++k) {
|
for (int k = 0; k < _itemsOnScreen; ++k) {
|
||||||
GridItemWidget *newItem = new GridItemWidget(this,
|
GridItemWidget *newItem = new GridItemWidget(this);
|
||||||
2 * _minGridXSpacing + col * (_gridItemWidth + _gridXSpacing),
|
newItem->setVisible(false);
|
||||||
_gridYSpacing + (row - 1) * (_gridItemHeight + _gridYSpacing) - (_scrollPos % (_gridItemHeight + _gridYSpacing)),
|
|
||||||
_gridItemWidth,
|
|
||||||
_gridItemHeight);
|
|
||||||
|
|
||||||
_gridItems.push_back(newItem);
|
_gridItems.push_back(newItem);
|
||||||
|
|
||||||
|
@ -733,6 +792,13 @@ void GridWidget::openTray(int x, int y, int entryId) {
|
||||||
_tray->runModal();
|
_tray->runModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GridWidget::openTrayAtSelected() {
|
||||||
|
if (_selectedEntry) {
|
||||||
|
_tray = new GridItemTray(this, _x + _selectedEntry->rect.left - _gridXSpacing / 3, _y + _selectedEntry->rect.bottom - _scrollPos, _gridItemWidth + 2 * (_gridXSpacing / 3), _trayHeight, _selectedEntry->entryID, this);
|
||||||
|
_tray->runModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GridWidget::scrollBarRecalc() {
|
void GridWidget::scrollBarRecalc() {
|
||||||
_scrollBar->_numEntries = _innerHeight;
|
_scrollBar->_numEntries = _innerHeight;
|
||||||
_scrollBar->_entriesPerPage = _scrollWindowHeight - _gridYSpacing;
|
_scrollBar->_entriesPerPage = _scrollWindowHeight - _gridYSpacing;
|
||||||
|
|
|
@ -68,6 +68,8 @@ struct GridItemInfo {
|
||||||
Common::Language language;
|
Common::Language language;
|
||||||
Common::Platform platform;
|
Common::Platform platform;
|
||||||
|
|
||||||
|
Common::Rect rect;
|
||||||
|
|
||||||
GridItemInfo(int id, const String &eid, const String &gid
|
GridItemInfo(int id, const String &eid, const String &gid
|
||||||
,const String &t, Common::Language l, Common::Platform p)
|
,const String &t, Common::Language l, Common::Platform p)
|
||||||
: entryID(id), gameid(gid), engineid(eid), title(t), language(l), platform(p), isHeader(false) {
|
: entryID(id), gameid(gid), engineid(eid), title(t), language(l), platform(p), isHeader(false) {
|
||||||
|
@ -142,6 +144,8 @@ protected:
|
||||||
public:
|
public:
|
||||||
int _gridItemHeight;
|
int _gridItemHeight;
|
||||||
int _gridItemWidth;
|
int _gridItemWidth;
|
||||||
|
int _gridHeaderHeight;
|
||||||
|
int _gridHeaderWidth;
|
||||||
int _gridXSpacing;
|
int _gridXSpacing;
|
||||||
int _gridYSpacing;
|
int _gridYSpacing;
|
||||||
int _trayHeight;
|
int _trayHeight;
|
||||||
|
@ -189,6 +193,7 @@ public:
|
||||||
void reflowLayout() override;
|
void reflowLayout() override;
|
||||||
|
|
||||||
void openTray(int x, int y, int entryID);
|
void openTray(int x, int y, int entryID);
|
||||||
|
void openTrayAtSelected();
|
||||||
void scrollBarRecalc();
|
void scrollBarRecalc();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,6 +221,7 @@ public:
|
||||||
void handleMouseEntered(int button) override;
|
void handleMouseEntered(int button) override;
|
||||||
void handleMouseLeft(int button) override;
|
void handleMouseLeft(int button) override;
|
||||||
void handleMouseDown(int x, int y, int button, int clickCount) override;
|
void handleMouseDown(int x, int y, int button, int clickCount) override;
|
||||||
|
void handleMouseUp(int x, int y, int button, int clickCount) override;
|
||||||
void handleMouseMoved(int x, int y, int button) override;
|
void handleMouseMoved(int x, int y, int button) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue