Memleak fixes

* Set parent of title- and content-widget to associated container-widget
* inside FloatingWidget::takeContent(...)

Restore state
* Restore all contents as visible and then call hideSectionContent(...) at
* the end. This is more consistent and uses same code (public API).
* Pass version to sub-methods to handle different formats
* Fix serialization, if nothing is visible (no sections, only hidden
* contents)
This commit is contained in:
mfreiholz 2016-02-19 12:00:39 +01:00
parent 6a1b6307c9
commit 032a5d6cdf
7 changed files with 216 additions and 42 deletions

View File

@ -102,10 +102,13 @@ private:
void addSection(SectionWidget* section); void addSection(SectionWidget* section);
SectionWidget* sectionAt(const QPoint& pos) const; SectionWidget* sectionAt(const QPoint& pos) const;
SectionWidget* dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append); SectionWidget* dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append);
// Serialization
void saveFloatingWidgets(QDataStream& out) const; void saveFloatingWidgets(QDataStream& out) const;
void saveSectionWidgets(QDataStream& out, QWidget* widget) const; void saveSectionWidgets(QDataStream& out, QWidget* widget) const;
bool restoreFloatingWidgets(QDataStream& in, QList<FloatingWidget*>& floatings); bool restoreFloatingWidgets(QDataStream& in, int version, QList<FloatingWidget*>& floatings);
bool restoreSectionWidgets(QDataStream& in, QSplitter* currentSplitter, QList<SectionWidget*>& sections); bool restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList<SectionWidget*>& sections, QList<SectionContent::RefPtr>& contentsToHide);
bool takeContent(const SectionContent::RefPtr& sc, InternalContentData& data); bool takeContent(const SectionContent::RefPtr& sc, InternalContentData& data);
private slots: private slots:

View File

