| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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; |
| } |