Skip to content

Commit 3ccec4b

Browse files
authored
Qtfred right click menu (#7354)
1 parent 35ce84e commit 3ccec4b

8 files changed

Lines changed: 240 additions & 17 deletions

File tree

code/prop/prop.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,9 @@ int prop_create(const matrix* orient, const vec3d* pos, int prop_type, const cha
473473
Error(LOCATION, "Cannot create prop %s; pof file is not valid", pip->name.c_str());
474474
return -1;
475475
}
476-
pip->model_num = model_load(pip->pof_file.c_str());
476+
if (pip->model_num == -1) {
477+
pip->model_num = model_load(pip->pof_file.c_str());
478+
}
477479

478480
polymodel* pm = model_get(pip->model_num);
479481

code/prop/prop.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ typedef struct prop_info {
1616
SCP_string pof_file; // Pof filename
1717
vec3d closeup_pos; // position for camera when using prop in closeup view (eg briefing and techroom)
1818
float closeup_zoom; // zoom when using prop in closeup view (eg briefing and techroom)
19-
int model_num; // The model number of the loaded POF
19+
int model_num = -1; // The model number of the loaded POF
2020
int num_detail_levels; // Detail levels of the model
2121
int detail_distance[MAX_PROP_DETAIL_LEVELS]; // distance to change detail levels at
2222
SCP_unordered_map<int, void*> glowpoint_bank_override_map; // Glow point bank overrides currently unused

qtfred/src/mission/Editor.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,15 @@ int Editor::dup_object(object* objp) {
10361036
} else if (objp->type == OBJ_WAYPOINT) {
10371037
obj = create_waypoint(&objp->pos, waypoint_instance);
10381038
waypoint_instance = Objects[obj].instance;
1039+
} else if (objp->type == OBJ_JUMP_NODE) {
1040+
CJumpNode jnp(&objp->pos);
1041+
obj = jnp.GetSCPObjectNumber();
1042+
Jump_nodes.push_back(std::move(jnp));
1043+
} else if (objp->type == OBJ_PROP) {
1044+
prop* propp = prop_id_lookup(inst);
1045+
if (propp != nullptr) {
1046+
obj = prop_create(&objp->orient, &objp->pos, propp->prop_info_index);
1047+
}
10391048
}
10401049

10411050
if (obj == -1) {

qtfred/src/mission/EditorViewport.cpp

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,25 +1140,37 @@ int EditorViewport::create_object_on_grid(int x, int y, int waypoint_instance) {
11401140
}
11411141

11421142
int EditorViewport::create_object_on_grid(int x, int y, int waypoint_instance, bool create_prop) {
1143-
int obj = -1;
1144-
float rval;
1145-
vec3d dir, pos;
1146-
1147-
g3_point_to_vec_delayed(&dir, x, y);
1148-
1149-
rval = fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.vec.uvec, &view_pos, &dir, 0.0f);
1143+
float fallbackDist = 200.0f;
1144+
if (create_prop) {
1145+
if (cur_prop_index >= 0 && cur_prop_index < prop_info_size()) {
1146+
prop_info* pip = &Prop_info[cur_prop_index];
1147+
if (pip->model_num >= 0) {
1148+
fallbackDist = model_get_radius(pip->model_num) * 1.5f;
1149+
} else if (VALID_FNAME(pip->pof_file)) {
1150+
int modelNum = model_load(pip->pof_file.c_str());
1151+
if (modelNum >= 0) {
1152+
fallbackDist = model_get_radius(modelNum) * 1.5f;
1153+
model_unload(modelNum);
1154+
}
1155+
}
1156+
}
1157+
} else if (cur_model_index >= 0 && cur_model_index < (int)Ship_info.size() &&
1158+
cur_model_index != editor->Id_select_type_waypoint &&
1159+
cur_model_index != editor->Id_select_type_jump_node &&
1160+
Ship_info[cur_model_index].model_num >= 0) {
1161+
fallbackDist = model_get_radius(Ship_info[cur_model_index].model_num) * 1.5f;
1162+
}
11501163

1151-
if (rval >= 0.0f) {
1152-
editor->unmark_all();
1153-
obj = create_object(&pos, waypoint_instance, create_prop);
1154-
if (obj >= 0) {
1155-
editor->markObject(obj);
1164+
vec3d pos = getCreatePosition(x, y, fallbackDist);
1165+
editor->unmark_all();
1166+
int obj = create_object(&pos, waypoint_instance, create_prop);
1167+
if (obj >= 0) {
1168+
editor->markObject(obj);
11561169

11571170
editor->autosave("object create");
11581171

1159-
} else if (obj == -1) {
1160-
dialogProvider->showButtonDialog(DialogType::Error, "Error", "Maximum ship limit reached. Can't add any more ships.", { DialogButton::Ok });
1161-
}
1172+
} else if (obj == -1) {
1173+
dialogProvider->showButtonDialog(DialogType::Error, "Error", "Maximum ship limit reached. Can't add any more ships.", { DialogButton::Ok });
11621174
}
11631175

11641176
return obj;
@@ -1205,6 +1217,56 @@ int EditorViewport::create_object(vec3d* pos, int waypoint_instance, bool create
12051217
needsUpdate();
12061218
return obj;
12071219
}
1220+
vec3d EditorViewport::getCreatePosition(int x, int y, float fallbackDist) {
1221+
vec3d dir, pos;
1222+
g3_point_to_vec_delayed(&dir, x, y);
1223+
if (fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.vec.uvec, &view_pos, &dir, 0.0f) >= 0.0f) {
1224+
return pos;
1225+
}
1226+
vm_vec_scale_add(&pos, &view_pos, &view_orient.vec.fvec, fallbackDist);
1227+
return pos;
1228+
}
1229+
1230+
int EditorViewport::createShipAtScreenPos(int x, int y, int modelIndex) {
1231+
if (modelIndex < 0 || modelIndex >= (int)Ship_info.size() ||
1232+
Ship_info[modelIndex].flags[Ship::Info_Flags::No_fred]) {
1233+
return -1;
1234+
}
1235+
int savedModelIndex = cur_model_index;
1236+
cur_model_index = modelIndex;
1237+
int obj = create_object_on_grid(x, y, -1, false);
1238+
cur_model_index = savedModelIndex;
1239+
return obj;
1240+
}
1241+
1242+
int EditorViewport::createPropAtScreenPos(int x, int y, int propIndex) {
1243+
if (propIndex < 0 || propIndex >= prop_info_size() ||
1244+
Prop_info[propIndex].flags[Prop::Info_Flags::No_fred]) {
1245+
return -1;
1246+
}
1247+
int savedPropIndex = cur_prop_index;
1248+
cur_prop_index = propIndex;
1249+
int obj = create_object_on_grid(x, y, -1, true);
1250+
cur_prop_index = savedPropIndex;
1251+
return obj;
1252+
}
1253+
1254+
int EditorViewport::createWaypointAtScreenPos(int x, int y, int waypoint_instance) {
1255+
int savedModelIndex = cur_model_index;
1256+
cur_model_index = editor->Id_select_type_waypoint;
1257+
int obj = create_object_on_grid(x, y, waypoint_instance, false);
1258+
cur_model_index = savedModelIndex;
1259+
return obj;
1260+
}
1261+
1262+
int EditorViewport::createJumpNodeAtScreenPos(int x, int y) {
1263+
int savedModelIndex = cur_model_index;
1264+
cur_model_index = editor->Id_select_type_jump_node;
1265+
int obj = create_object_on_grid(x, y, -1, false);
1266+
cur_model_index = savedModelIndex;
1267+
return obj;
1268+
}
1269+
12081270
void EditorViewport::initialSetup() {
12091271
cur_model_index = get_default_player_ship_index();
12101272
for (int i = 0; i < prop_info_size(); ++i) {

qtfred/src/mission/EditorViewport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ class EditorViewport {
127127

128128
int create_object(vec3d *pos, int waypoint_instance = -1, bool create_prop = false);
129129

130+
vec3d getCreatePosition(int x, int y, float fallbackDist);
131+
int createShipAtScreenPos(int x, int y, int modelIndex);
132+
int createPropAtScreenPos(int x, int y, int propIndex);
133+
int createWaypointAtScreenPos(int x, int y, int waypoint_instance = -1);
134+
int createJumpNodeAtScreenPos(int x, int y);
135+
130136
int duplicate_marked_objects();
131137
int drag_objects(int x, int y);
132138

qtfred/src/mission/object.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "object/waypoint.h"
55
#include "object/objectdock.h"
66
#include "ship/ship.h"
7+
#include "jumpnode/jumpnode.h"
8+
#include "prop/prop.h"
79

810
void object_moved(object *objp)
911
{
@@ -65,6 +67,20 @@ const char* object_name(int obj) {
6567

6668
case OBJ_POINT:
6769
return "Briefing icon";
70+
71+
case OBJ_JUMP_NODE: {
72+
const CJumpNode* jnp = jumpnode_get_by_objnum(obj);
73+
if (jnp != nullptr)
74+
return jnp->GetName();
75+
break;
76+
}
77+
78+
case OBJ_PROP: {
79+
int idx = Objects[obj].instance;
80+
if (idx >= 0 && idx < (int)Props.size() && Props[idx].has_value())
81+
return Props[idx]->prop_name;
82+
break;
83+
}
6884
}
6985

7086
return "*unknown*";

qtfred/src/ui/FredView.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656

5757
#include "mission/Editor.h"
5858
#include "mission/management.h"
59+
#include <prop/prop.h>
5960
#include "mission/missionparse.h"
6061
#include "missioneditor/missionsave.h"
6162

@@ -768,6 +769,7 @@ void FredView::connectActionToViewSetting(QAction* option, std::vector<bool>* ve
768769

769770
void FredView::showContextMenu(const QPoint& globalPos) {
770771
auto localPos = ui->centralWidget->mapFromGlobal(globalPos);
772+
_lastContextMenuLocalPos = localPos;
771773

772774
auto obj = _viewport->select_object(localPos.x(), localPos.y());
773775
if (obj >= 0) {
@@ -779,6 +781,14 @@ void FredView::showContextMenu(const QPoint& globalPos) {
779781
populateMoveToLayerMenu(obj);
780782
}
781783

784+
// Control Edit Wing / Select Wing visibility and enabled state
785+
const bool isShip = (objType == OBJ_SHIP) || (objType == OBJ_START);
786+
const bool inWing = isShip && Ships[Objects[obj].instance].wingnum >= 0;
787+
_editWingAction->setVisible(isShip);
788+
_editWingAction->setEnabled(inWing);
789+
_selectWingAction->setVisible(isShip);
790+
_selectWingAction->setEnabled(inWing);
791+
782792
// There is an object under the cursor
783793
SCP_string objName;
784794
if (fred->getNumMarked() > 1) {
@@ -792,6 +802,8 @@ void FredView::showContextMenu(const QPoint& globalPos) {
792802
_editPopup->exec(globalPos);
793803
} else {
794804
// Nothing is here...
805+
_createPropSubmenu->setEnabled(_viewport->cur_prop_index >= 0);
806+
_viewZoomSelectedAction->setEnabled(query_valid_object(fred->currentObject));
795807
_viewPopup->exec(globalPos);
796808
}
797809
}
@@ -820,10 +832,59 @@ void FredView::initializePopupMenus() {
820832
_viewPopup->addMenu(_controlModeMenu);
821833
_viewPopup->addMenu(ui->menuViewpoint);
822834
_viewPopup->addSeparator();
835+
836+
_createSubmenu = new QMenu(tr("Create"), _viewPopup);
837+
838+
_createShipSubmenu = new QMenu(tr("Ship"), _createSubmenu);
839+
_createShipSubmenu->setStyleSheet("QMenu { menu-scrollable: 1; }");
840+
connect(_createShipSubmenu, &QMenu::aboutToShow, this, [this]() {
841+
if (_createShipSubmenu->actions().isEmpty()) {
842+
populateCreateShipSubmenu();
843+
}
844+
});
845+
_createSubmenu->addMenu(_createShipSubmenu);
846+
847+
_createPropSubmenu = new QMenu(tr("Prop"), _createSubmenu);
848+
_createPropSubmenu->setStyleSheet("QMenu { menu-scrollable: 1; }");
849+
connect(_createPropSubmenu, &QMenu::aboutToShow, this, [this]() {
850+
if (_createPropSubmenu->actions().isEmpty()) {
851+
populateCreatePropSubmenu();
852+
}
853+
});
854+
_createSubmenu->addMenu(_createPropSubmenu);
855+
856+
auto* createWaypointAction = new QAction(tr("Waypoint"), _createSubmenu);
857+
connect(createWaypointAction, &QAction::triggered, this, [this]() {
858+
int waypoint_instance = -1;
859+
if (fred->cur_waypoint != nullptr) {
860+
waypoint_instance = Objects[fred->cur_waypoint->get_objnum()].instance;
861+
}
862+
_viewport->createWaypointAtScreenPos(_lastContextMenuLocalPos.x(), _lastContextMenuLocalPos.y(), waypoint_instance);
863+
});
864+
_createSubmenu->addAction(createWaypointAction);
865+
866+
auto* createJumpNodeAction = new QAction(tr("Jump Node"), _createSubmenu);
867+
connect(createJumpNodeAction, &QAction::triggered, this, [this]() {
868+
_viewport->createJumpNodeAtScreenPos(_lastContextMenuLocalPos.x(), _lastContextMenuLocalPos.y());
869+
});
870+
_createSubmenu->addAction(createJumpNodeAction);
871+
872+
_viewPopup->addMenu(_createSubmenu);
873+
_viewPopup->addSeparator();
874+
823875
auto* manageLayersViewAction = new QAction(tr("Manage Layers..."), _viewPopup);
824876
connect(manageLayersViewAction, &QAction::triggered, this, [this]() { openLayerManagerDialog(); });
825877
_viewPopup->addAction(manageLayersViewAction);
826878

879+
_viewPopup->addSeparator();
880+
_viewZoomSelectedAction = new QAction(tr("Zoom to Selected"), _viewPopup);
881+
connect(_viewZoomSelectedAction, &QAction::triggered, this, &FredView::on_actionZoomSelected_triggered);
882+
_viewPopup->addAction(_viewZoomSelectedAction);
883+
884+
auto* viewZoomExtentsAction = new QAction(tr("Zoom Extents"), _viewPopup);
885+
connect(viewZoomExtentsAction, &QAction::triggered, this, &FredView::on_actionZoomExtents_triggered);
886+
_viewPopup->addAction(viewZoomExtentsAction);
887+
827888
// Begin construction edit popup
828889
_editPopup = new QMenu(this);
829890

@@ -836,10 +897,69 @@ void FredView::initializePopupMenus() {
836897
_editPopup->addAction(_editOrientPositionAction);
837898

838899
_editWingAction = new QAction(tr("Edit Wing"), _editPopup);
900+
connect(_editWingAction, &QAction::triggered, this, &FredView::on_actionWings_triggered);
839901
_editPopup->addAction(_editWingAction);
902+
903+
_selectWingAction = new QAction(tr("Select Wing"), _editPopup);
904+
connect(_selectWingAction, &QAction::triggered, this, [this]() {
905+
int obj = fred->currentObject;
906+
if (query_valid_object(obj) && (Objects[obj].type == OBJ_SHIP || Objects[obj].type == OBJ_START)) {
907+
int wing = Ships[Objects[obj].instance].wingnum;
908+
if (wing >= 0) {
909+
fred->mark_wing(wing);
910+
}
911+
}
912+
});
913+
_editPopup->addAction(_selectWingAction);
914+
840915
_editPopup->addSeparator();
841916
_moveToLayerMenu = new QMenu(tr("Move to Layer"), _editPopup);
917+
_moveToLayerMenu->setStyleSheet("QMenu { menu-scrollable: 1; }");
842918
_editPopup->addMenu(_moveToLayerMenu);
919+
920+
_editPopup->addSeparator();
921+
auto* deleteAction = new QAction(tr("Delete"), _editPopup);
922+
connect(deleteAction, &QAction::triggered, this, &FredView::on_actionDelete_triggered);
923+
_editPopup->addAction(deleteAction);
924+
925+
auto* cloneAction = new QAction(tr("Clone"), _editPopup);
926+
connect(cloneAction, &QAction::triggered, this, &FredView::on_actionClone_Marked_Objects_triggered);
927+
_editPopup->addAction(cloneAction);
928+
929+
_editPopup->addSeparator();
930+
auto* editZoomSelectedAction = new QAction(tr("Zoom to Selected"), _editPopup);
931+
connect(editZoomSelectedAction, &QAction::triggered, this, &FredView::on_actionZoomSelected_triggered);
932+
_editPopup->addAction(editZoomSelectedAction);
933+
934+
auto* editZoomExtentsAction = new QAction(tr("Zoom Extents"), _editPopup);
935+
connect(editZoomExtentsAction, &QAction::triggered, this, &FredView::on_actionZoomExtents_triggered);
936+
_editPopup->addAction(editZoomExtentsAction);
937+
}
938+
939+
void FredView::populateCreateShipSubmenu() {
940+
for (int i = 0; i < (int)Ship_info.size(); ++i) {
941+
if (Ship_info[i].flags[Ship::Info_Flags::No_fred]) {
942+
continue;
943+
}
944+
auto* action = new QAction(QString::fromUtf8(Ship_info[i].name), _createShipSubmenu);
945+
connect(action, &QAction::triggered, this, [this, i]() {
946+
_viewport->createShipAtScreenPos(_lastContextMenuLocalPos.x(), _lastContextMenuLocalPos.y(), i);
947+
});
948+
_createShipSubmenu->addAction(action);
949+
}
950+
}
951+
952+
void FredView::populateCreatePropSubmenu() {
953+
for (int i = 0; i < prop_info_size(); ++i) {
954+
if (Prop_info[i].flags[Prop::Info_Flags::No_fred]) {
955+
continue;
956+
}
957+
auto* action = new QAction(QString::fromStdString(Prop_info[i].name), _createPropSubmenu);
958+
connect(action, &QAction::triggered, this, [this, i]() {
959+
_viewport->createPropAtScreenPos(_lastContextMenuLocalPos.x(), _lastContextMenuLocalPos.y(), i);
960+
});
961+
_createPropSubmenu->addAction(action);
962+
}
843963
}
844964

845965
void FredView::populateMoveToLayerMenu(int targetObject) {

qtfred/src/ui/FredView.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ class FredView: public QMainWindow, public IDialogProvider {
218218
void initializeStatusBar();
219219
void initializePopupMenus();
220220
void populateMoveToLayerMenu(int targetObject);
221+
void populateCreateShipSubmenu();
222+
void populateCreatePropSubmenu();
221223
void openLayerManagerDialog();
222224
void ensureViewportFocus();
223225

@@ -228,12 +230,18 @@ class FredView: public QMainWindow, public IDialogProvider {
228230
QLabel* _statusBarUnitsLabel = nullptr;
229231

230232
QMenu* _viewPopup = nullptr;
233+
QMenu* _createSubmenu = nullptr;
234+
QMenu* _createShipSubmenu = nullptr;
235+
QMenu* _createPropSubmenu = nullptr;
236+
QPoint _lastContextMenuLocalPos;
231237

232238
QMenu* _editPopup = nullptr;
233239
QAction* _editObjectAction = nullptr;
234240
QAction* _editOrientPositionAction = nullptr;
235241
QAction* _editWingAction = nullptr;
242+
QAction* _selectWingAction = nullptr;
236243
QMenu* _moveToLayerMenu = nullptr;
244+
QAction* _viewZoomSelectedAction = nullptr;
237245

238246
QMenu* _controlModeMenu = nullptr;
239247
QAction* _controlModeCamera = nullptr;

0 commit comments

Comments
 (0)