From 1d230c2b96b61cddad4478ad57d4a9f29f4c8791 Mon Sep 17 00:00:00 2001 From: Kurt Zenker Date: Thu, 6 Mar 2008 10:58:21 +0000 Subject: INTEGRATION: CWS layout_DEV300 (1.1.2); FILE ADDED 2008/02/11 15:13:38 jcn 1.1.2.2: Make layout stuff in toolkit build with --enable-layout. 2008/02/08 18:44:46 jcn 1.1.2.1: Initial toolkit import from ee9a2fcc29d7e2f01cc80ef7c13bf7bc7d55ae7e. layout/source/awt -> toolkit/source/awt layout/source/core -> toolkit/source/layout layout/source/wrapper -> toolkit/source/vclcompat layout/inc/layout -> toolkit/inc/layout layout/source/inc -> toolkit/inc/layout layout/workben -> toolkit/workben/layout That's ooo-build trunk r11539 @ ooh680-m5/src680-m245. --- toolkit/workben/layout/editor.cxx | 1876 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1876 insertions(+) create mode 100644 toolkit/workben/layout/editor.cxx (limited to 'toolkit/workben') diff --git a/toolkit/workben/layout/editor.cxx b/toolkit/workben/layout/editor.cxx new file mode 100644 index 000000000000..b93907b718e1 --- /dev/null +++ b/toolkit/workben/layout/editor.cxx @@ -0,0 +1,1876 @@ +#include "editor.hxx" + +#undef NDEBUG +#include +#include + +#include +#include +#include +#include + +using namespace layout::css; + +using rtl::OUString; + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +// FIXME: +//#define FILEDLG + +// somewhat of a hack unfortunately ... +#include "layoutcore.hxx" +#include "root.hxx" +#include "helper.hxx" + +//** Utilities + +#define OUSTRING_CSTR( str ) \ + rtl::OUStringToOString( str, RTL_TEXTENCODING_ASCII_US ).getStr() + +// TODO: automatically generated +struct WidgetSpec { + const char *pLabel, *pName, *pIconName; + bool bIsContainer; }; +static const WidgetSpec WIDGETS_SPECS[] = { + { "Label", "fixedtext" , "sc_label.png", false }, + { "Button", "pushbutton" , "sc_pushbutton.png", false }, + { "Radio Button", "radiobutton" , "sc_radiobutton.png", false }, + { "Check Box", "checkbox" , "sc_checkbox.png", false }, + { "Line Edit", "edit" , "sc_edit.png", false }, + { "Numeric Field", "numericfield", "sc_numericfield.png", false }, + { "List Box ", "listbox" , NULL, false }, + // containers + { "Hor Box", "hbox" , NULL, true }, + { "Ver Box", "vbox" , NULL, true }, + { "Table", "table" , NULL, true }, + { "Alignment", "align" , NULL, true }, + { "Tab Control", "tabcontrol" , NULL, true }, + { "Hor Splitter", "hsplitter" , NULL, true }, + { "Ver Splitter", "vsplitter" , NULL, true }, + { "Scroller", "scroller" , NULL, true }, +}; +const int WIDGETS_SPECS_LEN = sizeof (WIDGETS_SPECS) / sizeof (WidgetSpec); + +// TEMP: from helper +namespace layoutimpl { + + typedef std::list< std::pair< rtl::OUString, rtl::OUString > > PropList; + + css::uno::Reference< css::awt::XLayoutConstrains > + createWidget( css::uno::Reference< css::awt::XToolkit > xToolkit, + css::uno::Reference< css::uno::XInterface > xParent, + const rtl::OUString &rName, long nProps, bool bToplevel ); + + uno::Any anyFromString (const rtl::OUString &value, const uno::Type &type); +}; + +using namespace layout; +using namespace layoutimpl; +namespace css = ::com::sun::star; + +static rtl::OUString anyToString (uno::Any value) +{ + try + { + switch (value.getValueTypeClass()) { + case uno::TypeClass_STRING: + return value.get(); + case uno::TypeClass_CONSTANT: + return rtl::OUString::valueOf (value.get()); + case uno::TypeClass_LONG: + return rtl::OUString::valueOf (value.get()); + case uno::TypeClass_SHORT: + // FIXME: seems broken + return rtl::OUString::valueOf ((sal_Int32) value.get()); + + case uno::TypeClass_FLOAT: + return rtl::OUString::valueOf (value.get()); + case uno::TypeClass_DOUBLE: + return rtl::OUString::valueOf (value.get()); + + case uno::TypeClass_BOOLEAN: + { + bool val = value.get(); + return rtl::OUString( val ? "1" : "0", 1, RTL_TEXTENCODING_ASCII_US ); +/* if ( val ) + return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "true" ) ); + else + return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "false" ) );*/ + } + default: + break; + } + } + catch(...) {} + return rtl::OUString(); +} + +static inline long anyToNatural (uno::Any value) +{ return anyToString( value ).toInt64(); } +static inline double anyToDecimal (uno::Any value) +{ return anyToString( value ).toDouble(); } + +/* XLayoutContainer/XLayoutConstrains are a bit of a hasle to work with. + Let's wrap them. */ +class Widget : public layoutimpl::LayoutWidget { +friend class EditorRoot; + + Widget *mpParent; + std::vector< Widget *> maChildren; + bool mbForeign; + + rtl::OUString mrId; + rtl::OUString mrLabel, mrUnoName; + +// TODO: store original properties. And some property handling methods. + long mnOriAttrbs; + layoutimpl::PropList maOriProps, maOriChildProps; + +public: + + // to be used to wrap the root + Widget( uno::Reference< awt::XLayoutConstrains > xImport, const char *label ) + : mpParent( 0 ), mbForeign( true ) + { + mxWidget = xImport; + mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY ); + + mrLabel = rtl::OUString( label, strlen( label ), RTL_TEXTENCODING_UTF8 ); + +#if 0 /* obsolete */ + // FIXME: this code is meant to import a XML file. Just use the importer, + // then pass the root widget. But information like the ID string is lost. + // So, this needs to be more closely tight to the importer. + uno::Sequence< uno::Reference< awt::XLayoutConstrains > > aChildren; + for ( int i = 0; i < aChildren.getLength(); i++ ) + { + Widget *pChild = new Widget( aChildren[ i ], "---" ); + maChildren.push_back( pChild ); + pChild->mpParent = this; + } +#endif + } + + Widget( rtl::OUString id, uno::Reference< awt::XToolkit > xToolkit, + uno::Reference< awt::XLayoutContainer > xParent, + rtl::OUString unoName, long nAttrbs ) + : mpParent( 0 ), mbForeign( false ), mrId( id ), + mnOriAttrbs( nAttrbs ) + { + while ( xParent.is() && !uno::Reference< awt::XWindow >( xParent, uno::UNO_QUERY ).is() ) + { + uno::Reference< awt::XLayoutContainer > xContainer( xParent, uno::UNO_QUERY ); + assert( xContainer.is() ); + xParent = uno::Reference< awt::XLayoutContainer >( xContainer->getParent(), uno::UNO_QUERY ); + } + + mxWidget = layoutimpl::createWidget( xToolkit, xParent, unoName, nAttrbs ); + assert( mxWidget.is() ); + mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY ); + + mrLabel = mrUnoName = unoName; + // try to get a nicer label for the widget + for ( int i = 0; i < WIDGETS_SPECS_LEN; i++ ) + if ( unoName.equalsAscii( WIDGETS_SPECS[ i ].pName ) ) + { + const char *label = WIDGETS_SPECS[ i ].pLabel; + mrLabel = rtl::OUString( label, strlen( label ), RTL_TEXTENCODING_UTF8 ); + break; + } + + // set default Text property + // TODO: disable editing of text fields, check boxes selected, etc... +#if 0 + uno::Reference< awt::XVclWindowPeer> xVclPeer( mxWidget, uno::UNO_QUERY ) + if ( xVclPeer.is() ) // XVclWindowPeer ignores missing / incorrect properties + +//FIXME: it looks odd on widgets like NumericField seeing text which is deleted +// when you interact with it... We can avoid it for those widgets, by doing a getProp +// of "Text" and check if it is empty or not. + + xVclPeer->setProperty( rtl::OUString::createFromAscii( "Text" ), + uno::makeAny( rtl::OUString::createFromAscii( "new widget" ) ) ); +#endif + + // store original properties + { + PropertyIterator it( this, WINDOW_PROPERTY ); + while ( it.hasNext() ) + { + beans::Property prop = it.next(); + rtl::OUString name( prop.Name ); + rtl::OUString value( getProperty( name, WINDOW_PROPERTY ) ); +fprintf(stderr, "original property: %s = %s\n", OUSTRING_CSTR(name), OUSTRING_CSTR(value)); + std::pair< rtl::OUString, rtl::OUString > pair( name, value ); + maOriProps.push_back( pair ); + } + } + + } + + ~Widget() + { + for ( std::vector< Widget *>::const_iterator it = maChildren.begin(); + it != maChildren.end(); it++ ) + delete *it; + if ( !mbForeign ) + { + uno::Reference< lang::XComponent > xComp( mxWidget, uno::UNO_QUERY ); + if ( xComp.is() ) + // some widgets, like our containers, don't implement this interface... + xComp->dispose(); + } + } + + uno::Reference< awt::XLayoutConstrains > impl() + { + return mxWidget; + } + + // LayoutWidget + virtual bool addChild( LayoutWidget *pChild ) + { + return addChild( static_cast< Widget * >( pChild ) ); + } + + virtual void setProperties( const PropList &rProps ) + { +// maOriProps = rProps; + LayoutWidget::setProperties( rProps ); + } + + virtual void setChildProperties( LayoutWidget *pChild, const PropList &rProps ) + { + maOriChildProps = rProps; + LayoutWidget::setChildProperties( pChild, rProps ); + } + + // tree travel + Widget *up() + { + return mpParent; + } + + Widget *down() + { + if ( maChildren.empty() ) + return NULL; + return maChildren.front(); + } + + Widget *next() + { + if ( mpParent ) + { + int pos = mpParent->getChildPos( this ); + return mpParent->getChild( pos+1 ); + } + return NULL; + } + + Widget *prev() + { + if ( mpParent ) + { + int pos = mpParent->getChildPos( this ); + return mpParent->getChild( pos-1 ); + } + return NULL; + } + + // handle + bool addChild( Widget *pChild, int pos = 0xffff ) + { + if ( !mxContainer.is() ) + return false; + + uno::Sequence< uno::Reference < awt::XLayoutConstrains > > aChildren; + aChildren = mxContainer->getChildren(); + int nChildrenLen = aChildren.getLength(); + + // ugly, but let's check if the container is next to full... + try { + mxContainer->addChild( pChild->mxWidget ); + } + catch( awt::MaxChildrenException ex ) { + return false; + } + + if ( pos < nChildrenLen ) + { // if its on the middle, we need to make space for it + mxContainer->removeChild( pChild->mxWidget ); + for ( int i = pos; i < nChildrenLen; i++ ) + mxContainer->removeChild( aChildren[ i ] ); + mxContainer->addChild( pChild->mxWidget ); + for ( int i = pos; i < nChildrenLen; i++ ) + mxContainer->addChild( aChildren[ i ] ); + maChildren.insert( maChildren.begin()+pos, pChild ); + } + else + maChildren.push_back( pChild ); + + assert( pChild->mpParent == NULL ); + pChild->mpParent = this; + + // store container props + { + pChild->maOriChildProps.clear(); + PropertyIterator it( pChild, CONTAINER_PROPERTY ); + while ( it.hasNext() ) + { + beans::Property prop = it.next(); + rtl::OUString name( prop.Name ); + rtl::OUString value( pChild->getProperty( name, CONTAINER_PROPERTY ) ); + std::pair< rtl::OUString, rtl::OUString > pair( name, value ); + pChild->maOriChildProps.push_back( pair ); + } + } + + return true; + } + + bool removeChild( Widget *pChild ) + { + if ( !mxContainer.is() || pChild->mpParent != this ) + return false; + + mxContainer->removeChild( pChild->mxWidget ); + + unsigned int pos = getChildPos( pChild ); + if ( pos < maChildren.size() ) + maChildren.erase( maChildren.begin()+pos ); + pChild->mpParent = NULL; + + return true; + } + + bool swapWithChild( Widget *pChild ) + { + if ( !pChild->isContainer() ) + return false; + + // remove all child's childrens, and try to add them here + removeChild( pChild ); + + // keep a copy for failure + std::vector< Widget *> aChildren = maChildren; + std::vector< Widget *> aChildChildren = pChild->maChildren; + + for ( std::vector< Widget *>::const_iterator it = aChildChildren.begin(); + it != aChildChildren.end(); it++ ) + pChild->removeChild( *it ); + + for ( std::vector< Widget *>::const_iterator it = aChildChildren.begin(); + it != aChildChildren.end(); it++ ) + if ( !addChild( *it ) ) + { // failure + for ( std::vector< Widget *>::const_iterator jt = aChildChildren.begin(); + jt != it; jt++ ) + removeChild( *jt ); + for ( std::vector< Widget *>::const_iterator jt = aChildChildren.begin(); + jt != aChildChildren.end(); jt++ ) + pChild->addChild( *jt ); + return false; + } + + Widget *pParent = up(); + + if ( pParent ) + { + pParent->removeChild( this ); + pParent->addChild( pChild ); + } + pChild->addChild( this ); + return true; + } + + unsigned int getChildPos( Widget *pChild ) + { + int i = 0; + for ( std::vector< Widget *>::const_iterator it = maChildren.begin(); + it != maChildren.end(); it++, i++ ) + if ( *it == pChild ) + break; + return i; + } + + Widget *getChild( int pos ) + { + if ( pos >= 0 && pos < (signed) maChildren.size() ) + return *(maChildren.begin() + pos); + return NULL; + } + + bool isContainer() + { return mxContainer.is(); } + unsigned int getChildrenLen() + { return maChildren.size(); } + + rtl::OUString getLabel() const + { return mrLabel; } + rtl::OUString getUnoName() const + { return mrUnoName; } + + int getDepth() + { + int depth = 0; + for ( Widget *pWidget = mpParent; pWidget; pWidget = pWidget->mpParent ) + depth++; + return depth; + } + + enum PropertyKind { + WINDOW_PROPERTY, CONTAINER_PROPERTY, WINBITS_PROPERTY + }; + + static rtl::OUString findProperty( const PropList &props, rtl::OUString propName ) + { + for ( PropList::const_iterator it = props.begin(); it != props.end(); it++ ) + if ( it->first.equalsIgnoreAsciiCase( propName ) ) + return it->second; +fprintf(stderr, "Serious error: property '%s' not found\n", OUSTRING_CSTR(propName)); + return rtl::OUString(); + } + + rtl::OUString getOriginalProperty( rtl::OUString rPropName, PropertyKind rKind ) + { + rtl::OUString rValue; + switch ( rKind ) { + case WINDOW_PROPERTY: + rValue = findProperty( maOriProps, rPropName ); + break; + case CONTAINER_PROPERTY: + rValue = findProperty( maOriChildProps, rPropName ); + break; + case WINBITS_PROPERTY: + // TODO + break; + } + + return rValue; + } + + rtl::OUString getProperty( rtl::OUString rPropName, PropertyKind rKind ) + { + rtl::OUString rValue; + switch ( rKind ) { + case WINDOW_PROPERTY: + rValue = anyToString( layoutimpl::prophlp::getProperty( mxWidget, rPropName ) ); + break; + case CONTAINER_PROPERTY: + if ( mpParent ) + rValue = anyToString( layoutimpl::prophlp::getProperty( + mpParent->mxContainer->getChildProperties( mxWidget ), rPropName ) ); + break; + case WINBITS_PROPERTY: + // TODO + break; + } + + return rValue; + } + + bool isPropertyTouched( rtl::OUString propName, PropertyKind rKind ) + { + rtl::OUString oriValue = getOriginalProperty( propName, rKind ); + rtl::OUString newValue = getProperty( propName, rKind ); + bool isTouched = oriValue != newValue; +fprintf(stderr, "is property '%s' touched? %s (%s vs %s)\n", OUSTRING_CSTR(propName), isTouched ? "yes" : "no", OUSTRING_CSTR(oriValue), OUSTRING_CSTR(newValue)); + return isTouched; + } + + void setProperty( rtl::OUString rPropName, PropertyKind rKind, uno::Any rValue ) + { + switch ( rKind ) { + case WINDOW_PROPERTY: + layoutimpl::prophlp::setProperty( mxWidget, rPropName, rValue ); + break; + case CONTAINER_PROPERTY: + if ( mpParent ) + layoutimpl::prophlp::setProperty( + mpParent->mxContainer->getChildProperties( mxWidget ), rPropName, rValue ); + break; + case WINBITS_PROPERTY: + // TODO + break; + } + } + + struct PropertyIterator { + friend class Widget; + PropertyKind mrKind; + uno::Sequence< beans::Property > maProps; + int nPropIt; + + PropertyIterator( Widget *pWidget, PropertyKind rKind ) + : mrKind( rKind ), nPropIt( 0 ) + { + switch ( rKind ) + { + case WINDOW_PROPERTY: + if ( layoutimpl::prophlp::canHandleProps( pWidget->mxWidget ) ) + { + uno::Reference< beans::XPropertySetInfo > xInfo + = layoutimpl::prophlp::queryPropertyInfo( pWidget->mxWidget ); + if ( !xInfo.is() ) + return; + + maProps = xInfo->getProperties(); + } + break; + case CONTAINER_PROPERTY: + if ( pWidget->mpParent ) + { + uno::Reference< beans::XPropertySet >xParentSet( + pWidget->mpParent->mxContainer->getChildProperties( pWidget->mxWidget ) ); + if ( xParentSet.is()) + { + uno::Reference< beans::XPropertySetInfo > xInfo( xParentSet->getPropertySetInfo() ); + if ( xInfo.is() ) + maProps = xInfo->getProperties(); + } + } + break; + case WINBITS_PROPERTY: + // TODO + break; + } + } + + bool hasNext() + { + return nPropIt < maProps.getLength(); + } + + beans::Property next() + { +/* rtl::OUString propName, propValue; + propName = maProps[ nPropIt ]; + propValue = getProperty( propName, mrKind, false); + nPropIt++; + return std::pair< rtl::OUString, rtl::OUString > propPair( propName, propValue );*/ + return maProps[ nPropIt++ ]; + } + }; +}; + +class EditorRoot : public layoutimpl::LayoutRoot { + Widget *mpParent; + +public: + EditorRoot( const uno::Reference< lang::XMultiServiceFactory >& xFactory, + Widget *pParent ) + : layoutimpl::LayoutRoot( xFactory ), mpParent( pParent ) + { + } + + // generation + virtual layoutimpl::LayoutWidget *create( rtl::OUString id, const rtl::OUString unoName, + long attrbs, uno::Reference< awt::XLayoutContainer > xParent ) + { + if ( unoName.compareToAscii( "dialog" ) == 0 ) + return mpParent; + + // TODO: go through specs to map unoName to a more human-readable label + Widget *pWidget = new Widget( id, mxToolkit, xParent, unoName, attrbs ); + if ( !mxWindow.is() ) + mxWindow = uno::Reference< awt::XWindow >( pWidget->getPeer(), uno::UNO_QUERY ); + + if ( pWidget->mxContainer.is() ) + pWidget->mxContainer->setLayoutUnit( mpParent->mxContainer->getLayoutUnit() ); + + return pWidget; + } +}; + +/* Working with the layout in 1D, as if it was a flat list. */ +namespace FlatLayout +{ + Widget *next( Widget *pWidget ) + { + Widget *pNext; + pNext = pWidget->down(); + if ( pNext ) return pNext; + pNext = pWidget->next(); + if ( pNext ) return pNext; + for ( Widget *pUp = pWidget->up(); pUp != NULL; pUp = pUp->up() ) + if ( (pNext = pUp->next()) != NULL ) + return pNext; + return NULL; + } + +/* + Widget *prev( Widget *pWidget ) + { + Widget *pPrev; + pPrev = pWidget->prev(); + if ( !pPrev ) + return pWidget->up(); + + Widget *pBottom = pPrev->down(); + if ( pBottom ) + { + while ( pBottom->down() || pBottom->next() ) + { + for ( Widget *pNext = pBottom->next(); pNext; pNext = pNext->next() ) + pBottom = pNext; + Widget *pDown = pBottom->down(); + if ( pDown ) + pBottom = pDown; + } + return pBottom; + } + return pPrev; + } +*/ + + bool moveWidget( Widget *pWidget, bool up /*or down*/ ) + { + // Keep child parent&pos for in case of failure + Widget *pOriContainer = pWidget->up(); + unsigned int oriChildPos = pOriContainer->getChildPos( pWidget ); + + // Get parent&sibling before removing it, since relations get cut + Widget *pSibling = up ? pWidget->prev() : pWidget->next(); + Widget *pContainer = pWidget->up(); + if ( !pContainer ) + return false; + + // try to swap with parent or child + // We need to allow for this at least for the root node... + if ( !pSibling ) + { + if ( up ) + { + if ( pContainer->swapWithChild( pWidget ) ) + return true; + } + else + { +// TODO: this is a nice feature, but we probably want to do it explicitely... +#if 0 + if ( pWidget->down() && pWidget->swapWithChild( pWidget->down() ) ) + return true; +#endif + } + } + + pContainer->removeChild( pWidget ); + + // if has up sibling -- append to it, else swap with it + if ( pSibling ) + { + if ( pSibling->addChild( pWidget, up ? 0xffff : 0 ) ) + return true; + + unsigned int childPos = pContainer->getChildPos( pSibling ); + if ( pContainer->addChild( pWidget, childPos + (up ? 0 : 1) ) ) + return true; // should always be succesful + } + // go through parents -- try to get prepended to them + else + { + for ( ; pContainer && pContainer->up(); pContainer = pContainer->up() ) + { + unsigned int childPos = pContainer->up()->getChildPos( pContainer ); + if ( pContainer->up()->addChild( pWidget, childPos + (up ? 0 : 1) ) ) + return true; + } + } + + // failed -- try to get it to its old position + if ( !pOriContainer->addChild( pWidget, oriChildPos ) ) + { + // a parent should never reject a child back. but if it ever + // happens, just kill it, we don't run an orphanate here ;P + delete pWidget; + return true; + } + return false; + } + + // NOTE: root is considered to be number -1 + Widget *get( Widget *pRoot, int nb ) + { + Widget *it; + for ( it = pRoot; it != NULL && nb >= 0; it = next( it ) ) + nb--; + return it; + } + + int get( Widget *pRoot, Widget *pWidget ) + { + int nRet = -1; + Widget *it; + for ( it = pRoot; it != NULL && it != pWidget; it = next( it ) ) + nRet++; + return nRet; + } +}; + +//** PropertiesList widget + +class PropertiesList : public layout::Table +{ + class PropertyEntry + { + friend class PropertiesList; + + /* wrapper between the widget and Any */ + struct AnyWidget + { + DECL_LINK( ApplyPropertyHdl, layout::Window* ); + DECL_LINK( FlagToggledHdl, layout::CheckBox* ); + + AnyWidget( Widget *pWidget, rtl::OUString aPropName, Widget::PropertyKind aPropKind ) + : mpWidget( pWidget ), maPropName( aPropName ), maPropKind( aPropKind ) + { + mpFlag = 0; + mbBlockFlagCallback = false; + bFirstGet = true; + } + + virtual ~AnyWidget() + { + fprintf(stderr, "~AnyWidget\n"); + } + + void save( uno::Any aValue ) + { + mpWidget->setProperty( maPropName, maPropKind, aValue ); + checkProperty(); + } + +void checkProperty() +{ + bool flag = mpWidget->isPropertyTouched( maPropName, maPropKind ); + + if ( mpFlag && mpFlag->IsChecked() != flag ) + { + CheckFlag( flag, true ); + } +} + + void CheckFlag( bool bValue, bool bBlockCallback ) + { + if ( bBlockCallback ) + mbBlockFlagCallback = true; + mpFlag->Check( bValue ); + mbBlockFlagCallback = false; + } + + bool bFirstGet; // HACK + rtl::OUString getValue() + { +// return mpWidget->getOriProperty( maPropName ); + rtl::OUString value; + if ( bFirstGet ) // king of ugliness + value = mpWidget->getProperty( maPropName, maPropKind ); + else + value = mpWidget->getOriginalProperty( maPropName, maPropKind ); + bFirstGet = false; + return value; + } + + // FIXME: wrapper should have a base class for this... + virtual layout::Window *getWindow() = 0; + virtual layout::Container *getContainer() { return NULL; } + + virtual void load() = 0; + virtual void store() = 0; + + Widget *mpWidget; + rtl::OUString maPropName; + Widget::PropertyKind maPropKind; + layout::CheckBox *mpFlag; + bool mbBlockFlagCallback; + }; + + struct AnyEdit : public AnyWidget, layout::HBox + { + layout::Edit *mpEdit; + bool mbMultiLine; + layout::PushButton *mpExpand; + DECL_LINK( ExpandEditHdl, layout::PushButton* ); + + // so we can create widgets (like transforming the Edit into a + // MultiLineEdit) + layout::Window *mpWinParent; + + AnyEdit( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, layout::Window *pWinParent ) + : AnyWidget( pWidget, aPropName, aPropKind ), layout::HBox( 0, false ), mpWinParent( pWinParent ) + { + mpEdit = NULL; + mpExpand = new layout::PushButton( pWinParent, WB_TOGGLE ); + mpExpand->SetToggleHdl( LINK( this, AnyEdit, ExpandEditHdl ) ); + setAsMultiLine( false ); + + load(); + } + + virtual ~AnyEdit() + { + delete mpEdit; + delete mpExpand; + } + + virtual layout::Window *getWindow() + { return NULL; } + virtual layout::Container *getContainer() + { return this; } + + void setAsMultiLine( bool bMultiLine ) + { + Clear(); + XubString text; + if ( mpEdit ) + { + text = mpEdit->GetText(); +printf("Remove mpEdit and expand\n"); + Remove( mpEdit ); + Remove( mpExpand ); + delete mpEdit; + } + + if ( bMultiLine ) + { + mpEdit = new layout::Edit( mpWinParent, WB_BORDER ); + mpExpand->SetText( String::CreateFromAscii( "-" ) ); + } + else + { + mpEdit = new layout::Edit( mpWinParent, WB_BORDER ); + mpExpand->SetText( String::CreateFromAscii( "+" ) ); + } + + mpEdit->SetText( text ); + mpEdit->SetModifyHdl( LINK( this, AnyEdit, ApplyPropertyHdl ) ); + + Add( mpEdit, true, true, 0 ); + Add( mpExpand, false, true, 0 ); + + mbMultiLine = bMultiLine; + } + +#if 0 + // TODO: make this global... We'll likely need it for export... + struct Translate { + const char *ori, *dest; + }; + static rtl::OUString stringReplace( rtl::OUString _str, + Translate *trans ) + { + const sal_Unicode *str = _str.getStr(); + rtl::OUStringBuffer buf; + int i, j, k; + for ( i = 0; i < _str.getLength(); i++ ) + { + for ( j = 0; trans[ j ].ori; j++ ) + { + const char *ori = trans[ j ].ori; + for ( k = 0; ori[ k ] && i+k < _str.getLength(); k++ ) + if ( ori[ k ] != str[ i+k ] ) + break; + if ( !ori[ k ] ) + { + // found substring + buf.appendAscii( trans[ j ].dest ); + i += k; + continue; + } + } + buf.append( str[ i ] ); + } + return buf.makeStringAndClear(); + } +#endif + + virtual void load() + { +#if 0 + // replace end of lines by "\\n" strings + Translate trans[] = { + { "\\", "\\\\" }, { "\n", "\\n" }, { 0, 0 } + }; + rtl::OUString str = anyToString( getValue() ); + str = stringReplace( str, trans ); + SetText( str ); +#endif + mpEdit->SetText( getValue() ); + checkProperty(); + } + + virtual void store() + { +#if 0 + // replace "\\n" strings by actual end of lines + Translate trans[] = { + { "\\\\", "\\" }, { "\\n", "\n" }, + { "\\", "" }, { 0, 0 } + }; + rtl::OUString str = GetText(); + str = stringReplace( str, trans ); + save( uno::makeAny( str ) ); +#endif + save( uno::makeAny( (rtl::OUString) mpEdit->GetText() ) ); + } + }; + + struct AnyInteger : public AnyWidget, NumericField + { + AnyInteger( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, Window *pWinParent ) + : AnyWidget( pWidget, aPropName, aPropKind ), NumericField( pWinParent, WB_SPIN|WB_BORDER ) + { + load(); + SetModifyHdl( LINK( this, AnyInteger, ApplyPropertyHdl ) ); + } + + virtual Window *getWindow() + { return this; } + + virtual void load() + { + OUString text = getValue(); + SetText( text.getStr() ); + checkProperty(); + } + + virtual void store() + { +fprintf(stderr, "store number: %ld\n", rtl::OUString( GetText() ).toInt64()); + save( uno::makeAny( rtl::OUString( GetText() ).toInt64() ) ); + } + }; + + struct AnyFloat : public AnyInteger + { + AnyFloat( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, Window *pWinParent ) + : AnyInteger( pWidget, aPropName, aPropKind, pWinParent ) + {} + + virtual void store() + { + save( uno::makeAny( rtl::OUString( GetText() ).toDouble() ) ); + } + }; + + struct AnyCheckBox : public AnyWidget, layout::CheckBox + { + AnyCheckBox( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, layout::Window *pWinParent ) + : AnyWidget( pWidget, aPropName, aPropKind ), layout::CheckBox( pWinParent ) + { + // adding some whitespaces to make the hit area larger +// SetText( String::CreateFromAscii( "" ) ); + load(); + SetToggleHdl( LINK( this, AnyWidget, ApplyPropertyHdl ) ); + } + + virtual ~AnyCheckBox() + { + fprintf(stderr, "~AnyCheckBox\n"); + } + + virtual layout::Window *getWindow() + { return this; } + + virtual void load() + { +fprintf(stderr, "loading boolean value\n"); + Check( getValue().toInt64() ); + setLabel(); + checkProperty(); + } + + virtual void store() + { + save( uno::makeAny( IsChecked() ) ); + setLabel(); + } + + void setLabel() + { + SetText( String::CreateFromAscii( IsChecked() ? "true" : "false" ) ); + } + }; + + struct AnyListBox : public AnyWidget, layout::ListBox + { + AnyListBox( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, Window *pWinParent ) + : AnyWidget( pWidget, aPropName, aPropKind ), layout::ListBox( pWinParent, WB_DROPDOWN ) + { + SetSelectHdl( LINK( this, AnyWidget, ApplyPropertyHdl ) ); + } + + virtual layout::Window *getWindow() + { return this; } + + virtual void load() + { + SelectEntryPos( getValue().toInt32() ); + checkProperty(); + } + + virtual void store() + { + save( uno::makeAny( (short) GetSelectEntryPos() ) ); + } + }; + + struct AnyAlign : public AnyListBox + { + AnyAlign( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, Window *pWinParent ) + : AnyListBox( pWidget, aPropName, aPropKind, pWinParent ) + { + InsertEntry( XubString::CreateFromAscii( "Left" ) ); + InsertEntry( XubString::CreateFromAscii( "Center" ) ); + InsertEntry( XubString::CreateFromAscii( "Right" ) ); + load(); + } + }; + + /* AnyListBox and AnyComboBox different in that a ComboBox allows the user + to add other options, operating in strings, instead of constants. + (its more like a suggestive AnyEdit) */ + struct AnyComboBox : public AnyWidget, layout::ComboBox + { + AnyComboBox( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, Window *pWinParent ) + : AnyWidget( pWidget, aPropName, aPropKind ), layout::ComboBox( pWinParent, WB_DROPDOWN ) + { + SetModifyHdl( LINK( this, AnyComboBox, ApplyPropertyHdl ) ); + } + + virtual layout::Window *getWindow() + { return this; } + + virtual void load() + { + SetText( getValue() ); + checkProperty(); + } + + virtual void store() + { + save( uno::makeAny( (rtl::OUString) GetText() ) ); + } + }; + + struct AnyFontStyle : public AnyComboBox + { + AnyFontStyle( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, Window *pWinParent ) + : AnyComboBox( pWidget, aPropName, aPropKind, pWinParent ) + { + InsertEntry( XubString::CreateFromAscii( "Bold" ) ); + InsertEntry( XubString::CreateFromAscii( "Italic" ) ); + InsertEntry( XubString::CreateFromAscii( "Bold Italic" ) ); + InsertEntry( XubString::CreateFromAscii( "Fett" ) ); + load(); + } + }; + + layout::FixedText *mpLabel; + layout::CheckBox *mpFlag; + AnyWidget *mpValue; + + public: + PropertyEntry( layout::Window *pWinParent, AnyWidget *pAnyWidget ) + { + mpLabel = new layout::FixedText( pWinParent ); + { + // append ':' to aPropName + rtl::OUStringBuffer buf( pAnyWidget->maPropName ); + buf.append( sal_Unicode (':') ); + mpLabel->SetText( buf.makeStringAndClear() ); + } + mpValue = pAnyWidget; + mpFlag = new layout::CheckBox( pWinParent ); + mpFlag->SetToggleHdl( LINK( mpValue, AnyWidget, FlagToggledHdl ) ); + mpValue->mpFlag = mpFlag; + } + + ~PropertyEntry() + { + fprintf(stderr, "REMOVING label, flag and value\n"); + delete mpLabel; + delete mpFlag; + delete mpValue; + } + + // Use this factory rather than the constructor -- check for NULL + static PropertyEntry *construct( Widget *pWidget, rtl::OUString aPropName, + Widget::PropertyKind aPropKind, sal_uInt16 nType, + layout::Window *pWinParent ) + { + AnyWidget *pAnyWidget; + switch (nType) { + case uno::TypeClass_STRING: + if ( aPropName.compareToAscii( "FontStyleName" ) == 0 ) + { + pAnyWidget = new AnyFontStyle( pWidget, aPropName, aPropKind, pWinParent ); + break; + } + pAnyWidget = new AnyEdit( pWidget, aPropName, aPropKind, pWinParent ); + break; + case uno::TypeClass_SHORT: + if ( aPropName.compareToAscii( "Align" ) == 0 ) + { + pAnyWidget = new AnyAlign( pWidget, aPropName, aPropKind, pWinParent ); + break; + } + // otherwise, treat as any other number... + case uno::TypeClass_LONG: + case uno::TypeClass_UNSIGNED_LONG: + pAnyWidget = new AnyInteger( pWidget, aPropName, aPropKind, pWinParent ); + break; + case uno::TypeClass_FLOAT: + case uno::TypeClass_DOUBLE: + pAnyWidget = new AnyFloat( pWidget, aPropName, aPropKind, pWinParent ); + break; + case uno::TypeClass_BOOLEAN: + pAnyWidget = new AnyCheckBox( pWidget, aPropName, aPropKind, pWinParent ); + break; + default: + return NULL; + } + return new PropertyEntry( pWinParent, pAnyWidget ); + } + }; + + layout::Window *mpParentWindow; + + std::list< PropertyEntry* > maPropertiesList; + layout::FixedLine *mpSeparator; + + // some properties are obscure, or simply don't make sense in this + // context. Let's just ignore them. + // Maybe we could offer them in an expander or something... + static bool toIgnore( rtl::OUString prop ) + { + // binary search -- keep the list sorted alphabetically + static char const *toIgnoreList[] = { + "DefaultControl", "FocusOnClick", "FontCharWidth", "FontCharset", + "FontEmphasisMark", "FontFamily", "FontHeight", "FontKerning", "FontName", + "FontOrientation", "FontPitch", "FontRelief", "FontSlant", "FontStrikeout", + "FontType", "FontWordLineMode", "HelpText", "HelpURL", "MultiLine", + "Printable", "Repeat", "RepeatDelay", "Tabstop" + }; + +#if 0 + // checks list sanity -- enable this when you add some entries... + for ( unsigned int i = 1; i < sizeof( toIgnoreList )/sizeof( char * ); i++ ) + { + if ( strcmp(toIgnoreList[i-1], toIgnoreList[i]) >= 0 ) + { + printf("ignore list not ordered properly: " + "'%s' should come before '%s'\n", + toIgnoreList[i], toIgnoreList[i-1]); + exit(-1); + } + } +#endif + + int min = 0, max = sizeof( toIgnoreList )/sizeof( char * ) - 1, mid, cmp; + do { + mid = min + (max - min)/2; + cmp = prop.compareToAscii( toIgnoreList[ mid ] ); + if ( cmp > 0 ) + min = mid+1; + else if ( cmp < 0 ) + max = mid-1; + else + return true; + } while ( min <= max ); + return false; + } + +public: + PropertiesList( layout::Dialog *dialog ) + : layout::Table( dialog, "properties-box" ) + , mpParentWindow( dialog ), mpSeparator( 0 ) + { + } + + ~PropertiesList() + { + clear(); + } + +private: + // auxiliary, add properties from the peer to the list + void addProperties( Widget *pWidget, Widget::PropertyKind rKind ) + { + Widget::PropertyIterator it( pWidget, rKind ); + while ( it.hasNext() ) + { + beans::Property prop = it.next(); + rtl::OUString name( prop.Name ); + if ( toIgnore( name ) ) + continue; + sal_uInt16 type = prop.Type.getTypeClass(); + + PropertyEntry *propEntry = PropertyEntry::construct( + pWidget, name, rKind, type, mpParentWindow ); + + if ( propEntry ) + { + Add( propEntry->mpLabel, false, false ); + // HACK: one of these will return Null... + Add( propEntry->mpValue->getWindow(), true, false ); + Add( propEntry->mpValue->getContainer(), true, false ); + + Add( propEntry->mpFlag, false, false ); + maPropertiesList.push_back( propEntry ); + } + } + } + +public: + void selectedWidget( Widget *pWidget ) + { + clear(); + if ( !pWidget ) + return; + + addProperties( pWidget, Widget::CONTAINER_PROPERTY ); + + mpSeparator = new layout::FixedLine( mpParentWindow ); + // TODO: we may want to have to separate list widgets here... + Add( mpSeparator, false, false, 3, 1 ); + + addProperties( pWidget, Widget::WINDOW_PROPERTY ); + + ShowAll( true ); + } + + void clear() + { + Container::Clear(); + + for ( std::list< PropertyEntry* >::iterator it = maPropertiesList.begin(); + it != maPropertiesList.end(); it++) + delete *it; + maPropertiesList.clear(); + + delete mpSeparator; + mpSeparator = NULL; + } +}; + +IMPL_LINK( PropertiesList::PropertyEntry::AnyWidget, ApplyPropertyHdl, layout::Window *, pWin ) +{ + (void) pWin; + store(); + return 0; +} + +IMPL_LINK( PropertiesList::PropertyEntry::AnyWidget, FlagToggledHdl, layout::CheckBox *, pCheck ) +{ +fprintf(stderr, "Property flag pressed -- is: %d\n", pCheck->IsChecked()); + if ( !mbBlockFlagCallback ) + { + bool checked = pCheck->IsChecked(); + if ( !checked ) // revert +{ +fprintf(stderr, "revert\n"); + load(); +} + else + { +fprintf(stderr, "user can't dirty the flag!\n"); + // User can't flag the property as dirty + // Actually, we may want to allow the designer to force a property to be stored. + // Could be useful when the default value of some new property wasn't yet decided... + CheckFlag( false, true ); + } + } +else +fprintf(stderr, "Property flag pressed -- BLOCKED\n"); + return 0; +} + +IMPL_LINK( PropertiesList::PropertyEntry::AnyEdit, ExpandEditHdl, layout::PushButton *, pBtn ) +{ + setAsMultiLine( pBtn->IsChecked() ); + return 0; +} + +//** SortListBox auxiliary widget + +class SortListBox +{ // For a manual sort ListBox; asks for a ListBox and Up/Down/Remove + // buttons to wrap +DECL_LINK( ItemSelectedHdl, layout::ListBox* ); +DECL_LINK( UpPressedHdl, layout::Button* ); +DECL_LINK( DownPressedHdl, layout::Button* ); +DECL_LINK( RemovePressedHdl, layout::Button* ); +layout::PushButton *mpUpButton, *mpDownButton, *mpRemoveButton; + +protected: +layout::ListBox *mpListBox; + + virtual void upPressed( USHORT nPos ) + { + XubString str = mpListBox->GetSelectEntry(); + mpListBox->RemoveEntry( nPos ); + nPos = mpListBox->InsertEntry( str, nPos-1 ); + mpListBox->SelectEntryPos( nPos ); + } + + virtual void downPressed( USHORT nPos ) + { + XubString str = mpListBox->GetSelectEntry(); + mpListBox->RemoveEntry( nPos ); + nPos = mpListBox->InsertEntry( str, nPos+1 ); + mpListBox->SelectEntryPos( nPos ); + } + + virtual void removePressed( USHORT nPos ) + { + mpListBox->RemoveEntry( nPos ); + } + + virtual void itemSelected( USHORT nPos ) + { + // if we had some XLayoutContainer::canAdd() or maxChildren() function + // we could make a function to check if we can move selected and enable/ + // /disable the move buttons as appropriate + + if ( nPos == LISTBOX_ENTRY_NOTFOUND ) + { + mpUpButton->Disable(); + mpDownButton->Disable(); + mpRemoveButton->Disable(); + } + else + { + mpUpButton->Enable(); + mpDownButton->Enable(); + mpRemoveButton->Enable(); + } + } + +public: + SortListBox( layout::ListBox *pListBox, layout::PushButton *pUpButton, layout::PushButton *pDownButton, + layout::PushButton *pRemoveButton ) + : mpUpButton( pUpButton), mpDownButton( pDownButton), mpRemoveButton( pRemoveButton ), + mpListBox( pListBox ) + { + mpListBox->SetSelectHdl( LINK( this, SortListBox, ItemSelectedHdl ) ); + + mpUpButton->SetModeImage( layout::Image ( "res/commandimagelist/lc_moveup.png" ) ); + mpUpButton->SetImageAlign( IMAGEALIGN_LEFT ); + mpUpButton->SetClickHdl( LINK( this, SortListBox, UpPressedHdl ) ); + + mpDownButton->SetModeImage( layout::Image ( "res/commandimagelist/lc_movedown.png" ) ); + mpDownButton->SetImageAlign( IMAGEALIGN_LEFT ); + mpDownButton->SetClickHdl( LINK( this, SortListBox, DownPressedHdl ) ); + + // "res/commandimagelist/lch_delete.png", "res/commandimagelist/lc_delete.png" + mpRemoveButton->SetModeImage( layout::Image ( "res/commandimagelist/sc_closedoc.png" ) ); + mpRemoveButton->SetImageAlign( IMAGEALIGN_LEFT ); + mpRemoveButton->SetClickHdl( LINK( this, SortListBox, RemovePressedHdl ) ); + + // fire an un-select event + itemSelected( LISTBOX_ENTRY_NOTFOUND ); + } + + ~SortListBox() + { + delete mpListBox; + delete mpUpButton; + delete mpDownButton; + delete mpRemoveButton; + } +}; + +IMPL_LINK( SortListBox, UpPressedHdl, layout::Button *, pBtn ) +{ + (void) pBtn; + USHORT pos = mpListBox->GetSelectEntryPos(); + if ( pos > 0 && pos != LISTBOX_ENTRY_NOTFOUND ) + upPressed( pos ); + return 0; +} + +IMPL_LINK( SortListBox, DownPressedHdl, layout::Button *, pBtn ) +{ + (void) pBtn; + USHORT pos = mpListBox->GetSelectEntryPos(); + if ( pos < mpListBox->GetEntryCount() && pos != LISTBOX_ENTRY_NOTFOUND ) + downPressed( pos ); + return 0; +} + +IMPL_LINK( SortListBox, RemovePressedHdl, layout::Button *, pBtn ) +{ + (void) pBtn; + USHORT pos = mpListBox->GetSelectEntryPos(); + if ( pos != LISTBOX_ENTRY_NOTFOUND ) + removePressed( pos ); + return 0; +} + +IMPL_LINK( SortListBox, ItemSelectedHdl, layout::ListBox *, pList ) +{ + (void) pList; + USHORT pos = mpListBox->GetSelectEntryPos(); + itemSelected( pos ); + return 0; +} + +//** LayoutTree widget + +class LayoutTree : public SortListBox +{ +public: + struct Listener + { + virtual void widgetSelected( Widget *pWidget ) = 0; + }; + +private: + Listener *mpListener; + +public: + Widget *mpRootWidget; + + LayoutTree( layout::Dialog *dialog ) + : SortListBox( new layout::ListBox( dialog, "layout-tree" ), + new layout::PushButton( dialog, "layout-up-button" ), + new layout::PushButton( dialog, "layout-down-button" ), + new layout::PushButton( dialog, "layout-remove-button" ) ) + { + layout::PeerHandle handle = dialog->GetPeerHandle( "preview-box" ); + uno::Reference< awt::XLayoutConstrains > xWidget( handle, uno::UNO_QUERY ); + mpRootWidget = new Widget( xWidget, "root" ); + } + + ~LayoutTree() + { + delete mpRootWidget; + } + + Widget *getWidget( int nPos ) + { + if ( nPos != LISTBOX_ENTRY_NOTFOUND ) + return FlatLayout::get( mpRootWidget, nPos ); + return NULL; + } + + Widget *getSelectedWidget() + { + Widget *pWidget = getWidget( mpListBox->GetSelectEntryPos() ); + if ( !pWidget ) // return root, if none selected + pWidget = mpRootWidget; + return pWidget; + } + + void selectWidget( Widget *pWidget ) + { + int pos = FlatLayout::get( mpRootWidget, pWidget ); + if ( pos == -1 ) + // if asked to select hidden root, select visible root + pos = 0; + mpListBox->SelectEntryPos( pos ); + } + + void rebuild() + { + struct inner + { + // pads a string with whitespaces + static rtl::OUString padString( rtl::OUString name, int depth ) + { + rtl::OStringBuffer aBuf( depth * 4 + name.getLength() + 2 ); + for (int i = 0; i < depth; i++) + aBuf.append( " " ); + aBuf.append( rtl::OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) ); + return rtl::OUString( aBuf.getStr(), aBuf.getLength(), + RTL_TEXTENCODING_UTF8 ); + } + }; + + mpListBox->Clear(); + for ( Widget *i = FlatLayout::next( mpRootWidget ); i; i = FlatLayout::next( i ) ) + mpListBox->InsertEntry( inner::padString( i->getLabel(), i->getDepth()-1 ) ); + + // any selection, no longer is. ListBox doesn't fire the event on this case; + // force it. + itemSelected( LISTBOX_ENTRY_NOTFOUND ); + } + + void setListener( Listener *pListener ) + { mpListener = pListener; } + + // print in XML format... + +static rtl::OUString toXMLNaming (const rtl::OUString &string) +{ + rtl::OUStringBuffer buffer (string.getLength()); + sal_Unicode *str = string.pData->buffer; + for (int i = 0; i < string.getLength(); i++) { + if ( str[i] >= 'A' && str[i] <= 'Z' ) + { + if ( i > 0 ) + buffer.append ((sal_Unicode) '-'); + buffer.append ((sal_Unicode) (str[i] - 'A' + 'a')); + } + else + buffer.append ((sal_Unicode) str[i]); + } + + return buffer.makeStringAndClear(); +} + + void print() + { + printf("\t\tExport:\n"); + printf("\n" + "\n"); + + for ( Widget *i = FlatLayout::next( mpRootWidget ); i; i = FlatLayout::next( i ) ) + { + for ( int d = i->getDepth(); d > 0; d-- ) + printf(" "); + printf("<%s ", OUSTRING_CSTR( i->getUnoName() ) ); + + for ( int kind = 0; kind < 2; kind++ ) + { + Widget::PropertyKind wKind = kind == 0 ? Widget::WINDOW_PROPERTY + : Widget::CONTAINER_PROPERTY; + Widget::PropertyIterator it( i, wKind ); + while ( it.hasNext() ) + { + beans::Property prop = it.next(); + if ( !i->isPropertyTouched( prop.Name, wKind ) ) + continue; + + rtl::OUString value = i->getProperty( prop.Name, wKind ); + if ( prop.Type.getTypeClass() == uno::TypeClass_BOOLEAN ) + { + if ( value.compareToAscii( "0" ) ) + value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("false") ); + else + value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("true") ); + } + + if ( value.getLength() > 0 ) + printf("%s%s=\"%s\" ", + kind == 0 ? "" : "cnt:", + OUSTRING_CSTR( toXMLNaming( prop.Name ) ), OUSTRING_CSTR( value ) + ); + + } + } + printf("/>\n"); + } + printf("\n"); + } + +protected: + virtual void upPressed( USHORT nPos ) + { + Widget *pWidget = getWidget( nPos ); + if ( FlatLayout::moveWidget( pWidget, true ) ) + rebuild(); + selectWidget( pWidget ); + } + + virtual void downPressed( USHORT nPos ) + { + Widget *pWidget = getWidget( nPos ); + if ( FlatLayout::moveWidget( pWidget, false ) ) + rebuild(); + selectWidget( pWidget ); + } + + virtual void removePressed( USHORT nPos ) + { + Widget *pWidget = getWidget( nPos ); + if ( pWidget ) + { + pWidget->up()->removeChild( pWidget ); + delete pWidget; + rebuild(); + } + } + + virtual void itemSelected( USHORT nPos ) + { + mpListener->widgetSelected( getWidget( nPos ) ); + SortListBox::itemSelected( nPos ); + } +}; + +//** EditorImpl + +class EditorImpl : public LayoutTree::Listener +{ + void createWidget( const char *unoName ); + + PropertiesList *mpPropertiesList; + LayoutTree *mpLayoutTree; + + layout::PushButton *pImportButton, *pExportButton; +#ifdef FILEDLG + FileDialog *pImportDialog; +#endif + DECL_LINK( ImportButtonHdl, layout::PushButton* ); + DECL_LINK( ExportButtonHdl, layout::PushButton* ); +#ifdef FILEDLG + DECL_LINK( ImportDialogHdl, FileDialog* ); +#endif + + // framework stuff + uno::Reference< lang::XMultiServiceFactory > mxFactory; + uno::Reference< awt::XToolkit > mxToolkit; + uno::Reference< awt::XWindow > mxToplevel; + + virtual void widgetSelected( Widget *pWidget ); + DECL_LINK( CreateWidgetHdl, layout::Button* ); + + std::list< layout::PushButton *> maCreateButtons; + +public: + + EditorImpl( layout::Dialog *dialog, + // we should probable open this channel (or whatever its called) ourselves + uno::Reference< lang::XMultiServiceFactory > xMSF ); + ~EditorImpl(); + + void loadFile( const rtl::OUString &aTestFile ); +}; + +EditorImpl::EditorImpl( layout::Dialog *dialog, + uno::Reference< lang::XMultiServiceFactory > xFactory ) + : mxFactory( xFactory ) + , mxToplevel( dialog->GetPeerHandle( "dialog" ), uno::UNO_QUERY ) + // FIXME: any of these should work + //dialog->getContext()->getRoot(), uno::UNO_QUERY ) + // dialog->GetPeer(), uno::UNO_QUERY ) +{ +fprintf (stderr, "EditorImpl()\n"); + // framework + mxToolkit = uno::Reference< awt::XToolkit >( + mxFactory->createInstance( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.Toolkit" ) ) ), + uno::UNO_QUERY ); + assert( mxToolkit.is() ); + + // custom widgets +fprintf (stderr, "custom widgets\n"); + mpPropertiesList = new PropertiesList( dialog ); + + mpLayoutTree = new LayoutTree( dialog ); + mpLayoutTree->setListener( this ); + +/* if ( xImport.is() ) + mpLayoutTree->getWidget( -1 )->addChild( new Widget( xImport, "import" ) );*/ + + // create buttons + layout::Container aWidgets( dialog, "create-widget" ); + layout::Container aContainers( dialog, "create-container" ); + for ( int i = 0; i < WIDGETS_SPECS_LEN; i++ ) + { + layout::PushButton *pBtn = new layout::PushButton( (layout::Window *) dialog ); + pBtn->SetText( rtl::OUString::createFromAscii( WIDGETS_SPECS[ i ].pLabel ) ); + pBtn->SetClickHdl( LINK( this, EditorImpl, CreateWidgetHdl ) ); + if ( WIDGETS_SPECS[ i ].pIconName != NULL ) + { + rtl::OString aPath ("res/commandimagelist/"); + aPath += WIDGETS_SPECS[ i ].pIconName; + layout::Image aImg( aPath ); + pBtn->SetModeImage( aImg ); + pBtn->SetImageAlign( IMAGEALIGN_LEFT ); + } + pBtn->Show(); + maCreateButtons.push_back( pBtn ); + layout::Container *pBox = WIDGETS_SPECS[ i ].bIsContainer ? &aContainers : &aWidgets; + pBox->Add( pBtn ); + } + +#ifdef FILEDLG +fprintf(stderr,"creating file dialog\n"); + pImportDialog = new FileDialog( NULL/*(layout::Window *) dialog*/, 0 ); +fprintf(stderr,"connecting it\n"); + pImportDialog->SetFileSelectHdl( LINK( this, EditorImpl, ImportDialogHdl ) ); +fprintf(stderr,"done file dialog\n"); +#endif + +/* pImportButton = new layout::PushButton( dialog, "import-button" ); + pImportButton->SetClickHdl( LINK( this, EditorImpl, ImportButtonHdl ) );*/ + pExportButton = new layout::PushButton( dialog, "export-button" ); + pExportButton->SetClickHdl( LINK( this, EditorImpl, ExportButtonHdl ) ); +} + +EditorImpl::~EditorImpl() +{ + delete mpPropertiesList; + delete mpLayoutTree; + for ( std::list< layout::PushButton * >::const_iterator i = maCreateButtons.begin(); + i != maCreateButtons.end(); i++) + delete *i; + delete pImportButton; + delete pExportButton; +#ifdef FILEDLG + delete pImportDialog; +#endif +} + +void EditorImpl::loadFile( const rtl::OUString &aTestFile ) +{ +fprintf( stderr, "TEST: layout instance\n" ); + uno::Reference< awt::XLayoutRoot > xRoot + ( new EditorRoot( mxFactory, mpLayoutTree->mpRootWidget ) ); + +/* +mxMSF->createInstance + ( ::rtl::OUString::createFromAscii( "com.sun.star.awt.Layout" ) ), + uno::UNO_QUERY ); +*/ + if ( !xRoot.is() ) + { + throw uno::RuntimeException( + OUString( RTL_CONSTASCII_USTRINGPARAM("could not create awt Layout component!") ), + uno::Reference< uno::XInterface >() ); + } + +fprintf( stderr, "TEST: initing root\n" ); + uno::Reference< lang::XInitialization > xInit( xRoot, uno::UNO_QUERY ); + if ( !xInit.is() ) + { + throw uno::RuntimeException( + OUString( RTL_CONSTASCII_USTRINGPARAM("Layout has no XInitialization!") ), + uno::Reference< uno::XInterface >() ); + } + +fprintf( stderr, "TEST: running parser\n" ); + uno::Sequence< uno::Any > aParams( 1 ); + aParams[0] <<= aTestFile; +fprintf( stderr, "TEST: do it\n" ); + xInit->initialize( aParams ); +fprintf( stderr, "TEST: file loaded\n" ); + + mpLayoutTree->rebuild(); +} + +void EditorImpl::createWidget( const char *name ) +{ + Widget *pWidget = mpLayoutTree->getSelectedWidget(); + + Widget *pChild = new Widget( rtl::OUString(), mxToolkit, uno::Reference< awt::XLayoutContainer >( mxToplevel, uno::UNO_QUERY ), rtl::OUString::createFromAscii( name ), awt::WindowAttribute::SHOW ); + if ( !pWidget->addChild( pChild ) ) + { + delete pChild; + // we may want to popup an error message + } + else + { + mpLayoutTree->rebuild(); + mpLayoutTree->selectWidget( pWidget ); + } +} + +void EditorImpl::widgetSelected( Widget *pWidget ) +{ + // we know can't add widget to a non-container, so let's disable the create + // buttons then. Would be nice to have a method to check if a container is + // full as well... + if ( !pWidget || pWidget->isContainer() ) + { + for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin(); + it != maCreateButtons.end(); it++) + (*it)->Enable(); + } + else + { + for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin(); + it != maCreateButtons.end(); it++) + (*it)->Disable(); + } + + mpPropertiesList->selectedWidget( pWidget ); +} + +IMPL_LINK( EditorImpl, CreateWidgetHdl, layout::Button *, pBtn ) +{ + int i = 0; + for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin(); + it != maCreateButtons.end(); it++, i++ ) + { + if ( pBtn == *it ) + break; + } + assert( i < WIDGETS_SPECS_LEN ); + createWidget( WIDGETS_SPECS[i].pName ); + return 0; +} + +IMPL_LINK( EditorImpl, ImportButtonHdl, layout::PushButton *, pBtn ) +{ + (void) pBtn; +fprintf(stderr, "IMPORT!\n"); +#ifdef FILEDLG + pImportDialog->Execute(); +#endif + + return 0; +} + +#ifdef FILEDLG +IMPL_LINK( EditorImpl, ImportDialogHdl, FileDialog *, pDialog ) +{ +//fprintf(stderr, "Executing import dialog!\n"); + + UniString path = pDialog->GetPath(); +fprintf(stderr, "got import file: %s\n",rtl::OUStringToOString( path, RTL_TEXTENCODING_ASCII_US ).getStr() ); + + return 0; +} +#endif + +IMPL_LINK( EditorImpl, ExportButtonHdl, layout::PushButton *, pBtn ) +{ + (void) pBtn; + mpLayoutTree->print(); + return 0; +} + +//** Editor, the Dialog + +Editor::Editor( uno::Reference< lang::XMultiServiceFactory > xFactory, + rtl::OUString aFile ) + : layout::Dialog( (Window*) (NULL), "editor.xml", "dialog" ) + , mpImpl( new EditorImpl( this, xFactory ) ) +{ + if ( aFile.getLength() ) + mpImpl->loadFile( aFile ); + + // parent: + FreeResource(); +} + +Editor::~Editor() +{ + delete mpImpl; +} -- cgit