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"
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
/* Property path for property setting synchronization */
2016-07-23 05:40:51 +00:00
C4PropertyPath : : C4PropertyPath ( C4PropList * target ) : get_path_type ( PPT_Root ) , set_path_type ( PPT_Root )
2016-05-23 23:22:05 +00:00
{
// Build string to set target: supports objects only
if ( target )
{
C4Object * obj = target - > GetObject ( ) ;
2016-07-04 01:16:07 +00:00
if ( obj )
{
2016-07-23 05:40:51 +00:00
get_path . Format ( " Object(%d) " , ( int ) obj - > Number ) ;
root = get_path ;
2016-07-04 01:16:07 +00:00
}
2016-05-23 23:22:05 +00:00
}
}
2016-07-23 05:40:51 +00:00
C4PropertyPath : : C4PropertyPath ( C4Effect * fx , C4Object * target_obj ) : get_path_type ( PPT_Root ) , set_path_type ( PPT_Root )
2016-05-23 23:22:05 +00:00
{
// Effect property path: Represent as GetEffect("name", Object(%d), index)
if ( ! fx | | ! target_obj ) return ;
const char * name = fx - > GetName ( ) ;
int32_t index = 0 ;
for ( C4Effect * ofx = target_obj - > pEffects ; ofx ; ofx = ofx - > pNext )
if ( ofx = = fx ) break ; else if ( ! strcmp ( ofx - > GetName ( ) , name ) ) + + index ;
2016-07-23 05:40:51 +00:00
get_path . Format ( " GetEffect( \" %s \" , Object(%d), %d) " , name , ( int ) target_obj - > Number , ( int ) index ) ;
2016-07-04 01:16:07 +00:00
root . Format ( " Object(%d) " , ( int ) target_obj - > Number ) ;
2016-05-23 23:22:05 +00:00
}
2016-07-04 01:16:07 +00:00
C4PropertyPath : : C4PropertyPath ( const C4PropertyPath & parent , int32_t elem_index ) : root ( parent . root )
2016-04-04 03:46:58 +00:00
{
2016-07-23 05:40:51 +00:00
get_path . Format ( " %s[%d] " , parent . GetGetPath ( ) , ( int ) elem_index ) ;
get_path_type = set_path_type = PPT_Index ;
2016-04-04 03:46:58 +00:00
}
2016-07-23 05:40:51 +00:00
C4PropertyPath : : C4PropertyPath ( const C4PropertyPath & parent , const char * child_property )
: get_path_type ( PPT_Property ) , set_path_type ( PPT_Property ) , root ( parent . root )
2016-04-04 03:46:58 +00:00
{
2016-07-23 05:40:51 +00:00
get_path . Format ( " %s.%s " , parent . GetGetPath ( ) , child_property ) ;
}
void C4PropertyPath : : SetSetPath ( const C4PropertyPath & parent , const char * child_property , C4PropertyPath : : PathType path_type )
{
set_path_type = path_type ;
2016-04-04 03:46:58 +00:00
if ( path_type = = PPT_Property )
2016-07-23 05:40:51 +00:00
set_path . Format ( " %s.%s " , parent . GetGetPath ( ) , child_property ) ;
2016-04-04 03:46:58 +00:00
else if ( path_type = = PPT_SetFunction )
2016-07-23 05:40:51 +00:00
set_path . Format ( " %s->%s " , parent . GetGetPath ( ) , child_property ) ;
2016-05-23 23:22:05 +00:00
else if ( path_type = = PPT_GlobalSetFunction )
{
2016-07-23 05:40:51 +00:00
set_path . Copy ( parent . GetGetPath ( ) ) ;
2016-05-23 23:22:05 +00:00
argument . Copy ( child_property ) ;
}
2016-07-21 04:20:44 +00:00
else if ( path_type = = PPT_RootSetFunction )
{
2016-07-23 05:40:51 +00:00
set_path . Format ( " %s->%s " , parent . GetRoot ( ) , child_property ) ;
2016-07-21 04:20:44 +00:00
}
2016-04-04 03:46:58 +00:00
else
{
assert ( false ) ;
}
}
void C4PropertyPath : : SetProperty ( const char * set_string ) const
{
// Compose script to update property
2016-07-23 05:40:51 +00:00
const char * set_path_c = GetSetPath ( ) ;
2016-04-04 03:46:58 +00:00
StdStrBuf script ;
2016-07-23 05:40:51 +00:00
if ( set_path_type = = PPT_SetFunction | | set_path_type = = PPT_RootSetFunction )
script . Format ( " %s(%s) " , set_path_c , set_string ) ;
else if ( set_path_type = = PPT_GlobalSetFunction )
script . Format ( " %s(%s,%s) " , argument . getData ( ) , set_path_c , set_string ) ;
2016-05-23 23:22:05 +00:00
else
2016-07-23 05:40:51 +00:00
script . Format ( " %s=%s " , set_path_c , set_string ) ;
2016-04-04 03:46:58 +00:00
// Execute synced scripted
2016-05-23 23:22:05 +00:00
: : Console . EditCursor . EMControl ( CID_Script , new C4ControlScript ( script . getData ( ) , 0 , false ) ) ;
2016-04-04 03:46:58 +00:00
}
void C4PropertyPath : : SetProperty ( const C4Value & to_val ) const
{
SetProperty ( to_val . GetDataString ( 9999999 ) . getData ( ) ) ;
}
2016-05-25 03:06:00 +00:00
C4Value C4PropertyPath : : ResolveValue ( ) const
{
2016-07-23 05:40:51 +00:00
if ( ! get_path . getLength ( ) ) return C4VNull ;
return AulExec . DirectExec ( : : ScriptEngine . GetPropList ( ) , get_path . getData ( ) , " resolve property " , false , nullptr ) ;
2016-05-25 03:06:00 +00:00
}
2016-06-03 05:20:43 +00:00
void C4PropertyPath : : DoCall ( const char * call_string ) const
{
// Compose script call
StdStrBuf script ;
2016-07-23 05:40:51 +00:00
script . Format ( call_string , get_path . getData ( ) ) ;
2016-06-03 05:20:43 +00:00
// Execute synced scripted
: : Console . EditCursor . EMControl ( CID_Script , new C4ControlScript ( script . getData ( ) , 0 , false ) ) ;
}
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 ) ;
}
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 ( ) ) ) ;
}
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 ) ;
} ) ;
return editor ;
}
2016-07-28 02:35:11 +00:00
/* String delegate */
2016-06-16 04:40:25 +00:00
C4PropertyDelegateString : : C4PropertyDelegateString ( const C4PropertyDelegateFactory * factory , C4PropList * props )
: C4PropertyDelegate ( factory , props )
{
}
void C4PropertyDelegateString : : SetEditorData ( QWidget * editor , const C4Value & val , const C4PropertyPath & property_path ) const
{
Editor * line_edit = static_cast < Editor * > ( editor ) ;
C4String * s = val . getStr ( ) ;
line_edit - > setText ( QString ( s ? s - > GetCStr ( ) : " " ) ) ;
}
2016-07-18 23:24:53 +00:00
void C4PropertyDelegateString : : SetModelData ( QObject * editor , const C4PropertyPath & property_path , C4ConsoleQtShape * prop_shape ) const
2016-06-16 04:40:25 +00:00
{
Editor * line_edit = static_cast < Editor * > ( editor ) ;
// Only set model data when pressing Enter explicitely; not just when leaving
if ( line_edit - > commit_pending )
{
QString new_value = line_edit - > text ( ) ;
// TODO: Would be better to handle escaping in the C4Value-to-string code
new_value = new_value . replace ( " \\ " , " \\ \\ " ) . replace ( " \" " , " \\ \" " ) ;
property_path . SetProperty ( C4VString ( new_value . toUtf8 ( ) ) ) ;
line_edit - > commit_pending = false ;
}
}
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
{
Editor * editor = new Editor ( parent ) ;
// EditingDone only on Return; not just when leaving edit field
connect ( editor , & QLineEdit : : returnPressed , editor , [ this , editor ] ( ) {
editor - > commit_pending = true ;
emit EditingDoneSignal ( editor ) ;
} ) ;
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 ""
C4String * s = v . getStr ( ) ;
return QString ( s ? s - > GetCStr ( ) : " " ) ;
}
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-07-30 18:50:07 +00:00
QString C4PropertyDelegateArray : : GetDisplayString ( const C4Value & v , C4Object * obj , bool short_names ) const
2016-06-06 05:54:05 +00:00
{
C4ValueArray * arr = v . getArray ( ) ;
if ( ! arr ) return QString ( LoadResStr ( " IDS_CNS_INVALID " ) ) ;
int32_t n = v . _getArray ( ) - > GetSize ( ) ;
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-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-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-06-03 05:20:43 +00:00
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 )
: C4PropertyDelegate ( factory , props )
{
}
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 ) ;
}
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-04-25 20:17:10 +00:00
QColor clr = QColorDialog : : getColor ( QColor ( editor - > last_value . getInt ( ) ) , editor , QString ( ) , QColorDialog : : ShowAlphaChannel ) ;
2016-04-17 02:02:24 +00:00
editor - > last_value . SetInt ( clr . rgba ( ) ) ;
2016-05-25 03:06:00 +00:00
this - > SetEditorData ( editor , editor - > last_value , C4PropertyPath ( ) ) ; // force update on display
2016-04-17 02:02:24 +00:00
emit EditingDoneSignal ( editor ) ;
} ) ;
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-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 )
: QComboBox ( parent ) , last_popup_height ( 0 ) , is_next_close_blocked ( false ) , editable ( editable )
2016-06-15 04:15:08 +00:00
{
2016-07-28 02:35:11 +00:00
item_delegate . reset ( new 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 )
{
// 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 )
{
connect ( lineEdit ( ) , & QLineEdit : : returnPressed , [ this ] ( )
{
emit TextChanged ( this - > lineEdit ( ) - > text ( ) ) ;
} ) ;
}
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-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-07-28 04:24:28 +00:00
: C4PropertyDelegate ( factory , props ) , allow_editing ( 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-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 ;
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-07-04 01:16:07 +00:00
props - > GetProperty ( P_Get , & 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 ) ;
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 )
{
QStandardItem * parent = model - > invisibleRootItem ( ) ;
if ( opt . group )
{
QStringList group_names = QString ( opt . group - > GetCStr ( ) ) . split ( QString ( " / " ) ) ;
for ( const QString & group_name : group_names )
{
int row_index = - 1 ;
for ( int check_row_index = 0 ; check_row_index < parent - > rowCount ( ) ; + + check_row_index )
if ( parent - > child ( check_row_index , 0 ) - > text ( ) = = group_name )
{
row_index = check_row_index ;
parent = parent - > child ( check_row_index , 0 ) ;
break ;
}
if ( row_index < 0 )
{
QStandardItem * new_group = new QStandardItem ( group_name ) ;
parent - > appendRow ( new_group ) ;
parent = new_group ;
}
}
}
QStandardItem * new_item = new QStandardItem ( QString ( opt . name - > GetCStr ( ) ) ) ;
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-07-25 05:05:15 +00:00
new_item - > setData ( QString ( ( opt . help ? opt . help : opt . name ) - > GetCStr ( ) ) , Qt : : ToolTipRole ) ;
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
parent - > appendRow ( new_item ) ;
+ + idx ;
}
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 ;
}
// If no option matches, just pick first
2016-04-17 02:02:24 +00:00
return match ? iopt : - 1 ;
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 ( ) ;
editor - > parameter_widget = NULL ;
}
2016-07-18 23:24:53 +00:00
editor - > paint_parameter_delegate = nullptr ;
2016-06-20 23:48:02 +00:00
int32_t idx = editor - > option_box - > GetCurrentSelectionIndex ( ) ;
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 )
{
// Showing current selection: From last_val assigned in SetEditorData
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)
SetOptionValue ( editor - > last_get_path , option ) ;
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-07-13 02:29:24 +00:00
C4PropList * props = editor - > last_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-04-17 02:02:24 +00:00
int32_t index = std : : max < int32_t > ( GetOptionByValue ( val ) , 0 ) ;
2016-06-15 04:15:08 +00:00
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-06-15 04:15:08 +00:00
QStandardItemModel * model = static_cast < QStandardItemModel * > ( editor - > option_box - > model ( ) ) ;
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-04-04 03:46:58 +00:00
if ( idx < 0 | | idx > = options . size ( ) ) return ;
const Option & option = options [ idx ] ;
// Store directly in value or in a proplist field?
C4PropertyPath use_path ;
if ( option . value_key . Get ( ) )
use_path = C4PropertyPath ( property_path , option . value_key - > GetCStr ( ) ) ;
else
use_path = property_path ;
// Value from a parameter or directly from the enum?
if ( option . adelegate )
{
2016-07-18 23:24:53 +00:00
// Default value on enum change (on main path; not use_path because the default value is always givne as the whole proplist)
if ( editor - > option_changed ) SetOptionValue ( property_path , option ) ;
2016-04-09 18:20:31 +00:00
// Value from a parameter.
// Using a setter function?
2016-07-23 05:40:51 +00:00
use_path = option . adelegate - > GetPathForProperty ( use_path , nullptr ) ;
2016-07-18 23:24:53 +00:00
option . adelegate - > SetModelData ( editor - > parameter_widget , use_path , prop_shape ) ;
2016-04-04 03:46:58 +00:00
}
else
{
// No parameter. Use value.
2016-07-04 01:16:07 +00:00
SetOptionValue ( use_path , option ) ;
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-07-04 01:16:07 +00:00
void C4PropertyDelegateEnum : : SetOptionValue ( const C4PropertyPath & use_path , const C4PropertyDelegateEnum : : Option & option ) const
{
// After an enum entry has been selected, set its value
// Either directly by value or through a function
if ( option . value_function . GetType ( ) = = C4V_Function )
{
use_path . SetProperty ( FormatString ( " Call(%s, %s, %s) " , option . value_function . GetDataString ( ) . getData ( ) , use_path . GetRoot ( ) , option . value . GetDataString ( 20 ) . getData ( ) ) . getData ( ) ) ;
}
else
{
use_path . SetProperty ( option . value ) ;
}
}
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-20 23:48:02 +00:00
connect ( editor - > option_box , & C4DeepQComboBox : : NewItemSelected , editor , [ editor , this ] ( int32_t newval ) {
2016-04-04 03:46:58 +00:00
if ( ! editor - > updating ) this - > UpdateOptionIndex ( editor , newval ) ; } ) ;
editor - > updating = false ;
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-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 ;
}
void C4PropertyDelegateEnum : : UpdateOptionIndex ( C4PropertyDelegateEnum : : Editor * editor , int newval ) const
{
2016-07-13 03:28:31 +00:00
// Update value and parameter delegate if selection changed
if ( newval ! = editor - > last_selection_index )
{
editor - > option_changed = true ;
editor - > last_selection_index = newval ;
UpdateEditorParameter ( editor , true ) ;
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 ) ;
if ( idx < 0 )
{
// Value not found: Default display
2016-07-30 18:50:07 +00:00
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 ( ) )
{
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 ) ;
if ( option_idx < 0 ) return nullptr ;
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-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 ) ;
if ( option_idx < 0 ) return false ;
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 ( ) )
{
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-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-06-16 01:57:59 +00:00
C4String * filter_property = props ? props - > GetPropertyStr ( P_Filter ) : nullptr ;
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-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-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 ( ) ) ;
}
}
/* 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-05-28 13:30:54 +00:00
bool success = GetPropertyValueBase ( 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-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 )
: QWidget ( parent ) , layout ( NULL ) , edit ( NULL ) , extended_button ( NULL ) , commit_pending ( false )
{
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 ( ) ) ;
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-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 )
: C4PropertyDelegate ( factory , props ) , clr ( 0xffff0000 ) , can_move_center ( false )
{
if ( props )
{
shape_type = props - > GetPropertyStr ( P_Type ) ;
clr = props - > GetPropertyInt ( P_Color ) | 0xff000000 ;
can_move_center = props - > GetPropertyBool ( P_CanMoveCenter ) ;
}
}
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-07-23 05:40:51 +00:00
if ( prop_shape & & prop_shape - > GetParentDelegate ( ) = = this ) property_path . SetProperty ( prop_shape - > GetValue ( ) ) ;
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 ) ;
}
if ( shape_type & & shape_type - > GetData ( ) = = " point " )
{
QPoint ctr = inner_rect . center ( ) ;
int r = inner_rect . height ( ) * 7 / 20 ;
painter - > drawLine ( ctr + QPoint ( - r , - r ) , ctr + QPoint ( + r , + r ) ) ;
painter - > drawLine ( ctr + QPoint ( + r , - r ) , ctr + QPoint ( - r , + r ) ) ;
painter - > drawEllipse ( inner_rect ) ;
}
else if ( shape_type & & shape_type - > GetData ( ) = = " circle " )
2016-04-25 20:17:10 +00:00
{
painter - > drawEllipse ( inner_rect ) ;
if ( can_move_center ) painter - > drawPoint ( inner_rect . center ( ) ) ;
}
else
{
painter - > drawRect ( inner_rect ) ;
}
painter - > restore ( ) ;
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
/* Delegate factory: Create delegates based on the C4Value type */
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-07-30 05:05:12 +00:00
if ( str - > GetData ( ) = = " rect " | | str - > GetData ( ) = = " circle " | | str - > GetData ( ) = = " point " ) return new C4PropertyDelegateShape ( 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-04-04 03:46:58 +00:00
if ( ! prop ) return NULL ;
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
2016-07-18 23:24:53 +00:00
d - > SetModelData ( editor , d - > GetPathForProperty ( editor_prop ) , editor_prop - > shape . Get ( ) ) ;
2016-04-04 03:46:58 +00:00
}
QWidget * C4PropertyDelegateFactory : : createEditor ( QWidget * parent , const QStyleOptionViewItem & option , const QModelIndex & index ) const
{
C4PropertyDelegate * d = GetDelegateByIndex ( index ) ;
if ( ! d ) return NULL ;
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-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-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
}
C4ConsoleQtPropListModel : : ~ C4ConsoleQtPropListModel ( )
{
}
2016-07-30 05:05:12 +00:00
bool C4ConsoleQtPropListModel : : AddPropertyGroup ( C4PropList * add_proplist , int32_t group_index , QString name , C4PropList * target_proplist , C4Object * base_object , C4String * default_selection , int32_t * default_selection_index )
2016-04-17 02:02:24 +00:00
{
2016-07-13 21:18:08 +00:00
auto new_properties = add_proplist - > GetSortedLocalProperties ( false ) ;
2016-04-17 02:02:24 +00:00
if ( ! new_properties . size ( ) ) return false ;
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 ( ) ;
properties . name = name ;
2016-06-08 04:37:48 +00:00
if ( properties . props . size ( ) ! = new_properties . size ( ) )
{
layout_valid = false ;
properties . props . resize ( new_properties . size ( ) ) ;
}
2016-07-30 05:05:12 +00:00
C4Effect * fx = target_proplist - > GetEffect ( ) ;
2016-04-17 02:02:24 +00:00
for ( int32_t i = 0 ; i < new_properties . size ( ) ; + + i )
{
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-07-30 05:05:12 +00:00
if ( fx )
2016-05-23 23:22:05 +00:00
// Access to effect
2016-07-30 05:05:12 +00:00
prop - > property_path = C4PropertyPath ( fx , base_object ) ;
2016-05-23 23:22:05 +00:00
else
2016-05-25 03:06:00 +00:00
prop - > property_path = target_path ;
2016-06-03 05:20:43 +00:00
// ID for default selection memory
2016-07-13 21:18:08 +00:00
C4String * prop_id = new_properties [ i ] ;
if ( default_selection = = prop_id ) * default_selection_index = i ;
2016-05-23 23:22:05 +00:00
// Property data
2016-07-25 03:00:24 +00:00
prop - > key = nullptr ;
prop - > display_name = nullptr ;
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-04-17 02:02:24 +00:00
C4Value published_prop_val ;
add_proplist - > GetPropertyByS ( new_properties [ i ] , & published_prop_val ) ;
C4PropList * published_prop = published_prop_val . getPropList ( ) ;
if ( published_prop )
{
2016-04-25 20:17:10 +00:00
prop - > key = published_prop - > GetPropertyStr ( P_Key ) ;
prop - > display_name = published_prop - > GetPropertyStr ( P_Name ) ;
2016-07-25 03:10:43 +00:00
prop - > help_text = published_prop - > GetPropertyStr ( P_EditorHelp ) ;
2016-04-25 20:17:10 +00:00
prop - > delegate_info . SetPropList ( published_prop ) ;
}
2016-07-13 21:18:08 +00:00
if ( ! prop - > key ) properties . props [ i ] . key = prop_id ;
if ( ! prop - > display_name ) properties . props [ i ] . display_name = prop_id ;
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-07-30 05:05:12 +00:00
C4ConsoleQtShape * shape = : : Console . EditCursor . GetShapes ( ) - > CreateShape ( base_object ? base_object : target_proplist - > GetObject ( ) , new_shape_delegate - > GetCreationProps ( ) . getPropList ( ) , v , new_shape_delegate ) ;
2016-04-25 20:17:10 +00:00
C4PropertyDelegateFactory * factory = this - > delegate_factory ;
connect ( shape , & C4ConsoleQtShape : : ShapeDragged , new_shape_delegate , [ factory , new_shape_delegate , shape , prop ] ( ) {
2016-07-18 23:24:53 +00:00
new_shape_delegate - > SetModelData ( nullptr , prop - > shape_property_path , shape ) ;
2016-04-25 20:17:10 +00:00
} ) ;
prop - > shape . Set ( shape ) ;
}
2016-07-18 23:24:53 +00:00
else
{
prop - > shape . Clear ( ) ;
}
2016-04-17 02:02:24 +00:00
}
}
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
2016-07-13 21:18:08 +00:00
target_path_stack . push_back ( TargetStackEntry ( 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-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 ;
}
// 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-07-30 05:05:12 +00:00
C4Object * base_obj = this - > base_proplist . getObj ( ) ;
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-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-05-28 13:30:54 +00:00
C4Object * obj = info_proplist - > GetObject ( ) ;
// 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 ) ;
if ( effect_editorprops & & AddPropertyGroup ( effect_editorprops , num_groups , name , fx , 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-05-28 13:30:54 +00:00
QString name ;
2016-07-13 21:18:08 +00:00
C4PropListStatic * proplist_static = info_proplist - > IsStatic ( ) ;
2016-05-28 13:30:54 +00:00
if ( proplist_static )
name = QString ( proplist_static - > GetDataString ( ) . getData ( ) ) ;
else
2016-07-13 21:18:08 +00:00
name = info_proplist - > GetName ( ) ;
2016-07-30 05:05:12 +00:00
if ( AddPropertyGroup ( info_editorprops , num_groups , name , target_proplist , 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 ) ;
if ( editor_base )
2016-07-13 21:18:08 +00:00
{
C4PropList * info_editorprops = editor_base - > GetPropertyPropList ( P_EditorProps ) ;
2016-07-30 05:05:12 +00:00
if ( AddPropertyGroup ( info_editorprops , num_groups , LoadResStr ( " IDS_CNS_OBJECT " ) , target_proplist , 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-05-28 13:30:54 +00:00
internal_properties . props [ i ] . delegate_info . Set0 ( ) ; // default C4Value delegate
internal_properties . props [ i ] . delegate = NULL ; // init when needed
internal_properties . props [ i ] . group_idx = num_groups ;
internal_properties . props [ i ] . shape . Clear ( ) ;
internal_properties . props [ i ] . shape_delegate = nullptr ;
}
+ + num_groups ;
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-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 ;
prop . shape . Clear ( ) ; // array elements cannot have shape
prop . shape_delegate = nullptr ;
}
return 1 ; // one group for the values
2016-02-12 04:37:13 +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-07-30 18:50:07 +00:00
return QVariant ( prop - > delegate - > GetDisplayString ( v , target_value . getObj ( ) , false ) ) ;
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 " ) ;
}
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
const PropertyGroup * property_group = NULL ;
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-06-06 05:54:05 +00:00
bool readonly = IsTargetReadonly ( ) ;
2016-06-03 05:20:43 +00:00
// Only object properties are editable at the moment
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-07-24 04:25:46 +00:00
const char * C4ConsoleQtPropListModel : : GetTargetPathHelp ( ) const
{
// Help text in EditorInfo prop. Fall back to description.
C4PropList * info_proplist = this - > info_proplist . getPropList ( ) ;
if ( ! info_proplist ) return nullptr ;
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 ) ;
if ( ! desc ) return nullptr ;
return desc - > GetCStr ( ) ;
}
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 ( ) ;
if ( info_proplist ) info_proplist - > GetProperty ( P_DefaultValue , & new_val ) ;
target_path . DoCall ( FormatString ( " PushBack(%%s, %s) " , new_val . GetDataString ( 10 ) . getData ( ) ) . getData ( ) ) ;
}
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 ;
}
}