1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5 * Copyright (c) 2013, The OpenClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16 
17 /* Proplist table view */
18 
19 #ifndef INC_C4ConsoleQtPropListViewer
20 #define INC_C4ConsoleQtPropListViewer
21 #ifdef WITH_QT_EDITOR
22 
23 #include "C4Include.h" // needed for automoc
24 #include "editor/C4ConsoleGUI.h" // for glew.h
25 #include "editor/C4ConsoleQt.h"
26 #include "editor/C4ConsoleQtShapes.h"
27 #include "editor/C4PropertyPath.h"
28 #include "script/C4Value.h"
29 
30 class C4ConsoleQtPropListModel;
31 struct C4ConsoleQtPropListModelProperty;
32 
33 class C4PropertyDelegate : public QObject
34 {
35 	Q_OBJECT
36 
37 protected:
38 	const class C4PropertyDelegateFactory *factory;
39 	C4Value creation_props;
40 	C4RefCntPointer<C4String> set_function, async_get_function, name;
41 	C4PropertyPath::PathType set_function_type;
42 	C4RefCntPointer<C4String> update_callback;
43 
44 public:
45 	C4PropertyDelegate(const class C4PropertyDelegateFactory *factory, C4PropList *props);
46 	~C4PropertyDelegate() override = default;
47 
SetEditorData(QWidget * editor,const C4Value & val,const C4PropertyPath & property_path)48 	virtual void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const {};
SetModelData(QObject * editor,const C4PropertyPath & property_path,class C4ConsoleQtShape * prop_shape)49 	virtual void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const {};
50 	virtual QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const = 0;
51 	virtual void UpdateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option) const;
52 	virtual bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const;
53 	virtual bool GetPropertyValueBase(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const;
54 	virtual QString GetDisplayString(const C4Value &val, class C4Object *obj, bool short_names) const;
55 	virtual QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const;
56 	virtual QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const;
GetSetFunction()57 	const char *GetSetFunction() const { return set_function.Get() ? set_function->GetCStr() : nullptr; } // get name of setter function for this property
GetShapeDelegate(C4Value & val,C4PropertyPath * shape_path)58 	virtual const class C4PropertyDelegateShape *GetShapeDelegate(C4Value &val, C4PropertyPath *shape_path) const { return nullptr;  }
GetDirectShapeDelegate()59 	virtual const class C4PropertyDelegateShape *GetDirectShapeDelegate() const { return nullptr; }
GetUpdateCallback()60 	const char *GetUpdateCallback() const { return update_callback ? update_callback->GetCStr() : nullptr; }
HasCustomPaint()61 	virtual bool HasCustomPaint() const { return false; }
Paint(QPainter * painter,const QStyleOptionViewItem & option,const C4Value & val)62 	virtual bool Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const { return false; }
63 	virtual C4PropertyPath GetPathForProperty(struct C4ConsoleQtPropListModelProperty *editor_prop) const;
64 	C4PropertyPath GetPathForProperty(const C4PropertyPath &parent_path, const char *default_subpath) const;
GetNameStr()65 	C4String *GetNameStr() const { return name.Get(); }
GetCreationProps()66 	const C4Value &GetCreationProps() const { return creation_props; }
67 	virtual bool IsPasteValid(const C4Value &val) const = 0;
68 
69 signals:
70 	void EditorValueChangedSignal(QWidget *editor) const;
71 	void EditingDoneSignal(QWidget *editor) const;
72 };
73 
74 class C4PropertyDelegateInt : public C4PropertyDelegate
75 {
76 private:
77 	int32_t min, max, step;
78 public:
79 	C4PropertyDelegateInt(const class C4PropertyDelegateFactory *factory, C4PropList *props);
80 
81 	void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override;
82 	void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override;
83 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override;
84 	bool IsPasteValid(const C4Value &val) const override;
85 };
86 
87 class C4PropertyDelegateStringEditor : public QWidget
88 {
89 	Q_OBJECT
90 private:
91 	QLineEdit *edit;
92 	QPushButton *localization_button;
93 	bool text_edited, commit_pending;
94 	C4Value value;
95 	char lang_code[3];
96 	C4Value base_proplist;
97 	std::unique_ptr<class C4ConsoleQtLocalizeStringDlg> localization_dialogue;
98 
99 	void OpenLocalizationDialogue();
100 	void CloseLocalizationDialogue();
101 	void StoreEditedText();
102 public:
103 	C4PropertyDelegateStringEditor(QWidget *parent, bool has_localization_button);
104 	void SetValue(const C4Value &val);
105 	C4Value GetValue();
IsCommitPending()106 	bool IsCommitPending() const { return commit_pending; }
SetCommitPending(bool to_val)107 	void SetCommitPending(bool to_val) { commit_pending = to_val; }
108 signals:
109 	void EditingDoneSignal() const;
110 };
111 
112 class C4PropertyDelegateString : public C4PropertyDelegate
113 {
114 private:
115 	bool translatable;
116 public:
117 	typedef C4PropertyDelegateStringEditor Editor;
118 
119 	C4PropertyDelegateString(const class C4PropertyDelegateFactory *factory, C4PropList *props);
120 
121 	void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override;
122 	void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override;
123 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override;
124 	QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override;
125 	bool IsPasteValid(const C4Value &val) const override;
126 };
127 
128 // Editor: Text displaying value plus a button that opens an extended editor
129 class C4PropertyDelegateLabelAndButtonWidget : public QWidget
130 {
131 	Q_OBJECT
132 
133 public:
134 	QHBoxLayout *layout;
135 	QLabel *label;
136 	QPushButton *button;
137 	C4Value last_value;
138 	C4PropertyPath property_path;
139 	bool button_pending;
140 
141 	C4PropertyDelegateLabelAndButtonWidget(QWidget *parent);
142 };
143 
144 class C4PropertyDelegateDescendPath : public C4PropertyDelegate
145 {
146 protected:
147 	C4Value info_proplist;
148 	bool edit_on_selection;
149 	C4RefCntPointer<C4String> descend_path;
150 public:
151 	typedef C4PropertyDelegateLabelAndButtonWidget Editor;
152 
153 	C4PropertyDelegateDescendPath(const class C4PropertyDelegateFactory *factory, C4PropList *props);
154 
155 	void SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const override;
156 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override;
157 };
158 
159 class C4PropertyDelegateArray : public C4PropertyDelegateDescendPath
160 {
161 private:
162 	int32_t max_array_display;
163 	mutable C4PropertyDelegate *element_delegate; // lazy eval
164 
165 	void ResolveElementDelegate() const;
166 public:
167 	C4PropertyDelegateArray(const class C4PropertyDelegateFactory *factory, C4PropList *props);
168 
169 	QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override;
170 	bool IsPasteValid(const C4Value &val) const override;
171 };
172 
173 class C4PropertyDelegatePropList : public C4PropertyDelegateDescendPath
174 {
175 private:
176 	C4RefCntPointer<C4String> display_string;
177 public:
178 	C4PropertyDelegatePropList(const class C4PropertyDelegateFactory *factory, C4PropList *props);
179 
180 	QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override;
181 	bool IsPasteValid(const C4Value &val) const override;
182 };
183 
184 class C4PropertyDelegateEffectEditor : public QWidget
185 {
186 	Q_OBJECT
187 
188 public:
189 	QHBoxLayout *layout;
190 	QPushButton *remove_button, *edit_button;
191 	C4PropertyPath property_path;
192 
193 	C4PropertyDelegateEffectEditor(QWidget *parent);
194 };
195 
196 class C4PropertyDelegateEffect : public C4PropertyDelegate
197 {
198 public:
199 	typedef C4PropertyDelegateEffectEditor Editor;
200 
201 	C4PropertyDelegateEffect(const class C4PropertyDelegateFactory *factory, C4PropList *props);
202 
203 	void SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const override;
204 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override;
205 	QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override;
IsPasteValid(const C4Value & val)206 	bool IsPasteValid(const C4Value &val) const override { return false; }
207 	bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const override;
208 	C4PropertyPath GetPathForProperty(C4ConsoleQtPropListModelProperty *editor_prop) const override;
209 };
210 
211 class C4PropertyDelegateColor : public C4PropertyDelegate
212 {
213 private:
214 	uint32_t alpha_mask;
215 
216 public:
217 	typedef C4PropertyDelegateLabelAndButtonWidget Editor;
218 
219 	C4PropertyDelegateColor(const class C4PropertyDelegateFactory *factory, C4PropList *props);
220 
221 	void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override;
222 	void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override;
223 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override;
224 	QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override;
225 	QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const override;
226 	QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const override;
227 	bool IsPasteValid(const C4Value &val) const override;
228 
229 private:
230 	void OpenColorDialogue(Editor *editor) const;
231 };
232 
233 // Display delegate for deep combo box. Handles the help tooltip showing.
234 class C4StyledItemDelegateWithButton : public QStyledItemDelegate
235 {
236 	Q_OBJECT
237 
238 public:
239 	enum ButtonType
240 	{
241 		BT_Help,
242 		BT_PlaySound,
243 	} button_type;
244 
C4StyledItemDelegateWithButton(ButtonType bt)245 	C4StyledItemDelegateWithButton(ButtonType bt) : button_type(bt) { }
246 
247 public:
248 	bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override;
249 protected:
250 	void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
251 };
252 
253 // A combo box that can select from a model with nested elements
254 // On click, descend into child elements
255 class C4DeepQComboBox : public QComboBox
256 {
257 	Q_OBJECT
258 
259 	bool editable, manual_text_edited;
260 	QString last_edited_text;
261 	bool is_next_close_blocked;
262 	int last_popup_height;
263 	std::unique_ptr<C4StyledItemDelegateWithButton> item_delegate;
264 	QSize default_icon_size;
265 
266 public:
267 	enum
268 	{
269 		OptionIndexRole = Qt::UserRole + 1,
270 		ObjectHighlightRole = Qt::UserRole + 2,
271 		ValueStringRole = Qt::UserRole + 3,
272 		PriorityNameSortRole = Qt::UserRole + 4,
273 	};
274 
275 	C4DeepQComboBox(QWidget *parent, C4StyledItemDelegateWithButton::ButtonType button_type, bool editable);
276 
277 	void showPopup() override;
278 	void hidePopup() override;
279 
280 	void setCurrentModelIndex(QModelIndex new_index);
281 	int32_t GetCurrentSelectionIndex();
BlockNextCloseEvent()282 	void BlockNextCloseEvent() { is_next_close_blocked = true; }; // after item selection on a "play" button, the combo dropdown should stay open
283 
284 public slots:
doShowPopup()285 	void doShowPopup() { showPopup(); }
286 
287 signals:
288 	void NewItemSelected(int32_t new_item);
289 	void TextChanged(const QString &new_text);
290 
291 protected:
292 	// event filter for view: Catch mouse clicks to descend into children
293 	bool eventFilter(QObject *obj, QEvent *event) override;
294 };
295 
296 // Widget holder class
297 class C4PropertyDelegateEnumEditor : public QWidget
298 {
299 	Q_OBJECT
300 
301 public:
302 	enum { INDEX_Custom_Value = -1 };
303 	C4Value last_val;
304 	C4Value last_parameter_val; // Resolved parameter of last_val - assigned for shape parameters only
305 	int32_t last_selection_index; // Index of selection in model or INDEX_Custom_Value for custom value that does not resolve to an existing entry in editable enum
306 	C4PropertyPath last_get_path;
307 	C4DeepQComboBox *option_box;
308 	QHBoxLayout *layout;
309 	QWidget *parameter_widget;
310 	bool updating, option_changed, dropdown_pending;
311 	const C4PropertyDelegate *paint_parameter_delegate; // Delegate to draw over the parameter_widget if it's an empty transparent QWidget (for shape delegates)
312 
C4PropertyDelegateEnumEditor(QWidget * parent)313 	C4PropertyDelegateEnumEditor(QWidget *parent)
314 		: QWidget(parent), last_selection_index(-1), option_box(nullptr), layout(nullptr), parameter_widget(nullptr),
315 		updating(false), option_changed(false), dropdown_pending(false), paint_parameter_delegate(nullptr) { }
316 
317 	void paintEvent(QPaintEvent *) override;
318 };
319 
320 class C4PropertyDelegateEnum : public C4PropertyDelegate
321 {
322 	Q_OBJECT
323 
324 public:
325 	typedef C4PropertyDelegateEnumEditor Editor; // qmake doesn't like nested classes
326 
327 	class Option
328 	{
329 	public:
330 		C4RefCntPointer<C4String> name; // Display name in Editor enum dropdown box
331 		C4RefCntPointer<C4String> short_name; // Shortened name displayed as sub-delegate
332 		C4RefCntPointer<C4String> help; // Tooltip text
333 		C4RefCntPointer<C4String> group; // Grouping in enum dropdown box; nested groups separated by '/'
334 		C4RefCntPointer<C4String> option_key;
335 		C4RefCntPointer<C4String> value_key;
336 		C4RefCntPointer<C4String> sound_name; // Assigned for options that have a play button
337 		C4V_Type type{C4V_Any}; // Assume this option is set when value is of given type
338 		C4Value props; // Stored pointer to proplist defining this option
339 		C4Value value; // Value to set if this entry is selected
340 		bool force_serialization{false}; // If serialization should be forced on value
341 		C4Value value_function; // Function to be called to set value
342 		mutable C4PropertyDelegate *adelegate{nullptr}; // Delegate to display if this entry is selected (pointer owned by C4PropertyDelegateFactory)
343 		C4Value adelegate_val; // Value to resolve adelegate from
344 		// How the currently selected option is identified from the value
345 		enum StorageType {
346 			StorageNone=0, // Invalid option
347 			StorageByType=1, // Use type to identify this enum
348 			StorageByValue=2, // This option sets a constant value
349 			StorageByKey=3, // Assume value is a proplist; identify option by field option_key
350 		} storage_type{StorageNone};
351 		int32_t priority{0}; // Custom sort order
352 
353 		Option() = default;
354 	};
355 
356 protected:
GetOptionComboBoxButtonType()357 	virtual C4StyledItemDelegateWithButton::ButtonType GetOptionComboBoxButtonType() const { return C4StyledItemDelegateWithButton::BT_Help; }
358 
359 private:
360 	std::vector<Option> options;
361 	Option default_option;
362 	bool allow_editing;
363 	bool sorted;
364 
365 protected:
366 	C4RefCntPointer<C4String> empty_name; // Override for name of empty option
367 
368 protected:
369 	void ClearOptions();
370 	void ReserveOptions(int32_t num);
371 	QStandardItemModel *CreateOptionModel() const;
372 public:
373 	C4PropertyDelegateEnum(const class C4PropertyDelegateFactory *factory, C4PropList *props, const C4ValueArray *poptions=nullptr);
374 
375 	void AddTypeOption(C4String *name, C4V_Type type, const C4Value &val, C4PropertyDelegate *adelegate=nullptr);
376 	void AddConstOption(C4String *name, const C4Value &val, C4String *group=nullptr, C4String *sound_name=nullptr);
377 
378 	void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override;
379 	void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override;
380 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override;
381 	QString GetDisplayString(const C4Value &val, class C4Object *obj, bool short_names) const override;
382 	const class C4PropertyDelegateShape *GetShapeDelegate(C4Value &val, C4PropertyPath *shape_path) const override; // Forward to parameter of selected option
HasCustomPaint()383 	bool HasCustomPaint() const override { return true; }
384 	bool Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const override;
385 	bool IsPasteValid(const C4Value &val) const override;
386 
387 private:
388 	QModelIndex GetModelIndexByID(QStandardItemModel *model, QStandardItem *parent_item, int32_t id, const QModelIndex &parent) const;
389 	int32_t GetOptionByValue(const C4Value &val) const;
390 	void UpdateEditorParameter(C4PropertyDelegateEnum::Editor *editor, bool by_selection) const;
391 	void EnsureOptionDelegateResolved(const Option &option) const;
392 	void SetOptionValue(const C4PropertyPath &use_path, const C4PropertyDelegateEnum::Option &option, const C4Value &option_value) const;
393 	void UpdateOptionIndex(Editor *editor, int idx, const QString *custom_text) const;
394 };
395 
396 // Select a definition
397 class C4PropertyDelegateDef : public C4PropertyDelegateEnum
398 {
399 private:
400 	C4RefCntPointer<C4String> filter_property;
401 public:
402 	C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, C4PropList *props);
403 	bool IsPasteValid(const C4Value &val) const override;
404 
405 private:
406 	void AddDefinitions(class C4ConsoleQtDefinitionListModel *def_list_model, QModelIndex parent, C4String *group);
407 };
408 
409 // Select an object
410 class C4PropertyDelegateObject : public C4PropertyDelegateEnum
411 {
412 private:
413 	C4RefCntPointer<C4String> filter;
414 	size_t max_nearby_objects; // maximum number of objects shown in "nearby" list
415 
416 	C4RefCntPointer<C4String> GetObjectEntryString(C4Object *obj) const;
417 	void UpdateObjectList();
418 public:
419 	C4PropertyDelegateObject(const C4PropertyDelegateFactory *factory, C4PropList *props);
420 
421 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override;
422 	QString GetDisplayString(const C4Value &v, class C4Object *obj, bool short_names) const override;
423 	bool IsPasteValid(const C4Value &val) const override;
424 };
425 
426 // Select a sound
427 class C4PropertyDelegateSound : public C4PropertyDelegateEnum
428 {
429 public:
430 	C4PropertyDelegateSound(const C4PropertyDelegateFactory *factory, C4PropList *props);
431 	QString GetDisplayString(const C4Value &v, class C4Object *obj, bool short_names) const override;
432 	bool IsPasteValid(const C4Value &val) const override;
433 protected:
GetOptionComboBoxButtonType()434 	C4StyledItemDelegateWithButton::ButtonType GetOptionComboBoxButtonType() const override { return C4StyledItemDelegateWithButton::BT_PlaySound; }
435 };
436 
437 // true or false
438 class C4PropertyDelegateBool : public C4PropertyDelegateEnum
439 {
440 public:
441 	C4PropertyDelegateBool(const class C4PropertyDelegateFactory *factory, C4PropList *props);
442 
443 	bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const override;
444 	bool IsPasteValid(const C4Value &val) const override;
445 };
446 
447 // true or false depending on whether effect is present
448 class C4PropertyDelegateHasEffect : public C4PropertyDelegateBool
449 {
450 private:
451 	C4RefCntPointer<C4String> effect;
452 public:
453 	C4PropertyDelegateHasEffect(const class C4PropertyDelegateFactory *factory, C4PropList *props);
454 
455 	bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const override;
456 };
457 
458 // C4Value setting using an enum
459 class C4PropertyDelegateC4ValueEnum : public C4PropertyDelegateEnum
460 {
461 public:
462 	C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory, C4PropList *props);
IsPasteValid(const C4Value & val)463 	bool IsPasteValid(const C4Value &val) const override { return true; }
464 };
465 
466 class C4PropertyDelegateC4ValueInputEditor : public QWidget // TODO: Merge with C4PropertyDelegateLabelAndButtonWidget
467 {
468 	Q_OBJECT
469 
470 public:
471 	QHBoxLayout *layout;
472 	QLineEdit *edit;
473 	QPushButton *extended_button;
474 	bool commit_pending;
475 	C4PropertyPath property_path;
476 
477 	C4PropertyDelegateC4ValueInputEditor(QWidget *parent);
478 };
479 
480 // C4Value setting using an input box
481 class C4PropertyDelegateC4ValueInput : public C4PropertyDelegate
482 {
483 public:
484 	typedef C4PropertyDelegateC4ValueInputEditor Editor;
485 
C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory * factory,C4PropList * props)486 	C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory *factory, C4PropList *props) : C4PropertyDelegate(factory, props) { }
487 
488 	void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override;
489 	void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override;
490 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override;
IsPasteValid(const C4Value & val)491 	bool IsPasteValid(const C4Value &val) const override { return true; }
492 };
493 
494 // areas shown in viewport
495 class C4PropertyDelegateShape : public C4PropertyDelegate
496 {
497 	uint32_t clr;
498 
499 	virtual void DoPaint(QPainter *painter, const QRect &inner_rect) const = 0;
500 public:
501 	C4PropertyDelegateShape(const class C4PropertyDelegateFactory *factory, C4PropList *props);
SetEditorData(QWidget * editor,const C4Value & val,const C4PropertyPath & property_path)502 	void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override { } // TODO maybe implement update?
503 	void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override;
CreateEditor(const class C4PropertyDelegateFactory * parent_delegate,QWidget * parent,const QStyleOptionViewItem & option,bool by_selection,bool is_child)504 	QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override { return nullptr; }
GetShapeDelegate(C4Value & val,C4PropertyPath * shape_path)505 	const C4PropertyDelegateShape *GetShapeDelegate(C4Value &val, C4PropertyPath *shape_path) const override { return this; }
GetDirectShapeDelegate()506 	const C4PropertyDelegateShape *GetDirectShapeDelegate() const override { return this; }
HasCustomPaint()507 	bool HasCustomPaint() const override { return true; }
508 	bool Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const override;
GetDisplayString(const C4Value & v,class C4Object * obj,bool short_names)509 	QString GetDisplayString(const C4Value &v, class C4Object *obj, bool short_names) const override { return QString(); }
510 
511 	virtual void ConnectSignals(C4ConsoleQtShape *shape, const C4PropertyPath &property_path) const;
512 };
513 
514 class C4PropertyDelegateRect : public C4PropertyDelegateShape
515 {
516 	C4RefCntPointer<C4String> storage;
517 
518 	void DoPaint(QPainter *painter, const QRect &inner_rect) const override;
519 public:
520 	C4PropertyDelegateRect(const class C4PropertyDelegateFactory *factory, C4PropList *props);
521 	bool IsPasteValid(const C4Value &val) const override;
522 };
523 
524 class C4PropertyDelegateCircle : public C4PropertyDelegateShape
525 {
526 	bool can_move_center;
527 
528 	void DoPaint(QPainter *painter, const QRect &inner_rect) const override;
529 public:
530 	C4PropertyDelegateCircle(const class C4PropertyDelegateFactory *factory, C4PropList *props);
531 	bool IsPasteValid(const C4Value &val) const override;
532 };
533 
534 class C4PropertyDelegatePoint : public C4PropertyDelegateShape
535 {
536 	bool horizontal_fix{ false };
537 	bool vertical_fix{ false };
538 
539 	void DoPaint(QPainter *painter, const QRect &inner_rect) const override;
540 public:
541 	C4PropertyDelegatePoint(const class C4PropertyDelegateFactory *factory, C4PropList *props);
542 	bool IsPasteValid(const C4Value &val) const override;
543 };
544 
545 class C4PropertyDelegateGraph : public C4PropertyDelegateShape
546 {
547 	bool horizontal_fix = false;
548 	bool vertical_fix = false;
549 	bool structure_fix = false;
550 
551 	void DoPaint(QPainter *painter, const QRect &inner_rect) const override;
552 protected:
553 	bool IsVertexPasteValid(const C4Value &val) const;
554 	bool IsEdgePasteValid(const C4Value &val) const;
555 public:
556 	C4PropertyDelegateGraph(const class C4PropertyDelegateFactory *factory, C4PropList *props);
557 	bool IsPasteValid(const C4Value &val) const override;
558 
559 	void ConnectSignals(C4ConsoleQtShape *shape, const C4PropertyPath &property_path) const override;
560 };
561 
562 class C4PropertyDelegatePolyline : public C4PropertyDelegateGraph
563 {
564 	void DoPaint(QPainter *painter, const QRect &inner_rect) const override;
565 public:
566 	C4PropertyDelegatePolyline(const class C4PropertyDelegateFactory *factory, C4PropList *props);
567 	bool IsPasteValid(const C4Value &val) const override;
568 };
569 
570 class C4PropertyDelegatePolygon : public C4PropertyDelegateGraph
571 {
572 	void DoPaint(QPainter *painter, const QRect &inner_rect) const override;
573 public:
574 	C4PropertyDelegatePolygon(const class C4PropertyDelegateFactory *factory, C4PropList *props);
575 	bool IsPasteValid(const C4Value &val) const override;
576 };
577 
578 class C4PropertyDelegateFactory : public QStyledItemDelegate
579 {
580 	Q_OBJECT
581 
582 	mutable std::map<C4PropList *, std::unique_ptr<C4PropertyDelegate> > delegates;
583 	mutable C4PropertyDelegateEffect effect_delegate;
584 	mutable QWidget *current_editor{nullptr};
585 	mutable C4PropertyDelegate *current_editor_delegate;
586 	mutable C4Value last_edited_value;
587 	class C4ConsoleQtPropListModel *property_model{nullptr};
588 	class C4ConsoleQtDefinitionListModel *def_list_model;
589 
590 	C4PropertyDelegate *CreateDelegateByPropList(C4PropList *props) const;
591 	C4PropertyDelegate *GetDelegateByIndex(const QModelIndex &index) const;
592 public:
593 	C4PropertyDelegateFactory();
594 	~C4PropertyDelegateFactory() override = default;
595 
596 	C4PropertyDelegate *GetDelegateByValue(const C4Value &val) const;
GetEffectDelegate()597 	C4PropertyDelegateEffect *GetEffectDelegate() const { return &effect_delegate; }
598 
599 	void ClearDelegates();
600 	void SetPropertyData(const C4PropertyDelegate *d, QObject *editor, C4ConsoleQtPropListModelProperty *editor_prop) const;
SetPropertyModel(class C4ConsoleQtPropListModel * new_property_model)601 	void SetPropertyModel(class C4ConsoleQtPropListModel *new_property_model) { property_model = new_property_model; }
SetDefinitionListModel(class C4ConsoleQtDefinitionListModel * new_def_list_model)602 	void SetDefinitionListModel(class C4ConsoleQtDefinitionListModel *new_def_list_model) { def_list_model = new_def_list_model; }
GetDefinitionListModel()603 	class C4ConsoleQtDefinitionListModel *GetDefinitionListModel() const { return def_list_model; }
GetPropertyModel()604 	class C4ConsoleQtPropListModel *GetPropertyModel() const { return property_model; }
605 	void OnPropListChanged();
606 	bool CheckCurrentEditor(C4PropertyDelegate *d, QWidget *editor) const;
607 
608 private:
609 	void EditorValueChanged(QWidget *editor);
610 	void EditingDone(QWidget *editor);
611 	void CopyToClipboard(const QModelIndex &index);
612 	bool PasteFromClipboard(const QModelIndex &index, bool check_only);
613 
614 protected:
615 	// Model callbacks forwarded to actual delegates
616 	void setEditorData(QWidget *editor, const QModelIndex &index) const override;
617 	void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
618 	QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
619 	void destroyEditor(QWidget *editor, const QModelIndex &index) const override;
620 	void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
621 	QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
622 	void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
623 	bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override;
624 };
625 
626 // Delegate for the name column of the property window
627 // For now, just use the default + help button
628 class C4PropertyNameDelegate : public C4StyledItemDelegateWithButton
629 {
630 	Q_OBJECT
631 
632 	class C4ConsoleQtPropListModel *property_model{nullptr};
633 
634 public:
C4PropertyNameDelegate()635 	C4PropertyNameDelegate() : C4StyledItemDelegateWithButton(C4StyledItemDelegateWithButton::BT_Help) { }
636 
SetPropertyModel(class C4ConsoleQtPropListModel * new_property_model)637 	void SetPropertyModel(class C4ConsoleQtPropListModel *new_property_model) { property_model = new_property_model; }
638 };
639 
640 // One property in the prop list model view
641 struct C4ConsoleQtPropListModelProperty
642 {
643 	C4PropertyPath property_path;
644 	C4Value parent_value;
645 	C4RefCntPointer<C4String> display_name;
646 	C4RefCntPointer<C4String> help_text;
647 	C4RefCntPointer<C4String> key;
648 	C4Value delegate_info;
649 	C4PropertyDelegate *delegate{nullptr};
650 	bool about_to_edit{false};
651 	int32_t priority{0};
652 
653 	// Parent group index
654 	int32_t group_idx{-1};
655 
656 	// Each property may be connected to one shape shown in the viewport for editing
657 	C4ConsoleQtShapeHolder *shape;
658 	const C4PropertyDelegate *shape_delegate{nullptr};
659 	C4PropertyPath shape_property_path;
660 
661 	C4ConsoleQtPropListModelProperty() = default;
662 };
663 
664 // Prop list view implemented as a model view
665 class C4ConsoleQtPropListModel : public QAbstractItemModel
666 {
667 	Q_OBJECT
668 
669 public:
670 	typedef C4ConsoleQtPropListModelProperty Property;
671 	struct PropertyGroup
672 	{
673 		QString name;
674 		std::vector<Property> props;
675 	};
676 	struct TargetStackEntry // elements of the path for setting child properties
677 	{
678 		C4PropertyPath path;
679 		// TODO: Would be nice to store only path without values and info_proplist. However, info_proplist is hard to resolve when traversing up
680 		// So just keep the value for now and hope that proplists do not change during selection
681 		C4Value value, info_proplist;
682 
TargetStackEntryTargetStackEntry683 		TargetStackEntry(const C4PropertyPath &path, const C4Value &value, const C4Value &info_proplist)
684 			: path(path), value(value), info_proplist(info_proplist) {}
685 	};
686 	struct EditedPath // Information about how to find currently edited element (to restore after model update)
687 	{
688 		C4PropertyPath target_path;
689 		int32_t major_index, minor_index;
690 	};
691 private:
692 	C4Value target_value; // Target value for which properties are listed (either proplist or array)
693 	C4Value base_proplist; // Parent-most value, i.e. object or effect selected and framed in editor
694 	C4Value info_proplist; // Proplist from which available properties are derived. May differ from target_proplist in child proplists.
695 	C4PropertyPath target_path; // script path to target proplist to set values
696 	std::list<TargetStackEntry> target_path_stack; // stack of target paths descended into by setting child properties
697 	std::vector<PropertyGroup> property_groups;
698 	QFont header_font, important_property_font;
699 	C4PropertyDelegateFactory *delegate_factory;
700 	QItemSelectionModel *selection_model;
701 	bool layout_valid; // set to false when property numbers change
702 	std::map<std::string, C4ConsoleQtShapeHolder> shapes; // shapes currently shown in editor. Indexed by get path
703 public:
704 	C4ConsoleQtPropListModel(C4PropertyDelegateFactory *delegate_factory);
705 	~C4ConsoleQtPropListModel() override;
706 
SetSelectionModel(QItemSelectionModel * m)707 	void SetSelectionModel(QItemSelectionModel *m) { selection_model = m; }
GetSelectionModel()708 	QItemSelectionModel *GetSelectionModel() const { return selection_model; }
709 
710 	bool AddPropertyGroup(C4PropList *add_proplist, int32_t group_index, QString name, C4PropList *target_proplist, const C4PropertyPath &group_target_path, C4Object *base_object, C4String *default_selection, int32_t *default_selection_index);
711 	bool AddEffectGroup(int32_t group_index, C4Object *base_object);
712 	void SetBasePropList(C4PropList *new_proplist); // Clear stack and select new proplist
713 	void DescendPath(const C4Value &new_value, C4PropList *new_info_proplist, const C4PropertyPath &new_path); // Add proplist to stack
714 	void AscendPath(); // go back one element in target path stack
715 	void UpdateValue(bool select_default);
716 	void DoOnUpdateCall(const C4PropertyPath &updated_path, const C4PropertyDelegate *delegate);
717 
718 private:
719 	int32_t UpdateValuePropList(C4PropList *target_proplist, int32_t *default_selection_group, int32_t *default_selection_index);
720 	int32_t UpdateValueArray(C4ValueArray *target_array, int32_t *default_selection_group, int32_t *default_selection_index);
721 
722 signals:
723 	void ProplistChanged(int32_t major_sel, int32_t minor_sel) const;
724 
725 private slots:
726 	void UpdateSelection(int32_t major_sel, int32_t minor_sel) const;
727 
728 public:
GetTargetPropList()729 	class C4PropList *GetTargetPropList() const { return target_value.getPropList(); }
GetTargetArray()730 	class C4ValueArray *GetTargetArray() const { return target_value.getArray(); }
GetBasePropList()731 	class C4PropList *GetBasePropList() const { return base_proplist.getPropList(); }
GetTargetPathStackSize()732 	int32_t GetTargetPathStackSize() const { return target_path_stack.size(); }
GetTargetPathText()733 	const char *GetTargetPathText() const { return target_path.GetGetPath(); }
734 	QString GetTargetPathHelp() const;
735 	const char *GetTargetPathName() const;
IsArray()736 	bool IsArray() const { return !!target_value.getArray(); }
737 	void AddArrayElement();
738 	void RemoveArrayElement();
739 	bool IsTargetReadonly() const;
740 	C4ConsoleQtPropListModel::Property *GetPropByIndex(const QModelIndex &index) const;
741 	class C4ConsoleQtShape *GetShapeByPropertyPath(const char *property_path);
742 
743 public:
744 	int rowCount(const QModelIndex & parent = QModelIndex()) const override;
745 	int columnCount(const QModelIndex & parent = QModelIndex()) const override;
746 	QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
747 	QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
748 	QModelIndex index(int row, int column, const QModelIndex &parent) const override;
749 	QModelIndex parent(const QModelIndex &index) const override;
750 	Qt::ItemFlags flags(const QModelIndex &index) const override;
751 	Qt::DropActions supportedDropActions() const override;
752 	bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
753 	QStringList mimeTypes() const override;
754 	QMimeData *mimeData(const QModelIndexList &indexes) const override;
755 };
756 
757 #endif // WITH_QT_EDITOR
758 #endif // INC_C4ConsoleQtPropListViewer
759