1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "sexp_tree.h"
13 #include "mission/util.h"
14 #include "mission/Editor.h"
15 #include "mission/object.h"
16 
17 #include "parse/sexp.h"
18 #include "globalincs/linklist.h"
19 #include "ai/aigoals.h"
20 #include "mission/missionmessage.h"
21 #include "mission/missioncampaign.h"
22 #include "hud/hudsquadmsg.h"
23 #include "stats/medals.h"
24 #include "controlconfig/controlsconfig.h"
25 #include "hud/hudgauges.h"
26 #include "starfield/starfield.h"
27 #include "nebula/neb.h"
28 #include "nebula/neblightning.h"
29 #include "jumpnode/jumpnode.h"
30 #include "gamesnd/eventmusic.h"    // for change-soundtrack
31 #include "menuui/techmenu.h"    // for intel stuff
32 #include "weapon/emp.h"
33 #include "gamesnd/gamesnd.h"
34 #include "weapon/weapon.h"
35 #include "hud/hudartillery.h"
36 #include "iff_defs/iff_defs.h"
37 #include "mission/missionmessage.h"
38 #include "sound/ds.h"
39 #include "globalincs/alphacolors.h"
40 #include "localization/localize.h"
41 #include "mission/missiongoals.h"
42 #include "ship/ship.h"
43 
44 #include <ui/util/menu.h>
45 #include <ui/util/SignalBlockers.h>
46 
47 #include <QtWidgets/QMessageBox>
48 #include <QtWidgets/QMenu>
49 #include <QDebug>
50 
51 #define TREE_NODE_INCREMENT    100
52 
53 #define MAX_OP_MENUS    30
54 #define MAX_SUBMENUS    (MAX_OP_MENUS * MAX_OP_MENUS)
55 
56 extern SCP_vector<game_snd> Snds;
57 
58 //********************sexp_tree********************
59 
60 namespace fso {
61 namespace fred {
62 
63 namespace {
node_image_to_resource_name(NodeImage image)64 QString node_image_to_resource_name(NodeImage image) {
65 	switch (image) {
66 	case NodeImage::OPERATOR:
67 		return ":/images/bitmap1.png";
68 	case NodeImage::DATA:
69 		return ":/images/data.png";
70 	case NodeImage::VARIABLE:
71 		return ":/images/variable.png";
72 	case NodeImage::ROOT:
73 		return ":/images/root.png";
74 	case NodeImage::ROOT_DIRECTIVE:
75 		return ":/images/root_directive.png";
76 	case NodeImage::CHAIN:
77 		return ":/images/chained.png";
78 	case NodeImage::CHAIN_DIRECTIVE:
79 		return ":/images/chained_directive.png";
80 	case NodeImage::GREEN_DOT:
81 		return ":/images/green_do.png";
82 	case NodeImage::BLACK_DOT:
83 		return ":/images/black_do.png";
84 	case NodeImage::DATA_00:
85 		return ":/images/data00.png";
86 	case NodeImage::DATA_05:
87 		return ":/images/data05.png";
88 	case NodeImage::DATA_10:
89 		return ":/images/data10.png";
90 	case NodeImage::DATA_15:
91 		return ":/images/data15.png";
92 	case NodeImage::DATA_20:
93 		return ":/images/data20.png";
94 	case NodeImage::DATA_25:
95 		return ":/images/data25.png";
96 	case NodeImage::DATA_30:
97 		return ":/images/data30.png";
98 	case NodeImage::DATA_35:
99 		return ":/images/data35.png";
100 	case NodeImage::DATA_40:
101 		return ":/images/data40.png";
102 	case NodeImage::DATA_45:
103 		return ":/images/data45.png";
104 	case NodeImage::DATA_50:
105 		return ":/images/data50.png";
106 	case NodeImage::DATA_55:
107 		return ":/images/data55.png";
108 	case NodeImage::DATA_60:
109 		return ":/images/data60.png";
110 	case NodeImage::DATA_65:
111 		return ":/images/data65.png";
112 	case NodeImage::DATA_70:
113 		return ":/images/data70.png";
114 	case NodeImage::DATA_75:
115 		return ":/images/data75.png";
116 	case NodeImage::DATA_80:
117 		return ":/images/data80.png";
118 	case NodeImage::DATA_85:
119 		return ":/images/data85.png";
120 	case NodeImage::DATA_90:
121 		return ":/images/data90.png";
122 	case NodeImage::DATA_95:
123 		return ":/images/data95.png";
124 	case NodeImage::COMMENT:
125 		return ":/images/comment.png";
126 	}
127 	return ":/images/bitmap1.png";
128 }
129 }
130 
SexpTreeEditorInterface()131 SexpTreeEditorInterface::SexpTreeEditorInterface() :
132 	SexpTreeEditorInterface(flagset<TreeFlags>{ TreeFlags::LabeledRoot, TreeFlags::RootDeletable }) {
133 }
SexpTreeEditorInterface(const flagset<TreeFlags> & flags)134 SexpTreeEditorInterface::SexpTreeEditorInterface(const flagset<TreeFlags>& flags) : _flags(flags) {
135 
136 }
hasDefaultMessageParamter()137 bool SexpTreeEditorInterface::hasDefaultMessageParamter() {
138 	return Num_messages > Num_builtin_messages;
139 }
getMessages()140 SCP_vector<SCP_string> SexpTreeEditorInterface::getMessages() {
141 	SCP_vector<SCP_string> list;
142 
143 	for (auto i = Num_builtin_messages; i < Num_messages; i++) {
144 		list.emplace_back(Messages[i].name);
145 	}
146 
147 	return list;
148 }
getMissionGoals(const SCP_string &)149 SCP_vector<SCP_string> SexpTreeEditorInterface::getMissionGoals(const SCP_string&  /*reference_name*/) {
150 	SCP_vector<SCP_string> list;
151 
152 	for (auto i = 0; i < Num_goals; i++) {
153 		list.emplace_back(Mission_goals[i].name);
154 	}
155 
156 	return list;
157 }
getMissionEvents(const SCP_string &)158 SCP_vector<SCP_string> SexpTreeEditorInterface::getMissionEvents(const SCP_string&  /*reference_name*/) {
159 	SCP_vector<SCP_string> list;
160 
161 	for (auto i = 0; i < Num_mission_events; i++) {
162 		list.emplace_back(Mission_events[i].name);
163 	}
164 
165 	return list;
166 }
getMissionNames()167 SCP_vector<SCP_string> SexpTreeEditorInterface::getMissionNames() {
168 	return { SCP_string(Mission_filename) };
169 }
hasDefaultMissionName()170 bool SexpTreeEditorInterface::hasDefaultMissionName() {
171 	return *Mission_filename != '\0';
172 }
hasDefaultGoal(int operator_value)173 bool SexpTreeEditorInterface::hasDefaultGoal(int operator_value) {
174 	return (operator_value == OP_PREVIOUS_GOAL_TRUE) || (operator_value == OP_PREVIOUS_GOAL_FALSE)
175 		|| (operator_value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals;
176 }
hasDefaultEvent(int operator_value)177 bool SexpTreeEditorInterface::hasDefaultEvent(int operator_value) {
178 	return (operator_value == OP_PREVIOUS_EVENT_TRUE) || (operator_value == OP_PREVIOUS_EVENT_FALSE)
179 		|| (operator_value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events;
180 
181 }
getFlags() const182 const flagset<TreeFlags>& SexpTreeEditorInterface::getFlags() const {
183 	return _flags;
184 }
getRootReturnType() const185 int SexpTreeEditorInterface::getRootReturnType() const {
186 	return OPR_BOOL;
187 }
requireCampaignOperators() const188 bool SexpTreeEditorInterface::requireCampaignOperators() const {
189 	return false;
190 }
191 SexpTreeEditorInterface::~SexpTreeEditorInterface() = default;
192 
convertNodeImageToIcon(NodeImage image)193 QIcon sexp_tree::convertNodeImageToIcon(NodeImage image) {
194 	return QIcon(node_image_to_resource_name(image));
195 }
196 
197 // constructor
sexp_tree(QWidget * parent)198 sexp_tree::sexp_tree(QWidget* parent) : QTreeWidget(parent) {
199 	setSelectionMode(QTreeWidget::SingleSelection);
200 	setSelectionBehavior(QTreeWidget::SelectItems);
201 
202 	setContextMenuPolicy(Qt::CustomContextMenu);
203 
204 	setHeaderHidden(true);
205 
206 	select_sexp_node = -1;
207 	root_item = -1;
208 	clear_tree();
209 
210 	connect(this, &QWidget::customContextMenuRequested, this, &sexp_tree::customMenuHandler);
211 	connect(this, &QTreeWidget::itemChanged, this, &sexp_tree::handleItemChange);
212 	connect(this, &QTreeWidget::itemSelectionChanged, this, &sexp_tree::handleNewItemSelected);
213 }
214 
215 sexp_tree::~sexp_tree() = default;
216 
217 // clears out the tree, so all the nodes are unused.
clear_tree(const char * op)218 void sexp_tree::clear_tree(const char* op) {
219 	mprintf(("Resetting dynamic tree node limit from "
220 				SIZE_T_ARG
221 				" to %d...\n", tree_nodes.size(), 0));
222 
223 	total_nodes = flag = 0;
224 	tree_nodes.clear();
225 
226 	if (op) {
227 		clear();
228 		if (strlen(op)) {
229 			set_node(allocate_node(-1), (SEXPT_OPERATOR | SEXPT_VALID), op);
230 			build_tree();
231 		}
232 	}
233 }
234 
reset_handles()235 void sexp_tree::reset_handles() {
236 	uint i;
237 
238 	for (i = 0; i < tree_nodes.size(); i++) {
239 		tree_nodes[i].handle = NULL;
240 	}
241 }
242 
243 // initializes and creates a tree from a given sexp startpoint.
load_tree(int index,const char * deflt)244 void sexp_tree::load_tree(int index, const char* deflt) {
245 	int cur;
246 
247 	clear_tree();
248 	root_item = 0;
249 	if (index < 0) {
250 		cur = allocate_node(-1);
251 		set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), deflt);  // setup a default tree if none
252 		build_tree();
253 		return;
254 	}
255 
256 	if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {  // handle numbers allender likes to use so much..
257 		cur = allocate_node(-1);
258 		if (atoi(Sexp_nodes[index].text)) {
259 			set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");
260 		} else {
261 			set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "false");
262 		}
263 
264 		build_tree();
265 		return;
266 	}
267 
268 	// assumption: first token is an operator.  I require this because it would cause problems
269 	// with child/parent relations otherwise, and it should be this way anyway, since the
270 	// return type of the whole sexp is boolean, and only operators can satisfy this.
271 	Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
272 	load_branch(index, -1);
273 	build_tree();
274 }
275 
get_combined_variable_name(char * combined_name,const char * sexp_var_name)276 void get_combined_variable_name(char* combined_name, const char* sexp_var_name) {
277 	int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
278 	Assert(sexp_var_index > -1);
279 
280 	sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
281 }
282 
283 // creates a tree from a given Sexp_nodes[] point under a given parent.  Recursive.
284 // Returns the allocated current node.
load_branch(int index,int parent)285 int sexp_tree::load_branch(int index, int parent) {
286 	int cur = -1;
287 	char combined_var_name[2 * TOKEN_LENGTH + 2];
288 
289 	while (index != -1) {
290 		Assert(Sexp_nodes[index].type != SEXP_NOT_USED);
291 		if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
292 			load_branch(Sexp_nodes[index].first, parent);  // do the sublist and continue
293 
294 		} else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) {
295 			cur = allocate_node(parent);
296 			if ((index == select_sexp_node) && !flag) {  // translate sexp node to our node
297 				select_sexp_node = cur;
298 				flag = 1;
299 			}
300 
301 			set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text);
302 			load_branch(Sexp_nodes[index].rest, cur);  // operator is new parent now
303 			return cur;  // 'rest' was just used, so nothing left to use.
304 
305 		} else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
306 			cur = allocate_node(parent);
307 			if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
308 				get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
309 				set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name);
310 			} else {
311 				set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text);
312 			}
313 
314 		} else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
315 			cur = allocate_node(parent);
316 			if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
317 				get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
318 				set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name);
319 			} else {
320 				set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text);
321 			}
322 
323 		} else
324 			Assert(0);  // unknown and/or invalid sexp type
325 
326 		if ((index == select_sexp_node) && !flag) {  // translate sexp node to our node
327 			select_sexp_node = cur;
328 			flag = 1;
329 		}
330 
331 		index = Sexp_nodes[index].rest;
332 		if (index == -1) {
333 			return cur;
334 		}
335 	}
336 
337 	return cur;
338 }
339 
query_false(int node)340 int sexp_tree::query_false(int node) {
341 	if (node < 0) {
342 		node = root_item;
343 	}
344 
345 	Assert(node >= 0);
346 	Assert(tree_nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
347 	Assert(tree_nodes[node].next == -1);  // must make this assumption or else it will confuse code!
348 	if (get_operator_const(tree_nodes[node].text) == OP_FALSE) {
349 		return TRUE;
350 	}
351 
352 	return FALSE;
353 }
354 
355 // builds an sexp of the tree and returns the index of it.  This allocates sexp nodes.
save_tree(int node)356 int sexp_tree::save_tree(int node) {
357 	if (node < 0) {
358 		node = root_item;
359 	}
360 
361 	Assert(node >= 0);
362 	Assert(tree_nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
363 	Assert(tree_nodes[node].next == -1);  // must make this assumption or else it will confuse code!
364 	return save_branch(node);
365 }
366 
367 // get variable name from sexp_tree node .text
var_name_from_sexp_tree_text(char * var_name,const char * text)368 void var_name_from_sexp_tree_text(char* var_name, const char* text) {
369 	auto var_name_length = strcspn(text, "(");
370 	Assert(var_name_length < TOKEN_LENGTH - 1);
371 
372 	strncpy(var_name, text, var_name_length);
373 	var_name[var_name_length] = '\0';
374 }
375 
376 #define NO_PREVIOUS_NODE -9
377 // called recursively to save a tree branch and everything under it
save_branch(int cur,int at_root)378 int sexp_tree::save_branch(int cur, int at_root) {
379 	int start, node = -1, last = NO_PREVIOUS_NODE;
380 	char var_name_text[TOKEN_LENGTH];
381 
382 	start = -1;
383 	while (cur != -1) {
384 		if (tree_nodes[cur].type & SEXPT_OPERATOR) {
385 			node =
386 				alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(tree_nodes[cur].child));
387 
388 			if ((tree_nodes[cur].parent >= 0) && !at_root) {
389 				node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
390 			}
391 		} else if (tree_nodes[cur].type & SEXPT_NUMBER) {
392 			// allocate number, maybe variable
393 			if (tree_nodes[cur].type & SEXPT_VARIABLE) {
394 				var_name_from_sexp_tree_text(var_name_text, tree_nodes[cur].text);
395 				node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
396 			} else {
397 				node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
398 			}
399 		} else if (tree_nodes[cur].type & SEXPT_STRING) {
400 			// allocate string, maybe variable
401 			if (tree_nodes[cur].type & SEXPT_VARIABLE) {
402 				var_name_from_sexp_tree_text(var_name_text, tree_nodes[cur].text);
403 				node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
404 			} else {
405 				node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
406 			}
407 		} else {
408 			Assert(0); // unknown and/or invalid type
409 		}
410 
411 		if (last == NO_PREVIOUS_NODE) {
412 			start = node;
413 		} else if (last >= 0) {
414 			Sexp_nodes[last].rest = node;
415 		}
416 
417 		last = node;
418 		Assert(last != NO_PREVIOUS_NODE);  // should be impossible
419 		cur = tree_nodes[cur].next;
420 		if (at_root) {
421 			return start;
422 		}
423 	}
424 
425 	return start;
426 }
427 
428 // find the next free tree node and return its index.
find_free_node()429 int sexp_tree::find_free_node() {
430 	int i;
431 
432 	for (i = 0; i < (int) tree_nodes.size(); i++) {
433 		if (tree_nodes[i].type == SEXPT_UNUSED) {
434 			return i;
435 		}
436 	}
437 
438 	return -1;
439 }
440 
441 // allocate a node.  Remains used until freed.
allocate_node()442 int sexp_tree::allocate_node() {
443 	int node = find_free_node();
444 
445 	// need more tree nodes?
446 	if (node < 0) {
447 		int old_size = (int) tree_nodes.size();
448 
449 		Assert(TREE_NODE_INCREMENT > 0);
450 
451 		// allocate in blocks of TREE_NODE_INCREMENT
452 		tree_nodes.resize(tree_nodes.size() + TREE_NODE_INCREMENT);
453 
454 		mprintf(("Bumping dynamic tree node limit from %d to "
455 					SIZE_T_ARG
456 					"...\n", old_size, tree_nodes.size()));
457 
458 #ifndef NDEBUG
459 		for (int i = old_size; i < (int) tree_nodes.size(); i++) {
460 			sexp_tree_item* item = &tree_nodes[i];
461 			Assert(item->type == SEXPT_UNUSED);
462 		}
463 #endif
464 
465 		// our new sexp is the first out of the ones we just created
466 		node = old_size;
467 	}
468 
469 	// reset the new node
470 	tree_nodes[node].type = SEXPT_UNINIT;
471 	tree_nodes[node].parent = -1;
472 	tree_nodes[node].child = -1;
473 	tree_nodes[node].next = -1;
474 	tree_nodes[node].flags = 0;
475 	strcpy_s(tree_nodes[node].text, "<uninitialized tree node>");
476 	tree_nodes[node].handle = NULL;
477 
478 	total_nodes++;
479 	return node;
480 }
481 
482 // allocate a child node under 'parent'.  Appends to end of list.
allocate_node(int parent,int after)483 int sexp_tree::allocate_node(int parent, int after) {
484 	int i, index = allocate_node();
485 
486 	if (parent != -1) {
487 		i = tree_nodes[parent].child;
488 		if (i == -1) {
489 			tree_nodes[parent].child = index;
490 
491 		} else {
492 			while ((i != after) && (tree_nodes[i].next != -1)) {
493 				i = tree_nodes[i].next;
494 			}
495 
496 			tree_nodes[index].next = tree_nodes[i].next;
497 			tree_nodes[i].next = index;
498 		}
499 	}
500 
501 	tree_nodes[index].parent = parent;
502 	return index;
503 }
504 
505 // free a node and all its children.  Also clears pointers to it, if any.
506 //   node = node chain to free
507 //   cascade =  0: free just this node and children under it. (default)
508 //             !0: free this node and all siblings after it.
509 //
free_node(int node,int cascade)510 void sexp_tree::free_node(int node, int cascade) {
511 	int i;
512 
513 	// clear the pointer to node
514 	i = tree_nodes[node].parent;
515 	Assert(i != -1);
516 	if (tree_nodes[i].child == node) {
517 		tree_nodes[i].child = tree_nodes[node].next;
518 	} else {
519 		i = tree_nodes[i].child;
520 		while (tree_nodes[i].next != -1) {
521 			if (tree_nodes[i].next == node) {
522 				tree_nodes[i].next = tree_nodes[node].next;
523 				break;
524 			}
525 
526 			i = tree_nodes[i].next;
527 		}
528 	}
529 
530 	if (!cascade) {
531 		tree_nodes[node].next = -1;
532 	}
533 
534 	// now free up the node and its children
535 	free_node2(node);
536 }
537 
538 // more simple node freer, which works recursively.  It frees the given node and all siblings
539 // that come after it, as well as all children of these.  Doesn't clear any links to any of
540 // these freed nodes, so make sure all links are broken first. (i.e. use free_node() if you can)
541 //
free_node2(int node)542 void sexp_tree::free_node2(int node) {
543 	Assert(node != -1);
544 	Assert(tree_nodes[node].type != SEXPT_UNUSED);
545 	Assert(total_nodes > 0);
546 	modified();
547 	tree_nodes[node].type = SEXPT_UNUSED;
548 	total_nodes--;
549 	if (tree_nodes[node].child != -1) {
550 		free_node2(tree_nodes[node].child);
551 	}
552 
553 	if (tree_nodes[node].next != -1) {
554 		free_node2(tree_nodes[node].next);
555 	}
556 }
557 
558 // initialize the data for a node.  Should be called right after a new node is allocated.
set_node(int node,int type,const char * text)559 void sexp_tree::set_node(int node, int type, const char* text) {
560 	Assert(type != SEXPT_UNUSED);
561 	Assert(tree_nodes[node].type != SEXPT_UNUSED);
562 	tree_nodes[node].type = type;
563 	size_t max_length;
564 	if (type & SEXPT_VARIABLE) {
565 		max_length = 2 * TOKEN_LENGTH + 2;
566 	} else {
567 		max_length = TOKEN_LENGTH;
568 	}
569 	Assert(strlen(text) < max_length);
570 	strcpy_s(tree_nodes[node].text, text);
571 }
572 
post_load()573 void sexp_tree::post_load() {
574 	if (!flag) {
575 		select_sexp_node = -1;
576 	}
577 }
578 
579 // build or rebuild a CTreeCtrl object with the current tree data
build_tree()580 void sexp_tree::build_tree() {
581 	if (!flag) {
582 		select_sexp_node = -1;
583 	}
584 
585 	clear();
586 	add_sub_tree(0, nullptr);
587 }
588 
589 // Create the CTreeCtrl tree from the tree data.  The tree data should already be setup by
590 // this point.
add_sub_tree(int node,QTreeWidgetItem * root)591 void sexp_tree::add_sub_tree(int node, QTreeWidgetItem* root) {
592 //	char str[80];
593 	int node2;
594 
595 	Assert(node >= 0 && node < (int) tree_nodes.size());
596 	node2 = tree_nodes[node].child;
597 
598 	// check for single argument operator case (prints as one line)
599 /*	if (node2 != -1 && tree_nodes[node2].child == -1 && tree_nodes[node2].next == -1) {
600 		sprintf(str, "%s %s", tree_nodes[node].text, tree_nodes[node2].text);
601 		tree_nodes[node].handle = insert(str, root);
602 		tree_nodes[node].flags = OPERAND | EDITABLE;
603 		tree_nodes[node2].flags = COMBINED;
604 		return;
605 	}*/
606 
607 	// bitmap to draw in tree
608 	NodeImage bitmap;
609 
610 	if (tree_nodes[node].type & SEXPT_OPERATOR) {
611 		tree_nodes[node].flags = OPERAND;
612 		bitmap = NodeImage::OPERATOR;
613 	} else {
614 		if (tree_nodes[node].type & SEXPT_VARIABLE) {
615 			tree_nodes[node].handle->setFlags(tree_nodes[node].handle->flags().setFlag(Qt::ItemIsEditable, false));
616 			tree_nodes[node].flags = NOT_EDITABLE;
617 			bitmap = NodeImage::VARIABLE;
618 		} else {
619 			tree_nodes[node].handle->setFlags(tree_nodes[node].handle->flags().setFlag(Qt::ItemIsEditable, true));
620 			tree_nodes[node].flags = EDITABLE;
621 			bitmap = get_data_image(node);
622 		}
623 	}
624 
625 	root = tree_nodes[node].handle = insert(tree_nodes[node].text, bitmap, root);
626 
627 	node = node2;
628 	while (node != -1) {
629 		Assert(node >= 0 && node < (int) tree_nodes.size());
630 		Assert(tree_nodes[node].type & SEXPT_VALID);
631 		if (tree_nodes[node].type & SEXPT_OPERATOR) {
632 			add_sub_tree(node, root);
633 
634 		} else {
635 			Assert(tree_nodes[node].child == -1);
636 			if (tree_nodes[node].type & SEXPT_VARIABLE) {
637 				tree_nodes[node].handle = insert(tree_nodes[node].text, NodeImage::VARIABLE, root);
638 				tree_nodes[node].handle->setFlags(tree_nodes[node].handle->flags().setFlag(Qt::ItemIsEditable, false));
639 				tree_nodes[node].flags = NOT_EDITABLE;
640 			} else {
641 				auto bmap = get_data_image(node);
642 				tree_nodes[node].handle = insert(tree_nodes[node].text, bmap, root);
643 				tree_nodes[node].flags = EDITABLE;
644 			}
645 		}
646 
647 		node = tree_nodes[node].next;
648 	}
649 }
650 
651 // construct tree nodes for an sexp, adding them to the list and returning first node
load_sub_tree(int index,bool valid,const char * text)652 int sexp_tree::load_sub_tree(int index, bool valid, const char* text) {
653 	int cur;
654 
655 	if (index < 0) {
656 		cur = allocate_node(-1);
657 		set_node(cur, (SEXPT_OPERATOR | (valid ? SEXPT_VALID : 0)), text);  // setup a default tree if none
658 		return cur;
659 	}
660 
661 	// assumption: first token is an operator.  I require this because it would cause problems
662 	// with child/parent relations otherwise, and it should be this way anyway, since the
663 	// return type of the whole sexp is boolean, and only operators can satisfy this.
664 	Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
665 	cur = load_branch(index, -1);
666 	return cur;
667 }
668 
669 // counts the number of arguments an operator has.  Call this with the node of the first
670 // argument of the operator
count_args(int node)671 int sexp_tree::count_args(int node) {
672 	int count = 0;
673 
674 	while (node != -1) {
675 		count++;
676 		node = tree_nodes[node].next;
677 	}
678 
679 	return count;
680 }
681 
682 // identify what type of argument this is.  You call it with the node of the first argument
683 // of an operator.  It will search through enough of the arguments to determine what type of
684 // data they are.
identify_arg_type(int node)685 int sexp_tree::identify_arg_type(int node) {
686 	int type = -1;
687 
688 	while (node != -1) {
689 		Assert(tree_nodes[node].type & SEXPT_VALID);
690 		switch (SEXPT_TYPE(tree_nodes[node].type)) {
691 		case SEXPT_OPERATOR:
692 			type = get_operator_const(tree_nodes[node].text);
693 			Assert(type);
694 			return query_operator_return_type(type);
695 
696 		case SEXPT_NUMBER:
697 			return OPR_NUMBER;
698 
699 		case SEXPT_STRING:  // either a ship or a wing
700 			type = SEXP_ATOM_STRING;
701 			break;  // don't return, because maybe we can narrow selection down more.
702 		}
703 
704 		node = tree_nodes[node].next;
705 	}
706 
707 	return type;
708 }
709 
710 // given a tree node, returns the argument type it should be.
query_node_argument_type(int node)711 int sexp_tree::query_node_argument_type(int node) {
712 	int argnum = 0;
713 	int parent_node = tree_nodes[node].parent;
714 	Assert(parent_node >= 0);
715 	argnum = find_argument_number(parent_node, node);
716 	int op_num = get_operator_index(tree_nodes[parent_node].text);
717 	return query_operator_argument_type(op_num, argnum);
718 }
719 
720 //
721 // See https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
722 //
723 template<typename T>
GeneralizedLevensteinDistance(const T & source,const T & target,typename T::size_type insert_cost=1,typename T::size_type delete_cost=1,typename T::size_type replace_cost=1)724 typename T::size_type GeneralizedLevensteinDistance(const T &source,
725 	const T &target,
726 	typename T::size_type insert_cost = 1,
727 	typename T::size_type delete_cost = 1,
728 	typename T::size_type replace_cost = 1) {
729 	if (source.size() > target.size()) {
730 		return GeneralizedLevensteinDistance(target, source, delete_cost, insert_cost, replace_cost);
731 	}
732 
733 	using TSizeType = typename T::size_type;
734 	const TSizeType min_size = source.size(), max_size = target.size();
735 	std::vector<TSizeType> lev_dist(min_size + 1);
736 
737 	lev_dist[0] = 0;
738 	for (TSizeType i = 1; i <= min_size; ++i) {
739 		lev_dist[i] = lev_dist[i - 1] + delete_cost;
740 	}
741 
742 	for (TSizeType j = 1; j <= max_size; ++j) {
743 		TSizeType previous_diagonal = lev_dist[0], previous_diagonal_save;
744 		lev_dist[0] += insert_cost;
745 
746 		for (TSizeType i = 1; i <= min_size; ++i) {
747 			previous_diagonal_save = lev_dist[i];
748 			if (source[i - 1] == target[j - 1]) {
749 				lev_dist[i] = previous_diagonal;
750 			}
751 			else {
752 				lev_dist[i] = std::min(std::min(lev_dist[i - 1] + delete_cost, lev_dist[i] + insert_cost), previous_diagonal + replace_cost);
753 			}
754 			previous_diagonal = previous_diagonal_save;
755 		}
756 	}
757 
758 	return lev_dist[min_size];
759 }
760 
761 // Look for the valid operator that is the closest match for 'str' and return the operator
762 // number of it.  What operators are valid is determined by 'node', and an operator is valid
763 // if it is allowed to fit at position 'node'
764 //
match_closest_operator(const SCP_string & str,int node)765 SCP_string sexp_tree::match_closest_operator(const SCP_string &str, int node) {
766 	int z, i, op, arg_num, opf, opr;
767 	int min = -1, best = -1;
768 
769 	z = tree_nodes[node].parent;
770 	if (z < 0) {
771 		return str;
772 	}
773 
774 	op = get_operator_index(tree_nodes[z].text);
775 	if (op < 0) {
776 		return str;
777 	}
778 
779 	// determine which argument we are of the parent
780 	arg_num = find_argument_number(z, node);
781 	opf = query_operator_argument_type(op, arg_num);	// check argument type at this position
782 
783 	for (i = 0; i < (int) Operators.size(); i++) {
784 		opr = query_operator_return_type(i);			// figure out which type this operator returns
785 
786 		if (sexp_query_type_match(opf, opr)) {
787 			int dist = (int)GeneralizedLevensteinDistance(str, Operators[i].text, 2, 2, 3);
788 			if (min < 0 || dist < min) {
789 				min = dist;
790 				best = i;
791 			}
792 		}
793 	}
794 
795 	Assert(best >= 0);  // we better have some valid operator at this point.
796 	return Operators[best].text;
797 }
798 
799 // adds to or replaces (based on passed in flag) the current operator
add_or_replace_operator(int op,int replace_flag)800 void sexp_tree::add_or_replace_operator(int op, int replace_flag) {
801 	int i, op2;
802 
803 	if (replace_flag) {
804 		if (tree_nodes[item_index].flags & OPERAND) {  // are both operators?
805 			op2 = get_operator_index(tree_nodes[item_index].text);
806 			Assert(op2 >= 0);
807 			i = count_args(tree_nodes[item_index].child);
808 			if ((i >= Operators[op].min) && (i <= Operators[op].max)) {  // are old num args valid?
809 				while (i--) {
810 					if (query_operator_argument_type(op2, i)
811 						!= query_operator_argument_type(op, i)) {  // does each arg match expected type?
812 						break;
813 					}
814 				}
815 
816 				if (i < 0) {  // everything is ok, so we can keep old arguments with new operator
817 					set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text.c_str());
818 					tree_nodes[item_index].handle->setText(0, QString::fromStdString(Operators[op].text));
819 					tree_nodes[item_index].flags = OPERAND;
820 					return;
821 				}
822 			}
823 		}
824 
825 		replace_operator(Operators[op].text.c_str());
826 
827 	} else {
828 		add_operator(Operators[op].text.c_str());
829 	}
830 
831 	// fill in all the required (minimum) arguments with default values
832 	for (i = 0; i < Operators[op].min; i++) {
833 		add_default_operator(op, i);
834 	}
835 }
836 
837 // initialize node, type operator
838 //
set_op(int op_num)839 void sexp_list_item::set_op(int op_num) {
840 	int i;
841 
842 	if (op_num >= FIRST_OP) {  // do we have an op value instead of an op number (index)?
843 		for (i = 0; i < (int) Operators.size(); i++) {
844 			if (op_num == Operators[i].value) {
845 				op_num = i;
846 			}
847 		}  // convert op value to op number
848 	}
849 
850 	op = op_num;
851 	text = Operators[op].text;
852 	type = (SEXPT_OPERATOR | SEXPT_VALID);
853 }
854 
855 // initialize node, type data
856 // Defaults: t = SEXPT_STRING
857 //
set_data(const char * str,int t)858 void sexp_list_item::set_data(const char* str, int t) {
859 	op = -1;
860 	text = str;
861 	type = t;
862 }
863 
864 // initialize node, type data, allocating memory for the text
865 // Defaults: t = SEXPT_STRING
866 //
set_data_dup(const char * str,int t)867 void sexp_list_item::set_data_dup(const char* str, int t) {
868 	op = -1;
869 	text = strdup(str);
870 	flags |= SEXP_ITEM_F_DUP;
871 	type = t;
872 }
873 
874 // add a node to end of list
875 //
add_op(int op_num)876 void sexp_list_item::add_op(int op_num) {
877 	sexp_list_item* item, * ptr;
878 
879 	item = new sexp_list_item;
880 	ptr = this;
881 	while (ptr->next) {
882 		ptr = ptr->next;
883 	}
884 
885 	ptr->next = item;
886 	item->set_op(op_num);
887 }
888 
889 // add a node to end of list
890 // Defaults: t = SEXPT_STRING
891 //
add_data(const char * str,int t)892 void sexp_list_item::add_data(const char* str, int t) {
893 	sexp_list_item* item, * ptr;
894 
895 	item = new sexp_list_item;
896 	ptr = this;
897 	while (ptr->next) {
898 		ptr = ptr->next;
899 	}
900 
901 	ptr->next = item;
902 	item->set_data(str, t);
903 }
904 
905 // add a node to end of list, allocating memory for the text
906 // Defaults: t = SEXPT_STRING
907 //
add_data_dup(const char * str,int t)908 void sexp_list_item::add_data_dup(const char* str, int t) {
909 	sexp_list_item* item, * ptr;
910 
911 	item = new sexp_list_item;
912 	ptr = this;
913 	while (ptr->next) {
914 		ptr = ptr->next;
915 	}
916 
917 	ptr->next = item;
918 	item->set_data(strdup(str), t);
919 	item->flags |= SEXP_ITEM_F_DUP;
920 }
921 
922 // add an sexp list to end of another list (join lists)
923 //
add_list(sexp_list_item * list)924 void sexp_list_item::add_list(sexp_list_item* list) {
925 	sexp_list_item* ptr;
926 
927 	ptr = this;
928 	while (ptr->next) {
929 		ptr = ptr->next;
930 	}
931 
932 	ptr->next = list;
933 }
934 
935 // free all nodes of list
936 //
destroy()937 void sexp_list_item::destroy() {
938 	sexp_list_item* ptr, * ptr2;
939 
940 	ptr = this;
941 	while (ptr) {
942 		ptr2 = ptr->next;
943 
944 		delete ptr;
945 		ptr = ptr2;
946 	}
947 }
948 
add_default_operator(int op_index,int argnum)949 int sexp_tree::add_default_operator(int op_index, int argnum) {
950 	char buf[256];
951 	int index;
952 	sexp_list_item item;
953 
954 	index = item_index;
955 	if (get_default_value(&item, buf, op_index, argnum)) {
956 		return -1;
957 	}
958 
959 	if (item.type & SEXPT_OPERATOR) {
960 		Assert((item.op >= 0) && (item.op < (int) Operators.size()));
961 		add_or_replace_operator(item.op);
962 		item_index = index;
963 	} else {
964 		// special case for sexps that take variables
965 		if (query_operator_argument_type(op_index, argnum) == OPF_VARIABLE_NAME) {
966 			int sexp_var_index = get_index_sexp_variable_name(item.text);
967 			Assert(sexp_var_index != -1);
968 			int type = SEXPT_VALID | SEXPT_VARIABLE;
969 			if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
970 				type |= SEXPT_STRING;
971 			} else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
972 				type |= SEXPT_NUMBER;
973 			} else {
974 				Int3();
975 			}
976 
977 			char node_text[2 * TOKEN_LENGTH + 2];
978 			sprintf(node_text, "%s(%s)", item.text.c_str(), Sexp_variables[sexp_var_index].text);
979 			add_variable_data(node_text, type);
980 		}
981 			// modify-variable data type depends on type of variable being modified
982 			// (we know this block is handling the second argument since it's not OPF_VARIABLE_NAME)
983 		else if (Operators[op_index].value == OP_MODIFY_VARIABLE) {
984 			// the the variable name
985 			char buf2[256];
986 			Assert(argnum == 1);
987 			sexp_list_item temp_item;
988 			get_default_value(&temp_item, buf2, op_index, 0);
989 			int sexp_var_index = get_index_sexp_variable_name(temp_item.text);
990 			Assert(sexp_var_index != -1);
991 
992 			// from name get type
993 			int temp_type = Sexp_variables[sexp_var_index].type;
994 			int type = 0;
995 			if (temp_type & SEXP_VARIABLE_NUMBER) {
996 				type = SEXPT_VALID | SEXPT_NUMBER;
997 			} else if (temp_type & SEXP_VARIABLE_STRING) {
998 				type = SEXPT_VALID | SEXPT_STRING;
999 			} else {
1000 				Int3();
1001 			}
1002 			add_data(item.text.c_str(), type);
1003 		}
1004 			// all other sexps and parameters
1005 		else {
1006 			add_data(item.text.c_str(), item.type);
1007 		}
1008 	}
1009 
1010 	return 0;
1011 }
1012 
get_default_value(sexp_list_item * item,char * text_buf,int op,int i)1013 int sexp_tree::get_default_value(sexp_list_item* item, char* text_buf, int op, int i) {
1014 	const char* str = NULL;
1015 	int type, index;
1016 	sexp_list_item* list;
1017 
1018 	index = item_index;
1019 	type = query_operator_argument_type(op, i);
1020 	switch (type) {
1021 	case OPF_NULL:
1022 		item->set_op(OP_NOP);
1023 		return 0;
1024 
1025 	case OPF_BOOL:
1026 		item->set_op(OP_TRUE);
1027 		return 0;
1028 
1029 	case OPF_ANYTHING:
1030 		if (Operators[op].value == OP_INVALIDATE_ARGUMENT || Operators[op].value == OP_VALIDATE_ARGUMENT)
1031 			item->set_data(SEXP_ARGUMENT_STRING);	// this is almost always what you want for these sexps
1032 		else
1033 			item->set_data("<any data>");
1034 		return 0;
1035 
1036 	case OPF_NUMBER:
1037 	case OPF_POSITIVE:
1038 	case OPF_AMBIGUOUS:
1039 		// if the top level operators is an AI goal, and we are adding the last number required,
1040 		// assume that this number is a priority and make it 89 instead of 1.
1041 		if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1))) {
1042 			item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID));
1043 		} else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)
1044 			|| (Operators[op].value == OP_TIME_DOCKED) || (Operators[op].value == OP_TIME_UNDOCKED)) && (i == 2)) {
1045 			item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
1046 		} else if ((Operators[op].value == OP_SHIP_TYPE_DESTROYED) || (Operators[op].value == OP_GOOD_SECONDARY_TIME)) {
1047 			item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID));
1048 		} else if (Operators[op].value == OP_SET_SUPPORT_SHIP) {
1049 			item->set_data("-1", (SEXPT_NUMBER | SEXPT_VALID));
1050 		} else if (((Operators[op].value == OP_SHIP_TAG) && (i == 1))
1051 			|| ((Operators[op].value == OP_TRIGGER_SUBMODEL_ANIMATION) && (i == 3))) {
1052 			item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
1053 		} else if (Operators[op].value == OP_EXPLOSION_EFFECT) {
1054 			int temp;
1055 			char sexp_str_token[TOKEN_LENGTH];
1056 
1057 			switch (i) {
1058 			case 3:
1059 				temp = 10;
1060 				break;
1061 			case 4:
1062 				temp = 10;
1063 				break;
1064 			case 5:
1065 				temp = 100;
1066 				break;
1067 			case 6:
1068 				temp = 10;
1069 				break;
1070 			case 7:
1071 				temp = 100;
1072 				break;
1073 			case 11:
1074 				temp = (int) EMP_DEFAULT_INTENSITY;
1075 				break;
1076 			case 12:
1077 				temp = (int) EMP_DEFAULT_TIME;
1078 				break;
1079 			default:
1080 				temp = 0;
1081 				break;
1082 			}
1083 
1084 			// Goober5000 - set_data_dup is required if we're passing a variable
1085 			sprintf(sexp_str_token, "%d", temp);
1086 			item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
1087 		} else if (Operators[op].value == OP_WARP_EFFECT) {
1088 			int temp;
1089 			char sexp_str_token[TOKEN_LENGTH];
1090 
1091 			switch (i) {
1092 			case 6:
1093 				temp = 100;
1094 				break;
1095 			case 7:
1096 				temp = 10;
1097 				break;
1098 			default:
1099 				temp = 0;
1100 				break;
1101 			}
1102 
1103 			// Goober5000 - set_data_dup is required if we're passing a variable
1104 			sprintf(sexp_str_token, "%d", temp);
1105 			item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
1106 		} else if (Operators[op].value == OP_CHANGE_BACKGROUND) {
1107 			item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
1108 		} else if (Operators[op].value == OP_ADD_BACKGROUND_BITMAP) {
1109 			int temp = 0;
1110 			char sexp_str_token[TOKEN_LENGTH];
1111 
1112 			switch (i) {
1113 			case 4:
1114 			case 5:
1115 				temp = 100;
1116 				break;
1117 
1118 			case 6:
1119 			case 7:
1120 				temp = 1;
1121 				break;
1122 			}
1123 
1124 			sprintf(sexp_str_token, "%d", temp);
1125 			item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
1126 		} else if (Operators[op].value == OP_ADD_SUN_BITMAP) {
1127 			int temp = 0;
1128 			char sexp_str_token[TOKEN_LENGTH];
1129 
1130 			if (i == 4) {
1131 				temp = 100;
1132 			}
1133 
1134 			sprintf(sexp_str_token, "%d", temp);
1135 			item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
1136 		} else if (Operators[op].value == OP_MISSION_SET_NEBULA) {
1137 			if (i == 0) {
1138 				item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
1139 			} else {
1140 				item->set_data("3000", (SEXPT_NUMBER | SEXPT_VALID));
1141 			}
1142 		} else if (Operators[op].value == OP_MODIFY_VARIABLE) {
1143 			if (get_modify_variable_type(index) == OPF_NUMBER) {
1144 				item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
1145 			} else {
1146 				item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
1147 			}
1148 		} else if (Operators[op].value == OP_MODIFY_VARIABLE_XSTR) {
1149 			if (i == 1) {
1150 				item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
1151 			} else {
1152 				item->set_data("-1", (SEXPT_NUMBER | SEXPT_VALID));
1153 			}
1154 		} else if (Operators[op].value == OP_SET_VARIABLE_BY_INDEX) {
1155 			if (i == 0) {
1156 				item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
1157 			} else {
1158 				item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
1159 			}
1160 		} else if (Operators[op].value == OP_JETTISON_CARGO_NEW) {
1161 			item->set_data("25", (SEXPT_NUMBER | SEXPT_VALID));
1162 		} else if (Operators[op].value == OP_TECH_ADD_INTEL_XSTR || Operators[op].value == OP_TECH_REMOVE_INTEL_XSTR) {
1163 			item->set_data("-1", (SEXPT_NUMBER | SEXPT_VALID));
1164 		} else {
1165 			item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
1166 		}
1167 
1168 		return 0;
1169 
1170 		// Goober5000 - special cases that used to be numbers but are now hybrids
1171 		case OPF_GAME_SND:
1172 		{
1173 			gamesnd_id sound_index;
1174 
1175 			if (Operators[op].value == OP_EXPLOSION_EFFECT)
1176 			{
1177 				sound_index = GameSounds::SHIP_EXPLODE_1;
1178 			}
1179 			else if (Operators[op].value == OP_WARP_EFFECT)
1180 			{
1181 				sound_index = (i == 8) ? GameSounds::CAPITAL_WARP_IN : GameSounds::CAPITAL_WARP_OUT;
1182 			}
1183 
1184 			if (sound_index.isValid())
1185 			{
1186 				game_snd* snd = gamesnd_get_game_sound(sound_index);
1187 				if (can_construe_as_integer(snd->name.c_str()))
1188 					item->set_data(snd->name.c_str(), (SEXPT_NUMBER | SEXPT_VALID));
1189 				else
1190 					item->set_data(snd->name.c_str(), (SEXPT_STRING | SEXPT_VALID));
1191 				return 0;
1192 			}
1193 
1194 			// if no hardcoded default, just use the listing default
1195 			break;
1196 		}
1197 
1198 		// Goober5000 - ditto
1199 		case OPF_FIREBALL:
1200 		{
1201 			int fireball_index = -1;
1202 
1203 			if (Operators[op].value == OP_EXPLOSION_EFFECT)
1204 			{
1205 				fireball_index = FIREBALL_MEDIUM_EXPLOSION;
1206 			}
1207 			else if (Operators[op].value == OP_WARP_EFFECT)
1208 			{
1209 				fireball_index = FIREBALL_WARP;
1210 			}
1211 
1212 			if (fireball_index >= 0)
1213 			{
1214 				char *unique_id = Fireball_info[fireball_index].unique_id;
1215 				if (strlen(unique_id) > 0)
1216 					item->set_data(unique_id, (SEXPT_STRING | SEXPT_VALID));
1217 				else
1218 				{
1219 					char num_str[NAME_LENGTH];
1220 					sprintf(num_str, "%d", fireball_index);
1221 					item->set_data(num_str, (SEXPT_NUMBER | SEXPT_VALID));
1222 				}
1223 				return 0;
1224 			}
1225 
1226 			// if no hardcoded default, just use the listing default
1227 			break;
1228 		}
1229 
1230 		// new default value
1231 		case OPF_PRIORITY:
1232 			item->set_data("Normal", (SEXPT_STRING | SEXPT_VALID));
1233 			return 0;
1234 	}
1235 
1236 	list = get_listing_opf(type, index, i);
1237 
1238 	// Goober5000 - the way this is done is really stupid, so stupid hacks are needed to deal with it
1239 	// this particular hack is necessary because the argument string should never be a default
1240 	if (list && list->text == SEXP_ARGUMENT_STRING) {
1241 		sexp_list_item* first_ptr;
1242 
1243 		first_ptr = list;
1244 		list = list->next;
1245 
1246 		delete first_ptr;
1247 	}
1248 
1249 	if (list) {
1250 		// copy the information from the list to the passed-in item
1251 		*item = *list;
1252 
1253 		// but use the provided text buffer
1254 		strcpy(text_buf, list->text.c_str());
1255 		item->text = text_buf;
1256 
1257 		// get rid of the list, since we're done with it
1258 		list->destroy();
1259 		item->next = NULL;
1260 
1261 		return 0;
1262 	}
1263 
1264 	// catch anything that doesn't have a default value.  Just describe what should be here instead
1265 	switch (type) {
1266 	case OPF_SHIP:
1267 	case OPF_SHIP_NOT_PLAYER:
1268 	case OPF_SHIP_POINT:
1269 	case OPF_SHIP_WING:
1270 	case OPF_SHIP_WING_WHOLETEAM:
1271 	case OPF_SHIP_WING_SHIPONTEAM_POINT:
1272 	case OPF_SHIP_WING_POINT:
1273 		str = "<name of ship here>";
1274 		break;
1275 
1276 	case OPF_ORDER_RECIPIENT:
1277 		str = "<all fighters>";
1278 		break;
1279 
1280 	case OPF_SHIP_OR_NONE:
1281 	case OPF_SUBSYSTEM_OR_NONE:
1282 	case OPF_SHIP_WING_POINT_OR_NONE:
1283 		str = SEXP_NONE_STRING;
1284 		break;
1285 
1286 	case OPF_WING:
1287 		str = "<name of wing here>";
1288 		break;
1289 
1290 	case OPF_DOCKER_POINT:
1291 		str = "<docker point>";
1292 		break;
1293 
1294 	case OPF_DOCKEE_POINT:
1295 		str = "<dockee point>";
1296 		break;
1297 
1298 	case OPF_SUBSYSTEM:
1299 	case OPF_AWACS_SUBSYSTEM:
1300 	case OPF_ROTATING_SUBSYSTEM:
1301 	case OPF_SUBSYS_OR_GENERIC:
1302 		str = "<name of subsystem>";
1303 		break;
1304 
1305 	case OPF_SUBSYSTEM_TYPE:
1306 		str = Subsystem_types[SUBSYSTEM_NONE];
1307 		break;
1308 
1309 	case OPF_POINT:
1310 		str = "<waypoint>";
1311 		break;
1312 
1313 	case OPF_MESSAGE:
1314 		str = "<Message>";
1315 		break;
1316 
1317 	case OPF_WHO_FROM:
1318 		//str = "<any allied>";
1319 		str = "<any wingman>";
1320 		break;
1321 
1322 	case OPF_WAYPOINT_PATH:
1323 		str = "<waypoint path>";
1324 		break;
1325 
1326 	case OPF_MISSION_NAME:
1327 		str = "<mission name>";
1328 		break;
1329 
1330 	case OPF_GOAL_NAME:
1331 		str = "<goal name>";
1332 		break;
1333 
1334 	case OPF_SHIP_TYPE:
1335 		str = "<ship type here>";
1336 		break;
1337 
1338 	case OPF_EVENT_NAME:
1339 		str = "<event name>";
1340 		break;
1341 
1342 	case OPF_HUGE_WEAPON:
1343 		str = "<huge weapon type>";
1344 		break;
1345 
1346 	case OPF_JUMP_NODE_NAME:
1347 		str = "<Jump node name>";
1348 		break;
1349 
1350 	case OPF_NAV_POINT:
1351 		str = "<Nav 1>";
1352 		break;
1353 
1354 	case OPF_ANYTHING:
1355 		str = "<any data>";
1356 		break;
1357 
1358 	case OPF_PERSONA:
1359 		str = "<persona name>";
1360 		break;
1361 
1362 	case OPF_FONT:
1363 		str = const_cast<char*>(font::FontManager::getFont(0)->getName().c_str());
1364 		break;
1365 
1366 	case OPF_AUDIO_VOLUME_OPTION:
1367 		str = "Music";
1368 		break;
1369 
1370 	case OPF_POST_EFFECT:
1371 		str = "<Effect Name>";
1372 		break;
1373 
1374 	case OPF_CUSTOM_HUD_GAUGE:
1375 		str = "<Custom hud gauge>";
1376 		break;
1377 
1378 	default:
1379 		str = "<new default required!>";
1380 		break;
1381 	}
1382 
1383 	item->set_data(str, (SEXPT_STRING | SEXPT_VALID));
1384 	return 0;
1385 }
1386 
query_default_argument_available(int op)1387 int sexp_tree::query_default_argument_available(int op) {
1388 	int i;
1389 
1390 	Assert(op >= 0);
1391 	for (i = 0; i < Operators[op].min; i++) {
1392 		if (!query_default_argument_available(op, i)) {
1393 			return 0;
1394 		}
1395 	}
1396 
1397 	return 1;
1398 }
1399 
query_default_argument_available(int op,int i)1400 int sexp_tree::query_default_argument_available(int op, int i) {
1401 	int j, type;
1402 	object* ptr;
1403 
1404 	type = query_operator_argument_type(op, i);
1405 	switch (type) {
1406 	case OPF_NONE:
1407 	case OPF_NULL:
1408 	case OPF_BOOL:
1409 	case OPF_NUMBER:
1410 	case OPF_POSITIVE:
1411 	case OPF_IFF:
1412 	case OPF_AI_CLASS:
1413 	case OPF_WHO_FROM:
1414 	case OPF_PRIORITY:
1415 	case OPF_SHIP_TYPE:
1416 	case OPF_SUBSYSTEM:
1417 	case OPF_AWACS_SUBSYSTEM:
1418 	case OPF_ROTATING_SUBSYSTEM:
1419 	case OPF_SUBSYSTEM_TYPE:
1420 	case OPF_DOCKER_POINT:
1421 	case OPF_DOCKEE_POINT:
1422 	case OPF_AI_GOAL:
1423 	case OPF_KEYPRESS:
1424 	case OPF_AI_ORDER:
1425 	case OPF_SKILL_LEVEL:
1426 	case OPF_MEDAL_NAME:
1427 	case OPF_WEAPON_NAME:
1428 	case OPF_INTEL_NAME:
1429 	case OPF_SHIP_CLASS_NAME:
1430 	case OPF_HUGE_WEAPON:
1431 	case OPF_JUMP_NODE_NAME:
1432 	case OPF_AMBIGUOUS:
1433 	case OPF_CARGO:
1434 	case OPF_ARRIVAL_LOCATION:
1435 	case OPF_DEPARTURE_LOCATION:
1436 	case OPF_ARRIVAL_ANCHOR_ALL:
1437 	case OPF_SUPPORT_SHIP_CLASS:
1438 	case OPF_SHIP_WITH_BAY:
1439 	case OPF_SOUNDTRACK_NAME:
1440 	case OPF_STRING:
1441 	case OPF_FLEXIBLE_ARGUMENT:
1442 	case OPF_ANYTHING:
1443 	case OPF_SKYBOX_MODEL_NAME:
1444 	case OPF_SKYBOX_FLAGS:
1445 	case OPF_SHIP_OR_NONE:
1446 	case OPF_SUBSYSTEM_OR_NONE:
1447 	case OPF_SHIP_WING_POINT_OR_NONE:
1448 	case OPF_SUBSYS_OR_GENERIC:
1449 	case OPF_BACKGROUND_BITMAP:
1450 	case OPF_SUN_BITMAP:
1451 	case OPF_NEBULA_STORM_TYPE:
1452 	case OPF_NEBULA_POOF:
1453 	case OPF_TURRET_TARGET_ORDER:
1454 	case OPF_POST_EFFECT:
1455 	case OPF_TARGET_PRIORITIES:
1456 	case OPF_ARMOR_TYPE:
1457 	case OPF_DAMAGE_TYPE:
1458 	case OPF_FONT:
1459 	case OPF_HUD_ELEMENT:
1460 	case OPF_SOUND_ENVIRONMENT:
1461 	case OPF_SOUND_ENVIRONMENT_OPTION:
1462 	case OPF_EXPLOSION_OPTION:
1463 	case OPF_AUDIO_VOLUME_OPTION:
1464 	case OPF_WEAPON_BANK_NUMBER:
1465 	case OPF_MESSAGE_OR_STRING:
1466 	case OPF_BUILTIN_HUD_GAUGE:
1467 	case OPF_CUSTOM_HUD_GAUGE:
1468 	case OPF_SHIP_EFFECT:
1469 	case OPF_ANIMATION_TYPE:
1470 	case OPF_SHIP_FLAG:
1471 	case OPF_NEBULA_PATTERN:
1472 	case OPF_NAV_POINT:
1473 	case OPF_TEAM_COLOR:
1474 	case OPF_GAME_SND:
1475 	case OPF_FIREBALL:
1476 	case OPF_SPECIES:
1477 	case OPF_LANGUAGE:
1478 		return 1;
1479 
1480 	case OPF_SHIP:
1481 	case OPF_SHIP_WING:
1482 	case OPF_SHIP_POINT:
1483 	case OPF_SHIP_WING_POINT:
1484 	case OPF_SHIP_WING_WHOLETEAM:
1485 	case OPF_SHIP_WING_SHIPONTEAM_POINT:
1486 		ptr = GET_FIRST(&obj_used_list);
1487 		while (ptr != END_OF_LIST(&obj_used_list)) {
1488 			if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) {
1489 				return 1;
1490 			}
1491 
1492 			ptr = GET_NEXT(ptr);
1493 		}
1494 
1495 		return 0;
1496 
1497 	case OPF_SHIP_NOT_PLAYER:
1498 	case OPF_ORDER_RECIPIENT:
1499 		ptr = GET_FIRST(&obj_used_list);
1500 		while (ptr != END_OF_LIST(&obj_used_list)) {
1501 			if (ptr->type == OBJ_SHIP) {
1502 				return 1;
1503 			}
1504 
1505 			ptr = GET_NEXT(ptr);
1506 		}
1507 
1508 		return 0;
1509 
1510 	case OPF_WING:
1511 		for (j = 0; j < MAX_WINGS; j++) {
1512 			if (Wings[j].wave_count) {
1513 				return 1;
1514 			}
1515 		}
1516 
1517 		return 0;
1518 
1519 	case OPF_PERSONA:
1520 		return (Num_personas > 0) ? 1 : 0;
1521 
1522 	case OPF_POINT:
1523 	case OPF_WAYPOINT_PATH:
1524 		return Waypoint_lists.empty() ? 0 : 1;
1525 
1526 	case OPF_MISSION_NAME:
1527 		return _interface->hasDefaultMissionName() ? 1 : 0;
1528 
1529 		// The following code is kept for when the campaign editor is implemented
1530 		/*
1531 		if (m_mode != MODE_CAMPAIGN) {
1532 			if (!(*Mission_filename))
1533 				return 0;
1534 
1535 			return 1;
1536 		}
1537 
1538 		if (Campaign.num_missions > 0)
1539 			return 1;
1540 
1541 		return 0;
1542 		 */
1543 
1544 	case OPF_GOAL_NAME: {
1545 		return _interface->hasDefaultGoal(Operators[op].value) ? 1 : 0;
1546 
1547 		// The original code is kept until the campaign editor is implemented
1548 		/*
1549 		int value;
1550 
1551 		value = Operators[op].value;
1552 
1553 		if (m_mode == MODE_CAMPAIGN) {
1554 			return 1;
1555 
1556 			// need to be sure that previous-goal functions are available.  (i.e. we are providing a default argument for them)
1557 		} else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE)
1558 			|| (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals) {
1559 				return 1;
1560 		}
1561 
1562 		return 0;
1563 		 */
1564 	}
1565 
1566 	case OPF_EVENT_NAME: {
1567 		return _interface->hasDefaultEvent(Operators[op].value) ? 1 : 0;
1568 
1569 		// The original code is kept until the campaign editor is implemented
1570 		/*
1571 		int value;
1572 
1573 		value = Operators[op].value;
1574 		if (m_mode == MODE_CAMPAIGN) {
1575 			return 1;
1576 
1577 			// need to be sure that previous-event functions are available.  (i.e. we are providing a default argument for them)
1578 		} else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE)
1579 			|| (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events) {
1580 			return 1;
1581 		}
1582 
1583 		return 0;
1584 		 */
1585 	}
1586 
1587 	case OPF_MESSAGE:
1588 		return _interface->hasDefaultMessageParamter() ? 1 : 0;
1589 
1590 	case OPF_VARIABLE_NAME:
1591 		return (sexp_variable_count() > 0) ? 1 : 0;
1592 
1593 	case OPF_SSM_CLASS:
1594 		return Ssm_info.empty() ? 0 : 1;
1595 
1596 	case OPF_MISSION_MOOD:
1597 		return Builtin_moods.empty() ? 0 : 1;
1598 
1599 	default:
1600 		Int3();
1601 
1602 	}
1603 
1604 	return 0;
1605 }
1606 
1607 // expand a combined line (one with an operator and its one argument on the same line) into
1608 // 2 lines.
expand_operator(int node)1609 void sexp_tree::expand_operator(int node) {
1610 	if (tree_nodes[node].flags & COMBINED) {
1611 		node = tree_nodes[node].parent;
1612 		Assert((tree_nodes[node].flags & OPERAND) && (tree_nodes[node].flags & EDITABLE));
1613 	}
1614 
1615 	if ((tree_nodes[node].flags & OPERAND) && (tree_nodes[node].flags & EDITABLE)) {  // expandable?
1616 		Assert(tree_nodes[node].type & SEXPT_OPERATOR);
1617 		auto h = tree_nodes[node].handle;
1618 		auto child_data = tree_nodes[node].child;
1619 		Assert(child_data != -1 && tree_nodes[child_data].next == -1 && tree_nodes[child_data].child == -1);
1620 
1621 		h->setText(0, QString::fromUtf8(tree_nodes[node].text));
1622 		tree_nodes[node].flags = OPERAND;
1623 		auto bmap = get_data_image(child_data);
1624 		tree_nodes[child_data].handle = insert(tree_nodes[child_data].text, bmap, h);
1625 		tree_nodes[child_data].flags = EDITABLE;
1626 		h->setExpanded(true);
1627 	}
1628 }
1629 
1630 // expand a CTreeCtrl branch and all of its children
expand_branch(QTreeWidgetItem * h)1631 void sexp_tree::expand_branch(QTreeWidgetItem* h) {
1632 	h->setExpanded(true);
1633 	for (auto i = 0; i < h->childCount(); ++i) {
1634 		expand_branch(h->child(i));
1635 	}
1636 }
1637 
merge_operator(int)1638 void sexp_tree::merge_operator(int  /*node*/) {
1639 /*	char buf[256];
1640 	int child;
1641 
1642 	if (tree_nodes[node].flags == EDITABLE)  // data
1643 		node = tree_nodes[node].parent;
1644 
1645 	if (node != -1) {
1646 		child = tree_nodes[node].child;
1647 		if (child != -1 && tree_nodes[child].next == -1 && tree_nodes[child].child == -1) {
1648 			sprintf(buf, "%s %s", tree_nodes[node].text, tree_nodes[child].text);
1649 			SetItemText(tree_nodes[node].handle, buf);
1650 			tree_nodes[node].flags = OPERAND | EDITABLE;
1651 			tree_nodes[child].flags = COMBINED;
1652 			DeleteItem(tree_nodes[child].handle);
1653 			tree_nodes[child].handle = NULL;
1654 			return;
1655 		}
1656 	}*/
1657 }
1658 
1659 // add a data node under operator pointed to by item_index
add_data(const char * new_data,int type)1660 int sexp_tree::add_data(const char* new_data, int type) {
1661 	int node;
1662 
1663 	expand_operator(item_index);
1664 	node = allocate_node(item_index);
1665 	set_node(node, type, new_data);
1666 	auto bmap = get_data_image(node);
1667 	tree_nodes[node].handle = insert(new_data, bmap, tree_nodes[item_index].handle);
1668 	tree_nodes[node].flags = EDITABLE;
1669 	modified();
1670 	return node;
1671 }
1672 
1673 // add a (variable) data node under operator pointed to by item_index
add_variable_data(const char * new_data,int type)1674 int sexp_tree::add_variable_data(const char* new_data, int type) {
1675 	int node;
1676 
1677 	Assert(type & SEXPT_VARIABLE);
1678 
1679 	expand_operator(item_index);
1680 	node = allocate_node(item_index);
1681 	set_node(node, type, new_data);
1682 	tree_nodes[node].handle = insert(new_data, NodeImage::VARIABLE, tree_nodes[item_index].handle);
1683 	tree_nodes[node].handle->setFlags(tree_nodes[node].handle->flags().setFlag(Qt::ItemIsEditable, false));
1684 	tree_nodes[node].flags = NOT_EDITABLE;
1685 	modified();
1686 	return node;
1687 }
1688 
1689 // add an operator under operator pointed to by item_index.  Updates item_index to point
1690 // to this new operator.
add_operator(const char * op,QTreeWidgetItem * h)1691 int sexp_tree::add_operator(const char* op, QTreeWidgetItem* h) {
1692 	int node;
1693 
1694 	if (item_index == -1) {
1695 		node = allocate_node(-1);
1696 		set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
1697 		tree_nodes[node].handle = insert(op, NodeImage::OPERATOR, h);
1698 	} else {
1699 		expand_operator(item_index);
1700 		node = allocate_node(item_index);
1701 		set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
1702 		tree_nodes[node].handle = insert(op, NodeImage::OPERATOR, tree_nodes[item_index].handle);
1703 	}
1704 
1705 	tree_nodes[node].flags = OPERAND;
1706 	setCurrentItemIndex(node);
1707 	modified();
1708 
1709 	return node;
1710 }
1711 
1712 // add an operator with one argument under operator pointed to by item_index.  This function
1713 // exists because the one arg case is a special case.  The operator and argument is
1714 // displayed on the same line.
1715 /*void sexp_tree::add_one_arg_operator(char *op, char *data, int type)
1716 {
1717 	char str[80];
1718 	int node1, node2;
1719 
1720 	expand_operator(item_index);
1721 	node1 = allocate_node(item_index);
1722 	node2 = allocate_node(node1);
1723 	set_node(node1, SEXPT_OPERATOR, op);
1724 	set_node(node2, type, data);
1725 	sprintf(str, "%s %s", op, data);
1726 	tree_nodes[node1].handle = insert(str, tree_nodes[item_index].handle);
1727 	tree_nodes[node1].flags = OPERAND | EDITABLE;
1728 	tree_nodes[node2].flags = COMBINED;
1729 	*modified = 1;
1730 }*/
1731 
1732 /*
1733 int sexp_tree::verify_tree(int *bypass)
1734 {
1735 	return verify_tree(0, bypass);
1736 }
1737 
1738 // check the sexp tree for errors.  Return -1 if error, or 0 if no errors.  If an error
1739 // is found, item_index = node of error.
1740 int sexp_tree::verify_tree(int node, int *bypass)
1741 {
1742 	int i, type, count, op, type2, op2, argnum = 0;
1743 
1744 	if (!total_nodes)
1745 		return 0;  // nothing to check
1746 
1747 	Assert(node >= 0 && node < tree_nodes.size());
1748 	Assert(tree_nodes[node].type == SEXPT_OPERATOR);
1749 
1750 	op = get_operator_index(tree_nodes[node].text);
1751 	if (op == -1)
1752 		return node_error(node, "Unknown operator", bypass);
1753 
1754 	count = count_args(tree_nodes[node].child);
1755 	if (count < Operators[op].min)
1756 		return node_error(node, "Too few arguments for operator", bypass);
1757 	if (count > Operators[op].max)
1758 		return node_error(node, "Too many arguments for operator", bypass);
1759 
1760 	node = tree_nodes[node].child;  // get first argument
1761 	while (node != -1) {
1762 		type = query_operator_argument_type(op, argnum);
1763 		Assert(tree_nodes[node].type & SEXPT_VALID);
1764 		if (tree_nodes[node].type == SEXPT_OPERATOR) {
1765 			if (verify_tree(node) == -1)
1766 				return -1;
1767 
1768 			op2 = get_operator_index(tree_nodes[node].text);  // no error checking, because it was done in the call above.
1769 			type2 = query_operator_return_type(op2);
1770 
1771 		} else if (tree_nodes[node].type == SEXPT_NUMBER) {
1772 			char *ptr;
1773 
1774 			type2 = OPR_NUMBER;
1775 			ptr = tree_nodes[node].text;
1776 			while (*ptr)
1777 				if (!isdigit(*ptr++))
1778 					return node_error(node, "Number is invalid", bypass);
1779 
1780 		} else if (tree_nodes[node].type == SEXPT_STRING) {
1781 			type2 = SEXP_ATOM_STRING;
1782 
1783 		} else
1784 			Assert(0);  // unknown and invalid sexp node type.
1785 
1786 		switch (type) {
1787 			case OPF_NUMBER:
1788 				if (type2 != OPR_NUMBER)
1789 					return node_error(node, "Number or number return type expected here", bypass);
1790 
1791 				break;
1792 
1793 			case OPF_SHIP:
1794 				if (type2 == SEXP_ATOM_STRING)
1795 					if (ship_name_lookup(tree_nodes[node].text, 1) == -1)
1796 						type2 = 0;
1797 
1798 				if (type2 != SEXP_ATOM_STRING)
1799 					return node_error(node, "Ship name expected here", bypass);
1800 
1801 				break;
1802 
1803 			case OPF_WING:
1804 				if (type2 == SEXP_ATOM_STRING)
1805 					if (wing_name_lookup(tree_nodes[node].text) == -1)
1806 						type2 = 0;
1807 
1808 				if (type2 != SEXP_ATOM_STRING)
1809 					return node_error(node, "Wing name expected here", bypass);
1810 
1811 				break;
1812 
1813 			case OPF_SHIP_WING:
1814 				if (type2 == SEXP_ATOM_STRING)
1815 					if (ship_name_lookup(tree_nodes[node].text, 1) == -1)
1816 						if (wing_name_lookup(tree_nodes[node].text) == -1)
1817 							type2 = 0;
1818 
1819 				if (type2 != SEXP_ATOM_STRING)
1820 					return node_error(node, "Ship or wing name expected here", bypass);
1821 
1822 				break;
1823 
1824 			case OPF_BOOL:
1825 				if (type2 != OPR_BOOL)
1826 					return node_error(node, "Boolean return type expected here", bypass);
1827 
1828 				break;
1829 
1830 			case OPF_NULL:
1831 				if (type2 != OPR_NULL)
1832 					return node_error(node, "No return type operator expected here", bypass);
1833 
1834 				break;
1835 
1836 			case OPF_POINT:
1837 				if (type2 != SEXP_ATOM_STRING || verify_vector(tree_nodes[node].text))
1838 					return node_error(node, "3d coordinate expected here", bypass);
1839 
1840 				break;
1841 
1842 			case OPF_SUBSYSTEM:
1843 			case OPF_AWACS_SUBSYSTEM:
1844 			case OPF_ROTATING_SUBSYSTEM:
1845 				if (type2 == SEXP_ATOM_STRING)
1846 					if (ai_get_subsystem_type(tree_nodes[node].text) == SUBSYSTEM_UNKNOWN)
1847 						type2 = 0;
1848 
1849 				if (type2 != SEXP_ATOM_STRING)
1850 					return node_error(node, "Subsystem name expected here", bypass);
1851 
1852 				break;
1853 
1854 			case OPF_IFF:
1855 				if (type2 == SEXP_ATOM_STRING) {
1856 					for (i=0; i<Num_iffs; i++)
1857 						if (!stricmp(Team_names[i], tree_nodes[node].text))
1858 							break;
1859 				}
1860 
1861 				if (i == Num_iffs)
1862 					return node_error(node, "Iff team type expected here", bypass);
1863 
1864 				break;
1865 
1866 			case OPF_AI_GOAL:
1867 				if (type2 != OPR_AI_GOAL)
1868 					return node_error(node, "Ai goal return type expected here", bypass);
1869 
1870 				break;
1871 
1872 			case OPF_FLEXIBLE_ARGUMENT:
1873 				if (type2 != OPR_FLEXIBLE_ARGUMENT)
1874 					return node_error(node, "Flexible argument return type expected here", bypass);
1875 
1876 				break;
1877 
1878 			case OPF_ANYTHING:
1879 				break;
1880 
1881 			case OPF_DOCKER_POINT:
1882 				if (type2 != SEXP_ATOM_STRING)
1883 					return node_error(node, "Docker docking point name expected here", bypass);
1884 
1885 				break;
1886 
1887 			case OPF_DOCKEE_POINT:
1888 				if (type2 != SEXP_ATOM_STRING)
1889 					return node_error(node, "Dockee docking point name expected here", bypass);
1890 
1891 				break;
1892 		}
1893 
1894 		node = tree_nodes[node].next;
1895 		argnum++;
1896 	}
1897 
1898 	return 0;
1899 }
1900 */
1901 
1902 // display an error message and position to point of error (a node)
node_error(int node,const char * msg,int * bypass)1903 int sexp_tree::node_error(int node, const char* msg, int* bypass) {
1904 	if (bypass) {
1905 		*bypass = 1;
1906 	}
1907 
1908 	item_index = node;
1909 	auto item_handle = tree_nodes[node].handle;
1910 	if (tree_nodes[node].flags & COMBINED) {
1911 		item_handle = tree_nodes[tree_nodes[node].parent].handle;
1912 	}
1913 
1914 	ensure_visible(node);
1915 	item_handle->setSelected(true);
1916 
1917 	auto text = QString("%1\n\nContinue checking for more errors?").arg(msg);
1918 
1919 	if (QMessageBox::critical(this, "Sexp error", text, QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
1920 		return -1;
1921 	} else {
1922 		return 0;
1923 	}
1924 }
1925 
hilite_item(int node)1926 void sexp_tree::hilite_item(int node) {
1927 
1928 	ensure_visible(node);
1929 	clearSelection();
1930 	setCurrentItem(tree_nodes[node].handle);
1931 	scrollToItem(tree_nodes[node].handle);
1932 }
1933 
1934 // because the MFC function EnsureVisible() doesn't do what it says it does, I wrote this.
ensure_visible(int node)1935 void sexp_tree::ensure_visible(int node) {
1936 	auto handle = tree_nodes[node].handle->parent();
1937 
1938 	while (handle != nullptr) {
1939 		handle->setExpanded(true);
1940 		handle = handle->parent();
1941 	}
1942 }
1943 
get_variable_default_text_from_variable_text(char * text,char * default_text)1944 void get_variable_default_text_from_variable_text(char* text, char* default_text) {
1945 	char* start;
1946 
1947 	// find '('
1948 	start = strstr(text, "(");
1949 	Assert(start);
1950 	start++;
1951 
1952 	// get length and copy all but last char ")"
1953 	auto len = strlen(start);
1954 	strncpy(default_text, start, len - 1);
1955 
1956 	// add null termination
1957 	default_text[len - 1] = '\0';
1958 }
1959 
get_variable_name_from_sexp_tree_node_text(const char * text,char * var_name)1960 void get_variable_name_from_sexp_tree_node_text(const char* text, char* var_name) {
1961 	auto length = strcspn(text, "(");
1962 
1963 	strncpy(var_name, text, length);
1964 	var_name[length] = '\0';
1965 }
1966 
get_modify_variable_type(int parent)1967 int sexp_tree::get_modify_variable_type(int parent) {
1968 	int sexp_var_index = -1;
1969 
1970 	Assert(parent >= 0);
1971 	int op_const = get_operator_const(tree_nodes[parent].text);
1972 
1973 	Assert(tree_nodes[parent].child >= 0);
1974 	char* node_text = tree_nodes[tree_nodes[parent].child].text;
1975 
1976 	if (op_const == OP_MODIFY_VARIABLE) {
1977 		sexp_var_index = get_tree_name_to_sexp_variable_index(node_text);
1978 		Assert(sexp_var_index >= 0);
1979 	} else if (op_const == OP_SET_VARIABLE_BY_INDEX) {
1980 		if (can_construe_as_integer(node_text)) {
1981 			sexp_var_index = atoi(node_text);
1982 			Assert(sexp_var_index >= 0);
1983 		} else if (strchr(node_text, '(') && strchr(node_text, ')')) {
1984 			// the variable index is itself a variable!
1985 			return OPF_AMBIGUOUS;
1986 		}
1987 	} else {
1988 		Int3();  // should not be called otherwise
1989 	}
1990 
1991 	if (sexp_var_index < 0 || Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_BLOCK
1992 		|| Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NOT_USED) {
1993 		// assume number so that we can allow tree display of number operators
1994 		return OPF_NUMBER;
1995 	} else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
1996 		return OPF_NUMBER;
1997 	} else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
1998 		return OPF_AMBIGUOUS;
1999 	} else {
2000 		Int3();
2001 		return 0;
2002 	}
2003 }
2004 
2005 
verify_and_fix_arguments(int node)2006 void sexp_tree::verify_and_fix_arguments(int node) {
2007 	int op_index, arg_num, type, tmp;
2008 	static int here_count = 0;
2009 	sexp_list_item* list, * ptr;
2010 	bool is_variable_arg = false;
2011 
2012 	if (here_count) {
2013 		return;
2014 	}
2015 
2016 	here_count++;
2017 	op_index = get_operator_index(tree_nodes[node].text);
2018 	if (op_index < 0) {
2019 		return;
2020 	}
2021 
2022 	tmp = item_index;
2023 
2024 	arg_num = 0;
2025 	setCurrentItemIndex(tree_nodes[node].child);
2026 	while (item_index >= 0) {
2027 		// get listing of valid argument values for node item_index
2028 		type = query_operator_argument_type(op_index, arg_num);
2029 		// special case for modify-variable
2030 		if (type == OPF_AMBIGUOUS) {
2031 			is_variable_arg = true;
2032 			type = get_modify_variable_type(node);
2033 		}
2034 		if (query_restricted_opf_range(type)) {
2035 			list = get_listing_opf(type, node, arg_num);
2036 			if (!list && (arg_num >= Operators[op_index].min)) {
2037 				free_node(item_index, 1);
2038 				setCurrentItemIndex(tmp);
2039 				here_count--;
2040 				return;
2041 			}
2042 
2043 			if (list) {
2044 				// get a pointer to tree_nodes[item_index].text for normal value
2045 				// or default variable value if variable
2046 				char* text_ptr;
2047 				char default_variable_text[TOKEN_LENGTH];
2048 				if (tree_nodes[item_index].type & SEXPT_VARIABLE) {
2049 					// special case for SEXPs which can modify a variable
2050 					if (type == OPF_VARIABLE_NAME) {
2051 						// make text_ptr to start - before '('
2052 						get_variable_name_from_sexp_tree_node_text(tree_nodes[item_index].text, default_variable_text);
2053 						text_ptr = default_variable_text;
2054 					} else {
2055 						// only the type needs checking for variables. It's up the to the FREDder to ensure the value is valid
2056 						get_variable_name_from_sexp_tree_node_text(tree_nodes[item_index].text, default_variable_text);
2057 						int sexp_var_index = get_index_sexp_variable_name(default_variable_text);
2058 						bool types_match = false;
2059 						Assert(sexp_var_index != -1);
2060 
2061 						switch (type) {
2062 						case OPF_NUMBER:
2063 						case OPF_POSITIVE:
2064 							if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
2065 								types_match = true;
2066 							}
2067 							break;
2068 
2069 						default:
2070 							if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
2071 								types_match = true;
2072 							}
2073 						}
2074 
2075 						if (types_match) {
2076 							// on to the next argument
2077 							setCurrentItemIndex(tree_nodes[item_index].next);
2078 							arg_num++;
2079 							continue;
2080 						} else {
2081 							// shouldn't really be getting here unless someone has been hacking the mission in a text editor
2082 							get_variable_default_text_from_variable_text(tree_nodes[item_index].text,
2083 																		 default_variable_text);
2084 							text_ptr = default_variable_text;
2085 						}
2086 					}
2087 				} else {
2088 					text_ptr = tree_nodes[item_index].text;
2089 				}
2090 
2091 				ptr = list;
2092 				while (ptr) {
2093 					// make sure text is not NULL
2094 					// check that proposed text is valid for operator
2095 					if (!stricmp(ptr->text.c_str(), text_ptr)) {
2096 						break;
2097 					}
2098 
2099 					ptr = ptr->next;
2100 				}
2101 
2102 				if (!ptr) {  // argument isn't in list of valid choices,
2103 					if (list->op >= 0) {
2104 						replace_operator(list->text.c_str());
2105 					} else {
2106 						replace_data(list->text.c_str(), list->type);
2107 					}
2108 				}
2109 
2110 			} else {
2111 				bool invalid = false;
2112 				if (type == OPF_AMBIGUOUS) {
2113 					if (SEXPT_TYPE(tree_nodes[item_index].type) == SEXPT_OPERATOR) {
2114 						invalid = true;
2115 					}
2116 				} else {
2117 					if (SEXPT_TYPE(tree_nodes[item_index].type) != SEXPT_OPERATOR) {
2118 						invalid = true;
2119 					}
2120 				}
2121 
2122 				if (invalid) {
2123 					replace_data("<Invalid>", (SEXPT_STRING | SEXPT_VALID));
2124 				}
2125 			}
2126 
2127 			if (tree_nodes[item_index].type & SEXPT_OPERATOR) {
2128 				verify_and_fix_arguments(item_index);
2129 			}
2130 
2131 		}
2132 
2133 		//fix the node if it is the argument for modify-variable
2134 		if (is_variable_arg //&&
2135 			//	!(tree_nodes[item_index].type & SEXPT_OPERATOR || tree_nodes[item_index].type & SEXPT_VARIABLE )
2136 			) {
2137 			switch (type) {
2138 			case OPF_AMBIGUOUS:
2139 				tree_nodes[item_index].type |= SEXPT_STRING;
2140 				tree_nodes[item_index].type &= ~SEXPT_NUMBER;
2141 				break;
2142 
2143 			case OPF_NUMBER:
2144 				tree_nodes[item_index].type |= SEXPT_NUMBER;
2145 				tree_nodes[item_index].type &= ~SEXPT_STRING;
2146 				break;
2147 
2148 			default:
2149 				Int3();
2150 			}
2151 		}
2152 
2153 		setCurrentItemIndex(tree_nodes[item_index].next);
2154 		arg_num++;
2155 	}
2156 
2157 	setCurrentItemIndex(tmp);
2158 	here_count--;
2159 }
2160 
replace_data(const char * new_data,int type)2161 void sexp_tree::replace_data(const char* new_data, int type) {
2162 	auto node = tree_nodes[item_index].child;
2163 	if (node != -1) {
2164 		free_node2(node);
2165 	}
2166 
2167 	tree_nodes[item_index].child = -1;
2168 	auto h = tree_nodes[item_index].handle;
2169 	while (h->childCount() > 0) {
2170 		h->removeChild(h->child(0));
2171 	}
2172 
2173 	set_node(item_index, type, new_data);
2174 	h->setText(0, new_data);
2175 	auto bmap = get_data_image(item_index);
2176 	h->setIcon(0, convertNodeImageToIcon(bmap));
2177 	h->setFlags(h->flags().setFlag(Qt::ItemIsEditable, true));
2178 	tree_nodes[item_index].flags = EDITABLE;
2179 
2180 	// check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
2181 	verify_and_fix_arguments(tree_nodes[item_index].parent);
2182 
2183 	modified();
2184 	update_help(currentItem());
2185 }
2186 
2187 
2188 // Replaces data with sexp_variable type data
replace_variable_data(int var_idx,int type)2189 void sexp_tree::replace_variable_data(int var_idx, int type) {
2190 	char buf[128];
2191 
2192 	Assert(type & SEXPT_VARIABLE);
2193 
2194 	auto node = tree_nodes[item_index].child;
2195 	if (node != -1) {
2196 		free_node2(node);
2197 	}
2198 
2199 	tree_nodes[item_index].child = -1;
2200 	auto h = tree_nodes[item_index].handle;
2201 	while (h->childCount() > 0) {
2202 		h->removeChild(h->child(0));
2203 	}
2204 
2205 	// Assemble name
2206 	sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
2207 
2208 	set_node(item_index, type, buf);
2209 	h->setText(0, QString::fromUtf8(buf));
2210 	h->setIcon(0, convertNodeImageToIcon(NodeImage::VARIABLE));
2211 	h->setFlags(h->flags().setFlag(Qt::ItemIsEditable, false));
2212 	tree_nodes[item_index].flags = NOT_EDITABLE;
2213 
2214 	// check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
2215 	verify_and_fix_arguments(tree_nodes[item_index].parent);
2216 
2217 	modified();
2218 	update_help(currentItem());
2219 }
2220 
2221 
replace_operator(const char * op)2222 void sexp_tree::replace_operator(const char* op) {
2223 	auto node = tree_nodes[item_index].child;
2224 	if (node != -1) {
2225 		free_node2(node);
2226 	}
2227 
2228 	tree_nodes[item_index].child = -1;
2229 	auto h = tree_nodes[item_index].handle;
2230 	while (h->childCount() > 0) {
2231 		h->removeChild(h->child(0));
2232 	}
2233 
2234 	set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), op);
2235 	h->setText(0, op);
2236 	tree_nodes[item_index].flags = OPERAND;
2237 	modified();
2238 	update_help(currentItem());
2239 
2240 	// hack added at Allender's request.  If changing ship in an ai-dock operator, re-default
2241 	// docking point.
2242 }
2243 
2244 /*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type)
2245 {
2246 	char str[80];
2247 	int node;
2248 	HTREEITEM h;
2249 
2250 	node = tree_nodes[item_index].child;
2251 	if (node != -1)
2252 		free_node2(node);
2253 
2254 	tree_nodes[item_index].child = -1;
2255 	h = tree_nodes[item_index].handle;
2256 	while (ItemHasChildren(h))
2257 		DeleteItem(GetChildItem(h));
2258 
2259 	node = allocate_node(item_index);
2260 	set_node(item_index, SEXPT_OPERATOR, op);
2261 	set_node(node, type, data);
2262 	sprintf(str, "%s %s", op, data);
2263 	SetItemText(h, str);
2264 	tree_nodes[item_index].flags = OPERAND | EDITABLE;
2265 	tree_nodes[node].flags = COMBINED;
2266 	*modified = 1;
2267 	update_help(GetSelectedItem());
2268 }*/
2269 
2270 // moves a whole sexp tree branch to a new position under 'parent' and after 'after'.
2271 // The expansion state is preserved, and node handles are updated.
move_branch(int source,int parent)2272 void sexp_tree::move_branch(int source, int parent) {
2273 	int node;
2274 
2275 	// if no source, skip everything
2276 	if (source != -1) {
2277 		node = tree_nodes[source].parent;
2278 		if (node != -1) {
2279 			if (tree_nodes[node].child == source) {
2280 				tree_nodes[node].child = tree_nodes[source].next;
2281 			} else {
2282 				node = tree_nodes[node].child;
2283 				while (tree_nodes[node].next != source) {
2284 					node = tree_nodes[node].next;
2285 					Assert(node != -1);
2286 				}
2287 
2288 				tree_nodes[node].next = tree_nodes[source].next;
2289 			}
2290 		}
2291 
2292 		tree_nodes[source].parent = parent;
2293 		tree_nodes[source].next = -1;
2294 		if (parent) {
2295 			if (tree_nodes[parent].child == -1) {
2296 				tree_nodes[parent].child = source;
2297 			} else {
2298 				node = tree_nodes[parent].child;
2299 				while (tree_nodes[node].next != -1) {
2300 					node = tree_nodes[node].next;
2301 				}
2302 
2303 				tree_nodes[node].next = source;
2304 			}
2305 
2306 			move_branch(tree_nodes[source].handle, tree_nodes[parent].handle);
2307 
2308 		} else {
2309 			move_branch(tree_nodes[source].handle);
2310 		}
2311 	}
2312 }
2313 
move_branch(QTreeWidgetItem * source,QTreeWidgetItem * parent,QTreeWidgetItem * after)2314 QTreeWidgetItem* sexp_tree::move_branch(QTreeWidgetItem* source, QTreeWidgetItem* parent, QTreeWidgetItem* after) {
2315 	QTreeWidgetItem* h = nullptr;
2316 	if (source) {
2317 		uint i;
2318 
2319 		for (i = 0; i < tree_nodes.size(); i++) {
2320 			if (tree_nodes[i].handle == source) {
2321 				break;
2322 			}
2323 		}
2324 
2325 		if (i < tree_nodes.size()) {
2326 			auto icon = source->icon(0);
2327 			h = insertWithIcon(source->text(0), icon, parent, after);
2328 			tree_nodes[i].handle = h;
2329 		} else {
2330 			auto icon = source->icon(0);
2331 			h = insertWithIcon(source->text(0), icon, parent, after);
2332 		}
2333 
2334 		h->setData(0, FormulaDataRole, source->data(0, FormulaDataRole));
2335 		for (auto childIdx = 0; childIdx < source->childCount(); ++i) {
2336 			auto child = source->child(childIdx);
2337 
2338 			move_branch(child, h);
2339 		}
2340 
2341 		h->setExpanded(source->isExpanded());
2342 
2343 		source->parent()->removeChild(source);
2344 	}
2345 
2346 	return h;
2347 }
2348 
copy_branch(QTreeWidgetItem * source,QTreeWidgetItem * parent,QTreeWidgetItem * after)2349 void sexp_tree::copy_branch(QTreeWidgetItem* source, QTreeWidgetItem* parent, QTreeWidgetItem* after) {
2350 	QTreeWidgetItem* h = nullptr;
2351 	if (source) {
2352 		uint i;
2353 
2354 		for (i = 0; i < tree_nodes.size(); i++) {
2355 			if (tree_nodes[i].handle == source) {
2356 				break;
2357 			}
2358 		}
2359 
2360 		if (i < tree_nodes.size()) {
2361 			auto icon = source->icon(0);
2362 			h = insertWithIcon(source->text(0), icon, parent, after);
2363 			tree_nodes[i].handle = h;
2364 		} else {
2365 			auto icon = source->icon(0);
2366 			h = insertWithIcon(source->text(0), icon, parent, after);
2367 		}
2368 
2369 		h->setData(0, FormulaDataRole, source->data(0, FormulaDataRole));
2370 		for (auto childIdx = 0; childIdx < source->childCount(); ++i) {
2371 			auto child = source->child(childIdx);
2372 
2373 			move_branch(child, h);
2374 		}
2375 
2376 		h->setExpanded(source->isExpanded());
2377 	}
2378 }
2379 
swap_roots(QTreeWidgetItem * one,QTreeWidgetItem * two)2380 void sexp_tree::swap_roots(QTreeWidgetItem* one, QTreeWidgetItem* two) {
2381 //	copy_branch(one, TVI_ROOT, two);
2382 //	move_branch(two, TVI_ROOT, one);
2383 //	DeleteItem(one);
2384 	auto h = move_branch(one, itemFromIndex(rootIndex()), two);
2385 	setCurrentItem(h);
2386 	modified();
2387 }
2388 
2389 QTreeWidgetItem*
insert(const QString & lpszItem,NodeImage image,QTreeWidgetItem * hParent,QTreeWidgetItem * hInsertAfter)2390 sexp_tree::insert(const QString& lpszItem, NodeImage image, QTreeWidgetItem* hParent, QTreeWidgetItem* hInsertAfter) {
2391 	return insertWithIcon(lpszItem, convertNodeImageToIcon(image), hParent, hInsertAfter);
2392 }
2393 
insertWithIcon(const QString & lpszItem,const QIcon & image,QTreeWidgetItem * hParent,QTreeWidgetItem * hInsertAfter)2394 QTreeWidgetItem* sexp_tree::insertWithIcon(const QString& lpszItem,
2395 										   const QIcon& image,
2396 										   QTreeWidgetItem* hParent,
2397 										   QTreeWidgetItem* hInsertAfter) {
2398 	util::SignalBlockers blockers(this);
2399 
2400 	QTreeWidgetItem* item = nullptr;
2401 	if (hParent == nullptr) {
2402 		if (hInsertAfter == nullptr) {
2403 			item = new QTreeWidgetItem(this);
2404 		} else {
2405 			item = new QTreeWidgetItem(this, hInsertAfter);
2406 		}
2407 	} else {
2408 		if (hInsertAfter == nullptr) {
2409 			item = new QTreeWidgetItem(hParent);
2410 		} else {
2411 			item = new QTreeWidgetItem(hParent, hInsertAfter);
2412 		}
2413 	}
2414 	item->setText(0, lpszItem);
2415 	item->setIcon(0, image);
2416 	item->setFlags(item->flags() | Qt::ItemIsEditable);
2417 
2418 	return item;
2419 }
2420 
handle(int node)2421 QTreeWidgetItem* sexp_tree::handle(int node) {
2422 	return tree_nodes[node].handle;
2423 }
2424 
help(int code)2425 const char* sexp_tree::help(int code) {
2426 	int i;
2427 
2428 	i = (int) Sexp_help.size();
2429 	while (i--) {
2430 		if (Sexp_help[i].id == code) {
2431 			break;
2432 		}
2433 	}
2434 
2435 	if (i >= 0) {
2436 		return Sexp_help[i].help.c_str();
2437 	}
2438 
2439 	return NULL;
2440 }
2441 
2442 // get type of item clicked on
get_type(QTreeWidgetItem * h)2443 int sexp_tree::get_type(QTreeWidgetItem* h) {
2444 	uint i;
2445 
2446 	// get index into sexp_tree
2447 	for (i = 0; i < tree_nodes.size(); i++) {
2448 		if (tree_nodes[i].handle == h) {
2449 			break;
2450 		}
2451 	}
2452 
2453 	if ((i >= tree_nodes.size())) {
2454 		// Int3();	// This would be the root of the tree  -- ie, event name
2455 		return -1;
2456 	}
2457 
2458 	return tree_nodes[i].type;
2459 }
2460 
2461 // get node of item clicked on
get_node(QTreeWidgetItem * h)2462 int sexp_tree::get_node(QTreeWidgetItem* h) {
2463 	uint i;
2464 
2465 	// get index into sexp_tree
2466 	for (i = 0; i < tree_nodes.size(); i++) {
2467 		if (tree_nodes[i].handle == h) {
2468 			break;
2469 		}
2470 	}
2471 
2472 	if ((i >= tree_nodes.size())) {
2473 		// Int3();	// This would be the root of the tree  -- ie, event name
2474 		return -1;
2475 	}
2476 
2477 	return i;
2478 }
2479 
update_help(QTreeWidgetItem * h)2480 void sexp_tree::update_help(QTreeWidgetItem* h) {
2481 	if (h == nullptr) {
2482 		helpChanged("");
2483 		miniHelpChanged("");
2484 	}
2485 
2486 	int i, j, z, c, code, index, sibling_place;
2487 
2488 	for (i = 0; i < (int) Operators.size(); i++) {
2489 		for (j = 0; j < (int) op_menu.size(); j++) {
2490 			if (get_category(Operators[i].value) == op_menu[j].id) {
2491 				if (!help(Operators[i].value)) {
2492 					mprintf(("Allender!  If you add new sexp operators, add help for them too! :)\n"));
2493 				}
2494 			}
2495 		}
2496 	}
2497 
2498 	for (i = 0; i < (int) tree_nodes.size(); i++) {
2499 		if (tree_nodes[i].handle == h) {
2500 			break;
2501 		}
2502 	}
2503 
2504 	if ((i >= (int) tree_nodes.size()) || !tree_nodes[i].type) {
2505 		helpChanged("");
2506 		miniHelpChanged("");
2507 		return;
2508 	}
2509 
2510 	if (SEXPT_TYPE(tree_nodes[i].type) == SEXPT_OPERATOR) {
2511 		miniHelpChanged("");
2512 	} else {
2513 		z = tree_nodes[i].parent;
2514 		if (z < 0) {
2515 			Warning(LOCATION, "Sexp data \"%s\" has no parent!", tree_nodes[i].text);
2516 			return;
2517 		}
2518 
2519 		code = get_operator_const(tree_nodes[z].text);
2520 		index = get_operator_index(tree_nodes[z].text);
2521 		sibling_place = get_sibling_place(i) + 1;    //We want it to start at 1
2522 
2523 		//*****Minihelp box
2524 		if ((SEXPT_TYPE(tree_nodes[i].type) == SEXPT_NUMBER)
2525 			|| ((SEXPT_TYPE(tree_nodes[i].type) == SEXPT_STRING) && sibling_place > 0)) {
2526 			char buffer[10240] = { "" };
2527 
2528 			//Get the help for the current operator
2529 			const char* helpstr = help(code);
2530 			bool display_number = true;
2531 
2532 			//If a help string exists, try to display it
2533 			if (helpstr != NULL) {
2534 				char searchstr[32];
2535 				const char* loc = NULL, * loc2 = NULL;
2536 
2537 				if (loc == NULL) {
2538 					sprintf(searchstr, "\n%d:", sibling_place);
2539 					loc = strstr(helpstr, searchstr);
2540 				}
2541 
2542 				if (loc == NULL) {
2543 					sprintf(searchstr, "\t%d:", sibling_place);
2544 					loc = strstr(helpstr, searchstr);
2545 				}
2546 				if (loc == NULL) {
2547 					sprintf(searchstr, " %d:", sibling_place);
2548 					loc = strstr(helpstr, searchstr);
2549 				}
2550 				if (loc == NULL) {
2551 					sprintf(searchstr, "%d:", sibling_place);
2552 					loc = strstr(helpstr, searchstr);
2553 				}
2554 				if (loc == NULL) {
2555 					loc = strstr(helpstr, "Rest:");
2556 				}
2557 				if (loc == NULL) {
2558 					loc = strstr(helpstr, "All:");
2559 				}
2560 
2561 				if (loc != NULL) {
2562 					//Skip whitespace
2563 					while (*loc == '\r' || *loc == '\n' || *loc == ' ' || *loc == '\t') {
2564 						loc++;
2565 					}
2566 
2567 					//Find EOL
2568 					loc2 = strpbrk(loc, "\r\n");
2569 					if (loc2 != NULL) {
2570 						size_t size = loc2 - loc;
2571 						strncpy(buffer, loc, size);
2572 						if (size < sizeof(buffer)) {
2573 							buffer[size] = '\0';
2574 						}
2575 						display_number = false;
2576 					} else {
2577 						strcpy_s(buffer, loc);
2578 						display_number = false;
2579 					}
2580 				}
2581 			}
2582 
2583 			//Display argument number
2584 			if (display_number) {
2585 				sprintf(buffer, "%d:", sibling_place);
2586 			}
2587 
2588 			miniHelpChanged(QString::fromUtf8(buffer));
2589 		}
2590 
2591 		if (index >= 0) {
2592 			c = 0;
2593 			j = tree_nodes[z].child;
2594 			while ((j >= 0) && (j != i)) {
2595 				j = tree_nodes[j].next;
2596 				c++;
2597 			}
2598 
2599 			Assert(j >= 0);
2600 			if (query_operator_argument_type(index, c) == OPF_MESSAGE) {
2601 				for (j = 0; j < Num_messages; j++) {
2602 					if (!stricmp(Messages[j].name, tree_nodes[i].text)) {
2603 						auto text = QString("Message Text:\n%1").arg(Messages[j].message);
2604 						helpChanged(text);
2605 						return;
2606 					}
2607 				}
2608 			}
2609 		}
2610 
2611 		i = z;
2612 	}
2613 
2614 	code = get_operator_const(tree_nodes[i].text);
2615 	auto str = help(code);
2616 	if (!str) {
2617 		str = "No help available";
2618 	}
2619 
2620 	helpChanged(QString::fromUtf8(str));
2621 }
2622 
2623 // find list of sexp_tree nodes with text
2624 // stuff node indices into find[]
find_text(const char * text,int * find,int max_depth)2625 int sexp_tree::find_text(const char* text, int* find, int max_depth) {
2626 	int find_count;
2627 
2628 	// initialize find
2629 	for (int i = 0; i < max_depth; i++) {
2630 		find[i] = -1;
2631 	}
2632 
2633 	find_count = 0;
2634 
2635 	for (size_t i = 0; i < tree_nodes.size(); i++) {
2636 		// only look at used and editable nodes
2637 		if ((tree_nodes[i].flags & EDITABLE && (tree_nodes[i].type != SEXPT_UNUSED))) {
2638 			// find the text
2639 			if (!stricmp(tree_nodes[i].text, text)) {
2640 				find[find_count++] = static_cast<int>(i);
2641 
2642 				// don't exceed max count - array bounds
2643 				if (find_count == max_depth) {
2644 					break;
2645 				}
2646 			}
2647 		}
2648 	}
2649 
2650 	return find_count;
2651 }
2652 
2653 
2654 // Determine if a given opf code has a restricted argument range (i.e. has a specific, limited
2655 // set of argument values, or has virtually unlimited possibilities.  For example, boolean values
2656 // only have true or false, so it is restricted, but a number could be anything, so it's not.
2657 //
query_restricted_opf_range(int opf)2658 int sexp_tree::query_restricted_opf_range(int opf) {
2659 	switch (opf) {
2660 	case OPF_NUMBER:
2661 	case OPF_POSITIVE:
2662 	case OPF_WHO_FROM:
2663 
2664 		// Goober5000 - these are needed too (otherwise the arguments revert to their defaults)
2665 	case OPF_STRING:
2666 	case OPF_ANYTHING:
2667 		return 0;
2668 	}
2669 
2670 	return 1;
2671 }
2672 
2673 // generate listing of valid argument values.
2674 // opf = operator format to generate list for
2675 // parent_node = the parent node we are generating list for
2676 // arg_index = argument number of parent this argument will go at
2677 //
2678 // Goober5000 - add the listing from get_listing_opf_sub to the end of a new list containing
2679 // the special argument item, but only if it's a child of a when-argument (or similar) sexp.
2680 // Also only do this if the list has at least one item, because otherwise the argument code
2681 // would have nothing to select from.
get_listing_opf(int opf,int parent_node,int arg_index)2682 sexp_list_item* sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index) {
2683 	sexp_list_item head;
2684 	sexp_list_item* list = NULL;
2685 	int w_arg, e_arg;
2686 
2687 	switch (opf) {
2688 	case OPF_NONE:
2689 		list = NULL;
2690 		break;
2691 
2692 	case OPF_NULL:
2693 		list = get_listing_opf_null();
2694 		break;
2695 
2696 	case OPF_BOOL:
2697 		list = get_listing_opf_bool(parent_node);
2698 		break;
2699 
2700 	case OPF_NUMBER:
2701 		list = get_listing_opf_number();
2702 		break;
2703 
2704 	case OPF_SHIP:
2705 		list = get_listing_opf_ship(parent_node);
2706 		break;
2707 
2708 	case OPF_WING:
2709 		list = get_listing_opf_wing();
2710 		break;
2711 
2712 	case OPF_AWACS_SUBSYSTEM:
2713 	case OPF_ROTATING_SUBSYSTEM:
2714 	case OPF_SUBSYSTEM:
2715 		list = get_listing_opf_subsystem(parent_node, arg_index);
2716 		break;
2717 
2718 	case OPF_SUBSYSTEM_TYPE:
2719 		list = get_listing_opf_subsystem_type(parent_node);
2720 		break;
2721 
2722 	case OPF_POINT:
2723 		list = get_listing_opf_point();
2724 		break;
2725 
2726 	case OPF_IFF:
2727 		list = get_listing_opf_iff();
2728 		break;
2729 
2730 	case OPF_AI_CLASS:
2731 		list = get_listing_opf_ai_class();
2732 		break;
2733 
2734 	case OPF_SUPPORT_SHIP_CLASS:
2735 		list = get_listing_opf_support_ship_class();
2736 		break;
2737 
2738 	case OPF_SSM_CLASS:
2739 		list = get_listing_opf_ssm_class();
2740 		break;
2741 
2742 	case OPF_ARRIVAL_LOCATION:
2743 		list = get_listing_opf_arrival_location();
2744 		break;
2745 
2746 	case OPF_DEPARTURE_LOCATION:
2747 		list = get_listing_opf_departure_location();
2748 		break;
2749 
2750 	case OPF_ARRIVAL_ANCHOR_ALL:
2751 		list = get_listing_opf_arrival_anchor_all();
2752 		break;
2753 
2754 	case OPF_SHIP_WITH_BAY:
2755 		list = get_listing_opf_ship_with_bay();
2756 		break;
2757 
2758 	case OPF_SOUNDTRACK_NAME:
2759 		list = get_listing_opf_soundtrack_name();
2760 		break;
2761 
2762 	case OPF_AI_GOAL:
2763 		list = get_listing_opf_ai_goal(parent_node);
2764 		break;
2765 
2766 	case OPF_FLEXIBLE_ARGUMENT:
2767 		list = get_listing_opf_flexible_argument();
2768 		break;
2769 
2770 	case OPF_DOCKER_POINT:
2771 		list = get_listing_opf_docker_point(parent_node);
2772 		break;
2773 
2774 	case OPF_DOCKEE_POINT:
2775 		list = get_listing_opf_dockee_point(parent_node);
2776 		break;
2777 
2778 	case OPF_MESSAGE:
2779 		list = get_listing_opf_message();
2780 		break;
2781 
2782 	case OPF_WHO_FROM:
2783 		list = get_listing_opf_who_from();
2784 		break;
2785 
2786 	case OPF_PRIORITY:
2787 		list = get_listing_opf_priority();
2788 		break;
2789 
2790 	case OPF_WAYPOINT_PATH:
2791 		list = get_listing_opf_waypoint_path();
2792 		break;
2793 
2794 	case OPF_POSITIVE:
2795 		list = get_listing_opf_positive();
2796 		break;
2797 
2798 	case OPF_MISSION_NAME:
2799 		list = get_listing_opf_mission_name();
2800 		break;
2801 
2802 	case OPF_SHIP_POINT:
2803 		list = get_listing_opf_ship_point();
2804 		break;
2805 
2806 	case OPF_GOAL_NAME:
2807 		list = get_listing_opf_goal_name(parent_node);
2808 		break;
2809 
2810 	case OPF_SHIP_WING:
2811 		list = get_listing_opf_ship_wing();
2812 		break;
2813 
2814 	case OPF_SHIP_WING_WHOLETEAM:
2815 		list = get_listing_opf_ship_wing_wholeteam();
2816 		break;
2817 
2818 	case OPF_SHIP_WING_SHIPONTEAM_POINT:
2819 		list = get_listing_opf_ship_wing_shiponteam_point();
2820 		break;
2821 
2822 	case OPF_SHIP_WING_POINT:
2823 		list = get_listing_opf_ship_wing_point();
2824 		break;
2825 
2826 	case OPF_SHIP_WING_POINT_OR_NONE:
2827 		list = get_listing_opf_ship_wing_point_or_none();
2828 		break;
2829 
2830 	case OPF_ORDER_RECIPIENT:
2831 		list = get_listing_opf_order_recipient();
2832 		break;
2833 
2834 	case OPF_SHIP_TYPE:
2835 		list = get_listing_opf_ship_type();
2836 		break;
2837 
2838 	case OPF_KEYPRESS:
2839 		list = get_listing_opf_keypress();
2840 		break;
2841 
2842 	case OPF_EVENT_NAME:
2843 		list = get_listing_opf_event_name(parent_node);
2844 		break;
2845 
2846 	case OPF_AI_ORDER:
2847 		list = get_listing_opf_ai_order();
2848 		break;
2849 
2850 	case OPF_SKILL_LEVEL:
2851 		list = get_listing_opf_skill_level();
2852 		break;
2853 
2854 	case OPF_CARGO:
2855 		list = get_listing_opf_cargo();
2856 		break;
2857 
2858 	case OPF_STRING:
2859 		list = get_listing_opf_string();
2860 		break;
2861 
2862 	case OPF_MEDAL_NAME:
2863 		list = get_listing_opf_medal_name();
2864 		break;
2865 
2866 	case OPF_WEAPON_NAME:
2867 		list = get_listing_opf_weapon_name();
2868 		break;
2869 
2870 	case OPF_INTEL_NAME:
2871 		list = get_listing_opf_intel_name();
2872 		break;
2873 
2874 	case OPF_SHIP_CLASS_NAME:
2875 		list = get_listing_opf_ship_class_name();
2876 		break;
2877 
2878 	case OPF_HUGE_WEAPON:
2879 		list = get_listing_opf_huge_weapon();
2880 		break;
2881 
2882 	case OPF_SHIP_NOT_PLAYER:
2883 		list = get_listing_opf_ship_not_player();
2884 		break;
2885 
2886 	case OPF_SHIP_OR_NONE:
2887 		list = get_listing_opf_ship_or_none();
2888 		break;
2889 
2890 	case OPF_SUBSYSTEM_OR_NONE:
2891 		list = get_listing_opf_subsystem_or_none(parent_node, arg_index);
2892 		break;
2893 
2894 	case OPF_SUBSYS_OR_GENERIC:
2895 		list = get_listing_opf_subsys_or_generic(parent_node, arg_index);
2896 		break;
2897 
2898 	case OPF_JUMP_NODE_NAME:
2899 		list = get_listing_opf_jump_nodes();
2900 		break;
2901 
2902 	case OPF_VARIABLE_NAME:
2903 		list = get_listing_opf_variable_names();
2904 		break;
2905 
2906 	case OPF_AMBIGUOUS:
2907 		list = NULL;
2908 		break;
2909 
2910 	case OPF_ANYTHING:
2911 		list = NULL;
2912 		break;
2913 
2914 	case OPF_SKYBOX_MODEL_NAME:
2915 		list = get_listing_opf_skybox_model();
2916 		break;
2917 
2918 	case OPF_SKYBOX_FLAGS:
2919 		list = get_listing_opf_skybox_flags();
2920 		break;
2921 
2922 	case OPF_BACKGROUND_BITMAP:
2923 		list = get_listing_opf_background_bitmap();
2924 		break;
2925 
2926 	case OPF_SUN_BITMAP:
2927 		list = get_listing_opf_sun_bitmap();
2928 		break;
2929 
2930 	case OPF_NEBULA_STORM_TYPE:
2931 		list = get_listing_opf_nebula_storm_type();
2932 		break;
2933 
2934 	case OPF_NEBULA_POOF:
2935 		list = get_listing_opf_nebula_poof();
2936 		break;
2937 
2938 	case OPF_TURRET_TARGET_ORDER:
2939 		list = get_listing_opf_turret_target_order();
2940 		break;
2941 
2942 	case OPF_TARGET_PRIORITIES:
2943 		list = get_listing_opf_turret_target_priorities();
2944 		break;
2945 
2946 	case OPF_ARMOR_TYPE:
2947 		list = get_listing_opf_armor_type();
2948 		break;
2949 
2950 	case OPF_DAMAGE_TYPE:
2951 		list = get_listing_opf_damage_type();
2952 		break;
2953 
2954 	case OPF_ANIMATION_TYPE:
2955 		list = get_listing_opf_animation_type();
2956 		break;
2957 
2958 	case OPF_PERSONA:
2959 		list = get_listing_opf_persona();
2960 		break;
2961 
2962 	case OPF_POST_EFFECT:
2963 		list = get_listing_opf_post_effect();
2964 		break;
2965 
2966 	case OPF_FONT:
2967 		list = get_listing_opf_font();
2968 		break;
2969 
2970 	case OPF_HUD_ELEMENT:
2971 		list = get_listing_opf_hud_elements();
2972 		break;
2973 
2974 	case OPF_SOUND_ENVIRONMENT:
2975 		list = get_listing_opf_sound_environment();
2976 		break;
2977 
2978 	case OPF_SOUND_ENVIRONMENT_OPTION:
2979 		list = get_listing_opf_sound_environment_option();
2980 		break;
2981 
2982 	case OPF_AUDIO_VOLUME_OPTION:
2983 		list = get_listing_opf_adjust_audio_volume();
2984 		break;
2985 
2986 	case OPF_EXPLOSION_OPTION:
2987 		list = get_listing_opf_explosion_option();
2988 		break;
2989 
2990 	case OPF_WEAPON_BANK_NUMBER:
2991 		list = get_listing_opf_weapon_banks();
2992 		break;
2993 
2994 	case OPF_MESSAGE_OR_STRING:
2995 		list = get_listing_opf_message();
2996 		break;
2997 
2998 	case OPF_BUILTIN_HUD_GAUGE:
2999 		list = get_listing_opf_builtin_hud_gauge();
3000 		break;
3001 
3002 	case OPF_CUSTOM_HUD_GAUGE:
3003 		list = get_listing_opf_custom_hud_gauge();
3004 		break;
3005 
3006 	case OPF_SHIP_EFFECT:
3007 		list = get_listing_opf_ship_effect();
3008 		break;
3009 
3010 	case OPF_MISSION_MOOD:
3011 		list = get_listing_opf_mission_moods();
3012 		break;
3013 
3014 	case OPF_SHIP_FLAG:
3015 		list = get_listing_opf_ship_flags();
3016 		break;
3017 
3018 	case OPF_TEAM_COLOR:
3019 		list = get_listing_opf_team_colors();
3020 		break;
3021 
3022 	case OPF_NEBULA_PATTERN:
3023 		list = get_listing_opf_nebula_patterns();
3024 		break;
3025 
3026 	case OPF_GAME_SND:
3027 		list = get_listing_opf_game_snds();
3028 		break;
3029 
3030 	case OPF_FIREBALL:
3031 		list = get_listing_opf_fireball();
3032 		break;
3033 
3034 	case OPF_SPECIES:
3035 		list = get_listing_opf_species();
3036 		break;
3037 
3038 	case OPF_LANGUAGE:
3039 		list = get_listing_opf_language();
3040 		break;
3041 
3042 	default:
3043 		Int3();  // unknown OPF code
3044 		list = NULL;
3045 		break;
3046 	}
3047 
3048 
3049 	// skip OPF_NONE, also skip for OPF_NULL, because it takes no data (though it can take plenty of operators)
3050 	if (opf == OPF_NULL || opf == OPF_NONE) {
3051 		return list;
3052 	}
3053 
3054 	// skip the special argument if we aren't at the right spot in when-argument or
3055 	// every-time-argument... meaning if either w_arg or e_arg is >= 1, we can continue
3056 	w_arg = find_ancestral_argument_number(OP_WHEN_ARGUMENT, parent_node);
3057 	e_arg = find_ancestral_argument_number(OP_EVERY_TIME_ARGUMENT, parent_node);
3058 	if (w_arg < 1 && e_arg < 1 /* && the same for any future _ARGUMENT sexps */ ) {
3059 		return list;
3060 	}
3061 
3062 	// the special item is a string and should not be added for numeric lists
3063 	if (opf != OPF_NUMBER && opf != OPF_POSITIVE) {
3064 		head.add_data(SEXP_ARGUMENT_STRING);
3065 	}
3066 
3067 	if (list != NULL) {
3068 		// append other list
3069 		head.add_list(list);
3070 	}
3071 
3072 	// return listing
3073 	return head.next;
3074 }
3075 
3076 // Goober5000
find_argument_number(int parent_node,int child_node)3077 int sexp_tree::find_argument_number(int parent_node, int child_node) {
3078 	int arg_num, current_node;
3079 
3080 	// code moved/adapted from match_closest_operator
3081 	arg_num = 0;
3082 	current_node = tree_nodes[parent_node].child;
3083 	while (current_node >= 0) {
3084 		// found?
3085 		if (current_node == child_node) {
3086 			return arg_num;
3087 		}
3088 
3089 		// continue iterating
3090 		arg_num++;
3091 		current_node = tree_nodes[current_node].next;
3092 	}
3093 
3094 	// not found
3095 	return -1;
3096 }
3097 
3098 // Goober5000
3099 // backtrack through parents until we find the operator matching
3100 // parent_op, then find the argument we went through
find_ancestral_argument_number(int parent_op,int child_node)3101 int sexp_tree::find_ancestral_argument_number(int parent_op, int child_node) {
3102 	if (child_node == -1) {
3103 		return -1;
3104 	}
3105 
3106 	int parent_node;
3107 	int current_node;
3108 
3109 	current_node = child_node;
3110 	parent_node = tree_nodes[current_node].parent;
3111 
3112 	while (parent_node >= 0) {
3113 		// check if the parent operator is the one we're looking for
3114 		if (get_operator_const(tree_nodes[parent_node].text) == parent_op) {
3115 			return find_argument_number(parent_node, current_node);
3116 		}
3117 
3118 		// continue iterating up the tree
3119 		current_node = parent_node;
3120 		parent_node = tree_nodes[current_node].parent;
3121 	}
3122 
3123 	// not found
3124 	return -1;
3125 }
3126 
3127 /**
3128 * Gets the proper data image for the tree item's place
3129 * in its parent hierarchy.
3130 */
get_data_image(int node)3131 NodeImage sexp_tree::get_data_image(int node) {
3132 	int count = get_sibling_place(node) + 1;
3133 
3134 	if (count <= 0) {
3135 		return NodeImage::DATA;
3136 	}
3137 
3138 	if (count % 5 != 0) {
3139 		return NodeImage::DATA;
3140 	}
3141 
3142 	int idx = (count % 100) / 5;
3143 
3144 	return static_cast<NodeImage>(static_cast<int>(NodeImage::DATA_00) + idx);
3145 }
3146 
get_sibling_place(int node)3147 int sexp_tree::get_sibling_place(int node) {
3148 	if (tree_nodes[node].parent > (int) tree_nodes.size()) {
3149 		return -1;
3150 	}
3151 	if (tree_nodes[node].parent < 0) {
3152 		return -1;
3153 	}
3154 
3155 	sexp_tree_item* myparent = &tree_nodes[tree_nodes[node].parent];
3156 
3157 	if (myparent->child == -1) {
3158 		return -1;
3159 	}
3160 
3161 	sexp_tree_item* mysibling = &tree_nodes[myparent->child];
3162 
3163 	int count = 0;
3164 	while (true) {
3165 		if (mysibling == &tree_nodes[node]) {
3166 			break;
3167 		}
3168 
3169 		if (mysibling->next == -1) {
3170 			break;
3171 		}
3172 
3173 		count++;
3174 		mysibling = &tree_nodes[mysibling->next];
3175 	}
3176 
3177 	return count;
3178 }
3179 
3180 
get_listing_opf_null()3181 sexp_list_item* sexp_tree::get_listing_opf_null() {
3182 	int i;
3183 	sexp_list_item head;
3184 
3185 	for (i = 0; i < (int) Operators.size(); i++) {
3186 		if (query_operator_return_type(i) == OPR_NULL) {
3187 			head.add_op(i);
3188 		}
3189 	}
3190 
3191 	return head.next;
3192 }
3193 
get_listing_opf_flexible_argument()3194 sexp_list_item* sexp_tree::get_listing_opf_flexible_argument() {
3195 	int i;
3196 	sexp_list_item head;
3197 
3198 	for (i = 0; i < (int) Operators.size(); i++) {
3199 		if (query_operator_return_type(i) == OPR_FLEXIBLE_ARGUMENT) {
3200 			head.add_op(i);
3201 		}
3202 	}
3203 
3204 	return head.next;
3205 }
3206 
get_listing_opf_bool(int parent_node)3207 sexp_list_item* sexp_tree::get_listing_opf_bool(int parent_node) {
3208 	int i, only_basic;
3209 	sexp_list_item head;
3210 
3211 	// search for the previous goal/event operators.  If found, only add the true/false
3212 	// sexpressions to the list
3213 	only_basic = 0;
3214 	if (parent_node != -1) {
3215 		int op;
3216 
3217 		op = get_operator_const(tree_nodes[parent_node].text);
3218 		if ((op == OP_PREVIOUS_GOAL_TRUE) || (op == OP_PREVIOUS_GOAL_FALSE) || (op == OP_PREVIOUS_EVENT_TRUE)
3219 			|| (op == OP_PREVIOUS_EVENT_FALSE)) {
3220 			only_basic = 1;
3221 		}
3222 
3223 	}
3224 
3225 	for (i = 0; i < (int) Operators.size(); i++) {
3226 		if (query_operator_return_type(i) == OPR_BOOL) {
3227 			if (!only_basic || (only_basic && ((Operators[i].value == OP_TRUE) || (Operators[i].value == OP_FALSE)))) {
3228 				head.add_op(i);
3229 			}
3230 		}
3231 	}
3232 
3233 	return head.next;
3234 }
3235 
get_listing_opf_positive()3236 sexp_list_item* sexp_tree::get_listing_opf_positive() {
3237 	int i, z;
3238 	sexp_list_item head;
3239 
3240 	for (i = 0; i < (int) Operators.size(); i++) {
3241 		z = query_operator_return_type(i);
3242 		// Goober5000's number hack
3243 		if ((z == OPR_NUMBER) || (z == OPR_POSITIVE)) {
3244 			head.add_op(i);
3245 		}
3246 	}
3247 
3248 	return head.next;
3249 }
3250 
get_listing_opf_number()3251 sexp_list_item* sexp_tree::get_listing_opf_number() {
3252 	int i, z;
3253 	sexp_list_item head;
3254 
3255 	for (i = 0; i < (int) Operators.size(); i++) {
3256 		z = query_operator_return_type(i);
3257 		if ((z == OPR_NUMBER) || (z == OPR_POSITIVE)) {
3258 			head.add_op(i);
3259 		}
3260 	}
3261 
3262 	return head.next;
3263 }
3264 
get_listing_opf_ship(int parent_node)3265 sexp_list_item* sexp_tree::get_listing_opf_ship(int parent_node) {
3266 	object* ptr;
3267 	sexp_list_item head;
3268 	int op = 0, dock_ship = -1, require_cap_ship = 0;
3269 
3270 	// look at the parent node and get the operator.  Some ship lists should be filtered based
3271 	// on what the parent operator is
3272 	if (parent_node >= 0) {
3273 		op = get_operator_const(tree_nodes[parent_node].text);
3274 
3275 		// get the dock_ship number of if this goal is an ai dock goal.  used to prune out unwanted ships out
3276 		// of the generated ship list
3277 		dock_ship = -1;
3278 		if (op == OP_AI_DOCK) {
3279 			int z;
3280 
3281 			z = tree_nodes[parent_node].parent;
3282 			Assert(z >= 0);
3283 			Assert(!stricmp(tree_nodes[z].text, "add-ship-goal") || !stricmp(tree_nodes[z].text, "add-wing-goal")
3284 					   || !stricmp(tree_nodes[z].text, "add-goal"));
3285 
3286 			z = tree_nodes[z].child;
3287 			Assert(z >= 0);
3288 
3289 			dock_ship = ship_name_lookup(tree_nodes[z].text, 1);
3290 			Assert(dock_ship != -1);
3291 		}
3292 	}
3293 
3294 	ptr = GET_FIRST(&obj_used_list);
3295 	while (ptr != END_OF_LIST(&obj_used_list)) {
3296 		if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
3297 			if (op == OP_AI_DOCK) {
3298 				// only include those ships in the list which the given ship can dock with.
3299 				if ((dock_ship != ptr->instance) && ship_docking_valid(dock_ship, ptr->instance)) {
3300 					head.add_data(Ships[ptr->instance].ship_name);
3301 				}
3302 
3303 			} else if (op == OP_CAP_SUBSYS_CARGO_KNOWN_DELAY) {
3304 				if (((Ship_info[Ships[ptr->instance].ship_info_index].is_huge_ship()) &&    // big ship
3305 					!(Ships[ptr->instance].flags[Ship::Ship_Flags::Toggle_subsystem_scanning]))
3306 					||                // which is not flagged OR
3307 						((!(Ship_info[Ships[ptr->instance].ship_info_index].is_huge_ship())) &&  // small ship
3308 							(Ships[ptr->instance].flags[Ship::Ship_Flags::Toggle_subsystem_scanning]))) {                // which is flagged
3309 
3310 					head.add_data(Ships[ptr->instance].ship_name);
3311 				}
3312 			} else {
3313 				if (!require_cap_ship || Ship_info[Ships[ptr->instance].ship_info_index].is_huge_ship()) {
3314 					head.add_data(Ships[ptr->instance].ship_name);
3315 				}
3316 			}
3317 		}
3318 
3319 		ptr = GET_NEXT(ptr);
3320 	}
3321 
3322 	return head.next;
3323 }
3324 
get_listing_opf_wing()3325 sexp_list_item* sexp_tree::get_listing_opf_wing() {
3326 	int i;
3327 	sexp_list_item head;
3328 
3329 	for (i = 0; i < MAX_WINGS; i++) {
3330 		if (Wings[i].wave_count) {
3331 			head.add_data(Wings[i].name);
3332 		}
3333 	}
3334 
3335 	return head.next;
3336 }
3337 
3338 // specific types of subsystems we're looking for
3339 #define OPS_CAP_CARGO        1
3340 #define OPS_STRENGTH            2
3341 #define OPS_BEAM_TURRET        3
3342 #define OPS_AWACS                4
3343 #define OPS_ROTATE            5
3344 #define OPS_ARMOR            6
get_listing_opf_subsystem(int parent_node,int arg_index)3345 sexp_list_item* sexp_tree::get_listing_opf_subsystem(int parent_node, int arg_index) {
3346 	int op, child, sh;
3347 	int special_subsys = 0;
3348 	sexp_list_item head;
3349 	ship_subsys* subsys;
3350 
3351 	// determine if the parent is one of the set subsystem strength items.  If so,
3352 	// we want to append the "Hull" name onto the end of the menu
3353 	Assert(parent_node >= 0);
3354 
3355 	// get the operator type of the node
3356 	op = get_operator_const(tree_nodes[parent_node].text);
3357 
3358 	// first child node
3359 	child = tree_nodes[parent_node].child;
3360 	Assert(child >= 0);
3361 
3362 	switch (op) {
3363 		// where we care about hull strength
3364 	case OP_REPAIR_SUBSYSTEM:
3365 	case OP_SABOTAGE_SUBSYSTEM:
3366 	case OP_SET_SUBSYSTEM_STRNGTH:
3367 		special_subsys = OPS_STRENGTH;
3368 		break;
3369 
3370 		// Armor types need Hull and Shields but not Simulated Hull
3371 	case OP_SET_ARMOR_TYPE:
3372 		special_subsys = OPS_ARMOR;
3373 		break;
3374 
3375 		// awacs subsystems
3376 	case OP_AWACS_SET_RADIUS:
3377 		special_subsys = OPS_AWACS;
3378 		break;
3379 
3380 		// rotating
3381 	case OP_LOCK_ROTATING_SUBSYSTEM:
3382 	case OP_FREE_ROTATING_SUBSYSTEM:
3383 	case OP_REVERSE_ROTATING_SUBSYSTEM:
3384 	case OP_ROTATING_SUBSYS_SET_TURN_TIME:
3385 		special_subsys = OPS_ROTATE;
3386 		break;
3387 
3388 		// where we care about capital ship subsystem cargo
3389 	case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
3390 		special_subsys = OPS_CAP_CARGO;
3391 
3392 		// get the next sibling
3393 		child = tree_nodes[child].next;
3394 		break;
3395 
3396 		// where we care about turrets carrying beam weapons
3397 	case OP_BEAM_FIRE:
3398 		special_subsys = OPS_BEAM_TURRET;
3399 
3400 		// if this is arg index 3 (targeted ship)
3401 		if (arg_index == 3) {
3402 			special_subsys = OPS_STRENGTH;
3403 
3404 			// iterate to the next field two times
3405 			child = tree_nodes[child].next;
3406 			Assert(child >= 0);
3407 			child = tree_nodes[child].next;
3408 		} else {
3409 			Assert(arg_index == 1);
3410 		}
3411 		break;
3412 
3413 	case OP_BEAM_FIRE_COORDS:
3414 		special_subsys = OPS_BEAM_TURRET;
3415 		break;
3416 
3417 		// these sexps check the subsystem of the *second entry* on the list, not the first
3418 	case OP_DISTANCE_CENTER_SUBSYSTEM:
3419 	case OP_DISTANCE_BBOX_SUBSYSTEM:
3420 	case OP_SET_CARGO:
3421 	case OP_IS_CARGO:
3422 	case OP_CHANGE_AI_CLASS:
3423 	case OP_IS_AI_CLASS:
3424 	case OP_MISSILE_LOCKED:
3425 	case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
3426 	case OP_IS_IN_TURRET_FOV:
3427 		// iterate to the next field
3428 		child = tree_nodes[child].next;
3429 		break;
3430 
3431 		// this sexp checks the subsystem of the *fourth entry* on the list
3432 	case OP_QUERY_ORDERS:
3433 		// iterate to the next field three times
3434 		child = tree_nodes[child].next;
3435 		Assert(child >= 0);
3436 		child = tree_nodes[child].next;
3437 		Assert(child >= 0);
3438 		child = tree_nodes[child].next;
3439 		break;
3440 
3441 		// this sexp checks the subsystem of the *seventh entry* on the list
3442 	case OP_BEAM_FLOATING_FIRE:
3443 		// iterate to the next field six times
3444 		child = tree_nodes[child].next;
3445 		Assert(child >= 0);
3446 		child = tree_nodes[child].next;
3447 		Assert(child >= 0);
3448 		child = tree_nodes[child].next;
3449 		Assert(child >= 0);
3450 		child = tree_nodes[child].next;
3451 		Assert(child >= 0);
3452 		child = tree_nodes[child].next;
3453 		Assert(child >= 0);
3454 		child = tree_nodes[child].next;
3455 		break;
3456 
3457 		// this sexp checks the subsystem of the *ninth entry* on the list
3458 	case OP_WEAPON_CREATE:
3459 		// iterate to the next field eight times
3460 		child = tree_nodes[child].next;
3461 		Assert(child >= 0);
3462 		child = tree_nodes[child].next;
3463 		Assert(child >= 0);
3464 		child = tree_nodes[child].next;
3465 		Assert(child >= 0);
3466 		child = tree_nodes[child].next;
3467 		Assert(child >= 0);
3468 		child = tree_nodes[child].next;
3469 		Assert(child >= 0);
3470 		child = tree_nodes[child].next;
3471 		Assert(child >= 0);
3472 		child = tree_nodes[child].next;
3473 		Assert(child >= 0);
3474 		child = tree_nodes[child].next;
3475 		break;
3476 	}
3477 
3478 	// now find the ship and add all relevant subsystems
3479 	Assert(child >= 0);
3480 	sh = ship_name_lookup(tree_nodes[child].text, 1);
3481 	if (sh >= 0) {
3482 		subsys = GET_FIRST(&Ships[sh].subsys_list);
3483 		while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) {
3484 			// add stuff
3485 			switch (special_subsys) {
3486 				// subsystem cargo
3487 			case OPS_CAP_CARGO:
3488 				head.add_data(subsys->system_info->subobj_name);
3489 				break;
3490 
3491 				// beam fire
3492 			case OPS_BEAM_TURRET:
3493 				head.add_data(subsys->system_info->subobj_name);
3494 				break;
3495 
3496 				// awacs level
3497 			case OPS_AWACS:
3498 				if (subsys->system_info->flags[Model::Subsystem_Flags::Awacs]) {
3499 					head.add_data(subsys->system_info->subobj_name);
3500 				}
3501 				break;
3502 
3503 				// rotating
3504 			case OPS_ROTATE:
3505 				if (subsys->system_info->flags[Model::Subsystem_Flags::Rotates]) {
3506 					head.add_data(subsys->system_info->subobj_name);
3507 				}
3508 				break;
3509 
3510 				// everything else
3511 			default:
3512 				head.add_data(subsys->system_info->subobj_name);
3513 				break;
3514 			}
3515 
3516 			// next subsystem
3517 			subsys = GET_NEXT(subsys);
3518 		}
3519 	}
3520 
3521 	// if one of the subsystem strength operators, append the Hull string and the Simulated Hull string
3522 	if (special_subsys == OPS_STRENGTH) {
3523 		head.add_data(SEXP_HULL_STRING);
3524 		head.add_data(SEXP_SIM_HULL_STRING);
3525 	}
3526 	// if setting armor type we only need Hull and Shields
3527 	if (special_subsys == OPS_ARMOR) {
3528 		head.add_data(SEXP_HULL_STRING);
3529 		head.add_data(SEXP_SHIELD_STRING);
3530 	}
3531 
3532 	return head.next;
3533 }
3534 
get_listing_opf_subsystem_type(int parent_node)3535 sexp_list_item* sexp_tree::get_listing_opf_subsystem_type(int parent_node) {
3536 	int i, child, shipnum, num_added = 0;
3537 	sexp_list_item head;
3538 	ship_subsys* subsys;
3539 
3540 	// first child node
3541 	child = tree_nodes[parent_node].child;
3542 	Assert(child >= 0);
3543 
3544 	// now find the ship
3545 	shipnum = ship_name_lookup(tree_nodes[child].text, 1);
3546 	if (shipnum < 0) {
3547 		return head.next;
3548 	}
3549 
3550 	// add all relevant subsystem types
3551 	for (i = 0; i < SUBSYSTEM_MAX; i++) {
3552 		// don't allow these two
3553 		if (i == SUBSYSTEM_NONE || i == SUBSYSTEM_UNKNOWN) {
3554 			continue;
3555 		}
3556 
3557 		// loop through all ship subsystems
3558 		subsys = GET_FIRST(&Ships[shipnum].subsys_list);
3559 		while (subsys != END_OF_LIST(&Ships[shipnum].subsys_list)) {
3560 			// check if this subsystem is of this type
3561 			if (i == subsys->system_info->type) {
3562 				// subsystem type is applicable, so add it
3563 				head.add_data(Subsystem_types[i]);
3564 				num_added++;
3565 				break;
3566 			}
3567 
3568 			// next subsystem
3569 			subsys = GET_NEXT(subsys);
3570 		}
3571 	}
3572 
3573 	// if no subsystem types, go ahead and add NONE (even though it won't be checked)
3574 	if (num_added == 0) {
3575 		head.add_data(Subsystem_types[SUBSYSTEM_NONE]);
3576 	}
3577 
3578 	return head.next;
3579 }
3580 
get_listing_opf_point()3581 sexp_list_item* sexp_tree::get_listing_opf_point() {
3582 	char buf[NAME_LENGTH + 8];
3583 	SCP_list<waypoint_list>::iterator ii;
3584 	int j;
3585 	sexp_list_item head;
3586 
3587 	for (ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++ii) {
3588 		for (j = 0; (uint) j < ii->get_waypoints().size(); ++j) {
3589 			sprintf(buf, "%s:%d", ii->get_name(), j + 1);
3590 			head.add_data_dup(buf);
3591 		}
3592 	}
3593 
3594 	return head.next;
3595 }
3596 
get_listing_opf_iff()3597 sexp_list_item* sexp_tree::get_listing_opf_iff() {
3598 	int i;
3599 	sexp_list_item head;
3600 
3601 	for (i = 0; i < Num_iffs; i++) {
3602 		head.add_data(Iff_info[i].iff_name);
3603 	}
3604 
3605 	return head.next;
3606 }
3607 
get_listing_opf_ai_class()3608 sexp_list_item* sexp_tree::get_listing_opf_ai_class() {
3609 	int i;
3610 	sexp_list_item head;
3611 
3612 	for (i = 0; i < Num_ai_classes; i++) {
3613 		head.add_data(Ai_class_names[i]);
3614 	}
3615 
3616 	return head.next;
3617 }
3618 
get_listing_opf_support_ship_class()3619 sexp_list_item* sexp_tree::get_listing_opf_support_ship_class() {
3620 	sexp_list_item head;
3621 
3622 	head.add_data("<species support ship class>");
3623 
3624 	for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
3625 		if (it->flags[Ship::Info_Flags::Support]) {
3626 			head.add_data(it->name);
3627 		}
3628 	}
3629 
3630 	return head.next;
3631 }
3632 
get_listing_opf_ssm_class()3633 sexp_list_item* sexp_tree::get_listing_opf_ssm_class() {
3634 	sexp_list_item head;
3635 
3636 	for (auto it = Ssm_info.cbegin(); it != Ssm_info.cend(); ++it) {
3637 		head.add_data(it->name);
3638 	}
3639 
3640 	return head.next;
3641 }
3642 
get_listing_opf_ship_with_bay()3643 sexp_list_item* sexp_tree::get_listing_opf_ship_with_bay() {
3644 	object* objp;
3645 	sexp_list_item head;
3646 
3647 	head.add_data("<no anchor>");
3648 
3649 	for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
3650 		if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
3651 			// determine if this ship has a docking bay
3652 			if (ship_has_dock_bay(objp->instance)) {
3653 				head.add_data(Ships[objp->instance].ship_name);
3654 			}
3655 		}
3656 	}
3657 
3658 	return head.next;
3659 }
3660 
get_listing_opf_soundtrack_name()3661 sexp_list_item* sexp_tree::get_listing_opf_soundtrack_name() {
3662 	int i;
3663 	sexp_list_item head;
3664 
3665 	head.add_data("<No Music>");
3666 
3667 	for (i = 0; i < Num_soundtracks; i++) {
3668 		head.add_data(Soundtracks[i].name);
3669 	}
3670 
3671 	return head.next;
3672 }
3673 
get_listing_opf_arrival_location()3674 sexp_list_item* sexp_tree::get_listing_opf_arrival_location() {
3675 	int i;
3676 	sexp_list_item head;
3677 
3678 	for (i = 0; i < MAX_ARRIVAL_NAMES; i++) {
3679 		head.add_data(Arrival_location_names[i]);
3680 	}
3681 
3682 	return head.next;
3683 }
3684 
get_listing_opf_departure_location()3685 sexp_list_item* sexp_tree::get_listing_opf_departure_location() {
3686 	int i;
3687 	sexp_list_item head;
3688 
3689 	for (i = 0; i < MAX_DEPARTURE_NAMES; i++) {
3690 		head.add_data(Departure_location_names[i]);
3691 	}
3692 
3693 	return head.next;
3694 }
3695 
get_listing_opf_arrival_anchor_all()3696 sexp_list_item* sexp_tree::get_listing_opf_arrival_anchor_all() {
3697 	int i, restrict_to_players;
3698 	object* objp;
3699 	sexp_list_item head;
3700 
3701 	for (restrict_to_players = 0; restrict_to_players < 2; restrict_to_players++) {
3702 		for (i = 0; i < Num_iffs; i++) {
3703 			char tmp[NAME_LENGTH + 15];
3704 			stuff_special_arrival_anchor_name(tmp, i, restrict_to_players, 0);
3705 
3706 			head.add_data_dup(tmp);
3707 		}
3708 	}
3709 
3710 	for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
3711 		if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
3712 			head.add_data(Ships[objp->instance].ship_name);
3713 		}
3714 	}
3715 
3716 	return head.next;
3717 }
3718 
get_listing_opf_ai_goal(int parent_node)3719 sexp_list_item* sexp_tree::get_listing_opf_ai_goal(int parent_node) {
3720 	int i, n, w, z, child;
3721 	sexp_list_item head;
3722 
3723 	Assert(parent_node >= 0);
3724 	child = tree_nodes[parent_node].child;
3725 	Assert(child >= 0);
3726 	n = ship_name_lookup(tree_nodes[child].text, 1);
3727 	if (n >= 0) {
3728 		// add operators if it's an ai-goal and ai-goal is allowed for that ship
3729 		for (i = 0; i < (int) Operators.size(); i++) {
3730 			if ((query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n)) {
3731 				head.add_op(i);
3732 			}
3733 		}
3734 
3735 	} else {
3736 		z = wing_name_lookup(tree_nodes[child].text);
3737 		if (z >= 0) {
3738 			for (w = 0; w < Wings[z].wave_count; w++) {
3739 				n = Wings[z].ship_index[w];
3740 				// add operators if it's an ai-goal and ai-goal is allowed for that ship
3741 				for (i = 0; i < (int) Operators.size(); i++) {
3742 					if ((query_operator_return_type(i) == OPR_AI_GOAL)
3743 						&& query_sexp_ai_goal_valid(Operators[i].value, n)) {
3744 						head.add_op(i);
3745 					}
3746 				}
3747 			}
3748 			// when dealing with the special argument add them all. It's up to the FREDder to ensure invalid orders aren't given
3749 		} else if (!strcmp(tree_nodes[child].text, SEXP_ARGUMENT_STRING)) {
3750 			for (i = 0; i < (int) Operators.size(); i++) {
3751 				if (query_operator_return_type(i) == OPR_AI_GOAL) {
3752 					head.add_op(i);
3753 				}
3754 			}
3755 		} else {
3756 			return NULL;
3757 		}  // no valid ship or wing to check against, make nothing available
3758 	}
3759 
3760 	return head.next;
3761 }
3762 
get_listing_opf_docker_point(int parent_node)3763 sexp_list_item* sexp_tree::get_listing_opf_docker_point(int parent_node) {
3764 	int z;
3765 	sexp_list_item head;
3766 	int sh = -1;
3767 
3768 	Assert(parent_node >= 0);
3769 	Assert(!stricmp(tree_nodes[parent_node].text, "ai-dock") || !stricmp(tree_nodes[parent_node].text, "set-docked"));
3770 
3771 	if (!stricmp(tree_nodes[parent_node].text, "ai-dock")) {
3772 		z = tree_nodes[parent_node].parent;
3773 		Assert(z >= 0);
3774 		Assert(!stricmp(tree_nodes[z].text, "add-ship-goal") || !stricmp(tree_nodes[z].text, "add-wing-goal")
3775 				   || !stricmp(tree_nodes[z].text, "add-goal"));
3776 
3777 		z = tree_nodes[z].child;
3778 		Assert(z >= 0);
3779 
3780 		sh = ship_name_lookup(tree_nodes[z].text, 1);
3781 	} else if (!stricmp(tree_nodes[parent_node].text, "set-docked")) {
3782 		//Docker ship should be the first child node
3783 		z = tree_nodes[parent_node].child;
3784 		sh = ship_name_lookup(tree_nodes[z].text, 1);
3785 	}
3786 
3787 	if (sh >= 0) {
3788 		auto list = _editor->get_docking_list(Ship_info[Ships[sh].ship_info_index].model_num);
3789 		for (auto& dock : list) {
3790 			head.add_data(dock.c_str());
3791 		}
3792 	}
3793 
3794 	return head.next;
3795 }
3796 
get_listing_opf_dockee_point(int parent_node)3797 sexp_list_item* sexp_tree::get_listing_opf_dockee_point(int parent_node) {
3798 	int z;
3799 	sexp_list_item head;
3800 	int sh = -1;
3801 
3802 	Assert(parent_node >= 0);
3803 	Assert(!stricmp(tree_nodes[parent_node].text, "ai-dock") || !stricmp(tree_nodes[parent_node].text, "set-docked"));
3804 
3805 	if (!stricmp(tree_nodes[parent_node].text, "ai-dock")) {
3806 		z = tree_nodes[parent_node].child;
3807 		Assert(z >= 0);
3808 
3809 		sh = ship_name_lookup(tree_nodes[z].text, 1);
3810 	} else if (!stricmp(tree_nodes[parent_node].text, "set-docked")) {
3811 		//Dockee ship should be the third child node
3812 		z = tree_nodes[parent_node].child;    // 1
3813 		z = tree_nodes[z].next;                // 2
3814 		z = tree_nodes[z].next;                // 3
3815 
3816 		sh = ship_name_lookup(tree_nodes[z].text, 1);
3817 	}
3818 
3819 	if (sh >= 0) {
3820 		auto list = _editor->get_docking_list(Ship_info[Ships[sh].ship_info_index].model_num);
3821 		for (auto& dock : list) {
3822 			head.add_data(dock.c_str());
3823 		}
3824 	}
3825 
3826 	return head.next;
3827 }
3828 
get_listing_opf_message()3829 sexp_list_item* sexp_tree::get_listing_opf_message() {
3830 	sexp_list_item head;
3831 
3832 	for (auto& msg : _interface->getMessages()) {
3833 		head.add_data(msg.c_str());
3834 	}
3835 
3836 	return head.next;
3837 }
3838 
get_listing_opf_persona()3839 sexp_list_item* sexp_tree::get_listing_opf_persona() {
3840 	int i;
3841 	sexp_list_item head;
3842 
3843 	for (i = 0; i < Num_personas; i++) {
3844 		if (Personas[i].flags & PERSONA_FLAG_WINGMAN) {
3845 			head.add_data(Personas[i].name);
3846 		}
3847 	}
3848 
3849 	return head.next;
3850 }
3851 
get_listing_opf_font()3852 sexp_list_item* sexp_tree::get_listing_opf_font() {
3853 	int i;
3854 	sexp_list_item head;
3855 
3856 	for (i = 0; i < font::FontManager::numberOfFonts(); i++) {
3857 		head.add_data(const_cast<char*>(font::FontManager::getFont(i)->getName().c_str()));
3858 	}
3859 
3860 	return head.next;
3861 }
3862 
get_listing_opf_who_from()3863 sexp_list_item* sexp_tree::get_listing_opf_who_from() {
3864 	object* ptr;
3865 	sexp_list_item head;
3866 
3867 	//head.add_data("<any allied>");
3868 	head.add_data("#Command");
3869 	head.add_data("<any wingman>");
3870 	head.add_data("<none>");
3871 
3872 	ptr = GET_FIRST(&obj_used_list);
3873 	while (ptr != END_OF_LIST(&obj_used_list)) {
3874 		if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
3875 			if (Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].is_flyable()) {
3876 				head.add_data(Ships[ptr->instance].ship_name);
3877 			}
3878 		}
3879 
3880 		ptr = GET_NEXT(ptr);
3881 	}
3882 
3883 	return head.next;
3884 }
3885 
get_listing_opf_priority()3886 sexp_list_item* sexp_tree::get_listing_opf_priority() {
3887 	sexp_list_item head;
3888 
3889 	head.add_data("High");
3890 	head.add_data("Normal");
3891 	head.add_data("Low");
3892 	return head.next;
3893 }
3894 
get_listing_opf_sound_environment()3895 sexp_list_item* sexp_tree::get_listing_opf_sound_environment() {
3896 	sexp_list_item head;
3897 
3898 	head.add_data(SEXP_NONE_STRING);
3899 	for (int i = 0; i < (int) EFX_presets.size(); i++) {
3900 		head.add_data_dup(EFX_presets[i].name.c_str());
3901 	}
3902 
3903 	return head.next;
3904 }
3905 
get_listing_opf_sound_environment_option()3906 sexp_list_item* sexp_tree::get_listing_opf_sound_environment_option() {
3907 	sexp_list_item head;
3908 
3909 	for (int i = 0; i < Num_sound_environment_options; i++) {
3910 		head.add_data(Sound_environment_option[i]);
3911 	}
3912 
3913 	return head.next;
3914 }
3915 
get_listing_opf_adjust_audio_volume()3916 sexp_list_item* sexp_tree::get_listing_opf_adjust_audio_volume() {
3917 	sexp_list_item head;
3918 
3919 	for (int i = 0; i < Num_adjust_audio_options; i++) {
3920 		head.add_data(Adjust_audio_options[i]);
3921 	}
3922 
3923 	return head.next;
3924 }
3925 
get_listing_opf_builtin_hud_gauge()3926 sexp_list_item* sexp_tree::get_listing_opf_builtin_hud_gauge() {
3927 	sexp_list_item head;
3928 
3929 	for (int i = 0; i < Num_hud_gauge_types; i++) {
3930 		head.add_data(Hud_gauge_types[i].name);
3931 	}
3932 
3933 	return head.next;
3934 }
3935 
get_listing_opf_custom_hud_gauge()3936 sexp_list_item *sexp_tree::get_listing_opf_custom_hud_gauge()
3937 {
3938 	sexp_list_item head;
3939 	SCP_unordered_set<SCP_string> all_gauges;
3940 
3941 	for (auto &gauge : default_hud_gauges)
3942 	{
3943 		all_gauges.insert(gauge->getCustomGaugeName());
3944 		head.add_data(gauge->getCustomGaugeName());
3945 	}
3946 
3947 	for (auto &si : Ship_info)
3948 	{
3949 		for (auto &gauge : si.hud_gauges)
3950 		{
3951 			// avoid duplicating any HUD gauges
3952 			if (all_gauges.count(gauge->getCustomGaugeName()) == 0)
3953 			{
3954 				all_gauges.insert(gauge->getCustomGaugeName());
3955 				head.add_data(gauge->getCustomGaugeName());
3956 			}
3957 		}
3958 	}
3959 
3960 	return head.next;
3961 }
3962 
get_listing_opf_ship_effect()3963 sexp_list_item* sexp_tree::get_listing_opf_ship_effect() {
3964 	sexp_list_item head;
3965 
3966 	for (SCP_vector<ship_effect>::iterator sei = Ship_effects.begin(); sei != Ship_effects.end(); ++sei) {
3967 		head.add_data_dup(sei->name);
3968 	}
3969 
3970 	return head.next;
3971 }
3972 
get_listing_opf_explosion_option()3973 sexp_list_item* sexp_tree::get_listing_opf_explosion_option() {
3974 	sexp_list_item head;
3975 
3976 	for (int i = 0; i < Num_explosion_options; i++) {
3977 		head.add_data(Explosion_option[i]);
3978 	}
3979 
3980 	return head.next;
3981 }
3982 
get_listing_opf_waypoint_path()3983 sexp_list_item* sexp_tree::get_listing_opf_waypoint_path() {
3984 	SCP_list<waypoint_list>::iterator ii;
3985 	sexp_list_item head;
3986 
3987 	for (ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++ii) {
3988 		head.add_data(ii->get_name());
3989 	}
3990 
3991 	return head.next;
3992 }
3993 
get_listing_opf_ship_point()3994 sexp_list_item* sexp_tree::get_listing_opf_ship_point() {
3995 	sexp_list_item head;
3996 
3997 	head.add_list(get_listing_opf_ship());
3998 	head.add_list(get_listing_opf_point());
3999 
4000 	return head.next;
4001 }
4002 
get_listing_opf_ship_wing_wholeteam()4003 sexp_list_item* sexp_tree::get_listing_opf_ship_wing_wholeteam() {
4004 	int i;
4005 	sexp_list_item head;
4006 
4007 	for (i = 0; i < Num_iffs; i++) {
4008 		head.add_data(Iff_info[i].iff_name);
4009 	}
4010 
4011 	head.add_list(get_listing_opf_ship_wing());
4012 
4013 	return head.next;
4014 }
4015 
get_listing_opf_ship_wing_shiponteam_point()4016 sexp_list_item* sexp_tree::get_listing_opf_ship_wing_shiponteam_point() {
4017 	int i;
4018 	sexp_list_item head;
4019 
4020 	for (i = 0; i < Num_iffs; i++) {
4021 		SCP_string tmp;
4022 		sprintf(tmp, "<any %s>", Iff_info[i].iff_name);
4023 		std::transform(begin(tmp), end(tmp), begin(tmp), [](char c) { return (char)::tolower(c); });
4024 		head.add_data_dup(tmp.c_str());
4025 	}
4026 
4027 	head.add_list(get_listing_opf_ship_wing_point());
4028 
4029 	return head.next;
4030 }
4031 
get_listing_opf_ship_wing_point()4032 sexp_list_item* sexp_tree::get_listing_opf_ship_wing_point() {
4033 	sexp_list_item head;
4034 
4035 	head.add_list(get_listing_opf_ship());
4036 	head.add_list(get_listing_opf_wing());
4037 	head.add_list(get_listing_opf_point());
4038 
4039 	return head.next;
4040 }
4041 
get_listing_opf_ship_wing_point_or_none()4042 sexp_list_item* sexp_tree::get_listing_opf_ship_wing_point_or_none() {
4043 	sexp_list_item head;
4044 
4045 	head.add_data(SEXP_NONE_STRING);
4046 	head.add_list(get_listing_opf_ship_wing_point());
4047 
4048 	return head.next;
4049 }
4050 
get_listing_opf_mission_name()4051 sexp_list_item* sexp_tree::get_listing_opf_mission_name() {
4052 	sexp_list_item head;
4053 
4054 	for (auto& mission : _interface->getMissionNames()) {
4055 		head.add_data(mission.c_str());
4056 	}
4057 
4058 	// This code is kept until the campaign editor is added
4059 	/*
4060 	if ((m_mode == MODE_CAMPAIGN) && (Cur_campaign_mission >= 0)) {
4061 		for (i = 0; i < Campaign.num_missions; i++)
4062 			if ((i == Cur_campaign_mission)
4063 				|| (Campaign.missions[i].level < Campaign.missions[Cur_campaign_mission].level))
4064 				head.add_data(Campaign.missions[i].name);
4065 
4066 	} else
4067 		head.add_data(Mission_filename);
4068 	 */
4069 
4070 	return head.next;
4071 }
4072 
get_listing_opf_goal_name(int parent_node)4073 sexp_list_item* sexp_tree::get_listing_opf_goal_name(int parent_node) {
4074 	sexp_list_item head;
4075 
4076 	Assert(parent_node >= 0);
4077 	auto child = tree_nodes[parent_node].child;
4078 
4079 	// This is used by the campaign editor to show the entries for specific missions
4080 	SCP_string reference;
4081 	if (child >= 0) {
4082 		reference = tree_nodes[child].text;
4083 	}
4084 
4085 	for (auto& entry : _interface->getMissionGoals(reference)) {
4086 		head.add_data(entry.c_str());
4087 	}
4088 
4089 	// This code is kept for reference purposes until the campaign editor is added
4090 	/*
4091 	if (m_mode == MODE_CAMPAIGN) {
4092 		int child;
4093 
4094 		Assert(parent_node >= 0);
4095 		child = tree_nodes[parent_node].child;
4096 		Assert(child >= 0);
4097 
4098 		for (m = 0; m < Campaign.num_missions; m++)
4099 			if (!stricmp(Campaign.missions[m].name, tree_nodes[child].text))
4100 				break;
4101 
4102 		if (m < Campaign.num_missions) {
4103 			if (Campaign.missions[m].num_goals < 0)  // haven't loaded goal names yet.
4104 				read_mission_goal_list(m);
4105 
4106 			for (i = 0; i < Campaign.missions[m].num_goals; i++)
4107 				head.add_data(Campaign.missions[m].goals[i].name);
4108 		}
4109 
4110 	} else {
4111 		for (i = 0; i < Num_goals; i++)
4112 			head.add_data(Mission_goals[i].name);
4113 	}
4114 	 */
4115 
4116 	return head.next;
4117 }
4118 
get_listing_opf_ship_wing()4119 sexp_list_item* sexp_tree::get_listing_opf_ship_wing() {
4120 	sexp_list_item head;
4121 
4122 	head.add_list(get_listing_opf_ship());
4123 	head.add_list(get_listing_opf_wing());
4124 
4125 	return head.next;
4126 }
4127 
get_listing_opf_order_recipient()4128 sexp_list_item* sexp_tree::get_listing_opf_order_recipient() {
4129 	sexp_list_item head;
4130 
4131 	head.add_data("<all fighters>");
4132 
4133 	head.add_list(get_listing_opf_ship());
4134 	head.add_list(get_listing_opf_wing());
4135 	return head.next;
4136 }
4137 
get_listing_opf_ship_type()4138 sexp_list_item* sexp_tree::get_listing_opf_ship_type() {
4139 	unsigned int i;
4140 	sexp_list_item head;
4141 
4142 	for (i = 0; i < Ship_types.size(); i++) {
4143 		head.add_data(Ship_types[i].name);
4144 	}
4145 
4146 	return head.next;
4147 }
4148 
get_listing_opf_keypress()4149 sexp_list_item* sexp_tree::get_listing_opf_keypress() {
4150 	sexp_list_item head;
4151 	const auto& Default_config = Control_config_presets[0].bindings;
4152 
4153 	for (size_t i = 0; i < Control_config.size(); ++i) {
4154 		auto btn = Default_config[i].get_btn(CID_KEYBOARD);
4155 
4156 		if ((btn >= -1) && !Control_config[i].disabled) {
4157 			head.add_data_dup(textify_scancode(btn));
4158 		}
4159 	}
4160 
4161 	return head.next;
4162 }
4163 
get_listing_opf_event_name(int parent_node)4164 sexp_list_item* sexp_tree::get_listing_opf_event_name(int parent_node) {
4165 	sexp_list_item head;
4166 
4167 	Assert(parent_node >= 0);
4168 	auto child = tree_nodes[parent_node].child;
4169 
4170 	// This is used by the campaign editor to show the entries for specific missions
4171 	SCP_string reference;
4172 	if (child >= 0) {
4173 		reference = tree_nodes[child].text;
4174 	}
4175 
4176 	for (auto& entry : _interface->getMissionEvents(reference)) {
4177 		head.add_data(entry.c_str());
4178 	}
4179 
4180 	// This code is kept until the campaign editor is added
4181 	/*
4182 	if (m_mode == MODE_CAMPAIGN) {
4183 		Assert(parent_node >= 0);
4184 		auto child = tree_nodes[parent_node].child;
4185 		Assert(child >= 0);
4186 
4187 		for (m = 0; m < Campaign.num_missions; m++)
4188 			if (!stricmp(Campaign.missions[m].name, tree_nodes[child].text))
4189 				break;
4190 
4191 		if (m < Campaign.num_missions) {
4192 			if (Campaign.missions[m].num_events < 0)  // haven't loaded goal names yet.
4193 				read_mission_goal_list(m);
4194 
4195 			for (i = 0; i < Campaign.missions[m].num_events; i++)
4196 				head.add_data(Campaign.missions[m].events[i].name);
4197 		}
4198 	} else {
4199 		for (i = 0; i < Num_mission_events; i++)
4200 			head.add_data(Mission_events[i].name);
4201 	}
4202 	 */
4203 
4204 	return head.next;
4205 }
4206 
get_listing_opf_ai_order()4207 sexp_list_item* sexp_tree::get_listing_opf_ai_order() {
4208 	int i;
4209 	sexp_list_item head;
4210 
4211 	for (i = 0; i < NUM_COMM_ORDER_ITEMS; i++) {
4212 		head.add_data(Comm_orders[i].name.c_str());
4213 	}
4214 
4215 	return head.next;
4216 }
4217 
get_listing_opf_skill_level()4218 sexp_list_item* sexp_tree::get_listing_opf_skill_level() {
4219 	int i;
4220 	sexp_list_item head;
4221 
4222 	for (i = 0; i < NUM_SKILL_LEVELS; i++) {
4223 		head.add_data(Skill_level_names(i, 0));
4224 	}
4225 
4226 	return head.next;
4227 }
4228 
get_listing_opf_cargo()4229 sexp_list_item* sexp_tree::get_listing_opf_cargo() {
4230 	sexp_list_item head;
4231 
4232 	head.add_data("Nothing");
4233 	for (int i = 0; i < Num_cargo; i++) {
4234 		if (stricmp(Cargo_names[i], "nothing") != 0) {
4235 			head.add_data(Cargo_names[i]);
4236 		}
4237 	}
4238 
4239 	return head.next;
4240 }
4241 
get_listing_opf_string()4242 sexp_list_item* sexp_tree::get_listing_opf_string() {
4243 	sexp_list_item head;
4244 
4245 	head.add_data(SEXP_ANY_STRING);
4246 
4247 	return head.next;
4248 }
4249 
get_listing_opf_medal_name()4250 sexp_list_item* sexp_tree::get_listing_opf_medal_name() {
4251 	int i;
4252 	sexp_list_item head;
4253 
4254 	for (i = 0; i < Num_medals; i++) {
4255 		// don't add Rank or the Ace badges
4256 		if ((i == Rank_medal_index) || (Medals[i].kills_needed > 0)) {
4257 			continue;
4258 		}
4259 		head.add_data(Medals[i].name);
4260 	}
4261 
4262 	return head.next;
4263 }
4264 
get_listing_opf_weapon_name()4265 sexp_list_item* sexp_tree::get_listing_opf_weapon_name() {
4266 	sexp_list_item head;
4267 
4268 	for (auto &wi : Weapon_info) {
4269 		head.add_data(wi.name);
4270 	}
4271 
4272 	return head.next;
4273 }
4274 
get_listing_opf_intel_name()4275 sexp_list_item* sexp_tree::get_listing_opf_intel_name() {
4276 	sexp_list_item head;
4277 
4278 	for (auto &ii : Intel_info) {
4279 		head.add_data(ii.name);
4280 	}
4281 
4282 	return head.next;
4283 }
4284 
get_listing_opf_ship_class_name()4285 sexp_list_item* sexp_tree::get_listing_opf_ship_class_name() {
4286 	sexp_list_item head;
4287 
4288 	for (auto &si : Ship_info) {
4289 		head.add_data(si.name);
4290 	}
4291 
4292 	return head.next;
4293 }
4294 
get_listing_opf_huge_weapon()4295 sexp_list_item* sexp_tree::get_listing_opf_huge_weapon() {
4296 	sexp_list_item head;
4297 
4298 	for (auto &wi : Weapon_info) {
4299 		if (wi.wi_flags[Weapon::Info_Flags::Huge]) {
4300 			head.add_data(wi.name);
4301 		}
4302 	}
4303 
4304 	return head.next;
4305 }
4306 
get_listing_opf_ship_not_player()4307 sexp_list_item* sexp_tree::get_listing_opf_ship_not_player() {
4308 	object* ptr;
4309 	sexp_list_item head;
4310 
4311 	ptr = GET_FIRST(&obj_used_list);
4312 	while (ptr != END_OF_LIST(&obj_used_list)) {
4313 		if (ptr->type == OBJ_SHIP) {
4314 			head.add_data(Ships[ptr->instance].ship_name);
4315 		}
4316 
4317 		ptr = GET_NEXT(ptr);
4318 	}
4319 
4320 	return head.next;
4321 }
4322 
get_listing_opf_ship_or_none()4323 sexp_list_item* sexp_tree::get_listing_opf_ship_or_none() {
4324 	sexp_list_item head;
4325 
4326 	head.add_data(SEXP_NONE_STRING);
4327 	head.add_list(get_listing_opf_ship());
4328 
4329 	return head.next;
4330 }
4331 
get_listing_opf_subsystem_or_none(int parent_node,int arg_index)4332 sexp_list_item* sexp_tree::get_listing_opf_subsystem_or_none(int parent_node, int arg_index) {
4333 	sexp_list_item head;
4334 
4335 	head.add_data(SEXP_NONE_STRING);
4336 	head.add_list(get_listing_opf_subsystem(parent_node, arg_index));
4337 
4338 	return head.next;
4339 }
4340 
get_listing_opf_subsys_or_generic(int parent_node,int arg_index)4341 sexp_list_item* sexp_tree::get_listing_opf_subsys_or_generic(int parent_node, int arg_index) {
4342 	sexp_list_item head;
4343 
4344 	head.add_data(SEXP_ALL_ENGINES_STRING);
4345 	head.add_data(SEXP_ALL_TURRETS_STRING);
4346 	head.add_list(get_listing_opf_subsystem(parent_node, arg_index));
4347 
4348 	return head.next;
4349 }
4350 
get_listing_opf_jump_nodes()4351 sexp_list_item* sexp_tree::get_listing_opf_jump_nodes() {
4352 	sexp_list_item head;
4353 
4354 	SCP_list<CJumpNode>::iterator jnp;
4355 	for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
4356 		head.add_data(jnp->GetName());
4357 	}
4358 
4359 	return head.next;
4360 }
4361 
4362 // creates list of Sexp_variables
get_listing_opf_variable_names()4363 sexp_list_item* sexp_tree::get_listing_opf_variable_names() {
4364 	int i;
4365 	sexp_list_item head;
4366 
4367 	for (i = 0; i < MAX_SEXP_VARIABLES; i++) {
4368 		if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
4369 			head.add_data(Sexp_variables[i].variable_name);
4370 		}
4371 	}
4372 
4373 	return head.next;
4374 }
4375 
4376 // get default skybox model name
get_listing_opf_skybox_model()4377 sexp_list_item* sexp_tree::get_listing_opf_skybox_model() {
4378 
4379 	sexp_list_item head;
4380 	head.add_data("default");
4381 	return head.next;
4382 }
4383 
get_listing_opf_skybox_flags()4384 sexp_list_item* sexp_tree::get_listing_opf_skybox_flags() {
4385 	sexp_list_item head;
4386 	int i;
4387 
4388 	for (i = 0; i < Num_skybox_flags; ++i) {
4389 		head.add_data(Skybox_flags[i]);
4390 	}
4391 	return head.next;
4392 }
4393 
get_listing_opf_background_bitmap()4394 sexp_list_item* sexp_tree::get_listing_opf_background_bitmap() {
4395 	sexp_list_item head;
4396 	int i;
4397 
4398 	for (i = 0; i < stars_get_num_entries(false, true); i++) {
4399 		head.add_data(stars_get_name_FRED(i, false));
4400 	}
4401 
4402 	return head.next;
4403 }
4404 
get_listing_opf_sun_bitmap()4405 sexp_list_item* sexp_tree::get_listing_opf_sun_bitmap() {
4406 	sexp_list_item head;
4407 	int i;
4408 
4409 	for (i = 0; i < stars_get_num_entries(true, true); i++) {
4410 		head.add_data(stars_get_name_FRED(i, true));
4411 	}
4412 
4413 	return head.next;
4414 }
4415 
get_listing_opf_nebula_storm_type()4416 sexp_list_item* sexp_tree::get_listing_opf_nebula_storm_type() {
4417 	sexp_list_item head;
4418 
4419 	head.add_data(SEXP_NONE_STRING);
4420 
4421 	for (size_t i = 0; i < Storm_types.size(); i++) {
4422 		head.add_data(Storm_types[i].name);
4423 	}
4424 
4425 	return head.next;
4426 }
4427 
get_listing_opf_nebula_poof()4428 sexp_list_item* sexp_tree::get_listing_opf_nebula_poof() {
4429 	sexp_list_item head;
4430 
4431 	for (poof_info &pf : Poof_info) {
4432 		head.add_data(pf.name);
4433 	}
4434 
4435 	return head.next;
4436 }
4437 
get_listing_opf_turret_target_order()4438 sexp_list_item* sexp_tree::get_listing_opf_turret_target_order() {
4439 	int i;
4440 	sexp_list_item head;
4441 
4442 	for (i = 0; i < NUM_TURRET_ORDER_TYPES; i++) {
4443 		head.add_data(Turret_target_order_names[i]);
4444 	}
4445 
4446 	return head.next;
4447 }
4448 
get_listing_opf_post_effect()4449 sexp_list_item* sexp_tree::get_listing_opf_post_effect() {
4450 	unsigned int i;
4451 	sexp_list_item head;
4452 
4453 	SCP_vector<SCP_string> ppe_names;
4454 	gr_get_post_process_effect_names(ppe_names);
4455 	for (i = 0; i < ppe_names.size(); i++) {
4456 		head.add_data_dup(ppe_names[i].c_str());
4457 	}
4458 	head.add_data_dup("lightshafts");
4459 
4460 	return head.next;
4461 }
4462 
4463 
get_listing_opf_turret_target_priorities()4464 sexp_list_item* sexp_tree::get_listing_opf_turret_target_priorities() {
4465 	size_t t;
4466 	sexp_list_item head;
4467 
4468 	for (t = 0; t < Ai_tp_list.size(); t++) {
4469 		head.add_data(Ai_tp_list[t].name);
4470 	}
4471 
4472 	return head.next;
4473 }
4474 
get_listing_opf_armor_type()4475 sexp_list_item* sexp_tree::get_listing_opf_armor_type() {
4476 	size_t t;
4477 	sexp_list_item head;
4478 	head.add_data(SEXP_NONE_STRING);
4479 	for (t = 0; t < Armor_types.size(); t++) {
4480 		head.add_data(Armor_types[t].GetNamePtr());
4481 	}
4482 	return head.next;
4483 }
4484 
get_listing_opf_damage_type()4485 sexp_list_item* sexp_tree::get_listing_opf_damage_type() {
4486 	size_t t;
4487 	sexp_list_item head;
4488 	head.add_data(SEXP_NONE_STRING);
4489 	for (t = 0; t < Damage_types.size(); t++) {
4490 		head.add_data(Damage_types[t].name);
4491 	}
4492 
4493 	return head.next;
4494 }
4495 
get_listing_opf_animation_type()4496 sexp_list_item* sexp_tree::get_listing_opf_animation_type() {
4497 	sexp_list_item head;
4498 
4499 	for (const auto &animation_type_name: Animation_type_names) {
4500 		head.add_data(animation_type_name.second);
4501 	}
4502 
4503 	return head.next;
4504 }
4505 
get_listing_opf_hud_elements()4506 sexp_list_item* sexp_tree::get_listing_opf_hud_elements() {
4507 	sexp_list_item head;
4508 	head.add_data("warpout");
4509 
4510 	return head.next;
4511 }
4512 
get_listing_opf_weapon_banks()4513 sexp_list_item* sexp_tree::get_listing_opf_weapon_banks() {
4514 	sexp_list_item head;
4515 	head.add_data(SEXP_ALL_BANKS_STRING);
4516 
4517 	return head.next;
4518 }
4519 
get_listing_opf_mission_moods()4520 sexp_list_item* sexp_tree::get_listing_opf_mission_moods() {
4521 	sexp_list_item head;
4522 	for (SCP_vector<SCP_string>::iterator iter = Builtin_moods.begin(); iter != Builtin_moods.end(); ++iter) {
4523 		head.add_data_dup(iter->c_str());
4524 	}
4525 
4526 	return head.next;
4527 }
4528 
get_listing_opf_ship_flags()4529 sexp_list_item* sexp_tree::get_listing_opf_ship_flags() {
4530 	int i;
4531 	sexp_list_item head;
4532 	// object flags
4533 	for (i = 0; i < MAX_OBJECT_FLAG_NAMES; i++) {
4534 		head.add_data_dup(Object_flag_names[i].flag_name);
4535 	}
4536 	// ship flags
4537 	for (i = 0; i < MAX_SHIP_FLAG_NAMES; i++) {
4538 		head.add_data_dup(Ship_flag_names[i].flag_name);
4539 	}
4540 	// ai flags
4541 	for (i = 0; i < MAX_AI_FLAG_NAMES; i++) {
4542 		head.add_data_dup(Ai_flag_names[i].flag_name);
4543 	}
4544 
4545 	return head.next;
4546 }
4547 
get_listing_opf_team_colors()4548 sexp_list_item* sexp_tree::get_listing_opf_team_colors() {
4549 	sexp_list_item head;
4550 	head.add_data("None");
4551 	for (SCP_map<SCP_string, team_color>::iterator tcolor = Team_Colors.begin(); tcolor != Team_Colors.end();
4552 		 ++tcolor) {
4553 		head.add_data_dup(tcolor->first.c_str());
4554 	}
4555 
4556 	return head.next;
4557 }
4558 
get_listing_opf_nebula_patterns()4559 sexp_list_item* sexp_tree::get_listing_opf_nebula_patterns() {
4560 	sexp_list_item head;
4561 
4562 	head.add_data(SEXP_NONE_STRING);
4563 
4564 	for (int i = 0; i < MAX_NEB2_BITMAPS; i++) {
4565 		if (strlen(Neb2_bitmap_filenames[i]) > 0) {
4566 			head.add_data(Neb2_bitmap_filenames[i]);
4567 		}
4568 	}
4569 
4570 	return head.next;
4571 }
4572 
get_listing_opf_game_snds()4573 sexp_list_item* sexp_tree::get_listing_opf_game_snds() {
4574 	sexp_list_item head;
4575 
4576 	head.add_data(SEXP_NONE_STRING);
4577 
4578 	for (SCP_vector<game_snd>::iterator iter = Snds.begin(); iter != Snds.end(); ++iter) {
4579 		if (!can_construe_as_integer(iter->name.c_str())) {
4580 			head.add_data(iter->name.c_str());
4581 		}
4582 	}
4583 
4584 	return head.next;
4585 }
4586 
get_listing_opf_fireball()4587 sexp_list_item *sexp_tree::get_listing_opf_fireball()
4588 {
4589 	sexp_list_item head;
4590 
4591 	for (int i = 0; i < Num_fireball_types; ++i)
4592 	{
4593 		char *unique_id = Fireball_info[i].unique_id;
4594 
4595 		if (strlen(unique_id) > 0)
4596 			head.add_data(unique_id);
4597 	}
4598 
4599 	return head.next;
4600 }
4601 
get_listing_opf_species()4602 sexp_list_item *sexp_tree::get_listing_opf_species()	// NOLINT
4603 {
4604 	sexp_list_item head;
4605 
4606 	for (auto &species : Species_info)
4607 		head.add_data(species.species_name);
4608 
4609 	return head.next;
4610 }
4611 
get_listing_opf_language()4612 sexp_list_item *sexp_tree::get_listing_opf_language()	// NOLINT
4613 {
4614 	sexp_list_item head;
4615 
4616 	for (auto &lang: Lcl_languages)
4617 		head.add_data(lang.lang_name);
4618 
4619 	return head.next;
4620 }
4621 
4622 // Deletes sexp_variable from sexp_tree.
4623 // resets tree to not include given variable, and resets text and type
delete_sexp_tree_variable(const char * var_name)4624 void sexp_tree::delete_sexp_tree_variable(const char* var_name) {
4625 	char search_str[64];
4626 	char replace_text[TOKEN_LENGTH];
4627 
4628 	sprintf(search_str, "%s(", var_name);
4629 
4630 	// store old item index
4631 	int old_item_index = item_index;
4632 
4633 	for (uint idx = 0; idx < tree_nodes.size(); idx++) {
4634 		if (tree_nodes[idx].type & SEXPT_VARIABLE) {
4635 			if (strstr(tree_nodes[idx].text, search_str) != NULL) {
4636 
4637 				// check type is number or string
4638 				Assert((tree_nodes[idx].type & SEXPT_NUMBER) || (tree_nodes[idx].type & SEXPT_STRING));
4639 
4640 				// reset type as not variable
4641 				int type = tree_nodes[idx].type &= ~SEXPT_VARIABLE;
4642 
4643 				// reset text
4644 				if (tree_nodes[idx].type & SEXPT_NUMBER) {
4645 					strcpy_s(replace_text, "number");
4646 				} else {
4647 					strcpy_s(replace_text, "string");
4648 				}
4649 
4650 				// set item_index and replace data
4651 				setCurrentItemIndex(idx);
4652 				replace_data(replace_text, type);
4653 			}
4654 		}
4655 	}
4656 
4657 	// restore item_index
4658 	setCurrentItemIndex(old_item_index);
4659 }
4660 
4661 
4662 // Modify sexp_tree for a change in sexp_variable (name, type, or default value)
modify_sexp_tree_variable(const char * old_name,int sexp_var_index)4663 void sexp_tree::modify_sexp_tree_variable(const char* old_name, int sexp_var_index) {
4664 	char search_str[64];
4665 	int type;
4666 
4667 	Assert(Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_SET);
4668 	Assert((Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER)
4669 			   || (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING));
4670 
4671 	// Get type for sexp_tree node
4672 	if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
4673 		type = (SEXPT_NUMBER | SEXPT_VALID);
4674 	} else {
4675 		type = (SEXPT_STRING | SEXPT_VALID);
4676 	}
4677 
4678 	// store item index;
4679 	int old_item_index = item_index;
4680 
4681 	// Search string in sexp_tree nodes
4682 	sprintf(search_str, "%s(", old_name);
4683 
4684 	for (uint idx = 0; idx < tree_nodes.size(); idx++) {
4685 		if (tree_nodes[idx].type & SEXPT_VARIABLE) {
4686 			if (strstr(tree_nodes[idx].text, search_str) != NULL) {
4687 				// temp set item_index
4688 				item_index = idx;
4689 
4690 				// replace variable data
4691 				replace_variable_data(sexp_var_index, (type | SEXPT_VARIABLE));
4692 			}
4693 		}
4694 	}
4695 
4696 	// restore item_index
4697 	item_index = old_item_index;
4698 }
4699 
4700 
4701 // convert from item_index to sexp_variable index, -1 if not
get_item_index_to_var_index()4702 int sexp_tree::get_item_index_to_var_index() {
4703 	// check valid item index and node is a variable
4704 	if ((item_index > 0) && (tree_nodes[item_index].type & SEXPT_VARIABLE)) {
4705 
4706 		return get_tree_name_to_sexp_variable_index(tree_nodes[item_index].text);
4707 	} else {
4708 		return -1;
4709 	}
4710 }
4711 
get_tree_name_to_sexp_variable_index(const char * tree_name)4712 int sexp_tree::get_tree_name_to_sexp_variable_index(const char* tree_name) {
4713 	char var_name[TOKEN_LENGTH];
4714 
4715 	auto chars_to_copy = strcspn(tree_name, "(");
4716 	Assert(chars_to_copy < TOKEN_LENGTH - 1);
4717 
4718 	// Copy up to '(' and add null termination
4719 	strncpy(var_name, tree_name, chars_to_copy);
4720 	var_name[chars_to_copy] = '\0';
4721 
4722 	// Look up index
4723 	return get_index_sexp_variable_name(var_name);
4724 }
4725 
get_variable_count(const char * var_name)4726 int sexp_tree::get_variable_count(const char* var_name) {
4727 	uint idx;
4728 	int count = 0;
4729 	char compare_name[64];
4730 
4731 	// get name to compare
4732 	strcpy_s(compare_name, var_name);
4733 	strcat_s(compare_name, "(");
4734 
4735 	// look for compare name
4736 	for (idx = 0; idx < tree_nodes.size(); idx++) {
4737 		if (tree_nodes[idx].type & SEXPT_VARIABLE) {
4738 			if (strstr(tree_nodes[idx].text, compare_name)) {
4739 				count++;
4740 			}
4741 		}
4742 	}
4743 
4744 	return count;
4745 }
4746 
4747 // Returns the number of times a variable with this name has been used by player loadout
get_loadout_variable_count(int var_index)4748 int sexp_tree::get_loadout_variable_count(int var_index) {
4749 	// we shouldn't be being passed the index of variables that do not exist
4750 	Assert (var_index >= 0 && var_index < MAX_SEXP_VARIABLES);
4751 
4752 	int idx;
4753 	int count = 0;
4754 
4755 	for (int i = 0; i < MAX_TVT_TEAMS; i++) {
4756 		for (idx = 0; idx < Team_data[i].num_ship_choices; idx++) {
4757 			if (!strcmp(Team_data[i].ship_list_variables[idx], Sexp_variables[var_index].variable_name)) {
4758 				count++;
4759 			}
4760 
4761 			if (!strcmp(Team_data[i].ship_count_variables[idx], Sexp_variables[var_index].variable_name)) {
4762 				count++;
4763 			}
4764 		}
4765 
4766 		for (idx = 0; idx < Team_data[i].num_weapon_choices; idx++) {
4767 			if (!strcmp(Team_data[i].weaponry_pool_variable[idx], Sexp_variables[var_index].variable_name)) {
4768 				count++;
4769 			}
4770 			if (!strcmp(Team_data[i].weaponry_amount_variable[idx], Sexp_variables[var_index].variable_name)) {
4771 				count++;
4772 			}
4773 		}
4774 	}
4775 
4776 	return count;
4777 }
initializeEditor(::fso::fred::Editor * edit,SexpTreeEditorInterface * editorInterface)4778 void sexp_tree::initializeEditor(::fso::fred::Editor* edit, SexpTreeEditorInterface* editorInterface) {
4779 	if (editorInterface == nullptr) {
4780 		// If there is no special interface then we supply the default implementation
4781 		_owned_interface.reset(new SexpTreeEditorInterface());
4782 		editorInterface = _owned_interface.get();
4783 	}
4784 
4785 	_editor = edit;
4786 	_interface = editorInterface;
4787 }
customMenuHandler(const QPoint & pos)4788 void sexp_tree::customMenuHandler(const QPoint& pos) {
4789 	QTreeWidgetItem* h = this->itemAt(pos);
4790 
4791 	if (h == nullptr) {
4792 		return;
4793 	}
4794 
4795 	auto menu = buildContextMenu(h);
4796 
4797 	menu->exec(mapToGlobal(pos));
4798 }
buildContextMenu(QTreeWidgetItem * h)4799 std::unique_ptr<QMenu> sexp_tree::buildContextMenu(QTreeWidgetItem* h) {
4800 	int i, j, z, count, op, add_type, replace_type, type, subcategory_id;
4801 	sexp_list_item* list;
4802 
4803 	add_instance = replace_instance = -1;
4804 	Assert(Operators.size() <= MAX_OPERATORS);
4805 	Assert((int) op_menu.size() < MAX_OP_MENUS);
4806 	Assert((int) op_submenu.size() < MAX_SUBMENUS);
4807 
4808 	std::unique_ptr<QMenu> popup_menu(new QMenu(tr("Edit SEXP tree")));
4809 
4810 	auto delete_act =
4811 		popup_menu->addAction(tr("&Delete Item"), this, [this]() { deleteActionHandler(); }, QKeySequence::Delete);
4812 	auto edit_data_act = popup_menu->addAction(tr("&Edit Data"), this, [this]() { editDataActionHandler(); });
4813 	popup_menu->addAction(tr("Expand All"), this, [this]() { expand_branch(currentItem()); });
4814 
4815 	popup_menu->addSection(tr("Copy operations"));
4816 	auto cut_act = popup_menu->addAction(tr("Cut"), this, [this]() { cutActionHandler(); }, QKeySequence::Cut);
4817 	cut_act->setEnabled(false);
4818 	auto copy_act = popup_menu->addAction(tr("Copy"), this, [this]() { copyActionHandler(); }, QKeySequence::Copy);
4819 	auto paste_act = popup_menu->addAction(tr("Paste"), this, [this]() { pasteActionHandler(); }, QKeySequence::Paste);
4820 	paste_act->setEnabled(false);
4821 
4822 	popup_menu->addSection(tr("Add"));
4823 	auto add_op_menu = popup_menu->addMenu(tr("Add Operator"));
4824 
4825 	auto add_data_menu = popup_menu->addMenu(tr("Add Data"));
4826 	auto add_number_act = add_data_menu->addAction(tr("Number"), this, [this]() { addNumberDataHandler(); });
4827 	add_number_act->setEnabled(false);
4828 	auto add_string_act = add_data_menu->addAction(tr("String"), this, [this]() { addStringDataHandler(); });
4829 	add_string_act->setEnabled(false);
4830 	add_data_menu->addSeparator();
4831 
4832 	popup_menu->addSeparator();
4833 	auto add_paste_act = popup_menu->addAction(tr("Add Paste"), this, [this]() { addPasteActionHandler(); });
4834 	add_paste_act->setEnabled(false);
4835 	popup_menu->addSeparator();
4836 
4837 	auto insert_op_menu = popup_menu->addMenu(tr("Insert Operator"));
4838 	popup_menu->addSeparator();
4839 
4840 	auto replace_op_menu = popup_menu->addMenu(tr("Replace Operator"));
4841 
4842 	auto replace_data_menu = popup_menu->addMenu(tr("Replace Data"));
4843 	auto replace_number_act = replace_data_menu->addAction(tr("Number"), this, []() {});
4844 	replace_number_act->setEnabled(false);
4845 	auto replace_string_act = replace_data_menu->addAction(tr("String"), this, []() {});
4846 	replace_string_act->setEnabled(false);
4847 	replace_data_menu->addSeparator();
4848 
4849 	popup_menu->addSection("Variables");
4850 
4851 	popup_menu->addAction(tr("Add Variable"), this, []() {});
4852 	auto modify_variable_act = popup_menu->addAction(tr("Modify Variable"), this, []() {});
4853 
4854 	auto replace_variable_menu = popup_menu->addMenu(tr("Replace Variable"));
4855 
4856 	update_help(h);
4857 	//SelectDropTarget(h);  // WTF: Why was this here???
4858 
4859 	// get item_index
4860 	item_index = -1;
4861 	for (i = 0; i < (int) tree_nodes.size(); i++) {
4862 		if (tree_nodes[i].handle == h) {
4863 			setCurrentItemIndex(i);
4864 			break;
4865 		}
4866 	}
4867 
4868 	// check not root (-1)
4869 	if (item_index >= 0) {
4870 		// get type of sexp_tree item clicked on
4871 		type = get_type(h);
4872 
4873 		int parent = tree_nodes[item_index].parent;
4874 		if (parent >= 0) {
4875 			op = get_operator_index(tree_nodes[parent].text);
4876 			Assert(op >= 0);
4877 			int first_arg = tree_nodes[parent].child;
4878 
4879 			// get arg count of item to replace
4880 			Replace_count = 0;
4881 			int temp = first_arg;
4882 			while (temp != item_index) {
4883 				Replace_count++;
4884 				temp = tree_nodes[temp].next;
4885 
4886 				// DB - added 3/4/99
4887 				if (temp == -1) {
4888 					break;
4889 				}
4890 			}
4891 
4892 			int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position
4893 
4894 			// special case don't allow replace data for variable names
4895 			// Goober5000 - why?  the only place this happens is when replacing the ambiguous argument in
4896 			// modify-variable with a variable, which seems legal enough.
4897 			//if (op_type != OPF_AMBIGUOUS) {
4898 
4899 			// Goober5000 - given the above, we have to figure out what type this stands for
4900 			if (op_type == OPF_AMBIGUOUS) {
4901 				int modify_type = get_modify_variable_type(parent);
4902 				if (modify_type == OPF_NUMBER) {
4903 					type = SEXPT_NUMBER;
4904 				} else if (modify_type == OPF_AMBIGUOUS) {
4905 					type = SEXPT_STRING;
4906 				} else {
4907 					Int3();
4908 					type = tree_nodes[first_arg].type;
4909 				}
4910 			}
4911 
4912 			// Goober5000 - certain types accept both integers and a list of strings
4913 			if (op_type == OPF_GAME_SND || op_type == OPF_FIREBALL || op_type == OPF_WEAPON_BANK_NUMBER) {
4914 				type = SEXPT_NUMBER | SEXPT_STRING;
4915 			}
4916 
4917 			if ((type & SEXPT_STRING) || (type & SEXPT_NUMBER)) {
4918 
4919 				int max_sexp_vars = MAX_SEXP_VARIABLES;
4920 				// prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU
4921 				Assert(max_sexp_vars < 512);
4922 
4923 				for (int idx = 0; idx < max_sexp_vars; idx++) {
4924 					if (Sexp_variables[idx].type & SEXP_VARIABLE_SET) {
4925 						// skip block variables
4926 						if (Sexp_variables[idx].type & SEXP_VARIABLE_BLOCK) {
4927 							continue;
4928 						}
4929 
4930 						auto disabled = true;
4931 						// maybe gray flag MF_GRAYED
4932 
4933 						// get type -- gray "string" or number accordingly
4934 						if (type & SEXPT_STRING) {
4935 							if (Sexp_variables[idx].type & SEXP_VARIABLE_STRING) {
4936 								disabled = false;
4937 							}
4938 						}
4939 						if (type & SEXPT_NUMBER) {
4940 							if (Sexp_variables[idx].type & SEXP_VARIABLE_NUMBER) {
4941 								disabled = false;
4942 							}
4943 						}
4944 
4945 						// if modify-variable and changing variable, enable all variables
4946 						if (op_type == OPF_VARIABLE_NAME) {
4947 							Modify_variable = 1;
4948 							disabled = false;
4949 						} else {
4950 							Modify_variable = 0;
4951 						}
4952 
4953 						// enable navsystem always
4954 						if (op_type == OPF_NAV_POINT) {
4955 							disabled = false;
4956 						}
4957 
4958 						char buf[128];
4959 						// append list of variable names and values
4960 						// set id as ID_VARIABLE_MENU + idx
4961 						sprintf(buf, "%s (%s)", Sexp_variables[idx].variable_name, Sexp_variables[idx].text);
4962 
4963 						auto action = replace_variable_menu->addAction(QString::fromUtf8(buf),
4964 																	   this,
4965 																	   [this, idx]() { handleReplaceVariableAction(idx); });
4966 						action->setEnabled(!disabled);
4967 					}
4968 				}
4969 			}
4970 			//}
4971 		}
4972 	}
4973 
4974 	// can't modify if no variables
4975 	modify_variable_act->setEnabled(sexp_variable_count() > 0);
4976 
4977 	// add popup menus for all the operator categories
4978 	QMenu* add_op_submenu[MAX_OP_MENUS];
4979 	QMenu* replace_op_submenu[MAX_OP_MENUS];
4980 	QMenu* insert_op_submenu[MAX_OP_MENUS];
4981 	for (i = 0; i < (int) op_menu.size(); i++) {
4982 		add_op_submenu[i] = add_op_menu->addMenu(QString::fromStdString(op_menu[i].name.c_str()));
4983 		replace_op_submenu[i] = replace_op_menu->addMenu(QString::fromStdString(op_menu[i].name.c_str()));
4984 		insert_op_submenu[i] = insert_op_menu->addMenu(QString::fromStdString(op_menu[i].name));
4985 	}
4986 
4987 	// add all the submenu items first
4988 	QMenu* add_op_subcategory_menu[MAX_SUBMENUS];
4989 	QMenu* replace_op_subcategory_menu[MAX_SUBMENUS];
4990 	QMenu* insert_op_subcategory_menu[MAX_SUBMENUS];
4991 	for (i = 0; i < (int) op_submenu.size(); i++) {
4992 		for (j = 0; j < (int) op_menu.size(); j++) {
4993 			if (op_menu[j].id == category_of_subcategory(op_submenu[i].id)) {
4994 				add_op_subcategory_menu[i] = add_op_submenu[j]->addMenu(QString::fromStdString(op_submenu[i].name));
4995 				replace_op_subcategory_menu[i] =
4996 					replace_op_submenu[j]->addMenu(QString::fromStdString(op_submenu[i].name));
4997 				insert_op_subcategory_menu[i] =
4998 					insert_op_submenu[j]->addMenu(QString::fromStdString(op_submenu[i].name));
4999 				break;    // only 1 category valid
5000 			}
5001 		}
5002 	}
5003 
5004 	// The MFC code could use some internal WinAPI IDs for keeping this mapping but that is not available in Qt so we
5005 	// need to do this ourself. This could be improved by applying the actions for the operator actions when the action
5006 	// is added.
5007 	std::unordered_map<int, QAction*> operator_action_mapping;
5008 
5009 	// add operator menu items to the various CATEGORY submenus they belong in
5010 	for (i = 0; i < (int) Operators.size(); i++) {
5011 		// add only if it is not in a subcategory
5012 		subcategory_id = get_subcategory(Operators[i].value);
5013 		if (subcategory_id == -1) {
5014 			// put it in the appropriate menu
5015 			for (j = 0; j < (int) op_menu.size(); j++) {
5016 				if (op_menu[j].id == get_category(Operators[i].value)) {
5017 					switch (Operators[i].value) {
5018 // Commented out by Goober5000 to allow these operators to be selectable
5019 /*#ifdef NDEBUG
5020 						// various campaign operators
5021 						case OP_WAS_PROMOTION_GRANTED:
5022 						case OP_WAS_MEDAL_GRANTED:
5023 						case OP_GRANT_PROMOTION:
5024 						case OP_GRANT_MEDAL:
5025 						case OP_TECH_ADD_SHIP:
5026 						case OP_TECH_ADD_WEAPON:
5027 						case OP_TECH_ADD_INTEL_XSTR:
5028 						case OP_TECH_REMOVE_INTEL_XSTR:
5029 						case OP_TECH_RESET_TO_DEFAULT:
5030 #endif*/
5031 						// unlike the above operators, these are deprecated
5032 					case OP_HITS_LEFT_SUBSYSTEM:
5033 					case OP_CUTSCENES_SHOW_SUBTITLE:
5034 					case OP_ORDER:
5035 					case OP_TECH_ADD_INTEL:
5036 					case OP_TECH_REMOVE_INTEL:
5037 					case OP_HUD_GAUGE_SET_ACTIVE:
5038 					case OP_HUD_ACTIVATE_GAUGE_TYPE:
5039 					case OP_JETTISON_CARGO_DELAY:
5040 					case OP_STRING_CONCATENATE:
5041 					case OP_SET_OBJECT_SPEED_X:
5042 					case OP_SET_OBJECT_SPEED_Y:
5043 					case OP_SET_OBJECT_SPEED_Z:
5044 					case OP_DISTANCE:
5045 					case OP_SCRIPT_EVAL:
5046 						j = (int) op_menu.size();    // don't allow these operators to be visible
5047 						break;
5048 					}
5049 
5050 					if (j < (int) op_menu.size()) {
5051 						auto add_act =
5052 							add_op_submenu[j]->addAction(QString::fromStdString(Operators[i].text), this, [this, i]() {
5053 								add_or_replace_operator(i, 0);
5054 							});
5055 						add_act->setEnabled(false);
5056 						operator_action_mapping.insert(std::make_pair(Operators[i].value, add_act));
5057 
5058 						auto replace_act = replace_op_submenu[j]->addAction(QString::fromStdString(Operators[i].text),
5059 																			this,
5060 																			[this, i]() {
5061 																				add_or_replace_operator(i, 1);
5062 																			});
5063 						replace_act->setEnabled(false);
5064 						operator_action_mapping.insert(std::make_pair(Operators[i].value | OP_REPLACE_FLAG,
5065 																	  replace_act));
5066 
5067 						auto insert_act = insert_op_submenu[j]->addAction(QString::fromStdString(Operators[i].text),
5068 																		  this,
5069 																		  [this, i]() {
5070 																			  insertOperatorAction(i);
5071 																		  });
5072 						insert_act->setEnabled(true);
5073 						operator_action_mapping.insert(std::make_pair(Operators[i].value | OP_INSERT_FLAG, insert_act));
5074 					}
5075 
5076 					break;    // only 1 category valid
5077 				}
5078 			}
5079 		}
5080 			// if it is in a subcategory, handle it
5081 		else {
5082 			// put it in the appropriate submenu
5083 			for (j = 0; j < (int) op_submenu.size(); j++) {
5084 				if (op_submenu[j].id == subcategory_id) {
5085 					switch (Operators[i].value) {
5086 // Commented out by Goober5000 to allow these operators to be selectable
5087 /*#ifdef NDEBUG
5088 						// various campaign operators
5089 						case OP_WAS_PROMOTION_GRANTED:
5090 						case OP_WAS_MEDAL_GRANTED:
5091 						case OP_GRANT_PROMOTION:
5092 						case OP_GRANT_MEDAL:
5093 						case OP_TECH_ADD_SHIP:
5094 						case OP_TECH_ADD_WEAPON:
5095 						case OP_TECH_ADD_INTEL_XSTR:
5096 						case OP_TECH_REMOVE_INTEL_XSTR:
5097 						case OP_TECH_RESET_TO_DEFAULT:
5098 #endif*/
5099 						// unlike the above operators, these are deprecated
5100 					case OP_HITS_LEFT_SUBSYSTEM:
5101 					case OP_CUTSCENES_SHOW_SUBTITLE:
5102 					case OP_ORDER:
5103 					case OP_TECH_ADD_INTEL:
5104 					case OP_TECH_REMOVE_INTEL:
5105 					case OP_HUD_GAUGE_SET_ACTIVE:
5106 					case OP_HUD_ACTIVATE_GAUGE_TYPE:
5107 					case OP_JETTISON_CARGO_DELAY:
5108 					case OP_STRING_CONCATENATE:
5109 					case OP_SET_OBJECT_SPEED_X:
5110 					case OP_SET_OBJECT_SPEED_Y:
5111 					case OP_SET_OBJECT_SPEED_Z:
5112 					case OP_DISTANCE:
5113 					case OP_SCRIPT_EVAL:
5114 						j = (int) op_submenu.size();    // don't allow these operators to be visible
5115 						break;
5116 					}
5117 
5118 					if (j < (int) op_submenu.size()) {
5119 						auto add_act = add_op_subcategory_menu[j]->addAction(QString::fromStdString(Operators[i].text),
5120 																			 this,
5121 																			 [this, i]() {
5122 																				 add_or_replace_operator(i, 0);
5123 																			 });
5124 						add_act->setEnabled(false);
5125 						operator_action_mapping.insert(std::make_pair(Operators[i].value, add_act));
5126 
5127 						auto replace_act =
5128 							replace_op_subcategory_menu[j]->addAction(QString::fromStdString(Operators[i].text),
5129 																	  this,
5130 																	  [this, i]() {
5131 																		  add_or_replace_operator(i, 1);
5132 																	  });
5133 						replace_act->setEnabled(false);
5134 						operator_action_mapping.insert(std::make_pair(Operators[i].value | OP_REPLACE_FLAG,
5135 																	  replace_act));
5136 
5137 						auto insert_act =
5138 							insert_op_subcategory_menu[j]->addAction(QString::fromStdString(Operators[i].text),
5139 																	 this,
5140 																	 [this, i]() {
5141 																		 insertOperatorAction(i);
5142 																	 });
5143 						insert_act->setEnabled(true);
5144 						operator_action_mapping.insert(std::make_pair(Operators[i].value | OP_INSERT_FLAG, insert_act));
5145 					}
5146 
5147 					break;    // only 1 subcategory valid
5148 				}
5149 			}
5150 		}
5151 	}
5152 
5153 	// find local index (i) of current item (from its handle)
5154 	for (i = 0; i < (int) tree_nodes.size(); i++) {
5155 		if (tree_nodes[i].handle == h) {
5156 			break;
5157 		}
5158 	}
5159 
5160 	// special case: item is a ROOT node, and a label that can be edited (not an item in the sexp tree)
5161 	if ((item_index == -1) && _interface->getFlags()[TreeFlags::LabeledRoot]) {
5162 		edit_data_act->setEnabled(_interface->getFlags()[TreeFlags::RootEditable]);
5163 
5164 		// disable copy, insert op
5165 		copy_act->setEnabled(false);
5166 		insert_op_menu->setEnabled(false);
5167 		/*
5168 		for (j = 0; j < (int) Operators.size(); j++) {
5169 			menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
5170 		}
5171 		*/
5172 
5173 		util::propagate_disabled_status(popup_menu.get());
5174 		return popup_menu;
5175 	}
5176 
5177 	Assert(item_index != -1);  // handle not found, which should be impossible.
5178 	if (!(tree_nodes[item_index].flags & EDITABLE)) {
5179 		edit_data_act->setEnabled(false);
5180 	}
5181 
5182 	if (tree_nodes[item_index].parent == -1) {  // root node
5183 		delete_act->setEnabled(false); // can't delete the root item.
5184 	}
5185 
5186 /*		if ((tree_nodes[item_index].flags & OPERAND) && (tree_nodes[item_index].flags & EDITABLE))  // expandable?
5187 		menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
5188 
5189 	z = tree_nodes[item_index].child;
5190 	if (z != -1 && tree_nodes[z].next == -1 && tree_nodes[z].child == -1)
5191 		menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
5192 
5193 	z = tree_nodes[tree_nodes[item_index].parent].child;
5194 	if (z != -1 && tree_nodes[z].next == -1 && tree_nodes[z].child == -1)
5195 		menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/
5196 
5197 	// change enabled status of 'add' type menu options.
5198 	add_type = 0;
5199 	if (tree_nodes[item_index].flags & OPERAND) {
5200 		add_type = OPR_STRING;
5201 		int child = tree_nodes[item_index].child;
5202 		Add_count = count_args(child);
5203 		op = get_operator_index(tree_nodes[item_index].text);
5204 		Assert(op >= 0);
5205 
5206 		// get listing of valid argument values and add to menus
5207 		type = query_operator_argument_type(op, Add_count);
5208 		list = get_listing_opf(type, item_index, Add_count);
5209 		if (list) {
5210 			sexp_list_item* ptr;
5211 
5212 			int data_idx = 0;
5213 			ptr = list;
5214 			while (ptr) {
5215 				if (ptr->op >= 0) {
5216 					// enable operators with correct return type
5217 					auto iter = operator_action_mapping.find(Operators[ptr->op].value);
5218 					if (iter != operator_action_mapping.end()) {
5219 						iter->second->setEnabled(true);
5220 					}
5221 				} else {
5222 					// add data
5223 					add_data_menu->addAction(QString::fromStdString(ptr->text),
5224 											 this,
5225 											 [this, data_idx]() { addReplaceTypedDataHandler(data_idx, false); });
5226 				}
5227 
5228 				data_idx++;
5229 				ptr = ptr->next;
5230 			}
5231 		}
5232 
5233 		// special handling for the non-string formats
5234 		if (type == OPF_NONE) {  // an argument can't be added
5235 			add_type = 0;
5236 
5237 		} else if (type == OPF_NULL) {  // arguments with no return values
5238 			add_type = OPR_NULL;
5239 
5240 			// Goober5000
5241 		} else if (type == OPF_FLEXIBLE_ARGUMENT) {
5242 			add_type = OPR_FLEXIBLE_ARGUMENT;
5243 
5244 		} else if (type == OPF_NUMBER) {  // takes numbers
5245 			add_type = OPR_NUMBER;
5246 			add_number_act->setEnabled(true);
5247 		} else if (type == OPF_POSITIVE) {  // takes non-negative numbers
5248 			add_type = OPR_POSITIVE;
5249 			add_number_act->setEnabled(true);
5250 		} else if (type == OPF_BOOL) {  // takes true/false bool values
5251 			add_type = OPR_BOOL;
5252 
5253 		} else if (type == OPF_AI_GOAL) {
5254 			add_type = OPR_AI_GOAL;
5255 		}
5256 
5257 		// add_type unchanged from above
5258 		if (add_type == OPR_STRING) {
5259 			add_string_act->setEnabled(true);
5260 		}
5261 
5262 		list->destroy();
5263 	}
5264 
5265 	// disable operators that do not have arguments available
5266 	for (j = 0; j < (int) Operators.size(); j++) {
5267 		if (!query_default_argument_available(j)) {
5268 			auto iter = operator_action_mapping.find(Operators[j].value);
5269 			if (iter != operator_action_mapping.end()) {
5270 				iter->second->setEnabled(false);
5271 			}
5272 		}
5273 	}
5274 
5275 
5276 	// change enabled status of 'replace' type menu options.
5277 	replace_type = 0;
5278 	int parent = tree_nodes[item_index].parent;
5279 	if (parent >= 0) {
5280 		replace_type = OPR_STRING;
5281 		op = get_operator_index(tree_nodes[parent].text);
5282 		Assert(op >= 0);
5283 		int first_arg = tree_nodes[parent].child;
5284 		count = count_args(tree_nodes[parent].child);
5285 
5286 		// already at minimum number of arguments?
5287 		if (count <= Operators[op].min) {
5288 			delete_act->setEnabled(false);
5289 		}
5290 
5291 		// get arg count of item to replace
5292 		Replace_count = 0;
5293 		int temp = first_arg;
5294 		while (temp != item_index) {
5295 			Replace_count++;
5296 			temp = tree_nodes[temp].next;
5297 
5298 			// DB - added 3/4/99
5299 			if (temp == -1) {
5300 				break;
5301 			}
5302 		}
5303 
5304 		// maybe gray delete
5305 		for (i = Replace_count + 1; i < count; i++) {
5306 			if (query_operator_argument_type(op, i - 1) != query_operator_argument_type(op, i)) {
5307 				delete_act->setEnabled(false);
5308 				break;
5309 			}
5310 		}
5311 
5312 		type = query_operator_argument_type(op, Replace_count); // check argument type at this position
5313 
5314 		// special case reset type for ambiguous
5315 		if (type == OPF_AMBIGUOUS) {
5316 			type = get_modify_variable_type(parent);
5317 		}
5318 
5319 		list = get_listing_opf(type, parent, Replace_count);
5320 
5321 		// special case don't allow replace data for variable names
5322 		if ((type != OPF_VARIABLE_NAME) && list) {
5323 			sexp_list_item* ptr;
5324 
5325 			int data_idx = 0;
5326 			ptr = list;
5327 			while (ptr) {
5328 				if (ptr->op >= 0) {
5329 					auto iter = operator_action_mapping.find(Operators[ptr->op].value | OP_REPLACE_FLAG);
5330 					if (iter != operator_action_mapping.end()) {
5331 						iter->second->setEnabled(true);
5332 					}
5333 				} else {
5334 					replace_data_menu->addAction(QString::fromStdString(ptr->text),
5335 											 this,
5336 											 [this, data_idx]() { addReplaceTypedDataHandler(data_idx, true); });
5337 				}
5338 
5339 				data_idx++;
5340 				ptr = ptr->next;
5341 			}
5342 		}
5343 
5344 		if (type == OPF_NONE) {  // takes no arguments
5345 			replace_type = 0;
5346 
5347 		} else if (type == OPF_NUMBER) {  // takes numbers
5348 			replace_type = OPR_NUMBER;
5349 			replace_number_act->setEnabled(true);
5350 		} else if (type == OPF_POSITIVE) {  // takes non-negative numbers
5351 			replace_type = OPR_POSITIVE;
5352 			replace_number_act->setEnabled(true);
5353 		} else if (type == OPF_BOOL) {  // takes true/false bool values
5354 			replace_type = OPR_BOOL;
5355 
5356 		} else if (type == OPF_NULL) {  // takes operator that doesn't return a value
5357 			replace_type = OPR_NULL;
5358 		} else if (type == OPF_AI_GOAL) {
5359 			replace_type = OPR_AI_GOAL;
5360 		}
5361 
5362 			// Goober5000
5363 		else if (type == OPF_FLEXIBLE_ARGUMENT) {
5364 			replace_type = OPR_FLEXIBLE_ARGUMENT;
5365 		}
5366 			// Goober5000
5367 		else if (type == OPF_GAME_SND || type == OPF_FIREBALL || type == OPF_WEAPON_BANK_NUMBER) {
5368 			// even though these default to strings, we allow replacing them with index values
5369 			replace_type = OPR_POSITIVE;
5370 			replace_number_act->setEnabled(true);
5371 		}
5372 
5373 		// default to string
5374 		if (replace_type == OPR_STRING) {
5375 			replace_string_act->setEnabled(true);
5376 		}
5377 
5378 		// modify string or number if (modify_variable)
5379 		if (Operators[op].value == OP_MODIFY_VARIABLE) {
5380 			int modify_type = get_modify_variable_type(parent);
5381 
5382 			if (modify_type == OPF_NUMBER) {
5383 				replace_number_act->setEnabled(true);
5384 				replace_string_act->setEnabled(false);
5385 			}
5386 			// no change for string type
5387 		} else if (Operators[op].value == OP_SET_VARIABLE_BY_INDEX) {
5388 			// it depends on which argument we are modifying
5389 			// first argument is always a number
5390 			if (Replace_count == 0) {
5391 				replace_number_act->setEnabled(true);
5392 				replace_string_act->setEnabled(false);
5393 			}
5394 				// second argument could be anything
5395 			else {
5396 				int modify_type = get_modify_variable_type(parent);
5397 
5398 				if (modify_type == OPF_NUMBER) {
5399 					replace_number_act->setEnabled(true);
5400 					replace_string_act->setEnabled(false);
5401 				}
5402 				// no change for string type
5403 			}
5404 		}
5405 
5406 		list->destroy();
5407 
5408 	} else {  // top node, so should be a Boolean type.
5409 		replace_type = _interface->getRootReturnType();
5410 		for (j = 0; j < (int) Operators.size(); j++) {
5411 			if (query_operator_return_type(j) == replace_type) {
5412 				auto iter = operator_action_mapping.find(Operators[j].value | OP_REPLACE_FLAG);
5413 				if (iter != operator_action_mapping.end()) {
5414 					iter->second->setEnabled(true);
5415 				}
5416 			}
5417 		}
5418 	}
5419 
5420 	// disable operators that do not have arguments available
5421 	for (j = 0; j < (int) Operators.size(); j++) {
5422 		if (!query_default_argument_available(j)) {
5423 			auto iter = operator_action_mapping.find(Operators[j].value | OP_REPLACE_FLAG);
5424 			if (iter != operator_action_mapping.end()) {
5425 				iter->second->setEnabled(false);
5426 			}
5427 		}
5428 	}
5429 
5430 
5431 	// change enabled status of 'insert' type menu options.
5432 	z = tree_nodes[item_index].parent;
5433 	Assert(z >= -1);
5434 	if (z != -1) {
5435 		op = get_operator_index(tree_nodes[z].text);
5436 		Assert(op != -1);
5437 		j = tree_nodes[z].child;
5438 		count = 0;
5439 		while (j != item_index) {
5440 			count++;
5441 			j = tree_nodes[j].next;
5442 		}
5443 
5444 		type = query_operator_argument_type(op, count); // check argument type at this position
5445 
5446 	} else {
5447 		type = _interface->getRootReturnType();
5448 	}
5449 
5450 	for (j = 0; j < (int) Operators.size(); j++) {
5451 		z = query_operator_return_type(j);
5452 		if (!sexp_query_type_match(type, z) || (Operators[j].min < 1)) {
5453 			auto iter = operator_action_mapping.find(Operators[j].value | OP_INSERT_FLAG);
5454 			if (iter != operator_action_mapping.end()) {
5455 				iter->second->setEnabled(false);
5456 			}
5457 		}
5458 
5459 		z = query_operator_argument_type(j, 0);
5460 		if ((type == OPF_NUMBER) && (z == OPF_POSITIVE)) {
5461 			z = OPF_NUMBER;
5462 		}
5463 
5464 		// Goober5000's number hack
5465 		if ((type == OPF_POSITIVE) && (z == OPF_NUMBER)) {
5466 			z = OPF_POSITIVE;
5467 		}
5468 
5469 		if (z != type) {
5470 			auto iter = operator_action_mapping.find(Operators[j].value | OP_INSERT_FLAG);
5471 			if (iter != operator_action_mapping.end()) {
5472 				iter->second->setEnabled(false);
5473 			}
5474 		}
5475 	}
5476 
5477 	// disable operators that do not have arguments available
5478 	for (j = 0; j < (int) Operators.size(); j++) {
5479 		if (!query_default_argument_available(j)) {
5480 			auto iter = operator_action_mapping.find(Operators[j].value | OP_INSERT_FLAG);
5481 			if (iter != operator_action_mapping.end()) {
5482 				iter->second->setEnabled(false);
5483 			}
5484 		}
5485 	}
5486 
5487 
5488 	// disable non campaign operators if in campaign mode
5489 	for (j = 0; j < (int) Operators.size(); j++) {
5490 		z = 0;
5491 		if (_interface->requireCampaignOperators()) {
5492 			if (Operators[j].value & OP_NONCAMPAIGN_FLAG) {
5493 				z = 1;
5494 			}
5495 
5496 		} else {
5497 			if (Operators[j].value & OP_CAMPAIGN_ONLY_FLAG) {
5498 				z = 1;
5499 			}
5500 		}
5501 
5502 		if (z) {
5503 			auto iter = operator_action_mapping.find(Operators[j].value);
5504 			if (iter != operator_action_mapping.end()) {
5505 				iter->second->setEnabled(false);
5506 			}
5507 			iter = operator_action_mapping.find(Operators[j].value | OP_REPLACE_FLAG);
5508 			if (iter != operator_action_mapping.end()) {
5509 				iter->second->setEnabled(false);
5510 			}
5511 			iter = operator_action_mapping.find(Operators[j].value | OP_INSERT_FLAG);
5512 			if (iter != operator_action_mapping.end()) {
5513 				iter->second->setEnabled(false);
5514 			}
5515 		}
5516 	}
5517 
5518 	if ((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) {
5519 		Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
5520 
5521 		if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
5522 			j = get_operator_const(CTEXT(Sexp_clipboard));
5523 			Assert(j);
5524 			z = query_operator_return_type(j);
5525 
5526 			if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER)) {
5527 				z = OPR_NUMBER;
5528 			}
5529 
5530 			// Goober5000's number hack
5531 			if ((z == OPR_NUMBER) && (replace_type == OPR_POSITIVE)) {
5532 				z = OPR_POSITIVE;
5533 			}
5534 
5535 			if (replace_type == z) {
5536 				paste_act->setEnabled(true);
5537 			}
5538 
5539 			z = query_operator_return_type(j);
5540 			if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER)) {
5541 				z = OPR_NUMBER;
5542 			}
5543 
5544 			if (add_type == z) {
5545 				add_paste_act->setEnabled(true);
5546 			}
5547 
5548 		} else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
5549 			if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1)) {
5550 				edit_data_act->setEnabled(true);
5551 			} else if (replace_type == OPR_NUMBER) {
5552 				edit_data_act->setEnabled(true);
5553 			}
5554 
5555 			if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1)) {
5556 				add_paste_act->setEnabled(true);
5557 			} else if (add_type == OPR_NUMBER) {
5558 				add_paste_act->setEnabled(true);
5559 			}
5560 
5561 		} else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
5562 			if (replace_type == OPR_STRING) {
5563 				edit_data_act->setEnabled(true);
5564 			}
5565 
5566 			if (add_type == OPR_STRING) {
5567 				add_paste_act->setEnabled(true);
5568 			}
5569 
5570 		} else
5571 			Int3();  // unknown and/or invalid sexp type
5572 	}
5573 
5574 	if (delete_act->isEnabled()) {
5575 		cut_act->setEnabled(true);
5576 	}
5577 
5578 	util::propagate_disabled_status(popup_menu.get());
5579 	return popup_menu;
5580 }
cutActionHandler()5581 void sexp_tree::cutActionHandler() {
5582 	if (Sexp_clipboard != -1) {
5583 		sexp_unmark_persistent(Sexp_clipboard);
5584 		free_sexp2(Sexp_clipboard);
5585 	}
5586 
5587 	Sexp_clipboard = save_branch(item_index, 1);
5588 	sexp_mark_persistent(Sexp_clipboard);
5589 
5590 	// fall through to ID_DELETE case.
5591 	deleteActionHandler();
5592 }
deleteActionHandler()5593 void sexp_tree::deleteActionHandler() {
5594 	if (currentItem() == nullptr) {
5595 		return;
5596 	}
5597 
5598 	if (_interface->getFlags()[TreeFlags::RootDeletable] && (item_index == -1)) {
5599 		auto item = currentItem();
5600 		item_index = item->data(0, FormulaDataRole).toInt();
5601 
5602 		rootNodeDeleted(item_index);
5603 
5604 		free_node2(item_index);
5605 		delete item;
5606 		modified();
5607 		return;
5608 	}
5609 
5610 	Assert(item_index >= 0);
5611 	auto h_parent = currentItem()->parent();
5612 	auto parent = tree_nodes[item_index].parent;
5613 
5614 	Assert(parent != -1 && tree_nodes[parent].handle == h_parent);
5615 	free_node(item_index);
5616 	delete currentItem();
5617 
5618 	modified();
5619 }
editDataActionHandler()5620 void sexp_tree::editDataActionHandler() {
5621 	beginItemEdit(currentItem());
5622 }
handleItemChange(QTreeWidgetItem * item,int)5623 void sexp_tree::handleItemChange(QTreeWidgetItem* item, int  /*column*/) {
5624 	if (!_currently_editing) {
5625 		return;
5626 	}
5627 	_currently_editing = false;
5628 
5629 	auto str = item->text(0);
5630 	bool update_node = true;
5631 	uint node;
5632 
5633 	if (str.isEmpty()) {
5634 		return;
5635 	}
5636 
5637 	for (node = 0; node < tree_nodes.size(); node++) {
5638 		if (tree_nodes[node].handle == item) {
5639 			break;
5640 		}
5641 	}
5642 
5643 	if (node == tree_nodes.size()) {
5644 		setCurrentItemIndex(qvariant_cast<int>(item->data(0, FormulaDataRole)));
5645 
5646 		rootNodeRenamed(item_index);
5647 
5648 		return;
5649 	}
5650 
5651 	Assert(node < tree_nodes.size());
5652 	if (tree_nodes[node].type & SEXPT_OPERATOR) {
5653 		auto op = match_closest_operator(str.toStdString(), node);
5654 		if (op.empty()) {
5655 			return;
5656 		}    // Goober5000 - avoids crashing
5657 
5658 		// use the text of the operator we found
5659 		str = QString::fromStdString(op);
5660 		item->setText(0, str);
5661 
5662 		setCurrentItemIndex(node);
5663 		int op_num = get_operator_index(op.c_str());
5664 		if (op_num >= 0) {
5665 			add_or_replace_operator(op_num, 1);
5666 		} else {
5667 			update_node = false;
5668 		}
5669 	}
5670 	// gotta sidestep Goober5000's number hack and check entries are actually positive.
5671 	else if (tree_nodes[node].type & SEXPT_NUMBER) {
5672 		if (query_node_argument_type(node) == OPF_POSITIVE) {
5673 			int val = str.toInt();
5674 			if (val < 0) {
5675 				QMessageBox::critical(this, "Invalid Number", "Can not enter a negative value");
5676 				update_node = false;
5677 			}
5678 		}
5679 	}
5680 
5681 	// Error checking would not hurt here
5682 	auto len = str.size();
5683 	if (len >= TOKEN_LENGTH) {
5684 		len = TOKEN_LENGTH - 1;
5685 	}
5686 
5687 	if (update_node) {
5688 		modified();
5689 		strncpy(tree_nodes[node].text, str.toStdString().c_str(), len);
5690 		tree_nodes[node].text[len] = 0;
5691 
5692 		// let's make sure we aren't introducing any invalid characters, per Mantis #2893
5693 		lcl_fred_replace_stuff(tree_nodes[node].text, TOKEN_LENGTH - 1);
5694 	} else {
5695 		item->setText(0, QString::fromUtf8(tree_nodes[node].text, len));
5696 	}
5697 }
copyActionHandler()5698 void sexp_tree::copyActionHandler() {
5699 	// If a clipboard already exist, unmark it as persistent and free old clipboard
5700 	if (Sexp_clipboard != -1) {
5701 		sexp_unmark_persistent(Sexp_clipboard);
5702 		free_sexp2(Sexp_clipboard);
5703 	}
5704 
5705 	// Allocate new clipboard and mark persistent
5706 	Sexp_clipboard = save_branch(item_index, 1);
5707 	sexp_mark_persistent(Sexp_clipboard);
5708 }
pasteActionHandler()5709 void sexp_tree::pasteActionHandler() {
5710 	// the following assumptions are made..
5711 	Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
5712 	Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
5713 
5714 	if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
5715 		expand_operator(item_index);
5716 		replace_operator(CTEXT(Sexp_clipboard));
5717 		if (Sexp_nodes[Sexp_clipboard].rest != -1) {
5718 			load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
5719 			auto i = tree_nodes[item_index].child;
5720 			while (i != -1) {
5721 				add_sub_tree(i, tree_nodes[item_index].handle);
5722 				i = tree_nodes[i].next;
5723 			}
5724 		}
5725 
5726 	} else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
5727 		Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
5728 		if (Sexp_nodes[Sexp_clipboard].type & SEXP_FLAG_VARIABLE) {
5729 			int var_idx = get_index_sexp_variable_name(Sexp_nodes[Sexp_clipboard].text);
5730 			Assert(var_idx > -1);
5731 			replace_variable_data(var_idx, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID));
5732 		} else {
5733 			expand_operator(item_index);
5734 			replace_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
5735 		}
5736 
5737 	} else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
5738 		Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
5739 		if (Sexp_nodes[Sexp_clipboard].type & SEXP_FLAG_VARIABLE) {
5740 			int var_idx = get_index_sexp_variable_name(Sexp_nodes[Sexp_clipboard].text);
5741 			Assert(var_idx > -1);
5742 			replace_variable_data(var_idx, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID));
5743 		} else {
5744 			expand_operator(item_index);
5745 			replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
5746 		}
5747 
5748 	} else
5749 		Assert(0);  // unknown and/or invalid sexp type
5750 
5751 	expand_branch(currentItem());
5752 
5753 }
insertOperatorAction(int op)5754 void sexp_tree::insertOperatorAction(int op) {
5755 	int flags;
5756 
5757 	auto z = tree_nodes[item_index].parent;
5758 	flags = tree_nodes[item_index].flags;
5759 	auto node = allocate_node(z, item_index);
5760 	set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text.c_str());
5761 	tree_nodes[node].flags = flags;
5762 	QTreeWidgetItem* h;
5763 	if (z >= 0) {
5764 		h = tree_nodes[z].handle;
5765 	} else {
5766 		h = tree_nodes[item_index].handle->parent();
5767 		if (!_interface->getFlags()[TreeFlags::LabeledRoot]) {
5768 			h = nullptr;
5769 			root_item = node;
5770 		} else {
5771 			rootNodeFormulaChanged(item_index, node);
5772 			h->setData(0, FormulaDataRole, node);
5773 		}
5774 	}
5775 
5776 	auto item_handle = tree_nodes[node].handle =
5777 						   insert(Operators[op].text.c_str(), NodeImage::OPERATOR, h, tree_nodes[item_index].handle);
5778 	move_branch(item_index, node);
5779 
5780 	setCurrentItemIndex(node);
5781 	for (auto i = 1; i < Operators[op].min; i++) {
5782 		add_default_operator(op, i);
5783 	}
5784 
5785 	item_handle->setExpanded(true);
5786 	modified();
5787 }
addNumberDataHandler()5788 void sexp_tree::addNumberDataHandler() {
5789 	int theNode;
5790 
5791 	theNode = add_data("number", (SEXPT_NUMBER | SEXPT_VALID));
5792 	beginItemEdit(tree_nodes[theNode].handle);
5793 }
addStringDataHandler()5794 void sexp_tree::addStringDataHandler() {
5795 	int theNode;
5796 
5797 	theNode = add_data("string", (SEXPT_STRING | SEXPT_VALID));
5798 	beginItemEdit(tree_nodes[theNode].handle);
5799 }
beginItemEdit(QTreeWidgetItem * item)5800 void sexp_tree::beginItemEdit(QTreeWidgetItem* item) {
5801 	_currently_editing = true;
5802 	editItem(item);
5803 }
addReplaceTypedDataHandler(int data_idx,bool replace)5804 void sexp_tree::addReplaceTypedDataHandler(int data_idx, bool replace) {
5805 	Assert(item_index >= 0);
5806 	int op;
5807 	if (replace) {
5808 		op = get_operator_index(tree_nodes[tree_nodes[item_index].parent].text);
5809 	} else {
5810 		op = get_operator_index(tree_nodes[item_index].text);
5811 	}
5812 	Assert(op >= 0);
5813 
5814 	auto argcount = replace ? Replace_count : Add_count;
5815 
5816 	auto type = query_operator_argument_type(op, argcount);
5817 	auto list = get_listing_opf(type, item_index, argcount);
5818 	Assert(list);
5819 
5820 	auto ptr = list;
5821 	while (data_idx) {
5822 		data_idx--;
5823 		ptr = ptr->next;
5824 		Assert(ptr);
5825 	}
5826 
5827 	Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
5828 	expand_operator(item_index);
5829 	if (replace) {
5830 		replace_data(ptr->text.c_str(), ptr->type);
5831 	} else {
5832 		add_data(ptr->text.c_str(), ptr->type);
5833 	}
5834 	list->destroy();
5835 }
addPasteActionHandler()5836 void sexp_tree::addPasteActionHandler() {
5837 	// add paste, instead of replace.
5838 	// the following assumptions are made..
5839 	Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
5840 	Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
5841 
5842 	if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
5843 		expand_operator(item_index);
5844 		add_operator(CTEXT(Sexp_clipboard));
5845 		if (Sexp_nodes[Sexp_clipboard].rest != -1) {
5846 			load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
5847 			auto i = tree_nodes[item_index].child;
5848 			while (i != -1) {
5849 				add_sub_tree(i, tree_nodes[item_index].handle);
5850 				i = tree_nodes[i].next;
5851 			}
5852 		}
5853 
5854 	} else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
5855 		Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
5856 		expand_operator(item_index);
5857 		add_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
5858 
5859 	} else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
5860 		Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
5861 		expand_operator(item_index);
5862 		add_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
5863 
5864 	} else
5865 		Assert(0);  // unknown and/or invalid sexp type
5866 
5867 	expand_branch(currentItem());
5868 }
setCurrentItemIndex(int node)5869 void sexp_tree::setCurrentItemIndex(int node) {
5870 	item_index = node;
5871 	if (node < 0) {
5872 		setCurrentItem(nullptr);
5873 	} else {
5874 		setCurrentItem(tree_nodes[node].handle);
5875 	}
5876 }
handleReplaceVariableAction(int id)5877 void sexp_tree::handleReplaceVariableAction(int id) {
5878 	Assert(item_index >= 0);
5879 
5880 	// get index into list of type valid variables
5881 	Assert( (id >= 0) && (id < MAX_SEXP_VARIABLES) );
5882 
5883 	int type = get_type(currentItem());
5884 	Assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) );
5885 
5886 	// don't do type check for modify-variable
5887 	if (Modify_variable) {
5888 		if (Sexp_variables[id].type & SEXP_VARIABLE_NUMBER) {
5889 			type = SEXPT_NUMBER;
5890 		} else if (Sexp_variables[id].type & SEXP_VARIABLE_STRING) {
5891 			type = SEXPT_STRING;
5892 		} else {
5893 			Int3();	// unknown type
5894 		}
5895 
5896 	} else {
5897 		// verify type in tree is same as type in Sexp_variables array
5898 		if (type & SEXPT_NUMBER) {
5899 			Assert(Sexp_variables[id].type & SEXP_VARIABLE_NUMBER);
5900 		}
5901 
5902 		if (type & SEXPT_STRING) {
5903 			Assert( (Sexp_variables[id].type & SEXP_VARIABLE_STRING) );
5904 		}
5905 	}
5906 
5907 	// Replace data
5908 	replace_variable_data(id, (type | SEXPT_VARIABLE));
5909 
5910 }
handleNewItemSelected()5911 void sexp_tree::handleNewItemSelected() {
5912 	auto selectedItem = currentItem();
5913 
5914 	update_help(selectedItem);
5915 
5916 	if (selectedItem == nullptr) {
5917 		selectedRootChanged(-1);
5918 		setCurrentItemIndex(-1);
5919 		return;
5920 	}
5921 
5922 	// Set the item index so that it is always up to date
5923 	item_index = get_node(selectedItem);
5924 
5925 	auto item = selectedItem;
5926 	while (item->parent() != nullptr) {
5927 		item = item->parent();
5928 	}
5929 
5930 	selectedRootChanged(item->data(0, FormulaDataRole).toInt());
5931 }
deleteCurrentItem()5932 void sexp_tree::deleteCurrentItem() {
5933 	deleteActionHandler();
5934 }
getCurrentItemIndex() const5935 int sexp_tree::getCurrentItemIndex() const {
5936 	return item_index;
5937 }
5938 
5939 }
5940 }
5941