Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions code/missioneditor/sexp_tree_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ SexpTreeActions::SexpTreeActions(SexpTreeModel& model, ISexpTreeUI& ui)
{
}

void SexpTreeActions::set_node_flags(int node_index, int flags)
{
_model.tree_nodes[node_index].flags = flags;
_ui.ui_set_item_editable(_model.tree_nodes[node_index].handle, (flags & EDITABLE) != 0);
}

// Free all children of a node from both the model and the UI widget.
// After this call, the node has no children in either layer.
void SexpTreeActions::clear_node_children(int node_index)
Expand Down Expand Up @@ -52,7 +58,7 @@ void SexpTreeActions::replace_data(const char* data, int type)
_ui.ui_set_item_text(h, data);
NodeImage bmap = _model.get_data_image(node_idx);
_ui.ui_set_item_image(h, bmap);
_model.tree_nodes[node_idx].flags = EDITABLE;
set_node_flags(node_idx, EDITABLE);

// check remaining data beyond replaced data for validity
verify_and_fix_arguments(_model.tree_nodes[node_idx].parent);
Expand Down Expand Up @@ -82,7 +88,7 @@ void SexpTreeActions::replace_variable_data(int var_idx, int type)
void* h = _model.tree_nodes[node_idx].handle;
_ui.ui_set_item_text(h, buf);
_ui.ui_set_item_image(h, NodeImage::VARIABLE);
_model.tree_nodes[node_idx].flags = NOT_EDITABLE;
set_node_flags(node_idx, NOT_EDITABLE);

// check remaining data beyond replaced data for validity
verify_and_fix_arguments(_model.tree_nodes[node_idx].parent);
Expand All @@ -105,7 +111,7 @@ void SexpTreeActions::replace_container_name(const sexp_container& container)
void* h = _model.tree_nodes[node_idx].handle;
_ui.ui_set_item_image(h, NodeImage::CONTAINER_NAME);
_ui.ui_set_item_text(h, container.container_name.c_str());
_model.tree_nodes[node_idx].flags = NOT_EDITABLE;
set_node_flags(node_idx, NOT_EDITABLE);

if (_model.modified)
*_model.modified = 1;
Expand Down Expand Up @@ -151,7 +157,7 @@ void SexpTreeActions::replace_container_data(const sexp_container& container,
void* h = _model.tree_nodes[node_idx].handle;
_ui.ui_set_item_image(h, NodeImage::CONTAINER_DATA);
_ui.ui_set_item_text(h, container.container_name.c_str());
_model.tree_nodes[node_idx].flags = NOT_EDITABLE;
set_node_flags(node_idx, NOT_EDITABLE);

if (set_default_modifier) {
add_default_modifier(container);
Expand All @@ -174,7 +180,7 @@ void SexpTreeActions::replace_operator(const char* op)
_model.set_node(node_idx, (SEXPT_OPERATOR | SEXPT_VALID), op);
void* h = _model.tree_nodes[node_idx].handle;
_ui.ui_set_item_text(h, op);
_model.tree_nodes[node_idx].flags = OPERAND;
set_node_flags(node_idx, OPERAND);

if (_model.modified)
*_model.modified = 1;
Expand Down Expand Up @@ -207,10 +213,10 @@ void SexpTreeActions::expand_operator(int node)
Assertion(_model.tree_nodes[data].child == -1, "Child %d of node %d unexpectedly has its own children (child %d)", data, node, _model.tree_nodes[data].child);

_ui.ui_set_item_text(h, _model.tree_nodes[node].text);
_model.tree_nodes[node].flags = OPERAND;
set_node_flags(node, OPERAND);
NodeImage bmap = _model.get_data_image(data);
_model.tree_nodes[data].handle = _ui.ui_insert_item(_model.tree_nodes[data].text, bmap, h, nullptr);
_model.tree_nodes[data].flags = EDITABLE;
set_node_flags(data, EDITABLE);
_ui.ui_expand_item(h);
}
}
Expand All @@ -232,7 +238,7 @@ int SexpTreeActions::add_data(const char* data, int type)
_model.set_node(node, type, data);
NodeImage bmap = _model.get_data_image(node);
_model.tree_nodes[node].handle = _ui.ui_insert_item(data, bmap, _model.tree_nodes[node_idx].handle, nullptr);
_model.tree_nodes[node].flags = EDITABLE;
set_node_flags(node, EDITABLE);
if (_model.modified)
*_model.modified = 1;
return node;
Expand All @@ -252,7 +258,7 @@ int SexpTreeActions::add_variable_data(const char* data, int type)
int node = _model.allocate_node(node_idx);
_model.set_node(node, type, data);
_model.tree_nodes[node].handle = _ui.ui_insert_item(data, NodeImage::VARIABLE, _model.tree_nodes[node_idx].handle, nullptr);
_model.tree_nodes[node].flags = NOT_EDITABLE;
set_node_flags(node, NOT_EDITABLE);
if (_model.modified)
*_model.modified = 1;
return node;
Expand All @@ -275,7 +281,7 @@ int SexpTreeActions::add_container_name(const char* container_name)
_model.set_node(node, (SEXPT_VALID | SEXPT_CONTAINER_NAME | SEXPT_STRING), container_name);
_model.tree_nodes[node].handle =
_ui.ui_insert_item(container_name, NodeImage::CONTAINER_NAME, _model.tree_nodes[node_idx].handle, nullptr);
_model.tree_nodes[node].flags = NOT_EDITABLE;
set_node_flags(node, NOT_EDITABLE);
if (_model.modified)
*_model.modified = 1;
return node;
Expand All @@ -296,7 +302,7 @@ void SexpTreeActions::add_container_data(const char* container_name)
_model.set_node(node, (SEXPT_VALID | SEXPT_CONTAINER_DATA | SEXPT_STRING), container_name);
_model.tree_nodes[node].handle =
_ui.ui_insert_item(container_name, NodeImage::CONTAINER_DATA, _model.tree_nodes[node_idx].handle, nullptr);
_model.tree_nodes[node].flags = NOT_EDITABLE;
set_node_flags(node, NOT_EDITABLE);
_model.item_index = node;
if (_model.modified)
*_model.modified = 1;
Expand All @@ -322,7 +328,7 @@ void SexpTreeActions::add_operator(const char* op, void* parent_handle)
_model.tree_nodes[node].handle = _ui.ui_insert_item(op, NodeImage::OPERATOR, _model.tree_nodes[_model.item_index].handle, nullptr);
}

