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