summaryrefslogtreecommitdiff
path: root/toolkit/workben/layout/editor.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/workben/layout/editor.cxx')
-rw-r--r--toolkit/workben/layout/editor.cxx1944
1 files changed, 1944 insertions, 0 deletions
diff --git a/toolkit/workben/layout/editor.cxx b/toolkit/workben/layout/editor.cxx
new file mode 100644
index 000000000000..699c103d2ee8
--- /dev/null
+++ b/toolkit/workben/layout/editor.cxx
@@ -0,0 +1,1944 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include "editor.hxx"
+
+#undef NDEBUG
+
+/*
+#include <stdio.h>
+#include <string.h>
+*/
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <list>
+#include <vector>
+
+#include <com/sun/star/awt/WindowAttribute.hpp>
+#include <com/sun/star/awt/XLayoutConstrains.hpp>
+#include <com/sun/star/awt/XLayoutContainer.hpp>
+#include <com/sun/star/awt/XToolkit.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <toolkit/helper/property.hxx>
+#include <vcl/lstbox.h>
+
+using namespace layout::css;
+
+using rtl::OUString;
+
+// FIXME:
+//#define FILEDLG
+
+#include <layout/core/helper.hxx>
+#include <layout/core/root.hxx>
+#include <layout/core/helper.hxx>
+
+// 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);
+
+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<rtl::OUString>();
+ case uno::TypeClass_CONSTANT:
+ return rtl::OUString::valueOf (value.get<sal_Int32>());
+ case uno::TypeClass_LONG:
+ return rtl::OUString::valueOf (value.get<sal_Int64>());
+ case uno::TypeClass_SHORT:
+ // FIXME: seems broken
+ return rtl::OUString::valueOf ((sal_Int32) value.get<short>());
+
+ case uno::TypeClass_FLOAT:
+ return rtl::OUString::valueOf (value.get<float>());
+ case uno::TypeClass_DOUBLE:
+ return rtl::OUString::valueOf (value.get<double>());
+
+ case uno::TypeClass_BOOLEAN:
+ {
+ bool val = value.get<sal_Bool>();
+ 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 sal::static_int_cast<long>(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 );
+ OSL_ASSERT( xContainer.is() );
+ xParent = uno::Reference< awt::XLayoutContainer >( xContainer->getParent(), uno::UNO_QUERY );
+ }
+
+ mxWidget = WidgetFactory::createWidget( xToolkit, xParent, unoName, nAttrbs );
+ OSL_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 ) );
+#if DEBUG_PRINT
+ fprintf(stderr, "original property: %s = %s\n", OUSTRING_CSTR(name), OUSTRING_CSTR(value));
+#endif
+ 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 );
+
+ OSL_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 );
+ try {
+ rtl::OUString value( pChild->getProperty( name, CONTAINER_PROPERTY ) );
+ std::pair< rtl::OUString, rtl::OUString > pair( name, value );
+ pChild->maOriChildProps.push_back( pair );
+ } catch ( beans::UnknownPropertyException &rEx ) {
+ fprintf (stderr, "ERROR: widget reports that it has a property it cannot return: '%s' this normally means that someone screwed up their PROPERTY_SET_INFO macro usage.\n",
+ rtl::OUStringToOString (rEx.Message, RTL_TEXTENCODING_UTF8).getStr());
+ }
+ }
+ }
+
+ 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;
+#if DEBUG_PRINT
+ fprintf(stderr, "Serious error: property '%s' not found\n", OUSTRING_CSTR(propName));
+#endif
+ 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;
+#if DEBUG_PRINT
+ fprintf(stderr, "is property '%s' touched? %s (%s vs %s)\n", OUSTRING_CSTR(propName), isTouched ? "yes" : "no", OUSTRING_CSTR(oriValue), OUSTRING_CSTR(newValue));
+#endif
+ return isTouched;
+ }
+
+ using LayoutWidget::setProperty;
+
+ 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()
+ {
+#if DEBUG_PRINT
+ fprintf(stderr, "~AnyWidget\n");
+#endif
+ }
+
+ void save( uno::Any aValue )
+ {
+ mpWidget->setProperty( maPropName, maPropKind, aValue );
+ checkProperty();
+ }
+
+ void checkProperty()
+ {
+ bool flag = mpWidget->isPropertyTouched( maPropName, maPropKind );
+
+ if ( mpFlag && mpFlag->IsChecked() != (BOOL)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()
+ {
+#if DEBUG_PRINT
+ fprintf(stderr, "store number: %ld\n", rtl::OUString( GetText() ).toInt64());
+#endif
+ 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()
+ {
+#if DEBUG_PRINT
+ fprintf(stderr, "~AnyCheckBox\n");
+#endif
+ }
+
+ virtual layout::Window *getWindow()
+ { return this; }
+
+ virtual void load()
+ {
+#if DEBUG_PRINT
+ fprintf(stderr, "loading boolean value\n");
+#endif
+ Check( getValue().toInt64() != 0 );
+ 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( sal::static_int_cast< USHORT >( 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()
+ {
+#if DEBUG_PRINT
+ fprintf(stderr, "REMOVING label, flag and value\n");
+#endif
+ 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 = static_cast< sal_uInt16 >( 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()
+ {
+ ///FIXME: crash
+ 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 )
+{
+#if DEBUG_PRINT
+ fprintf(stderr, "Property flag pressed -- is: %d\n", pCheck->IsChecked());
+#endif
+ if ( !mbBlockFlagCallback )
+ {
+ bool checked = pCheck->IsChecked();
+ if ( !checked ) // revert
+ {
+#if DEBUG_PRINT
+ fprintf(stderr, "revert\n");
+#endif
+ load();
+ }
+ else
+ {
+#if DEBUG_PRINT
+ fprintf(stderr, "user can't dirty the flag!\n");
+#endif
+ // 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 );
+ }
+ }
+#if DEBUG_PRINT
+ else
+ fprintf(stderr, "Property flag pressed -- BLOCKED\n");
+#endif
+ 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 );
+ }
+
+ virtual ~SortListBox();
+};
+
+SortListBox::~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" );
+ }
+
+ virtual ~LayoutTree();
+
+ 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( sal::static_int_cast< USHORT >( 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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<dialog xmlns=\"http://openoffice.org/2007/layout\"\n"
+ " xmlns:cnt=\"http://openoffice.org/2007/layout/container\"\n"
+ " id=\"dialog\" title=\"Unnamed\" sizeable=\"true\" >\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("</dialog>\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 );
+ }
+};
+
+LayoutTree::~LayoutTree()
+{
+ delete mpRootWidget;
+}
+
+//** 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 );
+ virtual ~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 )
+{
+#if DEBUG_PRINT
+ fprintf (stderr, "EditorImpl()\n");
+#endif
+ // framework
+ mxToolkit = uno::Reference< awt::XToolkit >(
+ mxFactory->createInstance(
+ rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.Toolkit" ) ) ),
+ uno::UNO_QUERY );
+ OSL_ASSERT( mxToolkit.is() );
+
+ // custom widgets
+#if DEBUG_PRINT
+ fprintf (stderr, "custom widgets\n");
+#endif
+ 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 >() );
+ }
+
+#if DEBUG_PRINT
+ fprintf( stderr, "TEST: initing root\n" );
+#endif
+
+ 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 >() );
+ }
+
+#if DEBUG_PRINT
+ fprintf( stderr, "TEST: running parser\n" );
+#endif
+ uno::Sequence< uno::Any > aParams( 1 );
+ aParams[0] <<= aTestFile;
+#if DEBUG_PRINT
+ fprintf( stderr, "TEST: do it\n" );
+#endif
+ xInit->initialize( aParams );
+#if DEBUG_PRINT
+ fprintf( stderr, "TEST: file loaded\n" );
+#endif
+
+ 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;
+ }
+ OSL_ASSERT( i < WIDGETS_SPECS_LEN );
+ createWidget( WIDGETS_SPECS[i].pName );
+ return 0;
+}
+
+IMPL_LINK( EditorImpl, ImportButtonHdl, layout::PushButton *, pBtn )
+{
+ (void) pBtn;
+#if DEBUG_PRINT
+ fprintf(stderr, "IMPORT!\n");
+#endif
+#ifdef FILEDLG
+ pImportDialog->Execute();
+#endif
+
+ return 0;
+}
+
+#ifdef FILEDLG
+IMPL_LINK( EditorImpl, ImportDialogHdl, FileDialog *, pDialog )
+{
+ UniString path = pDialog->GetPath();
+//fprintf(stderr, "Executing import dialog!\n");
+
+#if DEBUG_PRINT
+ fprintf(stderr, "got import file: %s\n",rtl::OUStringToOString( path, RTL_TEXTENCODING_ASCII_US ).getStr() );
+#endif
+
+ 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;
+}