_model.tree_nodes[node].flags = OPERAND;
set_node_flags(node, OPERAND);
_model.item_index = node;
if (_model.modified)
*_model.modified = 1;
Expand Down Expand Up @@ -391,7 +397,7 @@ void SexpTreeActions::add_or_replace_operator(int op, int replace_flag)
if (i < 0) {
_model.set_node(_model.item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text.c_str());
_ui.ui_set_item_text(_model.tree_nodes[_model.item_index].handle, Operators[op].text.c_str());
_model.tree_nodes[_model.item_index].flags = OPERAND;
set_node_flags(_model.item_index, OPERAND);
return;
}
}
Expand Down Expand Up @@ -498,7 +504,6 @@ int SexpTreeActions::insert_operator(int op, void* root_parent_handle)

const int node = _model.allocate_node(parent_node, wrapped_node);
_model.set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text.c_str());
_model.tree_nodes[node].flags = node_flags;

void* parent_handle = nullptr;
if (parent_node >= 0) {
Expand All @@ -521,6 +526,7 @@ int SexpTreeActions::insert_operator(int op, void* root_parent_handle)
}

_model.tree_nodes[node].handle = _ui.ui_insert_item(Operators[op].text.c_str(), NodeImage::OPERATOR, parent_handle, wrapped_handle);
set_node_flags(node, node_flags);

