Fix some Qt editor crashes

Fix use-after-delete on proplists by item view and crash on recursive execution of message processing.
qteditor
Sven Eberhardt 2016-03-03 00:13:39 -05:00
parent 771a333e21
commit 74b3983f7f
5 changed files with 49 additions and 15 deletions

View File

@ -58,6 +58,14 @@ void C4ConsoleQtObjectListModel::Invalidate()
emit layoutChanged();
}
void C4ConsoleQtObjectListModel::OnItemRemoved(C4PropList *p)
{
for (auto idx : this->persistentIndexList())
if (idx.internalPointer() == p)
this->changePersistentIndex(idx, QModelIndex());
Invalidate();
}
void C4ConsoleQtObjectListModel::SetSelection(class C4EditCursorSelection &rSelection)
{
// Reflect selection change in view
@ -132,12 +140,12 @@ QVariant C4ConsoleQtObjectListModel::data(const QModelIndex & index, int role) c
else if (role == Qt::ForegroundRole)
{
// Deleted proplist?
if (!data) return clr_deleted;
if (!data) return QVariant(clr_deleted);
// Object?
C4Object *obj = data->GetObject();
if (obj) return QVariant(); // default
// Effect
return clr_effect;
return QVariant(clr_effect);
}
// Nothing to show
return QVariant();

View File

@ -42,6 +42,7 @@ public:
// Refresh object list on next redraw
void Invalidate();
void OnItemRemoved(C4PropList *p);
// Callback from EditCursor when selection was changed e.g. from viewport
void SetSelection(class C4EditCursorSelection &rSelection);

View File

@ -150,6 +150,22 @@ void C4ConsoleViewportWidget::closeEvent(QCloseEvent * event)
}
/* Recursion check to avoid some crashing Qt re-entry */
class ExecRecursionCheck
{
static int counter;
public:
ExecRecursionCheck() { ++counter; }
~ExecRecursionCheck() { --counter; }
bool IsRecursion() const { return counter > 1; }
};
int ExecRecursionCheck::counter = 0;
/* Console main window */
C4ConsoleQtMainWindow::C4ConsoleQtMainWindow(C4AbstractApp *app, C4ConsoleGUIState *state)
@ -339,6 +355,8 @@ void C4ConsoleGUIState::AddToolbarSpacer(int space)
bool C4ConsoleGUIState::CreateConsoleWindow(C4AbstractApp *app)
{
// No Qt main loop execution during console creation
ExecRecursionCheck no_qt_recursion;
// Basic Qt+Main window setup from .ui file
int fake_argc = 0;
application.reset(new QApplication(fake_argc, NULL));
@ -396,22 +414,28 @@ bool C4ConsoleGUIState::CreateConsoleWindow(C4AbstractApp *app)
return true;
}
void C4ConsoleGUIState::Execute()
void C4ConsoleGUIState::Execute(bool redraw_only)
{
// Avoid recursion in message processing; it's causing random crashes
ExecRecursionCheck recursion_check;
if (recursion_check.IsRecursion()) return;
// Qt window message handling and object cleanup
if (application)
{
application->processEvents();
application->sendPostedEvents(0, QEvent::DeferredDelete);
if (redraw_only)
{
// process only non-critical events
application->processEvents(QEventLoop::ExcludeUserInputEvents);
}
else
{
// process everything
application->processEvents();
application->sendPostedEvents(0, QEvent::DeferredDelete);
}
}
}
void C4ConsoleGUIState::Redraw()
{
// process only non-critical events
if (application) application->processEvents(QEventLoop::ExcludeUserInputEvents);
}
// Set action pressed/checked and enabled states
void C4ConsoleGUIState::UpdateActionStates()
{

View File

@ -182,8 +182,8 @@ public:
void AddToolbarSpacer(int space);
bool CreateConsoleWindow(C4AbstractApp *app);
void Execute();
void Redraw();
void Execute(bool redraw_only=false);
void Redraw() { Execute(true); }
void UpdateActionStates();
void UpdateMatTex();
void UpdateBackMatTex();

View File

@ -21,6 +21,7 @@
#include <C4Console.h>
#include <C4Object.h>
#include <C4GameObjects.h>
#include <C4Effect.h>
#ifdef USE_GTK
@ -865,7 +866,7 @@ void C4ObjectListDlg::Update(C4EditCursorSelection &rSelection)
// Could do some crazy fine-grained updates. But updating is cheap enough...
void C4ObjectListDlg::OnObjectRemove(C4ObjectList * pList, C4ObjectLink * pLnk)
{
if (view_model) view_model->Invalidate();
if (view_model) view_model->OnItemRemoved(pLnk->Obj);
}
void C4ObjectListDlg::OnObjectAdded(C4ObjectList * pList, C4ObjectLink * pLnk)
@ -890,7 +891,7 @@ void C4ObjectListDlg::OnEffectAdded(C4Effect *fx)
void C4ObjectListDlg::OnEffectRemoved(C4Effect *fx)
{
if (view_model) view_model->Invalidate();
if (view_model) view_model->OnItemRemoved(fx);
}