2016-02-12 04:37:13 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
* Copyright ( c ) 2001 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
* Copyright ( c ) 2013 , The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
*
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
*
* To redistribute this file separately , substitute the full license texts
* for the above references .
*/
2016-04-03 19:06:32 +00:00
# include "C4Include.h"
# include "script/C4Value.h"
# include "editor/C4ConsoleQtPropListViewer.h"
2016-06-16 01:57:59 +00:00
# include "editor/C4ConsoleQtDefinitionListViewer.h"
2016-04-25 20:17:10 +00:00
# include "editor/C4ConsoleQtState.h"
2017-05-07 18:25:03 +00:00
# include "editor/C4ConsoleQtLocalizeString.h"
2016-04-04 23:34:02 +00:00
# include "editor/C4Console.h"
# include "object/C4Object.h"
2016-06-16 18:59:04 +00:00
# include "object/C4GameObjects.h"
2016-04-09 18:20:31 +00:00
# include "object/C4DefList.h"
# include "object/C4Def.h"
2016-04-17 02:02:24 +00:00
# include "script/C4Effect.h"
2016-05-25 03:06:00 +00:00
# include "script/C4AulExec.h"
2016-07-28 02:35:11 +00:00
# include "platform/C4SoundInstance.h"
2016-04-04 03:46:58 +00:00
2016-07-28 02:35:11 +00:00
/* Property delegate base class */
2016-04-04 03:46:58 +00:00
2016-04-17 02:02:24 +00:00
C4PropertyDelegate : : C4PropertyDelegate ( const C4PropertyDelegateFactory * factory , C4PropList * props )
2016-07-21 04:20:44 +00:00
: QObject ( ) , factory ( factory ) , set_function_type ( C4PropertyPath : : PPT_SetFunction )
2016-04-09 18:20:31 +00:00
{
2016-04-17 02:02:24 +00:00
// Resolve getter+setter callback names
if ( props )
{
2016-07-18 23:24:53 +00:00
creation_props = C4VPropList ( props ) ;
2016-07-06 03:27:55 +00:00
name = props - > GetPropertyStr ( P_Name ) ;
2016-04-17 02:02:24 +00:00
set_function = props - > GetPropertyStr ( P_Set ) ;
2016-07-21 04:20:44 +00:00
if ( props - > GetPropertyBool ( P_SetGlobal ) )
{
set_function_type = C4PropertyPath : : PPT_GlobalSetFunction ;
}
else if ( props - > GetPropertyBool ( P_SetRoot ) )
{
set_function_type = C4PropertyPath : : PPT_RootSetFunction ;
}
else
{
set_function_type = C4PropertyPath : : PPT_SetFunction ;
}
2016-04-17 02:02:24 +00:00
async_get_function = props - > GetPropertyStr ( P_AsyncGet ) ;
2017-02-26 15:45:38 +00:00
update_callback = props - > GetPropertyStr ( P_OnUpdate ) ;
2016-04-17 02:02:24 +00:00
}
2016-04-09 18:20:31 +00:00
}
2016-04-04 03:46:58 +00:00
void C4PropertyDelegate : : UpdateEditorGeometry ( QWidget * editor , const QStyleOptionViewItem & option ) const
{
editor - > setGeometry ( option . rect ) ;
}
2016-05-28 13:30:54 +00:00
bool C4PropertyDelegate : : GetPropertyValueBase ( const C4Value & container , C4String * key , int32_t index , C4Value * out_val ) const
2016-04-17 02:02:24 +00:00
{
2016-05-28 13:30:54 +00:00
switch ( container . GetType ( ) )
2016-04-17 02:02:24 +00:00
{
2016-05-28 13:30:54 +00:00
case C4V_PropList :
return container . _getPropList ( ) - > GetPropertyByS ( key , out_val ) ;
case C4V_Array :
* out_val = container . _getArray ( ) - > GetItem ( index ) ;
2016-04-17 02:02:24 +00:00
return true ;
2016-05-28 13:30:54 +00:00
default :
return false ;
}
}
bool C4PropertyDelegate : : GetPropertyValue ( const C4Value & container , C4String * key , int32_t index , C4Value * out_val ) const
{
if ( async_get_function )
{
C4PropList * props = container . getPropList ( ) ;
if ( props )
{
* out_val = props - > Call ( async_get_function . Get ( ) ) ;
return true ;
}
return false ;
2016-04-17 02:02:24 +00:00
}
else
{
2016-05-28 13:30:54 +00:00
return GetPropertyValueBase ( container , key , index , out_val ) ;
2016-04-17 02:02:24 +00:00
}
}
2016-07-30 18:50:07 +00:00
QString C4PropertyDelegate : : GetDisplayString ( const C4Value & v , C4Object * obj , bool short_names ) const
2016-04-17 02:02:24 +00:00
{
return QString ( v . GetDataString ( ) . getData ( ) ) ;
}
QColor C4PropertyDelegate : : GetDisplayTextColor ( const C4Value & val , class C4Object * obj ) const
{
return QColor ( ) ; // invalid = default
}
QColor C4PropertyDelegate : : GetDisplayBackgroundColor ( const C4Value & val , class C4Object * obj ) const
{
return QColor ( ) ; // invalid = default
}
2016-05-25 03:06:00 +00:00
C4PropertyPath C4PropertyDelegate : : GetPathForProperty ( C4ConsoleQtPropListModelProperty * editor_prop ) const
{
C4PropertyPath path ;
if ( editor_prop - > property_path . IsEmpty ( ) )
2016-05-28 13:30:54 +00:00
path = C4PropertyPath ( editor_prop - > parent_value . getPropList ( ) ) ;
2016-05-25 03:06:00 +00:00
else
path = editor_prop - > property_path ;
2016-07-21 04:20:44 +00:00
return GetPathForProperty ( path , editor_prop - > key ? editor_prop - > key - > GetCStr ( ) : nullptr ) ;
}
C4PropertyPath C4PropertyDelegate : : GetPathForProperty ( const C4PropertyPath & parent_path , const char * default_subpath ) const
{
2016-07-23 05:40:51 +00:00
// Get path
2016-05-25 03:06:00 +00:00
C4PropertyPath subpath ;
2016-07-23 05:40:51 +00:00
if ( default_subpath & & * default_subpath )
subpath = C4PropertyPath ( parent_path , default_subpath ) ;
2016-05-25 03:06:00 +00:00
else
2016-07-23 05:40:51 +00:00
subpath = parent_path ;
// Set path
if ( GetSetFunction ( ) )
{
subpath . SetSetPath ( parent_path , GetSetFunction ( ) , set_function_type ) ;
}
2016-05-25 03:06:00 +00:00
return subpath ;
}
2016-07-28 02:35:11 +00:00
/* Integer delegate */
2016-04-17 02:02:24 +00:00
C4PropertyDelegateInt : : C4PropertyDelegateInt ( const C4PropertyDelegateFactory * factory , C4PropList * props )
2016-04-09 18:20:31 +00:00
: C4PropertyDelegate ( factory , props ) , min ( std : : numeric_limits < int32_t > : : min ( ) ) , max ( std : : numeric_limits < int32_t > : : max ( ) ) , step ( 1 )
2016-04-04 03:46:58 +00:00
{
2016-04-09 18:20:31 +00:00
if ( props )
{
min = props - > GetPropertyInt ( P_Min , min ) ;
max = props - > GetPropertyInt ( P_Max , max ) ;
step = props - > GetPropertyInt ( P_Step , step ) ;
}
2016-04-04 03:46:58 +00:00
}
2016-05-25 03:06:00 +00:00
void C4PropertyDelegateInt : : SetEditorData ( QWidget * editor , const C4Value & val , const C4PropertyPath & property_path ) const
2016-04-04 03:46:58 +00:00
{
QSpinBox * spinBox = static_cast < QSpinBox * > ( editor ) ;
spinBox - > setValue ( val . getInt ( ) ) ;
}
2016-07-18 23:24:53 +00:00
void C4PropertyDelegateInt : : SetModelData ( QObject * editor , const C4PropertyPath & property_path , C4ConsoleQtShape * prop_shape ) const
2016-04-04 03:46:58 +00:00
{
QSpinBox * spinBox = static_cast < QSpinBox * > ( editor ) ;
spinBox - > interpretText ( ) ;
property_path . SetProperty ( C4VInt ( spinBox - > value ( ) ) ) ;
2017-02-26 15:45:38 +00:00
factory - > GetPropertyModel ( ) - > DoOnUpdateCall ( property_path , this ) ;
2016-04-04 03:46:58 +00:00
}
2016-07-30 06:37:33 +00:00
QWidget * C4PropertyDelegateInt : : CreateEditor ( const C4PropertyDelegateFactory * parent_delegate , QWidget * parent , const QStyleOptionViewItem & option , bool by_selection , bool is_child ) const
2016-04-04 03:46:58 +00:00
{
QSpinBox * editor = new QSpinBox ( parent ) ;
2016-04-09 18:20:31 +00:00
editor - > setMinimum ( min ) ;
editor - > setMaximum ( max ) ;
editor - > setSingleStep ( step ) ;
2016-04-04 03:46:58 +00:00
connect ( editor , & QSpinBox : : editingFinished , this , [ editor , this ] ( ) {
emit EditingDoneSignal ( editor ) ;
} ) ;
2016-10-15 22:26:03 +00:00
// Selection in child enum: Direct focus
if ( by_selection & & is_child ) editor - > setFocus ( ) ;
2016-04-04 03:46:58 +00:00
return editor ;
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateInt : : IsPasteValid ( const C4Value & val ) const
{
// Check int type and limits
if ( val . GetType ( ) ! = C4V_Int ) return false ;
int32_t ival = val . _getInt ( ) ;
return ( ival > = min & & ival < = max ) ;
}
2016-07-28 02:35:11 +00:00
/* String delegate */
2017-05-07 18:25:03 +00:00
C4PropertyDelegateStringEditor : : C4PropertyDelegateStringEditor ( QWidget * parent , bool has_localization_button )
: QWidget ( parent ) , edit ( nullptr ) , localization_button ( nullptr ) , commit_pending ( false ) , text_edited ( false )
{
auto layout = new QHBoxLayout ( this ) ;
layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
layout - > setMargin ( 0 ) ;
layout - > setSpacing ( 0 ) ;
edit = new QLineEdit ( this ) ;
layout - > addWidget ( edit ) ;
if ( has_localization_button )
{
localization_button = new QPushButton ( QString ( LoadResStr ( " IDS_CNS_MORE " ) ) , this ) ;
layout - > addWidget ( localization_button ) ;
connect ( localization_button , & QPushButton : : pressed , this , [ this ] ( ) {
// Show dialogue
OpenLocalizationDialogue ( ) ;
} ) ;
}
connect ( edit , & QLineEdit : : returnPressed , this , [ this ] ( ) {
text_edited = true ;
commit_pending = true ;
emit EditingDoneSignal ( ) ;
} ) ;
connect ( edit , & QLineEdit : : textEdited , this , [ this ] ( ) {
text_edited = true ;
commit_pending = true ;
} ) ;
}
void C4PropertyDelegateStringEditor : : OpenLocalizationDialogue ( )
{
if ( ! localization_dialogue )
{
// Make sure we have an updated value
StoreEditedText ( ) ;
// Make sure we're using a localized string
if ( value . GetType ( ) ! = C4V_PropList )
{
C4PropList * value_proplist = : : Game . AllocateTranslatedString ( ) ;
if ( value . GetType ( ) = = C4V_String )
{
C4String * lang = : : Strings . RegString ( lang_code ) ;
value_proplist - > SetPropertyByS ( lang , value ) ;
}
value = C4VPropList ( value_proplist ) ;
}
// Open dialogue on value
localization_dialogue . reset ( new C4ConsoleQtLocalizeStringDlg ( : : Console . GetState ( ) - > window . get ( ) , value ) ) ;
connect ( localization_dialogue . get ( ) , & C4ConsoleQtLocalizeStringDlg : : accepted , this , [ this ] ( ) {
// Usually, the proplist owned by localization_dialogue is the same as this->value
// However, it may have changed if there was an update call that modified the value while the dialogue was open
// In this case, take the value from the dialogue
SetValue ( C4VPropList ( localization_dialogue - > GetTranslations ( ) ) ) ;
// Finish editing on the value
CloseLocalizationDialogue ( ) ;
commit_pending = true ;
emit EditingDoneSignal ( ) ;
} ) ;
connect ( localization_dialogue . get ( ) , & C4ConsoleQtLocalizeStringDlg : : rejected , this , [ this ] ( ) {
CloseLocalizationDialogue ( ) ;
} ) ;
localization_dialogue - > show ( ) ;
}
}
void C4PropertyDelegateStringEditor : : CloseLocalizationDialogue ( )
{
if ( localization_dialogue )
{
localization_dialogue - > close ( ) ;
localization_dialogue . reset ( ) ;
}
}
void C4PropertyDelegateStringEditor : : StoreEditedText ( )
{
if ( text_edited )
{
// TODO: Would be better to handle escaping in the C4Value-to-string code
QString new_value = edit - > text ( ) ;
2017-05-07 19:16:01 +00:00
new_value = new_value . replace ( R " ( \ ) " , R " ( \\ ) " ) . replace ( R " ( " ) " , R " ( \ " ) " ) ;
2017-05-07 18:25:03 +00:00
C4Value text_value = C4VString ( new_value . toUtf8 ( ) ) ;
// If translatable, always store as translation proplist
// This makes it easier to collect strings to be localized in the localization overview
if ( localization_button )
{
C4PropList * value_proplist = this - > value . getPropList ( ) ;
if ( ! value_proplist )
{
value_proplist = : : Game . AllocateTranslatedString ( ) ;
}
C4String * lang = : : Strings . RegString ( lang_code ) ;
value_proplist - > SetPropertyByS ( lang , text_value ) ;
}
else
{
this - > value = text_value ;
}
text_edited = false ;
}
}
void C4PropertyDelegateStringEditor : : SetValue ( const C4Value & val )
{
// Set editor text to value
// Resolve text string and default language for localized strings
C4String * s ;
C4Value language ;
if ( localization_button )
{
s = : : Game . GetTranslatedString ( val , & language , true ) ;
C4String * language_string = language . getStr ( ) ;
SCopy ( language_string ? language_string - > GetCStr ( ) : Config . General . LanguageEx , lang_code , 2 ) ;
2017-05-07 19:02:09 +00:00
QFontMetrics fm ( localization_button - > font ( ) ) ;
localization_button - > setFixedWidth ( fm . width ( lang_code ) + 4 ) ;
2017-05-07 18:25:03 +00:00
localization_button - > setText ( QString ( lang_code ) ) ;
}
else
{
s = val . getStr ( ) ;
}
edit - > setText ( QString ( s ? s - > GetCStr ( ) : " " ) ) ;
// Remember full value with all localizations
if ( val . GetType ( ) = = C4V_PropList )
{
if ( val ! = this - > value )
{
// Localization proplist: Create a copy (C4Value::Copy() would be nice)
C4PropList * new_value_proplist = new C4PropListScript ( ) ;
this - > value = C4VPropList ( new_value_proplist ) ;
C4PropList * val_proplist = val . getPropList ( ) ;
for ( C4String * lang : val_proplist - > GetSortedLocalProperties ( ) )
{
C4Value lang_string ;
val_proplist - > GetPropertyByS ( lang , & lang_string ) ;
new_value_proplist - > SetPropertyByS ( lang , lang_string ) ;
}
}
}
else
{
this - > value = val ;
}
}
C4Value C4PropertyDelegateStringEditor : : GetValue ( )
{
// Flush edits from the text field into value
StoreEditedText ( ) ;
// Return current value
return this - > value ;
}
2016-06-16 04:40:25 +00:00
C4PropertyDelegateString : : C4PropertyDelegateString ( const C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegate ( factory , props )
{
2017-05-07 18:25:03 +00:00
if ( props )
{
translatable = props - > GetPropertyBool ( P_Translatable ) ;
}
2016-06-16 04:40:25 +00:00
}
2017-05-07 18:25:03 +00:00
void C4PropertyDelegateString : : SetEditorData ( QWidget * aeditor , const C4Value & val , const C4PropertyPath & property_path ) const
2016-06-16 04:40:25 +00:00
{
2017-05-07 18:25:03 +00:00
Editor * editor = static_cast < Editor * > ( aeditor ) ;
editor - > SetValue ( val ) ;
2016-06-16 04:40:25 +00:00
}
2017-05-07 18:25:03 +00:00
void C4PropertyDelegateString : : SetModelData ( QObject * aeditor , const C4PropertyPath & property_path , C4ConsoleQtShape * prop_shape ) const
2016-06-16 04:40:25 +00:00
{
2017-05-07 18:25:03 +00:00
Editor * editor = static_cast < Editor * > ( aeditor ) ;
2016-06-16 04:40:25 +00:00
// Only set model data when pressing Enter explicitely; not just when leaving
2017-05-07 18:25:03 +00:00
if ( editor - > IsCommitPending ( ) )
2016-06-16 04:40:25 +00:00
{
2017-05-07 18:25:03 +00:00
property_path . SetProperty ( editor - > GetValue ( ) ) ;
2017-02-26 15:45:38 +00:00
factory - > GetPropertyModel ( ) - > DoOnUpdateCall ( property_path , this ) ;
2017-05-07 18:25:03 +00:00
editor - > SetCommitPending ( false ) ;
2016-06-16 04:40:25 +00:00
}
}
2016-07-30 06:37:33 +00:00
QWidget * C4PropertyDelegateString : : CreateEditor ( const C4PropertyDelegateFactory * parent_delegate , QWidget * parent , const QStyleOptionViewItem & option , bool by_selection , bool is_child ) const
2016-06-16 04:40:25 +00:00
{
2017-05-07 18:25:03 +00:00
Editor * editor = new Editor ( parent , translatable ) ;
2016-08-28 01:24:43 +00:00
// EditingDone on return or when leaving edit field after a change has been made
2017-05-07 18:25:03 +00:00
connect ( editor , & Editor : : EditingDoneSignal , editor , [ this , editor ] ( ) {
2016-06-16 04:40:25 +00:00
emit EditingDoneSignal ( editor ) ;
} ) ;
2016-10-15 22:26:03 +00:00
// Selection in child enum: Direct focus
if ( by_selection & & is_child ) editor - > setFocus ( ) ;
2016-06-16 04:40:25 +00:00
return editor ;
}
2016-07-30 18:50:07 +00:00
QString C4PropertyDelegateString : : GetDisplayString ( const C4Value & v , C4Object * obj , bool short_names ) const
2016-06-16 04:40:25 +00:00
{
// Raw string without ""
2017-05-07 18:25:03 +00:00
C4String * s = translatable ? : : Game . GetTranslatedString ( v , nullptr , true ) : v . getStr ( ) ;
2016-06-16 04:40:25 +00:00
return QString ( s ? s - > GetCStr ( ) : " " ) ;
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateString : : IsPasteValid ( const C4Value & val ) const
{
2017-05-07 18:25:03 +00:00
// Check string type or translatable proplist
if ( val . GetType ( ) = = C4V_String ) return true ;
if ( translatable )
{
C4PropList * val_p = val . getPropList ( ) ;
if ( val_p )
{
return val_p - > GetPropertyStr ( P_Function ) = = & : : Strings . P [ P_Translate ] ;
}
}
return false ;
2016-08-19 20:12:53 +00:00
}
2016-07-28 02:35:11 +00:00
/* Delegate editor: Text left and button right */
2016-04-17 02:02:24 +00:00
C4PropertyDelegateLabelAndButtonWidget : : C4PropertyDelegateLabelAndButtonWidget ( QWidget * parent )
2016-06-03 05:20:43 +00:00
: QWidget ( parent ) , layout ( nullptr ) , label ( nullptr ) , button ( nullptr ) , button_pending ( false )
2016-04-17 02:02:24 +00:00
{
layout = new QHBoxLayout ( this ) ;
layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
layout - > setMargin ( 0 ) ;
layout - > setSpacing ( 0 ) ;
label = new QLabel ( this ) ;
2016-06-08 04:37:48 +00:00
QPalette palette = label - > palette ( ) ;
palette . setColor ( label - > foregroundRole ( ) , palette . color ( QPalette : : HighlightedText ) ) ;
palette . setColor ( label - > backgroundRole ( ) , palette . color ( QPalette : : Highlight ) ) ;
label - > setPalette ( palette ) ;
2016-04-17 02:02:24 +00:00
layout - > addWidget ( label ) ;
button = new QPushButton ( QString ( LoadResStr ( " IDS_CNS_MORE " ) ) , this ) ;
layout - > addWidget ( button ) ;
2016-06-15 04:15:08 +00:00
// Make sure to draw over view in background
setPalette ( palette ) ;
2016-06-08 04:37:48 +00:00
setAutoFillBackground ( true ) ;
2016-04-17 02:02:24 +00:00
}
2016-07-28 02:35:11 +00:00
/* Descend path delegate base class for arrays and proplist */
2016-06-03 05:20:43 +00:00
C4PropertyDelegateDescendPath : : C4PropertyDelegateDescendPath ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegate ( factory , props ) , edit_on_selection ( true )
{
if ( props )
{
2016-07-13 21:18:08 +00:00
info_proplist = C4VPropList ( props ) ; // Descend info is this definition
2016-06-03 05:20:43 +00:00
edit_on_selection = props - > GetPropertyBool ( P_EditOnSelection , edit_on_selection ) ;
2016-07-06 04:56:08 +00:00
descend_path = props - > GetPropertyStr ( P_DescendPath ) ;
2016-06-03 05:20:43 +00:00
}
}
2016-06-06 05:54:05 +00:00
2016-06-03 05:20:43 +00:00
void C4PropertyDelegateDescendPath : : SetEditorData ( QWidget * aeditor , const C4Value & val , const C4PropertyPath & property_path ) const
{
Editor * editor = static_cast < Editor * > ( aeditor ) ;
2016-07-30 18:50:07 +00:00
editor - > label - > setText ( GetDisplayString ( val , nullptr , false ) ) ;
2016-06-03 05:20:43 +00:00
editor - > last_value = val ;
editor - > property_path = property_path ;
if ( editor - > button_pending ) emit editor - > button - > pressed ( ) ;
}
2016-07-30 06:37:33 +00:00
QWidget * C4PropertyDelegateDescendPath : : CreateEditor ( const class C4PropertyDelegateFactory * parent_delegate , QWidget * parent , const QStyleOptionViewItem & option , bool by_selection , bool is_child ) const
2016-06-03 05:20:43 +00:00
{
// Otherwise create display and button to descend path
Editor * editor ;
std : : unique_ptr < Editor > peditor ( ( editor = new Editor ( parent ) ) ) ;
connect ( editor - > button , & QPushButton : : pressed , this , [ editor , this ] ( ) {
// Value to descend into: Use last value on auto-press because it will not have been updated into the game yet
// (and cannot be without going async in network mode)
// On regular button press, re-resolve path to value
C4Value val = editor - > button_pending ? editor - > last_value : editor - > property_path . ResolveValue ( ) ;
2016-07-06 03:27:55 +00:00
bool is_proplist = ! ! val . getPropList ( ) , is_array = ! ! val . getArray ( ) ;
if ( is_proplist | | is_array )
2016-06-03 05:20:43 +00:00
{
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
2016-07-06 04:56:08 +00:00
// Allow descending into a sub-path
C4PropertyPath descend_property_path ( editor - > property_path ) ;
if ( is_proplist & & descend_path )
{
2016-07-13 21:18:08 +00:00
// Descend value into sub-path
2016-07-06 04:56:08 +00:00
val . _getPropList ( ) - > GetPropertyByS ( descend_path . Get ( ) , & val ) ;
2016-07-13 21:18:08 +00:00
// Descend info_proplist into sub-path
2016-07-06 04:56:08 +00:00
if ( info_proplist )
{
2016-07-13 21:18:08 +00:00
C4PropList * info_editorprops = info_proplist - > GetPropertyPropList ( P_EditorProps ) ;
if ( info_editorprops )
2016-07-06 04:56:08 +00:00
{
2016-07-13 21:18:08 +00:00
C4Value sub_info_proplist_val ;
info_editorprops - > GetPropertyByS ( descend_path . Get ( ) , & sub_info_proplist_val ) ;
info_proplist = sub_info_proplist_val . getPropList ( ) ;
2016-07-06 04:56:08 +00:00
}
}
2016-07-13 21:18:08 +00:00
// Descend property path into sub-path
2016-07-06 04:56:08 +00:00
descend_property_path = C4PropertyPath ( descend_property_path , descend_path - > GetCStr ( ) ) ;
}
// No info proplist: Fall back to regular proplist viewing mode
2016-06-03 05:20:43 +00:00
if ( ! info_proplist ) info_proplist = val . getPropList ( ) ;
2016-07-13 21:18:08 +00:00
this - > factory - > GetPropertyModel ( ) - > DescendPath ( val , info_proplist , descend_property_path ) ;
2016-06-03 05:20:43 +00:00
: : Console . EditCursor . InvalidateSelection ( ) ;
}
} ) ;
if ( by_selection & & edit_on_selection ) editor - > button_pending = true ;
return peditor . release ( ) ;
}
2016-07-28 02:35:11 +00:00
/* Array descend delegate */
2016-06-06 05:54:05 +00:00
C4PropertyDelegateArray : : C4PropertyDelegateArray ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateDescendPath ( factory , props ) , max_array_display ( 0 ) , element_delegate ( nullptr )
2016-06-03 05:20:43 +00:00
{
2016-06-06 05:54:05 +00:00
if ( props )
2016-06-03 05:20:43 +00:00
{
2016-06-06 05:54:05 +00:00
max_array_display = props - > GetPropertyInt ( P_Display ) ;
2016-06-03 05:20:43 +00:00
}
}
2016-08-19 20:12:53 +00:00
void C4PropertyDelegateArray : : ResolveElementDelegate ( ) const
2016-06-06 05:54:05 +00:00
{
2016-07-13 21:18:08 +00:00
if ( ! element_delegate )
{
C4Value element_delegate_value ;
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
if ( info_proplist ) info_proplist - > GetProperty ( P_Elements , & element_delegate_value ) ;
element_delegate = factory - > GetDelegateByValue ( element_delegate_value ) ;
}
2016-08-19 20:12:53 +00:00
}
QString C4PropertyDelegateArray : : GetDisplayString ( const C4Value & v , C4Object * obj , bool short_names ) const
{
C4ValueArray * arr = v . getArray ( ) ;
if ( ! arr ) return QString ( LoadResStr ( " IDS_CNS_INVALID " ) ) ;
int32_t n = v . _getArray ( ) - > GetSize ( ) ;
ResolveElementDelegate ( ) ;
2016-06-06 05:54:05 +00:00
if ( max_array_display & & n )
{
QString result = " [ " ;
for ( int32_t i = 0 ; i < std : : min < int32_t > ( n , max_array_display ) ; + + i )
{
if ( i ) result + = " , " ;
2016-07-30 18:50:07 +00:00
result + = element_delegate - > GetDisplayString ( v . _getArray ( ) - > GetItem ( i ) , obj , true ) ;
2016-06-06 05:54:05 +00:00
}
if ( n > max_array_display ) result + = " ,... " ;
result + = " ] " ;
return result ;
}
2016-07-30 18:50:07 +00:00
else if ( n | | ! short_names )
2016-06-06 05:54:05 +00:00
{
// Default display (or display with 0 elements): Just show element number
return QString ( LoadResStr ( " IDS_CNS_ARRAYSHORT " ) ) . arg ( n ) ;
}
2016-07-30 18:50:07 +00:00
else
{
// Short display of empty array: Just leave it out.
return QString ( " " ) ;
}
2016-06-06 05:54:05 +00:00
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateArray : : IsPasteValid ( const C4Value & val ) const
{
// Check array type and all contents
C4ValueArray * arr = val . getArray ( ) ;
if ( ! arr ) return false ;
int32_t n = arr - > GetSize ( ) ;
if ( n )
{
ResolveElementDelegate ( ) ;
for ( int32_t i = 0 ; i < arr - > GetSize ( ) ; + + i )
{
C4Value item = arr - > GetItem ( i ) ;
if ( ! element_delegate - > IsPasteValid ( item ) ) return false ;
}
}
return true ;
}
2016-07-28 02:35:11 +00:00
/* Proplist descend delegate */
2016-06-06 05:54:05 +00:00
C4PropertyDelegatePropList : : C4PropertyDelegatePropList ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateDescendPath ( factory , props )
{
if ( props )
{
display_string = props - > GetPropertyStr ( P_Display ) ;
}
}
2016-07-30 18:50:07 +00:00
QString C4PropertyDelegatePropList : : GetDisplayString ( const C4Value & v , C4Object * obj , bool short_names ) const
2016-06-06 05:54:05 +00:00
{
C4PropList * data = v . getPropList ( ) ;
if ( ! data ) return QString ( LoadResStr ( " IDS_CNS_INVALID " ) ) ;
if ( ! display_string ) return QString ( " {...} " ) ;
2016-07-04 17:20:07 +00:00
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
2016-07-13 21:18:08 +00:00
C4PropList * info_editorprops = info_proplist ? info_proplist - > GetPropertyPropList ( P_EditorProps ) : nullptr ;
2016-06-06 05:54:05 +00:00
// Replace all {{name}} by property values of name
QString result = display_string - > GetCStr ( ) ;
int32_t pos0 , pos1 ;
C4Value cv ;
while ( ( pos0 = result . indexOf ( " {{ " ) ) > = 0 )
{
pos1 = result . indexOf ( " }} " , pos0 + 2 ) ;
if ( pos1 < 0 ) break ; // placeholder not closed
2016-07-04 17:20:07 +00:00
// Get child value
2016-06-06 05:54:05 +00:00
QString substring = result . mid ( pos0 + 2 , pos1 - pos0 - 2 ) ;
2016-07-13 21:18:08 +00:00
C4RefCntPointer < C4String > psubstring = : : Strings . RegString ( substring . toUtf8 ( ) ) ;
if ( ! data - > GetPropertyByS ( psubstring . Get ( ) , & cv ) ) cv . Set0 ( ) ;
2016-07-04 17:20:07 +00:00
// Try to display using child delegate
QString display_value ;
2016-07-13 21:18:08 +00:00
if ( info_editorprops )
2016-07-04 17:20:07 +00:00
{
C4Value child_delegate_val ;
2016-07-13 21:18:08 +00:00
if ( info_editorprops - > GetPropertyByS ( psubstring . Get ( ) , & child_delegate_val ) )
2016-07-04 17:20:07 +00:00
{
C4PropertyDelegate * child_delegate = factory - > GetDelegateByValue ( child_delegate_val ) ;
if ( child_delegate )
{
2016-07-30 18:50:07 +00:00
display_value = child_delegate - > GetDisplayString ( cv , obj , true ) ;
2016-07-04 17:20:07 +00:00
}
}
}
// If there is no child delegate, fall back to GetDataString()
if ( display_value . isEmpty ( ) ) display_value = cv . GetDataString ( ) . getData ( ) ;
// Put value into display string
result . replace ( pos0 , pos1 - pos0 + 2 , display_value ) ;
2016-06-06 05:54:05 +00:00
}
return result ;
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegatePropList : : IsPasteValid ( const C4Value & val ) const
{
// Check proplist type
C4PropList * pval = val . getPropList ( ) ;
if ( ! pval ) return false ;
// Are there restrictions on allowed properties?
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
C4PropList * info_editorprops = info_proplist ? info_proplist - > GetPropertyPropList ( P_EditorProps ) : nullptr ;
if ( ! info_editorprops ) return true ; // No restrictions: Allow everything
// Otherwise all types properties must be valid for paste
// (Extra properties are OK)
std : : vector < C4String * > properties = info_editorprops - > GetUnsortedProperties ( nullptr , nullptr ) ;
for ( C4String * prop_name : properties )
{
if ( prop_name = = & : : Strings . P [ P_Prototype ] ) continue ;
C4Value child_delegate_val ;
if ( ! info_editorprops - > GetPropertyByS ( prop_name , & child_delegate_val ) ) continue ;
C4PropertyDelegate * child_delegate = factory - > GetDelegateByValue ( child_delegate_val ) ;
if ( ! child_delegate ) continue ;
C4Value child_val ;
pval - > GetPropertyByS ( prop_name , & child_val ) ;
if ( ! child_delegate - > IsPasteValid ( child_val ) ) return false ;
}
return true ;
}
2016-06-03 05:20:43 +00:00
2016-10-14 14:49:14 +00:00
/* Effect delegate: Allows removal and descend into proplist */
C4PropertyDelegateEffectEditor : : C4PropertyDelegateEffectEditor ( QWidget * parent ) : QWidget ( parent ) , layout ( nullptr ) , remove_button ( nullptr ) , edit_button ( nullptr )
{
layout = new QHBoxLayout ( this ) ;
layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
layout - > setMargin ( 0 ) ;
layout - > setSpacing ( 0 ) ;
remove_button = new QPushButton ( QString ( LoadResStr ( " IDS_CNS_REMOVE " ) ) , this ) ;
layout - > addWidget ( remove_button ) ;
edit_button = new QPushButton ( QString ( LoadResStr ( " IDS_CNS_MORE " ) ) , this ) ;
layout - > addWidget ( edit_button ) ;
// Make sure to draw over view in background
setAutoFillBackground ( true ) ;
}
C4PropertyDelegateEffect : : C4PropertyDelegateEffect ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegate ( factory , props )
{
}
void C4PropertyDelegateEffect : : SetEditorData ( QWidget * aeditor , const C4Value & val , const C4PropertyPath & property_path ) const
{
Editor * editor = static_cast < Editor * > ( aeditor ) ;
editor - > property_path = property_path ;
}
QWidget * C4PropertyDelegateEffect : : CreateEditor ( const class C4PropertyDelegateFactory * parent_delegate , QWidget * parent , const QStyleOptionViewItem & option , bool by_selection , bool is_child ) const
{
Editor * editor ;
std : : unique_ptr < Editor > peditor ( ( editor = new Editor ( parent ) ) ) ;
// Remove effect button
connect ( editor - > remove_button , & QPushButton : : pressed , this , [ editor , this ] ( ) {
// Compose an effect remove call
editor - > property_path . DoCall ( " RemoveEffect(nil, nil, %s) " ) ;
emit EditingDoneSignal ( editor ) ;
} ) ;
// Edit effect button
connect ( editor - > edit_button , & QPushButton : : pressed , this , [ editor , this ] ( ) {
// Descend into effect proplist (if the effect still exists)
C4Value effect_val = editor - > property_path . ResolveValue ( ) ;
C4PropList * effect_proplist = effect_val . getPropList ( ) ;
if ( ! effect_proplist )
{
// Effect lost
emit EditingDoneSignal ( editor ) ;
}
else
{
// Effect OK. Edit it.
this - > factory - > GetPropertyModel ( ) - > DescendPath ( effect_val , effect_proplist , editor - > property_path ) ;
: : Console . EditCursor . InvalidateSelection ( ) ;
}
} ) ;
return peditor . release ( ) ;
}
QString C4PropertyDelegateEffect : : GetDisplayString ( const C4Value & v , C4Object * obj , bool short_names ) const
{
C4PropList * effect_proplist = v . getPropList ( ) ;
C4Effect * effect = effect_proplist ? effect_proplist - > GetEffect ( ) : nullptr ;
if ( effect )
{
if ( effect - > IsActive ( ) )
{
return QString ( " t=%1, interval=%2 " ) . arg ( effect - > iTime ) . arg ( effect - > iInterval ) ;
}
else
{
return QString ( LoadResStr ( " IDS_CNS_DEADEFFECT " ) ) ;
}
}
else
{
return QString ( " nil " ) ;
}
}
bool C4PropertyDelegateEffect : : GetPropertyValue ( const C4Value & container , C4String * key , int32_t index , C4Value * out_val ) const
{
// Resolve effect by calling script function
if ( ! key ) return false ;
* out_val = AulExec . DirectExec ( : : ScriptEngine . GetPropList ( ) , key - > GetCStr ( ) , " resolve effect " , false , nullptr ) ;
return true ;
}
C4PropertyPath C4PropertyDelegateEffect : : GetPathForProperty ( C4ConsoleQtPropListModelProperty * editor_prop ) const
{
// Property path is used directly for getting effect. No set function needed.
return editor_prop - > property_path ;
}
2016-07-28 02:35:11 +00:00
/* Color delegate */
2016-04-17 02:02:24 +00:00
C4PropertyDelegateColor : : C4PropertyDelegateColor ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
2016-09-04 02:03:47 +00:00
: C4PropertyDelegate ( factory , props ) , alpha_mask ( 0u )
2016-04-17 02:02:24 +00:00
{
2016-09-04 02:03:47 +00:00
if ( props )
{
alpha_mask = props - > GetPropertyInt ( P_Alpha ) < < 24 ;
}
2016-04-17 02:02:24 +00:00
}
uint32_t GetTextColorForBackground ( uint32_t background_color )
{
// White text on dark background; black text on bright background
uint8_t r = ( background_color > > 16 ) & 0xff ;
uint8_t g = ( background_color > > 8 ) & 0xff ;
uint8_t b = ( background_color > > 0 ) & 0xff ;
int32_t lgt = r * 30 + g * 59 + b * 11 ;
return ( lgt > 16000 ) ? 0 : 0xffffff ;
}
2016-05-25 03:06:00 +00:00
void C4PropertyDelegateColor : : SetEditorData ( QWidget * aeditor , const C4Value & val , const C4PropertyPath & property_path ) const
2016-04-17 02:02:24 +00:00
{
Editor * editor = static_cast < Editor * > ( aeditor ) ;
uint32_t background_color = static_cast < uint32_t > ( val . getInt ( ) ) & 0xffffff ;
uint32_t foreground_color = GetTextColorForBackground ( background_color ) ;
QPalette palette = editor - > label - > palette ( ) ;
palette . setColor ( editor - > label - > backgroundRole ( ) , QColor ( QRgb ( background_color ) ) ) ;
palette . setColor ( editor - > label - > foregroundRole ( ) , QColor ( QRgb ( foreground_color ) ) ) ;
editor - > label - > setPalette ( palette ) ;
editor - > label - > setAutoFillBackground ( true ) ;
2016-07-30 18:50:07 +00:00
editor - > label - > setText ( GetDisplayString ( val , nullptr , false ) ) ;
2016-04-17 02:02:24 +00:00
editor - > last_value = val ;
}
2016-07-18 23:24:53 +00:00
void C4PropertyDelegateColor : : SetModelData ( QObject * aeditor , const C4PropertyPath & property_path , C4ConsoleQtShape * prop_shape ) const
2016-04-17 02:02:24 +00:00
{
Editor * editor = static_cast < Editor * > ( aeditor ) ;
property_path . SetProperty ( editor - > last_value ) ;
2017-02-26 15:45:38 +00:00
factory - > GetPropertyModel ( ) - > DoOnUpdateCall ( property_path , this ) ;
2016-04-17 02:02:24 +00:00
}
2016-07-30 06:37:33 +00:00
QWidget * C4PropertyDelegateColor : : CreateEditor ( const class C4PropertyDelegateFactory * parent_delegate , QWidget * parent , const QStyleOptionViewItem & option , bool by_selection , bool is_child ) const
2016-04-17 02:02:24 +00:00
{
Editor * editor ;
std : : unique_ptr < Editor > peditor ( ( editor = new Editor ( parent ) ) ) ;
connect ( editor - > button , & QPushButton : : pressed , this , [ editor , this ] ( ) {
2016-10-15 22:26:03 +00:00
this - > OpenColorDialogue ( editor ) ;
2016-04-17 02:02:24 +00:00
} ) ;
2016-10-15 22:26:03 +00:00
// Selection in child enum: Open dialogue immediately
if ( by_selection & & is_child ) OpenColorDialogue ( editor ) ;
2016-04-17 02:02:24 +00:00
return peditor . release ( ) ;
}
2016-07-30 18:50:07 +00:00
QString C4PropertyDelegateColor : : GetDisplayString ( const C4Value & v , C4Object * obj , bool short_names ) const
2016-04-17 02:02:24 +00:00
{
2016-04-25 20:17:10 +00:00
return QString ( " #%1 " ) . arg ( uint32_t ( v . getInt ( ) ) , 8 , 16 , QChar ( ' 0 ' ) ) ;
2016-04-17 02:02:24 +00:00
}
QColor C4PropertyDelegateColor : : GetDisplayTextColor ( const C4Value & val , class C4Object * obj ) const
{
uint32_t background_color = static_cast < uint32_t > ( val . getInt ( ) ) & 0xffffff ;
uint32_t foreground_color = GetTextColorForBackground ( background_color ) ;
return QColor ( foreground_color ) ;
}
QColor C4PropertyDelegateColor : : GetDisplayBackgroundColor ( const C4Value & val , class C4Object * obj ) const
{
return static_cast < uint32_t > ( val . getInt ( ) ) & 0xffffff ;
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateColor : : IsPasteValid ( const C4Value & val ) const
{
// Color is always int
if ( val . GetType ( ) ! = C4V_Int ) return false ;
return true ;
}
2016-10-15 22:26:03 +00:00
void C4PropertyDelegateColor : : OpenColorDialogue ( C4PropertyDelegateLabelAndButtonWidget * editor ) const
{
// Show actual dialogue to change the color
QColor clr = QColorDialog : : getColor ( QColor ( editor - > last_value . getInt ( ) & ( ~ alpha_mask ) ) , editor , QString ( ) , QColorDialog : : ShowAlphaChannel ) ;
editor - > last_value . SetInt ( clr . rgba ( ) | alpha_mask ) ;
this - > SetEditorData ( editor , editor - > last_value , C4PropertyPath ( ) ) ; // force update on display
emit EditingDoneSignal ( editor ) ;
}
2016-07-25 05:05:15 +00:00
/* Enum delegate combo box item delegate */
2016-07-28 02:35:11 +00:00
bool C4StyledItemDelegateWithButton : : editorEvent ( QEvent * event , QAbstractItemModel * model , const QStyleOptionViewItem & option , const QModelIndex & index )
2016-07-25 05:05:15 +00:00
{
// Mouse move over a cell: Display tooltip if over help button
2016-07-28 02:35:11 +00:00
QEvent : : Type trigger_type = ( button_type = = BT_Help ) ? QEvent : : MouseMove : QEvent : : MouseButtonPress ;
if ( event - > type ( ) = = trigger_type )
2016-07-25 05:05:15 +00:00
{
2016-07-28 02:35:11 +00:00
QVariant btn = model - > data ( index , Qt : : DecorationRole ) ;
if ( ! btn . isNull ( ) )
2016-07-25 05:05:15 +00:00
{
2016-07-28 02:35:11 +00:00
QMouseEvent * mevent = static_cast < QMouseEvent * > ( event ) ;
if ( option . rect . contains ( mevent - > localPos ( ) . toPoint ( ) ) )
2016-07-25 05:05:15 +00:00
{
2016-07-28 02:35:11 +00:00
if ( mevent - > localPos ( ) . x ( ) > = option . rect . x ( ) + option . rect . width ( ) - option . rect . height ( ) )
2016-07-25 05:05:15 +00:00
{
2016-07-28 02:35:11 +00:00
switch ( button_type )
2016-07-26 01:39:35 +00:00
{
2016-07-28 02:35:11 +00:00
case BT_Help :
if ( Config . Developer . ShowHelp )
{
QString tooltip_text = model - > data ( index , Qt : : ToolTipRole ) . toString ( ) ;
QToolTip : : showText ( mevent - > globalPos ( ) , tooltip_text ) ;
}
break ;
case BT_PlaySound :
StartSoundEffect ( model - > data ( index , Qt : : ToolTipRole ) . toString ( ) . toUtf8 ( ) ) ;
return true ; // handled
2016-07-26 01:39:35 +00:00
}
2016-07-25 05:05:15 +00:00
}
}
}
}
return QStyledItemDelegate : : editorEvent ( event , model , option , index ) ;
}
2016-07-28 02:35:11 +00:00
void C4StyledItemDelegateWithButton : : paint ( QPainter * painter , const QStyleOptionViewItem & option , const QModelIndex & index ) const
2016-07-25 05:05:15 +00:00
{
// Paint icon on the right
QStyleOptionViewItem override_option = option ;
override_option . decorationPosition = QStyleOptionViewItem : : Right ;
QStyledItemDelegate : : paint ( painter , override_option , index ) ;
}
/* Enum delegate combo box */
2016-07-28 04:24:28 +00:00
C4DeepQComboBox : : C4DeepQComboBox ( QWidget * parent , C4StyledItemDelegateWithButton : : ButtonType button_type , bool editable )
2016-10-16 05:12:21 +00:00
: QComboBox ( parent ) , last_popup_height ( 0 ) , is_next_close_blocked ( false ) , editable ( editable ) , manual_text_edited ( false )
2016-06-15 04:15:08 +00:00
{
2017-05-03 18:28:00 +00:00
item_delegate = std : : make_unique < C4StyledItemDelegateWithButton > ( button_type ) ;
2016-06-15 04:15:08 +00:00
QTreeView * view = new QTreeView ( this ) ;
view - > setFrameShape ( QFrame : : NoFrame ) ;
view - > setSelectionBehavior ( QTreeView : : SelectRows ) ;
view - > setAllColumnsShowFocus ( true ) ;
view - > header ( ) - > hide ( ) ;
2016-07-25 05:05:15 +00:00
view - > setItemDelegate ( item_delegate . get ( ) ) ;
2016-07-28 04:24:28 +00:00
setEditable ( editable ) ;
2016-06-16 04:09:53 +00:00
// On expansion, enlarge view if necessery
connect ( view , & QTreeView : : expanded , this , [ this , view ] ( const QModelIndex & index )
{
if ( this - > model ( ) & & view - > parentWidget ( ) )
{
int child_row_count = this - > model ( ) - > rowCount ( index ) ;
if ( child_row_count > 0 )
{
// Get space to contain expanded leaf+1 item
QModelIndex last_index = this - > model ( ) - > index ( child_row_count - 1 , 0 , index ) ;
int needed_height = view - > visualRect ( last_index ) . bottom ( ) - view - > visualRect ( index ) . top ( ) + view - > height ( ) - view - > parentWidget ( ) - > height ( ) + view - > visualRect ( last_index ) . height ( ) ;
int available_height = QApplication : : desktop ( ) - > availableGeometry ( view - > mapToGlobal ( QPoint ( 1 , 1 ) ) ) . height ( ) ; // but do not expand past screen size
int new_height = std : : min ( needed_height , available_height - 20 ) ;
if ( view - > parentWidget ( ) - > height ( ) < new_height ) view - > parentWidget ( ) - > resize ( view - > parentWidget ( ) - > width ( ) , ( this - > last_popup_height = new_height ) ) ;
}
}
} ) ;
2016-06-16 20:15:15 +00:00
// On selection, highlight object in editor
view - > setMouseTracking ( true ) ;
connect ( view , & QTreeView : : entered , this , [ this ] ( const QModelIndex & index )
{
2016-06-20 23:13:50 +00:00
C4Object * obj = nullptr ;
int32_t obj_number = this - > model ( ) - > data ( index , ObjectHighlightRole ) . toInt ( ) ;
if ( obj_number ) obj = : : Objects . SafeObjectPointer ( obj_number ) ;
: : Console . EditCursor . SetHighlightedObject ( obj ) ;
2016-06-16 20:15:15 +00:00
} ) ;
2016-07-28 02:35:11 +00:00
// New item selection through combo box: Update model position
connect ( this , static_cast < void ( QComboBox : : * ) ( int ) > ( & QComboBox : : activated ) ,
[ this ] ( int index )
{
QModelIndex current = this - > view ( ) - > currentIndex ( ) ;
QVariant selected_data = this - > model ( ) - > data ( current , OptionIndexRole ) ;
if ( selected_data . type ( ) = = QVariant : : Int )
{
2016-10-16 05:12:21 +00:00
// Reset manual text edit flag because the text is now provided by the view
manual_text_edited = false ;
2016-07-28 02:35:11 +00:00
// Finish selection
2016-07-28 04:24:28 +00:00
setCurrentModelIndex ( current ) ;
2016-07-28 02:35:11 +00:00
emit NewItemSelected ( selected_data . toInt ( ) ) ;
}
} ) ;
2016-07-28 04:24:28 +00:00
// New text typed in
if ( editable )
{
2016-10-16 05:12:21 +00:00
// text change event only sent after manual text change
connect ( lineEdit ( ) , & QLineEdit : : textEdited , [ this ] ( const QString & text )
{
manual_text_edited = true ;
last_edited_text = text ;
} ) ;
// reflect in data after return press and when focus is lost
2016-07-28 04:24:28 +00:00
connect ( lineEdit ( ) , & QLineEdit : : returnPressed , [ this ] ( )
{
2016-10-16 05:12:21 +00:00
if ( manual_text_edited )
{
emit TextChanged ( last_edited_text ) ;
manual_text_edited = false ;
}
} ) ;
connect ( lineEdit ( ) , & QLineEdit : : editingFinished , [ this ] ( )
{
if ( manual_text_edited )
{
emit TextChanged ( last_edited_text ) ;
manual_text_edited = false ;
}
2016-07-28 04:24:28 +00:00
} ) ;
}
2016-06-16 20:15:15 +00:00
// Connect view to combobox
2016-06-15 04:15:08 +00:00
setView ( view ) ;
view - > viewport ( ) - > installEventFilter ( this ) ;
2016-07-25 05:05:15 +00:00
// No help icons in main box, unless dropped down
default_icon_size = iconSize ( ) ;
setIconSize ( QSize ( 0 , 0 ) ) ;
2016-06-15 04:15:08 +00:00
}
void C4DeepQComboBox : : showPopup ( )
{
// New selection: Reset to root of model
setRootModelIndex ( QModelIndex ( ) ) ;
2016-07-25 05:05:15 +00:00
setIconSize ( default_icon_size ) ;
2016-06-15 04:15:08 +00:00
QComboBox : : showPopup ( ) ;
view ( ) - > setMinimumWidth ( 200 ) ; // prevent element list from becoming too small in nested dialogues
2016-06-16 04:09:53 +00:00
if ( last_popup_height & & view ( ) - > parentWidget ( ) ) view ( ) - > parentWidget ( ) - > resize ( view ( ) - > parentWidget ( ) - > width ( ) , last_popup_height ) ;
2016-06-15 04:15:08 +00:00
}
void C4DeepQComboBox : : hidePopup ( )
{
2016-07-28 02:35:11 +00:00
// Cleanup tree combobox
: : Console . EditCursor . SetHighlightedObject ( nullptr ) ;
setIconSize ( QSize ( 0 , 0 ) ) ;
QComboBox : : hidePopup ( ) ;
2016-06-15 04:15:08 +00:00
}
void C4DeepQComboBox : : setCurrentModelIndex ( QModelIndex new_index )
{
setRootModelIndex ( new_index . parent ( ) ) ;
setCurrentIndex ( new_index . row ( ) ) ;
2016-07-28 04:24:28 +00:00
// Adjust text
if ( editable )
{
lineEdit ( ) - > setText ( this - > model ( ) - > data ( new_index , ValueStringRole ) . toString ( ) ) ;
2016-10-16 05:12:21 +00:00
manual_text_edited = false ;
2016-07-28 04:24:28 +00:00
}
2016-06-15 04:15:08 +00:00
}
2016-06-20 23:48:02 +00:00
int32_t C4DeepQComboBox : : GetCurrentSelectionIndex ( )
{
QVariant selected_data = model ( ) - > data ( model ( ) - > index ( currentIndex ( ) , 0 , rootModelIndex ( ) ) , OptionIndexRole ) ;
if ( selected_data . type ( ) = = QVariant : : Int )
{
// Valid selection
return selected_data . toInt ( ) ;
}
else
{
// Invalid selection
return - 1 ;
}
}
2016-06-16 04:09:53 +00:00
// event filter for view: Catch mouse clicks to prevent closing from simple mouse clicks
2016-06-15 04:15:08 +00:00
bool C4DeepQComboBox : : eventFilter ( QObject * obj , QEvent * event )
{
2016-07-28 02:35:11 +00:00
if ( obj = = view ( ) - > viewport ( ) )
2016-06-15 04:15:08 +00:00
{
2016-07-28 02:35:11 +00:00
if ( event - > type ( ) = = QEvent : : MouseButtonPress )
{
QPoint pos = static_cast < QMouseEvent * > ( event ) - > pos ( ) ;
QModelIndex pressed_index = view ( ) - > indexAt ( pos ) ;
QRect item_rect = view ( ) - > visualRect ( pressed_index ) ;
// Check if a group was clicked
bool item_clicked = item_rect . contains ( pos ) ;
if ( item_clicked )
{
QVariant selected_data = model ( ) - > data ( pressed_index , OptionIndexRole ) ;
if ( selected_data . type ( ) ! = QVariant : : Int )
{
// This is a group. Just expand that entry.
QTreeView * tview = static_cast < QTreeView * > ( view ( ) ) ;
if ( ! tview - > isExpanded ( pressed_index ) )
{
tview - > setExpanded ( pressed_index , true ) ;
int32_t child_row_count = model ( ) - > rowCount ( pressed_index ) ;
tview - > scrollTo ( model ( ) - > index ( child_row_count - 1 , 0 , pressed_index ) , QAbstractItemView : : EnsureVisible ) ;
tview - > scrollTo ( pressed_index , QAbstractItemView : : EnsureVisible ) ;
}
is_next_close_blocked = true ;
return true ;
}
}
else
{
is_next_close_blocked = true ;
return false ;
}
// Delegate handling: The forward to delegate screws up for me sometimes and just stops randomly
// Prevent this by calling the event directly
QStyleOptionViewItem option ;
option . rect = view ( ) - > visualRect ( pressed_index ) ;
if ( item_delegate - > editorEvent ( event , model ( ) , option , pressed_index ) )
{
// If the down event is taken by a music play event, ignore the following button up
is_next_close_blocked = true ;
return true ;
}
}
else if ( event - > type ( ) = = QEvent : : MouseButtonRelease )
{
if ( is_next_close_blocked )
{
is_next_close_blocked = false ;
return true ;
}
}
2016-06-15 04:15:08 +00:00
}
2016-07-28 02:35:11 +00:00
return QComboBox : : eventFilter ( obj , event ) ;
2016-06-15 04:15:08 +00:00
}
2016-07-28 02:35:11 +00:00
/* Enumeration delegate editor */
2016-07-18 23:24:53 +00:00
void C4PropertyDelegateEnumEditor : : paintEvent ( QPaintEvent * ev )
{
// Draw self
QWidget : : paintEvent ( ev ) ;
// Draw shape widget
if ( paint_parameter_delegate & & parameter_widget )
{
QPainter p ( this ) ;
QStyleOptionViewItem view_item ;
view_item . rect . setTopLeft ( parameter_widget - > mapToParent ( parameter_widget - > rect ( ) . topLeft ( ) ) ) ;
view_item . rect . setBottomRight ( parameter_widget - > mapToParent ( parameter_widget - > rect ( ) . bottomRight ( ) ) ) ;
paint_parameter_delegate - > Paint ( & p , view_item , last_parameter_val ) ;
//p.fillRect(view_item.rect, QColor("red"));
}
}
2016-07-28 02:35:11 +00:00
/* Enumeration (dropdown list) delegate */
2016-04-17 02:02:24 +00:00
C4PropertyDelegateEnum : : C4PropertyDelegateEnum ( const C4PropertyDelegateFactory * factory , C4PropList * props , const C4ValueArray * poptions )
2016-08-27 20:46:38 +00:00
: C4PropertyDelegate ( factory , props ) , allow_editing ( false ) , sorted ( false )
2016-04-04 03:46:58 +00:00
{
// Build enum options from C4Value definitions in script
2016-04-09 18:20:31 +00:00
if ( ! poptions & & props ) poptions = props - > GetPropertyArray ( P_Options ) ;
2016-06-08 04:37:48 +00:00
C4String * default_option_key , * default_value_key = nullptr ;
if ( props )
{
default_option_key = props - > GetPropertyStr ( P_OptionKey ) ;
default_value_key = props - > GetPropertyStr ( P_ValueKey ) ;
2016-07-28 04:24:28 +00:00
allow_editing = props - > GetPropertyBool ( P_AllowEditing ) ;
2016-07-30 18:13:23 +00:00
empty_name = props - > GetPropertyStr ( P_EmptyName ) ;
2016-08-27 20:46:38 +00:00
sorted = props - > GetPropertyBool ( P_Sorted ) ;
2016-10-16 05:12:21 +00:00
default_option . option_key = default_option_key ;
default_option . value_key = default_value_key ;
2016-06-08 04:37:48 +00:00
}
2016-04-09 18:20:31 +00:00
if ( poptions )
2016-04-04 03:46:58 +00:00
{
2016-04-09 18:20:31 +00:00
options . reserve ( poptions - > GetSize ( ) ) ;
for ( int32_t i = 0 ; i < poptions - > GetSize ( ) ; + + i )
{
const C4Value & v = poptions - > GetItem ( i ) ;
2016-04-17 02:02:24 +00:00
C4PropList * props = v . getPropList ( ) ;
2016-04-09 18:20:31 +00:00
if ( ! props ) continue ;
Option option ;
2016-08-27 05:12:08 +00:00
option . props . SetPropList ( props ) ;
2016-04-09 18:20:31 +00:00
option . name = props - > GetPropertyStr ( P_Name ) ;
2016-07-30 18:13:23 +00:00
if ( ! option . name ) option . name = : : Strings . RegString ( " ??? " ) ;
2016-07-25 05:05:15 +00:00
option . help = props - > GetPropertyStr ( P_EditorHelp ) ;
2016-06-15 04:15:08 +00:00
option . group = props - > GetPropertyStr ( P_Group ) ;
2016-04-09 18:20:31 +00:00
option . value_key = props - > GetPropertyStr ( P_ValueKey ) ;
2016-06-08 04:37:48 +00:00
if ( ! option . value_key ) option . value_key = default_value_key ;
2016-04-09 18:20:31 +00:00
props - > GetProperty ( P_Value , & option . value ) ;
2016-07-30 18:13:23 +00:00
if ( option . value . GetType ( ) = = C4V_Nil & & empty_name ) option . name = empty_name . Get ( ) ;
2016-07-30 18:50:07 +00:00
option . short_name = props - > GetPropertyStr ( P_ShortName ) ;
if ( ! option . short_name ) option . short_name = option . name . Get ( ) ;
2016-09-01 22:32:24 +00:00
props - > GetProperty ( P_DefaultValueFunction , & option . value_function ) ;
2016-04-09 18:20:31 +00:00
option . type = C4V_Type ( props - > GetPropertyInt ( P_Type , C4V_Any ) ) ;
option . option_key = props - > GetPropertyStr ( P_OptionKey ) ;
2016-06-08 04:37:48 +00:00
if ( ! option . option_key ) option . option_key = default_option_key ;
2016-04-09 18:20:31 +00:00
// Derive storage type from given elements in delegate definition
if ( option . type ! = C4V_Any )
option . storage_type = Option : : StorageByType ;
2016-06-08 04:37:48 +00:00
else if ( option . option_key & & option . value . GetType ( ) ! = C4V_Nil )
2016-04-09 18:20:31 +00:00
option . storage_type = Option : : StorageByKey ;
else
option . storage_type = Option : : StorageByValue ;
// Child delegate for value (resolved at runtime because there may be circular references)
props - > GetProperty ( P_Delegate , & option . adelegate_val ) ;
2016-08-27 20:46:38 +00:00
option . priority = props - > GetPropertyInt ( P_Priority ) ;
2017-02-20 00:07:51 +00:00
option . force_serialization = props - > GetPropertyInt ( P_ForceSerialization ) ;
2016-04-09 18:20:31 +00:00
options . push_back ( option ) ;
}
2016-04-04 03:46:58 +00:00
}
}
2016-06-15 04:15:08 +00:00
QStandardItemModel * C4PropertyDelegateEnum : : CreateOptionModel ( ) const
{
// Create a QStandardItemModel tree from all options and their groups
std : : unique_ptr < QStandardItemModel > model ( new QStandardItemModel ( ) ) ;
model - > setColumnCount ( 1 ) ;
int idx = 0 ;
for ( const Option & opt : options )
{
2016-09-05 23:19:33 +00:00
QStandardItem * new_item = model - > invisibleRootItem ( ) , * parent = nullptr ;
QStringList group_names ;
if ( opt . group ) group_names . append ( QString ( opt . group - > GetCStr ( ) ) . split ( QString ( " / " ) ) ) ;
group_names . append ( opt . name - > GetCStr ( ) ) ;
for ( const QString & group_name : group_names )
2016-06-15 04:15:08 +00:00
{
2016-09-05 23:19:33 +00:00
parent = new_item ;
int row_index = - 1 ;
for ( int check_row_index = 0 ; check_row_index < new_item - > rowCount ( ) ; + + check_row_index )
if ( new_item - > child ( check_row_index , 0 ) - > text ( ) = = group_name )
{
row_index = check_row_index ;
new_item = new_item - > child ( check_row_index , 0 ) ;
break ;
}
if ( row_index < 0 )
2016-06-15 04:15:08 +00:00
{
2016-09-05 23:19:33 +00:00
QStandardItem * new_group = new QStandardItem ( group_name ) ;
if ( sorted )
2016-06-15 04:15:08 +00:00
{
2016-09-05 23:19:33 +00:00
// Groups always sorted by name. Could also sort by priority of highest priority element?
new_group - > setData ( " 010000000 " + group_name , C4DeepQComboBox : : PriorityNameSortRole ) ;
2016-06-15 04:15:08 +00:00
}
2016-09-05 23:19:33 +00:00
new_item - > appendRow ( new_group ) ;
new_item = new_group ;
2016-06-15 04:15:08 +00:00
}
}
2016-09-05 23:19:33 +00:00
// If this item is already set, add a duplicate entry
if ( new_item - > data ( C4DeepQComboBox : : OptionIndexRole ) . isValid ( ) )
{
new_item = new QStandardItem ( QString ( opt . name - > GetCStr ( ) ) ) ;
parent - > appendRow ( new_item ) ;
}
// Sort key
2016-08-27 20:46:38 +00:00
if ( sorted )
{
// Reverse priority and make positive, so we can sort by descending priority but ascending name
new_item - > setData ( QString ( FormatString ( " %09d%s " , ( int ) ( 10000000 - opt . priority ) , opt . name - > GetCStr ( ) ) . getData ( ) ) , C4DeepQComboBox : : PriorityNameSortRole ) ;
}
2016-06-16 20:15:15 +00:00
new_item - > setData ( QVariant ( idx ) , C4DeepQComboBox : : OptionIndexRole ) ;
2016-06-20 23:13:50 +00:00
C4Object * item_obj_data = opt . value . getObj ( ) ;
if ( item_obj_data ) new_item - > setData ( QVariant ( item_obj_data - > Number ) , C4DeepQComboBox : : ObjectHighlightRole ) ;
2016-10-10 20:27:43 +00:00
QString help = QString ( ( opt . help ? opt . help : opt . name ) - > GetCStr ( ) ) ;
new_item - > setData ( help . replace ( ' | ' , ' \n ' ) , Qt : : ToolTipRole ) ;
2016-07-25 05:05:15 +00:00
if ( opt . help ) new_item - > setData ( QIcon ( " :/editor/res/Help.png " ) , Qt : : DecorationRole ) ;
2016-07-28 02:35:11 +00:00
if ( opt . sound_name ) new_item - > setData ( QIcon ( " :/editor/res/Sound.png " ) , Qt : : DecorationRole ) ;
2016-07-28 04:24:28 +00:00
if ( allow_editing )
{
C4String * s = opt . value . getStr ( ) ;
new_item - > setData ( QString ( s ? s - > GetCStr ( ) : " " ) , C4DeepQComboBox : : ValueStringRole ) ;
}
2016-06-15 04:15:08 +00:00
+ + idx ;
}
2016-08-27 20:46:38 +00:00
// Sort model and all groups
if ( sorted )
{
model - > setSortRole ( C4DeepQComboBox : : PriorityNameSortRole ) ;
model - > sort ( 0 , Qt : : AscendingOrder ) ;
}
2016-06-15 04:15:08 +00:00
return model . release ( ) ;
}
2016-06-16 18:59:04 +00:00
void C4PropertyDelegateEnum : : ClearOptions ( )
{
options . clear ( ) ;
}
2016-04-09 18:20:31 +00:00
void C4PropertyDelegateEnum : : ReserveOptions ( int32_t num )
{
options . reserve ( num ) ;
}
2016-04-04 03:46:58 +00:00
void C4PropertyDelegateEnum : : AddTypeOption ( C4String * name , C4V_Type type , const C4Value & val , C4PropertyDelegate * adelegate )
{
Option option ;
option . name = name ;
2016-07-30 18:50:07 +00:00
option . short_name = name ;
2016-04-04 03:46:58 +00:00
option . type = type ;
option . value = val ;
option . storage_type = Option : : StorageByType ;
option . adelegate = adelegate ;
options . push_back ( option ) ;
}
2016-07-28 02:35:11 +00:00
void C4PropertyDelegateEnum : : AddConstOption ( C4String * name , const C4Value & val , C4String * group , C4String * sound_name )
2016-04-09 18:20:31 +00:00
{
Option option ;
option . name = name ;
2016-07-30 18:50:07 +00:00
option . short_name = name ;
2016-06-16 01:57:59 +00:00
option . group = group ;
2016-04-09 18:20:31 +00:00
option . value = val ;
option . storage_type = Option : : StorageByValue ;
2016-07-28 02:35:11 +00:00
if ( sound_name )
{
option . sound_name = sound_name ;
option . help = sound_name ;
}
2016-04-09 18:20:31 +00:00
options . push_back ( option ) ;
}
2016-04-04 03:46:58 +00:00
int32_t C4PropertyDelegateEnum : : GetOptionByValue ( const C4Value & val ) const
{
int32_t iopt = 0 ;
2016-04-17 02:02:24 +00:00
bool match = false ;
2016-04-04 03:46:58 +00:00
for ( auto & option : options )
{
switch ( option . storage_type )
{
case Option : : StorageByType :
match = ( val . GetTypeEx ( ) = = option . type ) ;
break ;
case Option : : StorageByValue :
match = ( val = = option . value ) ;
break ;
case Option : : StorageByKey : // Compare value to value in property. Assume undefined as nil.
{
C4PropList * props = val . getPropList ( ) ;
2016-06-03 05:20:43 +00:00
C4PropList * def_props = option . value . getPropList ( ) ;
if ( props & & def_props )
2016-04-04 03:46:58 +00:00
{
2016-06-03 05:20:43 +00:00
C4Value propval , defval ;
2016-04-04 03:46:58 +00:00
props - > GetPropertyByS ( option . option_key . Get ( ) , & propval ) ;
2016-06-03 05:20:43 +00:00
def_props - > GetPropertyByS ( option . option_key . Get ( ) , & defval ) ;
match = ( defval = = propval ) ;
2016-04-04 03:46:58 +00:00
}
break ;
}
default : break ;
}
if ( match ) break ;
+ + iopt ;
}
2016-10-16 05:12:21 +00:00
// If no option matches, return sentinel value
return match ? iopt : Editor : : INDEX_Custom_Value ;
2016-04-04 03:46:58 +00:00
}
2016-06-03 05:20:43 +00:00
void C4PropertyDelegateEnum : : UpdateEditorParameter ( C4PropertyDelegateEnum : : Editor * editor , bool by_selection ) const
2016-04-04 03:46:58 +00:00
{
// Recreate parameter settings editor associated with the currently selected option of an enum
if ( editor - > parameter_widget )
{
editor - > parameter_widget - > deleteLater ( ) ;
2016-11-02 23:58:02 +00:00
editor - > parameter_widget = nullptr ;
2016-04-04 03:46:58 +00:00
}
2016-07-18 23:24:53 +00:00
editor - > paint_parameter_delegate = nullptr ;
2016-10-16 05:12:21 +00:00
int32_t idx = editor - > last_selection_index ;
if ( by_selection )
{
idx = editor - > option_box - > GetCurrentSelectionIndex ( ) ;
}
// No parameter delegate if not a known option (custom text or invalid value)
2016-04-04 03:46:58 +00:00
if ( idx < 0 | | idx > = options . size ( ) ) return ;
const Option & option = options [ idx ] ;
// Lazy-resolve parameter delegate
2016-04-25 20:17:10 +00:00
EnsureOptionDelegateResolved ( option ) ;
2016-04-04 03:46:58 +00:00
// Create editor if needed
if ( option . adelegate )
{
// Determine value to be shown in editor
2016-06-03 05:20:43 +00:00
C4Value parameter_val ;
if ( ! by_selection )
{
2016-10-16 05:12:21 +00:00
// Showing current selection: From last_val assigned in SetEditorData or by custom text
2016-06-03 05:20:43 +00:00
parameter_val = editor - > last_val ;
}
else
2016-04-04 03:46:58 +00:00
{
2016-06-03 05:20:43 +00:00
// Selecting a new item: Set the default value
parameter_val = option . value ;
// Although the default value is taken directly from SetEditorData, it needs to be set here to make child access into proplists and arrays possible
2016-07-04 01:16:07 +00:00
// (note that actual setting is delayed by control queue and this may often the wrong value in some cases - the correct value will be shown on execution of the queue)
2016-10-16 05:12:21 +00:00
SetOptionValue ( editor - > last_get_path , option , option . value ) ;
2016-04-04 03:46:58 +00:00
}
2016-07-13 02:29:24 +00:00
// Resolve parameter value
if ( option . value_key )
{
2016-07-31 14:35:15 +00:00
C4Value child_val ;
2016-08-08 03:41:48 +00:00
C4PropList * props = parameter_val . getPropList ( ) ;
2016-07-31 14:35:15 +00:00
if ( props ) props - > GetPropertyByS ( option . value_key . Get ( ) , & child_val ) ;
parameter_val = child_val ;
2016-07-13 02:29:24 +00:00
}
2016-04-04 03:46:58 +00:00
// Show it
2016-07-30 06:37:33 +00:00
editor - > parameter_widget = option . adelegate - > CreateEditor ( factory , editor , QStyleOptionViewItem ( ) , by_selection , true ) ;
2016-04-04 03:46:58 +00:00
if ( editor - > parameter_widget )
{
editor - > layout - > addWidget ( editor - > parameter_widget ) ;
2016-06-08 04:37:48 +00:00
C4PropertyPath delegate_value_path = editor - > last_get_path ;
if ( option . value_key ) delegate_value_path = C4PropertyPath ( delegate_value_path , option . value_key - > GetCStr ( ) ) ;
option . adelegate - > SetEditorData ( editor - > parameter_widget , parameter_val , delegate_value_path ) ;
2016-04-04 03:46:58 +00:00
// Forward editing signals
connect ( option . adelegate , & C4PropertyDelegate : : EditorValueChangedSignal , editor - > parameter_widget , [ this , editor ] ( QWidget * changed_editor )
{
if ( changed_editor = = editor - > parameter_widget )
if ( ! editor - > updating )
emit EditorValueChangedSignal ( editor ) ;
} ) ;
connect ( option . adelegate , & C4PropertyDelegate : : EditingDoneSignal , editor - > parameter_widget , [ this , editor ] ( QWidget * changed_editor )
{
if ( changed_editor = = editor - > parameter_widget ) emit EditingDoneSignal ( editor ) ;
} ) ;
}
2016-07-18 23:24:53 +00:00
else
{
// If the parameter widget is a shape display, show a dummy widget displaying the shape instead
const C4PropertyDelegateShape * shape_delegate = option . adelegate - > GetDirectShapeDelegate ( ) ;
if ( shape_delegate )
{
// dummy widget that is not rendered. shape rendering is forwarded through own paint function
editor - > parameter_widget = new QWidget ( editor ) ;
editor - > layout - > addWidget ( editor - > parameter_widget ) ;
editor - > parameter_widget - > setAttribute ( Qt : : WA_NoSystemBackground ) ;
editor - > parameter_widget - > setAttribute ( Qt : : WA_TranslucentBackground ) ;
editor - > parameter_widget - > setAttribute ( Qt : : WA_TransparentForMouseEvents ) ;
editor - > paint_parameter_delegate = shape_delegate ;
editor - > last_parameter_val = parameter_val ;
}
}
2016-04-04 03:46:58 +00:00
}
}
2016-06-15 04:15:08 +00:00
QModelIndex C4PropertyDelegateEnum : : GetModelIndexByID ( QStandardItemModel * model , QStandardItem * parent_item , int32_t id , const QModelIndex & parent ) const
{
// Resolve data stored in model to model index in tree
for ( int row = 0 ; row < parent_item - > rowCount ( ) ; + + row )
{
QStandardItem * child = parent_item - > child ( row , 0 ) ;
2016-06-16 20:15:15 +00:00
QVariant v = child - > data ( C4DeepQComboBox : : OptionIndexRole ) ;
2016-06-15 04:15:08 +00:00
if ( v . type ( ) = = QVariant : : Int & & v . toInt ( ) = = id ) return model - > index ( row , 0 , parent ) ;
if ( child - > rowCount ( ) )
{
QModelIndex child_match = GetModelIndexByID ( model , child , id , model - > index ( row , 0 , parent ) ) ;
if ( child_match . isValid ( ) ) return child_match ;
}
}
return QModelIndex ( ) ;
}
2016-05-25 03:06:00 +00:00
void C4PropertyDelegateEnum : : SetEditorData ( QWidget * aeditor , const C4Value & val , const C4PropertyPath & property_path ) const
2016-04-04 03:46:58 +00:00
{
Editor * editor = static_cast < Editor * > ( aeditor ) ;
editor - > last_val = val ;
2016-05-25 03:06:00 +00:00
editor - > last_get_path = property_path ;
2016-04-04 03:46:58 +00:00
editor - > updating = true ;
// Update option selection
2016-10-16 05:12:21 +00:00
int32_t index = GetOptionByValue ( val ) ;
if ( index = = Editor : : INDEX_Custom_Value & & ! allow_editing )
{
// Invalid value and no custom values allowed? Select first item.
index = 0 ;
}
if ( index = = Editor : : INDEX_Custom_Value )
{
// Custom value
C4String * val_string = val . getStr ( ) ;
QString edit_string = val_string ? QString ( val_string - > GetCStr ( ) ) : QString ( val . GetDataString ( ) . getData ( ) ) ;
editor - > option_box - > setEditText ( edit_string ) ;
}
else
{
// Regular enum entry
QStandardItemModel * model = static_cast < QStandardItemModel * > ( editor - > option_box - > model ( ) ) ;
editor - > option_box - > setCurrentModelIndex ( GetModelIndexByID ( model , model - > invisibleRootItem ( ) , index , QModelIndex ( ) ) ) ;
}
2016-07-13 03:28:31 +00:00
editor - > last_selection_index = index ;
2016-04-04 03:46:58 +00:00
// Update parameter
2016-06-03 05:20:43 +00:00
UpdateEditorParameter ( editor , false ) ;
2016-04-04 03:46:58 +00:00
editor - > updating = false ;
2016-07-30 06:37:33 +00:00
// Execute pending dropdowns from creation as child enums
if ( editor - > dropdown_pending )
{
editor - > dropdown_pending = false ;
QMetaObject : : invokeMethod ( editor - > option_box , " doShowPopup " , Qt : : QueuedConnection ) ;
editor - > option_box - > showPopup ( ) ;
}
2016-04-04 03:46:58 +00:00
}
2016-07-18 23:24:53 +00:00
void C4PropertyDelegateEnum : : SetModelData ( QObject * aeditor , const C4PropertyPath & property_path , C4ConsoleQtShape * prop_shape ) const
2016-04-04 03:46:58 +00:00
{
// Fetch value from editor
Editor * editor = static_cast < Editor * > ( aeditor ) ;
2016-10-16 05:12:21 +00:00
/*QStandardItemModel *model = static_cast<QStandardItemModel *>(editor->option_box->model());
2016-06-15 04:15:08 +00:00
QModelIndex selected_model_index = model - > index ( editor - > option_box - > currentIndex ( ) , 0 , editor - > option_box - > rootModelIndex ( ) ) ;
2016-06-16 20:15:15 +00:00
QVariant vidx = model - > data ( selected_model_index , C4DeepQComboBox : : OptionIndexRole ) ;
2016-06-15 04:15:08 +00:00
if ( vidx . type ( ) ! = QVariant : : Int ) return ;
int32_t idx = vidx . toInt ( ) ;
2016-10-16 05:12:21 +00:00
if ( idx < 0 | | idx > = options . size ( ) ) return ; */
int32_t idx = editor - > last_selection_index ;
const Option * option ;
const C4Value * option_value ;
if ( idx < 0 )
{
option = & default_option ;
option_value = & editor - > last_val ;
}
else
{
option = & options [ std : : max < int32_t > ( idx , 0 ) ] ;
option_value = & option - > value ;
}
2016-04-04 03:46:58 +00:00
// Store directly in value or in a proplist field?
C4PropertyPath use_path ;
2016-10-16 05:12:21 +00:00
if ( option - > value_key . Get ( ) )
use_path = C4PropertyPath ( property_path , option - > value_key - > GetCStr ( ) ) ;
2016-04-04 03:46:58 +00:00
else
use_path = property_path ;
// Value from a parameter or directly from the enum?
2016-10-16 05:12:21 +00:00
if ( option - > adelegate )
2016-04-04 03:46:58 +00:00
{
2016-08-27 23:05:12 +00:00
// Default value on enum change (on main path; not use_path because the default value is always given as the whole proplist)
2016-10-16 05:12:21 +00:00
if ( editor - > option_changed ) SetOptionValue ( property_path , * option , * option_value ) ;
2016-04-09 18:20:31 +00:00
// Value from a parameter.
// Using a setter function?
2016-10-16 05:12:21 +00:00
use_path = option - > adelegate - > GetPathForProperty ( use_path , nullptr ) ;
option - > adelegate - > SetModelData ( editor - > parameter_widget , use_path , prop_shape ) ;
2016-04-04 03:46:58 +00:00
}
else
{
// No parameter. Use value.
2016-11-20 17:26:51 +00:00
if ( editor - > option_changed ) SetOptionValue ( property_path , * option , * option_value ) ;
2016-04-04 03:46:58 +00:00
}
2016-06-03 05:20:43 +00:00
editor - > option_changed = false ;
2016-04-04 03:46:58 +00:00
}
2016-10-16 05:12:21 +00:00
void C4PropertyDelegateEnum : : SetOptionValue ( const C4PropertyPath & use_path , const C4PropertyDelegateEnum : : Option & option , const C4Value & option_value ) const
2016-07-04 01:16:07 +00:00
{
// After an enum entry has been selected, set its value
// Either directly by value or through a function
2017-02-20 00:07:51 +00:00
// Get serialization base
const C4PropList * ignore_base_props ;
if ( option . force_serialization )
{
ignore_base_props = option_value . getPropList ( ) ;
if ( ignore_base_props ) ignore_base_props = ( ignore_base_props - > IsStatic ( ) ? ignore_base_props - > IsStatic ( ) - > GetParent ( ) : nullptr ) ;
}
else
{
ignore_base_props = option . props . getPropList ( ) ;
}
const C4PropListStatic * ignore_base_props_static = ignore_base_props ? ignore_base_props - > IsStatic ( ) : nullptr ;
2016-07-04 01:16:07 +00:00
if ( option . value_function . GetType ( ) = = C4V_Function )
{
2017-02-20 00:07:51 +00:00
use_path . SetProperty ( FormatString ( " Call(%s, %s, %s) " , option . value_function . GetDataString ( ) . getData ( ) , use_path . GetRoot ( ) , option_value . GetDataString ( 20 , ignore_base_props_static ) . getData ( ) ) . getData ( ) ) ;
2016-07-04 01:16:07 +00:00
}
else
{
2016-08-27 05:12:08 +00:00
C4PropList * option_props = option . props . getPropList ( ) ;
2017-02-20 00:07:51 +00:00
use_path . SetProperty ( option_value , ignore_base_props_static ) ;
2016-07-04 01:16:07 +00:00
}
2017-02-26 15:45:38 +00:00
factory - > GetPropertyModel ( ) - > DoOnUpdateCall ( use_path , this ) ;
2016-07-04 01:16:07 +00:00
}
2016-07-30 06:37:33 +00:00
QWidget * C4PropertyDelegateEnum : : CreateEditor ( const C4PropertyDelegateFactory * parent_delegate , QWidget * parent , const QStyleOptionViewItem & option , bool by_selection , bool is_child ) const
2016-04-04 03:46:58 +00:00
{
2016-04-04 23:34:02 +00:00
Editor * editor = new Editor ( parent ) ;
2016-04-04 03:46:58 +00:00
editor - > layout = new QHBoxLayout ( editor ) ;
editor - > layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
editor - > layout - > setMargin ( 0 ) ;
editor - > layout - > setSpacing ( 0 ) ;
editor - > updating = true ;
2016-07-28 04:24:28 +00:00
editor - > option_box = new C4DeepQComboBox ( editor , GetOptionComboBoxButtonType ( ) , allow_editing ) ;
2016-04-04 03:46:58 +00:00
editor - > layout - > addWidget ( editor - > option_box ) ;
2016-04-17 02:02:24 +00:00
for ( auto & option : options ) editor - > option_box - > addItem ( option . name - > GetCStr ( ) ) ;
2016-06-15 04:15:08 +00:00
editor - > option_box - > setModel ( CreateOptionModel ( ) ) ;
2016-06-17 05:26:38 +00:00
editor - > option_box - > model ( ) - > setParent ( editor - > option_box ) ;
2016-10-16 05:12:21 +00:00
// Signal for selecting a new entry from the dropdown menu
connect ( editor - > option_box , & C4DeepQComboBox : : NewItemSelected , editor , [ editor , this ] ( int32_t newval ) {
if ( ! editor - > updating ) this - > UpdateOptionIndex ( editor , newval , nullptr ) ; } ) ;
// Signal for write-in on enum delegates that allow editing
if ( allow_editing )
{
connect ( editor - > option_box , & C4DeepQComboBox : : TextChanged , editor , [ editor , this ] ( const QString & new_text ) {
if ( ! editor - > updating )
{
this - > UpdateOptionIndex ( editor , GetOptionByValue ( C4VString ( new_text . toUtf8 ( ) ) ) , & new_text ) ;
}
} ) ;
}
editor - > updating = false ;
2016-07-30 06:37:33 +00:00
// If created by a selection from a parent enum, show drop down immediately after value has been set
editor - > dropdown_pending = by_selection & & is_child ;
2016-04-04 03:46:58 +00:00
return editor ;
}
2016-10-16 05:12:21 +00:00
void C4PropertyDelegateEnum : : UpdateOptionIndex ( C4PropertyDelegateEnum : : Editor * editor , int newval , const QString * custom_text ) const
2016-04-04 03:46:58 +00:00
{
2016-10-16 05:12:21 +00:00
bool has_changed = false ;
// Change by text entry?
if ( custom_text )
{
C4String * last_value_string = editor - > last_val . getStr ( ) ;
if ( ! last_value_string | | last_value_string - > GetData ( ) ! = custom_text - > toUtf8 ( ) )
{
editor - > last_val = C4VString ( custom_text - > toUtf8 ( ) ) ;
has_changed = true ;
}
}
2016-07-13 03:28:31 +00:00
// Update value and parameter delegate if selection changed
if ( newval ! = editor - > last_selection_index )
{
editor - > last_selection_index = newval ;
2016-10-16 05:12:21 +00:00
UpdateEditorParameter ( editor , ! custom_text ) ;
has_changed = true ;
}
// Change either by text entry or by dropdown selection: Emit signal to parent
if ( has_changed )
{
editor - > option_changed = true ;
2016-07-13 03:28:31 +00:00
emit EditorValueChangedSignal ( editor ) ;
}
2016-04-04 03:46:58 +00:00
}
2016-04-25 20:17:10 +00:00
void C4PropertyDelegateEnum : : EnsureOptionDelegateResolved ( const Option & option ) const
{
// Lazy-resolve parameter delegate
if ( ! option . adelegate & & option . adelegate_val . GetType ( ) ! = C4V_Nil )
option . adelegate = factory - > GetDelegateByValue ( option . adelegate_val ) ;
}
2016-07-30 18:50:07 +00:00
QString C4PropertyDelegateEnum : : GetDisplayString ( const C4Value & v , class C4Object * obj , bool short_names ) const
2016-04-17 02:02:24 +00:00
{
// Display string from value
int32_t idx = GetOptionByValue ( v ) ;
2016-10-16 05:12:21 +00:00
if ( idx = = Editor : : INDEX_Custom_Value )
2016-04-17 02:02:24 +00:00
{
2016-10-16 05:12:21 +00:00
// Value not found: Default display of strings; full display of nonsense values for debugging purposes.
C4String * custom_string = v . getStr ( ) ;
if ( custom_string )
{
return QString ( custom_string - > GetCStr ( ) ) ;
}
else
{
return C4PropertyDelegate : : GetDisplayString ( v , obj , short_names ) ;
}
2016-04-17 02:02:24 +00:00
}
else
{
// Value found: Display option string plus parameter
2016-04-25 20:17:10 +00:00
const Option & option = options [ idx ] ;
2016-07-30 18:50:07 +00:00
QString result = ( short_names ? option . short_name : option . name ) - > GetCStr ( ) ;
2016-04-17 02:02:24 +00:00
// Lazy-resolve parameter delegate
2016-04-25 20:17:10 +00:00
EnsureOptionDelegateResolved ( option ) ;
2016-04-17 02:02:24 +00:00
if ( option . adelegate )
{
C4Value param_val = v ;
if ( option . value_key . Get ( ) )
{
2016-08-27 06:21:35 +00:00
param_val . Set0 ( ) ;
2016-04-17 02:02:24 +00:00
C4PropList * vp = v . getPropList ( ) ;
if ( vp ) vp - > GetPropertyByS ( option . value_key , & param_val ) ;
}
2016-07-30 18:50:07 +00:00
if ( ! result . isEmpty ( ) ) result + = " " ;
result + = option . adelegate - > GetDisplayString ( param_val , obj , short_names ) ;
2016-04-17 02:02:24 +00:00
}
return result ;
}
}
2016-07-18 23:24:53 +00:00
const C4PropertyDelegateShape * C4PropertyDelegateEnum : : GetShapeDelegate ( C4Value & val , C4PropertyPath * shape_path ) const
2016-04-25 20:17:10 +00:00
{
// Does this delegate own a shape? Forward decision into selected option.
int32_t option_idx = GetOptionByValue ( val ) ;
2016-10-16 05:12:21 +00:00
if ( option_idx = = Editor : : INDEX_Custom_Value ) return nullptr ;
2016-04-25 20:17:10 +00:00
const Option & option = options [ option_idx ] ;
EnsureOptionDelegateResolved ( option ) ;
if ( ! option . adelegate ) return nullptr ;
if ( option . value_key . Get ( ) )
{
2016-07-21 04:20:44 +00:00
* shape_path = option . adelegate - > GetPathForProperty ( * shape_path , option . value_key - > GetCStr ( ) ) ;
2016-04-25 20:17:10 +00:00
C4PropList * vp = val . getPropList ( ) ;
2016-08-29 19:18:02 +00:00
val . Set0 ( ) ;
2016-07-18 23:24:53 +00:00
if ( vp ) vp - > GetPropertyByS ( option . value_key , & val ) ;
2016-04-25 20:17:10 +00:00
}
2016-07-18 23:24:53 +00:00
return option . adelegate - > GetShapeDelegate ( val , shape_path ) ;
}
bool C4PropertyDelegateEnum : : Paint ( QPainter * painter , const QStyleOptionViewItem & option , const C4Value & val ) const
{
// Custom painting: Forward to selected child delegate
int32_t option_idx = GetOptionByValue ( val ) ;
2016-10-16 05:12:21 +00:00
if ( option_idx = = Editor : : INDEX_Custom_Value ) return false ;
2016-07-18 23:24:53 +00:00
const Option & selected_option = options [ option_idx ] ;
EnsureOptionDelegateResolved ( selected_option ) ;
2016-07-19 15:21:23 +00:00
if ( ! selected_option . adelegate ) return false ;
2016-07-18 23:24:53 +00:00
if ( selected_option . adelegate - > HasCustomPaint ( ) )
{
QStyleOptionViewItem parameter_option = QStyleOptionViewItem ( option ) ;
parameter_option . rect . adjust ( parameter_option . rect . width ( ) / 2 , 0 , 0 , 0 ) ;
C4Value parameter_val = val ;
if ( selected_option . value_key . Get ( ) )
{
2016-08-29 19:18:02 +00:00
parameter_val . Set0 ( ) ;
2016-07-18 23:24:53 +00:00
C4PropList * vp = val . getPropList ( ) ;
if ( vp ) vp - > GetPropertyByS ( selected_option . value_key , & parameter_val ) ;
}
selected_option . adelegate - > Paint ( painter , parameter_option , parameter_val ) ;
}
// Always return false to draw self using the standard method
return false ;
2016-04-25 20:17:10 +00:00
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateEnum : : IsPasteValid ( const C4Value & val ) const
{
2016-10-16 05:12:21 +00:00
// Strings always OK in editable enums
if ( val . GetType ( ) = = C4V_String & & allow_editing ) return true ;
2016-08-19 20:12:53 +00:00
// Must be a valid selection
int32_t option_idx = GetOptionByValue ( val ) ;
2016-10-16 05:12:21 +00:00
if ( option_idx = = Editor : : INDEX_Custom_Value ) return false ;
2016-08-19 20:12:53 +00:00
const Option & option = options [ option_idx ] ;
// Check validity for parameter
EnsureOptionDelegateResolved ( option ) ;
if ( ! option . adelegate ) return true ; // No delegate? Then any value is OK.
C4Value parameter_val ;
if ( option . value_key . Get ( ) )
{
C4PropList * vp = val . getPropList ( ) ;
if ( ! vp ) return false ;
vp - > GetPropertyByS ( option . value_key , & parameter_val ) ; // if this fails, check parameter against nil
}
else
{
parameter_val = val ;
}
return option . adelegate - > IsPasteValid ( parameter_val ) ;
}
2016-07-28 02:35:11 +00:00
/* Definition delegate */
2016-04-17 02:02:24 +00:00
C4PropertyDelegateDef : : C4PropertyDelegateDef ( const C4PropertyDelegateFactory * factory , C4PropList * props )
2016-04-09 18:20:31 +00:00
: C4PropertyDelegateEnum ( factory , props )
{
2016-07-22 04:28:01 +00:00
// nil is always an option
2016-07-30 18:13:23 +00:00
AddConstOption ( empty_name ? empty_name . Get ( ) : : : Strings . RegString ( " nil " ) , C4VNull ) ;
2016-04-09 18:20:31 +00:00
// Collect sorted definitions
2016-08-19 20:12:53 +00:00
filter_property = props ? props - > GetPropertyStr ( P_Filter ) : nullptr ;
2016-06-16 01:57:59 +00:00
if ( filter_property )
2016-04-09 18:20:31 +00:00
{
2016-06-16 01:57:59 +00:00
// With filter just create a flat list
std : : vector < C4Def * > defs = : : Definitions . GetAllDefs ( filter_property ) ;
std : : sort ( defs . begin ( ) , defs . end ( ) , [ ] ( C4Def * a , C4Def * b ) - > bool {
return strcmp ( a - > GetName ( ) , b - > GetName ( ) ) < 0 ;
} ) ;
// Add them
for ( C4Def * def : defs )
{
C4RefCntPointer < C4String > option_name = : : Strings . RegString ( FormatString ( " %s (%s) " , def - > id . ToString ( ) , def - > GetName ( ) ) ) ;
AddConstOption ( option_name , C4Value ( def ) , nullptr ) ;
}
}
else
{
// Without filter copy tree from definition list model
C4ConsoleQtDefinitionListModel * def_list_model = factory - > GetDefinitionListModel ( ) ;
// Recursively add all defs from model
AddDefinitions ( def_list_model , QModelIndex ( ) , nullptr ) ;
}
}
void C4PropertyDelegateDef : : AddDefinitions ( C4ConsoleQtDefinitionListModel * def_list_model , QModelIndex parent , C4String * group )
{
int32_t count = def_list_model - > rowCount ( parent ) ;
for ( int32_t i = 0 ; i < count ; + + i )
{
QModelIndex index = def_list_model - > index ( i , 0 , parent ) ;
C4Def * def = def_list_model - > GetDefByModelIndex ( index ) ;
C4RefCntPointer < C4String > name = : : Strings . RegString ( def_list_model - > GetNameByModelIndex ( index ) ) ;
if ( def ) AddConstOption ( name . Get ( ) , C4Value ( def ) , group ) ;
if ( def_list_model - > rowCount ( index ) )
{
AddDefinitions ( def_list_model , index , group ? : : Strings . RegString ( FormatString ( " %s/%s " , group - > GetCStr ( ) , name - > GetCStr ( ) ) . getData ( ) ) : name . Get ( ) ) ;
}
2016-04-09 18:20:31 +00:00
}
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateDef : : IsPasteValid ( const C4Value & val ) const
{
// Must be a definition or nil
if ( val . GetType ( ) = = C4V_Nil ) return true ;
C4Def * def = val . getDef ( ) ;
if ( ! def ) return false ;
// Check filter
if ( filter_property )
{
C4Value prop_val ;
if ( ! def - > GetPropertyByS ( filter_property , & prop_val ) ) return false ;
if ( ! prop_val ) return false ;
}
return true ;
}
2016-07-28 02:35:11 +00:00
/* Object delegate */
2016-06-16 18:59:04 +00:00
C4PropertyDelegateObject : : C4PropertyDelegateObject ( const C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateEnum ( factory , props ) , max_nearby_objects ( 20 )
{
// Settings
if ( props )
{
filter = props - > GetPropertyStr ( P_Filter ) ;
}
// Actual object list is created/updated when the editor is created
}
C4RefCntPointer < C4String > C4PropertyDelegateObject : : GetObjectEntryString ( C4Object * obj ) const
{
// Compose object display string from containment(*), name, position (@x,y) and object number (#n)
return : : Strings . RegString ( FormatString ( " %s%s @%d,%d (#%d) " , obj - > Contained ? " * " : " " , obj - > GetName ( ) , ( int ) obj - > GetX ( ) , ( int ) obj - > GetY ( ) , ( int ) obj - > Number ) ) ;
}
void C4PropertyDelegateObject : : UpdateObjectList ( )
{
// Re-create object list from current position
ClearOptions ( ) ;
// Get matching objects first
std : : vector < C4Object * > objects ;
for ( C4Object * obj : : : Objects ) if ( obj - > Status )
{
C4Value filter_val ;
if ( filter )
{
if ( ! obj - > GetPropertyByS ( filter , & filter_val ) ) continue ;
if ( ! filter_val ) continue ;
}
objects . push_back ( obj ) ;
}
// Get list sorted by distance from selected object
std : : vector < C4Object * > objects_by_distance ;
int32_t cx = 0 , cy = 0 ;
if ( : : Console . EditCursor . GetCurrentSelectionPosition ( & cx , & cy ) )
{
objects_by_distance = objects ;
auto ObjDist = [ cx , cy ] ( C4Object * o ) { return ( o - > GetX ( ) - cx ) * ( o - > GetX ( ) - cx ) + ( o - > GetY ( ) - cy ) * ( o - > GetY ( ) - cy ) ; } ;
std : : stable_sort ( objects_by_distance . begin ( ) , objects_by_distance . end ( ) , [ & ObjDist ] ( C4Object * a , C4Object * b ) { return ObjDist ( a ) < ObjDist ( b ) ; } ) ;
}
size_t num_nearby = objects_by_distance . size ( ) ;
bool has_all_objects_list = ( num_nearby > max_nearby_objects ) ;
if ( has_all_objects_list ) num_nearby = max_nearby_objects ;
// Add actual objects
ReserveOptions ( 1 + num_nearby + ! ! num_nearby + ( has_all_objects_list ? objects . size ( ) : 0 ) ) ;
AddConstOption ( : : Strings . RegString ( " nil " ) , C4VNull ) ; // nil is always an option
if ( num_nearby )
{
// TODO: "Select object" entry
//AddCallbackOption(LoadResStr("IDS_CNS_SELECTOBJECT"));
// Nearby list
C4RefCntPointer < C4String > nearby_group ;
// If there are main objects, Create a subgroup. Otherwise, just put all elements into the main group.
if ( has_all_objects_list ) nearby_group = : : Strings . RegString ( LoadResStr ( " IDS_CNS_NEARBYOBJECTS " ) ) ;
for ( int32_t i = 0 ; i < num_nearby ; + + i )
{
C4Object * obj = objects_by_distance [ i ] ;
AddConstOption ( GetObjectEntryString ( obj ) . Get ( ) , C4VObj ( obj ) , nearby_group . Get ( ) ) ;
}
// All objects
if ( has_all_objects_list )
{
C4RefCntPointer < C4String > all_group = : : Strings . RegString ( LoadResStr ( " IDS_CNS_ALLOBJECTS " ) ) ;
for ( C4Object * obj : objects ) AddConstOption ( GetObjectEntryString ( obj ) . Get ( ) , C4VObj ( obj ) , all_group . Get ( ) ) ;
}
}
}
2016-07-30 06:37:33 +00:00
QWidget * C4PropertyDelegateObject : : CreateEditor ( const class C4PropertyDelegateFactory * parent_delegate , QWidget * parent , const QStyleOptionViewItem & option , bool by_selection , bool is_child ) const
2016-06-16 18:59:04 +00:00
{
// Update object list for created editor
// (This should be safe since the object delegate cannot contain nested delegates)
const_cast < C4PropertyDelegateObject * > ( this ) - > UpdateObjectList ( ) ;
2016-07-30 06:37:33 +00:00
return C4PropertyDelegateEnum : : CreateEditor ( parent_delegate , parent , option , by_selection , is_child ) ;
2016-06-16 18:59:04 +00:00
}
2016-07-30 18:50:07 +00:00
QString C4PropertyDelegateObject : : GetDisplayString ( const C4Value & v , class C4Object * obj , bool short_names ) const
2016-07-14 04:45:18 +00:00
{
C4Object * vobj = v . getObj ( ) ;
if ( vobj )
{
C4RefCntPointer < C4String > s = GetObjectEntryString ( vobj ) ;
return QString ( s - > GetCStr ( ) ) ;
}
else
{
return QString ( v . GetDataString ( ) . getData ( ) ) ;
}
}
2016-06-16 18:59:04 +00:00
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateObject : : IsPasteValid ( const C4Value & val ) const
{
// Must be an object or nil
if ( val . GetType ( ) = = C4V_Nil ) return true ;
C4Object * obj = val . getObj ( ) ;
if ( ! obj ) return false ;
// Check filter
if ( filter )
{
C4Value prop_val ;
if ( ! obj - > GetPropertyByS ( filter , & prop_val ) ) return false ;
if ( ! prop_val ) return false ;
}
return true ;
}
2016-07-28 02:35:11 +00:00
/* Sound delegate */
C4PropertyDelegateSound : : C4PropertyDelegateSound ( const C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateEnum ( factory , props )
{
// Add none-option
AddConstOption ( : : Strings . RegString ( " nil " ) , C4VNull ) ;
// Add all sounds as options
for ( C4SoundEffect * fx = : : Application . SoundSystem . GetFirstSound ( ) ; fx ; fx = fx - > Next )
{
// Extract group name as path to sound, replacing "::" by "/" for enum groups
StdStrBuf full_name_s ( fx - > GetFullName ( ) , true ) ;
RemoveExtension ( & full_name_s ) ;
const char * full_name = full_name_s . getData ( ) ;
const char * base_name = full_name , * pos ;
StdStrBuf group_string ;
while ( ( pos = SSearch ( base_name , " :: " ) ) )
{
if ( group_string . getLength ( ) ) group_string . AppendChar ( ' / ' ) ;
group_string . Append ( base_name , pos - base_name - 2 ) ;
base_name = pos ;
}
C4RefCntPointer < C4String > group ;
if ( group_string . getLength ( ) ) group = : : Strings . RegString ( group_string ) ;
// Script name: Full name (without extension)
C4RefCntPointer < C4String > sound_string = : : Strings . RegString ( full_name_s ) ;
// Add the option
AddConstOption ( : : Strings . RegString ( base_name ) , C4VString ( sound_string . Get ( ) ) , group . Get ( ) , sound_string . Get ( ) ) ;
}
}
2016-10-16 05:15:38 +00:00
QString C4PropertyDelegateSound : : GetDisplayString ( const C4Value & v , class C4Object * obj , bool short_names ) const
{
// Always show full sound name
C4String * val_string = v . getStr ( ) ;
return val_string ? QString ( val_string - > GetCStr ( ) ) : QString ( v . GetDataString ( ) . getData ( ) ) ;
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateSound : : IsPasteValid ( const C4Value & val ) const
{
// Must be nil or a string
if ( val . GetType ( ) = = C4V_Nil ) return true ;
if ( val . GetType ( ) ! = C4V_String ) return false ;
return true ;
}
2016-07-28 02:35:11 +00:00
/* Boolean delegate */
2016-04-17 02:02:24 +00:00
C4PropertyDelegateBool : : C4PropertyDelegateBool ( const C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateEnum ( factory , props )
{
// Add boolean options
ReserveOptions ( 2 ) ;
AddConstOption ( : : Strings . RegString ( LoadResStr ( " IDS_CNS_FALSE " ) ) , C4VBool ( false ) ) ;
AddConstOption ( : : Strings . RegString ( LoadResStr ( " IDS_CNS_TRUE " ) ) , C4VBool ( true ) ) ;
}
2016-05-28 13:30:54 +00:00
bool C4PropertyDelegateBool : : GetPropertyValue ( const C4Value & container , C4String * key , int32_t index , C4Value * out_val ) const
2016-04-17 02:02:24 +00:00
{
// Force value to bool
2016-08-05 05:00:33 +00:00
bool success = C4PropertyDelegateEnum : : GetPropertyValue ( container , key , index , out_val ) ;
2016-04-17 02:02:24 +00:00
if ( out_val - > GetType ( ) ! = C4V_Bool ) * out_val = C4VBool ( ! ! * out_val ) ;
2016-05-28 13:30:54 +00:00
return success ;
2016-04-17 02:02:24 +00:00
}
2016-08-19 20:12:53 +00:00
bool C4PropertyDelegateBool : : IsPasteValid ( const C4Value & val ) const
{
// Must be a boolean
if ( val . GetType ( ) ! = C4V_Bool ) return false ;
return true ;
}
2016-07-28 02:35:11 +00:00
/* Has-effect delegate */
2016-04-17 02:02:24 +00:00
C4PropertyDelegateHasEffect : : C4PropertyDelegateHasEffect ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateBool ( factory , props )
{
if ( props ) effect = props - > GetPropertyStr ( P_Effect ) ;
}
2016-05-28 13:30:54 +00:00
bool C4PropertyDelegateHasEffect : : GetPropertyValue ( const C4Value & container , C4String * key , int32_t index , C4Value * out_val ) const
2016-04-17 02:02:24 +00:00
{
2016-05-28 13:30:54 +00:00
const C4Object * obj = container . getObj ( ) ;
2016-04-17 02:02:24 +00:00
if ( obj & & effect )
{
bool has_effect = false ;
for ( C4Effect * fx = obj - > pEffects ; fx ; fx = fx - > pNext )
if ( ! fx - > IsDead ( ) )
if ( ! strcmp ( fx - > GetName ( ) , effect - > GetCStr ( ) ) )
{
has_effect = true ;
break ;
}
* out_val = C4VBool ( has_effect ) ;
return true ;
}
return false ;
}
2016-07-28 02:35:11 +00:00
/* C4Value via an enumeration delegate */
2016-04-17 02:02:24 +00:00
C4PropertyDelegateC4ValueEnum : : C4PropertyDelegateC4ValueEnum ( const C4PropertyDelegateFactory * factory , C4PropList * props )
2016-04-09 18:20:31 +00:00
: C4PropertyDelegateEnum ( factory , props )
2016-04-04 03:46:58 +00:00
{
// Add default C4Value selections
2016-04-09 18:20:31 +00:00
ReserveOptions ( 10 ) ;
2016-04-04 03:46:58 +00:00
AddTypeOption ( : : Strings . RegString ( " nil " ) , C4V_Nil , C4VNull ) ;
AddTypeOption ( : : Strings . RegString ( " bool " ) , C4V_Bool , C4VNull , factory - > GetDelegateByValue ( C4VString ( " bool " ) ) ) ;
AddTypeOption ( : : Strings . RegString ( " int " ) , C4V_Int , C4VNull , factory - > GetDelegateByValue ( C4VString ( " int " ) ) ) ;
AddTypeOption ( : : Strings . RegString ( " string " ) , C4V_String , C4VNull , factory - > GetDelegateByValue ( C4VString ( " string " ) ) ) ;
AddTypeOption ( : : Strings . RegString ( " array " ) , C4V_Array , C4VNull , factory - > GetDelegateByValue ( C4VString ( " array " ) ) ) ;
AddTypeOption ( : : Strings . RegString ( " function " ) , C4V_Function , C4VNull , factory - > GetDelegateByValue ( C4VString ( " function " ) ) ) ;
AddTypeOption ( : : Strings . RegString ( " object " ) , C4V_Object , C4VNull , factory - > GetDelegateByValue ( C4VString ( " object " ) ) ) ;
AddTypeOption ( : : Strings . RegString ( " def " ) , C4V_Def , C4VNull , factory - > GetDelegateByValue ( C4VString ( " def " ) ) ) ;
AddTypeOption ( : : Strings . RegString ( " effect " ) , C4V_Effect , C4VNull , factory - > GetDelegateByValue ( C4VString ( " effect " ) ) ) ;
AddTypeOption ( : : Strings . RegString ( " proplist " ) , C4V_PropList , C4VNull , factory - > GetDelegateByValue ( C4VString ( " proplist " ) ) ) ;
}
2016-07-28 02:35:11 +00:00
/* C4Value via an edit field delegate */
2016-05-25 03:06:00 +00:00
C4PropertyDelegateC4ValueInputEditor : : C4PropertyDelegateC4ValueInputEditor ( QWidget * parent )
2016-11-02 23:58:02 +00:00
: QWidget ( parent ) , layout ( nullptr ) , edit ( nullptr ) , extended_button ( nullptr ) , commit_pending ( false )
2016-05-25 03:06:00 +00:00
{
layout = new QHBoxLayout ( this ) ;
layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
layout - > setMargin ( 0 ) ;
layout - > setSpacing ( 0 ) ;
edit = new QLineEdit ( this ) ;
layout - > addWidget ( edit ) ;
extended_button = new QPushButton ( " ... " , this ) ;
extended_button - > setMaximumWidth ( extended_button - > fontMetrics ( ) . boundingRect ( " ... " ) . width ( ) + 6 ) ;
layout - > addWidget ( extended_button ) ;
extended_button - > hide ( ) ;
edit - > setFocus ( ) ;
setLayout ( layout ) ;
}
void C4PropertyDelegateC4ValueInput : : SetEditorData ( QWidget * aeditor , const C4Value & val , const C4PropertyPath & property_path ) const
2016-04-04 23:34:02 +00:00
{
Editor * editor = static_cast < Editor * > ( aeditor ) ;
editor - > edit - > setText ( val . GetDataString ( ) . getData ( ) ) ;
2016-05-28 13:30:54 +00:00
if ( val . GetType ( ) = = C4V_PropList | | val . GetType ( ) = = C4V_Array )
2016-05-25 03:06:00 +00:00
{
editor - > extended_button - > show ( ) ;
editor - > property_path = property_path ;
}
else
{
editor - > extended_button - > hide ( ) ;
}
2016-04-04 23:34:02 +00:00
}
2016-07-18 23:24:53 +00:00
void C4PropertyDelegateC4ValueInput : : SetModelData ( QObject * aeditor , const C4PropertyPath & property_path , C4ConsoleQtShape * prop_shape ) const
2016-04-04 23:34:02 +00:00
{
// Only set model data when pressing Enter explicitely; not just when leaving
Editor * editor = static_cast < Editor * > ( aeditor ) ;
if ( editor - > commit_pending )
{
property_path . SetProperty ( editor - > edit - > text ( ) . toUtf8 ( ) ) ;
2017-02-26 15:45:38 +00:00
factory - > GetPropertyModel ( ) - > DoOnUpdateCall ( property_path , this ) ;
2016-04-04 23:34:02 +00:00
editor - > commit_pending = false ;
}
}
2016-07-30 06:37:33 +00:00
QWidget * C4PropertyDelegateC4ValueInput : : CreateEditor ( const class C4PropertyDelegateFactory * parent_delegate , QWidget * parent , const QStyleOptionViewItem & option , bool by_selection , bool is_child ) const
2016-04-04 23:34:02 +00:00
{
// Editor is just an edit box plus a "..." button for array/proplist types
Editor * editor = new Editor ( parent ) ;
// EditingDone only on Return; not just when leaving edit field
connect ( editor - > edit , & QLineEdit : : returnPressed , editor , [ this , editor ] ( ) {
editor - > commit_pending = true ;
emit EditingDoneSignal ( editor ) ;
} ) ;
2016-05-25 03:06:00 +00:00
connect ( editor - > extended_button , & QPushButton : : pressed , editor , [ this , editor ] ( ) {
C4Value val = editor - > property_path . ResolveValue ( ) ;
2016-05-28 13:30:54 +00:00
if ( val . getPropList ( ) | | val . getArray ( ) )
2016-05-25 03:06:00 +00:00
{
2016-07-13 21:18:08 +00:00
this - > factory - > GetPropertyModel ( ) - > DescendPath ( val , val . getPropList ( ) , editor - > property_path ) ;
2016-05-25 03:06:00 +00:00
: : Console . EditCursor . InvalidateSelection ( ) ;
}
} ) ;
2016-10-15 22:26:03 +00:00
// Selection in child enum: Direct focus
if ( by_selection & & is_child ) editor - > edit - > setFocus ( ) ;
2016-04-04 23:34:02 +00:00
return editor ;
}
2016-04-04 03:46:58 +00:00
2016-04-25 20:17:10 +00:00
/* Areas shown in viewport */
C4PropertyDelegateShape : : C4PropertyDelegateShape ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
2016-08-19 20:12:53 +00:00
: C4PropertyDelegate ( factory , props ) , clr ( 0xffff0000 )
2016-04-25 20:17:10 +00:00
{
if ( props )
{
clr = props - > GetPropertyInt ( P_Color ) | 0xff000000 ;
}
}
2016-07-18 23:24:53 +00:00
void C4PropertyDelegateShape : : SetModelData ( QObject * editor , const C4PropertyPath & property_path , C4ConsoleQtShape * prop_shape ) const
2016-04-25 20:17:10 +00:00
{
2016-11-20 17:26:51 +00:00
// Only set shape data if triggered through shape movement signal; ignore update calls from e.g. parent enum editor
if ( ! editor )
{
2017-02-26 15:45:38 +00:00
if ( prop_shape & & prop_shape - > GetParentDelegate ( ) = = this )
{
property_path . SetProperty ( prop_shape - > GetValue ( ) ) ;
factory - > GetPropertyModel ( ) - > DoOnUpdateCall ( property_path , this ) ;
}
2016-11-20 17:26:51 +00:00
}
2016-04-25 20:17:10 +00:00
}
2016-07-18 23:24:53 +00:00
bool C4PropertyDelegateShape : : Paint ( QPainter * painter , const QStyleOptionViewItem & option , const C4Value & val ) const
2016-04-25 20:17:10 +00:00
{
// Background color
if ( option . state & QStyle : : State_Selected )
painter - > fillRect ( option . rect , option . palette . highlight ( ) ) ;
else
painter - > fillRect ( option . rect , option . palette . base ( ) ) ;
// Draw a frame in shape color
painter - > save ( ) ;
QColor frame_color = QColor ( QRgb ( clr & 0xffffff ) ) ;
int32_t width = Clamp < int32_t > ( option . rect . height ( ) / 8 , 2 , 6 ) & ~ 1 ;
QPen rect_pen ( QBrush ( frame_color ) , width , Qt : : SolidLine , Qt : : SquareCap , Qt : : MiterJoin ) ;
painter - > setPen ( rect_pen ) ;
QRect inner_rect = option . rect . adjusted ( width / 2 , width / 2 , - width / 2 , - width / 2 ) ;
2016-07-30 05:05:12 +00:00
if ( inner_rect . width ( ) > inner_rect . height ( ) )
{
// Draw shape in right corner
inner_rect . adjust ( inner_rect . width ( ) - inner_rect . height ( ) , 0 , 0 , 0 ) ;
}
2016-08-19 20:12:53 +00:00
// Paint by shape type
DoPaint ( painter , inner_rect ) ;
// Done painting
painter - > restore ( ) ;
return true ;
}
2016-07-30 05:05:12 +00:00
2016-11-11 02:43:03 +00:00
void C4PropertyDelegateShape : : ConnectSignals ( C4ConsoleQtShape * shape , const C4PropertyPath & property_path ) const
{
connect ( shape , & C4ConsoleQtShape : : ShapeDragged , this , [ this , shape , property_path ] ( ) {
this - > SetModelData ( nullptr , property_path , shape ) ;
} ) ;
}
2016-08-19 20:12:53 +00:00
/* Areas shown in viewport: Rectangle */
C4PropertyDelegateRect : : C4PropertyDelegateRect ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateShape ( factory , props )
{
if ( props )
{
storage = props - > GetPropertyStr ( P_Storage ) ;
2016-07-30 05:05:12 +00:00
}
2016-08-19 20:12:53 +00:00
}
void C4PropertyDelegateRect : : DoPaint ( QPainter * painter , const QRect & inner_rect ) const
{
painter - > drawRect ( inner_rect ) ;
}
bool C4PropertyDelegateRect : : IsPasteValid ( const C4Value & val ) const
{
// Check storage as prop list
if ( storage )
2016-04-25 20:17:10 +00:00
{
2016-08-19 20:12:53 +00:00
// Proplist-stored rect must have defined properties
C4PropertyName def_property_names [ 2 ] [ 4 ] = { { P_x , P_y , P_wdt , P_hgt } , { P_X , P_Y , P_Wdt , P_Hgt } } ;
C4PropertyName * property_names = nullptr ;
2016-11-02 13:24:14 +00:00
if ( storage = = & : : Strings . P [ P_proplist ] )
2016-08-19 20:12:53 +00:00
{
property_names = def_property_names [ 0 ] ;
}
2016-11-02 13:24:14 +00:00
else if ( storage = = & : : Strings . P [ P_Proplist ] )
2016-08-19 20:12:53 +00:00
{
property_names = def_property_names [ 1 ] ;
}
if ( property_names )
{
C4PropList * val_proplist = val . getPropList ( ) ;
if ( ! val_proplist ) return false ;
for ( int32_t i = 0 ; i < 4 ; + + i )
{
C4Value propval ;
if ( ! val_proplist - > GetProperty ( property_names [ i ] , & propval ) ) return false ;
if ( propval . GetType ( ) ! = C4V_Int ) return false ;
}
// extra properties are OK
}
return true ;
2016-04-25 20:17:10 +00:00
}
2016-08-19 20:12:53 +00:00
// Check storage as array: Expect array with four elements. Width and height non-negative.
C4ValueArray * val_arr = val . getArray ( ) ;
if ( ! val_arr | | val_arr - > GetSize ( ) ! = 4 ) return false ;
for ( int32_t i = 0 ; i < 4 ; + + i ) if ( val_arr - > GetItem ( i ) . GetType ( ) ! = C4V_Int ) return false ;
if ( val_arr - > GetItem ( 2 ) . _getInt ( ) < 0 ) return false ;
if ( val_arr - > GetItem ( 3 ) . _getInt ( ) < 0 ) return false ;
return true ;
}
/* Areas shown in viewport: Circle */
C4PropertyDelegateCircle : : C4PropertyDelegateCircle ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateShape ( factory , props )
{
if ( props )
2016-04-25 20:17:10 +00:00
{
2016-08-19 20:12:53 +00:00
can_move_center = props - > GetPropertyBool ( P_CanMoveCenter ) ;
2016-04-25 20:17:10 +00:00
}
2016-08-19 20:12:53 +00:00
}
void C4PropertyDelegateCircle : : DoPaint ( QPainter * painter , const QRect & inner_rect ) const
{
painter - > drawEllipse ( inner_rect ) ;
if ( can_move_center ) painter - > drawPoint ( inner_rect . center ( ) ) ;
}
bool C4PropertyDelegateCircle : : IsPasteValid ( const C4Value & val ) const
{
// Circle radius stored as single non-negative int
if ( ! can_move_center ) return ( val . GetType ( ) = = C4V_Int ) & & ( val . getInt ( ) > = 0 ) ;
// Circle+Center stored as array with three elements (radius, x, y)
C4ValueArray * val_arr = val . getArray ( ) ;
if ( ! val_arr | | val_arr - > GetSize ( ) ! = 3 ) return false ;
for ( int32_t i = 0 ; i < 3 ; + + i ) if ( val_arr - > GetItem ( i ) . GetType ( ) ! = C4V_Int ) return false ;
if ( val_arr - > GetItem ( 0 ) . _getInt ( ) < 0 ) return false ;
return true ;
}
/* Areas shown in viewport: Point */
C4PropertyDelegatePoint : : C4PropertyDelegatePoint ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateShape ( factory , props )
{
2017-05-10 22:37:08 +00:00
if ( props )
{
horizontal_fix = props - > GetPropertyBool ( P_HorizontalFix ) ;
vertical_fix = props - > GetPropertyBool ( P_VerticalFix ) ;
}
2016-08-19 20:12:53 +00:00
}
void C4PropertyDelegatePoint : : DoPaint ( QPainter * painter , const QRect & inner_rect ) const
{
QPoint ctr = inner_rect . center ( ) ;
int r = inner_rect . height ( ) * 7 / 20 ;
2017-05-10 22:37:08 +00:00
if ( horizontal_fix & & ! vertical_fix )
{
painter - > drawLine ( ctr + QPoint ( 0 , - r ) , ctr + QPoint ( 0 , + r ) ) ;
painter - > drawLine ( ctr + QPoint ( - r / 2 , - r ) , ctr + QPoint ( + r / 2 , - r ) ) ;
painter - > drawLine ( ctr + QPoint ( - r / 2 , + r ) , ctr + QPoint ( + r / 2 , + r ) ) ;
}
else if ( vertical_fix & & ! horizontal_fix )
{
painter - > drawLine ( ctr + QPoint ( - r , 0 ) , ctr + QPoint ( + r , 0 ) ) ;
painter - > drawLine ( ctr + QPoint ( - r , - r / 2 ) , ctr + QPoint ( - r , + r / 2 ) ) ;
painter - > drawLine ( ctr + QPoint ( + r , - r / 2 ) , ctr + QPoint ( + r , + r / 2 ) ) ;
}
else
{
if ( ! horizontal_fix )
{
painter - > drawLine ( ctr + QPoint ( - r , - r ) , ctr + QPoint ( + r , + r ) ) ;
}
painter - > drawLine ( ctr + QPoint ( + r , - r ) , ctr + QPoint ( - r , + r ) ) ;
painter - > drawEllipse ( inner_rect ) ;
}
2016-08-19 20:12:53 +00:00
}
bool C4PropertyDelegatePoint : : IsPasteValid ( const C4Value & val ) const
{
// Point stored as array with two elements
C4ValueArray * val_arr = val . getArray ( ) ;
if ( ! val_arr | | val_arr - > GetSize ( ) ! = 2 ) return false ;
for ( int32_t i = 0 ; i < 2 ; + + i ) if ( val_arr - > GetItem ( i ) . GetType ( ) ! = C4V_Int ) return false ;
2016-07-18 23:24:53 +00:00
return true ;
2016-04-25 20:17:10 +00:00
}
2016-04-04 03:46:58 +00:00
2016-11-02 13:24:14 +00:00
/* Areas shown in viewport: Graph */
C4PropertyDelegateGraph : : C4PropertyDelegateGraph ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateShape ( factory , props )
{
2016-11-13 02:23:00 +00:00
if ( props )
{
horizontal_fix = props - > GetPropertyBool ( P_HorizontalFix ) ;
vertical_fix = props - > GetPropertyBool ( P_VerticalFix ) ;
structure_fix = props - > GetPropertyBool ( P_StructureFix ) ;
}
2016-11-02 13:24:14 +00:00
}
void C4PropertyDelegateGraph : : DoPaint ( QPainter * painter , const QRect & inner_rect ) const
{
// Draw symbol as a bunch of connected lines
QPoint ctr = inner_rect . center ( ) ;
int r = inner_rect . height ( ) * 7 / 20 ;
painter - > drawLine ( ctr , ctr + QPoint ( - r / 2 , - r ) ) ;
painter - > drawLine ( ctr , ctr + QPoint ( + r / 2 , - r ) ) ;
painter - > drawLine ( ctr , ctr + QPoint ( 0 , + r ) ) ;
}
bool C4PropertyDelegateGraph : : IsVertexPasteValid ( const C4Value & val ) const
{
// Check that it's an array of at least one point
const C4ValueArray * arr = val . getArray ( ) ;
if ( ! arr | | ! arr - > GetSize ( ) ) return false ;
// Check validity of each point
const int32_t n_props = 2 ;
C4PropertyName property_names [ n_props ] = { P_X , P_Y } ;
for ( int32_t i_pt = 0 ; i_pt < arr - > GetSize ( ) ; + + i_pt )
{
const C4Value & pt = arr - > GetItem ( i_pt ) ;
2016-11-11 02:43:03 +00:00
const C4PropList * ptp = pt . getPropList ( ) ;
if ( ! ptp ) return false ;
2017-05-03 18:28:00 +00:00
for ( auto & property_name : property_names )
2016-11-02 13:24:14 +00:00
{
2016-11-11 02:43:03 +00:00
C4Value ptprop ;
2017-05-03 18:28:00 +00:00
if ( ! ptp - > GetProperty ( property_name , & ptprop ) ) return false ;
2016-11-11 02:43:03 +00:00
if ( ptprop . GetType ( ) ! = C4V_Int ) return false ;
2016-11-02 13:24:14 +00:00
}
}
return true ;
}
bool C4PropertyDelegateGraph : : IsEdgePasteValid ( const C4Value & val ) const
{
// Check that it's an array
// Empty is OK; it could be a graph with one vertex and no edges
const C4ValueArray * arr = val . getArray ( ) ;
if ( ! arr | | ! arr - > GetSize ( ) ) return false ;
// Check validity of each edge
for ( int32_t i_pt = 0 ; i_pt < arr - > GetSize ( ) ; + + i_pt )
{
const C4Value pt = arr - > GetItem ( i_pt ) ;
const C4ValueArray * pta ;
2016-11-11 02:43:03 +00:00
const C4PropList * ptp = pt . getPropList ( ) ;
if ( ! ptp ) return false ;
pta = ptp - > GetPropertyArray ( P_Vertices ) ;
2016-11-02 13:24:14 +00:00
if ( ! pta ) return false ;
// Needs two vertices (may have more values which are ignored)
if ( pta - > GetSize ( ) < 2 ) return false ;
}
return true ;
}
bool C4PropertyDelegateGraph : : IsPasteValid ( const C4Value & val ) const
{
2016-11-13 02:23:00 +00:00
// Unfortunately, there is no good way to determine the correct value for fixed structure / position graph pastes
// So just reject pastes for now
// (TODO: Could store a default structure in a property and compare to that)
if ( horizontal_fix | | vertical_fix | | structure_fix ) return false ;
2016-11-02 13:24:14 +00:00
// Check storage as prop list
const int32_t n_props = 2 ;
C4Value prop_vals [ n_props ] ; // vertices & edges
C4PropertyName property_names [ n_props ] = { P_Vertices , P_Edges } ;
2016-11-11 02:43:03 +00:00
C4PropList * val_proplist = val . getPropList ( ) ;
if ( ! val_proplist ) return false ;
for ( int32_t i = 0 ; i < n_props ; + + i )
2016-11-02 13:24:14 +00:00
{
2016-11-11 02:43:03 +00:00
val_proplist - > GetProperty ( property_names [ i ] , & prop_vals [ i ] ) ;
2016-11-02 13:24:14 +00:00
}
2016-11-11 02:43:03 +00:00
// extra properties are OK
2016-11-02 13:24:14 +00:00
// Check validity of vertices and edges
return IsVertexPasteValid ( prop_vals [ 0 ] ) & & IsEdgePasteValid ( prop_vals [ 1 ] ) ;
}
2016-11-11 02:43:03 +00:00
void C4PropertyDelegateGraph : : ConnectSignals ( C4ConsoleQtShape * shape , const C4PropertyPath & property_path ) const
{
C4ConsoleQtGraph * shape_graph = static_cast < C4ConsoleQtGraph * > ( shape ) ;
connect ( shape_graph , & C4ConsoleQtGraph : : GraphEdit , this , [ this , shape , property_path ] ( C4ControlEditGraph : : Action action , int32_t index , int32_t x , int32_t y ) {
// Send graph editing via queue
: : Control . DoInput ( CID_EditGraph , new C4ControlEditGraph ( property_path . GetGetPath ( ) , action , index , x , y ) , CDT_Decide ) ;
2016-11-13 03:19:42 +00:00
// Also send update callback to root object
2017-02-26 15:45:38 +00:00
factory - > GetPropertyModel ( ) - > DoOnUpdateCall ( property_path , this ) ;
2016-11-11 02:43:03 +00:00
} ) ;
connect ( shape , & C4ConsoleQtShape : : BorderSelectionChanged , this , [ ] ( ) {
// Different part of the shape selected: Refresh info on next update
: : Console . EditCursor . InvalidateSelection ( ) ;
} ) ;
}
2016-11-02 13:24:14 +00:00
/* Areas shown in viewport: Polyline */
C4PropertyDelegatePolyline : : C4PropertyDelegatePolyline ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateGraph ( factory , props )
{
}
void C4PropertyDelegatePolyline : : DoPaint ( QPainter * painter , const QRect & inner_rect ) const
{
// Draw symbol as a sequence of connected lines
QPoint ctr = inner_rect . center ( ) ;
int r = inner_rect . height ( ) * 7 / 20 ;
painter - > drawLine ( ctr + QPoint ( - r , + r ) , ctr + QPoint ( - r / 3 , - r ) ) ;
painter - > drawLine ( ctr + QPoint ( - r / 3 , - r ) , ctr + QPoint ( + r / 3 , + r ) ) ;
painter - > drawLine ( ctr + QPoint ( + r / 3 , + r ) , ctr + QPoint ( + r , - r ) ) ;
}
bool C4PropertyDelegatePolyline : : IsPasteValid ( const C4Value & val ) const
{
// Expect just a vertex array
return IsVertexPasteValid ( val ) ;
}
/* Areas shown in viewport: Closed polyon */
C4PropertyDelegatePolygon : : C4PropertyDelegatePolygon ( const class C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegateGraph ( factory , props )
{
}
void C4PropertyDelegatePolygon : : DoPaint ( QPainter * painter , const QRect & inner_rect ) const
{
// Draw symbol as a parallelogram
QPoint ctr = inner_rect . center ( ) ;
int r = inner_rect . height ( ) * 7 / 20 ;
painter - > drawLine ( ctr + QPoint ( - r * 3 / 2 , + r ) , ctr + QPoint ( - r , - r ) ) ;
painter - > drawLine ( ctr + QPoint ( - r , - r ) , ctr + QPoint ( + r * 3 / 2 , - r ) ) ;
painter - > drawLine ( ctr + QPoint ( + r * 3 / 2 , - r ) , ctr + QPoint ( + r , + r ) ) ;
painter - > drawLine ( ctr + QPoint ( + r , + r ) , ctr + QPoint ( - r * 3 / 2 , + r ) ) ;
}
bool C4PropertyDelegatePolygon : : IsPasteValid ( const C4Value & val ) const
{
// Expect just a vertex array
return IsVertexPasteValid ( val ) ;
}
2016-04-04 03:46:58 +00:00
/* Delegate factory: Create delegates based on the C4Value type */
2017-05-07 11:50:00 +00:00
C4PropertyDelegateFactory : : C4PropertyDelegateFactory ( ) : effect_delegate ( this , nullptr )
2016-10-14 14:49:14 +00:00
{
}
2016-06-08 04:37:48 +00:00
C4PropertyDelegate * C4PropertyDelegateFactory : : CreateDelegateByPropList ( C4PropList * props ) const
{
if ( props )
2016-04-04 03:46:58 +00:00
{
2016-06-08 04:37:48 +00:00
const C4String * str = props - > GetPropertyStr ( P_Type ) ;
if ( str )
{
// create default base types
if ( str - > GetData ( ) = = " int " ) return new C4PropertyDelegateInt ( this , props ) ;
2016-06-16 04:40:25 +00:00
if ( str - > GetData ( ) = = " string " ) return new C4PropertyDelegateString ( this , props ) ;
2016-06-08 04:37:48 +00:00
if ( str - > GetData ( ) = = " array " ) return new C4PropertyDelegateArray ( this , props ) ;
if ( str - > GetData ( ) = = " proplist " ) return new C4PropertyDelegatePropList ( this , props ) ;
if ( str - > GetData ( ) = = " color " ) return new C4PropertyDelegateColor ( this , props ) ;
if ( str - > GetData ( ) = = " def " ) return new C4PropertyDelegateDef ( this , props ) ;
2016-06-16 18:59:04 +00:00
if ( str - > GetData ( ) = = " object " ) return new C4PropertyDelegateObject ( this , props ) ;
2016-06-08 04:37:48 +00:00
if ( str - > GetData ( ) = = " enum " ) return new C4PropertyDelegateEnum ( this , props ) ;
2016-07-28 02:35:11 +00:00
if ( str - > GetData ( ) = = " sound " ) return new C4PropertyDelegateSound ( this , props ) ;
2016-06-08 04:37:48 +00:00
if ( str - > GetData ( ) = = " bool " ) return new C4PropertyDelegateBool ( this , props ) ;
if ( str - > GetData ( ) = = " has_effect " ) return new C4PropertyDelegateHasEffect ( this , props ) ;
if ( str - > GetData ( ) = = " c4valueenum " ) return new C4PropertyDelegateC4ValueEnum ( this , props ) ;
2016-08-19 20:12:53 +00:00
if ( str - > GetData ( ) = = " rect " ) return new C4PropertyDelegateRect ( this , props ) ;
if ( str - > GetData ( ) = = " circle " ) return new C4PropertyDelegateCircle ( this , props ) ;
if ( str - > GetData ( ) = = " point " ) return new C4PropertyDelegatePoint ( this , props ) ;
2016-11-02 13:24:14 +00:00
if ( str - > GetData ( ) = = " graph " ) return new C4PropertyDelegateGraph ( this , props ) ;
if ( str - > GetData ( ) = = " polyline " ) return new C4PropertyDelegatePolyline ( this , props ) ;
if ( str - > GetData ( ) = = " polygon " ) return new C4PropertyDelegatePolygon ( this , props ) ;
2016-06-08 04:37:48 +00:00
if ( str - > GetData ( ) = = " any " ) return new C4PropertyDelegateC4ValueInput ( this , props ) ;
// unknown type
LogF ( " Invalid delegate type: %s. " , str - > GetCStr ( ) ) ;
}
2016-04-04 03:46:58 +00:00
}
2016-06-08 04:37:48 +00:00
// Default fallback
return new C4PropertyDelegateC4ValueInput ( this , props ) ;
2016-04-04 03:46:58 +00:00
}
C4PropertyDelegate * C4PropertyDelegateFactory : : GetDelegateByValue ( const C4Value & val ) const
{
2016-06-08 04:37:48 +00:00
auto iter = delegates . find ( val . getPropList ( ) ) ;
2016-04-04 03:46:58 +00:00
if ( iter ! = delegates . end ( ) ) return iter - > second . get ( ) ;
2016-06-08 04:37:48 +00:00
C4PropertyDelegate * new_delegate = CreateDelegateByPropList ( val . getPropList ( ) ) ;
delegates . insert ( std : : make_pair ( val . getPropList ( ) , std : : unique_ptr < C4PropertyDelegate > ( new_delegate ) ) ) ;
2016-04-04 03:46:58 +00:00
return new_delegate ;
}
C4PropertyDelegate * C4PropertyDelegateFactory : : GetDelegateByIndex ( const QModelIndex & index ) const
{
2016-07-06 02:53:41 +00:00
C4ConsoleQtPropListModel : : Property * prop = property_model - > GetPropByIndex ( index ) ;
2016-11-02 23:58:02 +00:00
if ( ! prop ) return nullptr ;
2016-04-04 03:46:58 +00:00
if ( ! prop - > delegate ) prop - > delegate = GetDelegateByValue ( prop - > delegate_info ) ;
return prop - > delegate ;
}
void C4PropertyDelegateFactory : : ClearDelegates ( )
{
delegates . clear ( ) ;
}
void C4PropertyDelegateFactory : : EditorValueChanged ( QWidget * editor )
{
emit commitData ( editor ) ;
}
void C4PropertyDelegateFactory : : EditingDone ( QWidget * editor )
{
emit commitData ( editor ) ;
//emit closeEditor(editor); - done by qt somewhere else...
}
void C4PropertyDelegateFactory : : setEditorData ( QWidget * editor , const QModelIndex & index ) const
{
// Put property value from proplist into editor
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
2016-06-06 05:54:05 +00:00
if ( ! CheckCurrentEditor ( d , editor ) ) return ;
2016-06-03 05:20:43 +00:00
// Fetch property only first time - ignore further updates to the same value to simplify editing
2016-07-06 02:53:41 +00:00
C4ConsoleQtPropListModel : : Property * prop = property_model - > GetPropByIndex ( index ) ;
2016-06-03 05:20:43 +00:00
if ( ! prop ) return ;
2016-04-04 03:46:58 +00:00
C4Value val ;
2016-05-28 13:30:54 +00:00
d - > GetPropertyValue ( prop - > parent_value , prop - > key , index . row ( ) , & val ) ;
2016-06-03 05:20:43 +00:00
if ( ! prop - > about_to_edit & & val = = last_edited_value ) return ;
last_edited_value = val ;
prop - > about_to_edit = false ;
2016-05-28 13:30:54 +00:00
d - > SetEditorData ( editor , val , d - > GetPathForProperty ( prop ) ) ;
2016-04-04 03:46:58 +00:00
}
void C4PropertyDelegateFactory : : setModelData ( QWidget * editor , QAbstractItemModel * model , const QModelIndex & index ) const
{
// Fetch property value from editor and set it into proplist
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
2016-06-06 05:54:05 +00:00
if ( ! CheckCurrentEditor ( d , editor ) ) return ;
2016-07-06 02:53:41 +00:00
C4ConsoleQtPropListModel : : Property * prop = property_model - > GetPropByIndex ( index ) ;
2016-04-25 20:17:10 +00:00
SetPropertyData ( d , editor , prop ) ;
}
void C4PropertyDelegateFactory : : SetPropertyData ( const C4PropertyDelegate * d , QObject * editor , C4ConsoleQtPropListModel : : Property * editor_prop ) const
{
// Set according to delegate
2017-02-26 15:45:38 +00:00
const C4PropertyPath set_path = d - > GetPathForProperty ( editor_prop ) ;
d - > SetModelData ( editor , set_path , editor_prop - > shape ? editor_prop - > shape - > Get ( ) : nullptr ) ;
2016-04-04 03:46:58 +00:00
}
QWidget * C4PropertyDelegateFactory : : createEditor ( QWidget * parent , const QStyleOptionViewItem & option , const QModelIndex & index ) const
{
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
2016-11-02 23:58:02 +00:00
if ( ! d ) return nullptr ;
2016-07-06 02:53:41 +00:00
C4ConsoleQtPropListModel : : Property * prop = property_model - > GetPropByIndex ( index ) ;
2016-04-04 03:46:58 +00:00
prop - > about_to_edit = true ;
2016-07-30 06:37:33 +00:00
QWidget * editor = d - > CreateEditor ( this , parent , option , true , false ) ;
2016-04-25 20:17:10 +00:00
// Connect value change signals (if editing is possible for this property)
2016-04-04 03:46:58 +00:00
// For some reason, commitData needs a non-const pointer
2016-04-25 20:17:10 +00:00
if ( editor )
{
connect ( d , & C4PropertyDelegate : : EditorValueChangedSignal , editor , [ editor , this ] ( QWidget * signal_editor ) {
if ( signal_editor = = editor ) const_cast < C4PropertyDelegateFactory * > ( this ) - > EditorValueChanged ( editor ) ;
} ) ;
connect ( d , & C4PropertyDelegate : : EditingDoneSignal , editor , [ editor , this ] ( QWidget * signal_editor ) {
if ( signal_editor = = editor ) const_cast < C4PropertyDelegateFactory * > ( this ) - > EditingDone ( editor ) ;
} ) ;
}
2016-05-23 23:22:05 +00:00
current_editor = editor ;
2016-06-06 05:54:05 +00:00
current_editor_delegate = d ;
2016-04-04 03:46:58 +00:00
return editor ;
}
2016-05-23 23:22:05 +00:00
void C4PropertyDelegateFactory : : destroyEditor ( QWidget * editor , const QModelIndex & index ) const
{
2016-06-06 05:54:05 +00:00
if ( editor = = current_editor )
{
current_editor = nullptr ;
current_editor_delegate = nullptr ;
2016-06-16 20:15:15 +00:00
: : Console . EditCursor . SetHighlightedObject ( nullptr ) ;
2016-06-06 05:54:05 +00:00
}
2016-05-23 23:22:05 +00:00
QStyledItemDelegate : : destroyEditor ( editor , index ) ;
}
2016-04-04 03:46:58 +00:00
void C4PropertyDelegateFactory : : updateEditorGeometry ( QWidget * editor , const QStyleOptionViewItem & option , const QModelIndex & index ) const
{
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
2016-06-06 05:54:05 +00:00
if ( ! CheckCurrentEditor ( d , editor ) ) return ;
2016-04-04 03:46:58 +00:00
return d - > UpdateEditorGeometry ( editor , option ) ;
}
2016-04-09 18:20:31 +00:00
QSize C4PropertyDelegateFactory : : sizeHint ( const QStyleOptionViewItem & option , const QModelIndex & index ) const
{
int height = QApplication : : fontMetrics ( ) . height ( ) + 4 ;
return QSize ( 100 , height ) ;
}
2016-04-25 20:17:10 +00:00
void C4PropertyDelegateFactory : : paint ( QPainter * painter , const QStyleOptionViewItem & option , const QModelIndex & index ) const
{
// Delegate has custom painting?
2016-07-06 02:53:41 +00:00
C4ConsoleQtPropListModel : : Property * prop = property_model - > GetPropByIndex ( index ) ;
2016-04-25 20:17:10 +00:00
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
if ( d & & prop & & d - > HasCustomPaint ( ) )
{
C4Value val ;
2016-05-28 13:30:54 +00:00
d - > GetPropertyValue ( prop - > parent_value , prop - > key , index . row ( ) , & val ) ;
2016-07-18 23:24:53 +00:00
if ( d - > Paint ( painter , option , val ) ) return ;
2016-04-25 20:17:10 +00:00
}
// Otherwise use default paint implementation
QStyledItemDelegate : : paint ( painter , option , index ) ;
}
2016-05-23 23:22:05 +00:00
void C4PropertyDelegateFactory : : OnPropListChanged ( )
{
if ( current_editor ) emit closeEditor ( current_editor ) ;
}
2016-06-06 05:54:05 +00:00
bool C4PropertyDelegateFactory : : CheckCurrentEditor ( C4PropertyDelegate * d , QWidget * editor ) const
{
if ( ! d | | ( editor & & editor ! = current_editor ) | | d ! = current_editor_delegate )
{
//const_cast<C4PropertyDelegateFactory *>(this)->emit closeEditor(current_editor);
destroyEditor ( current_editor , QModelIndex ( ) ) ;
return false ;
}
return true ;
}
2016-08-19 20:12:53 +00:00
static const QString property_mime_type ( " application/OpenClonkProperty " ) ;
void C4PropertyDelegateFactory : : CopyToClipboard ( const QModelIndex & index )
{
// Re-resolve property. May have shifted while the menu was open
C4ConsoleQtPropListModel : : Property * prop = property_model - > GetPropByIndex ( index ) ;
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
if ( ! prop | | ! d ) return ;
// Get data to copy
C4Value val ;
d - > GetPropertyValue ( prop - > parent_value , prop - > key , index . row ( ) , & val ) ;
StdStrBuf data_str ( val . GetDataString ( 99999 ) ) ;
// Copy it as an internal mime type and text
// Presence of the internal type shows that this is a copied property so it can be safely evaluate without sync problems
QClipboard * clipboard = QApplication : : clipboard ( ) ;
clipboard - > clear ( ) ;
std : : unique_ptr < QMimeData > data ( new QMimeData ( ) ) ;
data - > setData ( property_mime_type , QByteArray ( data_str . getData ( ) , data_str . getSize ( ) ) ) ;
data - > setText ( data_str . getData ( ) ) ;
clipboard - > setMimeData ( data . release ( ) ) ;
}
bool C4PropertyDelegateFactory : : PasteFromClipboard ( const QModelIndex & index , bool check_only )
{
// Re-resolve property. May have shifted while the menu was open
C4ConsoleQtPropListModel : : Property * prop = property_model - > GetPropByIndex ( index ) ;
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
if ( ! prop | | ! d ) return false ;
// Check value to paste
QClipboard * clipboard = QApplication : : clipboard ( ) ;
const QMimeData * data = clipboard - > mimeData ( ) ;
if ( ! data ) return false ; // empty clipboard
// Prefer copied property; fall back to text
StdStrBuf str_data ;
if ( data - > hasFormat ( property_mime_type ) )
{
QByteArray prop_data = data - > data ( property_mime_type ) ;
str_data . Copy ( prop_data ) ;
// Check data type
C4Value val = : : AulExec . DirectExec ( & : : ScriptEngine , str_data . getData ( ) , " paste check " , false , nullptr , false ) ;
if ( ! d - > IsPasteValid ( val ) ) return false ;
}
else if ( data - > hasText ( ) )
{
// Text can always be pasted.
// Cannot perform a type check here because a function may have been copied that affects sync.
QString text = data - > text ( ) ;
str_data . Copy ( text . toUtf8 ( ) ) ;
}
else
{
// Unknown data type in clipboard. Cannot paste.
return false ;
}
if ( check_only ) return true ;
// Alright, paste!
d - > GetPathForProperty ( prop ) . SetProperty ( str_data . getData ( ) ) ;
2017-02-26 16:00:20 +00:00
return true ;
2016-08-19 20:12:53 +00:00
}
bool C4PropertyDelegateFactory : : editorEvent ( QEvent * event , QAbstractItemModel * model , const QStyleOptionViewItem & option , const QModelIndex & index )
{
// Custom context menu on item
// I would like to use the regular context menu functions of Qt on the parent widget
// but something is eating the right-click event before it triggers a context event.
// So just hack it on right click.
// Button check
if ( event - > type ( ) = = QEvent : : Type : : MouseButtonPress )
{
QMouseEvent * mev = static_cast < QMouseEvent * > ( event ) ;
if ( mev - > button ( ) = = Qt : : MouseButton : : RightButton )
{
// Item check
C4ConsoleQtPropListModel : : Property * prop = property_model - > GetPropByIndex ( index ) ;
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
if ( d & & prop )
{
// Context menu on a valid property: Show copy+paste menu
QMenu * context = new QMenu ( const_cast < QWidget * > ( option . widget ) ) ;
QAction * copy_action = new QAction ( LoadResStr ( " IDS_DLG_COPY " ) , context ) ;
QAction * paste_action = new QAction ( LoadResStr ( " IDS_DLG_PASTE " ) , context ) ;
QModelIndex index_copy ( index ) ;
connect ( copy_action , & QAction : : triggered , this , [ this , index_copy ] ( ) {
this - > CopyToClipboard ( index_copy ) ;
} ) ;
connect ( paste_action , & QAction : : triggered , this , [ this , index_copy ] ( ) {
this - > PasteFromClipboard ( index_copy , false ) ;
} ) ;
paste_action - > setEnabled ( PasteFromClipboard ( index_copy , true ) ) ; // Paste grayed out if not valid
context - > addAction ( copy_action ) ;
context - > addAction ( paste_action ) ;
context - > popup ( mev - > globalPos ( ) ) ;
context - > connect ( context , & QMenu : : aboutToHide , context , & QWidget : : deleteLater ) ;
// It's easier to see which item is affected when it's selected
QItemSelectionModel * sel_model = property_model - > GetSelectionModel ( ) ;
QItemSelection new_sel ;
new_sel . select ( model - > index ( index . row ( ) , 0 , index . parent ( ) ) , index ) ;
sel_model - > select ( new_sel , QItemSelectionModel : : SelectionFlag : : SelectCurrent ) ;
return true ;
}
}
}
return QStyledItemDelegate : : editorEvent ( event , model , option , index ) ;
}
2016-04-04 03:46:58 +00:00
/* Proplist table view */
2016-02-12 04:37:13 +00:00
2016-04-17 02:02:24 +00:00
C4ConsoleQtPropListModel : : C4ConsoleQtPropListModel ( C4PropertyDelegateFactory * delegate_factory )
2016-06-03 05:20:43 +00:00
: delegate_factory ( delegate_factory ) , selection_model ( nullptr )
2016-02-12 04:37:13 +00:00
{
2016-04-09 18:20:31 +00:00
header_font . setBold ( true ) ;
2016-08-02 05:06:24 +00:00
important_property_font . setBold ( true ) ;
2016-06-03 05:20:43 +00:00
connect ( this , & C4ConsoleQtPropListModel : : ProplistChanged , this , & C4ConsoleQtPropListModel : : UpdateSelection , Qt : : QueuedConnection ) ;
2016-06-08 04:37:48 +00:00
layout_valid = false ;
2016-02-12 04:37:13 +00:00
}
2017-05-03 18:28:00 +00:00
C4ConsoleQtPropListModel : : ~ C4ConsoleQtPropListModel ( ) = default ;
2016-02-12 04:37:13 +00:00
2016-11-11 02:43:03 +00:00
bool C4ConsoleQtPropListModel : : 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 )
2016-04-17 02:02:24 +00:00
{
2016-08-02 05:06:24 +00:00
// Add all properties from this EditorProps group
std : : vector < C4String * > property_names = add_proplist - > GetUnsortedProperties ( nullptr ) ;
if ( ! property_names . size ( ) ) return false ;
// Prepare group array
2016-06-08 04:37:48 +00:00
if ( property_groups . size ( ) = = group_index )
{
layout_valid = false ;
property_groups . resize ( group_index + 1 ) ;
}
2016-04-17 02:02:24 +00:00
PropertyGroup & properties = property_groups [ group_index ] ;
C4PropListStatic * proplist_static = add_proplist - > IsStatic ( ) ;
2016-08-02 05:06:24 +00:00
// Resolve properties
struct PropAndKey
{
C4PropList * prop ;
C4String * key ;
int32_t priority ;
C4String * name ;
PropAndKey ( C4PropList * prop , C4String * key , int32_t priority , C4String * name )
: prop ( prop ) , key ( key ) , priority ( priority ) , name ( name ) { }
} ;
std : : vector < PropAndKey > new_properties_resolved ;
new_properties_resolved . reserve ( property_names . size ( ) ) ;
for ( C4String * prop_name : property_names )
{
C4Value prop_val ;
add_proplist - > GetPropertyByS ( prop_name , & prop_val ) ;
C4PropList * prop = prop_val . getPropList ( ) ;
if ( prop )
{
C4String * name = prop - > GetPropertyStr ( P_Name ) ;
if ( ! name ) name = prop_name ;
int32_t priority = prop - > GetPropertyInt ( P_Priority ) ;
new_properties_resolved . emplace_back ( PropAndKey ( { prop , prop_name , priority , name } ) ) ;
}
}
// Sort by priority primarily and name secondarily
std : : sort ( new_properties_resolved . begin ( ) , new_properties_resolved . end ( ) , [ ] ( const PropAndKey & a , const PropAndKey & b ) - > bool {
if ( a . priority ! = b . priority ) return a . priority > b . priority ;
return strcmp ( a . name - > GetCStr ( ) , b . name - > GetCStr ( ) ) < 0 ;
} ) ;
// Setup group
2016-04-17 02:02:24 +00:00
properties . name = name ;
2016-08-02 05:06:24 +00:00
if ( properties . props . size ( ) ! = new_properties_resolved . size ( ) )
2016-06-08 04:37:48 +00:00
{
layout_valid = false ;
2016-08-02 05:06:24 +00:00
properties . props . resize ( new_properties_resolved . size ( ) ) ;
2016-06-08 04:37:48 +00:00
}
2016-07-30 05:05:12 +00:00
C4Effect * fx = target_proplist - > GetEffect ( ) ;
2016-08-02 05:06:24 +00:00
for ( int32_t i = 0 ; i < new_properties_resolved . size ( ) ; + + i )
2016-04-17 02:02:24 +00:00
{
2016-04-25 20:17:10 +00:00
Property * prop = & properties . props [ i ] ;
2016-05-23 23:22:05 +00:00
// Property access path
2016-05-28 13:30:54 +00:00
prop - > parent_value . SetPropList ( target_proplist ) ;
2016-11-11 02:43:03 +00:00
prop - > property_path = group_target_path ;
2016-06-03 05:20:43 +00:00
// ID for default selection memory
2016-08-02 05:06:24 +00:00
const PropAndKey & prop_def = new_properties_resolved [ i ] ;
if ( default_selection = = prop_def . key ) * default_selection_index = i ;
2016-05-23 23:22:05 +00:00
// Property data
2016-07-25 03:00:24 +00:00
prop - > help_text = nullptr ;
2016-04-25 20:17:10 +00:00
prop - > delegate_info . Set0 ( ) ; // default C4Value delegate
prop - > group_idx = group_index ;
2016-10-14 14:49:14 +00:00
prop - > about_to_edit = false ;
2016-08-02 05:06:24 +00:00
prop - > key = prop_def . prop - > GetPropertyStr ( P_Key ) ;
if ( ! prop - > key ) properties . props [ i ] . key = prop_def . key ;
prop - > display_name = prop_def . name ;
if ( ! prop - > display_name ) prop - > display_name = prop_def . key ;
prop - > help_text = prop_def . prop - > GetPropertyStr ( P_EditorHelp ) ;
prop - > priority = prop_def . priority ;
prop - > delegate_info . SetPropList ( prop_def . prop ) ;
2016-04-25 20:17:10 +00:00
prop - > delegate = delegate_factory - > GetDelegateByValue ( prop - > delegate_info ) ;
C4Value v ;
2016-05-28 13:30:54 +00:00
C4Value v_target_proplist = C4VPropList ( target_proplist ) ;
prop - > delegate - > GetPropertyValue ( v_target_proplist , prop - > key , 0 , & v ) ;
2016-04-25 20:17:10 +00:00
// Connect editable shape to property
2016-07-21 04:20:44 +00:00
C4PropertyPath new_shape_property_path = prop - > delegate - > GetPathForProperty ( prop ) ;
2016-07-18 23:24:53 +00:00
const C4PropertyDelegateShape * new_shape_delegate = prop - > delegate - > GetShapeDelegate ( v , & new_shape_property_path ) ;
if ( new_shape_delegate ! = prop - > shape_delegate | | ! ( prop - > shape_property_path = = new_shape_property_path ) )
2016-04-25 20:17:10 +00:00
{
prop - > shape_delegate = new_shape_delegate ;
2016-07-18 23:24:53 +00:00
prop - > shape_property_path = new_shape_property_path ;
2016-04-25 20:17:10 +00:00
if ( new_shape_delegate )
{
2016-11-11 02:43:03 +00:00
// Re-use loaded shape if possible (e.g. if only the index has moved)
2016-11-03 01:27:53 +00:00
std : : string shape_index = std : : string ( prop - > shape_property_path . GetGetPath ( ) ) ;
prop - > shape = & shapes [ shape_index ] ;
2016-11-11 02:43:03 +00:00
C4ConsoleQtShape * shape = prop - > shape - > Get ( ) ;
if ( shape )
{
if ( shape - > GetProperties ( ) ! = new_shape_delegate - > GetCreationProps ( ) . getPropList ( ) )
{
// Shape at same path but with different properties? Then re-create
shape = nullptr ;
}
}
if ( ! shape )
{
// New shape or shape type mismatch: Generate new shape at this path and put into the shape holder list
shape = : : Console . EditCursor . GetShapes ( ) - > CreateShape ( base_object ? base_object : target_proplist - > GetObject ( ) , new_shape_delegate - > GetCreationProps ( ) . getPropList ( ) , v , new_shape_delegate ) ;
C4PropertyDelegateFactory * factory = this - > delegate_factory ;
new_shape_delegate - > ConnectSignals ( shape , prop - > shape_property_path ) ;
prop - > shape - > Set ( shape ) ;
2016-11-13 02:51:56 +00:00
prop - > shape - > SetLastValue ( v ) ;
2016-11-11 02:43:03 +00:00
}
2016-04-25 20:17:10 +00:00
}
2016-07-18 23:24:53 +00:00
else
{
2016-11-03 01:27:53 +00:00
prop - > shape = nullptr ;
2016-07-18 23:24:53 +00:00
}
2016-04-17 02:02:24 +00:00
}
2016-11-03 01:27:53 +00:00
if ( prop - > shape )
{
2016-11-13 02:51:56 +00:00
// Mark this shape to be kept aftre update is complete
2016-11-03 01:27:53 +00:00
prop - > shape - > visit ( ) ;
2016-11-13 02:51:56 +00:00
// Update shape by value if it was changed externally
if ( ! prop - > shape - > GetLastValue ( ) . IsIdenticalTo ( v ) )
{
prop - > shape - > Get ( ) - > SetValue ( v ) ;
prop - > shape - > SetLastValue ( v ) ;
}
2016-11-03 01:27:53 +00:00
}
2016-04-17 02:02:24 +00:00
}
return true ;
}
2016-10-14 14:49:14 +00:00
bool C4ConsoleQtPropListModel : : AddEffectGroup ( int32_t group_index , C4Object * base_object )
{
// Count non-dead effects
C4Effect * * effect_list = base_object ? & base_object - > pEffects : & : : ScriptEngine . pGlobalEffects ;
int32_t num_effects = 0 ;
for ( C4Effect * effect = * effect_list ; effect ; effect = effect - > pNext )
{
num_effects + = effect - > IsActive ( ) ;
}
// Return false to signal that no effect group has been added
if ( ! num_effects ) return false ;
// Prepare group array
if ( property_groups . size ( ) = = group_index )
{
layout_valid = false ;
property_groups . resize ( group_index + 1 ) ;
}
PropertyGroup & properties = property_groups [ group_index ] ;
if ( properties . props . size ( ) ! = num_effects )
{
layout_valid = false ;
properties . props . resize ( num_effects ) ;
}
properties . name = LoadResStr ( " IDS_CNS_EFFECTS " ) ;
// Add all (non-dead) effects of given object (or global effects if base_object is nullptr)
int32_t num_added = 0 ;
for ( C4Effect * effect = * effect_list ; effect ; effect = effect - > pNext )
{
if ( effect - > IsActive ( ) )
{
Property * prop = & properties . props [ num_added + + ] ;
prop - > parent_value . SetPropList ( base_object ? ( C4PropList * ) base_object : & : : ScriptEngine ) ;
prop - > property_path = C4PropertyPath ( effect , base_object ) ;
prop - > help_text = nullptr ;
prop - > delegate_info . Set0 ( ) ;
prop - > group_idx = group_index ;
prop - > key = : : Strings . RegString ( prop - > property_path . GetGetPath ( ) ) ;
prop - > display_name = effect - > GetPropertyStr ( P_Name ) ;
prop - > priority = 0 ;
prop - > delegate = delegate_factory - > GetEffectDelegate ( ) ;
2016-11-03 01:27:53 +00:00
prop - > shape = nullptr ;
2016-10-14 14:49:14 +00:00
prop - > shape_delegate = nullptr ;
prop - > shape_property_path . Clear ( ) ;
prop - > about_to_edit = false ;
prop - > group_idx = group_index ;
}
}
// Return true to signal that effect group has been added
return true ;
}
2016-05-25 03:06:00 +00:00
void C4ConsoleQtPropListModel : : SetBasePropList ( C4PropList * new_proplist )
2016-02-12 04:37:13 +00:00
{
2016-05-25 03:06:00 +00:00
// Clear stack and select new proplist
2016-02-12 04:37:13 +00:00
// Update properties
2016-05-28 13:30:54 +00:00
target_value . SetPropList ( new_proplist ) ;
2016-05-25 03:06:00 +00:00
base_proplist . SetPropList ( new_proplist ) ;
// objects derive their custom properties
2016-05-28 13:30:54 +00:00
info_proplist . SetPropList ( target_value . getObj ( ) ) ;
2016-05-25 03:06:00 +00:00
target_path = C4PropertyPath ( new_proplist ) ;
target_path_stack . clear ( ) ;
2016-06-03 05:20:43 +00:00
UpdateValue ( true ) ;
2016-05-28 13:30:54 +00:00
delegate_factory - > OnPropListChanged ( ) ;
2016-05-25 03:06:00 +00:00
}
2016-07-13 21:18:08 +00:00
void C4ConsoleQtPropListModel : : DescendPath ( const C4Value & new_value , C4PropList * new_info_proplist , const C4PropertyPath & new_path )
2016-05-25 03:06:00 +00:00
{
// Add previous proplist to stack
2017-05-03 18:28:00 +00:00
target_path_stack . emplace_back ( target_path , target_value , info_proplist ) ;
2016-05-25 03:06:00 +00:00
// descend
2016-05-28 13:30:54 +00:00
target_value = new_value ;
2016-05-25 03:06:00 +00:00
info_proplist . SetPropList ( new_info_proplist ) ;
target_path = new_path ;
2016-06-03 05:20:43 +00:00
UpdateValue ( true ) ;
2016-05-28 13:30:54 +00:00
delegate_factory - > OnPropListChanged ( ) ;
2016-05-25 03:06:00 +00:00
}
void C4ConsoleQtPropListModel : : AscendPath ( )
{
// Go up in target stack (if possible)
for ( ; ; )
{
if ( ! target_path_stack . size ( ) )
{
SetBasePropList ( nullptr ) ;
return ;
}
TargetStackEntry entry = target_path_stack . back ( ) ;
target_path_stack . pop_back ( ) ;
if ( ! entry . value | | ! entry . info_proplist ) continue ; // property was removed; go up further in stack
// Safety: Make sure we're still on the same value
C4Value target = entry . path . ResolveValue ( ) ;
if ( ! target . IsIdenticalTo ( entry . value ) ) continue ;
// Set new value
target_path = entry . path ;
2016-05-28 13:30:54 +00:00
target_value = entry . value ;
2016-05-25 03:06:00 +00:00
info_proplist = entry . info_proplist ;
2016-06-03 05:20:43 +00:00
UpdateValue ( true ) ;
2016-05-25 03:06:00 +00:00
break ;
}
2016-07-06 02:53:41 +00:00
// Any current editor needs to close
delegate_factory - > OnPropListChanged ( ) ;
2016-05-25 03:06:00 +00:00
}
2016-06-03 05:20:43 +00:00
void C4ConsoleQtPropListModel : : UpdateValue ( bool select_default )
2016-05-25 03:06:00 +00:00
{
2016-07-06 02:53:41 +00:00
emit layoutAboutToBeChanged ( ) ;
2016-06-03 05:20:43 +00:00
// Update target value from path
target_value = target_path . ResolveValue ( ) ;
2016-11-03 01:27:53 +00:00
// Prepare shape list update
C4ConsoleQtShapeHolder : : begin_visit ( ) ;
2016-05-28 13:30:54 +00:00
// Safe-get from C4Values in case any prop lists or arrays got deleted
2016-06-03 05:20:43 +00:00
int32_t num_groups , default_selection_group = - 1 , default_selection_index = - 1 ;
2016-05-28 13:30:54 +00:00
switch ( target_value . GetType ( ) )
{
case C4V_PropList :
2016-06-03 05:20:43 +00:00
num_groups = UpdateValuePropList ( target_value . _getPropList ( ) , & default_selection_group , & default_selection_index ) ;
2016-05-28 13:30:54 +00:00
break ;
case C4V_Array :
2016-06-03 05:20:43 +00:00
num_groups = UpdateValueArray ( target_value . _getArray ( ) , & default_selection_group , & default_selection_index ) ;
2016-05-28 13:30:54 +00:00
break ;
default : // probably nil
num_groups = 0 ;
break ;
}
2016-11-03 01:27:53 +00:00
// Remove any unreferenced shapes
for ( auto iter = shapes . begin ( ) ; iter ! = shapes . end ( ) ; )
{
if ( ! iter - > second . was_visited ( ) )
{
iter = shapes . erase ( iter ) ;
}
else
{
+ + iter ;
}
}
2016-05-28 13:30:54 +00:00
// Update model range
2016-06-08 04:37:48 +00:00
if ( num_groups ! = property_groups . size ( ) )
{
layout_valid = false ;
property_groups . resize ( num_groups ) ;
}
2016-07-06 02:53:41 +00:00
if ( ! layout_valid )
{
// We do not adjust persistent indices for now
// Usually, if layout changed, it's because the target value changed and we don't want to select/expand random stuff in the new proplist
layout_valid = true ;
}
emit layoutChanged ( ) ;
2016-05-28 13:30:54 +00:00
QModelIndex topLeft = index ( 0 , 0 , QModelIndex ( ) ) ;
QModelIndex bottomRight = index ( rowCount ( ) - 1 , columnCount ( ) - 1 , QModelIndex ( ) ) ;
emit dataChanged ( topLeft , bottomRight ) ;
2016-06-03 05:20:43 +00:00
// Initial selection
if ( select_default ) emit ProplistChanged ( default_selection_group , default_selection_index ) ;
}
2016-05-28 13:30:54 +00:00
2016-06-03 05:20:43 +00:00
void C4ConsoleQtPropListModel : : UpdateSelection ( int32_t major_sel , int32_t minor_sel ) const
{
if ( selection_model )
{
// Select by indexed elements only
selection_model - > clearSelection ( ) ;
if ( major_sel > = 0 )
{
QModelIndex sel = index ( major_sel , 0 , QModelIndex ( ) ) ;
if ( minor_sel > = 0 ) sel = index ( minor_sel , 0 , sel ) ;
selection_model - > select ( sel , QItemSelectionModel : : SelectCurrent ) ;
}
else
{
selection_model - > select ( QModelIndex ( ) , QItemSelectionModel : : SelectCurrent ) ;
}
}
2016-05-28 13:30:54 +00:00
}
2016-06-03 05:20:43 +00:00
int32_t C4ConsoleQtPropListModel : : UpdateValuePropList ( C4PropList * target_proplist , int32_t * default_selection_group , int32_t * default_selection_index )
2016-05-28 13:30:54 +00:00
{
assert ( target_proplist ) ;
2016-05-25 03:06:00 +00:00
C4PropList * base_proplist = this - > base_proplist . getPropList ( ) ;
2016-10-14 14:49:14 +00:00
C4Object * base_obj = this - > base_proplist . getObj ( ) , * obj = nullptr ;
2016-05-25 03:06:00 +00:00
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
2016-04-17 02:02:24 +00:00
int32_t num_groups = 0 ;
2016-11-11 02:43:03 +00:00
// Selected shape properties
C4ConsoleQtShape * selected_shape = : : Console . EditCursor . GetShapes ( ) - > GetSelectedShape ( ) ;
if ( selected_shape )
{
// Find property information for this shape
// Could also remember this pointer for every shape holder
// - but that would have to be updated on any property group vector resize
Property * prop = nullptr ;
for ( PropertyGroup & grp : property_groups )
{
for ( Property & check_prop : grp . props )
{
if ( check_prop . shape & & check_prop . shape - > Get ( ) = = selected_shape )
{
prop = & check_prop ;
break ;
}
}
if ( prop ) break ;
}
// Update selected shape item information
if ( prop & & prop - > delegate )
{
C4PropList * shape_item_editorprops , * shape_item_value ;
C4String * shape_item_name = nullptr ;
C4PropertyPath shape_item_target_path ;
C4Value v ;
C4Value v_target_proplist = C4VPropList ( target_proplist ) ;
prop - > delegate - > GetPropertyValue ( v_target_proplist , prop - > key , 0 , & v ) ;
2016-11-20 17:37:00 +00:00
C4PropertyPath shape_property_path = prop - > delegate - > GetPathForProperty ( prop ) ;
const C4PropertyDelegateShape * current_shape_delegate = prop - > delegate - > GetShapeDelegate ( v , & shape_property_path ) ; // to resolve v
2016-11-11 02:43:03 +00:00
if ( : : Console . EditCursor . GetShapes ( ) - > GetSelectedShapeData ( v , prop - > shape_property_path , & shape_item_editorprops , & shape_item_value , & shape_item_name , & shape_item_target_path ) )
{
if ( AddPropertyGroup ( shape_item_editorprops , num_groups , QString ( shape_item_name ? shape_item_name - > GetCStr ( ) : " ??? " ) , shape_item_value , shape_item_target_path , obj , nullptr , nullptr ) )
{
+ + num_groups ;
}
}
}
}
2016-05-28 13:30:54 +00:00
// Published properties
if ( info_proplist )
2016-04-04 03:46:58 +00:00
{
2016-06-03 05:20:43 +00:00
C4String * default_selection = info_proplist - > GetPropertyStr ( P_DefaultEditorProp ) ;
2016-10-14 14:49:14 +00:00
obj = info_proplist - > GetObject ( ) ;
2016-05-28 13:30:54 +00:00
// Properties from effects (no inheritance supported)
if ( obj )
2016-04-09 18:20:31 +00:00
{
2016-05-28 13:30:54 +00:00
for ( C4Effect * fx = obj - > pEffects ; fx ; fx = fx - > pNext )
2016-05-25 03:06:00 +00:00
{
2016-07-14 02:14:28 +00:00
if ( ! fx - > IsActive ( ) ) continue ; // skip dead effects
2016-05-28 13:30:54 +00:00
QString name = fx - > GetName ( ) ;
2016-07-13 21:18:08 +00:00
C4PropList * effect_editorprops = fx - > GetPropertyPropList ( P_EditorProps ) ;
2016-11-11 02:43:03 +00:00
if ( effect_editorprops & & AddPropertyGroup ( effect_editorprops , num_groups , name , fx , C4PropertyPath ( fx , obj ) , obj , nullptr , nullptr ) )
2016-05-28 13:30:54 +00:00
+ + num_groups ;
2016-04-17 02:02:24 +00:00
}
2016-05-28 13:30:54 +00:00
}
// Properties from object (but not on definition)
if ( obj | | ! info_proplist - > GetDef ( ) )
{
2016-07-13 21:18:08 +00:00
C4PropList * info_editorprops = info_proplist - > GetPropertyPropList ( P_EditorProps ) ;
if ( info_editorprops )
2016-04-17 02:02:24 +00:00
{
2016-08-27 05:12:08 +00:00
QString name = info_proplist - > GetName ( ) ;
2016-11-11 02:43:03 +00:00
if ( AddPropertyGroup ( info_editorprops , num_groups , name , target_proplist , target_path , base_obj , default_selection , default_selection_index ) )
2016-05-28 13:30:54 +00:00
+ + num_groups ;
2016-06-03 05:20:43 +00:00
// Assign group for default selection
if ( * default_selection_index > = 0 )
{
* default_selection_group = num_groups - 1 ;
default_selection = nullptr ; // don't find any other instances
}
2016-04-09 18:20:31 +00:00
}
}
2016-05-28 13:30:54 +00:00
// properties from global list for objects
if ( obj )
2016-04-09 18:20:31 +00:00
{
2016-05-28 13:30:54 +00:00
C4Def * editor_base = C4Id2Def ( C4ID : : EditorBase ) ;
2016-10-23 16:47:11 +00:00
C4PropList * info_editorprops = nullptr ;
if ( editor_base & & ( info_editorprops = editor_base - > GetPropertyPropList ( P_EditorProps ) ) )
2016-07-13 21:18:08 +00:00
{
2016-11-11 02:43:03 +00:00
if ( AddPropertyGroup ( info_editorprops , num_groups , LoadResStr ( " IDS_CNS_OBJECT " ) , target_proplist , target_path , base_obj , nullptr , nullptr ) )
2016-05-28 13:30:54 +00:00
+ + num_groups ;
2016-07-13 21:18:08 +00:00
}
2016-04-04 03:46:58 +00:00
}
}
2016-05-28 13:30:54 +00:00
// Always: Internal properties
auto new_properties = target_proplist - > GetSortedLocalProperties ( ) ;
if ( property_groups . size ( ) = = num_groups ) property_groups . resize ( num_groups + 1 ) ;
PropertyGroup & internal_properties = property_groups [ num_groups ] ;
internal_properties . name = LoadResStr ( " IDS_CNS_INTERNAL " ) ;
internal_properties . props . resize ( new_properties . size ( ) ) ;
for ( int32_t i = 0 ; i < new_properties . size ( ) ; + + i )
{
internal_properties . props [ i ] . parent_value = this - > target_value ;
internal_properties . props [ i ] . property_path = target_path ;
internal_properties . props [ i ] . key = new_properties [ i ] ;
internal_properties . props [ i ] . display_name = new_properties [ i ] ;
2016-07-25 03:00:24 +00:00
internal_properties . props [ i ] . help_text = nullptr ;
2016-08-02 05:06:24 +00:00
internal_properties . props [ i ] . priority = 0 ;
2016-05-28 13:30:54 +00:00
internal_properties . props [ i ] . delegate_info . Set0 ( ) ; // default C4Value delegate
2016-11-02 23:58:02 +00:00
internal_properties . props [ i ] . delegate = nullptr ; // init when needed
2016-05-28 13:30:54 +00:00
internal_properties . props [ i ] . group_idx = num_groups ;
2016-11-03 01:27:53 +00:00
internal_properties . props [ i ] . shape = nullptr ;
internal_properties . props [ i ] . shape_property_path . Clear ( ) ;
2016-05-28 13:30:54 +00:00
internal_properties . props [ i ] . shape_delegate = nullptr ;
2016-10-14 14:49:14 +00:00
internal_properties . props [ i ] . about_to_edit = false ;
2016-05-28 13:30:54 +00:00
}
+ + num_groups ;
2016-10-14 14:49:14 +00:00
// Effects
// Add after internal because the gorup may be added/removed quickly
if ( obj )
{
// Object: Show object effects
if ( AddEffectGroup ( num_groups , obj ) )
{
+ + num_groups ;
}
}
2016-10-15 14:06:23 +00:00
else if ( target_proplist = = & : : ScriptEngine )
2016-10-14 14:49:14 +00:00
{
// Global object: Show global effects
if ( AddEffectGroup ( num_groups , nullptr ) )
{
+ + num_groups ;
}
}
2016-05-28 13:30:54 +00:00
return num_groups ;
}
2016-06-03 05:20:43 +00:00
int32_t C4ConsoleQtPropListModel : : UpdateValueArray ( C4ValueArray * target_array , int32_t * default_selection_group , int32_t * default_selection_index )
2016-05-28 13:30:54 +00:00
{
2016-06-08 04:37:48 +00:00
if ( property_groups . empty ( ) )
{
layout_valid = false ;
property_groups . resize ( 1 ) ;
}
2016-07-13 21:18:08 +00:00
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
C4Value elements_delegate_value ;
if ( info_proplist ) info_proplist - > GetProperty ( P_Elements , & elements_delegate_value ) ;
property_groups [ 0 ] . name = ( info_proplist ? info_proplist - > GetName ( ) : LoadResStr ( " IDS_CNS_ARRAYEDIT " ) ) ;
2016-05-28 13:30:54 +00:00
PropertyGroup & properties = property_groups [ 0 ] ;
2016-06-08 04:37:48 +00:00
if ( properties . props . size ( ) ! = target_array - > GetSize ( ) )
{
layout_valid = false ;
properties . props . resize ( target_array - > GetSize ( ) ) ;
}
2016-07-13 21:18:08 +00:00
C4PropertyDelegate * item_delegate = delegate_factory - > GetDelegateByValue ( elements_delegate_value ) ;
2016-05-28 13:30:54 +00:00
for ( int32_t i = 0 ; i < properties . props . size ( ) ; + + i )
{
Property & prop = properties . props [ i ] ;
prop . property_path = C4PropertyPath ( target_path , i ) ;
prop . parent_value = target_value ;
prop . display_name = : : Strings . RegString ( FormatString ( " %d " , ( int ) i ) . getData ( ) ) ;
2016-07-25 03:00:24 +00:00
prop . help_text = nullptr ;
2016-05-28 13:30:54 +00:00
prop . key = nullptr ;
2016-08-02 05:06:24 +00:00
prop . priority = 0 ;
2016-07-13 21:18:08 +00:00
prop . delegate_info = elements_delegate_value ;
2016-05-28 13:30:54 +00:00
prop . delegate = item_delegate ;
prop . about_to_edit = false ;
prop . group_idx = 0 ;
2016-11-03 01:27:53 +00:00
prop . shape = nullptr ; // array elements cannot have shapes
prop . shape_property_path . Clear ( ) ;
2016-05-28 13:30:54 +00:00
prop . shape_delegate = nullptr ;
}
return 1 ; // one group for the values
2016-02-12 04:37:13 +00:00
}
2017-02-26 15:45:38 +00:00
void C4ConsoleQtPropListModel : : DoOnUpdateCall ( const C4PropertyPath & updated_path , const C4PropertyDelegate * delegate )
{
// If delegate has its own update clalback, perform that on the root
const char * update_callback = delegate - > GetUpdateCallback ( ) ;
if ( update_callback )
{
: : Console . EditCursor . EMControl ( CID_Script , new C4ControlScript ( FormatString ( " %s->%s(%s) " , updated_path . GetRoot ( ) , update_callback , updated_path . GetGetPath ( ) ) . getData ( ) , 0 , false ) ) ;
}
// Do general object property update control
C4PropList * base_proplist = this - > base_proplist . getPropList ( ) ;
C4Value q ;
if ( base_proplist & & base_proplist - > GetProperty ( P_EditorPropertyChanged , & q ) )
{
2017-05-03 18:28:00 +00:00
: : Console . EditCursor . EMControl ( CID_Script , new C4ControlScript ( FormatString ( R " (%s->%s( " % s " )) " , updated_path . GetRoot ( ) , : : Strings . P [ P_EditorPropertyChanged ] . GetCStr ( ) , updated_path . GetGetPath ( ) ) . getData ( ) , 0 , false ) ) ;
2017-02-26 15:45:38 +00:00
}
}
2016-07-06 02:53:41 +00:00
C4ConsoleQtPropListModel : : Property * C4ConsoleQtPropListModel : : GetPropByIndex ( const QModelIndex & index ) const
{
if ( ! index . isValid ( ) ) return nullptr ;
// Resolve group and row
int32_t group_index = index . internalId ( ) , row = index . row ( ) ;
// Prop list access: Properties are on 2nd level
if ( ! group_index ) return nullptr ;
- - group_index ;
if ( group_index > = property_groups . size ( ) ) return nullptr ;
if ( row < 0 | | row > = property_groups [ group_index ] . props . size ( ) ) return nullptr ;
return const_cast < Property * > ( & property_groups [ group_index ] . props [ row ] ) ;
}
2016-02-12 04:37:13 +00:00
int C4ConsoleQtPropListModel : : rowCount ( const QModelIndex & parent ) const
{
2016-05-28 13:30:54 +00:00
QModelIndex grandparent ;
2016-07-06 02:53:41 +00:00
// Top level: Property groups
if ( ! parent . isValid ( ) )
2016-04-09 18:20:31 +00:00
{
2016-07-06 02:53:41 +00:00
return property_groups . size ( ) ;
2016-04-09 18:20:31 +00:00
}
2016-07-06 02:53:41 +00:00
// Mid level: Descend into property lists
grandparent = parent . parent ( ) ;
if ( ! grandparent . isValid ( ) )
{
if ( parent . row ( ) > = 0 & & parent . row ( ) < property_groups . size ( ) )
return property_groups [ parent . row ( ) ] . props . size ( ) ;
}
return 0 ; // no 3rd level depth
2016-02-12 04:37:13 +00:00
}
int C4ConsoleQtPropListModel : : columnCount ( const QModelIndex & parent ) const
{
2016-05-28 13:30:54 +00:00
return 2 ; // Name + Data (or Index + Data)
2016-02-12 04:37:13 +00:00
}
QVariant C4ConsoleQtPropListModel : : headerData ( int section , Qt : : Orientation orientation , int role ) const
{
// Table headers
if ( role = = Qt : : DisplayRole & & orientation = = Qt : : Orientation : : Horizontal )
{
2016-05-28 13:30:54 +00:00
if ( section = = 0 )
if ( target_value . GetType ( ) = = C4V_Array )
return QVariant ( LoadResStr ( " IDS_CNS_INDEXSHORT " ) ) ;
else
return QVariant ( LoadResStr ( " IDS_CTL_NAME " ) ) ;
2016-04-09 18:20:31 +00:00
if ( section = = 1 ) return QVariant ( LoadResStr ( " IDS_CNS_VALUE " ) ) ;
2016-02-12 04:37:13 +00:00
}
return QVariant ( ) ;
}
QVariant C4ConsoleQtPropListModel : : data ( const QModelIndex & index , int role ) const
{
2016-07-06 02:53:41 +00:00
// Headers
int32_t group_index = index . internalId ( ) ;
if ( ! group_index )
2016-05-28 13:30:54 +00:00
{
2016-07-06 02:53:41 +00:00
if ( ! index . column ( ) )
2016-04-09 18:20:31 +00:00
{
2016-07-06 02:53:41 +00:00
if ( role = = Qt : : DisplayRole )
2016-04-09 18:20:31 +00:00
{
2016-07-06 02:53:41 +00:00
if ( index . row ( ) > = 0 & & index . row ( ) < property_groups . size ( ) )
return property_groups [ index . row ( ) ] . name ;
}
else if ( role = = Qt : : FontRole )
{
return header_font ;
2016-04-09 18:20:31 +00:00
}
}
2016-07-06 02:53:41 +00:00
return QVariant ( ) ;
2016-04-09 18:20:31 +00:00
}
// Query latest data from prop list
2016-07-06 02:53:41 +00:00
Property * prop = GetPropByIndex ( index ) ;
2016-04-17 02:02:24 +00:00
if ( ! prop ) return QVariant ( ) ;
if ( ! prop - > delegate ) prop - > delegate = delegate_factory - > GetDelegateByValue ( prop - > delegate_info ) ;
2016-04-09 18:20:31 +00:00
if ( role = = Qt : : DisplayRole )
2016-02-12 04:37:13 +00:00
{
switch ( index . column ( ) )
{
case 0 : // First col: Property Name
2016-04-17 02:02:24 +00:00
return QVariant ( prop - > display_name - > GetCStr ( ) ) ;
2016-02-12 04:37:13 +00:00
case 1 : // Second col: Property value
{
C4Value v ;
2016-07-14 02:02:18 +00:00
prop - > delegate - > GetPropertyValue ( prop - > parent_value , prop - > key , index . row ( ) , & v ) ;
2016-08-27 14:12:17 +00:00
return QVariant ( prop - > delegate - > GetDisplayString ( v , target_value . getObj ( ) , true ) ) ;
2016-02-12 04:37:13 +00:00
}
}
}
2016-04-17 02:02:24 +00:00
else if ( role = = Qt : : BackgroundColorRole & & index . column ( ) = = 1 )
{
C4Value v ;
2016-07-14 02:02:18 +00:00
prop - > delegate - > GetPropertyValue ( prop - > parent_value , prop - > key , index . row ( ) , & v ) ;
2016-05-28 13:30:54 +00:00
QColor bgclr = prop - > delegate - > GetDisplayBackgroundColor ( v , target_value . getObj ( ) ) ;
2016-04-17 02:02:24 +00:00
if ( bgclr . isValid ( ) ) return bgclr ;
}
else if ( role = = Qt : : TextColorRole & & index . column ( ) = = 1 )
{
C4Value v ;
2016-07-14 02:02:18 +00:00
prop - > delegate - > GetPropertyValue ( prop - > parent_value , prop - > key , index . row ( ) , & v ) ;
2016-05-28 13:30:54 +00:00
QColor txtclr = prop - > delegate - > GetDisplayTextColor ( v , target_value . getObj ( ) ) ;
2016-04-17 02:02:24 +00:00
if ( txtclr . isValid ( ) ) return txtclr ;
}
2016-07-26 01:39:35 +00:00
else if ( role = = Qt : : DecorationRole & & index . column ( ) = = 0 & & prop - > help_text & & Config . Developer . ShowHelp )
2016-07-25 03:00:24 +00:00
{
// Help icons in left column
return QIcon ( " :/editor/res/Help.png " ) ;
}
2016-08-02 05:06:24 +00:00
else if ( role = = Qt : : FontRole & & index . column ( ) = = 0 )
{
if ( prop - > priority > = 100 ) return important_property_font ;
}
2016-07-25 03:00:24 +00:00
else if ( role = = Qt : : ToolTipRole & & index . column ( ) = = 0 )
{
// Tooltip from property description. Default to display name in case it got truncated.
if ( prop - > help_text )
return QString ( prop - > help_text - > GetCStr ( ) ) ;
else
return QString ( prop - > display_name - > GetCStr ( ) ) ;
}
2016-02-12 04:37:13 +00:00
// Nothing to show
return QVariant ( ) ;
}
2016-04-04 03:46:58 +00:00
QModelIndex C4ConsoleQtPropListModel : : index ( int row , int column , const QModelIndex & parent ) const
{
if ( column < 0 | | column > 1 ) return QModelIndex ( ) ;
2016-07-06 02:53:41 +00:00
// Top level index?
if ( ! parent . isValid ( ) )
2016-04-09 18:20:31 +00:00
{
2016-07-06 02:53:41 +00:00
// Top level has headers only
if ( row < 0 | | row > = property_groups . size ( ) ) return QModelIndex ( ) ;
return createIndex ( row , column , ( quintptr ) 0u ) ;
2016-05-28 13:30:54 +00:00
}
2016-07-06 02:53:41 +00:00
if ( parent . internalId ( ) ) return QModelIndex ( ) ; // No 3rd level depth
// Validate range of property
2016-11-02 23:58:02 +00:00
const PropertyGroup * property_group = nullptr ;
2016-07-06 02:53:41 +00:00
if ( parent . row ( ) > = 0 & & parent . row ( ) < property_groups . size ( ) )
2016-05-28 13:30:54 +00:00
{
2016-07-06 02:53:41 +00:00
property_group = & property_groups [ parent . row ( ) ] ;
2016-05-28 13:30:54 +00:00
if ( row < 0 | | row > = property_group - > props . size ( ) ) return QModelIndex ( ) ;
2016-07-06 02:53:41 +00:00
return createIndex ( row , column , ( quintptr ) parent . row ( ) + 1 ) ;
2016-04-09 18:20:31 +00:00
}
2016-07-06 02:53:41 +00:00
return QModelIndex ( ) ;
2016-04-04 03:46:58 +00:00
}
QModelIndex C4ConsoleQtPropListModel : : parent ( const QModelIndex & index ) const
{
2016-07-06 02:53:41 +00:00
// Parent: Stored in internal ID
auto parent_idx = index . internalId ( ) ;
if ( parent_idx ) return createIndex ( parent_idx - 1 , 0 , ( quintptr ) 0u ) ;
2016-05-28 13:30:54 +00:00
return QModelIndex ( ) ;
2016-04-04 03:46:58 +00:00
}
Qt : : ItemFlags C4ConsoleQtPropListModel : : flags ( const QModelIndex & index ) const
{
2016-06-03 05:20:43 +00:00
Qt : : ItemFlags flags = QAbstractItemModel : : flags ( index ) | Qt : : ItemIsDropEnabled ;
2016-07-06 02:53:41 +00:00
Property * prop = GetPropByIndex ( index ) ;
if ( index . isValid ( ) & & prop )
2016-04-04 03:46:58 +00:00
{
2016-06-03 05:20:43 +00:00
flags & = ~ Qt : : ItemIsDropEnabled ; // only drop between the lines
if ( index . column ( ) = = 0 )
{
// array elements can be re-arranged
if ( prop - > parent_value . GetType ( ) = = C4V_Array ) flags | = Qt : : ItemIsDragEnabled ;
}
else if ( index . column ( ) = = 1 )
{
2016-10-14 14:49:14 +00:00
// Disallow editing on readonly target (e.g. frozen proplist).
// But always allow editing of effects.
bool readonly = IsTargetReadonly ( ) & & prop - > delegate ! = delegate_factory - > GetEffectDelegate ( ) ;
2016-06-03 05:20:43 +00:00
if ( ! readonly )
flags | = Qt : : ItemIsEditable ;
else
flags & = ~ Qt : : ItemIsEnabled ;
}
2016-04-04 03:46:58 +00:00
}
return flags ;
2016-04-04 23:34:02 +00:00
}
2016-06-03 05:20:43 +00:00
Qt : : DropActions C4ConsoleQtPropListModel : : supportedDropActions ( ) const
{
return Qt : : MoveAction ;
}
bool C4ConsoleQtPropListModel : : dropMimeData ( const QMimeData * data , Qt : : DropAction action , int row , int column , const QModelIndex & parent )
{
// Drag+Drop movement on array only
if ( action ! = Qt : : MoveAction ) return false ;
C4ValueArray * arr = target_value . getArray ( ) ;
if ( ! arr ) return false ;
if ( ! data - > hasFormat ( " application/vnd.text " ) ) return false ;
if ( row < 0 ) return false ; // outside range: Could be above or below. Better don't drag at all.
2016-07-06 02:53:41 +00:00
if ( ! parent . isValid ( ) ) return false ; // in array only
2016-06-03 05:20:43 +00:00
// Decode indices of rows to move
QByteArray encodedData = data - > data ( " application/vnd.text " ) ;
StdStrBuf rearrange_call ;
rearrange_call . Format ( " MoveArrayItems(%%s, [%s], %d) " , encodedData . constData ( ) , row ) ;
target_path . DoCall ( rearrange_call . getData ( ) ) ;
return true ;
}
QStringList C4ConsoleQtPropListModel : : mimeTypes ( ) const
{
QStringList types ;
types < < " application/vnd.text " ;
return types ;
}
QMimeData * C4ConsoleQtPropListModel : : mimeData ( const QModelIndexList & indexes ) const
{
// Add all moved indexes
QMimeData * mimeData = new QMimeData ( ) ;
QByteArray encodedData ;
int32_t count = 0 ;
for ( const QModelIndex & index : indexes )
{
2016-07-06 02:53:41 +00:00
if ( index . isValid ( ) & & index . internalId ( ) )
2016-06-03 05:20:43 +00:00
{
if ( count ) encodedData . append ( " , " ) ;
encodedData . append ( QString : : number ( index . row ( ) ) ) ;
+ + count ;
}
}
mimeData - > setData ( " application/vnd.text " , encodedData ) ;
return mimeData ;
2016-06-06 05:54:05 +00:00
}
2016-10-10 20:27:43 +00:00
QString C4ConsoleQtPropListModel : : GetTargetPathHelp ( ) const
2016-07-24 04:25:46 +00:00
{
// Help text in EditorInfo prop. Fall back to description.
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
2016-10-10 20:27:43 +00:00
if ( ! info_proplist ) return QString ( ) ;
2016-07-25 03:10:43 +00:00
C4String * desc = info_proplist - > GetPropertyStr ( P_EditorHelp ) ;
2016-07-24 04:25:46 +00:00
if ( ! desc ) desc = info_proplist - > GetPropertyStr ( P_Description ) ;
2016-10-10 20:27:43 +00:00
if ( ! desc ) return QString ( ) ;
QString result = QString ( desc - > GetCStr ( ) ) ;
result = result . replace ( ' | ' , ' \n ' ) ;
return result ;
2016-07-24 04:25:46 +00:00
}
2016-07-28 02:35:11 +00:00
const char * C4ConsoleQtPropListModel : : GetTargetPathName ( ) const
{
// Name prop of current info.
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
if ( ! info_proplist ) return nullptr ;
C4String * name = info_proplist - > GetPropertyStr ( P_Name ) ;
return name ? name - > GetCStr ( ) : nullptr ;
}
2016-06-06 05:54:05 +00:00
void C4ConsoleQtPropListModel : : AddArrayElement ( )
{
C4Value new_val ;
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
2016-09-04 06:14:30 +00:00
C4PropListStatic * info_proplist_static = nullptr ;
if ( info_proplist )
{
info_proplist - > GetProperty ( P_DefaultValue , & new_val ) ;
info_proplist_static = info_proplist - > IsStatic ( ) ;
}
target_path . DoCall ( FormatString ( " PushBack(%%s, %s) " , new_val . GetDataString ( 10 , info_proplist_static ) . getData ( ) ) . getData ( ) ) ;
2016-06-06 05:54:05 +00:00
}
void C4ConsoleQtPropListModel : : RemoveArrayElement ( )
{
// Compose script command to remove all selected array indices
StdStrBuf script ;
for ( QModelIndex idx : selection_model - > selectedIndexes ( ) )
if ( idx . isValid ( ) & & idx . column ( ) = = 0 )
if ( script . getLength ( ) )
script . AppendFormat ( " ,%d " , idx . row ( ) ) ;
else
script . AppendFormat ( " %d " , idx . row ( ) ) ;
if ( script . getLength ( ) ) target_path . DoCall ( FormatString ( " RemoveArrayIndices(%%s, [%s]) " , script . getData ( ) ) . getData ( ) ) ;
}
bool C4ConsoleQtPropListModel : : IsTargetReadonly ( ) const
{
if ( target_path . IsEmpty ( ) ) return true ;
switch ( target_value . GetType ( ) )
{
case C4V_Array :
// Arrays are never frozen
return false ;
case C4V_PropList :
{
C4PropList * parent_proplist = target_value . _getPropList ( ) ;
if ( parent_proplist - > IsFrozen ( ) ) return true ;
return false ;
}
default :
return true ;
}
}
2016-11-11 02:43:03 +00:00
class C4ConsoleQtShape * C4ConsoleQtPropListModel : : GetShapeByPropertyPath ( const char * property_path )
{
// Lookup in map
auto entry = shapes . find ( std : : string ( property_path ) ) ;
if ( entry = = shapes . end ( ) ) return nullptr ;
return entry - > second . Get ( ) ;
}