_ui.ui_move_branch(wrapped_node, node);
_model.item_index = node;
Expand Down
3 changes: 3 additions & 0 deletions code/missioneditor/sexp_tree_actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ class SexpTreeActions {
bool rename_container_nodes(const SCP_string& old_name, const SCP_string& new_name);

private:
// Update model flags and synchronize the widget's inline editability.
void set_node_flags(int node_index, int flags);

// Delete all UI children of a node and free their model data.
// Resets the model's child link to -1.
void clear_node_children(int node_index);
Expand Down
2 changes: 2 additions & 0 deletions code/missioneditor/sexp_tree_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ class ISexpTreeUI {
virtual void ui_set_item_text(void* handle, const char* text) = 0;
// Update the icon of a tree item
virtual void ui_set_item_image(void* handle, NodeImage image) = 0;
// Sync native widget editability. FRED2 checks the model flags when editing instead.
virtual void ui_set_item_editable(void* /*handle*/, bool /*editable*/) {}
// Return the first child handle of the given tree item, or nullptr if none
virtual void* ui_get_child_item(void* handle) const = 0;
// Return true if the tree item has any children
Expand Down
19 changes: 11 additions & 8 deletions qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "mission/missionmessage.h"
#include "mission/missionparse.h"
#include "missioneditor/common.h"
#include "missioneditor/sexp_tree_model.h"

#include <globalincs/linklist.h>
#include <globalincs/utility.h>
Expand Down Expand Up @@ -1332,22 +1333,23 @@ bool ShipEditorDialogModel::getArrivalCue() const
return _updateArrival;
}

void ShipEditorDialogModel::setArrivalTreeDirty(int formula)
void ShipEditorDialogModel::setArrivalTreeDirty(const SexpTreeModel& tree)
{
if (_multiEdit && !_updateArrival)
return;

_arrivalTreeFormula = formula;
_updateArrival = true;

for (auto* ptr = GET_FIRST(&obj_used_list); ptr != END_OF_LIST(&obj_used_list); ptr = GET_NEXT(ptr)) {
if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && ptr->flags[Object::Object_Flags::Marked]) {
auto i = ptr->instance;
if (Ships[i].wingnum >= 0)
continue;
if (Ships[i].arrival_cue >= 0 && Ships[i].arrival_cue != formula)
if (Ships[i].arrival_cue >= 0)
free_sexp2(Ships[i].arrival_cue);
Ships[i].arrival_cue = formula;
// Each ship owns its cue, so serialize a separate expression for each one.
Ships[i].arrival_cue = tree.save_tree();
_arrivalTreeFormula = Ships[i].arrival_cue;
}
}

Expand Down Expand Up @@ -1494,22 +1496,23 @@ bool ShipEditorDialogModel::getDepartureCue() const
return _updateDeparture;
}

void ShipEditorDialogModel::setDepartureTreeDirty(int formula)
void ShipEditorDialogModel::setDepartureTreeDirty(const SexpTreeModel& tree)
{
if (_multiEdit && !_updateDeparture)
return;

_departureTreeFormula = formula;
_updateDeparture = true;

for (auto* ptr = GET_FIRST(&obj_used_list); ptr != END_OF_LIST(&obj_used_list); ptr = GET_NEXT(ptr)) {
if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && ptr->flags[Object::Object_Flags::Marked]) {
auto i = ptr->instance;
if (Ships[i].wingnum >= 0)
continue;
if (Ships[i].departure_cue >= 0 && Ships[i].departure_cue != formula)
if (Ships[i].departure_cue >= 0)
free_sexp2(Ships[i].departure_cue);
Ships[i].departure_cue = formula;
// Each ship owns its cue, so serialize a separate expression for each one.
Ships[i].departure_cue = tree.save_tree();
_departureTreeFormula = Ships[i].departure_cue;
}
}

Expand Down
6 changes: 4 additions & 2 deletions qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "mission/util.h"
#include "ship/ship.h"

class SexpTreeModel;

