00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <qdom.h>
00022 #include <qfileinfo.h>
00023 #include <qpainter.h>
00024 #include <qpaintdevicemetrics.h>
00025
00026 #include <kconfig.h>
00027 #include <kdebug.h>
00028 #include <klocale.h>
00029 #include <ktempfile.h>
00030 #include <KoTemplateChooseDia.h>
00031 #include <KoStoreDevice.h>
00032 #include <KoOasisStyles.h>
00033 #include <KoOasisLoadingContext.h>
00034 #include <KoXmlWriter.h>
00035 #include <KoXmlNS.h>
00036 #include <KoDom.h>
00037 #include <KoOasisSettings.h>
00038 #include <KoMainWindow.h>
00039
00040 #include "karbon_factory.h"
00041 #include "karbon_part.h"
00042 #include "karbon_part_iface.h"
00043 #include "karbon_view.h"
00044 #include "vcommand.h"
00045 #include "vglobal.h"
00046 #include "vpainter.h"
00047 #include "vpainterfactory.h"
00048 #include "vselection.h"
00049 #include "vcanvas.h"
00050 #include "vlayer.h"
00051 #include "vdocumentdocker.h"
00052 #include "vtoolcontroller.h"
00053 #include "KoApplication.h"
00054 #include "vtool.h"
00055 #include "commands/vtransformcmd.h"
00056
00057
00058
00059
00060 KarbonPart::KarbonPart( QWidget* parentWidget, const char* widgetName,
00061 QObject* parent, const char* name, bool singleViewMode )
00062 : KoDocument( parentWidget, widgetName, parent, name, singleViewMode )
00063 {
00064 setInstance( KarbonFactory::instance(), false );
00065 setTemplateType( "karbon_template" );
00066 m_bShowStatusBar = true;
00067 dcop = 0L;
00068
00069 m_commandHistory = new VCommandHistory( this );
00070 connect( m_commandHistory, SIGNAL( documentRestored() ), this, SLOT( slotDocumentRestored() ) );
00071 connect( m_commandHistory, SIGNAL( commandExecuted( VCommand * ) ), this, SLOT( slotCommandExecuted( VCommand * ) ) );
00072
00073 initConfig();
00074
00075 m_merge = false;
00076
00077 m_maxRecentFiles = 10;
00078
00079
00080 dcopObject();
00081
00082
00083 m_pageLayout.format = KoPageFormat::defaultFormat();
00084 m_pageLayout.orientation = PG_PORTRAIT;
00085 m_pageLayout.ptWidth = MM_TO_POINT( KoPageFormat::width( m_pageLayout.format, m_pageLayout.orientation ) );
00086 m_pageLayout.ptHeight = MM_TO_POINT( KoPageFormat::height( m_pageLayout.format, m_pageLayout.orientation ) );
00087 m_doc.setWidth( m_pageLayout.ptWidth );
00088 m_doc.setHeight( m_pageLayout.ptHeight );
00089
00090 m_doc.selection()->showHandle();
00091 m_doc.selection()->setSelectObjects();
00092 m_doc.selection()->setState( VObject::selected );
00093 m_doc.selection()->selectNodes();
00094 }
00095
00096 KarbonPart::~KarbonPart()
00097 {
00098
00099 delete m_commandHistory;
00100 delete dcop;
00101 }
00102
00103 DCOPObject* KarbonPart::dcopObject()
00104 {
00105 if( !dcop )
00106 dcop = new KarbonPartIface( this );
00107
00108 return dcop;
00109 }
00110
00111 void
00112 KarbonPart::setPageLayout( KoPageLayout& layout, KoUnit::Unit _unit )
00113 {
00114 m_pageLayout = layout;
00115 m_doc.setUnit( _unit );
00116 m_doc.setWidth( m_pageLayout.ptWidth );
00117 m_doc.setHeight( m_pageLayout.ptHeight );
00118 }
00119
00120 bool
00121 KarbonPart::initDoc(InitDocFlags flags, QWidget* parentWidget)
00122 {
00123 if (flags==KoDocument::InitDocEmpty)
00124 {
00125 return true;
00126 }
00127 QString file;
00128 KoTemplateChooseDia::ReturnType result;
00129
00130 KoTemplateChooseDia::DialogType dlgtype;
00131 if (flags != KoDocument::InitDocFileNew)
00132 dlgtype = KoTemplateChooseDia::Everything;
00133 else
00134 dlgtype = KoTemplateChooseDia::OnlyTemplates;
00135
00136 result = KoTemplateChooseDia::choose( KarbonFactory::instance(), file, dlgtype, "karbon_template", parentWidget );
00137
00138 if( result == KoTemplateChooseDia::Template )
00139 {
00140 resetURL();
00141 bool ok = loadNativeFormat( file );
00142 if ( !ok )
00143 showLoadingErrorDialog();
00144 setEmpty();
00145 return ok;
00146 }
00147 else if( result == KoTemplateChooseDia::Empty )
00148 {
00149 return true;
00150 }
00151 else if( result == KoTemplateChooseDia::File )
00152 {
00153 KURL url( file );
00154 return openURL( url );
00155 }
00156
00157 return false;
00158 }
00159
00160 KoView*
00161 KarbonPart::createViewInstance( QWidget* parent, const char* name )
00162 {
00163 KarbonView *result = new KarbonView( this, parent, name );
00164 return result;
00165 }
00166
00167 void
00168 KarbonPart::removeView( KoView *view )
00169 {
00170 kdDebug(38000) << "KarbonPart::removeView" << endl;
00171 KoDocument::removeView( view );
00172 }
00173
00174 double getAttribute(QDomElement &element, const char *attributeName, double defaultValue)
00175 {
00176 QString value;
00177 if ( ( value = element.attribute( attributeName ) ) != QString::null )
00178 return value.toDouble();
00179 else
00180 return defaultValue;
00181 }
00182
00183 int getAttribute(QDomElement &element, const char *attributeName, int defaultValue)
00184 {
00185 QString value;
00186 if ( ( value = element.attribute( attributeName ) ) != QString::null )
00187 return value.toInt();
00188 else
00189 return defaultValue;
00190 }
00191
00192 bool
00193 KarbonPart::loadXML( QIODevice*, const QDomDocument& document )
00194 {
00195 bool success = false;
00196
00197 QDomElement doc = document.documentElement();
00198
00199 if( m_merge )
00200 {
00201 m_doc.loadDocumentContent( doc );
00202 return true;
00203 }
00204
00205 success = m_doc.loadXML( doc );
00206
00207
00208
00209
00210 QDomElement paper = doc.namedItem( "PAPER" ).toElement();
00211 if ( !paper.isNull() )
00212 {
00213 m_pageLayout.format = static_cast<KoFormat>( getAttribute( paper, "format", 0 ) );
00214 m_pageLayout.orientation = static_cast<KoOrientation>( getAttribute( paper, "orientation", 0 ) );
00215
00216 if( m_pageLayout.format == PG_CUSTOM )
00217 {
00218 m_pageLayout.ptWidth = m_doc.width();
00219 m_pageLayout.ptHeight = m_doc.height();
00220 }
00221 else
00222 {
00223 m_pageLayout.ptWidth = getAttribute( paper, "width", 0.0 );
00224 m_pageLayout.ptHeight = getAttribute( paper, "height", 0.0 );
00225 }
00226 }
00227 else
00228 {
00229 m_pageLayout.ptWidth = getAttribute( doc, "width", 595.277);
00230 m_pageLayout.ptHeight = getAttribute( doc, "height", 841.891 );
00231 }
00232
00233 kdDebug() << " ptWidth=" << m_pageLayout.ptWidth << endl;
00234 kdDebug() << " ptHeight=" << m_pageLayout.ptHeight << endl;
00235 QDomElement borders = paper.namedItem( "PAPERBORDERS" ).toElement();
00236 if( !borders.isNull() )
00237 {
00238 if( borders.hasAttribute( "ptLeft" ) )
00239 m_pageLayout.ptLeft = borders.attribute( "ptLeft" ).toDouble();
00240 if( borders.hasAttribute( "ptTop" ) )
00241 m_pageLayout.ptTop = borders.attribute( "ptTop" ).toDouble();
00242 if( borders.hasAttribute( "ptRight" ) )
00243 m_pageLayout.ptRight = borders.attribute( "ptRight" ).toDouble();
00244 if( borders.hasAttribute( "ptBottom" ) )
00245 m_pageLayout.ptBottom = borders.attribute( "ptBottom" ).toDouble();
00246 }
00247
00248 setUnit( m_doc.unit() );
00249
00250 return success;
00251 }
00252
00253 QDomDocument
00254 KarbonPart::saveXML()
00255 {
00256 QDomDocument doc = m_doc.saveXML();
00257 QDomElement me = doc.documentElement();
00258 QDomElement paper = doc.createElement( "PAPER" );
00259 me.appendChild( paper );
00260 paper.setAttribute( "format", static_cast<int>( m_pageLayout.format ) );
00261 paper.setAttribute( "pages", pageCount() );
00262 paper.setAttribute( "width", m_pageLayout.ptWidth );
00263 paper.setAttribute( "height", m_pageLayout.ptHeight );
00264 paper.setAttribute( "orientation", static_cast<int>( m_pageLayout.orientation ) );
00265
00266 QDomElement paperBorders = doc.createElement( "PAPERBORDERS" );
00267 paperBorders.setAttribute( "ptLeft", m_pageLayout.ptLeft );
00268 paperBorders.setAttribute( "ptTop", m_pageLayout.ptTop );
00269 paperBorders.setAttribute( "ptRight", m_pageLayout.ptRight );
00270 paperBorders.setAttribute( "ptBottom", m_pageLayout.ptBottom );
00271 paper.appendChild(paperBorders);
00272
00273 return doc;
00274 }
00275
00276 bool
00277 KarbonPart::loadOasis( const QDomDocument &doc, KoOasisStyles &styles, const QDomDocument &settings, KoStore *store )
00278 {
00279 kdDebug(38000) << "Start loading OASIS document..." << doc.toString() << endl;
00280
00281 QDomElement contents = doc.documentElement();
00282 kdDebug(38000) << "Start loading OASIS document..." << contents.text() << endl;
00283 kdDebug(38000) << "Start loading OASIS contents..." << contents.lastChild().localName() << endl;
00284 kdDebug(38000) << "Start loading OASIS contents..." << contents.lastChild().namespaceURI() << endl;
00285 kdDebug(38000) << "Start loading OASIS contents..." << contents.lastChild().isElement() << endl;
00286 QDomElement body( KoDom::namedItemNS( contents, KoXmlNS::office, "body" ) );
00287 kdDebug(38000) << "Start loading OASIS document..." << body.text() << endl;
00288 if( body.isNull() )
00289 {
00290 kdDebug(38000) << "No office:body found!" << endl;
00291 setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) );
00292 return false;
00293 }
00294
00295 body = KoDom::namedItemNS( body, KoXmlNS::office, "drawing");
00296 if(body.isNull())
00297 {
00298 kdDebug(38000) << "No office:drawing found!" << endl;
00299 setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) );
00300 return false;
00301 }
00302
00303 QDomElement page( KoDom::namedItemNS( body, KoXmlNS::draw, "page" ) );
00304 if(page.isNull())
00305 {
00306 kdDebug(38000) << "No office:drawing found!" << endl;
00307 setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) );
00308 return false;
00309 }
00310
00311 QString masterPageName = "Standard";
00312 QDomElement *master = styles.masterPages()[ masterPageName ];
00313 if ( !master )
00314 master = styles.masterPages()[ "Default" ];
00315 Q_ASSERT( master );
00316 const QDomElement *style = master ? styles.findStyle( master->attributeNS( KoXmlNS::style, "page-layout-name", QString::null ) ) : 0;
00317 if( style )
00318 {
00319 m_pageLayout.loadOasis( *style );
00320 m_doc.setWidth( m_pageLayout.ptWidth );
00321 m_doc.setHeight( m_pageLayout.ptHeight );
00322 }
00323 else
00324 return false;
00325
00326 KoOasisLoadingContext context( this, styles, store );
00327 m_doc.loadOasis( page, context );
00328
00329 QWMatrix mat;
00330 mat.scale( 1, -1 );
00331 mat.translate( 0, -m_doc.height() );
00332 VTransformCmd trafo( 0L, mat );
00333 trafo.visit( m_doc );
00334
00335 loadOasisSettings( settings );
00336
00337 return true;
00338 }
00339
00340 void
00341 KarbonPart::loadOasisSettings( const QDomDocument&settingsDoc )
00342 {
00343 if ( settingsDoc.isNull() )
00344 return ;
00345 KoOasisSettings settings( settingsDoc );
00346 KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
00347 if ( !viewSettings.isNull() )
00348 {
00349 setUnit(KoUnit::unit(viewSettings.parseConfigItemString("unit")));
00350
00351 }
00352 }
00353
00354
00355 bool
00356 KarbonPart::saveOasis( KoStore *store, KoXmlWriter *manifestWriter )
00357 {
00358 if( !store->open( "content.xml" ) )
00359 return false;
00360
00361 KoStoreDevice storeDev( store );
00362 KoXmlWriter* docWriter = createOasisXmlWriter( &storeDev, "office:document-content" );
00363 KoGenStyles mainStyles;
00364
00365 KoGenStyle pageLayout = m_pageLayout.saveOasis();
00366 QString layoutName = mainStyles.lookup( pageLayout, "PL" );
00367 KoGenStyle masterPage( KoGenStyle::STYLE_MASTER );
00368 masterPage.addAttribute( "style:page-layout-name", layoutName );
00369 mainStyles.lookup( masterPage, "Default", KoGenStyles::DontForceNumbering );
00370
00371 KTempFile contentTmpFile;
00372 contentTmpFile.setAutoDelete( true );
00373 QFile* tmpFile = contentTmpFile.file();
00374 KoXmlWriter contentTmpWriter( tmpFile, 1 );
00375
00376 contentTmpWriter.startElement( "office:body" );
00377 contentTmpWriter.startElement( "office:drawing" );
00378
00379 m_doc.saveOasis( store, &contentTmpWriter, mainStyles );
00380
00381 contentTmpWriter.endElement();
00382 contentTmpWriter.endElement();
00383
00384 docWriter->startElement( "office:automatic-styles" );
00385
00386 QValueList<KoGenStyles::NamedStyle> styles = mainStyles.styles( VDocument::STYLE_GRAPHICAUTO );
00387 QValueList<KoGenStyles::NamedStyle>::const_iterator it = styles.begin();
00388 for( ; it != styles.end() ; ++it )
00389 (*it).style->writeStyle( docWriter, mainStyles, "style:style", (*it).name , "style:graphic-properties" );
00390
00391 docWriter->endElement();
00392
00393
00394 tmpFile->close();
00395 docWriter->addCompleteElement( tmpFile );
00396 contentTmpFile.close();
00397
00398 docWriter->endElement();
00399 docWriter->endDocument();
00400 delete docWriter;
00401
00402 if( !store->close() )
00403 return false;
00404
00405 manifestWriter->addManifestEntry( "content.xml", "text/xml" );
00406
00407 if( !store->open( "styles.xml" ) )
00408 return false;
00409
00410 KoXmlWriter* styleWriter = createOasisXmlWriter( &storeDev, "office:document-styles" );
00411
00412 styleWriter->startElement( "office:styles" );
00413
00414 styles = mainStyles.styles( VDocument::STYLE_LINEAR_GRADIENT );
00415 it = styles.begin();
00416 for( ; it != styles.end() ; ++it )
00417 (*it).style->writeStyle( styleWriter, mainStyles, "svg:linearGradient", (*it).name, 0, true, true );
00418
00419 styles = mainStyles.styles( VDocument::STYLE_RADIAL_GRADIENT );
00420 it = styles.begin();
00421 for( ; it != styles.end() ; ++it )
00422 (*it).style->writeStyle( styleWriter, mainStyles, "svg:radialGradient", (*it).name, 0, true, true );
00423
00424 styleWriter->endElement();
00425
00426 styleWriter->startElement( "office:automatic-styles" );
00427
00428 QValueList<KoGenStyles::NamedStyle> styleList = mainStyles.styles( KoGenStyle::STYLE_PAGELAYOUT );
00429 it = styleList.begin();
00430
00431 for( ; it != styleList.end(); ++it )
00432 (*it).style->writeStyle( styleWriter, mainStyles, "style:page-layout", (*it).name, "style:page-layout-properties" );
00433
00434 styleWriter->endElement();
00435
00436 styles = mainStyles.styles( KoGenStyle::STYLE_MASTER );
00437 it = styles.begin();
00438 styleWriter->startElement("office:master-styles");
00439
00440 for( ; it != styles.end(); ++it)
00441 (*it).style->writeStyle( styleWriter, mainStyles, "style:master-page", (*it).name, "");
00442
00443 styleWriter->endElement();
00444
00445 styleWriter->endElement();
00446 styleWriter->endDocument();
00447 delete styleWriter;
00448
00449 if( !store->close() )
00450 return false;
00451
00452 manifestWriter->addManifestEntry( "styles.xml", "text/xml" );
00453
00454
00455 if(!store->open("settings.xml"))
00456 return false;
00457
00458
00459 KoXmlWriter& settingsWriter = *createOasisXmlWriter(&storeDev, "office:document-settings");
00460 settingsWriter.startElement("office:settings");
00461 settingsWriter.startElement("config:config-item-set");
00462 settingsWriter.addAttribute("config:name", "view-settings");
00463
00464 KoUnit::saveOasis(&settingsWriter, unit());
00465 saveOasisSettings( settingsWriter );
00466
00467 settingsWriter.endElement();
00468 settingsWriter.endElement();
00469 settingsWriter.endElement();
00470 settingsWriter.endDocument();
00471 delete &settingsWriter;
00472
00473
00474 if(!store->close())
00475 return false;
00476
00477 manifestWriter->addManifestEntry("settings.xml", "text/xml");
00478
00479 setModified( false );
00480 return true;
00481 }
00482
00483 void
00484 KarbonPart::saveOasisSettings( KoXmlWriter & )
00485 {
00486
00487 }
00488
00489 void
00490 KarbonPart::insertObject( VObject* object )
00491 {
00492
00493
00494 m_doc.append( object );
00495 setModified( true );
00496 }
00497
00498 void
00499 KarbonPart::addCommand( VCommand* cmd, bool repaint )
00500 {
00501 m_commandHistory->addCommand( cmd );
00502 setModified( true );
00503
00504 if( repaint )
00505 repaintAllViews();
00506 }
00507
00508 void
00509 KarbonPart::slotDocumentRestored()
00510 {
00511 setModified( false );
00512 }
00513
00514 void
00515 KarbonPart::slotCommandExecuted( VCommand *command )
00516 {
00517 setModified( true );
00518 if( command && command->changesSelection() )
00519 {
00520 QPtrListIterator<KoView> itr( views() );
00521 for( ; itr.current() ; ++itr )
00522 static_cast<KarbonView*>( itr.current() )->selectionChanged();
00523 }
00524 }
00525
00526 void
00527 KarbonPart::clearHistory()
00528 {
00529 m_commandHistory->clear();
00530 }
00531
00532 void
00533 KarbonPart::repaintAllViews( bool repaint )
00534 {
00535 QPtrListIterator<KoView> itr( views() );
00536
00537 for( ; itr.current() ; ++itr )
00538 static_cast<KarbonView*>( itr.current() )->canvasWidget()->repaintAll( repaint );
00539 }
00540
00541 void
00542 KarbonPart::repaintAllViews( const KoRect &rect )
00543 {
00544 QPtrListIterator<KoView> itr( views() );
00545
00546 for( ; itr.current() ; ++itr )
00547 static_cast<KarbonView*>( itr.current() )->canvasWidget()->repaintAll( rect );
00548 }
00549
00550 void
00551 KarbonPart::paintContent( QPainter& painter, const QRect& rect,
00552 bool , double , double )
00553 {
00554 kdDebug(38000) << "**** part->paintContent()" << endl;
00555
00556 KoRect r = KoRect::fromQRect( rect );
00557 double zoomFactorX = double( r.width() ) / double( document().width() );
00558 double zoomFactorY = double( r.height() ) / double( document().height() );
00559 double zoomFactor = kMin( zoomFactorX, zoomFactorY );
00560
00561 painter.eraseRect( rect );
00562 VPainterFactory *painterFactory = new VPainterFactory;
00563
00564 painterFactory->setPainter( painter.device(), rect.width(), rect.height() );
00565 VPainter *p = painterFactory->painter();
00566
00567 p->begin();
00568 p->setZoomFactor( zoomFactor );
00569 kdDebug(38000) << "painter.worldMatrix().dx() : " << painter.worldMatrix().dx() << endl;
00570 kdDebug(38000) << "painter.worldMatrix().dy() : " << painter.worldMatrix().dy() << endl;
00571 kdDebug(38000) << "rect.x() : "<< rect.x() << endl;
00572 kdDebug(38000) << "rect.y() : "<< rect.y() << endl;
00573 kdDebug(38000) << "rect.width() : "<< rect.width() << endl;
00574 kdDebug(38000) << "rect.height() : "<< rect.height() << endl;
00575 r = document().boundingBox();
00576 QWMatrix mat = painter.worldMatrix();
00577 mat.scale( 1, -1 );
00578 mat.translate( 0, -r.height() * zoomFactor );
00579 p->setWorldMatrix( mat );
00580
00581 m_doc.selection()->clear();
00582 QPtrListIterator<VLayer> itr( m_doc.layers() );
00583
00584 for( ; itr.current(); ++itr )
00585 {
00586 itr.current()->draw( p, &r );
00587 }
00588
00589 p->end();
00590 delete painterFactory;
00591 }
00592
00593 void
00594 KarbonPart::setShowStatusBar( bool b )
00595 {
00596 m_bShowStatusBar = b;
00597 }
00598
00599 void
00600 KarbonPart::reorganizeGUI()
00601 {
00602 QPtrListIterator<KoView> itr( views() );
00603
00604 for( ; itr.current(); ++itr )
00605 {
00606 static_cast<KarbonView*>( itr.current() )->reorganizeGUI();
00607 }
00608 }
00609
00610 void
00611 KarbonPart::setUndoRedoLimit( int undos )
00612 {
00613 m_commandHistory->setUndoLimit( undos );
00614 m_commandHistory->setRedoLimit( undos );
00615 }
00616
00617 void
00618 KarbonPart::initConfig()
00619 {
00620 KConfig* config = KarbonPart::instance()->config();
00621
00622 if( config->hasGroup( "Interface" ) )
00623 {
00624 config->setGroup( "Interface" );
00625 setAutoSave( config->readNumEntry( "AutoSave", defaultAutoSave() / 60 ) * 60 );
00626 m_maxRecentFiles = config->readNumEntry( "NbRecentFile", 10 );
00627 setShowStatusBar( config->readBoolEntry( "ShowStatusBar" , true ) );
00628 setBackupFile( config->readNumEntry( "BackupFile", true ) );
00629 m_doc.saveAsPath( config->readBoolEntry( "SaveAsPath", true ) );
00630 }
00631 int undos = 30;
00632 if( config->hasGroup( "Misc" ) )
00633 {
00634 config->setGroup( "Misc" );
00635 undos = config->readNumEntry( "UndoRedo", -1 );
00636 QString defaultUnit = "cm";
00637
00638 if( KGlobal::locale()->measureSystem() == KLocale::Imperial )
00639 defaultUnit = "in";
00640
00641 setUnit( KoUnit::unit( config->readEntry( "Units", defaultUnit ) ) );
00642 m_doc.setUnit( unit() );
00643 }
00644 if( undos != -1 )
00645 setUndoRedoLimit( undos );
00646 }
00647
00648 bool
00649 KarbonPart::mergeNativeFormat( const QString &file )
00650 {
00651 m_merge = true;
00652 bool result = loadNativeFormat( file );
00653 if ( !result )
00654 showLoadingErrorDialog();
00655 m_merge = false;
00656 return result;
00657 }
00658
00659 void
00660 KarbonPart::addShell( KoMainWindow *shell )
00661 {
00662 connect( shell, SIGNAL( documentSaved() ), m_commandHistory, SLOT( documentSaved() ) );
00663 KoDocument::addShell( shell );
00664 }
00665
00666
00667 void
00668 KarbonPart::slotUnitChanged( KoUnit::Unit )
00669 {
00670 #if 0
00671
00672 m_doc.setUnit( unit );
00673 if( m_toolController->activeTool() )
00674 m_toolController->activeTool()->refreshUnit();
00675 #endif
00676 }
00677
00678 #include "karbon_part.moc"
00679