xref: /aoo41x/main/toolkit/workben/layout/editor.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
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.
14  *
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).
20  *
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.
25  *
26  ************************************************************************/
27 
28 #include "editor.hxx"
29 
30 #undef NDEBUG
31 
32 /*
33 #include <stdio.h>
34 #include <string.h>
35 */
36 
37 #include <cassert>
38 #include <cstdio>
39 #include <cstring>
40 #include <list>
41 #include <vector>
42 
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>
54 
55 using namespace layout::css;
56 
57 using rtl::OUString;
58 
59 // FIXME:
60 //#define FILEDLG
61 
62 #include <layout/core/helper.hxx>
63 #include <layout/core/root.hxx>
64 #include <layout/core/helper.hxx>
65 
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  },
87 };
88 const int WIDGETS_SPECS_LEN = sizeof (WIDGETS_SPECS) / sizeof (WidgetSpec);
89 
90 using namespace layout;
91 using namespace layoutimpl;
92 namespace css = ::com::sun::star;
93 
94 static rtl::OUString anyToString (uno::Any value)
95 {
96     try
97     {
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>());
108 
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>());
113 
114             case uno::TypeClass_BOOLEAN:
115             {
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" ) );*/
122             }
123             default:
124                 break;
125         }
126     }
127     catch(...) {}
128     return rtl::OUString();
129 }
130 
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(); }
135 
136 /* XLayoutContainer/XLayoutConstrains are a bit of a hasle to work with.
137    Let's wrap them. */
138 class Widget : public layoutimpl::LayoutWidget
139 {
140     friend class EditorRoot;
141 
142     Widget *mpParent;
143     std::vector< Widget *> maChildren;
144     bool mbForeign;
145 
146     rtl::OUString mrId;
147     rtl::OUString mrLabel, mrUnoName;
148 
149 // TODO: store original properties. And some property handling methods.
150     long mnOriAttrbs;
151     layoutimpl::PropList maOriProps, maOriChildProps;
152 
153 public:
154 
155     // to be used to wrap the root
156     Widget( uno::Reference< awt::XLayoutConstrains > xImport, const char *label )
157         : mpParent( 0 ), mbForeign( true )
158     {
159         mxWidget = xImport;
160         mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY );
161 
162         mrLabel = rtl::OUString( label, strlen( label ), RTL_TEXTENCODING_UTF8  );
163 
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++ )
170         {
171             Widget *pChild = new Widget( aChildren[ i ], "---" );
172             maChildren.push_back( pChild );
173             pChild->mpParent = this;
174         }
175 #endif
176     }
177 
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 )
183     {
184         while ( xParent.is() && !uno::Reference< awt::XWindow >( xParent, uno::UNO_QUERY ).is() )
185         {
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 );
189         }
190 
191         mxWidget = WidgetFactory::createWidget( xToolkit, xParent, unoName, nAttrbs );
192         OSL_ASSERT( mxWidget.is() );
193         mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY );
194 
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 ) )
199             {
200                 const char *label = WIDGETS_SPECS[ i ].pLabel;
201                 mrLabel = rtl::OUString( label, strlen( label ), RTL_TEXTENCODING_UTF8  );
202                 break;
203             }
204 
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
210 
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.
214 
215                 xVclPeer->setProperty( rtl::OUString::createFromAscii( "Text" ),
216                                        uno::makeAny( rtl::OUString::createFromAscii( "new widget" ) ) );
217 #endif
218 
219         // store original properties
220         {
221             PropertyIterator it( this, WINDOW_PROPERTY );
222             while ( it.hasNext() )
223             {
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 );
232             }
233         }
234 
235     }
236 
237     ~Widget()
238     {
239         for ( std::vector< Widget *>::const_iterator it = maChildren.begin();
240              it != maChildren.end(); it++ )
241             delete *it;
242         if ( !mbForeign )
243         {
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();
248         }
249     }
250 
251     uno::Reference< awt::XLayoutConstrains > impl()
252     {
253         return mxWidget;
254     }
255 
256     // LayoutWidget
257     virtual bool addChild( LayoutWidget *pChild )
258     {
259         return addChild( static_cast< Widget * >( pChild ) );
260     }
261 
262     virtual void setProperties( const PropList &rProps )
263     {
264 //        maOriProps = rProps;
265         LayoutWidget::setProperties( rProps );
266     }
267 
268     virtual void setChildProperties( LayoutWidget *pChild, const PropList &rProps )
269     {
270         maOriChildProps = rProps;
271         LayoutWidget::setChildProperties( pChild, rProps );
272     }
273 
274     // tree travel
275     Widget *up()
276     {
277         return mpParent;
278     }
279 
280     Widget *down()
281     {
282         if ( maChildren.empty() )
283             return NULL;
284         return maChildren.front();
285     }
286 
287     Widget *next()
288     {
289         if ( mpParent )
290         {
291             int pos = mpParent->getChildPos( this );
292             return mpParent->getChild( pos+1 );
293         }
294         return NULL;
295     }
296 
297     Widget *prev()
298     {
299         if ( mpParent )
300         {
301             int pos = mpParent->getChildPos( this );
302             return mpParent->getChild( pos-1 );
303         }
304         return NULL;
305     }
306 
307     // handle
308     bool addChild( Widget *pChild, int pos = 0xffff )
309     {
310         if ( !mxContainer.is() )
311             return false;
312 
313         uno::Sequence< uno::Reference < awt::XLayoutConstrains > > aChildren;
314         aChildren = mxContainer->getChildren();
315         int nChildrenLen = aChildren.getLength();
316 
317         // ugly, but let's check if the container is next to full...
318         try {
319             mxContainer->addChild( pChild->mxWidget );
320         }
321         catch( awt::MaxChildrenException ex ) {
322             return false;
323         }
324 
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 );
334         }
335         else
336             maChildren.push_back( pChild );
337 
338         OSL_ASSERT( pChild->mpParent == NULL );
339         pChild->mpParent = this;
340 
341         // store container props
342         {
343             pChild->maOriChildProps.clear();
344             PropertyIterator it( pChild, CONTAINER_PROPERTY );
345             while ( it.hasNext() )
346             {
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());
356 		}
357             }
358         }
359 
360         return true;
361     }
362 
363     bool removeChild( Widget *pChild )
364     {
365         if ( !mxContainer.is() || pChild->mpParent != this )
366             return false;
367 
368         mxContainer->removeChild( pChild->mxWidget );
369 
370         unsigned int pos = getChildPos( pChild );
371         if ( pos < maChildren.size() )
372             maChildren.erase( maChildren.begin()+pos );
373         pChild->mpParent = NULL;
374 
375         return true;
376     }
377 
378     bool swapWithChild( Widget *pChild )
379     {
380         if ( !pChild->isContainer() )
381             return false;
382 
383         // remove all child's childrens, and try to add them here
384         removeChild( pChild );
385 
386         // keep a copy for failure
387         std::vector< Widget *> aChildren = maChildren;
388         std::vector< Widget *> aChildChildren = pChild->maChildren;
389 
390         for ( std::vector< Widget *>::const_iterator it = aChildChildren.begin();
391               it != aChildChildren.end(); it++ )
392             pChild->removeChild( *it );
393 
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;
405             }
406 
407         Widget *pParent = up();
408 
409         if ( pParent )
410         {
411             pParent->removeChild( this );
412             pParent->addChild( pChild );
413         }
414         pChild->addChild( this );
415         return true;
416     }
417 
418     unsigned int getChildPos( Widget *pChild )
419     {
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;
426     }
427 
428     Widget *getChild( int pos )
429     {
430         if ( pos >= 0 && pos < (signed) maChildren.size() )
431             return *(maChildren.begin() + pos);
432         return NULL;
433     }
434 
435     bool isContainer()
436     { return mxContainer.is(); }
437     unsigned int getChildrenLen()
438     { return maChildren.size(); }
439 
440     rtl::OUString getLabel() const
441     { return mrLabel; }
442     rtl::OUString getUnoName() const
443     { return mrUnoName; }
444 
445     int getDepth()
446     {
447         int depth = 0;
448         for ( Widget *pWidget = mpParent; pWidget; pWidget = pWidget->mpParent )
449             depth++;
450         return depth;
451     }
452 
453     enum PropertyKind {
454         WINDOW_PROPERTY, CONTAINER_PROPERTY, WINBITS_PROPERTY
455     };
456 
457     static rtl::OUString findProperty( const PropList &props, rtl::OUString propName )
458     {
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();
466     }
467 
468     rtl::OUString getOriginalProperty( rtl::OUString rPropName, PropertyKind rKind )
469     {
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;
481         }
482 
483         return rValue;
484     }
485 
486     rtl::OUString getProperty( rtl::OUString rPropName, PropertyKind rKind )
487     {
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;
501         }
502 
503         return rValue;
504     }
505 
506     bool isPropertyTouched( rtl::OUString propName, PropertyKind rKind )
507     {
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;
515     }
516 
517     using LayoutWidget::setProperty;
518 
519     void setProperty( rtl::OUString rPropName, PropertyKind rKind, uno::Any rValue )
520     {
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;
533         }
534     }
535 
536     struct PropertyIterator {
537         friend class Widget;
538         PropertyKind mrKind;
539         uno::Sequence< beans::Property > maProps;
540         int nPropIt;
541 
542         PropertyIterator( Widget *pWidget, PropertyKind rKind )
543             : mrKind( rKind ), nPropIt( 0 )
544         {
545             switch ( rKind )
546             {
547                 case WINDOW_PROPERTY:
548                     if ( layoutimpl::prophlp::canHandleProps( pWidget->mxWidget ) )
549                     {
550                         uno::Reference< beans::XPropertySetInfo > xInfo
551                             = layoutimpl::prophlp::queryPropertyInfo( pWidget->mxWidget );
552                         if ( !xInfo.is() )
553                             return;
554 
555                         maProps = xInfo->getProperties();
556                     }
557                     break;
558                 case CONTAINER_PROPERTY:
559                     if ( pWidget->mpParent )
560                     {
561                         uno::Reference< beans::XPropertySet >xParentSet(
562                             pWidget->mpParent->mxContainer->getChildProperties( pWidget->mxWidget ) );
563                         if ( xParentSet.is())
564                         {
565                             uno::Reference< beans::XPropertySetInfo > xInfo( xParentSet->getPropertySetInfo() );
566                             if ( xInfo.is() )
567                                 maProps = xInfo->getProperties();
568                         }
569                     }
570                     break;
571                 case WINBITS_PROPERTY:
572                     // TODO
573                     break;
574             }
575         }
576 
577         bool hasNext()
578         {
579             return nPropIt < maProps.getLength();
580         }
581 
582         beans::Property next()
583         {
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++ ];
590         }
591     };
592 };
593 
594 class EditorRoot : public layoutimpl::LayoutRoot {
595     Widget *mpParent;
596 
597 public:
598     EditorRoot( const uno::Reference< lang::XMultiServiceFactory >& xFactory,
599                 Widget *pParent )
600         : layoutimpl::LayoutRoot( xFactory ), mpParent( pParent )
601     {
602     }
603 
604     // generation
605     virtual layoutimpl::LayoutWidget *create( rtl::OUString id, const rtl::OUString unoName,
606                                               long attrbs, uno::Reference< awt::XLayoutContainer > xParent )
607     {
608         if ( unoName.compareToAscii( "dialog" ) == 0 )
609             return mpParent;
610 
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 );
615 
616         if ( pWidget->mxContainer.is() )
617             pWidget->mxContainer->setLayoutUnit( mpParent->mxContainer->getLayoutUnit() );
618 
619         return pWidget;
620     }
621 };
622 
623 /* Working with the layout in 1D, as if it was a flat list. */
624 namespace FlatLayout
625 {
626 Widget *next( Widget *pWidget )
627 {
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;
637 }
638 
639 /*
640   Widget *prev( Widget *pWidget )
641   {
642   Widget *pPrev;
643   pPrev = pWidget->prev();
644   if ( !pPrev )
645   return pWidget->up();
646 
647   Widget *pBottom = pPrev->down();
648   if ( pBottom )
649   {
650   while ( pBottom->down() || pBottom->next() )
651   {
652   for ( Widget *pNext = pBottom->next(); pNext; pNext = pNext->next() )
653   pBottom = pNext;
654   Widget *pDown = pBottom->down();
655   if ( pDown )
656   pBottom = pDown;
657   }
658   return pBottom;
659   }
660   return pPrev;
661   }
662 */
663 
664 bool moveWidget( Widget *pWidget, bool up /*or down*/ )
665 {
666     // Keep child parent&pos for in case of failure
667     Widget *pOriContainer = pWidget->up();
668     unsigned int oriChildPos = pOriContainer->getChildPos( pWidget );
669 
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;
675 
676     // try to swap with parent or child
677     // We need to allow for this at least for the root node...
678     if ( !pSibling )
679     {
680         if ( up )
681         {
682             if ( pContainer->swapWithChild( pWidget ) )
683                 return true;
684         }
685         else
686         {
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
692         }
693     }
694 
695     pContainer->removeChild( pWidget );
696 
697     // if has up sibling -- append to it, else swap with it
698     if ( pSibling )
699     {
700         if ( pSibling->addChild( pWidget, up ? 0xffff : 0 ) )
701             return true;
702 
703         unsigned int childPos = pContainer->getChildPos( pSibling );
704         if ( pContainer->addChild( pWidget, childPos + (up ? 0 : 1) ) )
705             return true;  // should always be succesful
706     }
707     // go through parents -- try to get prepended to them
708     else
709     {
710         for ( ; pContainer && pContainer->up(); pContainer = pContainer->up() )
711         {
712             unsigned int childPos = pContainer->up()->getChildPos( pContainer );
713             if ( pContainer->up()->addChild( pWidget, childPos + (up ? 0 : 1) ) )
714                 return true;
715         }
716     }
717 
718     // failed -- try to get it to its old position
719     if ( !pOriContainer->addChild( pWidget, oriChildPos ) )
720     {
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;
725     }
726     return false;
727 }
728 
729 // NOTE: root is considered to be number -1
730 Widget *get( Widget *pRoot, int nb )
731 {
732     Widget *it;
733     for ( it = pRoot; it != NULL && nb >= 0; it = next( it ) )
734         nb--;
735     return it;
736 }
737 
738 int get( Widget *pRoot, Widget *pWidget )
739 {
740     int nRet = -1;
741     Widget *it;
742     for ( it = pRoot; it != NULL && it != pWidget; it = next( it ) )
743         nRet++;
744     return nRet;
745 }
746 }
747 
748 //** PropertiesList widget
749 
750 class PropertiesList : public layout::Table
751 {
752     class PropertyEntry
753     {
754         friend class PropertiesList;
755 
756         /* wrapper between the widget and Any */
757         struct AnyWidget
758         {
759             DECL_LINK( ApplyPropertyHdl, layout::Window* );
760             DECL_LINK( FlagToggledHdl, layout::CheckBox* );
761 
762             AnyWidget( Widget *pWidget, rtl::OUString aPropName, Widget::PropertyKind aPropKind )
763                 : mpWidget( pWidget ), maPropName( aPropName ), maPropKind( aPropKind )
764             {
765                 mpFlag = 0;
766                 mbBlockFlagCallback = false;
767                 bFirstGet = true;
768             }
769 
770             virtual ~AnyWidget()
771             {
772 #if DEBUG_PRINT
773                 fprintf(stderr, "~AnyWidget\n");
774 #endif
775             }
776 
777             void save( uno::Any aValue )
778             {
779                 mpWidget->setProperty( maPropName, maPropKind, aValue );
780                 checkProperty();
781             }
782 
783             void checkProperty()
784             {
785                 bool flag = mpWidget->isPropertyTouched( maPropName, maPropKind );
786 
787                 if ( mpFlag && mpFlag->IsChecked() != (BOOL)flag )
788                 {
789                     CheckFlag( flag, true );
790                 }
791             }
792 
793             void CheckFlag( bool bValue, bool bBlockCallback )
794             {
795                 if ( bBlockCallback )
796                     mbBlockFlagCallback = true;
797                 mpFlag->Check( bValue );
798                 mbBlockFlagCallback = false;
799             }
800 
801             bool bFirstGet;  // HACK
802             rtl::OUString getValue()
803             {
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;
812             }
813 
814             // FIXME: wrapper should have a base class for this...
815             virtual layout::Window *getWindow() = 0;
816             virtual layout::Container *getContainer() { return NULL; }
817 
818             virtual void load() = 0;
819             virtual void store() = 0;
820 
821             Widget *mpWidget;
822             rtl::OUString maPropName;
823             Widget::PropertyKind maPropKind;
824             layout::CheckBox *mpFlag;
825             bool mbBlockFlagCallback;
826         };
827 
828         struct AnyEdit : public AnyWidget, layout::HBox
829         {
830             layout::Edit *mpEdit;
831             bool mbMultiLine;
832             layout::PushButton *mpExpand;
833             DECL_LINK( ExpandEditHdl, layout::PushButton* );
834 
835             // so we can create widgets (like transforming the Edit into a
836             // MultiLineEdit)
837             layout::Window *mpWinParent;
838 
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 )
842             {
843                 mpEdit = NULL;
844                 mpExpand = new layout::PushButton( pWinParent, WB_TOGGLE );
845                 mpExpand->SetToggleHdl( LINK( this, AnyEdit, ExpandEditHdl ) );
846                 setAsMultiLine( false );
847 
848                 load();
849             }
850 
851             virtual ~AnyEdit()
852             {
853                 delete mpEdit;
854                 delete mpExpand;
855             }
856 
857             virtual layout::Window *getWindow()
858             { return NULL; }
859             virtual layout::Container *getContainer()
860             { return this; }
861 
862             void setAsMultiLine( bool bMultiLine )
863             {
864                 Clear();
865                 XubString text;
866                 if ( mpEdit )
867                 {
868                     text = mpEdit->GetText();
869                     printf("Remove mpEdit and expand\n");
870                     Remove( mpEdit );
871                     Remove( mpExpand );
872                     delete mpEdit;
873                 }
874 
875                 if ( bMultiLine )
876                 {
877                     mpEdit = new layout::Edit( mpWinParent, WB_BORDER );
878                     mpExpand->SetText( String::CreateFromAscii( "-" ) );
879                 }
880                 else
881                 {
882                     mpEdit = new layout::Edit( mpWinParent, WB_BORDER );
883                     mpExpand->SetText( String::CreateFromAscii( "+" ) );
884                 }
885 
886                 mpEdit->SetText( text );
887                 mpEdit->SetModifyHdl( LINK( this, AnyEdit, ApplyPropertyHdl ) );
888 
889                 Add( mpEdit, true, true, 0 );
890                 Add( mpExpand, false, true, 0 );
891 
892                 mbMultiLine = bMultiLine;
893             }
894 
895 #if 0
896             // TODO: make this global... We'll likely need it for export...
897             struct Translate {
898                 const char *ori, *dest;
899             };
900             static rtl::OUString stringReplace( rtl::OUString _str,
901                                                 Translate *trans )
902             {
903                 const sal_Unicode *str = _str.getStr();
904                 rtl::OUStringBuffer buf;
905                 int i, j, k;
906                 for ( i = 0; i < _str.getLength(); i++ )
907                 {
908                     for ( j = 0; trans[ j ].ori; j++ )
909                     {
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 ] )
915                         {
916                             // found substring
917                             buf.appendAscii( trans[ j ].dest );
918                             i += k;
919                             continue;
920                         }
921                     }
922                     buf.append( str[ i ] );
923                 }
924                 return buf.makeStringAndClear();
925             }
926 #endif
927 
928             virtual void load()
929             {
930 #if 0
931                 // replace end of lines by "\\n" strings
932                 Translate trans[] = {
933                     { "\\", "\\\\" }, { "\n", "\\n" }, { 0, 0 }
934                 };
935                 rtl::OUString str = anyToString( getValue() );
936                 str = stringReplace( str, trans );
937                 SetText( str );
938 #endif
939                 mpEdit->SetText( getValue() );
940                 checkProperty();
941             }
942 
943             virtual void store()
944             {
945 #if 0
946                 // replace "\\n" strings by actual end of lines
947                 Translate trans[] = {
948                     { "\\\\", "\\"  }, { "\\n", "\n" },
949                     { "\\", "" }, { 0, 0 }
950                 };
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() ) );
956             }
957         };
958 
959         struct AnyInteger : public AnyWidget, NumericField
960         {
961             AnyInteger( Widget *pWidget, rtl::OUString aPropName,
962                         Widget::PropertyKind aPropKind, Window *pWinParent )
963                 : AnyWidget( pWidget, aPropName, aPropKind ), NumericField( pWinParent, WB_SPIN|WB_BORDER )
964             {
965                 load();
966                 SetModifyHdl( LINK( this, AnyInteger, ApplyPropertyHdl ) );
967             }
968 
969             virtual Window *getWindow()
970             { return this; }
971 
972             virtual void load()
973             {
974                 OUString text = getValue();
975                 SetText( text.getStr() );
976                 checkProperty();
977             }
978 
979             virtual void store()
980             {
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() ) );
985             }
986         };
987 
988         struct AnyFloat : public AnyInteger
989         {
990             AnyFloat( Widget *pWidget, rtl::OUString aPropName,
991                       Widget::PropertyKind aPropKind, Window *pWinParent )
992                 : AnyInteger( pWidget, aPropName, aPropKind, pWinParent )
993             {}
994 
995             virtual void store()
996             {
997                 save( uno::makeAny( rtl::OUString( GetText() ).toDouble() ) );
998             }
999         };
1000 
1001         struct AnyCheckBox : public AnyWidget, layout::CheckBox
1002         {
1003             AnyCheckBox( Widget *pWidget, rtl::OUString aPropName,
1004                          Widget::PropertyKind aPropKind, layout::Window *pWinParent )
1005                 : AnyWidget( pWidget, aPropName, aPropKind ), layout::CheckBox( pWinParent )
1006             {
1007                 // adding some whitespaces to make the hit area larger
1008 //                SetText( String::CreateFromAscii( "" ) );
1009                 load();
1010                 SetToggleHdl( LINK( this, AnyWidget, ApplyPropertyHdl ) );
1011             }
1012 
1013             virtual ~AnyCheckBox()
1014             {
1015 #if DEBUG_PRINT
1016                 fprintf(stderr, "~AnyCheckBox\n");
1017 #endif
1018             }
1019 
1020             virtual layout::Window *getWindow()
1021             { return this; }
1022 
1023             virtual void load()
1024             {
1025 #if DEBUG_PRINT
1026                 fprintf(stderr, "loading boolean value\n");
1027 #endif
1028                 Check( getValue().toInt64() != 0 );
1029                 setLabel();
1030                 checkProperty();
1031             }
1032 
1033             virtual void store()
1034             {
1035                 save( uno::makeAny( IsChecked() ) );
1036                 setLabel();
1037             }
1038 
1039             void setLabel()
1040             {
1041                 SetText( String::CreateFromAscii( IsChecked() ? "true" : "false" ) );
1042             }
1043         };
1044 
1045         struct AnyListBox : public AnyWidget, layout::ListBox
1046         {
1047             AnyListBox( Widget *pWidget, rtl::OUString aPropName,
1048                         Widget::PropertyKind aPropKind, Window *pWinParent )
1049                 : AnyWidget( pWidget, aPropName, aPropKind ), layout::ListBox( pWinParent, WB_DROPDOWN )
1050             {
1051                 SetSelectHdl( LINK( this, AnyWidget, ApplyPropertyHdl ) );
1052             }
1053 
1054             virtual layout::Window *getWindow()
1055             { return this; }
1056 
1057             virtual void load()
1058             {
1059                 SelectEntryPos( sal::static_int_cast< USHORT >( getValue().toInt32() ) );
1060                 checkProperty();
1061             }
1062 
1063             virtual void store()
1064             {
1065                 save( uno::makeAny( (short) GetSelectEntryPos() ) );
1066             }
1067         };
1068 
1069         struct AnyAlign : public AnyListBox
1070         {
1071             AnyAlign( Widget *pWidget, rtl::OUString aPropName,
1072                       Widget::PropertyKind aPropKind, Window *pWinParent )
1073                 : AnyListBox( pWidget, aPropName, aPropKind, pWinParent )
1074             {
1075                 InsertEntry( XubString::CreateFromAscii( "Left" ) );
1076                 InsertEntry( XubString::CreateFromAscii( "Center" ) );
1077                 InsertEntry( XubString::CreateFromAscii( "Right" ) );
1078                 load();
1079             }
1080         };
1081 
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
1086         {
1087             AnyComboBox( Widget *pWidget, rtl::OUString aPropName,
1088                          Widget::PropertyKind aPropKind, Window *pWinParent )
1089                 : AnyWidget( pWidget, aPropName, aPropKind ), layout::ComboBox( pWinParent, WB_DROPDOWN )
1090             {
1091                 SetModifyHdl( LINK( this, AnyComboBox, ApplyPropertyHdl ) );
1092             }
1093 
1094             virtual layout::Window *getWindow()
1095             { return this; }
1096 
1097             virtual void load()
1098             {
1099                 SetText( getValue() );
1100                 checkProperty();
1101             }
1102 
1103             virtual void store()
1104             {
1105                 save( uno::makeAny( (rtl::OUString) GetText() ) );
1106             }
1107         };
1108 
1109         struct AnyFontStyle : public AnyComboBox
1110         {
1111             AnyFontStyle( Widget *pWidget, rtl::OUString aPropName,
1112                           Widget::PropertyKind aPropKind, Window *pWinParent )
1113                 : AnyComboBox( pWidget, aPropName, aPropKind, pWinParent )
1114             {
1115                 InsertEntry( XubString::CreateFromAscii( "Bold" ) );
1116                 InsertEntry( XubString::CreateFromAscii( "Italic" ) );
1117                 InsertEntry( XubString::CreateFromAscii( "Bold Italic" ) );
1118                 InsertEntry( XubString::CreateFromAscii( "Fett" ) );
1119                 load();
1120             }
1121         };
1122 
1123         layout::FixedText *mpLabel;
1124         layout::CheckBox *mpFlag;
1125         AnyWidget *mpValue;
1126 
1127     public:
1128         PropertyEntry( layout::Window *pWinParent, AnyWidget *pAnyWidget )
1129         {
1130             mpLabel = new layout::FixedText( pWinParent );
1131             {
1132                 // append ':' to aPropName
1133                 rtl::OUStringBuffer buf( pAnyWidget->maPropName );
1134                 buf.append( sal_Unicode (':') );
1135                 mpLabel->SetText( buf.makeStringAndClear() );
1136             }
1137             mpValue = pAnyWidget;
1138             mpFlag = new layout::CheckBox( pWinParent );
1139             mpFlag->SetToggleHdl( LINK( mpValue, AnyWidget, FlagToggledHdl ) );
1140             mpValue->mpFlag = mpFlag;
1141         }
1142 
1143         ~PropertyEntry()
1144         {
1145 #if DEBUG_PRINT
1146                 fprintf(stderr, "REMOVING label, flag and value\n");
1147 #endif
1148             delete mpLabel;
1149             delete mpFlag;
1150             delete mpValue;
1151         }
1152 
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 )
1157         {
1158             AnyWidget *pAnyWidget;
1159             switch (nType) {
1160                 case uno::TypeClass_STRING:
1161                     if ( aPropName.compareToAscii( "FontStyleName" ) == 0 )
1162                     {
1163                         pAnyWidget = new AnyFontStyle( pWidget, aPropName, aPropKind, pWinParent );
1164                         break;
1165                     }
1166                     pAnyWidget = new AnyEdit( pWidget, aPropName, aPropKind, pWinParent );
1167                     break;
1168                 case uno::TypeClass_SHORT:
1169                     if ( aPropName.compareToAscii( "Align" ) == 0 )
1170                     {
1171                         pAnyWidget = new AnyAlign( pWidget, aPropName, aPropKind, pWinParent );
1172                         break;
1173                     }
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;
1188             }
1189             return new PropertyEntry( pWinParent, pAnyWidget );
1190         }
1191     };
1192 
1193     layout::Window *mpParentWindow;
1194 
1195     std::list< PropertyEntry* > maPropertiesList;
1196     layout::FixedLine *mpSeparator;
1197 
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 )
1202     {
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"
1210         };
1211 
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++ )
1215         {
1216             if ( strcmp(toIgnoreList[i-1], toIgnoreList[i]) >= 0 )
1217             {
1218                 printf("ignore list not ordered properly: "
1219                        "'%s' should come before '%s'\n",
1220                        toIgnoreList[i], toIgnoreList[i-1]);
1221                 exit(-1);
1222             }
1223         }
1224 #endif
1225 
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;
1238     }
1239 
1240 public:
1241     PropertiesList( layout::Dialog *dialog )
1242         : layout::Table( dialog, "properties-box" )
1243         , mpParentWindow( dialog ), mpSeparator( 0 )
1244     {
1245     }
1246 
1247     ~PropertiesList()
1248     {
1249         clear();
1250     }
1251 
1252 private:
1253     // auxiliary, add properties from the peer to the list
1254     void addProperties( Widget *pWidget, Widget::PropertyKind rKind )
1255     {
1256         Widget::PropertyIterator it( pWidget, rKind );
1257         while ( it.hasNext() )
1258         {
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() );
1264 
1265             PropertyEntry *propEntry = PropertyEntry::construct(
1266                 pWidget, name, rKind, type, mpParentWindow );
1267 
1268             if ( propEntry )
1269             {
1270                 Add( propEntry->mpLabel, false, false );
1271 
1272                 // HACK: one of these will return Null...
1273                 Add( propEntry->mpValue->getWindow(), true, false );
1274                 Add( propEntry->mpValue->getContainer(), true, false );
1275 
1276                 Add( propEntry->mpFlag, false, false );
1277                 maPropertiesList.push_back( propEntry );
1278             }
1279         }
1280     }
1281 
1282 public:
1283     void selectedWidget( Widget *pWidget )
1284     {
1285         clear();
1286 
1287         if ( !pWidget )
1288             return;
1289 
1290         addProperties( pWidget, Widget::CONTAINER_PROPERTY );
1291 
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 );
1295 
1296         addProperties( pWidget, Widget::WINDOW_PROPERTY );
1297 
1298         ShowAll( true );
1299     }
1300 
1301     void clear()
1302     {
1303         ///FIXME: crash
1304         Container::Clear();
1305 
1306         for ( std::list< PropertyEntry* >::iterator it = maPropertiesList.begin();
1307               it != maPropertiesList.end(); it++)
1308             delete *it;
1309         maPropertiesList.clear();
1310 
1311         delete mpSeparator;
1312         mpSeparator = NULL;
1313     }
1314 };
1315 
1316 IMPL_LINK( PropertiesList::PropertyEntry::AnyWidget, ApplyPropertyHdl, layout::Window *, pWin )
1317 {
1318     (void) pWin;
1319     store();
1320     return 0;
1321 }
1322 
1323 IMPL_LINK( PropertiesList::PropertyEntry::AnyWidget, FlagToggledHdl, layout::CheckBox *, pCheck )
1324 {
1325 #if DEBUG_PRINT
1326     fprintf(stderr, "Property flag pressed -- is: %d\n", pCheck->IsChecked());
1327 #endif
1328     if ( !mbBlockFlagCallback )
1329     {
1330         bool checked = pCheck->IsChecked();
1331         if ( !checked )  // revert
1332         {
1333 #if DEBUG_PRINT
1334             fprintf(stderr, "revert\n");
1335 #endif
1336             load();
1337         }
1338         else
1339         {
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 );
1347         }
1348     }
1349 #if DEBUG_PRINT
1350     else
1351         fprintf(stderr, "Property flag pressed -- BLOCKED\n");
1352 #endif
1353     return 0;
1354 }
1355 
1356 IMPL_LINK( PropertiesList::PropertyEntry::AnyEdit, ExpandEditHdl, layout::PushButton *, pBtn )
1357 {
1358     setAsMultiLine( pBtn->IsChecked() );
1359     return 0;
1360 }
1361 
1362 //** SortListBox auxiliary widget
1363 
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;
1372 
1373 protected:
1374     layout::ListBox *mpListBox;
1375 
1376     virtual void upPressed( USHORT nPos )
1377     {
1378         XubString str = mpListBox->GetSelectEntry();
1379         mpListBox->RemoveEntry( nPos );
1380         nPos = mpListBox->InsertEntry( str, nPos-1 );
1381         mpListBox->SelectEntryPos( nPos );
1382     }
1383 
1384     virtual void downPressed( USHORT nPos )
1385     {
1386         XubString str = mpListBox->GetSelectEntry();
1387         mpListBox->RemoveEntry( nPos );
1388         nPos = mpListBox->InsertEntry( str, nPos+1 );
1389         mpListBox->SelectEntryPos( nPos );
1390     }
1391 
1392     virtual void removePressed( USHORT nPos )
1393     {
1394         mpListBox->RemoveEntry( nPos );
1395     }
1396 
1397     virtual void itemSelected( USHORT nPos )
1398     {
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
1402 
1403         if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1404         {
1405             mpUpButton->Disable();
1406             mpDownButton->Disable();
1407             mpRemoveButton->Disable();
1408         }
1409         else
1410         {
1411             mpUpButton->Enable();
1412             mpDownButton->Enable();
1413             mpRemoveButton->Enable();
1414         }
1415     }
1416 
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 )
1422     {
1423         mpListBox->SetSelectHdl( LINK( this, SortListBox, ItemSelectedHdl ) );
1424 
1425         mpUpButton->SetModeImage( layout::Image ( "res/commandimagelist/lc_moveup.png" ) );
1426         mpUpButton->SetImageAlign( IMAGEALIGN_LEFT );
1427         mpUpButton->SetClickHdl( LINK( this, SortListBox, UpPressedHdl ) );
1428 
1429         mpDownButton->SetModeImage( layout::Image ( "res/commandimagelist/lc_movedown.png" ) );
1430         mpDownButton->SetImageAlign( IMAGEALIGN_LEFT );
1431         mpDownButton->SetClickHdl( LINK( this, SortListBox, DownPressedHdl ) );
1432 
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 ) );
1437 
1438         // fire an un-select event
1439         itemSelected( LISTBOX_ENTRY_NOTFOUND );
1440     }
1441 
1442     virtual ~SortListBox();
1443 };
1444 
1445 SortListBox::~SortListBox()
1446 {
1447     delete mpListBox;
1448     delete mpUpButton;
1449     delete mpDownButton;
1450     delete mpRemoveButton;
1451 }
1452 
1453 IMPL_LINK( SortListBox, UpPressedHdl, layout::Button *, pBtn )
1454 {
1455     (void) pBtn;
1456     USHORT pos = mpListBox->GetSelectEntryPos();
1457     if ( pos > 0 && pos != LISTBOX_ENTRY_NOTFOUND )
1458         upPressed( pos );
1459     return 0;
1460 }
1461 
1462 IMPL_LINK( SortListBox, DownPressedHdl, layout::Button *, pBtn )
1463 {
1464     (void) pBtn;
1465     USHORT pos = mpListBox->GetSelectEntryPos();
1466     if ( pos < mpListBox->GetEntryCount() && pos != LISTBOX_ENTRY_NOTFOUND )
1467         downPressed( pos );
1468     return 0;
1469 }
1470 
1471 IMPL_LINK( SortListBox, RemovePressedHdl, layout::Button *, pBtn )
1472 {
1473     (void) pBtn;
1474     USHORT pos = mpListBox->GetSelectEntryPos();
1475     if ( pos != LISTBOX_ENTRY_NOTFOUND )
1476         removePressed( pos );
1477     return 0;
1478 }
1479 
1480 IMPL_LINK( SortListBox, ItemSelectedHdl, layout::ListBox *, pList )
1481 {
1482     (void) pList;
1483     USHORT pos = mpListBox->GetSelectEntryPos();
1484     itemSelected( pos );
1485     return 0;
1486 }
1487 
1488 //** LayoutTree widget
1489 
1490 class LayoutTree : public SortListBox
1491 {
1492 public:
1493     struct Listener
1494     {
1495         virtual void widgetSelected( Widget *pWidget ) = 0;
1496     };
1497 
1498 private:
1499     Listener *mpListener;
1500 
1501 public:
1502     Widget *mpRootWidget;
1503 
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" ) )
1509     {
1510         layout::PeerHandle handle = dialog->GetPeerHandle( "preview-box" );
1511         uno::Reference< awt::XLayoutConstrains > xWidget( handle, uno::UNO_QUERY );
1512         mpRootWidget = new Widget( xWidget, "root" );
1513     }
1514 
1515     virtual ~LayoutTree();
1516 
1517     Widget *getWidget( int nPos )
1518     {
1519         if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1520             return FlatLayout::get( mpRootWidget, nPos );
1521         return NULL;
1522     }
1523 
1524     Widget *getSelectedWidget()
1525     {
1526         Widget *pWidget = getWidget( mpListBox->GetSelectEntryPos() );
1527         if ( !pWidget )  // return root, if none selected
1528             pWidget = mpRootWidget;
1529         return pWidget;
1530     }
1531 
1532     void selectWidget( Widget *pWidget )
1533     {
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 ) );
1539     }
1540 
1541     void rebuild()
1542     {
1543         struct inner
1544         {
1545             // pads a string with whitespaces
1546             static rtl::OUString padString( rtl::OUString name, int depth )
1547             {
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 );
1554             }
1555         };
1556 
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 ) );
1560 
1561         // any selection, no longer is. ListBox doesn't fire the event on this case;
1562         // force it.
1563         itemSelected( LISTBOX_ENTRY_NOTFOUND );
1564     }
1565 
1566     void setListener( Listener *pListener )
1567     { mpListener = pListener; }
1568 
1569     // print in XML format...
1570 
1571     static rtl::OUString toXMLNaming (const rtl::OUString &string)
1572     {
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' )
1577             {
1578                 if ( i > 0 )
1579                     buffer.append ((sal_Unicode) '-');
1580                 buffer.append ((sal_Unicode) (str[i] - 'A' + 'a'));
1581             }
1582             else
1583                 buffer.append ((sal_Unicode) str[i]);
1584         }
1585 
1586         return buffer.makeStringAndClear();
1587     }
1588 
1589     void print()
1590     {
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");
1596 
1597         for ( Widget *i = FlatLayout::next( mpRootWidget ); i; i = FlatLayout::next( i ) )
1598         {
1599             for ( int d = i->getDepth(); d > 0; d-- )
1600                 printf("    ");
1601             printf("<%s ", OUSTRING_CSTR( i->getUnoName() ) );
1602 
1603             for ( int kind = 0; kind < 2; kind++ )
1604             {
1605                 Widget::PropertyKind wKind = kind == 0 ? Widget::WINDOW_PROPERTY
1606                     : Widget::CONTAINER_PROPERTY;
1607                 Widget::PropertyIterator it( i, wKind );
1608                 while ( it.hasNext() )
1609                 {
1610                     beans::Property prop = it.next();
1611                     if ( !i->isPropertyTouched( prop.Name, wKind ) )
1612                         continue;
1613 
1614                     rtl::OUString value = i->getProperty( prop.Name, wKind );
1615                     if ( prop.Type.getTypeClass() == uno::TypeClass_BOOLEAN )
1616                     {
1617                         if ( value.compareToAscii( "0" ) )
1618                             value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("false") );
1619                         else
1620                             value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("true") );
1621                     }
1622 
1623                     if ( value.getLength() > 0 )
1624                         printf("%s%s=\"%s\" ",
1625                                kind == 0 ? "" : "cnt:",
1626                                OUSTRING_CSTR( toXMLNaming( prop.Name ) ), OUSTRING_CSTR( value )
1627                             );
1628 
1629                 }
1630             }
1631             printf("/>\n");
1632         }
1633         printf("</dialog>\n");
1634     }
1635 
1636 protected:
1637     virtual void upPressed( USHORT nPos )
1638     {
1639         Widget *pWidget = getWidget( nPos );
1640         if ( FlatLayout::moveWidget( pWidget, true ) )
1641             rebuild();
1642         selectWidget( pWidget );
1643     }
1644 
1645     virtual void downPressed( USHORT nPos )
1646     {
1647         Widget *pWidget = getWidget( nPos );
1648         if ( FlatLayout::moveWidget( pWidget, false ) )
1649             rebuild();
1650         selectWidget( pWidget );
1651     }
1652 
1653     virtual void removePressed( USHORT nPos )
1654     {
1655         Widget *pWidget = getWidget( nPos );
1656         if ( pWidget )
1657         {
1658             pWidget->up()->removeChild( pWidget );
1659             delete pWidget;
1660             rebuild();
1661         }
1662     }
1663 
1664     virtual void itemSelected( USHORT nPos )
1665     {
1666         mpListener->widgetSelected( getWidget( nPos ) );
1667         SortListBox::itemSelected( nPos );
1668     }
1669 };
1670 
1671 LayoutTree::~LayoutTree()
1672 {
1673     delete mpRootWidget;
1674 }
1675 
1676 //** EditorImpl
1677 
1678 class EditorImpl : public LayoutTree::Listener
1679 {
1680     void createWidget( const char *unoName );
1681 
1682     PropertiesList *mpPropertiesList;
1683     LayoutTree *mpLayoutTree;
1684 
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
1694 
1695     // framework stuff
1696     uno::Reference< lang::XMultiServiceFactory > mxFactory;
1697     uno::Reference< awt::XToolkit > mxToolkit;
1698     uno::Reference< awt::XWindow > mxToplevel;
1699 
1700     virtual void widgetSelected( Widget *pWidget );
1701     DECL_LINK( CreateWidgetHdl, layout::Button* );
1702 
1703     std::list< layout::PushButton *> maCreateButtons;
1704 
1705 public:
1706 
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();
1711 
1712     void loadFile( const rtl::OUString &aTestFile );
1713 };
1714 
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 )
1722 {
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() );
1732 
1733     // custom widgets
1734 #if DEBUG_PRINT
1735     fprintf (stderr, "custom widgets\n");
1736 #endif
1737     mpPropertiesList = new PropertiesList( dialog );
1738 
1739     mpLayoutTree = new LayoutTree( dialog );
1740     mpLayoutTree->setListener( this );
1741 
1742 /*    if ( xImport.is() )
1743       mpLayoutTree->getWidget( -1 )->addChild( new Widget( xImport, "import" ) );*/
1744 
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++ )
1749     {
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 )
1754         {
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 );
1760         }
1761         pBtn->Show();
1762         maCreateButtons.push_back( pBtn );
1763         layout::Container *pBox = WIDGETS_SPECS[ i ].bIsContainer ? &aContainers : &aWidgets;
1764         pBox->Add( pBtn );
1765     }
1766 
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
1774 
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 ) );
1779 }
1780 
1781 EditorImpl::~EditorImpl()
1782 {
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
1793 }
1794 
1795 void EditorImpl::loadFile( const rtl::OUString &aTestFile )
1796 {
1797     fprintf( stderr, "TEST: layout instance\n" );
1798     uno::Reference< awt::XLayoutRoot > xRoot
1799         ( new EditorRoot( mxFactory, mpLayoutTree->mpRootWidget ) );
1800 
1801 /*
1802   mxMSF->createInstance
1803   ( ::rtl::OUString::createFromAscii( "com.sun.star.awt.Layout" ) ),
1804   uno::UNO_QUERY );
1805 */
1806     if ( !xRoot.is() )
1807     {
1808         throw uno::RuntimeException(
1809             OUString( RTL_CONSTASCII_USTRINGPARAM("could not create awt Layout component!") ),
1810             uno::Reference< uno::XInterface >() );
1811     }
1812 
1813 #if DEBUG_PRINT
1814     fprintf( stderr, "TEST: initing root\n" );
1815 #endif
1816 
1817     uno::Reference< lang::XInitialization > xInit( xRoot, uno::UNO_QUERY );
1818     if ( !xInit.is() )
1819     {
1820         throw uno::RuntimeException(
1821             OUString( RTL_CONSTASCII_USTRINGPARAM("Layout has no XInitialization!") ),
1822             uno::Reference< uno::XInterface >() );
1823     }
1824 
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
1837 
1838     mpLayoutTree->rebuild();
1839 }
1840 
1841 void EditorImpl::createWidget( const char *name )
1842 {
1843     Widget *pWidget = mpLayoutTree->getSelectedWidget();
1844 
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 ) )
1847     {
1848         delete pChild;
1849         // we may want to popup an error message
1850     }
1851     else
1852     {
1853         mpLayoutTree->rebuild();
1854         mpLayoutTree->selectWidget( pWidget );
1855     }
1856 }
1857 
1858 void EditorImpl::widgetSelected( Widget *pWidget )
1859 {
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() )
1864     {
1865         for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1866               it != maCreateButtons.end(); it++)
1867             (*it)->Enable();
1868     }
1869     else
1870     {
1871         for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1872               it != maCreateButtons.end(); it++)
1873             (*it)->Disable();
1874     }
1875 
1876     mpPropertiesList->selectedWidget( pWidget );
1877 }
1878 
1879 IMPL_LINK( EditorImpl, CreateWidgetHdl, layout::Button *, pBtn )
1880 {
1881     int i = 0;
1882     for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1883           it != maCreateButtons.end(); it++, i++ )
1884     {
1885         if ( pBtn == *it )
1886             break;
1887     }
1888     OSL_ASSERT( i < WIDGETS_SPECS_LEN );
1889     createWidget( WIDGETS_SPECS[i].pName );
1890     return 0;
1891 }
1892 
1893 IMPL_LINK( EditorImpl, ImportButtonHdl, layout::PushButton *, pBtn )
1894 {
1895     (void) pBtn;
1896 #if DEBUG_PRINT
1897     fprintf(stderr, "IMPORT!\n");
1898 #endif
1899 #ifdef FILEDLG
1900     pImportDialog->Execute();
1901 #endif
1902 
1903     return 0;
1904 }
1905 
1906 #ifdef FILEDLG
1907 IMPL_LINK( EditorImpl, ImportDialogHdl, FileDialog *, pDialog )
1908 {
1909     UniString path = pDialog->GetPath();
1910 //fprintf(stderr, "Executing import dialog!\n");
1911 
1912 #if DEBUG_PRINT
1913     fprintf(stderr, "got import file: %s\n",rtl::OUStringToOString( path, RTL_TEXTENCODING_ASCII_US ).getStr() );
1914 #endif
1915 
1916     return 0;
1917 }
1918 #endif
1919 
1920 IMPL_LINK( EditorImpl, ExportButtonHdl, layout::PushButton *, pBtn )
1921 {
1922     (void) pBtn;
1923     mpLayoutTree->print();
1924     return 0;
1925 }
1926 
1927 //** Editor, the Dialog
1928 
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 ) )
1933 {
1934     if ( aFile.getLength() )
1935         mpImpl->loadFile( aFile );
1936 
1937     // parent:
1938     FreeResource();
1939 }
1940 
1941 Editor::~Editor()
1942 {
1943     delete mpImpl;
1944 }
1945