namespace fso::fred::dialogs {

class ShipEditorDialogModel : public AbstractDialogModel {
Expand Down Expand Up @@ -84,7 +86,7 @@ class ShipEditorDialogModel : public AbstractDialogModel {
void setArrivalCue(bool updateCue);
bool getArrivalCue() const;

void setArrivalTreeDirty(int formula);
void setArrivalTreeDirty(const SexpTreeModel& tree);
int getArrivalFormula() const;

void setNoArrivalWarp(int state);
Expand All @@ -107,7 +109,7 @@ class ShipEditorDialogModel : public AbstractDialogModel {
void setDepartureCue(bool updateCue);
bool getDepartureCue() const;

void setDepartureTreeDirty(int formula);
void setDepartureTreeDirty(const SexpTreeModel& tree);
int getDepartureFormula() const;
void setNoDepartureWarp(int state);
int getNoDepartureWarp() const;
Expand Down
4 changes: 2 additions & 2 deletions qtfred/src/ui/dialogs/ShipEditor/ShipEditorDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ void ShipEditorDialog::on_noArrivalWarpCheckBox_stateChanged(int state)
}
void ShipEditorDialog::on_arrivalTree_modified()
{
_model->setArrivalTreeDirty(ui->arrivalTree->_model.save_tree());
_model->setArrivalTreeDirty(ui->arrivalTree->_model);
}
void ShipEditorDialog::on_arrivalTree_helpChanged(const QString& help)
{
Expand Down Expand Up @@ -898,7 +898,7 @@ void ShipEditorDialog::on_updateDepartureCueCheckBox_toggled(bool value)
}
void fred::dialogs::ShipEditorDialog::on_departureTree_modified()
{
_model->setDepartureTreeDirty(ui->departureTree->_model.save_tree());
_model->setDepartureTreeDirty(ui->departureTree->_model);
}
void ShipEditorDialog::on_departureTree_helpChanged(const QString& help)
{
Expand Down
16 changes: 15 additions & 1 deletion qtfred/src/ui/widgets/sexp_tree_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,13 @@ void sexp_tree_view::ui_set_item_image(void* handle, NodeImage image)
applyNodeIcon(static_cast<QTreeWidgetItem*>(handle), image);
}

// Keep Qt inline editing consistent with the shared model after node type changes.
void sexp_tree_view::ui_set_item_editable(void* handle, bool editable)
{
auto* item = static_cast<QTreeWidgetItem*>(handle);
item->setFlags(item->flags().setFlag(Qt::ItemIsEditable, editable));
}

// Returns the first child QTreeWidgetItem, or nullptr. Called by _actions to traverse the tree.
void* sexp_tree_view::ui_get_child_item(void* handle) const
{
Expand Down Expand Up @@ -587,6 +594,7 @@ QTreeWidgetItem* sexp_tree_view::move_branch(QTreeWidgetItem* source, QTreeWidge
// Create the destination item
const auto icon = source->icon(0);
QTreeWidgetItem* h = insertWithIcon(source->text(0), icon, parent, after);
h->setFlags(source->flags());
if (idx < tree_nodes.size()) {
tree_nodes[idx].handle = h;
}
Expand Down Expand Up @@ -630,6 +638,7 @@ void sexp_tree_view::copy_branch(QTreeWidgetItem* source, QTreeWidgetItem* paren

const auto icon = source->icon(0);
QTreeWidgetItem* h = insertWithIcon(source->text(0), icon, parent, after);
h->setFlags(source->flags());
size_t idx = 0;
for (; idx < tree_nodes.size(); ++idx) {
if (tree_nodes[idx].handle == source) {
Expand Down Expand Up @@ -1815,9 +1824,10 @@ void sexp_tree_view::insertOperatorAction(int op) {
auto* old_item = tree_item_handle(tree_nodes[item_index]);
auto* root_parent = old_item ? old_item->parent() : nullptr;
const int old_item_index = item_index;
const bool is_formula_root = tree_nodes[old_item_index].parent == -1;
const int node = _actions.insert_operator(op, root_parent);

if (_interface->getFlags()[TreeFlags::LabeledRoot] && root_parent != nullptr) {
if (is_formula_root && _interface->getFlags()[TreeFlags::LabeledRoot] && root_parent != nullptr) {
rootNodeFormulaChanged(old_item_index, node);
root_parent->setData(0, FormulaDataRole, node);
}
Expand Down Expand Up @@ -1882,6 +1892,10 @@ void sexp_tree_view::replaceStringDataHandler() {
// Sets the _currently_editing flag and calls Qt's editItem() to start inline text editing.
// The flag ensures that handleItemChange() only processes intentional edits, not programmatic changes.
void sexp_tree_view::beginItemEdit(QTreeWidgetItem* item) {
if (item == nullptr || !item->flags().testFlag(Qt::ItemIsEditable)) {
return;
}

_currently_editing = true;

editItem(item);
Expand Down
1 change: 1 addition & 0 deletions qtfred/src/ui/widgets/sexp_tree_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ class sexp_tree_view: public QTreeWidget, public ISexpTreeUI {
void ui_delete_item(void* handle) override; //!< Deletes a QTreeWidgetItem
void ui_set_item_text(void* handle, const char* text) override; //!< Sets display text via setText()
void ui_set_item_image(void* handle, NodeImage image) override; //!< Sets icon via setIcon()
void ui_set_item_editable(void* handle, bool editable) override; //!< Syncs Qt::ItemIsEditable with model flags
void* ui_get_child_item(void* handle) const override; //!< Returns first child item, or nullptr
bool ui_has_children(void* handle) const override; //!< Returns true if childCount() > 0
void ui_expand_item(void* handle) override; //!< Expands a single item via setExpanded()
Expand Down
Loading