merged tag ooo/DEV300_m102
[LibreOffice.git] / toolkit / workben / layout / editor.cxx
blob699c103d2ee892df325a1d3bffa13d70db1e5db1
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 #include "editor.hxx"
30 #undef NDEBUG
33 #include <stdio.h>
34 #include <string.h>
37 #include <cassert>
38 #include <cstdio>
39 #include <cstring>
40 #include <list>
41 #include <vector>
43 #include <com/sun/star/awt/WindowAttribute.hpp>
44 #include <com/sun/star/awt/XLayoutConstrains.hpp>
45 #include <com/sun/star/awt/XLayoutContainer.hpp>
46 #include <com/sun/star/awt/XToolkit.hpp>
47 #include <com/sun/star/awt/XVclWindowPeer.hpp>
48 #include <com/sun/star/awt/XWindow.hpp>
49 #include <com/sun/star/awt/XWindowPeer.hpp>
50 #include <rtl/strbuf.hxx>
51 #include <rtl/ustrbuf.hxx>
52 #include <toolkit/helper/property.hxx>
53 #include <vcl/lstbox.h>
55 using namespace layout::css;
57 using rtl::OUString;
59 // FIXME:
60 //#define FILEDLG
62 #include <layout/core/helper.hxx>
63 #include <layout/core/root.hxx>
64 #include <layout/core/helper.hxx>
66 // TODO: automatically generated
67 struct WidgetSpec {
68 const char *pLabel, *pName, *pIconName;
69 bool bIsContainer; };
70 static const WidgetSpec WIDGETS_SPECS[] = {
71 { "Label", "fixedtext" , "sc_label.png", false },
72 { "Button", "pushbutton" , "sc_pushbutton.png", false },
73 { "Radio Button", "radiobutton" , "sc_radiobutton.png", false },
74 { "Check Box", "checkbox" , "sc_checkbox.png", false },
75 { "Line Edit", "edit" , "sc_edit.png", false },
76 { "Numeric Field", "numericfield", "sc_numericfield.png", false },
77 { "List Box ", "listbox" , NULL, false },
78 // containers
79 { "Hor Box", "hbox" , NULL, true },
80 { "Ver Box", "vbox" , NULL, true },
81 { "Table", "table" , NULL, true },
82 { "Alignment", "align" , NULL, true },
83 { "Tab Control", "tabcontrol" , NULL, true },
84 { "Hor Splitter", "hsplitter" , NULL, true },
85 { "Ver Splitter", "vsplitter" , NULL, true },
86 { "Scroller", "scroller" , NULL, true },
88 const int WIDGETS_SPECS_LEN = sizeof (WIDGETS_SPECS) / sizeof (WidgetSpec);
90 using namespace layout;
91 using namespace layoutimpl;
92 namespace css = ::com::sun::star;
94 static rtl::OUString anyToString (uno::Any value)
96 try
98 switch (value.getValueTypeClass()) {
99 case uno::TypeClass_STRING:
100 return value.get<rtl::OUString>();
101 case uno::TypeClass_CONSTANT:
102 return rtl::OUString::valueOf (value.get<sal_Int32>());
103 case uno::TypeClass_LONG:
104 return rtl::OUString::valueOf (value.get<sal_Int64>());
105 case uno::TypeClass_SHORT:
106 // FIXME: seems broken
107 return rtl::OUString::valueOf ((sal_Int32) value.get<short>());
109 case uno::TypeClass_FLOAT:
110 return rtl::OUString::valueOf (value.get<float>());
111 case uno::TypeClass_DOUBLE:
112 return rtl::OUString::valueOf (value.get<double>());
114 case uno::TypeClass_BOOLEAN:
116 bool val = value.get<sal_Bool>();
117 return rtl::OUString( val ? "1" : "0", 1, RTL_TEXTENCODING_ASCII_US );
118 /* if ( val )
119 return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "true" ) );
120 else
121 return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "false" ) );*/
123 default:
124 break;
127 catch(...) {}
128 return rtl::OUString();
131 static inline long anyToNatural (uno::Any value)
132 { return sal::static_int_cast<long>(anyToString( value ).toInt64()); }
133 static inline double anyToDecimal (uno::Any value)
134 { return anyToString( value ).toDouble(); }
136 /* XLayoutContainer/XLayoutConstrains are a bit of a hasle to work with.
137 Let's wrap them. */
138 class Widget : public layoutimpl::LayoutWidget
140 friend class EditorRoot;
142 Widget *mpParent;
143 std::vector< Widget *> maChildren;
144 bool mbForeign;
146 rtl::OUString mrId;
147 rtl::OUString mrLabel, mrUnoName;
149 // TODO: store original properties. And some property handling methods.
150 long mnOriAttrbs;
151 layoutimpl::PropList maOriProps, maOriChildProps;
153 public:
155 // to be used to wrap the root
156 Widget( uno::Reference< awt::XLayoutConstrains > xImport, const char *label )
157 : mpParent( 0 ), mbForeign( true )
159 mxWidget = xImport;
160 mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY );
162 mrLabel = rtl::OUString( label, strlen( label ), RTL_TEXTENCODING_UTF8 );
164 #if 0 /* obsolete */
165 // FIXME: this code is meant to import a XML file. Just use the importer,
166 // then pass the root widget. But information like the ID string is lost.
167 // So, this needs to be more closely tight to the importer.
168 uno::Sequence< uno::Reference< awt::XLayoutConstrains > > aChildren;
169 for ( int i = 0; i < aChildren.getLength(); i++ )
171 Widget *pChild = new Widget( aChildren[ i ], "---" );
172 maChildren.push_back( pChild );
173 pChild->mpParent = this;
175 #endif
178 Widget( rtl::OUString id, uno::Reference< awt::XToolkit > xToolkit,
179 uno::Reference< awt::XLayoutContainer > xParent,
180 rtl::OUString unoName, long nAttrbs )
181 : mpParent( 0 ), mbForeign( false ), mrId( id ),
182 mnOriAttrbs( nAttrbs )
184 while ( xParent.is() && !uno::Reference< awt::XWindow >( xParent, uno::UNO_QUERY ).is() )
186 uno::Reference< awt::XLayoutContainer > xContainer( xParent, uno::UNO_QUERY );
187 OSL_ASSERT( xContainer.is() );
188 xParent = uno::Reference< awt::XLayoutContainer >( xContainer->getParent(), uno::UNO_QUERY );
191 mxWidget = WidgetFactory::createWidget( xToolkit, xParent, unoName, nAttrbs );
192 OSL_ASSERT( mxWidget.is() );
193 mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY );
195 mrLabel = mrUnoName = unoName;
196 // try to get a nicer label for the widget
197 for ( int i = 0; i < WIDGETS_SPECS_LEN; i++ )
198 if ( unoName.equalsAscii( WIDGETS_SPECS[ i ].pName ) )
200 const char *label = WIDGETS_SPECS[ i ].pLabel;
201 mrLabel = rtl::OUString( label, strlen( label ), RTL_TEXTENCODING_UTF8 );
202 break;
205 // set default Text property
206 // TODO: disable editing of text fields, check boxes selected, etc...
207 #if 0
208 uno::Reference< awt::XVclWindowPeer> xVclPeer( mxWidget, uno::UNO_QUERY )
209 if ( xVclPeer.is() ) // XVclWindowPeer ignores missing / incorrect properties
211 //FIXME: it looks odd on widgets like NumericField seeing text which is deleted
212 // when you interact with it... We can avoid it for those widgets, by doing a getProp
213 // of "Text" and check if it is empty or not.
215 xVclPeer->setProperty( rtl::OUString::createFromAscii( "Text" ),
216 uno::makeAny( rtl::OUString::createFromAscii( "new widget" ) ) );
217 #endif
219 // store original properties
221 PropertyIterator it( this, WINDOW_PROPERTY );
222 while ( it.hasNext() )
224 beans::Property prop = it.next();
225 rtl::OUString name( prop.Name );
226 rtl::OUString value( getProperty( name, WINDOW_PROPERTY ) );
227 #if DEBUG_PRINT
228 fprintf(stderr, "original property: %s = %s\n", OUSTRING_CSTR(name), OUSTRING_CSTR(value));
229 #endif
230 std::pair< rtl::OUString, rtl::OUString > pair( name, value );
231 maOriProps.push_back( pair );
237 ~Widget()
239 for ( std::vector< Widget *>::const_iterator it = maChildren.begin();
240 it != maChildren.end(); it++ )
241 delete *it;
242 if ( !mbForeign )
244 uno::Reference< lang::XComponent > xComp( mxWidget, uno::UNO_QUERY );
245 if ( xComp.is() )
246 // some widgets, like our containers, don't implement this interface...
247 xComp->dispose();
251 uno::Reference< awt::XLayoutConstrains > impl()
253 return mxWidget;
256 // LayoutWidget
257 virtual bool addChild( LayoutWidget *pChild )
259 return addChild( static_cast< Widget * >( pChild ) );
262 virtual void setProperties( const PropList &rProps )
264 // maOriProps = rProps;
265 LayoutWidget::setProperties( rProps );
268 virtual void setChildProperties( LayoutWidget *pChild, const PropList &rProps )
270 maOriChildProps = rProps;
271 LayoutWidget::setChildProperties( pChild, rProps );
274 // tree travel
275 Widget *up()
277 return mpParent;
280 Widget *down()
282 if ( maChildren.empty() )
283 return NULL;
284 return maChildren.front();
287 Widget *next()
289 if ( mpParent )
291 int pos = mpParent->getChildPos( this );
292 return mpParent->getChild( pos+1 );
294 return NULL;
297 Widget *prev()
299 if ( mpParent )
301 int pos = mpParent->getChildPos( this );
302 return mpParent->getChild( pos-1 );
304 return NULL;
307 // handle
308 bool addChild( Widget *pChild, int pos = 0xffff )
310 if ( !mxContainer.is() )
311 return false;
313 uno::Sequence< uno::Reference < awt::XLayoutConstrains > > aChildren;
314 aChildren = mxContainer->getChildren();
315 int nChildrenLen = aChildren.getLength();
317 // ugly, but let's check if the container is next to full...
318 try {
319 mxContainer->addChild( pChild->mxWidget );
321 catch( awt::MaxChildrenException ex ) {
322 return false;
325 if ( pos < nChildrenLen )
326 { // if its on the middle, we need to make space for it
327 mxContainer->removeChild( pChild->mxWidget );
328 for ( int i = pos; i < nChildrenLen; i++ )
329 mxContainer->removeChild( aChildren[ i ] );
330 mxContainer->addChild( pChild->mxWidget );
331 for ( int i = pos; i < nChildrenLen; i++ )
332 mxContainer->addChild( aChildren[ i ] );
333 maChildren.insert( maChildren.begin()+pos, pChild );
335 else
336 maChildren.push_back( pChild );
338 OSL_ASSERT( pChild->mpParent == NULL );
339 pChild->mpParent = this;
341 // store container props
343 pChild->maOriChildProps.clear();
344 PropertyIterator it( pChild, CONTAINER_PROPERTY );
345 while ( it.hasNext() )
347 beans::Property prop = it.next();
348 rtl::OUString name( prop.Name );
349 try {
350 rtl::OUString value( pChild->getProperty( name, CONTAINER_PROPERTY ) );
351 std::pair< rtl::OUString, rtl::OUString > pair( name, value );
352 pChild->maOriChildProps.push_back( pair );
353 } catch ( beans::UnknownPropertyException &rEx ) {
354 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",
355 rtl::OUStringToOString (rEx.Message, RTL_TEXTENCODING_UTF8).getStr());
360 return true;
363 bool removeChild( Widget *pChild )
365 if ( !mxContainer.is() || pChild->mpParent != this )
366 return false;
368 mxContainer->removeChild( pChild->mxWidget );
370 unsigned int pos = getChildPos( pChild );
371 if ( pos < maChildren.size() )
372 maChildren.erase( maChildren.begin()+pos );
373 pChild->mpParent = NULL;
375 return true;
378 bool swapWithChild( Widget *pChild )
380 if ( !pChild->isContainer() )
381 return false;
383 // remove all child's childrens, and try to add them here
384 removeChild( pChild );
386 // keep a copy for failure
387 std::vector< Widget *> aChildren = maChildren;
388 std::vector< Widget *> aChildChildren = pChild->maChildren;
390 for ( std::vector< Widget *>::const_iterator it = aChildChildren.begin();
391 it != aChildChildren.end(); it++ )
392 pChild->removeChild( *it );
394 for ( std::vector< Widget *>::const_iterator it = aChildChildren.begin();
395 it != aChildChildren.end(); it++ )
396 if ( !addChild( *it ) )
397 { // failure
398 for ( std::vector< Widget *>::const_iterator jt = aChildChildren.begin();
399 jt != it; jt++ )
400 removeChild( *jt );
401 for ( std::vector< Widget *>::const_iterator jt = aChildChildren.begin();
402 jt != aChildChildren.end(); jt++ )
403 pChild->addChild( *jt );
404 return false;
407 Widget *pParent = up();
409 if ( pParent )
411 pParent->removeChild( this );
412 pParent->addChild( pChild );
414 pChild->addChild( this );
415 return true;
418 unsigned int getChildPos( Widget *pChild )
420 int i = 0;
421 for ( std::vector< Widget *>::const_iterator it = maChildren.begin();
422 it != maChildren.end(); it++, i++ )
423 if ( *it == pChild )
424 break;
425 return i;
428 Widget *getChild( int pos )
430 if ( pos >= 0 && pos < (signed) maChildren.size() )
431 return *(maChildren.begin() + pos);
432 return NULL;
435 bool isContainer()
436 { return mxContainer.is(); }
437 unsigned int getChildrenLen()
438 { return maChildren.size(); }
440 rtl::OUString getLabel() const
441 { return mrLabel; }
442 rtl::OUString getUnoName() const
443 { return mrUnoName; }
445 int getDepth()
447 int depth = 0;
448 for ( Widget *pWidget = mpParent; pWidget; pWidget = pWidget->mpParent )
449 depth++;
450 return depth;
453 enum PropertyKind {
454 WINDOW_PROPERTY, CONTAINER_PROPERTY, WINBITS_PROPERTY
457 static rtl::OUString findProperty( const PropList &props, rtl::OUString propName )
459 for ( PropList::const_iterator it = props.begin(); it != props.end(); it++ )
460 if ( it->first.equalsIgnoreAsciiCase( propName ) )
461 return it->second;
462 #if DEBUG_PRINT
463 fprintf(stderr, "Serious error: property '%s' not found\n", OUSTRING_CSTR(propName));
464 #endif
465 return rtl::OUString();
468 rtl::OUString getOriginalProperty( rtl::OUString rPropName, PropertyKind rKind )
470 rtl::OUString rValue;
471 switch ( rKind ) {
472 case WINDOW_PROPERTY:
473 rValue = findProperty( maOriProps, rPropName );
474 break;
475 case CONTAINER_PROPERTY:
476 rValue = findProperty( maOriChildProps, rPropName );
477 break;
478 case WINBITS_PROPERTY:
479 // TODO
480 break;
483 return rValue;
486 rtl::OUString getProperty( rtl::OUString rPropName, PropertyKind rKind )
488 rtl::OUString rValue;
489 switch ( rKind ) {
490 case WINDOW_PROPERTY:
491 rValue = anyToString( layoutimpl::prophlp::getProperty( mxWidget, rPropName ) );
492 break;
493 case CONTAINER_PROPERTY:
494 if ( mpParent )
495 rValue = anyToString( layoutimpl::prophlp::getProperty(
496 mpParent->mxContainer->getChildProperties( mxWidget ), rPropName ) );
497 break;
498 case WINBITS_PROPERTY:
499 // TODO
500 break;
503 return rValue;
506 bool isPropertyTouched( rtl::OUString propName, PropertyKind rKind )
508 rtl::OUString oriValue = getOriginalProperty( propName, rKind );
509 rtl::OUString newValue = getProperty( propName, rKind );
510 bool isTouched = oriValue != newValue;
511 #if DEBUG_PRINT
512 fprintf(stderr, "is property '%s' touched? %s (%s vs %s)\n", OUSTRING_CSTR(propName), isTouched ? "yes" : "no", OUSTRING_CSTR(oriValue), OUSTRING_CSTR(newValue));
513 #endif
514 return isTouched;
517 using LayoutWidget::setProperty;
519 void setProperty( rtl::OUString rPropName, PropertyKind rKind, uno::Any rValue )
521 switch ( rKind ) {
522 case WINDOW_PROPERTY:
523 layoutimpl::prophlp::setProperty( mxWidget, rPropName, rValue );
524 break;
525 case CONTAINER_PROPERTY:
526 if ( mpParent )
527 layoutimpl::prophlp::setProperty(
528 mpParent->mxContainer->getChildProperties( mxWidget ), rPropName, rValue );
529 break;
530 case WINBITS_PROPERTY:
531 // TODO
532 break;
536 struct PropertyIterator {
537 friend class Widget;
538 PropertyKind mrKind;
539 uno::Sequence< beans::Property > maProps;
540 int nPropIt;
542 PropertyIterator( Widget *pWidget, PropertyKind rKind )
543 : mrKind( rKind ), nPropIt( 0 )
545 switch ( rKind )
547 case WINDOW_PROPERTY:
548 if ( layoutimpl::prophlp::canHandleProps( pWidget->mxWidget ) )
550 uno::Reference< beans::XPropertySetInfo > xInfo
551 = layoutimpl::prophlp::queryPropertyInfo( pWidget->mxWidget );
552 if ( !xInfo.is() )
553 return;
555 maProps = xInfo->getProperties();
557 break;
558 case CONTAINER_PROPERTY:
559 if ( pWidget->mpParent )
561 uno::Reference< beans::XPropertySet >xParentSet(
562 pWidget->mpParent->mxContainer->getChildProperties( pWidget->mxWidget ) );
563 if ( xParentSet.is())
565 uno::Reference< beans::XPropertySetInfo > xInfo( xParentSet->getPropertySetInfo() );
566 if ( xInfo.is() )
567 maProps = xInfo->getProperties();
570 break;
571 case WINBITS_PROPERTY:
572 // TODO
573 break;
577 bool hasNext()
579 return nPropIt < maProps.getLength();
582 beans::Property next()
584 /* rtl::OUString propName, propValue;
585 propName = maProps[ nPropIt ];
586 propValue = getProperty( propName, mrKind, false);
587 nPropIt++;
588 return std::pair< rtl::OUString, rtl::OUString > propPair( propName, propValue );*/
589 return maProps[ nPropIt++ ];
594 class EditorRoot : public layoutimpl::LayoutRoot {
595 Widget *mpParent;
597 public:
598 EditorRoot( const uno::Reference< lang::XMultiServiceFactory >& xFactory,
599 Widget *pParent )
600 : layoutimpl::LayoutRoot( xFactory ), mpParent( pParent )
604 // generation
605 virtual layoutimpl::LayoutWidget *create( rtl::OUString id, const rtl::OUString unoName,
606 long attrbs, uno::Reference< awt::XLayoutContainer > xParent )
608 if ( unoName.compareToAscii( "dialog" ) == 0 )
609 return mpParent;
611 // TODO: go through specs to map unoName to a more human-readable label
612 Widget *pWidget = new Widget( id, mxToolkit, xParent, unoName, attrbs );
613 if ( !mxWindow.is() )
614 mxWindow = uno::Reference< awt::XWindow >( pWidget->getPeer(), uno::UNO_QUERY );
616 if ( pWidget->mxContainer.is() )
617 pWidget->mxContainer->setLayoutUnit( mpParent->mxContainer->getLayoutUnit() );
619 return pWidget;
623 /* Working with the layout in 1D, as if it was a flat list. */
624 namespace FlatLayout
626 Widget *next( Widget *pWidget )
628 Widget *pNext;
629 pNext = pWidget->down();
630 if ( pNext ) return pNext;
631 pNext = pWidget->next();
632 if ( pNext ) return pNext;
633 for ( Widget *pUp = pWidget->up(); pUp != NULL; pUp = pUp->up() )
634 if ( (pNext = pUp->next()) != NULL )
635 return pNext;
636 return NULL;
640 Widget *prev( Widget *pWidget )
642 Widget *pPrev;
643 pPrev = pWidget->prev();
644 if ( !pPrev )
645 return pWidget->up();
647 Widget *pBottom = pPrev->down();
648 if ( pBottom )
650 while ( pBottom->down() || pBottom->next() )
652 for ( Widget *pNext = pBottom->next(); pNext; pNext = pNext->next() )
653 pBottom = pNext;
654 Widget *pDown = pBottom->down();
655 if ( pDown )
656 pBottom = pDown;
658 return pBottom;
660 return pPrev;
664 bool moveWidget( Widget *pWidget, bool up /*or down*/ )
666 // Keep child parent&pos for in case of failure
667 Widget *pOriContainer = pWidget->up();
668 unsigned int oriChildPos = pOriContainer->getChildPos( pWidget );
670 // Get parent&sibling before removing it, since relations get cut
671 Widget *pSibling = up ? pWidget->prev() : pWidget->next();
672 Widget *pContainer = pWidget->up();
673 if ( !pContainer )
674 return false;
676 // try to swap with parent or child
677 // We need to allow for this at least for the root node...
678 if ( !pSibling )
680 if ( up )
682 if ( pContainer->swapWithChild( pWidget ) )
683 return true;
685 else
687 // TODO: this is a nice feature, but we probably want to do it explicitely...
688 #if 0
689 if ( pWidget->down() && pWidget->swapWithChild( pWidget->down() ) )
690 return true;
691 #endif
695 pContainer->removeChild( pWidget );
697 // if has up sibling -- append to it, else swap with it
698 if ( pSibling )
700 if ( pSibling->addChild( pWidget, up ? 0xffff : 0 ) )
701 return true;
703 unsigned int childPos = pContainer->getChildPos( pSibling );
704 if ( pContainer->addChild( pWidget, childPos + (up ? 0 : 1) ) )
705 return true; // should always be succesful
707 // go through parents -- try to get prepended to them
708 else
710 for ( ; pContainer && pContainer->up(); pContainer = pContainer->up() )
712 unsigned int childPos = pContainer->up()->getChildPos( pContainer );
713 if ( pContainer->up()->addChild( pWidget, childPos + (up ? 0 : 1) ) )
714 return true;
718 // failed -- try to get it to its old position
719 if ( !pOriContainer->addChild( pWidget, oriChildPos ) )
721 // a parent should never reject a child back. but if it ever
722 // happens, just kill it, we don't run an orphanate here ;P
723 delete pWidget;
724 return true;
726 return false;
729 // NOTE: root is considered to be number -1
730 Widget *get( Widget *pRoot, int nb )
732 Widget *it;
733 for ( it = pRoot; it != NULL && nb >= 0; it = next( it ) )
734 nb--;
735 return it;
738 int get( Widget *pRoot, Widget *pWidget )
740 int nRet = -1;
741 Widget *it;
742 for ( it = pRoot; it != NULL && it != pWidget; it = next( it ) )
743 nRet++;
744 return nRet;
748 //** PropertiesList widget
750 class PropertiesList : public layout::Table
752 class PropertyEntry
754 friend class PropertiesList;
756 /* wrapper between the widget and Any */
757 struct AnyWidget
759 DECL_LINK( ApplyPropertyHdl, layout::Window* );
760 DECL_LINK( FlagToggledHdl, layout::CheckBox* );
762 AnyWidget( Widget *pWidget, rtl::OUString aPropName, Widget::PropertyKind aPropKind )
763 : mpWidget( pWidget ), maPropName( aPropName ), maPropKind( aPropKind )
765 mpFlag = 0;
766 mbBlockFlagCallback = false;
767 bFirstGet = true;
770 virtual ~AnyWidget()
772 #if DEBUG_PRINT
773 fprintf(stderr, "~AnyWidget\n");
774 #endif
777 void save( uno::Any aValue )
779 mpWidget->setProperty( maPropName, maPropKind, aValue );
780 checkProperty();
783 void checkProperty()
785 bool flag = mpWidget->isPropertyTouched( maPropName, maPropKind );
787 if ( mpFlag && mpFlag->IsChecked() != (BOOL)flag )
789 CheckFlag( flag, true );
793 void CheckFlag( bool bValue, bool bBlockCallback )
795 if ( bBlockCallback )
796 mbBlockFlagCallback = true;
797 mpFlag->Check( bValue );
798 mbBlockFlagCallback = false;
801 bool bFirstGet; // HACK
802 rtl::OUString getValue()
804 // return mpWidget->getOriProperty( maPropName );
805 rtl::OUString value;
806 if ( bFirstGet ) // king of ugliness
807 value = mpWidget->getProperty( maPropName, maPropKind );
808 else
809 value = mpWidget->getOriginalProperty( maPropName, maPropKind );
810 bFirstGet = false;
811 return value;
814 // FIXME: wrapper should have a base class for this...
815 virtual layout::Window *getWindow() = 0;
816 virtual layout::Container *getContainer() { return NULL; }
818 virtual void load() = 0;
819 virtual void store() = 0;
821 Widget *mpWidget;
822 rtl::OUString maPropName;
823 Widget::PropertyKind maPropKind;
824 layout::CheckBox *mpFlag;
825 bool mbBlockFlagCallback;
828 struct AnyEdit : public AnyWidget, layout::HBox
830 layout::Edit *mpEdit;
831 bool mbMultiLine;
832 layout::PushButton *mpExpand;
833 DECL_LINK( ExpandEditHdl, layout::PushButton* );
835 // so we can create widgets (like transforming the Edit into a
836 // MultiLineEdit)
837 layout::Window *mpWinParent;
839 AnyEdit( Widget *pWidget, rtl::OUString aPropName,
840 Widget::PropertyKind aPropKind, layout::Window *pWinParent )
841 : AnyWidget( pWidget, aPropName, aPropKind ), layout::HBox( 0, false ), mpWinParent( pWinParent )
843 mpEdit = NULL;
844 mpExpand = new layout::PushButton( pWinParent, WB_TOGGLE );
845 mpExpand->SetToggleHdl( LINK( this, AnyEdit, ExpandEditHdl ) );
846 setAsMultiLine( false );
848 load();
851 virtual ~AnyEdit()
853 delete mpEdit;
854 delete mpExpand;
857 virtual layout::Window *getWindow()
858 { return NULL; }
859 virtual layout::Container *getContainer()
860 { return this; }
862 void setAsMultiLine( bool bMultiLine )
864 Clear();
865 XubString text;
866 if ( mpEdit )
868 text = mpEdit->GetText();
869 printf("Remove mpEdit and expand\n");
870 Remove( mpEdit );
871 Remove( mpExpand );
872 delete mpEdit;
875 if ( bMultiLine )
877 mpEdit = new layout::Edit( mpWinParent, WB_BORDER );
878 mpExpand->SetText( String::CreateFromAscii( "-" ) );
880 else
882 mpEdit = new layout::Edit( mpWinParent, WB_BORDER );
883 mpExpand->SetText( String::CreateFromAscii( "+" ) );
886 mpEdit->SetText( text );
887 mpEdit->SetModifyHdl( LINK( this, AnyEdit, ApplyPropertyHdl ) );
889 Add( mpEdit, true, true, 0 );
890 Add( mpExpand, false, true, 0 );
892 mbMultiLine = bMultiLine;
895 #if 0
896 // TODO: make this global... We'll likely need it for export...
897 struct Translate {
898 const char *ori, *dest;
900 static rtl::OUString stringReplace( rtl::OUString _str,
901 Translate *trans )
903 const sal_Unicode *str = _str.getStr();
904 rtl::OUStringBuffer buf;
905 int i, j, k;
906 for ( i = 0; i < _str.getLength(); i++ )
908 for ( j = 0; trans[ j ].ori; j++ )
910 const char *ori = trans[ j ].ori;
911 for ( k = 0; ori[ k ] && i+k < _str.getLength(); k++ )
912 if ( ori[ k ] != str[ i+k ] )
913 break;
914 if ( !ori[ k ] )
916 // found substring
917 buf.appendAscii( trans[ j ].dest );
918 i += k;
919 continue;
922 buf.append( str[ i ] );
924 return buf.makeStringAndClear();
926 #endif
928 virtual void load()
930 #if 0
931 // replace end of lines by "\\n" strings
932 Translate trans[] = {
933 { "\\", "\\\\" }, { "\n", "\\n" }, { 0, 0 }
935 rtl::OUString str = anyToString( getValue() );
936 str = stringReplace( str, trans );
937 SetText( str );
938 #endif
939 mpEdit->SetText( getValue() );
940 checkProperty();
943 virtual void store()
945 #if 0
946 // replace "\\n" strings by actual end of lines
947 Translate trans[] = {
948 { "\\\\", "\\" }, { "\\n", "\n" },
949 { "\\", "" }, { 0, 0 }
951 rtl::OUString str = GetText();
952 str = stringReplace( str, trans );
953 save( uno::makeAny( str ) );
954 #endif
955 save( uno::makeAny( (rtl::OUString) mpEdit->GetText() ) );
959 struct AnyInteger : public AnyWidget, NumericField
961 AnyInteger( Widget *pWidget, rtl::OUString aPropName,
962 Widget::PropertyKind aPropKind, Window *pWinParent )
963 : AnyWidget( pWidget, aPropName, aPropKind ), NumericField( pWinParent, WB_SPIN|WB_BORDER )
965 load();
966 SetModifyHdl( LINK( this, AnyInteger, ApplyPropertyHdl ) );
969 virtual Window *getWindow()
970 { return this; }
972 virtual void load()
974 OUString text = getValue();
975 SetText( text.getStr() );
976 checkProperty();
979 virtual void store()
981 #if DEBUG_PRINT
982 fprintf(stderr, "store number: %ld\n", rtl::OUString( GetText() ).toInt64());
983 #endif
984 save( uno::makeAny( rtl::OUString( GetText() ).toInt64() ) );
988 struct AnyFloat : public AnyInteger
990 AnyFloat( Widget *pWidget, rtl::OUString aPropName,
991 Widget::PropertyKind aPropKind, Window *pWinParent )
992 : AnyInteger( pWidget, aPropName, aPropKind, pWinParent )
995 virtual void store()
997 save( uno::makeAny( rtl::OUString( GetText() ).toDouble() ) );
1001 struct AnyCheckBox : public AnyWidget, layout::CheckBox
1003 AnyCheckBox( Widget *pWidget, rtl::OUString aPropName,
1004 Widget::PropertyKind aPropKind, layout::Window *pWinParent )
1005 : AnyWidget( pWidget, aPropName, aPropKind ), layout::CheckBox( pWinParent )
1007 // adding some whitespaces to make the hit area larger
1008 // SetText( String::CreateFromAscii( "" ) );
1009 load();
1010 SetToggleHdl( LINK( this, AnyWidget, ApplyPropertyHdl ) );
1013 virtual ~AnyCheckBox()
1015 #if DEBUG_PRINT
1016 fprintf(stderr, "~AnyCheckBox\n");
1017 #endif
1020 virtual layout::Window *getWindow()
1021 { return this; }
1023 virtual void load()
1025 #if DEBUG_PRINT
1026 fprintf(stderr, "loading boolean value\n");
1027 #endif
1028 Check( getValue().toInt64() != 0 );
1029 setLabel();
1030 checkProperty();
1033 virtual void store()
1035 save( uno::makeAny( IsChecked() ) );
1036 setLabel();
1039 void setLabel()
1041 SetText( String::CreateFromAscii( IsChecked() ? "true" : "false" ) );
1045 struct AnyListBox : public AnyWidget, layout::ListBox
1047 AnyListBox( Widget *pWidget, rtl::OUString aPropName,
1048 Widget::PropertyKind aPropKind, Window *pWinParent )
1049 : AnyWidget( pWidget, aPropName, aPropKind ), layout::ListBox( pWinParent, WB_DROPDOWN )
1051 SetSelectHdl( LINK( this, AnyWidget, ApplyPropertyHdl ) );
1054 virtual layout::Window *getWindow()
1055 { return this; }
1057 virtual void load()
1059 SelectEntryPos( sal::static_int_cast< USHORT >( getValue().toInt32() ) );
1060 checkProperty();
1063 virtual void store()
1065 save( uno::makeAny( (short) GetSelectEntryPos() ) );
1069 struct AnyAlign : public AnyListBox
1071 AnyAlign( Widget *pWidget, rtl::OUString aPropName,
1072 Widget::PropertyKind aPropKind, Window *pWinParent )
1073 : AnyListBox( pWidget, aPropName, aPropKind, pWinParent )
1075 InsertEntry( XubString::CreateFromAscii( "Left" ) );
1076 InsertEntry( XubString::CreateFromAscii( "Center" ) );
1077 InsertEntry( XubString::CreateFromAscii( "Right" ) );
1078 load();
1082 /* AnyListBox and AnyComboBox different in that a ComboBox allows the user
1083 to add other options, operating in strings, instead of constants.
1084 (its more like a suggestive AnyEdit) */
1085 struct AnyComboBox : public AnyWidget, layout::ComboBox
1087 AnyComboBox( Widget *pWidget, rtl::OUString aPropName,
1088 Widget::PropertyKind aPropKind, Window *pWinParent )
1089 : AnyWidget( pWidget, aPropName, aPropKind ), layout::ComboBox( pWinParent, WB_DROPDOWN )
1091 SetModifyHdl( LINK( this, AnyComboBox, ApplyPropertyHdl ) );
1094 virtual layout::Window *getWindow()
1095 { return this; }
1097 virtual void load()
1099 SetText( getValue() );
1100 checkProperty();
1103 virtual void store()
1105 save( uno::makeAny( (rtl::OUString) GetText() ) );
1109 struct AnyFontStyle : public AnyComboBox
1111 AnyFontStyle( Widget *pWidget, rtl::OUString aPropName,
1112 Widget::PropertyKind aPropKind, Window *pWinParent )
1113 : AnyComboBox( pWidget, aPropName, aPropKind, pWinParent )
1115 InsertEntry( XubString::CreateFromAscii( "Bold" ) );
1116 InsertEntry( XubString::CreateFromAscii( "Italic" ) );
1117 InsertEntry( XubString::CreateFromAscii( "Bold Italic" ) );
1118 InsertEntry( XubString::CreateFromAscii( "Fett" ) );
1119 load();
1123 layout::FixedText *mpLabel;
1124 layout::CheckBox *mpFlag;
1125 AnyWidget *mpValue;
1127 public:
1128 PropertyEntry( layout::Window *pWinParent, AnyWidget *pAnyWidget )
1130 mpLabel = new layout::FixedText( pWinParent );
1132 // append ':' to aPropName
1133 rtl::OUStringBuffer buf( pAnyWidget->maPropName );
1134 buf.append( sal_Unicode (':') );
1135 mpLabel->SetText( buf.makeStringAndClear() );
1137 mpValue = pAnyWidget;
1138 mpFlag = new layout::CheckBox( pWinParent );
1139 mpFlag->SetToggleHdl( LINK( mpValue, AnyWidget, FlagToggledHdl ) );
1140 mpValue->mpFlag = mpFlag;
1143 ~PropertyEntry()
1145 #if DEBUG_PRINT
1146 fprintf(stderr, "REMOVING label, flag and value\n");
1147 #endif
1148 delete mpLabel;
1149 delete mpFlag;
1150 delete mpValue;
1153 // Use this factory rather than the constructor -- check for NULL
1154 static PropertyEntry *construct( Widget *pWidget, rtl::OUString aPropName,
1155 Widget::PropertyKind aPropKind, sal_uInt16 nType,
1156 layout::Window *pWinParent )
1158 AnyWidget *pAnyWidget;
1159 switch (nType) {
1160 case uno::TypeClass_STRING:
1161 if ( aPropName.compareToAscii( "FontStyleName" ) == 0 )
1163 pAnyWidget = new AnyFontStyle( pWidget, aPropName, aPropKind, pWinParent );
1164 break;
1166 pAnyWidget = new AnyEdit( pWidget, aPropName, aPropKind, pWinParent );
1167 break;
1168 case uno::TypeClass_SHORT:
1169 if ( aPropName.compareToAscii( "Align" ) == 0 )
1171 pAnyWidget = new AnyAlign( pWidget, aPropName, aPropKind, pWinParent );
1172 break;
1174 // otherwise, treat as any other number...
1175 case uno::TypeClass_LONG:
1176 case uno::TypeClass_UNSIGNED_LONG:
1177 pAnyWidget = new AnyInteger( pWidget, aPropName, aPropKind, pWinParent );
1178 break;
1179 case uno::TypeClass_FLOAT:
1180 case uno::TypeClass_DOUBLE:
1181 pAnyWidget = new AnyFloat( pWidget, aPropName, aPropKind, pWinParent );
1182 break;
1183 case uno::TypeClass_BOOLEAN:
1184 pAnyWidget = new AnyCheckBox( pWidget, aPropName, aPropKind, pWinParent );
1185 break;
1186 default:
1187 return NULL;
1189 return new PropertyEntry( pWinParent, pAnyWidget );
1193 layout::Window *mpParentWindow;
1195 std::list< PropertyEntry* > maPropertiesList;
1196 layout::FixedLine *mpSeparator;
1198 // some properties are obscure, or simply don't make sense in this
1199 // context. Let's just ignore them.
1200 // Maybe we could offer them in an expander or something...
1201 static bool toIgnore( rtl::OUString prop )
1203 // binary search -- keep the list sorted alphabetically
1204 static char const *toIgnoreList[] = {
1205 "DefaultControl", "FocusOnClick", "FontCharWidth", "FontCharset",
1206 "FontEmphasisMark", "FontFamily", "FontHeight", "FontKerning", "FontName",
1207 "FontOrientation", "FontPitch", "FontRelief", "FontSlant", "FontStrikeout",
1208 "FontType", "FontWordLineMode", "HelpText", "HelpURL", "MultiLine",
1209 "Printable", "Repeat", "RepeatDelay", "Tabstop"
1212 #if 0
1213 // checks list sanity -- enable this when you add some entries...
1214 for ( unsigned int i = 1; i < sizeof( toIgnoreList )/sizeof( char * ); i++ )
1216 if ( strcmp(toIgnoreList[i-1], toIgnoreList[i]) >= 0 )
1218 printf("ignore list not ordered properly: "
1219 "'%s' should come before '%s'\n",
1220 toIgnoreList[i], toIgnoreList[i-1]);
1221 exit(-1);
1224 #endif
1226 int min = 0, max = sizeof( toIgnoreList )/sizeof( char * ) - 1, mid, cmp;
1227 do {
1228 mid = min + (max - min)/2;
1229 cmp = prop.compareToAscii( toIgnoreList[ mid ] );
1230 if ( cmp > 0 )
1231 min = mid+1;
1232 else if ( cmp < 0 )
1233 max = mid-1;
1234 else
1235 return true;
1236 } while ( min <= max );
1237 return false;
1240 public:
1241 PropertiesList( layout::Dialog *dialog )
1242 : layout::Table( dialog, "properties-box" )
1243 , mpParentWindow( dialog ), mpSeparator( 0 )
1247 ~PropertiesList()
1249 clear();
1252 private:
1253 // auxiliary, add properties from the peer to the list
1254 void addProperties( Widget *pWidget, Widget::PropertyKind rKind )
1256 Widget::PropertyIterator it( pWidget, rKind );
1257 while ( it.hasNext() )
1259 beans::Property prop = it.next();
1260 rtl::OUString name( prop.Name );
1261 if ( toIgnore( name ) )
1262 continue;
1263 sal_uInt16 type = static_cast< sal_uInt16 >( prop.Type.getTypeClass() );
1265 PropertyEntry *propEntry = PropertyEntry::construct(
1266 pWidget, name, rKind, type, mpParentWindow );
1268 if ( propEntry )
1270 Add( propEntry->mpLabel, false, false );
1272 // HACK: one of these will return Null...
1273 Add( propEntry->mpValue->getWindow(), true, false );
1274 Add( propEntry->mpValue->getContainer(), true, false );
1276 Add( propEntry->mpFlag, false, false );
1277 maPropertiesList.push_back( propEntry );
1282 public:
1283 void selectedWidget( Widget *pWidget )
1285 clear();
1287 if ( !pWidget )
1288 return;
1290 addProperties( pWidget, Widget::CONTAINER_PROPERTY );
1292 mpSeparator = new layout::FixedLine( mpParentWindow );
1293 // TODO: we may want to have to separate list widgets here...
1294 Add( mpSeparator, false, false, 3, 1 );
1296 addProperties( pWidget, Widget::WINDOW_PROPERTY );
1298 ShowAll( true );
1301 void clear()
1303 ///FIXME: crash
1304 Container::Clear();
1306 for ( std::list< PropertyEntry* >::iterator it = maPropertiesList.begin();
1307 it != maPropertiesList.end(); it++)
1308 delete *it;
1309 maPropertiesList.clear();
1311 delete mpSeparator;
1312 mpSeparator = NULL;
1316 IMPL_LINK( PropertiesList::PropertyEntry::AnyWidget, ApplyPropertyHdl, layout::Window *, pWin )
1318 (void) pWin;
1319 store();
1320 return 0;
1323 IMPL_LINK( PropertiesList::PropertyEntry::AnyWidget, FlagToggledHdl, layout::CheckBox *, pCheck )
1325 #if DEBUG_PRINT
1326 fprintf(stderr, "Property flag pressed -- is: %d\n", pCheck->IsChecked());
1327 #endif
1328 if ( !mbBlockFlagCallback )
1330 bool checked = pCheck->IsChecked();
1331 if ( !checked ) // revert
1333 #if DEBUG_PRINT
1334 fprintf(stderr, "revert\n");
1335 #endif
1336 load();
1338 else
1340 #if DEBUG_PRINT
1341 fprintf(stderr, "user can't dirty the flag!\n");
1342 #endif
1343 // User can't flag the property as dirty
1344 // Actually, we may want to allow the designer to force a property to be stored.
1345 // Could be useful when the default value of some new property wasn't yet decided...
1346 CheckFlag( false, true );
1349 #if DEBUG_PRINT
1350 else
1351 fprintf(stderr, "Property flag pressed -- BLOCKED\n");
1352 #endif
1353 return 0;
1356 IMPL_LINK( PropertiesList::PropertyEntry::AnyEdit, ExpandEditHdl, layout::PushButton *, pBtn )
1358 setAsMultiLine( pBtn->IsChecked() );
1359 return 0;
1362 //** SortListBox auxiliary widget
1364 class SortListBox
1365 { // For a manual sort ListBox; asks for a ListBox and Up/Down/Remove
1366 // buttons to wrap
1367 DECL_LINK( ItemSelectedHdl, layout::ListBox* );
1368 DECL_LINK( UpPressedHdl, layout::Button* );
1369 DECL_LINK( DownPressedHdl, layout::Button* );
1370 DECL_LINK( RemovePressedHdl, layout::Button* );
1371 layout::PushButton *mpUpButton, *mpDownButton, *mpRemoveButton;
1373 protected:
1374 layout::ListBox *mpListBox;
1376 virtual void upPressed( USHORT nPos )
1378 XubString str = mpListBox->GetSelectEntry();
1379 mpListBox->RemoveEntry( nPos );
1380 nPos = mpListBox->InsertEntry( str, nPos-1 );
1381 mpListBox->SelectEntryPos( nPos );
1384 virtual void downPressed( USHORT nPos )
1386 XubString str = mpListBox->GetSelectEntry();
1387 mpListBox->RemoveEntry( nPos );
1388 nPos = mpListBox->InsertEntry( str, nPos+1 );
1389 mpListBox->SelectEntryPos( nPos );
1392 virtual void removePressed( USHORT nPos )
1394 mpListBox->RemoveEntry( nPos );
1397 virtual void itemSelected( USHORT nPos )
1399 // if we had some XLayoutContainer::canAdd() or maxChildren() function
1400 // we could make a function to check if we can move selected and enable/
1401 // /disable the move buttons as appropriate
1403 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1405 mpUpButton->Disable();
1406 mpDownButton->Disable();
1407 mpRemoveButton->Disable();
1409 else
1411 mpUpButton->Enable();
1412 mpDownButton->Enable();
1413 mpRemoveButton->Enable();
1417 public:
1418 SortListBox( layout::ListBox *pListBox, layout::PushButton *pUpButton, layout::PushButton *pDownButton,
1419 layout::PushButton *pRemoveButton )
1420 : mpUpButton( pUpButton), mpDownButton( pDownButton), mpRemoveButton( pRemoveButton ),
1421 mpListBox( pListBox )
1423 mpListBox->SetSelectHdl( LINK( this, SortListBox, ItemSelectedHdl ) );
1425 mpUpButton->SetModeImage( layout::Image ( "res/commandimagelist/lc_moveup.png" ) );
1426 mpUpButton->SetImageAlign( IMAGEALIGN_LEFT );
1427 mpUpButton->SetClickHdl( LINK( this, SortListBox, UpPressedHdl ) );
1429 mpDownButton->SetModeImage( layout::Image ( "res/commandimagelist/lc_movedown.png" ) );
1430 mpDownButton->SetImageAlign( IMAGEALIGN_LEFT );
1431 mpDownButton->SetClickHdl( LINK( this, SortListBox, DownPressedHdl ) );
1433 // "res/commandimagelist/lch_delete.png", "res/commandimagelist/lc_delete.png"
1434 mpRemoveButton->SetModeImage( layout::Image ( "res/commandimagelist/sc_closedoc.png" ) );
1435 mpRemoveButton->SetImageAlign( IMAGEALIGN_LEFT );
1436 mpRemoveButton->SetClickHdl( LINK( this, SortListBox, RemovePressedHdl ) );
1438 // fire an un-select event
1439 itemSelected( LISTBOX_ENTRY_NOTFOUND );
1442 virtual ~SortListBox();
1445 SortListBox::~SortListBox()
1447 delete mpListBox;
1448 delete mpUpButton;
1449 delete mpDownButton;
1450 delete mpRemoveButton;
1453 IMPL_LINK( SortListBox, UpPressedHdl, layout::Button *, pBtn )
1455 (void) pBtn;
1456 USHORT pos = mpListBox->GetSelectEntryPos();
1457 if ( pos > 0 && pos != LISTBOX_ENTRY_NOTFOUND )
1458 upPressed( pos );
1459 return 0;
1462 IMPL_LINK( SortListBox, DownPressedHdl, layout::Button *, pBtn )
1464 (void) pBtn;
1465 USHORT pos = mpListBox->GetSelectEntryPos();
1466 if ( pos < mpListBox->GetEntryCount() && pos != LISTBOX_ENTRY_NOTFOUND )
1467 downPressed( pos );
1468 return 0;
1471 IMPL_LINK( SortListBox, RemovePressedHdl, layout::Button *, pBtn )
1473 (void) pBtn;
1474 USHORT pos = mpListBox->GetSelectEntryPos();
1475 if ( pos != LISTBOX_ENTRY_NOTFOUND )
1476 removePressed( pos );
1477 return 0;
1480 IMPL_LINK( SortListBox, ItemSelectedHdl, layout::ListBox *, pList )
1482 (void) pList;
1483 USHORT pos = mpListBox->GetSelectEntryPos();
1484 itemSelected( pos );
1485 return 0;
1488 //** LayoutTree widget
1490 class LayoutTree : public SortListBox
1492 public:
1493 struct Listener
1495 virtual void widgetSelected( Widget *pWidget ) = 0;
1498 private:
1499 Listener *mpListener;
1501 public:
1502 Widget *mpRootWidget;
1504 LayoutTree( layout::Dialog *dialog )
1505 : SortListBox( new layout::ListBox( dialog, "layout-tree" ),
1506 new layout::PushButton( dialog, "layout-up-button" ),
1507 new layout::PushButton( dialog, "layout-down-button" ),
1508 new layout::PushButton( dialog, "layout-remove-button" ) )
1510 layout::PeerHandle handle = dialog->GetPeerHandle( "preview-box" );
1511 uno::Reference< awt::XLayoutConstrains > xWidget( handle, uno::UNO_QUERY );
1512 mpRootWidget = new Widget( xWidget, "root" );
1515 virtual ~LayoutTree();
1517 Widget *getWidget( int nPos )
1519 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1520 return FlatLayout::get( mpRootWidget, nPos );
1521 return NULL;
1524 Widget *getSelectedWidget()
1526 Widget *pWidget = getWidget( mpListBox->GetSelectEntryPos() );
1527 if ( !pWidget ) // return root, if none selected
1528 pWidget = mpRootWidget;
1529 return pWidget;
1532 void selectWidget( Widget *pWidget )
1534 int pos = FlatLayout::get( mpRootWidget, pWidget );
1535 if ( pos == -1 )
1536 // if asked to select hidden root, select visible root
1537 pos = 0;
1538 mpListBox->SelectEntryPos( sal::static_int_cast< USHORT >( pos ) );
1541 void rebuild()
1543 struct inner
1545 // pads a string with whitespaces
1546 static rtl::OUString padString( rtl::OUString name, int depth )
1548 rtl::OStringBuffer aBuf( depth * 4 + name.getLength() + 2 );
1549 for (int i = 0; i < depth; i++)
1550 aBuf.append( " " );
1551 aBuf.append( rtl::OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
1552 return rtl::OUString( aBuf.getStr(), aBuf.getLength(),
1553 RTL_TEXTENCODING_UTF8 );
1557 mpListBox->Clear();
1558 for ( Widget *i = FlatLayout::next( mpRootWidget ); i; i = FlatLayout::next( i ) )
1559 mpListBox->InsertEntry( inner::padString( i->getLabel(), i->getDepth()-1 ) );
1561 // any selection, no longer is. ListBox doesn't fire the event on this case;
1562 // force it.
1563 itemSelected( LISTBOX_ENTRY_NOTFOUND );
1566 void setListener( Listener *pListener )
1567 { mpListener = pListener; }
1569 // print in XML format...
1571 static rtl::OUString toXMLNaming (const rtl::OUString &string)
1573 rtl::OUStringBuffer buffer (string.getLength());
1574 sal_Unicode *str = string.pData->buffer;
1575 for (int i = 0; i < string.getLength(); i++) {
1576 if ( str[i] >= 'A' && str[i] <= 'Z' )
1578 if ( i > 0 )
1579 buffer.append ((sal_Unicode) '-');
1580 buffer.append ((sal_Unicode) (str[i] - 'A' + 'a'));
1582 else
1583 buffer.append ((sal_Unicode) str[i]);
1586 return buffer.makeStringAndClear();
1589 void print()
1591 printf("\t\tExport:\n");
1592 printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1593 "<dialog xmlns=\"http://openoffice.org/2007/layout\"\n"
1594 " xmlns:cnt=\"http://openoffice.org/2007/layout/container\"\n"
1595 " id=\"dialog\" title=\"Unnamed\" sizeable=\"true\" >\n");
1597 for ( Widget *i = FlatLayout::next( mpRootWidget ); i; i = FlatLayout::next( i ) )
1599 for ( int d = i->getDepth(); d > 0; d-- )
1600 printf(" ");
1601 printf("<%s ", OUSTRING_CSTR( i->getUnoName() ) );
1603 for ( int kind = 0; kind < 2; kind++ )
1605 Widget::PropertyKind wKind = kind == 0 ? Widget::WINDOW_PROPERTY
1606 : Widget::CONTAINER_PROPERTY;
1607 Widget::PropertyIterator it( i, wKind );
1608 while ( it.hasNext() )
1610 beans::Property prop = it.next();
1611 if ( !i->isPropertyTouched( prop.Name, wKind ) )
1612 continue;
1614 rtl::OUString value = i->getProperty( prop.Name, wKind );
1615 if ( prop.Type.getTypeClass() == uno::TypeClass_BOOLEAN )
1617 if ( value.compareToAscii( "0" ) )
1618 value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("false") );
1619 else
1620 value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("true") );
1623 if ( value.getLength() > 0 )
1624 printf("%s%s=\"%s\" ",
1625 kind == 0 ? "" : "cnt:",
1626 OUSTRING_CSTR( toXMLNaming( prop.Name ) ), OUSTRING_CSTR( value )
1631 printf("/>\n");
1633 printf("</dialog>\n");
1636 protected:
1637 virtual void upPressed( USHORT nPos )
1639 Widget *pWidget = getWidget( nPos );
1640 if ( FlatLayout::moveWidget( pWidget, true ) )
1641 rebuild();
1642 selectWidget( pWidget );
1645 virtual void downPressed( USHORT nPos )
1647 Widget *pWidget = getWidget( nPos );
1648 if ( FlatLayout::moveWidget( pWidget, false ) )
1649 rebuild();
1650 selectWidget( pWidget );
1653 virtual void removePressed( USHORT nPos )
1655 Widget *pWidget = getWidget( nPos );
1656 if ( pWidget )
1658 pWidget->up()->removeChild( pWidget );
1659 delete pWidget;
1660 rebuild();
1664 virtual void itemSelected( USHORT nPos )
1666 mpListener->widgetSelected( getWidget( nPos ) );
1667 SortListBox::itemSelected( nPos );
1671 LayoutTree::~LayoutTree()
1673 delete mpRootWidget;
1676 //** EditorImpl
1678 class EditorImpl : public LayoutTree::Listener
1680 void createWidget( const char *unoName );
1682 PropertiesList *mpPropertiesList;
1683 LayoutTree *mpLayoutTree;
1685 layout::PushButton *pImportButton, *pExportButton;
1686 #ifdef FILEDLG
1687 FileDialog *pImportDialog;
1688 #endif
1689 DECL_LINK( ImportButtonHdl, layout::PushButton* );
1690 DECL_LINK( ExportButtonHdl, layout::PushButton* );
1691 #ifdef FILEDLG
1692 DECL_LINK( ImportDialogHdl, FileDialog* );
1693 #endif
1695 // framework stuff
1696 uno::Reference< lang::XMultiServiceFactory > mxFactory;
1697 uno::Reference< awt::XToolkit > mxToolkit;
1698 uno::Reference< awt::XWindow > mxToplevel;
1700 virtual void widgetSelected( Widget *pWidget );
1701 DECL_LINK( CreateWidgetHdl, layout::Button* );
1703 std::list< layout::PushButton *> maCreateButtons;
1705 public:
1707 EditorImpl( layout::Dialog *dialog,
1708 // we should probable open this channel (or whatever its called) ourselves
1709 uno::Reference< lang::XMultiServiceFactory > xMSF );
1710 virtual ~EditorImpl();
1712 void loadFile( const rtl::OUString &aTestFile );
1715 EditorImpl::EditorImpl( layout::Dialog *dialog,
1716 uno::Reference< lang::XMultiServiceFactory > xFactory )
1717 : mxFactory( xFactory )
1718 , mxToplevel( dialog->GetPeerHandle( "dialog" ), uno::UNO_QUERY )
1719 // FIXME: any of these should work
1720 //dialog->getContext()->getRoot(), uno::UNO_QUERY )
1721 // dialog->GetPeer(), uno::UNO_QUERY )
1723 #if DEBUG_PRINT
1724 fprintf (stderr, "EditorImpl()\n");
1725 #endif
1726 // framework
1727 mxToolkit = uno::Reference< awt::XToolkit >(
1728 mxFactory->createInstance(
1729 rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.Toolkit" ) ) ),
1730 uno::UNO_QUERY );
1731 OSL_ASSERT( mxToolkit.is() );
1733 // custom widgets
1734 #if DEBUG_PRINT
1735 fprintf (stderr, "custom widgets\n");
1736 #endif
1737 mpPropertiesList = new PropertiesList( dialog );
1739 mpLayoutTree = new LayoutTree( dialog );
1740 mpLayoutTree->setListener( this );
1742 /* if ( xImport.is() )
1743 mpLayoutTree->getWidget( -1 )->addChild( new Widget( xImport, "import" ) );*/
1745 // create buttons
1746 layout::Container aWidgets( dialog, "create-widget" );
1747 layout::Container aContainers( dialog, "create-container" );
1748 for ( int i = 0; i < WIDGETS_SPECS_LEN; i++ )
1750 layout::PushButton *pBtn = new layout::PushButton( (layout::Window *) dialog );
1751 pBtn->SetText( rtl::OUString::createFromAscii( WIDGETS_SPECS[ i ].pLabel ) );
1752 pBtn->SetClickHdl( LINK( this, EditorImpl, CreateWidgetHdl ) );
1753 if ( WIDGETS_SPECS[ i ].pIconName != NULL )
1755 rtl::OString aPath ("res/commandimagelist/");
1756 aPath += WIDGETS_SPECS[ i ].pIconName;
1757 layout::Image aImg( aPath );
1758 pBtn->SetModeImage( aImg );
1759 pBtn->SetImageAlign( IMAGEALIGN_LEFT );
1761 pBtn->Show();
1762 maCreateButtons.push_back( pBtn );
1763 layout::Container *pBox = WIDGETS_SPECS[ i ].bIsContainer ? &aContainers : &aWidgets;
1764 pBox->Add( pBtn );
1767 #ifdef FILEDLG
1768 fprintf(stderr,"creating file dialog\n");
1769 pImportDialog = new FileDialog( NULL/*(layout::Window *) dialog*/, 0 );
1770 fprintf(stderr,"connecting it\n");
1771 pImportDialog->SetFileSelectHdl( LINK( this, EditorImpl, ImportDialogHdl ) );
1772 fprintf(stderr,"done file dialog\n");
1773 #endif
1775 /* pImportButton = new layout::PushButton( dialog, "import-button" );
1776 pImportButton->SetClickHdl( LINK( this, EditorImpl, ImportButtonHdl ) );*/
1777 pExportButton = new layout::PushButton( dialog, "export-button" );
1778 pExportButton->SetClickHdl( LINK( this, EditorImpl, ExportButtonHdl ) );
1781 EditorImpl::~EditorImpl()
1783 delete mpPropertiesList;
1784 delete mpLayoutTree;
1785 for ( std::list< layout::PushButton * >::const_iterator i = maCreateButtons.begin();
1786 i != maCreateButtons.end(); i++)
1787 delete *i;
1788 delete pImportButton;
1789 delete pExportButton;
1790 #ifdef FILEDLG
1791 delete pImportDialog;
1792 #endif
1795 void EditorImpl::loadFile( const rtl::OUString &aTestFile )
1797 fprintf( stderr, "TEST: layout instance\n" );
1798 uno::Reference< awt::XLayoutRoot > xRoot
1799 ( new EditorRoot( mxFactory, mpLayoutTree->mpRootWidget ) );
1802 mxMSF->createInstance
1803 ( ::rtl::OUString::createFromAscii( "com.sun.star.awt.Layout" ) ),
1804 uno::UNO_QUERY );
1806 if ( !xRoot.is() )
1808 throw uno::RuntimeException(
1809 OUString( RTL_CONSTASCII_USTRINGPARAM("could not create awt Layout component!") ),
1810 uno::Reference< uno::XInterface >() );
1813 #if DEBUG_PRINT
1814 fprintf( stderr, "TEST: initing root\n" );
1815 #endif
1817 uno::Reference< lang::XInitialization > xInit( xRoot, uno::UNO_QUERY );
1818 if ( !xInit.is() )
1820 throw uno::RuntimeException(
1821 OUString( RTL_CONSTASCII_USTRINGPARAM("Layout has no XInitialization!") ),
1822 uno::Reference< uno::XInterface >() );
1825 #if DEBUG_PRINT
1826 fprintf( stderr, "TEST: running parser\n" );
1827 #endif
1828 uno::Sequence< uno::Any > aParams( 1 );
1829 aParams[0] <<= aTestFile;
1830 #if DEBUG_PRINT
1831 fprintf( stderr, "TEST: do it\n" );
1832 #endif
1833 xInit->initialize( aParams );
1834 #if DEBUG_PRINT
1835 fprintf( stderr, "TEST: file loaded\n" );
1836 #endif
1838 mpLayoutTree->rebuild();
1841 void EditorImpl::createWidget( const char *name )
1843 Widget *pWidget = mpLayoutTree->getSelectedWidget();
1845 Widget *pChild = new Widget( rtl::OUString(), mxToolkit, uno::Reference< awt::XLayoutContainer >( mxToplevel, uno::UNO_QUERY ), rtl::OUString::createFromAscii( name ), awt::WindowAttribute::SHOW );
1846 if ( !pWidget->addChild( pChild ) )
1848 delete pChild;
1849 // we may want to popup an error message
1851 else
1853 mpLayoutTree->rebuild();
1854 mpLayoutTree->selectWidget( pWidget );
1858 void EditorImpl::widgetSelected( Widget *pWidget )
1860 // we know can't add widget to a non-container, so let's disable the create
1861 // buttons then. Would be nice to have a method to check if a container is
1862 // full as well...
1863 if ( !pWidget || pWidget->isContainer() )
1865 for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1866 it != maCreateButtons.end(); it++)
1867 (*it)->Enable();
1869 else
1871 for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1872 it != maCreateButtons.end(); it++)
1873 (*it)->Disable();
1876 mpPropertiesList->selectedWidget( pWidget );
1879 IMPL_LINK( EditorImpl, CreateWidgetHdl, layout::Button *, pBtn )
1881 int i = 0;
1882 for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1883 it != maCreateButtons.end(); it++, i++ )
1885 if ( pBtn == *it )
1886 break;
1888 OSL_ASSERT( i < WIDGETS_SPECS_LEN );
1889 createWidget( WIDGETS_SPECS[i].pName );
1890 return 0;
1893 IMPL_LINK( EditorImpl, ImportButtonHdl, layout::PushButton *, pBtn )
1895 (void) pBtn;
1896 #if DEBUG_PRINT
1897 fprintf(stderr, "IMPORT!\n");
1898 #endif
1899 #ifdef FILEDLG
1900 pImportDialog->Execute();
1901 #endif
1903 return 0;
1906 #ifdef FILEDLG
1907 IMPL_LINK( EditorImpl, ImportDialogHdl, FileDialog *, pDialog )
1909 UniString path = pDialog->GetPath();
1910 //fprintf(stderr, "Executing import dialog!\n");
1912 #if DEBUG_PRINT
1913 fprintf(stderr, "got import file: %s\n",rtl::OUStringToOString( path, RTL_TEXTENCODING_ASCII_US ).getStr() );
1914 #endif
1916 return 0;
1918 #endif
1920 IMPL_LINK( EditorImpl, ExportButtonHdl, layout::PushButton *, pBtn )
1922 (void) pBtn;
1923 mpLayoutTree->print();
1924 return 0;
1927 //** Editor, the Dialog
1929 Editor::Editor( uno::Reference< lang::XMultiServiceFactory > xFactory,
1930 rtl::OUString aFile )
1931 : layout::Dialog( (Window*) (NULL), "editor.xml", "dialog" )
1932 , mpImpl( new EditorImpl( this, xFactory ) )
1934 if ( aFile.getLength() )
1935 mpImpl->loadFile( aFile );
1937 // parent:
1938 FreeResource();
1941 Editor::~Editor()
1943 delete mpImpl;