@ -106,7 +106,9 @@ bool ContainerWidget::showSectionContent(const SectionContent::RefPtr& sc)
} }
else else
{ {
qDebug() << "TODO Create new SW here and add SC"; sw = newSectionWidget();
addSection(sw);
sw->addContent(hsi.data, true);
return true; return true;
} }
} }
@ -236,6 +238,29 @@ QMenu* ContainerWidget::createContextMenu() const
QByteArray ContainerWidget::saveState() const QByteArray ContainerWidget::saveState() const
{ {
/*
# Data Format
quint32 Magic
quint32 Version
int Number of floating widgets
LOOP Floating widgets
QString Unique name of content
QByteArray Geometry of floating widget
bool Visibility
int Number of layout items (Valid values: 0, 1)
IF 0
int Number of hidden contents
LOOP Contents
QString Unique name of content
ELSEIF 1
... todo ...
ENDIF
*/
qDebug() << "Begin save state";
QByteArray ba; QByteArray ba;
QDataStream out(&ba, QIODevice::WriteOnly); QDataStream out(&ba, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_5); out.setVersion(QDataStream::Qt_4_5);
@ -245,21 +270,67 @@ QByteArray ContainerWidget::saveState() const
// Save state of floating contents // Save state of floating contents
saveFloatingWidgets(out); saveFloatingWidgets(out);
// Walk through layout for splitters // Save state of sections and contents
// Well.. there actually shouldn't be more than one if (_mainLayout->count() <= 0 || _sections.isEmpty())
for (int i = 0; i < _mainLayout->count(); ++i)
{ {
QLayoutItem* li = _mainLayout->itemAt(i); // Looks like the user has hidden all contents and no more sections
if (!li->widget()) // are available. We can simply write a list of all hidden contents.
continue; out << 0;
saveSectionWidgets(out, li->widget()); out << _hiddenSectionContents.count();
}
QHashIterator<int, HiddenSectionItem> iter(_hiddenSectionContents);
while (iter.hasNext())
{
iter.next();
out << iter.value().data.content->uniqueName();
}
}
else if (_mainLayout->count() == 1)
{
// There should only be one!
out << 1;
QLayoutItem* li = _mainLayout->itemAt(0);
if (!li->widget())
qWarning() << "Not a widget in _mainLayout, this shouldn't happen.";
else
saveSectionWidgets(out, li->widget());
// Safe state of hidden contents, which doesn't have an section association
// or the section association points to a no longer existing section.
QHashIterator<int, HiddenSectionItem> iter(_hiddenSectionContents);
int cnt = 0;
while (iter.hasNext())
{
iter.next();
if (iter.value().preferredSectionId <= 0 || !SectionWidget::LookupMap.contains(iter.value().preferredSectionId))
cnt++;
}
out << cnt;
iter.toFront();
while (iter.hasNext())
{
iter.next();
if (iter.value().preferredSectionId <= 0 || !SectionWidget::LookupMap.contains(iter.value().preferredSectionId))
out << iter.value().data.content->uniqueName();
}
}
else
{
// More? Oh oh.. something is wrong :-/
out << -1;
qWarning() << "Oh noooz.. Something went wrong. There are too many items in _mainLayout.";
}
qDebug() << "End save state";
return ba; return ba;
} }
bool ContainerWidget::restoreState(const QByteArray& data) bool ContainerWidget::restoreState(const QByteArray& data)
{ {
if (data.isEmpty())
return false;
qDebug() << "Begin to restore state";
QDataStream in(data); QDataStream in(data);
in.setVersion(QDataStream::Qt_4_5); in.setVersion(QDataStream::Qt_4_5);
@ -278,18 +349,75 @@ bool ContainerWidget::restoreState(const QByteArray& data)
// Restore floating widgets // Restore floating widgets
QList<FloatingWidget*> floatings; QList<FloatingWidget*> floatings;
bool success = restoreFloatingWidgets(in, floatings); bool success = restoreFloatingWidgets(in, version, floatings);
if (!success) if (!success)
{ {
qWarning() << "Could not restore floatings completely"; qWarning() << "Could not restore floatings completely";
} }
// Restore splitters and section widgets // Restore splitters, sections and contents
QList<SectionWidget*> sections; QList<SectionWidget*> sections;
success = restoreSectionWidgets(in, NULL, sections); QList<SectionContent::RefPtr> contentsToHide;
if (!success)
int mode = 0;
in >> mode;
if (mode == 0)
{ {
qWarning() << "Could not restore sections completely"; // List of hidden contents. There are no sections at all.
int cnt = 0;
in >> cnt;
// Create dummy section, required to call hideSectionContent() later.
SectionWidget* sw = new SectionWidget(this);
sections.append(sw);
for (int i = 0; i < cnt; ++i)
{
QString uname;
in >> uname;
const SectionContent::RefPtr sc = SectionContent::LookupMapByName.value(uname);
if (!sc)
continue;
InternalContentData data;
if (!takeContent(sc, data))
qFatal("This should never happen!!!");
sw->addContent(data, false);
contentsToHide.append(sc);
}
}
else if (mode == 1)
{
success = restoreSectionWidgets(in, version, NULL, sections, contentsToHide);
if (!success)
qWarning() << "Could not restore sections completely";
// Restore lonely hidden contents
int cnt = 0;
in >> cnt;
for (int i = 0; i < cnt; ++i)
{
QString uname;
in >> uname;
const SectionContent::RefPtr sc = SectionContent::LookupMapByName.value(uname);
if (!sc)
continue;
InternalContentData data;
if (!takeContent(sc, data))
qFatal("This should never happen!!!");
SectionWidget* sw = NULL;
if (sections.size() <= 0)
qFatal("This should never happen, because above a section should have been created.");
else
sw = sections.first();
sw->addContent(data, false);
contentsToHide.append(sc);
}
} }
// Handle SectionContent which is not mentioned by deserialized data. // Handle SectionContent which is not mentioned by deserialized data.
@ -305,6 +433,8 @@ bool ContainerWidget::restoreState(const QByteArray& data)
for (int i = 0; i < sections.count(); ++i) for (int i = 0; i < sections.count(); ++i)
for (int j = 0; j < sections.at(i)->contents().count(); ++j) for (int j = 0; j < sections.at(i)->contents().count(); ++j)
contents.append(sections.at(i)->contents().at(j)); contents.append(sections.at(i)->contents().at(j));
for (int i = 0; i < contentsToHide.count(); ++i)
contents.append(contentsToHide.at(i));
// Compare restored contents with available contents // Compare restored contents with available contents
const QList<SectionContent::WeakPtr> allContents = SectionContent::LookupMap.values(); const QList<SectionContent::WeakPtr> allContents = SectionContent::LookupMap.values();
@ -319,22 +449,27 @@ bool ContainerWidget::restoreState(const QByteArray& data)
} }
// What should we do with a drunken sailor.. what should.. erm.. // What should we do with a drunken sailor.. what should.. erm..
// .. we do with the left-contents? // What should we do with the left-contents?
// We might need to add them into the hidden-list, if no other sections are available // Lets add them to the first found SW or create one, if no SW is available.
for (int i = 0; i < leftContents.count(); ++i) for (int i = 0; i < leftContents.count(); ++i)
{ {
const SectionContent::RefPtr sc = leftContents.at(i); const SectionContent::RefPtr sc = leftContents.at(i);
InternalContentData data; SectionWidget* sw = NULL;
this->takeContent(sc, data);
if (sections.isEmpty()) if (sections.isEmpty())
{ {
HiddenSectionItem hsi; sw = new SectionWidget(this);
hsi.data = data; sections.append(sw);
_hiddenSectionContents.insert(sc->uid(), hsi); addSection(sw);
} }
else else
sections.first()->addContent(sc); sw = sections.first();
InternalContentData data;
if (!takeContent(sc, data))
sw->addContent(sc);
else
sw->addContent(data, false);
} }
} }
@ -348,6 +483,11 @@ bool ContainerWidget::restoreState(const QByteArray& data)
qDeleteAll(oldFloatings); qDeleteAll(oldFloatings);
qDeleteAll(oldSections); qDeleteAll(oldSections);
// Hide all as "hidden" marked contents
for (int i = 0; i < contentsToHide.count(); ++i)
hideSectionContent(contentsToHide.at(i));
qDebug() << "End of restore state" << success;
return success; return success;
} }
@ -632,6 +772,15 @@ void ContainerWidget::saveSectionWidgets(QDataStream& out, QWidget* widget) cons
} }
else if ((sw = dynamic_cast<SectionWidget*>(widget)) != NULL) else if ((sw = dynamic_cast<SectionWidget*>(widget)) != NULL)
{ {
// Format (version 1)
// int Object type (SectionWidget=2)
// int Current active index
// int Number of contents (visible + hidden)
// LOOP Contents of section (last int)
// QString Unique name of SectionContent
// bool Visibility
// int Preferred index
const QList<SectionContent::RefPtr>& contents = sw->contents(); const QList<SectionContent::RefPtr>& contents = sw->contents();
QList<HiddenSectionItem> hiddenContents; QList<HiddenSectionItem> hiddenContents;
@ -664,8 +813,10 @@ void ContainerWidget::saveSectionWidgets(QDataStream& out, QWidget* widget) cons
} }
} }
bool ContainerWidget::restoreFloatingWidgets(QDataStream& in, QList<FloatingWidget*>& floatings) bool ContainerWidget::restoreFloatingWidgets(QDataStream& in, int version, QList<FloatingWidget*>& floatings)
{ {
Q_UNUSED(version)
int fwCount = 0; int fwCount = 0;
in >> fwCount; in >> fwCount;
if (fwCount <= 0) if (fwCount <= 0)
@ -701,8 +852,11 @@ bool ContainerWidget::restoreFloatingWidgets(QDataStream& in, QList<FloatingWidg
return true; return true;
} }
bool ContainerWidget::restoreSectionWidgets(QDataStream& in, QSplitter* currentSplitter, QList<SectionWidget*>& sections) bool ContainerWidget::restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList<SectionWidget*>& sections, QList<SectionContent::RefPtr>& contentsToHide)
{ {
if (in.atEnd())
return true;
int type; int type;
in >> type; in >> type;
@ -716,7 +870,7 @@ bool ContainerWidget::restoreSectionWidgets(QDataStream& in, QSplitter* currentS
QSplitter* sp = newSplitter((Qt::Orientation) orientation); QSplitter* sp = newSplitter((Qt::Orientation) orientation);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
if (!restoreSectionWidgets(in, sp, sections)) if (!restoreSectionWidgets(in, version, sp, sections, contentsToHide))
return false; return false;
} }
if (sp->count() <= 0) if (sp->count() <= 0)
@ -762,19 +916,18 @@ bool ContainerWidget::restoreSectionWidgets(QDataStream& in, QSplitter* currentS
qWarning() << "Can not find SectionContent:" << uname; qWarning() << "Can not find SectionContent:" << uname;
continue; continue;
} }
InternalContentData data;
this->takeContent(sc, data);
if (visible) InternalContentData data;
sw->addContent(sc); if (!takeContent(sc, data))
else
{ {
HiddenSectionItem hsi; qCritical() << "Can not find InternalContentData of SC, this should never happen!" << sc->uid() << sc->uniqueName();
hsi.preferredSectionId = sw->uid(); sw->addContent(sc);
hsi.preferredSectionIndex = preferredIndex;
hsi.data = data;
_hiddenSectionContents.insert(sc->uid(), hsi);
} }
else
sw->addContent(data, false);
if (!visible)
contentsToHide.append(sc);
} }
if (sw->contents().isEmpty()) if (sw->contents().isEmpty())
{ {

View File

@ -63,9 +63,11 @@ bool FloatingWidget::takeContent(InternalContentData& data)
data.contentWidget = _contentWidget; data.contentWidget = _contentWidget;
_titleLayout->removeWidget(_titleWidget); _titleLayout->removeWidget(_titleWidget);
_titleWidget->setParent(_container);
_titleWidget = NULL; _titleWidget = NULL;
layout()->removeWidget(_contentWidget); layout()->removeWidget(_contentWidget);
_contentWidget->setParent(_container);
_contentWidget = NULL; _contentWidget = NULL;
return true; return true;

View File

@ -9,6 +9,8 @@ SectionContentWidget::SectionContentWidget(SectionContent::RefPtr c, QWidget* pa
QFrame(parent), QFrame(parent),
_content(c) _content(c)
{ {
qDebug() << Q_FUNC_INFO;
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom); QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
l->setContentsMargins(0, 0, 0, 0); l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0); l->setSpacing(0);

View File

@ -31,6 +31,8 @@ SectionTitleWidget::SectionTitleWidget(SectionContent::RefPtr content, QWidget*
_tabMoving(false), _tabMoving(false),
_activeTab(false) _activeTab(false)
{ {
qDebug() << Q_FUNC_INFO;
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight); QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
l->setContentsMargins(0, 0, 0, 0); l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0); l->setSpacing(0);

View File

@ -159,6 +159,9 @@ void SectionWidget::addContent(const InternalContentData& data, bool autoActivat
// Switch to newest. // Switch to newest.
else if (autoActivate) else if (autoActivate)
setCurrentIndex(_contents.count() - 1); setCurrentIndex(_contents.count() - 1);
// Mark is as inactive tab.
else
data.titleWidget->setActiveTab(false); // or: setCurrentIndex(currentIndex())
} }
bool SectionWidget::takeContent(int uid, InternalContentData& data) bool SectionWidget::takeContent(int uid, InternalContentData& data)
@ -265,8 +268,14 @@ void SectionWidget::moveContent(int from, int to)
void SectionWidget::setCurrentIndex(int index) void SectionWidget::setCurrentIndex(int index)
{ {
// Set active TAB. if (index < 0 || index > _contents.count() - 1)
qDebug() << Q_FUNC_INFO << index; {
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
return;
}
qDebug() << Q_FUNC_INFO << index << QString("section=%1; content=%2").arg(_uid).arg(_contents.at(index)->uniqueName());
// Set active TAB
for (int i = 0; i < _tabsLayout->count(); ++i) for (int i = 0; i < _tabsLayout->count(); ++i)
{ {
QLayoutItem* item = _tabsLayout->itemAt(i); QLayoutItem* item = _tabsLayout->itemAt(i);
@ -283,7 +292,7 @@ void SectionWidget::setCurrentIndex(int index)
} }
} }
// Set active CONTENT. // Set active CONTENT
_contentsLayout->setCurrentIndex(index); _contentsLayout->setCurrentIndex(index);
} }

View File

@ -37,10 +37,13 @@ Sorted by priority
- [x] Restore: Manage new or deleted SectionContent objects, which are not available - [x] Restore: Manage new or deleted SectionContent objects, which are not available
- [x] Working with outer-edge-drops sometimes leaves empty splitters #BUG - [x] Working with outer-edge-drops sometimes leaves empty splitters #BUG
- [x] Clean up of unused e.g. count()<=1 QSplitters doesn't work well #BUG - [x] Clean up of unused e.g. count()<=1 QSplitters doesn't work well #BUG
- [ ] Show close button on right corner of SectionWidget. How to safe last section position? - [x] Show close button on right corner of SectionWidget. How to safe last section position?
- [ ] Serialize state of `_hiddenSectionContents` - [x] Serialize state of `_hiddenSectionContents`
- [ ] It would be easier when the SectionTitleWidget and SectionContentWidget are created inside the "SectionContent::newSectionContent(..)" method.
This would make sure, that those two objects always exists.
- [ ] `ContainerWidget::showSectionContent` needs to insert the SC at the correct preferred position of SW - [ ] `ContainerWidget::showSectionContent` needs to insert the SC at the correct preferred position of SW
- [ ] Empty splitters, if only 2 or 1 items are in container - [ ] Empty splitters, if only 2 or 1 items are in container
- [ ] Restore: Handle out-of-screen geometry for floating widgets
### Some day... ### Some day...
- [ ] Drop indicator images should be fully visible over the DropOverlay rectangle - [ ] Drop indicator images should be fully visible over the DropOverlay rectangle