2017-12-13 13:45:10 +00:00
# ifndef SLIC3R_GUI_FIELD_HPP
# define SLIC3R_GUI_FIELD_HPP
# include <wx/wxprec.h>
# ifndef WX_PRECOMP
# include <wx/wx.h>
# endif
# include <memory>
# include <functional>
# include <boost/any.hpp>
2017-12-18 12:58:51 +00:00
# include <wx/spinctrl.h>
2017-12-22 10:50:28 +00:00
# include <wx/clrpicker.h>
2017-12-18 12:58:51 +00:00
2017-12-13 13:45:10 +00:00
# include "../../libslic3r/libslic3r.h"
# include "../../libslic3r/Config.hpp"
//#include "slic3r_gui.hpp"
# include "GUI.hpp"
2018-03-06 08:44:53 +00:00
# include "Utils.hpp"
2017-12-13 13:45:10 +00:00
2018-03-06 12:34:39 +00:00
# ifdef __WXMSW__
# define wxMSW true
# else
# define wxMSW false
# endif
2017-12-13 13:45:10 +00:00
namespace Slic3r { namespace GUI {
class Field ;
using t_field = std : : unique_ptr < Field > ;
2018-01-31 13:59:44 +00:00
using t_kill_focus = std : : function < void ( ) > ;
2018-04-13 10:35:04 +00:00
using t_change = std : : function < void ( t_config_option_key , const boost : : any & ) > ;
using t_back_to_init = std : : function < void ( const std : : string & ) > ;
2017-12-13 13:45:10 +00:00
2018-02-19 08:15:15 +00:00
wxString double_to_string ( double const value ) ;
2018-04-26 13:11:02 +00:00
class MyButton : public wxButton
{
public :
MyButton ( ) { }
MyButton ( wxWindow * parent , wxWindowID id , const wxString & label = wxEmptyString ,
const wxPoint & pos = wxDefaultPosition ,
const wxSize & size = wxDefaultSize , long style = 0 ,
const wxValidator & validator = wxDefaultValidator ,
const wxString & name = wxTextCtrlNameStr )
{
this - > Create ( parent , id , label , pos , size , style , validator , name ) ;
}
// overridden from wxWindow base class
virtual bool
AcceptsFocusFromKeyboard ( ) const { return false ; }
} ;
2017-12-26 22:04:54 +00:00
class Field {
2017-12-18 12:58:51 +00:00
protected :
2017-12-13 13:45:10 +00:00
// factory function to defer and enforce creation of derived type.
2018-03-06 11:34:20 +00:00
virtual void PostInitialize ( ) ;
2017-12-13 13:45:10 +00:00
/// Finish constructing the Field's wxWidget-related properties, including setting its own sizer, etc.
2018-01-31 15:46:17 +00:00
virtual void BUILD ( ) = 0 ;
2017-12-13 13:45:10 +00:00
/// Call the attached on_kill_focus method.
2018-01-31 13:59:44 +00:00
//! It's important to use wxEvent instead of wxFocusEvent,
//! in another case we can't unfocused control at all
2018-01-31 15:46:17 +00:00
void on_kill_focus ( wxEvent & event ) ;
2017-12-13 13:45:10 +00:00
/// Call the attached on_change method.
2018-01-31 15:46:17 +00:00
void on_change_field ( ) ;
2018-03-06 11:34:20 +00:00
/// Call the attached m_back_to_initial_value method.
void on_back_to_initial_value ( ) ;
2018-03-16 16:25:11 +00:00
/// Call the attached m_back_to_sys_value method.
void on_back_to_sys_value ( ) ;
2017-12-13 13:45:10 +00:00
2017-12-26 22:04:54 +00:00
public :
2017-12-13 13:45:10 +00:00
/// parent wx item, opportunity to refactor (probably not necessary - data duplication)
2018-01-05 14:11:33 +00:00
wxWindow * m_parent { nullptr } ;
2017-12-13 13:45:10 +00:00
/// Function object to store callback passed in from owning object.
2018-01-31 13:59:44 +00:00
t_kill_focus m_on_kill_focus { nullptr } ;
2017-12-13 13:45:10 +00:00
/// Function object to store callback passed in from owning object.
2018-01-05 14:11:33 +00:00
t_change m_on_change { nullptr } ;
2017-12-13 13:45:10 +00:00
2018-03-16 16:25:11 +00:00
/// Function object to store callback passed in from owning object.
2018-03-06 11:34:20 +00:00
t_back_to_init m_back_to_initial_value { nullptr } ;
2018-03-16 16:25:11 +00:00
t_back_to_init m_back_to_sys_value { nullptr } ;
2018-03-06 11:34:20 +00:00
2018-01-27 16:39:00 +00:00
// This is used to avoid recursive invocation of the field change/update by wxWidgets.
2018-01-05 14:11:33 +00:00
bool m_disable_change_event { false } ;
2018-03-07 14:05:41 +00:00
bool m_is_modified_value { false } ;
2018-03-16 16:25:11 +00:00
bool m_is_nonsys_value { true } ;
2017-12-13 13:45:10 +00:00
/// Copy of ConfigOption for deduction purposes
2018-01-05 14:11:33 +00:00
const ConfigOptionDef m_opt { ConfigOptionDef ( ) } ;
const t_config_option_key m_opt_id ; //! {""};
2018-06-22 14:13:34 +00:00
int m_opt_idx = 0 ;
2017-12-13 13:45:10 +00:00
/// Sets a value for this control.
/// subclasses should overload with a specific version
/// Postcondition: Method does not fire the on_change event.
2018-04-13 10:35:04 +00:00
virtual void set_value ( const boost : : any & value , bool change_event ) = 0 ;
2017-12-13 13:45:10 +00:00
/// Gets a boost::any representing this control.
/// subclasses should overload with a specific version
2018-04-24 08:33:11 +00:00
virtual boost : : any & get_value ( ) = 0 ;
2017-12-13 13:45:10 +00:00
2018-01-05 14:11:33 +00:00
virtual void enable ( ) = 0 ;
virtual void disable ( ) = 0 ;
2017-12-13 13:45:10 +00:00
2018-04-17 08:15:48 +00:00
/// Fires the enable or disable function, based on the input.
2018-01-05 14:11:33 +00:00
inline void toggle ( bool en ) { en ? enable ( ) : disable ( ) ; }
2017-12-13 13:45:10 +00:00
2018-01-27 13:21:16 +00:00
virtual wxString get_tooltip_text ( const wxString & default_string ) ;
2017-12-13 13:45:10 +00:00
2018-03-16 11:56:03 +00:00
// set icon to "UndoToSystemValue" button according to an inheritance of preset
2018-04-16 09:03:08 +00:00
// void set_nonsys_btn_icon(const wxBitmap& icon);
2018-03-16 11:56:03 +00:00
2018-01-05 14:11:33 +00:00
Field ( const ConfigOptionDef & opt , const t_config_option_key & id ) : m_opt ( opt ) , m_opt_id ( id ) { } ;
Field ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) : m_parent ( parent ) , m_opt ( opt ) , m_opt_id ( id ) { } ;
2017-12-13 13:45:10 +00:00
/// If you don't know what you are getting back, check both methods for nullptr.
2018-01-05 14:11:33 +00:00
virtual wxSizer * getSizer ( ) { return nullptr ; }
virtual wxWindow * getWindow ( ) { return nullptr ; }
2017-12-13 13:45:10 +00:00
2018-04-13 10:35:04 +00:00
bool is_matched ( const std : : string & string , const std : : string & pattern ) ;
2018-04-24 08:33:11 +00:00
void get_value_by_opt_type ( wxString & str ) ;
2017-12-13 13:45:10 +00:00
/// Factory method for generating new derived classes.
template < class T >
static t_field Create ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) // interface for creating shared objects
{
2018-01-21 20:42:06 +00:00
auto p = Slic3r : : make_unique < T > ( parent , opt , id ) ;
2017-12-13 13:45:10 +00:00
p - > PostInitialize ( ) ;
2017-12-18 12:58:51 +00:00
return std : : move ( p ) ; //!p;
2017-12-13 13:45:10 +00:00
}
2018-04-16 09:03:08 +00:00
bool set_undo_bitmap ( const wxBitmap * bmp ) {
if ( m_undo_bitmap ! = bmp ) {
m_undo_bitmap = bmp ;
m_Undo_btn - > SetBitmap ( * bmp ) ;
return true ;
}
return false ;
}
bool set_undo_to_sys_bitmap ( const wxBitmap * bmp ) {
if ( m_undo_to_sys_bitmap ! = bmp ) {
m_undo_to_sys_bitmap = bmp ;
m_Undo_to_sys_btn - > SetBitmap ( * bmp ) ;
return true ;
}
return false ;
}
2018-04-17 08:15:48 +00:00
bool set_label_colour ( const wxColour * clr ) {
if ( m_Label = = nullptr ) return false ;
if ( m_label_color ! = clr ) {
m_label_color = clr ;
m_Label - > SetForegroundColour ( * clr ) ;
m_Label - > Refresh ( true ) ;
}
return false ;
}
2018-04-30 12:20:33 +00:00
bool set_label_colour_force ( const wxColour * clr ) {
if ( m_Label = = nullptr ) return false ;
m_Label - > SetForegroundColour ( * clr ) ;
m_Label - > Refresh ( true ) ;
return false ;
}
2018-04-24 12:11:23 +00:00
bool set_undo_tooltip ( const wxString * tip ) {
if ( m_undo_tooltip ! = tip ) {
m_undo_tooltip = tip ;
m_Undo_btn - > SetToolTip ( * tip ) ;
return true ;
}
return false ;
}
bool set_undo_to_sys_tooltip ( const wxString * tip ) {
if ( m_undo_to_sys_tooltip ! = tip ) {
m_undo_to_sys_tooltip = tip ;
m_Undo_to_sys_btn - > SetToolTip ( * tip ) ;
return true ;
}
return false ;
}
2018-04-16 09:03:08 +00:00
protected :
2018-04-26 13:11:02 +00:00
MyButton * m_Undo_btn = nullptr ;
2018-04-24 12:11:23 +00:00
// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
2018-04-16 09:03:08 +00:00
const wxBitmap * m_undo_bitmap = nullptr ;
2018-04-24 12:11:23 +00:00
const wxString * m_undo_tooltip = nullptr ;
2018-04-26 13:11:02 +00:00
MyButton * m_Undo_to_sys_btn = nullptr ;
2018-04-24 12:11:23 +00:00
// Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
2018-04-16 09:03:08 +00:00
const wxBitmap * m_undo_to_sys_bitmap = nullptr ;
2018-04-24 12:11:23 +00:00
const wxString * m_undo_to_sys_tooltip = nullptr ;
2018-04-16 09:03:08 +00:00
2018-04-17 08:15:48 +00:00
wxStaticText * m_Label = nullptr ;
// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
2018-04-30 12:20:33 +00:00
const wxColour * m_label_color = nullptr ;
2018-04-17 08:15:48 +00:00
2018-04-24 08:33:11 +00:00
// current value
boost : : any m_value ;
2018-04-16 09:03:08 +00:00
friend class OptionsGroup ;
2017-12-13 13:45:10 +00:00
} ;
/// Convenience function, accepts a const reference to t_field and checks to see whether
/// or not both wx pointers are null.
inline bool is_bad_field ( const t_field & obj ) { return obj - > getSizer ( ) = = nullptr & & obj - > getWindow ( ) = = nullptr ; }
/// Covenience function to determine whether this field is a valid window field.
inline bool is_window_field ( const t_field & obj ) { return ! is_bad_field ( obj ) & & obj - > getWindow ( ) ! = nullptr ; }
/// Covenience function to determine whether this field is a valid sizer field.
inline bool is_sizer_field ( const t_field & obj ) { return ! is_bad_field ( obj ) & & obj - > getSizer ( ) ! = nullptr ; }
class TextCtrl : public Field {
using Field : : Field ;
2018-06-11 14:23:10 +00:00
# ifdef __WXGTK__
bool bChangedValueEvent = false ;
2018-09-05 07:47:36 +00:00
void change_field_value ( wxEvent & event ) ;
2018-06-11 14:23:10 +00:00
# endif //__WXGTK__
2017-12-13 13:45:10 +00:00
public :
2017-12-18 12:58:51 +00:00
TextCtrl ( const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( opt , id ) { }
TextCtrl ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( parent , opt , id ) { }
2018-04-13 10:35:04 +00:00
~ TextCtrl ( ) { }
2017-12-18 12:58:51 +00:00
2017-12-13 13:45:10 +00:00
void BUILD ( ) ;
wxWindow * window { nullptr } ;
2018-04-13 10:35:04 +00:00
virtual void set_value ( const std : : string & value , bool change_event = false ) {
2018-03-15 17:06:26 +00:00
m_disable_change_event = ! change_event ;
2017-12-13 13:45:10 +00:00
dynamic_cast < wxTextCtrl * > ( window ) - > SetValue ( wxString ( value ) ) ;
2018-01-27 16:39:00 +00:00
m_disable_change_event = false ;
2017-12-13 13:45:10 +00:00
}
2018-04-13 10:35:04 +00:00
virtual void set_value ( const boost : : any & value , bool change_event = false ) {
2018-03-15 17:06:26 +00:00
m_disable_change_event = ! change_event ;
2018-01-09 08:41:07 +00:00
dynamic_cast < wxTextCtrl * > ( window ) - > SetValue ( boost : : any_cast < wxString > ( value ) ) ;
2018-01-27 16:39:00 +00:00
m_disable_change_event = false ;
2017-12-13 13:45:10 +00:00
}
2018-04-24 08:33:11 +00:00
boost : : any & get_value ( ) override ;
2017-12-13 13:45:10 +00:00
2018-01-09 12:52:01 +00:00
virtual void enable ( ) ;
virtual void disable ( ) ;
2017-12-26 22:04:54 +00:00
virtual wxWindow * getWindow ( ) { return window ; }
2017-12-13 13:45:10 +00:00
} ;
2017-12-18 12:58:51 +00:00
class CheckBox : public Field {
using Field : : Field ;
public :
CheckBox ( const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( opt , id ) { }
CheckBox ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( parent , opt , id ) { }
2018-04-13 10:35:04 +00:00
~ CheckBox ( ) { }
2017-12-18 12:58:51 +00:00
wxWindow * window { nullptr } ;
void BUILD ( ) override ;
2018-03-15 17:06:26 +00:00
void set_value ( const bool value , bool change_event = false ) {
m_disable_change_event = ! change_event ;
2017-12-18 12:58:51 +00:00
dynamic_cast < wxCheckBox * > ( window ) - > SetValue ( value ) ;
2018-01-27 16:39:00 +00:00
m_disable_change_event = false ;
2017-12-18 12:58:51 +00:00
}
2018-04-13 10:35:04 +00:00
void set_value ( const boost : : any & value , bool change_event = false ) {
2018-03-15 17:06:26 +00:00
m_disable_change_event = ! change_event ;
2017-12-18 12:58:51 +00:00
dynamic_cast < wxCheckBox * > ( window ) - > SetValue ( boost : : any_cast < bool > ( value ) ) ;
2018-01-27 16:39:00 +00:00
m_disable_change_event = false ;
2017-12-18 12:58:51 +00:00
}
2018-04-24 08:33:11 +00:00
boost : : any & get_value ( ) override ;
2017-12-18 12:58:51 +00:00
void enable ( ) override { dynamic_cast < wxCheckBox * > ( window ) - > Enable ( ) ; }
void disable ( ) override { dynamic_cast < wxCheckBox * > ( window ) - > Disable ( ) ; }
wxWindow * getWindow ( ) override { return window ; }
} ;
class SpinCtrl : public Field {
using Field : : Field ;
public :
SpinCtrl ( const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( opt , id ) , tmp_value ( - 9999 ) { }
SpinCtrl ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( parent , opt , id ) , tmp_value ( - 9999 ) { }
2018-04-13 10:35:04 +00:00
~ SpinCtrl ( ) { }
2017-12-18 12:58:51 +00:00
int tmp_value ;
wxWindow * window { nullptr } ;
void BUILD ( ) override ;
2018-04-13 10:35:04 +00:00
void set_value ( const std : : string & value , bool change_event = false ) {
2018-03-15 17:06:26 +00:00
m_disable_change_event = ! change_event ;
2017-12-18 12:58:51 +00:00
dynamic_cast < wxSpinCtrl * > ( window ) - > SetValue ( value ) ;
2018-01-27 16:39:00 +00:00
m_disable_change_event = false ;
2017-12-18 12:58:51 +00:00
}
2018-04-13 10:35:04 +00:00
void set_value ( const boost : : any & value , bool change_event = false ) {
2018-03-15 17:06:26 +00:00
m_disable_change_event = ! change_event ;
2018-04-05 10:12:35 +00:00
tmp_value = boost : : any_cast < int > ( value ) ;
2018-04-13 10:35:04 +00:00
dynamic_cast < wxSpinCtrl * > ( window ) - > SetValue ( tmp_value ) ;
2018-01-27 16:39:00 +00:00
m_disable_change_event = false ;
2017-12-18 12:58:51 +00:00
}
2018-04-24 08:33:11 +00:00
boost : : any & get_value ( ) override {
// return boost::any(tmp_value);
return m_value = tmp_value ;
2017-12-18 12:58:51 +00:00
}
void enable ( ) override { dynamic_cast < wxSpinCtrl * > ( window ) - > Enable ( ) ; }
void disable ( ) override { dynamic_cast < wxSpinCtrl * > ( window ) - > Disable ( ) ; }
wxWindow * getWindow ( ) override { return window ; }
} ;
class Choice : public Field {
using Field : : Field ;
public :
Choice ( const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( opt , id ) { }
Choice ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( parent , opt , id ) { }
2018-04-13 10:35:04 +00:00
~ Choice ( ) { }
2017-12-18 12:58:51 +00:00
wxWindow * window { nullptr } ;
2017-12-26 22:04:54 +00:00
void BUILD ( ) override ;
2017-12-18 12:58:51 +00:00
void set_selection ( ) ;
2018-04-13 10:35:04 +00:00
void set_value ( const std : : string & value , bool change_event = false ) ;
void set_value ( const boost : : any & value , bool change_event = false ) ;
void set_values ( const std : : vector < std : : string > & values ) ;
2018-04-24 08:33:11 +00:00
boost : : any & get_value ( ) override ;
2017-12-18 12:58:51 +00:00
void enable ( ) override { dynamic_cast < wxComboBox * > ( window ) - > Enable ( ) ; } ;
void disable ( ) override { dynamic_cast < wxComboBox * > ( window ) - > Disable ( ) ; } ;
wxWindow * getWindow ( ) override { return window ; }
} ;
2017-12-13 13:45:10 +00:00
2017-12-22 10:50:28 +00:00
class ColourPicker : public Field {
using Field : : Field ;
public :
ColourPicker ( const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( opt , id ) { }
ColourPicker ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( parent , opt , id ) { }
2018-04-13 10:35:04 +00:00
~ ColourPicker ( ) { }
2017-12-22 10:50:28 +00:00
wxWindow * window { nullptr } ;
void BUILD ( ) override ;
2018-04-13 10:35:04 +00:00
void set_value ( const std : : string & value , bool change_event = false ) {
2018-03-15 17:06:26 +00:00
m_disable_change_event = ! change_event ;
2018-01-27 16:39:00 +00:00
dynamic_cast < wxColourPickerCtrl * > ( window ) - > SetColour ( value ) ;
m_disable_change_event = false ;
2017-12-22 10:50:28 +00:00
}
2018-04-13 10:35:04 +00:00
void set_value ( const boost : : any & value , bool change_event = false ) {
2018-03-15 17:06:26 +00:00
m_disable_change_event = ! change_event ;
2018-02-13 15:05:53 +00:00
dynamic_cast < wxColourPickerCtrl * > ( window ) - > SetColour ( boost : : any_cast < wxString > ( value ) ) ;
2018-01-27 16:39:00 +00:00
m_disable_change_event = false ;
2017-12-22 10:50:28 +00:00
}
2018-01-09 12:52:01 +00:00
2018-04-24 08:33:11 +00:00
boost : : any & get_value ( ) override ;
2017-12-22 10:50:28 +00:00
void enable ( ) override { dynamic_cast < wxColourPickerCtrl * > ( window ) - > Enable ( ) ; } ;
void disable ( ) override { dynamic_cast < wxColourPickerCtrl * > ( window ) - > Disable ( ) ; } ;
wxWindow * getWindow ( ) override { return window ; }
} ;
2018-01-25 12:46:04 +00:00
class PointCtrl : public Field {
2017-12-22 10:50:28 +00:00
using Field : : Field ;
public :
2018-01-25 12:46:04 +00:00
PointCtrl ( const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( opt , id ) { }
PointCtrl ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( parent , opt , id ) { }
2018-04-13 10:35:04 +00:00
~ PointCtrl ( ) { }
2017-12-22 10:50:28 +00:00
wxSizer * sizer { nullptr } ;
2018-04-13 10:35:04 +00:00
wxTextCtrl * x_textctrl { nullptr } ;
wxTextCtrl * y_textctrl { nullptr } ;
2017-12-22 10:50:28 +00:00
void BUILD ( ) override ;
2018-04-13 10:35:04 +00:00
void set_value ( const Pointf & value , bool change_event = false ) ;
void set_value ( const boost : : any & value , bool change_event = false ) ;
2018-04-24 08:33:11 +00:00
boost : : any & get_value ( ) override ;
2017-12-22 10:50:28 +00:00
2017-12-26 22:04:54 +00:00
void enable ( ) override {
x_textctrl - > Enable ( ) ;
2018-04-13 10:35:04 +00:00
y_textctrl - > Enable ( ) ; }
2017-12-26 22:04:54 +00:00
void disable ( ) override {
x_textctrl - > Disable ( ) ;
2018-04-13 10:35:04 +00:00
y_textctrl - > Disable ( ) ; }
2017-12-22 10:50:28 +00:00
wxSizer * getSizer ( ) override { return sizer ; }
} ;
2018-06-21 14:15:56 +00:00
class StaticText : public Field {
using Field : : Field ;
public :
StaticText ( const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( opt , id ) { }
StaticText ( wxWindow * parent , const ConfigOptionDef & opt , const t_config_option_key & id ) : Field ( parent , opt , id ) { }
~ StaticText ( ) { }
wxWindow * window { nullptr } ;
void BUILD ( ) override ;
void set_value ( const std : : string & value , bool change_event = false ) {
m_disable_change_event = ! change_event ;
dynamic_cast < wxStaticText * > ( window ) - > SetLabel ( value ) ;
m_disable_change_event = false ;
}
void set_value ( const boost : : any & value , bool change_event = false ) {
m_disable_change_event = ! change_event ;
dynamic_cast < wxStaticText * > ( window ) - > SetLabel ( boost : : any_cast < wxString > ( value ) ) ;
m_disable_change_event = false ;
}
boost : : any & get_value ( ) override { return m_value ; }
void enable ( ) override { dynamic_cast < wxColourPickerCtrl * > ( window ) - > Enable ( ) ; } ;
void disable ( ) override { dynamic_cast < wxColourPickerCtrl * > ( window ) - > Disable ( ) ; } ;
wxWindow * getWindow ( ) override { return window ; }
} ;
2017-12-18 12:58:51 +00:00
} // GUI
} // Slic3r
2018-03-12 15:04:32 +00:00
# endif /* SLIC3R_GUI_FIELD_HPP */