xref: /aoo42x/main/toolkit/workben/layout/editor.cxx (revision b0724fc6)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "editor.hxx"
25 
26 #undef NDEBUG
27 
28 /*
29 #include <stdio.h>
30 #include <string.h>
31 */
32 
33 #include <cassert>
34 #include <cstdio>
35 #include <cstring>
36 #include <list>
37 #include <vector>
38 
39 #include <com/sun/star/awt/WindowAttribute.hpp>
40 #include <com/sun/star/awt/XLayoutConstrains.hpp>
41 #include <com/sun/star/awt/XLayoutContainer.hpp>
42 #include <com/sun/star/awt/XToolkit.hpp>
43 #include <com/sun/star/awt/XVclWindowPeer.hpp>
44 #include <com/sun/star/awt/XWindow.hpp>
45 #include <com/sun/star/awt/XWindowPeer.hpp>
46 #include <rtl/strbuf.hxx>
47 #include <rtl/ustrbuf.hxx>
48 #include <toolkit/helper/property.hxx>
49 #include <vcl/lstbox.h>
50 
51 using namespace layout::css;
52 
53 using rtl::OUString;
54 
55 // FIXME:
56 //#define FILEDLG
57 
58 #include <layout/core/helper.hxx>
59 #include <layout/core/root.hxx>
60 #include <layout/core/helper.hxx>
61 
62 // TODO: automatically generated
63 struct WidgetSpec {
64     const char *pLabel, *pName, *pIconName;
65     bool bIsContainer; };
66 static const WidgetSpec WIDGETS_SPECS[] = {
67     { "Label",         "fixedtext"   , "sc_label.png",        false },
68     { "Button",        "pushbutton"  , "sc_pushbutton.png",   false },
69     { "Radio Button",  "radiobutton" , "sc_radiobutton.png",  false },
70     { "Check Box",     "checkbox"    , "sc_checkbox.png",     false },
71     { "Line Edit",     "edit"        , "sc_edit.png",         false },
72     { "Numeric Field", "numericfield", "sc_numericfield.png", false },
73     { "List Box                  ", "listbox"     , NULL,                  false },
74     // containers
75     { "Hor Box",       "hbox"        , NULL,                  true  },
76     { "Ver Box",       "vbox"        , NULL,                  true  },
77     { "Table",         "table"       , NULL,                  true  },
78     { "Alignment",     "align"       , NULL,                  true  },
79     { "Tab Control",   "tabcontrol"  , NULL,                  true  },
80     { "Hor Splitter",  "hsplitter"   , NULL,                  true  },
81     { "Ver Splitter",  "vsplitter"   , NULL,                  true  },
82     { "Scroller",      "scroller"    , NULL,                  true  },
83 };
84 const int WIDGETS_SPECS_LEN = sizeof (WIDGETS_SPECS) / sizeof (WidgetSpec);
85 
86 using namespace layout;
87 using namespace layoutimpl;
88 namespace css = ::com::sun::star;
89 
90 static rtl::OUString anyToString (uno::Any value)
91 {
92     try
93     {
94         switch (value.getValueTypeClass()) {
95             case uno::TypeClass_STRING:
96                 return value.get<rtl::OUString>();
97             case uno::TypeClass_CONSTANT:
98                 return rtl::OUString::valueOf (value.get<sal_Int32>());
99             case uno::TypeClass_LONG:
100                 return rtl::OUString::valueOf (value.get<sal_Int64>());
101             case uno::TypeClass_SHORT:
102                 // FIXME: seems broken
103                 return rtl::OUString::valueOf ((sal_Int32) value.get<short>());
104 
105             case uno::TypeClass_FLOAT:
106                 return rtl::OUString::valueOf (value.get<float>());
107             case uno::TypeClass_DOUBLE:
108                 return rtl::OUString::valueOf (value.get<double>());
109 
110             case uno::TypeClass_BOOLEAN:
111             {
112                 bool val = value.get<sal_Bool>();
113                 return rtl::OUString( val ? "1" : "0", 1, RTL_TEXTENCODING_ASCII_US );
114 /*                if ( val )
115                   return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "true" ) );
116                   else
117                   return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "false" ) );*/
118             }
119             default:
120                 break;
121         }
122     }
123     catch(...) {}
124     return rtl::OUString();
125 }
126 
127 static inline long anyToNatural (uno::Any value)
128 { return sal::static_int_cast<long>(anyToString( value ).toInt64()); }
129 static inline double anyToDecimal (uno::Any value)
130 { return anyToString( value ).toDouble(); }
131 
132 /* XLayoutContainer/XLayoutConstrains are a bit of a hasle to work with.
133    Let's wrap them. */
134 class Widget : public layoutimpl::LayoutWidget
135 {
136     friend class EditorRoot;
137 
138     Widget *mpParent;
139     std::vector< Widget *> maChildren;
140     bool mbForeign;
141 
142     rtl::OUString mrId;
143     rtl::OUString mrLabel, mrUnoName;
144 
145 // TODO: store original properties. And some property handling methods.
146     long mnOriAttrbs;
147     layoutimpl::PropList maOriProps, maOriChildProps;
148 
149 public:
150 
151     // to be used to wrap the root
152     Widget( uno::Reference< awt::XLayoutConstrains > xImport, const char *label )
153         : mpParent( 0 ), mbForeign( true )
154     {
155         mxWidget = xImport;
156         mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY );
157 
158         mrLabel = rtl::OUString( label, strlen( label ), RTL_TEXTENCODING_UTF8  );
159 
160 #if 0  /* obsolete */
161         // FIXME: this code is meant to import a XML file. Just use the importer,
162         // then pass the root widget. But information like the ID string is lost.
163         // So, this needs to be more closely tight to the importer.
164         uno::Sequence< uno::Reference< awt::XLayoutConstrains > > aChildren;
165         for ( int i = 0; i < aChildren.getLength(); i++ )
166         {
167             Widget *pChild = new Widget( aChildren[ i ], "---" );
168             maChildren.push_back( pChild );
169             pChild->mpParent = this;
170         }
171 #endif
172     }
173 
174     Widget( rtl::OUString id, uno::Reference< awt::XToolkit > xToolkit,
175             uno::Reference< awt::XLayoutContainer > xParent,
176             rtl::OUString unoName, long nAttrbs )
177         : mpParent( 0 ), mbForeign( false ), mrId( id ),
178           mnOriAttrbs( nAttrbs )
179     {
180         while ( xParent.is() && !uno::Reference< awt::XWindow >( xParent, uno::UNO_QUERY ).is() )
181         {
182             uno::Reference< awt::XLayoutContainer > xContainer( xParent, uno::UNO_QUERY );
183             OSL_ASSERT( xContainer.is() );
184             xParent = uno::Reference< awt::XLayoutContainer >( xContainer->getParent(), uno::UNO_QUERY );
185         }
186 
187         mxWidget = WidgetFactory::createWidget( xToolkit, xParent, unoName, nAttrbs );
188         OSL_ASSERT( mxWidget.is() );
189         mxContainer = uno::Reference< awt::XLayoutContainer >( mxWidget, uno::UNO_QUERY );
190 
191         mrLabel = mrUnoName = unoName;
192         // try to get a nicer label for the widget
193         for ( int i = 0; i < WIDGETS_SPECS_LEN; i++ )
194             if ( unoName.equalsAscii( WIDGETS_SPECS[ i ].pName ) )
195             {
196                 const char *label = WIDGETS_SPECS[ i ].pLabel;
197                 mrLabel = rtl::OUString( label, strlen( label ), RTL_TEXTENCODING_UTF8  );
198                 break;
199             }
200 
201         // set default Text property
202         // TODO: disable editing of text fields, check boxes selected, etc...
203 #if 0
204         uno::Reference< awt::XVclWindowPeer> xVclPeer( mxWidget, uno::UNO_QUERY )
205             if ( xVclPeer.is() ) // XVclWindowPeer ignores missing / incorrect properties
206 
207 //FIXME: it looks odd on widgets like NumericField seeing text which is deleted
208 // when you interact with it... We can avoid it for those widgets, by doing a getProp
209 // of "Text" and check if it is empty or not.
210 
211                 xVclPeer->setProperty( rtl::OUString::createFromAscii( "Text" ),
212                                        uno::makeAny( rtl::OUString::createFromAscii( "new widget" ) ) );
213 #endif
214 
215         // store original properties
216         {
217             PropertyIterator it( this, WINDOW_PROPERTY );
218             while ( it.hasNext() )
219             {
220                 beans::Property prop = it.next();
221                 rtl::OUString name( prop.Name );
222                 rtl::OUString value( getProperty( name, WINDOW_PROPERTY ) );
223 #if DEBUG_PRINT
224                 fprintf(stderr, "original property: %s = %s\n", OUSTRING_CSTR(name), OUSTRING_CSTR(value));
225 #endif
226                 std::pair< rtl::OUString, rtl::OUString > pair( name, value );
227                 maOriProps.push_back( pair );
228             }
229         }
230 
231     }
232 
233     ~Widget()
234     {
235         for ( std::vector< Widget *>::const_iterator it = maChildren.begin();
236              it != maChildren.end(); it++ )
237             delete *it;
238         if ( !mbForeign )
239         {
240             uno::Reference< lang::XComponent > xComp( mxWidget, uno::UNO_QUERY );
241             if ( xComp.is() )
242                 // some widgets, like our containers, don't implement this interface...
243                 xComp->dispose();
244         }
245     }
246 
247     uno::Reference< awt::XLayoutConstrains > impl()
248     {
249         return mxWidget;
250     }
251 
252     // LayoutWidget
253     virtual bool addChild( LayoutWidget *pChild )
254     {
255         return addChild( static_cast< Widget * >( pChild ) );
256     }
257 
258     virtual void setProperties( const PropList &rProps )
259     {
260 //        maOriProps = rProps;
261         LayoutWidget::setProperties( rProps );
262     }
263 
264     virtual void setChildProperties( LayoutWidget *pChild, const PropList &rProps )
265     {
266         maOriChildProps = rProps;
267         LayoutWidget::setChildProperties( pChild, rProps );
268     }
269 
270     // tree travel
271     Widget *up()
272     {
273         return mpParent;
274     }
275 
276     Widget *down()
277     {
278         if ( maChildren.empty() )
279             return NULL;
280         return maChildren.front();
281     }
282 
283     Widget *next()
284     {
285         if ( mpParent )
286         {
287             int pos = mpParent->getChildPos( this );
288             return mpParent->getChild( pos+1 );
289         }
290         return NULL;
291     }
292 
293     Widget *prev()
294     {
295         if ( mpParent )
296         {
297             int pos = mpParent->getChildPos( this );
298             return mpParent->getChild( pos-1 );
299         }
300         return NULL;
301     }
302 
303     // handle
304     bool addChild( Widget *pChild, int pos = 0xffff )
305     {
306         if ( !mxContainer.is() )
307             return false;
308 
309         uno::Sequence< uno::Reference < awt::XLayoutConstrains > > aChildren;
310         aChildren = mxContainer->getChildren();
311         int nChildrenLen = aChildren.getLength();
312 
313         // ugly, but let's check if the container is next to full...
314         try {
315             mxContainer->addChild( pChild->mxWidget );
316         }
317         catch( awt::MaxChildrenException ex ) {
318             return false;
319         }
320 
321         if ( pos < nChildrenLen )
322         {  // if its on the middle, we need to make space for it
323             mxContainer->removeChild( pChild->mxWidget );
324             for ( int i = pos; i < nChildrenLen; i++ )
325                 mxContainer->removeChild( aChildren[ i ] );
326             mxContainer->addChild( pChild->mxWidget );
327             for ( int i = pos; i < nChildrenLen; i++ )
328                 mxContainer->addChild( aChildren[ i ] );
329             maChildren.insert( maChildren.begin()+pos, pChild );
330         }
331         else
332             maChildren.push_back( pChild );
333 
334         OSL_ASSERT( pChild->mpParent == NULL );
335         pChild->mpParent = this;
336 
337         // store container props
338         {
339             pChild->maOriChildProps.clear();
340             PropertyIterator it( pChild, CONTAINER_PROPERTY );
341             while ( it.hasNext() )
342             {
343                 beans::Property prop = it.next();
344                 rtl::OUString name( prop.Name );
345 		try {
346 			rtl::OUString value( pChild->getProperty( name, CONTAINER_PROPERTY ) );
347 			std::pair< rtl::OUString, rtl::OUString > pair( name, value );
348 			pChild->maOriChildProps.push_back( pair );
349 		} catch ( beans::UnknownPropertyException &rEx ) {
350 			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",
351 				 rtl::OUStringToOString (rEx.Message, RTL_TEXTENCODING_UTF8).getStr());
352 		}
353             }
354         }
355 
356         return true;
357     }
358 
359     bool removeChild( Widget *pChild )
360     {
361         if ( !mxContainer.is() || pChild->mpParent != this )
362             return false;
363 
364         mxContainer->removeChild( pChild->mxWidget );
365 
366         unsigned int pos = getChildPos( pChild );
367         if ( pos < maChildren.size() )
368             maChildren.erase( maChildren.begin()+pos );
369         pChild->mpParent = NULL;
370 
371         return true;
372     }
373 
374     bool swapWithChild( Widget *pChild )
375     {
376         if ( !pChild->isContainer() )
377             return false;
378 
379         // remove all child's childrens, and try to add them here
380         removeChild( pChild );
381 
382         // keep a copy for failure
383         std::vector< Widget *> aChildren = maChildren;
384         std::vector< Widget *> aChildChildren = pChild->maChildren;
385 
386         for ( std::vector< Widget *>::const_iterator it = aChildChildren.begin();
387               it != aChildChildren.end(); it++ )
388             pChild->removeChild( *it );
389 
390         for ( std::vector< Widget *>::const_iterator it = aChildChildren.begin();
391               it != aChildChildren.end(); it++ )
392             if ( !addChild( *it ) )
393             {    // failure
394                 for ( std::vector< Widget *>::const_iterator jt = aChildChildren.begin();
395                       jt != it; jt++ )
396                     removeChild( *jt );
397                 for ( std::vector< Widget *>::const_iterator jt = aChildChildren.begin();
398                       jt != aChildChildren.end(); jt++ )
399                     pChild->addChild( *jt );
400                 return false;
401             }
402 
403         Widget *pParent = up();
404 
405         if ( pParent )
406         {
407             pParent->removeChild( this );
408             pParent->addChild( pChild );
409         }
410         pChild->addChild( this );
411         return true;
412     }
413 
414     unsigned int getChildPos( Widget *pChild )
415     {
416         int i = 0;
417         for ( std::vector< Widget *>::const_iterator it = maChildren.begin();
418               it != maChildren.end(); it++, i++ )
419             if ( *it == pChild )
420                 break;
421         return i;
422     }
423 
424     Widget *getChild( int pos )
425     {
426         if ( pos >= 0 && pos < (signed) maChildren.size() )
427             return *(maChildren.begin() + pos);
428         return NULL;
429     }
430 
431     bool isContainer()
432     { return mxContainer.is(); }
433     unsigned int getChildrenLen()
434     { return maChildren.size(); }
435 
436     rtl::OUString getLabel() const
437     { return mrLabel; }
438     rtl::OUString getUnoName() const
439     { return mrUnoName; }
440 
441     int getDepth()
442     {
443         int depth = 0;
444         for ( Widget *pWidget = mpParent; pWidget; pWidget = pWidget->mpParent )
445             depth++;
446         return depth;
447     }
448 
449     enum PropertyKind {
450         WINDOW_PROPERTY, CONTAINER_PROPERTY, WINBITS_PROPERTY
451     };
452 
453     static rtl::OUString findProperty( const PropList &props, rtl::OUString propName )
454     {
455         for ( PropList::const_iterator it = props.begin(); it != props.end(); it++ )
456             if ( it->first.equalsIgnoreAsciiCase( propName ) )
457                 return it->second;
458 #if DEBUG_PRINT
459         fprintf(stderr, "Serious error: property '%s' not found\n", OUSTRING_CSTR(propName));
460 #endif
461         return rtl::OUString();
462     }
463 
464     rtl::OUString getOriginalProperty( rtl::OUString rPropName, PropertyKind rKind )
465     {
466         rtl::OUString rValue;
467         switch ( rKind ) {
468             case WINDOW_PROPERTY:
469                 rValue = findProperty( maOriProps, rPropName );
470                 break;
471             case CONTAINER_PROPERTY:
472                 rValue = findProperty( maOriChildProps, rPropName );
473                 break;
474             case WINBITS_PROPERTY:
475                 // TODO
476                 break;
477         }
478 
479         return rValue;
480     }
481 
482     rtl::OUString getProperty( rtl::OUString rPropName, PropertyKind rKind )
483     {
484         rtl::OUString rValue;
485         switch ( rKind ) {
486             case WINDOW_PROPERTY:
487                 rValue = anyToString( layoutimpl::prophlp::getProperty( mxWidget, rPropName ) );
488                 break;
489             case CONTAINER_PROPERTY:
490                 if ( mpParent )
491                     rValue = anyToString( layoutimpl::prophlp::getProperty(
492                                               mpParent->mxContainer->getChildProperties( mxWidget ), rPropName ) );
493                 break;
494             case WINBITS_PROPERTY:
495                 // TODO
496                 break;
497         }
498 
499         return rValue;
500     }
501 
502     bool isPropertyTouched( rtl::OUString propName, PropertyKind rKind )
503     {
504         rtl::OUString oriValue = getOriginalProperty( propName, rKind );
505         rtl::OUString newValue = getProperty( propName, rKind );
506         bool isTouched = oriValue != newValue;
507 #if DEBUG_PRINT
508         fprintf(stderr, "is property '%s' touched? %s  (%s vs %s)\n", OUSTRING_CSTR(propName), isTouched ? "yes" : "no", OUSTRING_CSTR(oriValue), OUSTRING_CSTR(newValue));
509 #endif
510         return isTouched;
511     }
512 
513     using LayoutWidget::setProperty;
514 
515     void setProperty( rtl::OUString rPropName, PropertyKind rKind, uno::Any rValue )
516     {
517         switch ( rKind ) {
518             case WINDOW_PROPERTY:
519                 layoutimpl::prophlp::setProperty( mxWidget, rPropName, rValue );
520                 break;
521             case CONTAINER_PROPERTY:
522                 if ( mpParent )
523                     layoutimpl::prophlp::setProperty(
524                         mpParent->mxContainer->getChildProperties( mxWidget ), rPropName, rValue );
525                 break;
526             case WINBITS_PROPERTY:
527                 // TODO
528                 break;
529         }
530     }
531 
532     struct PropertyIterator {
533         friend class Widget;
534         PropertyKind mrKind;
535         uno::Sequence< beans::Property > maProps;
536         int nPropIt;
537 
538         PropertyIterator( Widget *pWidget, PropertyKind rKind )
539             : mrKind( rKind ), nPropIt( 0 )
540         {
541             switch ( rKind )
542             {
543                 case WINDOW_PROPERTY:
544                     if ( layoutimpl::prophlp::canHandleProps( pWidget->mxWidget ) )
545                     {
546                         uno::Reference< beans::XPropertySetInfo > xInfo
547                             = layoutimpl::prophlp::queryPropertyInfo( pWidget->mxWidget );
548                         if ( !xInfo.is() )
549                             return;
550 
551                         maProps = xInfo->getProperties();
552                     }
553                     break;
554                 case CONTAINER_PROPERTY:
555                     if ( pWidget->mpParent )
556                     {
557                         uno::Reference< beans::XPropertySet >xParentSet(
558                             pWidget->mpParent->mxContainer->getChildProperties( pWidget->mxWidget ) );
559                         if ( xParentSet.is())
560                         {
561                             uno::Reference< beans::XPropertySetInfo > xInfo( xParentSet->getPropertySetInfo() );
562                             if ( xInfo.is() )
563                                 maProps = xInfo->getProperties();
564                         }
565                     }
566                     break;
567                 case WINBITS_PROPERTY:
568                     // TODO
569                     break;
570             }
571         }
572 
573         bool hasNext()
574         {
575             return nPropIt < maProps.getLength();
576         }
577 
578         beans::Property next()
579         {
580 /*            rtl::OUString propName, propValue;
581               propName = maProps[ nPropIt ];
582               propValue = getProperty( propName, mrKind, false);
583               nPropIt++;
584               return std::pair< rtl::OUString, rtl::OUString > propPair( propName, propValue );*/
585             return maProps[ nPropIt++ ];
586         }
587     };
588 };
589 
590 class EditorRoot : public layoutimpl::LayoutRoot {
591     Widget *mpParent;
592 
593 public:
594     EditorRoot( const uno::Reference< lang::XMultiServiceFactory >& xFactory,
595                 Widget *pParent )
596         : layoutimpl::LayoutRoot( xFactory ), mpParent( pParent )
597     {
598     }
599 
600     // generation
601     virtual layoutimpl::LayoutWidget *create( rtl::OUString id, const rtl::OUString unoName,
602                                               long attrbs, uno::Reference< awt::XLayoutContainer > xParent )
603     {
604         if ( unoName.compareToAscii( "dialog" ) == 0 )
605             return mpParent;
606 
607         // TODO: go through specs to map unoName to a more human-readable label
608         Widget *pWidget = new Widget( id, mxToolkit, xParent, unoName, attrbs );
609         if ( !mxWindow.is() )
610             mxWindow = uno::Reference< awt::XWindow >( pWidget->getPeer(), uno::UNO_QUERY );
611 
612         if ( pWidget->mxContainer.is() )
613             pWidget->mxContainer->setLayoutUnit( mpParent->mxContainer->getLayoutUnit() );
614 
615         return pWidget;
616     }
617 };
618 
619 /* Working with the layout in 1D, as if it was a flat list. */
620 namespace FlatLayout
621 {
622 Widget *next( Widget *pWidget )
623 {
624     Widget *pNext;
625     pNext = pWidget->down();
626     if ( pNext ) return pNext;
627     pNext = pWidget->next();
628     if ( pNext ) return pNext;
629     for ( Widget *pUp = pWidget->up(); pUp != NULL; pUp = pUp->up() )
630         if ( (pNext = pUp->next()) != NULL )
631             return pNext;
632     return NULL;
633 }
634 
635 /*
636   Widget *prev( Widget *pWidget )
637   {
638   Widget *pPrev;
639   pPrev = pWidget->prev();
640   if ( !pPrev )
641   return pWidget->up();
642 
643   Widget *pBottom = pPrev->down();
644   if ( pBottom )
645   {
646   while ( pBottom->down() || pBottom->next() )
647   {
648   for ( Widget *pNext = pBottom->next(); pNext; pNext = pNext->next() )
649   pBottom = pNext;
650   Widget *pDown = pBottom->down();
651   if ( pDown )
652   pBottom = pDown;
653   }
654   return pBottom;
655   }
656   return pPrev;
657   }
658 */
659 
660 bool moveWidget( Widget *pWidget, bool up /*or down*/ )
661 {
662     // Keep child parent&pos for in case of failure
663     Widget *pOriContainer = pWidget->up();
664     unsigned int oriChildPos = pOriContainer->getChildPos( pWidget );
665 
666     // Get parent&sibling before removing it, since relations get cut
667     Widget *pSibling = up ? pWidget->prev() : pWidget->next();
668     Widget *pContainer = pWidget->up();
669     if ( !pContainer )
670         return false;
671 
672     // try to swap with parent or child
673     // We need to allow for this at least for the root node...
674     if ( !pSibling )
675     {
676         if ( up )
677         {
678             if ( pContainer->swapWithChild( pWidget ) )
679                 return true;
680         }
681         else
682         {
683 // TODO: this is a nice feature, but we probably want to do it explicitely...
684 #if 0
685             if ( pWidget->down() && pWidget->swapWithChild( pWidget->down() ) )
686                 return true;
687 #endif
688         }
689     }
690 
691     pContainer->removeChild( pWidget );
692 
693     // if has up sibling -- append to it, else swap with it
694     if ( pSibling )
695     {
696         if ( pSibling->addChild( pWidget, up ? 0xffff : 0 ) )
697             return true;
698 
699         unsigned int childPos = pContainer->getChildPos( pSibling );
700         if ( pContainer->addChild( pWidget, childPos + (up ? 0 : 1) ) )
701             return true;  // should always be succesful
702     }
703     // go through parents -- try to get prepended to them
704     else
705     {
706         for ( ; pContainer && pContainer->up(); pContainer = pContainer->up() )
707         {
708             unsigned int childPos = pContainer->up()->getChildPos( pContainer );
709             if ( pContainer->up()->addChild( pWidget, childPos + (up ? 0 : 1) ) )
710                 return true;
711         }
712     }
713 
714     // failed -- try to get it to its old position
715     if ( !pOriContainer->addChild( pWidget, oriChildPos ) )
716     {
717         // a parent should never reject a child back. but if it ever
718         // happens, just kill it, we don't run an orphanate here ;P
719         delete pWidget;
720         return true;
721     }
722     return false;
723 }
724 
725 // NOTE: root is considered to be number -1
726 Widget *get( Widget *pRoot, int nb )
727 {
728     Widget *it;
729     for ( it = pRoot; it != NULL && nb >= 0; it = next( it ) )
730         nb--;
731     return it;
732 }
733 
734 int get( Widget *pRoot, Widget *pWidget )
735 {
736     int nRet = -1;
737     Widget *it;
738     for ( it = pRoot; it != NULL && it != pWidget; it = next( it ) )
739         nRet++;
740     return nRet;
741 }
742 }
743 
744 //** PropertiesList widget
745 
746 class PropertiesList : public layout::Table
747 {
748     class PropertyEntry
749     {
750         friend class PropertiesList;
751 
752         /* wrapper between the widget and Any */
753         struct AnyWidget
754         {
755             DECL_LINK( ApplyPropertyHdl, layout::Window* );
756             DECL_LINK( FlagToggledHdl, layout::CheckBox* );
757 
758             AnyWidget( Widget *pWidget, rtl::OUString aPropName, Widget::PropertyKind aPropKind )
759                 : mpWidget( pWidget ), maPropName( aPropName ), maPropKind( aPropKind )
760             {
761                 mpFlag = 0;
762                 mbBlockFlagCallback = false;
763                 bFirstGet = true;
764             }
765 
766             virtual ~AnyWidget()
767             {
768 #if DEBUG_PRINT
769                 fprintf(stderr, "~AnyWidget\n");
770 #endif
771             }
772 
773             void save( uno::Any aValue )
774             {
775                 mpWidget->setProperty( maPropName, maPropKind, aValue );
776                 checkProperty();
777             }
778 
779             void checkProperty()
780             {
781                 bool flag = mpWidget->isPropertyTouched( maPropName, maPropKind );
782 
783                 if ( mpFlag && mpFlag->IsChecked() != (BOOL)flag )
784                 {
785                     CheckFlag( flag, true );
786                 }
787             }
788 
789             void CheckFlag( bool bValue, bool bBlockCallback )
790             {
791                 if ( bBlockCallback )
792                     mbBlockFlagCallback = true;
793                 mpFlag->Check( bValue );
794                 mbBlockFlagCallback = false;
795             }
796 
797             bool bFirstGet;  // HACK
798             rtl::OUString getValue()
799             {
800 //                return mpWidget->getOriProperty( maPropName );
801                 rtl::OUString value;
802                 if ( bFirstGet )    // king of ugliness
803                     value = mpWidget->getProperty( maPropName, maPropKind );
804                 else
805                     value = mpWidget->getOriginalProperty( maPropName, maPropKind );
806                 bFirstGet = false;
807                 return value;
808             }
809 
810             // FIXME: wrapper should have a base class for this...
811             virtual layout::Window *getWindow() = 0;
812             virtual layout::Container *getContainer() { return NULL; }
813 
814             virtual void load() = 0;
815             virtual void store() = 0;
816 
817             Widget *mpWidget;
818             rtl::OUString maPropName;
819             Widget::PropertyKind maPropKind;
820             layout::CheckBox *mpFlag;
821             bool mbBlockFlagCallback;
822         };
823 
824         struct AnyEdit : public AnyWidget, layout::HBox
825         {
826             layout::Edit *mpEdit;
827             bool mbMultiLine;
828             layout::PushButton *mpExpand;
829             DECL_LINK( ExpandEditHdl, layout::PushButton* );
830 
831             // so we can create widgets (like transforming the Edit into a
832             // MultiLineEdit)
833             layout::Window *mpWinParent;
834 
835             AnyEdit( Widget *pWidget, rtl::OUString aPropName,
836                      Widget::PropertyKind aPropKind, layout::Window *pWinParent )
837                 : AnyWidget( pWidget, aPropName, aPropKind ), layout::HBox( 0, false ), mpWinParent( pWinParent )
838             {
839                 mpEdit = NULL;
840                 mpExpand = new layout::PushButton( pWinParent, WB_TOGGLE );
841                 mpExpand->SetToggleHdl( LINK( this, AnyEdit, ExpandEditHdl ) );
842                 setAsMultiLine( false );
843 
844                 load();
845             }
846 
847             virtual ~AnyEdit()
848             {
849                 delete mpEdit;
850                 delete mpExpand;
851             }
852 
853             virtual layout::Window *getWindow()
854             { return NULL; }
855             virtual layout::Container *getContainer()
856             { return this; }
857 
858             void setAsMultiLine( bool bMultiLine )
859             {
860                 Clear();
861                 XubString text;
862                 if ( mpEdit )
863                 {
864                     text = mpEdit->GetText();
865                     printf("Remove mpEdit and expand\n");
866                     Remove( mpEdit );
867                     Remove( mpExpand );
868                     delete mpEdit;
869                 }
870 
871                 if ( bMultiLine )
872                 {
873                     mpEdit = new layout::Edit( mpWinParent, WB_BORDER );
874                     mpExpand->SetText( String::CreateFromAscii( "-" ) );
875                 }
876                 else
877                 {
878                     mpEdit = new layout::Edit( mpWinParent, WB_BORDER );
879                     mpExpand->SetText( String::CreateFromAscii( "+" ) );
880                 }
881 
882                 mpEdit->SetText( text );
883                 mpEdit->SetModifyHdl( LINK( this, AnyEdit, ApplyPropertyHdl ) );
884 
885                 Add( mpEdit, true, true, 0 );
886                 Add( mpExpand, false, true, 0 );
887 
888                 mbMultiLine = bMultiLine;
889             }
890 
891 #if 0
892             // TODO: make this global... We'll likely need it for export...
893             struct Translate {
894                 const char *ori, *dest;
895             };
896             static rtl::OUString stringReplace( rtl::OUString _str,
897                                                 Translate *trans )
898             {
899                 const sal_Unicode *str = _str.getStr();
900                 rtl::OUStringBuffer buf;
901                 int i, j, k;
902                 for ( i = 0; i < _str.getLength(); i++ )
903                 {
904                     for ( j = 0; trans[ j ].ori; j++ )
905                     {
906                         const char *ori = trans[ j ].ori;
907                         for ( k = 0; ori[ k ] && i+k < _str.getLength(); k++ )
908                             if ( ori[ k ] != str[ i+k ] )
909                                 break;
910                         if ( !ori[ k ] )
911                         {
912                             // found substring
913                             buf.appendAscii( trans[ j ].dest );
914                             i += k;
915                             continue;
916                         }
917                     }
918                     buf.append( str[ i ] );
919                 }
920                 return buf.makeStringAndClear();
921             }
922 #endif
923 
924             virtual void load()
925             {
926 #if 0
927                 // replace end of lines by "\\n" strings
928                 Translate trans[] = {
929                     { "\\", "\\\\" }, { "\n", "\\n" }, { 0, 0 }
930                 };
931                 rtl::OUString str = anyToString( getValue() );
932                 str = stringReplace( str, trans );
933                 SetText( str );
934 #endif
935                 mpEdit->SetText( getValue() );
936                 checkProperty();
937             }
938 
939             virtual void store()
940             {
941 #if 0
942                 // replace "\\n" strings by actual end of lines
943                 Translate trans[] = {
944                     { "\\\\", "\\"  }, { "\\n", "\n" },
945                     { "\\", "" }, { 0, 0 }
946                 };
947                 rtl::OUString str = GetText();
948                 str = stringReplace( str, trans );
949                 save( uno::makeAny( str ) );
950 #endif
951                 save( uno::makeAny( (rtl::OUString) mpEdit->GetText() ) );
952             }
953         };
954 
955         struct AnyInteger : public AnyWidget, NumericField
956         {
957             AnyInteger( Widget *pWidget, rtl::OUString aPropName,
958                         Widget::PropertyKind aPropKind, Window *pWinParent )
959                 : AnyWidget( pWidget, aPropName, aPropKind ), NumericField( pWinParent, WB_SPIN|WB_BORDER )
960             {
961                 load();
962                 SetModifyHdl( LINK( this, AnyInteger, ApplyPropertyHdl ) );
963             }
964 
965             virtual Window *getWindow()
966             { return this; }
967 
968             virtual void load()
969             {
970                 OUString text = getValue();
971                 SetText( text.getStr() );
972                 checkProperty();
973             }
974 
975             virtual void store()
976             {
977 #if DEBUG_PRINT
978                 fprintf(stderr, "store number: %ld\n", rtl::OUString( GetText() ).toInt64());
979 #endif
980                 save( uno::makeAny( rtl::OUString( GetText() ).toInt64() ) );
981             }
982         };
983 
984         struct AnyFloat : public AnyInteger
985         {
986             AnyFloat( Widget *pWidget, rtl::OUString aPropName,
987                       Widget::PropertyKind aPropKind, Window *pWinParent )
988                 : AnyInteger( pWidget, aPropName, aPropKind, pWinParent )
989             {}
990 
991             virtual void store()
992             {
993                 save( uno::makeAny( rtl::OUString( GetText() ).toDouble() ) );
994             }
995         };
996 
997         struct AnyCheckBox : public AnyWidget, layout::CheckBox
998         {
999             AnyCheckBox( Widget *pWidget, rtl::OUString aPropName,
1000                          Widget::PropertyKind aPropKind, layout::Window *pWinParent )
1001                 : AnyWidget( pWidget, aPropName, aPropKind ), layout::CheckBox( pWinParent )
1002             {
1003                 // adding some whitespaces to make the hit area larger
1004 //                SetText( String::CreateFromAscii( "" ) );
1005                 load();
1006                 SetToggleHdl( LINK( this, AnyWidget, ApplyPropertyHdl ) );
1007             }
1008 
1009             virtual ~AnyCheckBox()
1010             {
1011 #if DEBUG_PRINT
1012                 fprintf(stderr, "~AnyCheckBox\n");
1013 #endif
1014             }
1015 
1016             virtual layout::Window *getWindow()
1017             { return this; }
1018 
1019             virtual void load()
1020             {
1021 #if DEBUG_PRINT
1022                 fprintf(stderr, "loading boolean value\n");
1023 #endif
1024                 Check( getValue().toInt64() != 0 );
1025                 setLabel();
1026                 checkProperty();
1027             }
1028 
1029             virtual void store()
1030             {
1031                 save( uno::makeAny( IsChecked() ) );
1032                 setLabel();
1033             }
1034 
1035             void setLabel()
1036             {
1037                 SetText( String::CreateFromAscii( IsChecked() ? "true" : "false" ) );
1038             }
1039         };
1040 
1041         struct AnyListBox : public AnyWidget, layout::ListBox
1042         {
1043             AnyListBox( Widget *pWidget, rtl::OUString aPropName,
1044                         Widget::PropertyKind aPropKind, Window *pWinParent )
1045                 : AnyWidget( pWidget, aPropName, aPropKind ), layout::ListBox( pWinParent, WB_DROPDOWN )
1046             {
1047                 SetSelectHdl( LINK( this, AnyWidget, ApplyPropertyHdl ) );
1048             }
1049 
1050             virtual layout::Window *getWindow()
1051             { return this; }
1052 
1053             virtual void load()
1054             {
1055                 SelectEntryPos( sal::static_int_cast< USHORT >( getValue().toInt32() ) );
1056                 checkProperty();
1057             }
1058 
1059             virtual void store()
1060             {
1061                 save( uno::makeAny( (short) GetSelectEntryPos() ) );
1062             }
1063         };
1064 
1065         struct AnyAlign : public AnyListBox
1066         {
1067             AnyAlign( Widget *pWidget, rtl::OUString aPropName,
1068                       Widget::PropertyKind aPropKind, Window *pWinParent )
1069                 : AnyListBox( pWidget, aPropName, aPropKind, pWinParent )
1070             {
1071                 InsertEntry( XubString::CreateFromAscii( "Left" ) );
1072                 InsertEntry( XubString::CreateFromAscii( "Center" ) );
1073                 InsertEntry( XubString::CreateFromAscii( "Right" ) );
1074                 load();
1075             }
1076         };
1077 
1078         /* AnyListBox and AnyComboBox different in that a ComboBox allows the user
1079            to add other options, operating in strings, instead of constants.
1080            (its more like a suggestive AnyEdit) */
1081         struct AnyComboBox : public AnyWidget, layout::ComboBox
1082         {
1083             AnyComboBox( Widget *pWidget, rtl::OUString aPropName,
1084                          Widget::PropertyKind aPropKind, Window *pWinParent )
1085                 : AnyWidget( pWidget, aPropName, aPropKind ), layout::ComboBox( pWinParent, WB_DROPDOWN )
1086             {
1087                 SetModifyHdl( LINK( this, AnyComboBox, ApplyPropertyHdl ) );
1088             }
1089 
1090             virtual layout::Window *getWindow()
1091             { return this; }
1092 
1093             virtual void load()
1094             {
1095                 SetText( getValue() );
1096                 checkProperty();
1097             }
1098 
1099             virtual void store()
1100             {
1101                 save( uno::makeAny( (rtl::OUString) GetText() ) );
1102             }
1103         };
1104 
1105         struct AnyFontStyle : public AnyComboBox
1106         {
1107             AnyFontStyle( Widget *pWidget, rtl::OUString aPropName,
1108                           Widget::PropertyKind aPropKind, Window *pWinParent )
1109                 : AnyComboBox( pWidget, aPropName, aPropKind, pWinParent )
1110             {
1111                 InsertEntry( XubString::CreateFromAscii( "Bold" ) );
1112                 InsertEntry( XubString::CreateFromAscii( "Italic" ) );
1113                 InsertEntry( XubString::CreateFromAscii( "Bold Italic" ) );
1114                 InsertEntry( XubString::CreateFromAscii( "Fett" ) );
1115                 load();
1116             }
1117         };
1118 
1119         layout::FixedText *mpLabel;
1120         layout::CheckBox *mpFlag;
1121         AnyWidget *mpValue;
1122 
1123     public:
1124         PropertyEntry( layout::Window *pWinParent, AnyWidget *pAnyWidget )
1125         {
1126             mpLabel = new layout::FixedText( pWinParent );
1127             {
1128                 // append ':' to aPropName
1129                 rtl::OUStringBuffer buf( pAnyWidget->maPropName );
1130                 buf.append( sal_Unicode (':') );
1131                 mpLabel->SetText( buf.makeStringAndClear() );
1132             }
1133             mpValue = pAnyWidget;
1134             mpFlag = new layout::CheckBox( pWinParent );
1135             mpFlag->SetToggleHdl( LINK( mpValue, AnyWidget, FlagToggledHdl ) );
1136             mpValue->mpFlag = mpFlag;
1137         }
1138 
1139         ~PropertyEntry()
1140         {
1141 #if DEBUG_PRINT
1142                 fprintf(stderr, "REMOVING label, flag and value\n");
1143 #endif
1144             delete mpLabel;
1145             delete mpFlag;
1146             delete mpValue;
1147         }
1148 
1149         // Use this factory rather than the constructor -- check for NULL
1150         static PropertyEntry *construct( Widget *pWidget, rtl::OUString aPropName,
1151                                          Widget::PropertyKind aPropKind, sal_uInt16 nType,
1152                                          layout::Window *pWinParent )
1153         {
1154             AnyWidget *pAnyWidget;
1155             switch (nType) {
1156                 case uno::TypeClass_STRING:
1157                     if ( aPropName.compareToAscii( "FontStyleName" ) == 0 )
1158                     {
1159                         pAnyWidget = new AnyFontStyle( pWidget, aPropName, aPropKind, pWinParent );
1160                         break;
1161                     }
1162                     pAnyWidget = new AnyEdit( pWidget, aPropName, aPropKind, pWinParent );
1163                     break;
1164                 case uno::TypeClass_SHORT:
1165                     if ( aPropName.compareToAscii( "Align" ) == 0 )
1166                     {
1167                         pAnyWidget = new AnyAlign( pWidget, aPropName, aPropKind, pWinParent );
1168                         break;
1169                     }
1170                     // otherwise, treat as any other number...
1171                 case uno::TypeClass_LONG:
1172                 case uno::TypeClass_UNSIGNED_LONG:
1173                     pAnyWidget = new AnyInteger( pWidget, aPropName, aPropKind, pWinParent );
1174                     break;
1175                 case uno::TypeClass_FLOAT:
1176                 case uno::TypeClass_DOUBLE:
1177                     pAnyWidget = new AnyFloat( pWidget, aPropName, aPropKind, pWinParent );
1178                     break;
1179                 case uno::TypeClass_BOOLEAN:
1180                     pAnyWidget = new AnyCheckBox( pWidget, aPropName, aPropKind, pWinParent );
1181                     break;
1182                 default:
1183                     return NULL;
1184             }
1185             return new PropertyEntry( pWinParent, pAnyWidget );
1186         }
1187     };
1188 
1189     layout::Window *mpParentWindow;
1190 
1191     std::list< PropertyEntry* > maPropertiesList;
1192     layout::FixedLine *mpSeparator;
1193 
1194     // some properties are obscure, or simply don't make sense in this
1195     // context. Let's just ignore them.
1196     // Maybe we could offer them in an expander or something...
1197     static bool toIgnore( rtl::OUString prop )
1198     {
1199         // binary search -- keep the list sorted alphabetically
1200         static char const *toIgnoreList[] = {
1201             "DefaultControl", "FocusOnClick", "FontCharWidth", "FontCharset",
1202             "FontEmphasisMark", "FontFamily", "FontHeight", "FontKerning", "FontName",
1203             "FontOrientation", "FontPitch", "FontRelief", "FontSlant", "FontStrikeout",
1204             "FontType", "FontWordLineMode", "HelpText", "HelpURL", "MultiLine",
1205             "Printable", "Repeat", "RepeatDelay", "Tabstop"
1206         };
1207 
1208 #if 0
1209         // checks list sanity -- enable this when you add some entries...
1210         for ( unsigned int i = 1; i < sizeof( toIgnoreList )/sizeof( char * ); i++ )
1211         {
1212             if ( strcmp(toIgnoreList[i-1], toIgnoreList[i]) >= 0 )
1213             {
1214                 printf("ignore list not ordered properly: "
1215                        "'%s' should come before '%s'\n",
1216                        toIgnoreList[i], toIgnoreList[i-1]);
1217                 exit(-1);
1218             }
1219         }
1220 #endif
1221 
1222         int min = 0, max = sizeof( toIgnoreList )/sizeof( char * ) - 1, mid, cmp;
1223         do {
1224             mid = min + (max - min)/2;
1225             cmp = prop.compareToAscii( toIgnoreList[ mid ] );
1226             if ( cmp > 0 )
1227                 min = mid+1;
1228             else if ( cmp < 0 )
1229                 max = mid-1;
1230             else
1231                 return true;
1232         } while ( min <= max );
1233         return false;
1234     }
1235 
1236 public:
1237     PropertiesList( layout::Dialog *dialog )
1238         : layout::Table( dialog, "properties-box" )
1239         , mpParentWindow( dialog ), mpSeparator( 0 )
1240     {
1241     }
1242 
1243     ~PropertiesList()
1244     {
1245         clear();
1246     }
1247 
1248 private:
1249     // auxiliary, add properties from the peer to the list
1250     void addProperties( Widget *pWidget, Widget::PropertyKind rKind )
1251     {
1252         Widget::PropertyIterator it( pWidget, rKind );
1253         while ( it.hasNext() )
1254         {
1255             beans::Property prop = it.next();
1256             rtl::OUString name( prop.Name );
1257             if ( toIgnore( name ) )
1258                 continue;
1259             sal_uInt16 type = static_cast< sal_uInt16 >( prop.Type.getTypeClass() );
1260 
1261             PropertyEntry *propEntry = PropertyEntry::construct(
1262                 pWidget, name, rKind, type, mpParentWindow );
1263 
1264             if ( propEntry )
1265             {
1266                 Add( propEntry->mpLabel, false, false );
1267 
1268                 // HACK: one of these will return Null...
1269                 Add( propEntry->mpValue->getWindow(), true, false );
1270                 Add( propEntry->mpValue->getContainer(), true, false );
1271 
1272                 Add( propEntry->mpFlag, false, false );
1273                 maPropertiesList.push_back( propEntry );
1274             }
1275         }
1276     }
1277 
1278 public:
1279     void selectedWidget( Widget *pWidget )
1280     {
1281         clear();
1282 
1283         if ( !pWidget )
1284             return;
1285 
1286         addProperties( pWidget, Widget::CONTAINER_PROPERTY );
1287 
1288         mpSeparator = new layout::FixedLine( mpParentWindow );
1289         // TODO: we may want to have to separate list widgets here...
1290         Add( mpSeparator, false, false, 3, 1 );
1291 
1292         addProperties( pWidget, Widget::WINDOW_PROPERTY );
1293 
1294         ShowAll( true );
1295     }
1296 
1297     void clear()
1298     {
1299         ///FIXME: crash
1300         Container::Clear();
1301 
1302         for ( std::list< PropertyEntry* >::iterator it = maPropertiesList.begin();
1303               it != maPropertiesList.end(); it++)
1304             delete *it;
1305         maPropertiesList.clear();
1306 
1307         delete mpSeparator;
1308         mpSeparator = NULL;
1309     }
1310 };
1311 
1312 IMPL_LINK( PropertiesList::PropertyEntry::AnyWidget, ApplyPropertyHdl, layout::Window *, pWin )
1313 {
1314     (void) pWin;
1315     store();
1316     return 0;
1317 }
1318 
1319 IMPL_LINK( PropertiesList::PropertyEntry::AnyWidget, FlagToggledHdl, layout::CheckBox *, pCheck )
1320 {
1321 #if DEBUG_PRINT
1322     fprintf(stderr, "Property flag pressed -- is: %d\n", pCheck->IsChecked());
1323 #endif
1324     if ( !mbBlockFlagCallback )
1325     {
1326         bool checked = pCheck->IsChecked();
1327         if ( !checked )  // revert
1328         {
1329 #if DEBUG_PRINT
1330             fprintf(stderr, "revert\n");
1331 #endif
1332             load();
1333         }
1334         else
1335         {
1336 #if DEBUG_PRINT
1337             fprintf(stderr, "user can't dirty the flag!\n");
1338 #endif
1339             // User can't flag the property as dirty
1340             // Actually, we may want to allow the designer to force a property to be stored.
1341             // Could be useful when the default value of some new property wasn't yet decided...
1342             CheckFlag( false, true );
1343         }
1344     }
1345 #if DEBUG_PRINT
1346     else
1347         fprintf(stderr, "Property flag pressed -- BLOCKED\n");
1348 #endif
1349     return 0;
1350 }
1351 
1352 IMPL_LINK( PropertiesList::PropertyEntry::AnyEdit, ExpandEditHdl, layout::PushButton *, pBtn )
1353 {
1354     setAsMultiLine( pBtn->IsChecked() );
1355     return 0;
1356 }
1357 
1358 //** SortListBox auxiliary widget
1359 
1360 class SortListBox
1361 {        // For a manual sort ListBox; asks for a ListBox and Up/Down/Remove
1362          // buttons to wrap
1363     DECL_LINK( ItemSelectedHdl, layout::ListBox* );
1364     DECL_LINK( UpPressedHdl, layout::Button* );
1365     DECL_LINK( DownPressedHdl, layout::Button* );
1366     DECL_LINK( RemovePressedHdl, layout::Button* );
1367     layout::PushButton *mpUpButton, *mpDownButton, *mpRemoveButton;
1368 
1369 protected:
1370     layout::ListBox *mpListBox;
1371 
1372     virtual void upPressed( USHORT nPos )
1373     {
1374         XubString str = mpListBox->GetSelectEntry();
1375         mpListBox->RemoveEntry( nPos );
1376         nPos = mpListBox->InsertEntry( str, nPos-1 );
1377         mpListBox->SelectEntryPos( nPos );
1378     }
1379 
1380     virtual void downPressed( USHORT nPos )
1381     {
1382         XubString str = mpListBox->GetSelectEntry();
1383         mpListBox->RemoveEntry( nPos );
1384         nPos = mpListBox->InsertEntry( str, nPos+1 );
1385         mpListBox->SelectEntryPos( nPos );
1386     }
1387 
1388     virtual void removePressed( USHORT nPos )
1389     {
1390         mpListBox->RemoveEntry( nPos );
1391     }
1392 
1393     virtual void itemSelected( USHORT nPos )
1394     {
1395         // if we had some XLayoutContainer::canAdd() or maxChildren() function
1396         // we could make a function to check if we can move selected and enable/
1397         // /disable the move buttons as appropriate
1398 
1399         if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1400         {
1401             mpUpButton->Disable();
1402             mpDownButton->Disable();
1403             mpRemoveButton->Disable();
1404         }
1405         else
1406         {
1407             mpUpButton->Enable();
1408             mpDownButton->Enable();
1409             mpRemoveButton->Enable();
1410         }
1411     }
1412 
1413 public:
1414     SortListBox( layout::ListBox *pListBox, layout::PushButton *pUpButton, layout::PushButton *pDownButton,
1415                  layout::PushButton *pRemoveButton )
1416         : mpUpButton( pUpButton), mpDownButton( pDownButton), mpRemoveButton( pRemoveButton ),
1417           mpListBox( pListBox )
1418     {
1419         mpListBox->SetSelectHdl( LINK( this, SortListBox, ItemSelectedHdl ) );
1420 
1421         mpUpButton->SetModeImage( layout::Image ( "res/commandimagelist/lc_moveup.png" ) );
1422         mpUpButton->SetImageAlign( IMAGEALIGN_LEFT );
1423         mpUpButton->SetClickHdl( LINK( this, SortListBox, UpPressedHdl ) );
1424 
1425         mpDownButton->SetModeImage( layout::Image ( "res/commandimagelist/lc_movedown.png" ) );
1426         mpDownButton->SetImageAlign( IMAGEALIGN_LEFT );
1427         mpDownButton->SetClickHdl( LINK( this, SortListBox, DownPressedHdl ) );
1428 
1429         // "res/commandimagelist/lch_delete.png", "res/commandimagelist/lc_delete.png"
1430         mpRemoveButton->SetModeImage( layout::Image ( "res/commandimagelist/sc_closedoc.png" ) );
1431         mpRemoveButton->SetImageAlign( IMAGEALIGN_LEFT );
1432         mpRemoveButton->SetClickHdl( LINK( this, SortListBox, RemovePressedHdl ) );
1433 
1434         // fire an un-select event
1435         itemSelected( LISTBOX_ENTRY_NOTFOUND );
1436     }
1437 
1438     virtual ~SortListBox();
1439 };
1440 
1441 SortListBox::~SortListBox()
1442 {
1443     delete mpListBox;
1444     delete mpUpButton;
1445     delete mpDownButton;
1446     delete mpRemoveButton;
1447 }
1448 
1449 IMPL_LINK( SortListBox, UpPressedHdl, layout::Button *, pBtn )
1450 {
1451     (void) pBtn;
1452     USHORT pos = mpListBox->GetSelectEntryPos();
1453     if ( pos > 0 && pos != LISTBOX_ENTRY_NOTFOUND )
1454         upPressed( pos );
1455     return 0;
1456 }
1457 
1458 IMPL_LINK( SortListBox, DownPressedHdl, layout::Button *, pBtn )
1459 {
1460     (void) pBtn;
1461     USHORT pos = mpListBox->GetSelectEntryPos();
1462     if ( pos < mpListBox->GetEntryCount() && pos != LISTBOX_ENTRY_NOTFOUND )
1463         downPressed( pos );
1464     return 0;
1465 }
1466 
1467 IMPL_LINK( SortListBox, RemovePressedHdl, layout::Button *, pBtn )
1468 {
1469     (void) pBtn;
1470     USHORT pos = mpListBox->GetSelectEntryPos();
1471     if ( pos != LISTBOX_ENTRY_NOTFOUND )
1472         removePressed( pos );
1473     return 0;
1474 }
1475 
1476 IMPL_LINK( SortListBox, ItemSelectedHdl, layout::ListBox *, pList )
1477 {
1478     (void) pList;
1479     USHORT pos = mpListBox->GetSelectEntryPos();
1480     itemSelected( pos );
1481     return 0;
1482 }
1483 
1484 //** LayoutTree widget
1485 
1486 class LayoutTree : public SortListBox
1487 {
1488 public:
1489     struct Listener
1490     {
1491         virtual void widgetSelected( Widget *pWidget ) = 0;
1492     };
1493 
1494 private:
1495     Listener *mpListener;
1496 
1497 public:
1498     Widget *mpRootWidget;
1499 
1500     LayoutTree( layout::Dialog *dialog )
1501         : SortListBox( new layout::ListBox( dialog, "layout-tree" ),
1502                        new layout::PushButton( dialog, "layout-up-button" ),
1503                        new layout::PushButton( dialog, "layout-down-button" ),
1504                        new layout::PushButton( dialog, "layout-remove-button" ) )
1505     {
1506         layout::PeerHandle handle = dialog->GetPeerHandle( "preview-box" );
1507         uno::Reference< awt::XLayoutConstrains > xWidget( handle, uno::UNO_QUERY );
1508         mpRootWidget = new Widget( xWidget, "root" );
1509     }
1510 
1511     virtual ~LayoutTree();
1512 
1513     Widget *getWidget( int nPos )
1514     {
1515         if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1516             return FlatLayout::get( mpRootWidget, nPos );
1517         return NULL;
1518     }
1519 
1520     Widget *getSelectedWidget()
1521     {
1522         Widget *pWidget = getWidget( mpListBox->GetSelectEntryPos() );
1523         if ( !pWidget )  // return root, if none selected
1524             pWidget = mpRootWidget;
1525         return pWidget;
1526     }
1527 
1528     void selectWidget( Widget *pWidget )
1529     {
1530         int pos = FlatLayout::get( mpRootWidget, pWidget );
1531         if ( pos == -1 )
1532             // if asked to select hidden root, select visible root
1533             pos = 0;
1534         mpListBox->SelectEntryPos( sal::static_int_cast< USHORT >( pos ) );
1535     }
1536 
1537     void rebuild()
1538     {
1539         struct inner
1540         {
1541             // pads a string with whitespaces
1542             static rtl::OUString padString( rtl::OUString name, int depth )
1543             {
1544                 rtl::OStringBuffer aBuf( depth * 4 + name.getLength() + 2 );
1545                 for (int i = 0; i < depth; i++)
1546                     aBuf.append( "    " );
1547                 aBuf.append( rtl::OUStringToOString( name, RTL_TEXTENCODING_ASCII_US ) );
1548                 return rtl::OUString( aBuf.getStr(), aBuf.getLength(),
1549                                       RTL_TEXTENCODING_UTF8 );
1550             }
1551         };
1552 
1553         mpListBox->Clear();
1554         for ( Widget *i = FlatLayout::next( mpRootWidget ); i; i = FlatLayout::next( i ) )
1555             mpListBox->InsertEntry( inner::padString( i->getLabel(), i->getDepth()-1 ) );
1556 
1557         // any selection, no longer is. ListBox doesn't fire the event on this case;
1558         // force it.
1559         itemSelected( LISTBOX_ENTRY_NOTFOUND );
1560     }
1561 
1562     void setListener( Listener *pListener )
1563     { mpListener = pListener; }
1564 
1565     // print in XML format...
1566 
1567     static rtl::OUString toXMLNaming (const rtl::OUString &string)
1568     {
1569         rtl::OUStringBuffer buffer (string.getLength());
1570         sal_Unicode *str = string.pData->buffer;
1571         for (int i = 0; i < string.getLength(); i++) {
1572             if ( str[i] >= 'A' && str[i] <= 'Z' )
1573             {
1574                 if ( i > 0 )
1575                     buffer.append ((sal_Unicode) '-');
1576                 buffer.append ((sal_Unicode) (str[i] - 'A' + 'a'));
1577             }
1578             else
1579                 buffer.append ((sal_Unicode) str[i]);
1580         }
1581 
1582         return buffer.makeStringAndClear();
1583     }
1584 
1585     void print()
1586     {
1587         printf("\t\tExport:\n");
1588         printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1589                "<dialog xmlns=\"http://openoffice.org/2007/layout\"\n"
1590                "        xmlns:cnt=\"http://openoffice.org/2007/layout/container\"\n"
1591                "        id=\"dialog\" title=\"Unnamed\" sizeable=\"true\" >\n");
1592 
1593         for ( Widget *i = FlatLayout::next( mpRootWidget ); i; i = FlatLayout::next( i ) )
1594         {
1595             for ( int d = i->getDepth(); d > 0; d-- )
1596                 printf("    ");
1597             printf("<%s ", OUSTRING_CSTR( i->getUnoName() ) );
1598 
1599             for ( int kind = 0; kind < 2; kind++ )
1600             {
1601                 Widget::PropertyKind wKind = kind == 0 ? Widget::WINDOW_PROPERTY
1602                     : Widget::CONTAINER_PROPERTY;
1603                 Widget::PropertyIterator it( i, wKind );
1604                 while ( it.hasNext() )
1605                 {
1606                     beans::Property prop = it.next();
1607                     if ( !i->isPropertyTouched( prop.Name, wKind ) )
1608                         continue;
1609 
1610                     rtl::OUString value = i->getProperty( prop.Name, wKind );
1611                     if ( prop.Type.getTypeClass() == uno::TypeClass_BOOLEAN )
1612                     {
1613                         if ( value.compareToAscii( "0" ) )
1614                             value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("false") );
1615                         else
1616                             value = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("true") );
1617                     }
1618 
1619                     if ( value.getLength() > 0 )
1620                         printf("%s%s=\"%s\" ",
1621                                kind == 0 ? "" : "cnt:",
1622                                OUSTRING_CSTR( toXMLNaming( prop.Name ) ), OUSTRING_CSTR( value )
1623                             );
1624 
1625                 }
1626             }
1627             printf("/>\n");
1628         }
1629         printf("</dialog>\n");
1630     }
1631 
1632 protected:
1633     virtual void upPressed( USHORT nPos )
1634     {
1635         Widget *pWidget = getWidget( nPos );
1636         if ( FlatLayout::moveWidget( pWidget, true ) )
1637             rebuild();
1638         selectWidget( pWidget );
1639     }
1640 
1641     virtual void downPressed( USHORT nPos )
1642     {
1643         Widget *pWidget = getWidget( nPos );
1644         if ( FlatLayout::moveWidget( pWidget, false ) )
1645             rebuild();
1646         selectWidget( pWidget );
1647     }
1648 
1649     virtual void removePressed( USHORT nPos )
1650     {
1651         Widget *pWidget = getWidget( nPos );
1652         if ( pWidget )
1653         {
1654             pWidget->up()->removeChild( pWidget );
1655             delete pWidget;
1656             rebuild();
1657         }
1658     }
1659 
1660     virtual void itemSelected( USHORT nPos )
1661     {
1662         mpListener->widgetSelected( getWidget( nPos ) );
1663         SortListBox::itemSelected( nPos );
1664     }
1665 };
1666 
1667 LayoutTree::~LayoutTree()
1668 {
1669     delete mpRootWidget;
1670 }
1671 
1672 //** EditorImpl
1673 
1674 class EditorImpl : public LayoutTree::Listener
1675 {
1676     void createWidget( const char *unoName );
1677 
1678     PropertiesList *mpPropertiesList;
1679     LayoutTree *mpLayoutTree;
1680 
1681     layout::PushButton *pImportButton, *pExportButton;
1682 #ifdef FILEDLG
1683     FileDialog *pImportDialog;
1684 #endif
1685     DECL_LINK( ImportButtonHdl, layout::PushButton* );
1686     DECL_LINK( ExportButtonHdl, layout::PushButton* );
1687 #ifdef FILEDLG
1688     DECL_LINK( ImportDialogHdl, FileDialog* );
1689 #endif
1690 
1691     // framework stuff
1692     uno::Reference< lang::XMultiServiceFactory > mxFactory;
1693     uno::Reference< awt::XToolkit > mxToolkit;
1694     uno::Reference< awt::XWindow > mxToplevel;
1695 
1696     virtual void widgetSelected( Widget *pWidget );
1697     DECL_LINK( CreateWidgetHdl, layout::Button* );
1698 
1699     std::list< layout::PushButton *> maCreateButtons;
1700 
1701 public:
1702 
1703     EditorImpl( layout::Dialog *dialog,
1704                 // we should probable open this channel (or whatever its called) ourselves
1705                 uno::Reference< lang::XMultiServiceFactory > xMSF );
1706     virtual ~EditorImpl();
1707 
1708     void loadFile( const rtl::OUString &aTestFile );
1709 };
1710 
1711 EditorImpl::EditorImpl( layout::Dialog *dialog,
1712                         uno::Reference< lang::XMultiServiceFactory > xFactory )
1713     : mxFactory( xFactory )
1714     , mxToplevel( dialog->GetPeerHandle( "dialog" ), uno::UNO_QUERY )
1715     // FIXME: any of these should work
1716     //dialog->getContext()->getRoot(), uno::UNO_QUERY )
1717     // dialog->GetPeer(), uno::UNO_QUERY )
1718 {
1719 #if DEBUG_PRINT
1720     fprintf (stderr, "EditorImpl()\n");
1721 #endif
1722     // framework
1723     mxToolkit = uno::Reference< awt::XToolkit >(
1724         mxFactory->createInstance(
1725             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.Toolkit" ) ) ),
1726         uno::UNO_QUERY );
1727     OSL_ASSERT( mxToolkit.is() );
1728 
1729     // custom widgets
1730 #if DEBUG_PRINT
1731     fprintf (stderr, "custom widgets\n");
1732 #endif
1733     mpPropertiesList = new PropertiesList( dialog );
1734 
1735     mpLayoutTree = new LayoutTree( dialog );
1736     mpLayoutTree->setListener( this );
1737 
1738 /*    if ( xImport.is() )
1739       mpLayoutTree->getWidget( -1 )->addChild( new Widget( xImport, "import" ) );*/
1740 
1741     // create buttons
1742     layout::Container aWidgets( dialog, "create-widget" );
1743     layout::Container aContainers( dialog, "create-container" );
1744     for ( int i = 0; i < WIDGETS_SPECS_LEN; i++ )
1745     {
1746         layout::PushButton *pBtn = new layout::PushButton( (layout::Window *) dialog );
1747         pBtn->SetText( rtl::OUString::createFromAscii( WIDGETS_SPECS[ i ].pLabel ) );
1748         pBtn->SetClickHdl( LINK( this, EditorImpl, CreateWidgetHdl ) );
1749         if ( WIDGETS_SPECS[ i ].pIconName != NULL )
1750         {
1751             rtl::OString aPath ("res/commandimagelist/");
1752             aPath += WIDGETS_SPECS[ i ].pIconName;
1753             layout::Image aImg( aPath );
1754             pBtn->SetModeImage( aImg );
1755             pBtn->SetImageAlign( IMAGEALIGN_LEFT );
1756         }
1757         pBtn->Show();
1758         maCreateButtons.push_back( pBtn );
1759         layout::Container *pBox = WIDGETS_SPECS[ i ].bIsContainer ? &aContainers : &aWidgets;
1760         pBox->Add( pBtn );
1761     }
1762 
1763 #ifdef FILEDLG
1764     fprintf(stderr,"creating file dialog\n");
1765     pImportDialog = new FileDialog( NULL/*(layout::Window *) dialog*/, 0 );
1766     fprintf(stderr,"connecting it\n");
1767     pImportDialog->SetFileSelectHdl( LINK( this, EditorImpl, ImportDialogHdl ) );
1768     fprintf(stderr,"done file dialog\n");
1769 #endif
1770 
1771 /*    pImportButton = new layout::PushButton( dialog, "import-button" );
1772     pImportButton->SetClickHdl( LINK( this, EditorImpl, ImportButtonHdl ) );*/
1773     pExportButton = new layout::PushButton( dialog, "export-button" );
1774     pExportButton->SetClickHdl( LINK( this, EditorImpl, ExportButtonHdl ) );
1775 }
1776 
1777 EditorImpl::~EditorImpl()
1778 {
1779     delete mpPropertiesList;
1780     delete mpLayoutTree;
1781     for ( std::list< layout::PushButton * >::const_iterator i = maCreateButtons.begin();
1782           i != maCreateButtons.end(); i++)
1783         delete *i;
1784     delete pImportButton;
1785     delete pExportButton;
1786 #ifdef FILEDLG
1787     delete pImportDialog;
1788 #endif
1789 }
1790 
1791 void EditorImpl::loadFile( const rtl::OUString &aTestFile )
1792 {
1793     fprintf( stderr, "TEST: layout instance\n" );
1794     uno::Reference< awt::XLayoutRoot > xRoot
1795         ( new EditorRoot( mxFactory, mpLayoutTree->mpRootWidget ) );
1796 
1797 /*
1798   mxMSF->createInstance
1799   ( ::rtl::OUString::createFromAscii( "com.sun.star.awt.Layout" ) ),
1800   uno::UNO_QUERY );
1801 */
1802     if ( !xRoot.is() )
1803     {
1804         throw uno::RuntimeException(
1805             OUString( RTL_CONSTASCII_USTRINGPARAM("could not create awt Layout component!") ),
1806             uno::Reference< uno::XInterface >() );
1807     }
1808 
1809 #if DEBUG_PRINT
1810     fprintf( stderr, "TEST: initing root\n" );
1811 #endif
1812 
1813     uno::Reference< lang::XInitialization > xInit( xRoot, uno::UNO_QUERY );
1814     if ( !xInit.is() )
1815     {
1816         throw uno::RuntimeException(
1817             OUString( RTL_CONSTASCII_USTRINGPARAM("Layout has no XInitialization!") ),
1818             uno::Reference< uno::XInterface >() );
1819     }
1820 
1821 #if DEBUG_PRINT
1822     fprintf( stderr, "TEST: running parser\n" );
1823 #endif
1824     uno::Sequence< uno::Any > aParams( 1 );
1825     aParams[0] <<= aTestFile;
1826 #if DEBUG_PRINT
1827     fprintf( stderr, "TEST: do it\n" );
1828 #endif
1829     xInit->initialize( aParams );
1830 #if DEBUG_PRINT
1831     fprintf( stderr, "TEST: file loaded\n" );
1832 #endif
1833 
1834     mpLayoutTree->rebuild();
1835 }
1836 
1837 void EditorImpl::createWidget( const char *name )
1838 {
1839     Widget *pWidget = mpLayoutTree->getSelectedWidget();
1840 
1841     Widget *pChild = new Widget( rtl::OUString(), mxToolkit, uno::Reference< awt::XLayoutContainer >( mxToplevel, uno::UNO_QUERY ), rtl::OUString::createFromAscii( name ), awt::WindowAttribute::SHOW );
1842     if ( !pWidget->addChild( pChild ) )
1843     {
1844         delete pChild;
1845         // we may want to popup an error message
1846     }
1847     else
1848     {
1849         mpLayoutTree->rebuild();
1850         mpLayoutTree->selectWidget( pWidget );
1851     }
1852 }
1853 
1854 void EditorImpl::widgetSelected( Widget *pWidget )
1855 {
1856     // we know can't add widget to a non-container, so let's disable the create
1857     // buttons then. Would be nice to have a method to check if a container is
1858     // full as well...
1859     if ( !pWidget || pWidget->isContainer() )
1860     {
1861         for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1862               it != maCreateButtons.end(); it++)
1863             (*it)->Enable();
1864     }
1865     else
1866     {
1867         for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1868               it != maCreateButtons.end(); it++)
1869             (*it)->Disable();
1870     }
1871 
1872     mpPropertiesList->selectedWidget( pWidget );
1873 }
1874 
1875 IMPL_LINK( EditorImpl, CreateWidgetHdl, layout::Button *, pBtn )
1876 {
1877     int i = 0;
1878     for ( std::list< layout::PushButton *>::const_iterator it = maCreateButtons.begin();
1879           it != maCreateButtons.end(); it++, i++ )
1880     {
1881         if ( pBtn == *it )
1882             break;
1883     }
1884     OSL_ASSERT( i < WIDGETS_SPECS_LEN );
1885     createWidget( WIDGETS_SPECS[i].pName );
1886     return 0;
1887 }
1888 
1889 IMPL_LINK( EditorImpl, ImportButtonHdl, layout::PushButton *, pBtn )
1890 {
1891     (void) pBtn;
1892 #if DEBUG_PRINT
1893     fprintf(stderr, "IMPORT!\n");
1894 #endif
1895 #ifdef FILEDLG
1896     pImportDialog->Execute();
1897 #endif
1898 
1899     return 0;
1900 }
1901 
1902 #ifdef FILEDLG
1903 IMPL_LINK( EditorImpl, ImportDialogHdl, FileDialog *, pDialog )
1904 {
1905     UniString path = pDialog->GetPath();
1906 //fprintf(stderr, "Executing import dialog!\n");
1907 
1908 #if DEBUG_PRINT
1909     fprintf(stderr, "got import file: %s\n",rtl::OUStringToOString( path, RTL_TEXTENCODING_ASCII_US ).getStr() );
1910 #endif
1911 
1912     return 0;
1913 }
1914 #endif
1915 
1916 IMPL_LINK( EditorImpl, ExportButtonHdl, layout::PushButton *, pBtn )
1917 {
1918     (void) pBtn;
1919     mpLayoutTree->print();
1920     return 0;
1921 }
1922 
1923 //** Editor, the Dialog
1924 
1925 Editor::Editor( uno::Reference< lang::XMultiServiceFactory > xFactory,
1926                 rtl::OUString aFile )
1927     : layout::Dialog( (Window*) (NULL), "editor.xml", "dialog" )
1928     , mpImpl( new EditorImpl( this, xFactory ) )
1929 {
1930     if ( aFile.getLength() )
1931         mpImpl->loadFile( aFile );
1932 
1933     // parent:
1934     FreeResource();
1935 }
1936 
1937 Editor::~Editor()
1938 {
1939     delete mpImpl;
1940 }
1941