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