/************************************************************** * * 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 #include */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace layout::css; using rtl::OUString; // FIXME: //#define FILEDLG #include #include #include // 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(); case uno::TypeClass_CONSTANT: return rtl::OUString::valueOf (value.get()); case uno::TypeClass_LONG: return rtl::OUString::valueOf (value.get()); case uno::TypeClass_SHORT: // FIXME: seems broken return rtl::OUString::valueOf ((sal_Int32) value.get()); case uno::TypeClass_FLOAT: return rtl::OUString::valueOf (value.get()); case uno::TypeClass_DOUBLE: return rtl::OUString::valueOf (value.get()); case uno::TypeClass_BOOLEAN: { bool val = value.get(); return rtl::OUString( val ? "1" : "0", 1, RTL_TEXTENCODING_ASCII_US ); /* if ( val ) return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "true" ) ); else return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "false" ) );*/ } default: break; } } catch(...) {} return rtl::OUString(); } static inline long anyToNatural (uno::Any value) { return sal::static_int_cast(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 explicitly... #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 successful } // 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("\n" "\n"); for ( Widget *i = FlatLayout::next( mpRootWidget ); i; i = FlatLayout::next( i ) ) { for ( int d = i->getDepth(); d > 0; d-- ) printf(" "); printf("<%s ", OUSTRING_CSTR( i->getUnoName() ) ); for ( int kind = 0; kind < 2; kind++ ) { Widget::PropertyKind wKind = kind == 0 ? Widget::WINDOW_PROPERTY : Widget::CONTAINER_PROPERTY; Widget::PropertyIterator it( i, wKind ); while ( it.hasNext() ) { beans::Property prop = it.next(); if ( !i->isPropertyTouched( prop.Name, wKind ) ) continue; rtl::OUString value = i->getProperty( prop.Name, wKind ); if ( prop.Type.getTypeClass() == uno::TypeClass_BOOLEAN ) { if ( value.compareToAscii( "0" ) ) value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("false") ); else value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("true") ); } if ( value.getLength() > 0 ) printf("%s%s=\"%s\" ", kind == 0 ? "" : "cnt:", OUSTRING_CSTR( toXMLNaming( prop.Name ) ), OUSTRING_CSTR( value ) ); } } printf("/>\n"); } printf("\n"); } protected: virtual void upPressed( USHORT nPos ) { Widget *pWidget = getWidget( nPos ); if ( FlatLayout::moveWidget( pWidget, true ) ) rebuild(); selectWidget( pWidget ); } virtual void downPressed( USHORT nPos ) { Widget *pWidget = getWidget( nPos ); if ( FlatLayout::moveWidget( pWidget, false ) ) rebuild(); selectWidget( pWidget ); } virtual void removePressed( USHORT nPos ) { Widget *pWidget = getWidget( nPos ); if ( pWidget ) { pWidget->up()->removeChild( pWidget ); delete pWidget; rebuild(); } } virtual void itemSelected( USHORT nPos ) { mpListener->widgetSelected( getWidget( nPos ) ); SortListBox::itemSelected( nPos ); } }; 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; }