#include "astyle_part.h" #include <tqwhatsthis.h> #include <tqvbox.h> #include <tqtextstream.h> #include <tqpopupmenu.h> #include <kdeversion.h> #include <kdebug.h> #include <kdialogbase.h> #include <kdevgenericfactory.h> #include <kiconloader.h> #include <klocale.h> #include <kparts/part.h> #include <kparts/partmanager.h> #include <ktexteditor/editinterface.h> #include <ktexteditor/document.h> #include <ktexteditor/viewcursorinterface.h> #include <ktexteditor/selectioninterface.h> #include <kprogress.h> #include <kdevcore.h> #include <kdevapi.h> #include <kdevpartcontroller.h> #include <kdevplugininfo.h> #include <configwidgetproxy.h> #include <kapplication.h> #include <kconfig.h> #include <kfiledialog.h> #include <klocale.h> #include <kmessagebox.h> #include <tqlineedit.h> #include <tqregexp.h> #include "astyle_widget.h" #include "astyle_adaptor.h" static const KDevPluginInfo data("kdevastyle"); namespace { const char* defaultFormatExtensions = "*.cpp *.h *.hpp,*.c *.h,*.cxx *.hxx,*.c++ *.h++,*.cc *.hh,*.C *.H,*.diff ,*.inl,*.java,*.moc,*.patch,*.tlh,*.xpm"; } typedef KDevGenericFactory<AStylePart> AStyleFactory; K_EXPORT_COMPONENT_FACTORY( libkdevastyle, AStyleFactory( data ) ) AStylePart::AStylePart(TQObject *parent, const char *name, const TQStringList &) : KDevSourceFormatter(&data, parent, name ? name : "AStylePart") { setInstance(AStyleFactory::instance()); setXMLFile("kdevpart_astyle.rc"); formatTextAction = new KAction(i18n("&Reformat Source"), 0, this, TQT_SLOT(beautifySource()), actionCollection(), "edit_astyle"); formatTextAction->setEnabled(false); formatTextAction->setToolTip(i18n("Reformat source")); formatTextAction->setWhatsThis(i18n("<b>Reformat source</b><p>Source reformatting functionality using <b>astyle</b> library. " "Also available in <b>New Class</b> and <b>Subclassing</b> wizards.")); formatFileAction = new KAction(i18n("Format files"), 0, this, TQT_SLOT(formatFilesSelect()), actionCollection(), "tools_astyle"); formatFileAction->setEnabled(false); formatFileAction->setToolTip(i18n("Format files")); formatFileAction->setWhatsThis(i18n("<b>Fomat files</b><p>Formatting functionality using <b>astyle</b> library. " "Also available in <b>New Class</b> and <b>Subclassing</b> wizards.")); formatFileAction->setEnabled ( true ); m_configProxy = new ConfigWidgetProxy(core()); m_configProxy->createGlobalConfigPage(i18n("Formatting"), GLOBALDOC_OPTIONS, info()->icon()); m_configProxy->createProjectConfigPage(i18n("Formatting"), PROJECTDOC_OPTIONS, info()->icon()); connect(m_configProxy, TQT_SIGNAL(insertConfigWidget(const KDialogBase* ,TQWidget*,unsigned int)), this, TQT_SLOT(insertConfigWidget(const KDialogBase*,TQWidget*,unsigned int))); connect(partController(), TQT_SIGNAL(activePartChanged(KParts::Part*)), this, TQT_SLOT(activePartChanged(KParts::Part*))); connect( core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)), this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)) ); loadGlobal(); //use the globals first, project level will override later.. m_project=m_global; m_projectExtensions = m_globalExtensions; setExtensions(m_globalExtensions.join("\n"),false); // maybe there is a file open already activePartChanged( partController()->activePart() ); } void AStylePart::loadGlobal() { // kdDebug(9009) << "Load global"<<endl; KConfig *config = kapp->config(); config->setGroup("AStyle"); TQString options = config->readEntry("Options","BlockBreak=0,BlockBreakAll=0,BlockIfElse=0,Brackets=Break,BracketsCloseHeaders=0,FStyle=UserDefined,Fill=Tabs,FillCount=4,FillEmptyLines=0,FillForce=0,IndentBlocks=0,IndentBrackets=0,IndentCases=0,IndentClasses=1,IndentLabels=1,IndentNamespaces=1,IndentPreprocessors=0,IndentSwitches=1,KeepBlocks=1,KeepStatements=1,MaxStatement=40,MinConditional=-1,PadOperators=0,PadParenthesesIn=1,PadParenthesesOut=1,PadParenthesesUn=1,"); m_globalExtensions=TQStringList::split(",",config->readEntry("Extensions",defaultFormatExtensions)); TQStringList pairs = TQStringList::split( ",", options); TQStringList::Iterator it; for ( it = pairs.begin(); it != pairs.end(); ++it ) { TQStringList bits = TQStringList::split( "=", (*it) ); m_global[bits[0]] = bits[1]; } // for (TQMap<TQString, TQVariant>::iterator iter = m_global.begin();iter != m_global.end();iter++) // { // kdDebug(9009) << "load: " <<iter.key() << "="<< iter.data() << endl; // } } void AStylePart::saveGlobal() { TQString options; for (TQMap<TQString, TQVariant>::iterator iter = m_global.begin();iter != m_global.end();iter++) { // kdDebug(9009) <<"saveGlobal" <<iter.key() << "="<< iter.data() << endl; options += iter.key(); options += "="; options += iter.data().toString(); options += ","; } // for (TQMap<TQString, TQVariant>::iterator iter = m_project.begin();iter != m_project.end();iter++) // { // kdDebug(9009) << "project before: " <<iter.key() << "="<< iter.data() << endl; // } KConfig *config = kapp->config(); config->setGroup("AStyle"); config->writeEntry("Options",options); config->writeEntry("Extensions",m_globalExtensions.join(",")); config->sync(); // for (TQMap<TQString, TQVariant>::iterator iter = m_global.begin();iter != m_global.end();iter++) // { // kdDebug(9009) << "global after: " <<iter.key() << "="<< iter.data() << endl; // } // for (TQMap<TQString, TQVariant>::iterator iter = m_project.begin();iter != m_project.end();iter++) // { // kdDebug(9009) << "project after: " <<iter.key() << "="<< iter.data() << endl; // } } AStylePart::~AStylePart() { saveGlobal(); delete m_configProxy; } void AStylePart::beautifySource() { KTextEditor::EditInterface *iface = dynamic_cast<KTextEditor::EditInterface*>(partController()->activePart()); if (!iface) return; bool has_selection = false; KTextEditor::SelectionInterface *sel_iface = dynamic_cast<KTextEditor::SelectionInterface*>(partController()->activePart()); if (sel_iface && sel_iface->hasSelection()) has_selection = true; //if there is a selection, we only format it. ASStringIterator is(has_selection ? sel_iface->selection() : iface->text()); KDevFormatter formatter(m_project); formatter.init(&is); TQString output; TQTextStream os(&output, IO_WriteOnly); // put the selection back to the same indent level. // taking note of the config options. unsigned int indentCount=0; TQString indentWith(""); if ( has_selection){ TQString original = sel_iface->selection(); for (;indentCount<original.length();indentCount++){ TQChar ch = original[indentCount]; if ( ch.isSpace()){ if ( ch == TQChar('\n') || ch == TQChar('\r')){ indentWith=""; } else{ indentWith+=original[indentCount]; } } else{ break; } } int wsCount = m_project["FillCount"].toInt(); if (m_project["Fill"].toString() == "Tabs") { // tabs and wsCount spaces to be a tab TQString replace; for (int i =0;i<wsCount;i++) replace+=' '; indentWith=indentWith.replace(replace, TQChar('\t')); indentWith=indentWith.remove(' '); } else { if ( m_project["FillForce"].toBool()){ //convert tabs to spaces TQString replace; for (int i =0;i<wsCount;i++) replace+=' '; indentWith=indentWith.replace(TQChar('\t'),replace); } } } while (formatter.hasMoreLines()){ if ( has_selection ){ os << indentWith; } os << TQString::fromUtf8(formatter.nextLine().c_str()) << endl; } uint col = 0; uint line = 0; if(has_selection) //there was a selection, so only change the part of the text related to it { //remove the final newline character, unless it should be there if ( !sel_iface->selection().endsWith( "\n" ) ) output.setLength(output.length()-1); sel_iface->removeSelectedText(); cursorPos( partController()->activePart(), &col, &line ); iface->insertText( col, line, output); return; } cursorPos( partController()->activePart(), &col, &line ); iface->setText( output ); setCursorPos( partController()->activePart(), col, line ); } void AStylePart::insertConfigWidget(const KDialogBase *dlg, TQWidget *page, unsigned int pageNo) { switch (pageNo) { case GLOBALDOC_OPTIONS: { AStyleWidget *w = new AStyleWidget(this, true, page, "astyle config widget"); connect(dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept())); break; } case PROJECTDOC_OPTIONS: { AStyleWidget *w = new AStyleWidget(this, false, page, "astyle config widget"); connect(dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept())); break; } } } TQString AStylePart::getGlobalExtensions(){ TQString values = m_globalExtensions.join("\n"); return values.stripWhiteSpace(); } TQString AStylePart::getProjectExtensions(){ TQString values = m_projectExtensions.join("\n"); return values.stripWhiteSpace(); } /** * Extensions from the widget passed in. * We preserve the order, so common extensions will * end up at the top * @param ext */ void AStylePart::setExtensions ( TQString ext, bool global ) { kdDebug(9009) << "setExtensions " << ext<<endl; if ( global){ m_globalExtensions.clear(); m_globalExtensions=TQStringList::split ( TQRegExp("\n"), ext ); } else{ m_searchExtensions.clear(); m_projectExtensions.clear(); m_projectExtensions = TQStringList::split ( TQRegExp("\n"), ext ); TQStringList bits = TQStringList::split(TQRegExp("\\s+"),ext); for ( TQStringList::Iterator iter = bits.begin(); iter != bits.end(); iter++ ) { TQString ending=*iter; if ( ending.startsWith ( "*" ) ) { if (ending.length() ==1 ){ // special case.. any file. m_searchExtensions.insert(ending, ending); } else{ m_searchExtensions.insert( ending.mid( 1 ), ending); } } else { m_searchExtensions.insert(ending, ending); } } } } void AStylePart::activePartChanged ( KParts::Part *part ) { bool enabled = false; KParts::ReadWritePart *rw_part = dynamic_cast<KParts::ReadWritePart*> ( part ); if ( rw_part ) { KTextEditor::EditInterface *iface = dynamic_cast<KTextEditor::EditInterface*> ( rw_part ); if ( iface ) { // check for the everything case.. if ( m_searchExtensions.find ( "*" ) == m_searchExtensions.end() ) { TQString extension = rw_part->url().path(); int pos = extension.findRev ( '.' ); if ( pos >= 0 ) { extension = extension.mid ( pos ); enabled = ( m_searchExtensions.find ( extension ) != m_searchExtensions.end() ); } } else { enabled = true; } } } formatTextAction->setEnabled ( enabled ); } TQString AStylePart::formatSource( const TQString text, AStyleWidget * widget, const TQMap<TQString, TQVariant>& options ) { ASStringIterator is(text); KDevFormatter * formatter = ( widget)? new KDevFormatter( widget ) : new KDevFormatter(options); formatter->init(&is); TQString output; TQTextStream os(&output, IO_WriteOnly); while ( formatter->hasMoreLines() ) os << TQString::fromUtf8( formatter->nextLine().c_str() ) << endl; delete formatter; return output; } void AStylePart::cursorPos( KParts::Part *part, uint * line, uint * col ) { if (!part || !part->inherits("KTextEditor::Document")) return; KTextEditor::ViewCursorInterface *iface = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget()); if (iface) { iface->cursorPositionReal( line, col ); } } void AStylePart::setCursorPos( KParts::Part *part, uint line, uint col ) { if (!part || !part->inherits("KTextEditor::Document")) return; KTextEditor::ViewCursorInterface *iface = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget()); if (iface) { iface->setCursorPositionReal( line, col ); } } TQString AStylePart::formatSource( const TQString text ) { return formatSource(text, 0, m_project); } TQString AStylePart::indentString( ) const { KDevFormatter formatter(m_project); return formatter.indentString(); } void AStylePart::contextMenu(TQPopupMenu *popup, const Context *context) { if (context->hasType( Context::EditorContext )) { popup->insertSeparator(); int id = popup->insertItem( i18n("Format selection"), this, TQT_SLOT(beautifySource()) ); popup->TQMenuData::setWhatsThis(id, i18n("<b>Format</b><p>Formats the current selection, if possible")); } else if ( context->hasType( Context::FileContext )){ const FileContext *ctx = static_cast<const FileContext*>(context); m_urls = ctx->urls(); popup->insertSeparator(); int id = popup->insertItem( i18n("Format files"), this, TQT_SLOT(formatFiles()) ); popup->TQMenuData::setWhatsThis(id, i18n("<b>Format files</b><p>Formats selected files if possible")); } } void AStylePart::restorePartialProjectSession(const TQDomElement * el) { kdDebug(9009) << "Load project" << endl; TQDomElement style = el->namedItem("AStyle").toElement(); if (style.attribute("FStyle", "GLOBAL") == "GLOBAL") { m_project = m_global; m_project["FStyle"] = "GLOBAL"; m_projectExtensions=m_globalExtensions; } else { for (TQMap<TQString, TQVariant>::iterator iter = m_global.begin();iter != m_global.end();iter++) { m_project[iter.key()] = style.attribute(iter.key(),iter.data().toString()); } TQDomElement exten = el->namedItem("Extensions").toElement(); TQString ext = exten.attribute("ext").simplifyWhiteSpace(); if ( ext.isEmpty()){ ext=defaultFormatExtensions; } setExtensions(ext.replace(TQChar(','), TQChar('\n')),false); } } void AStylePart::savePartialProjectSession(TQDomElement * el) { TQDomDocument domDoc = el->ownerDocument(); if (domDoc.isNull()) return; TQDomElement style = domDoc.createElement("AStyle"); style.setAttribute("FStyle", m_project["FStyle"].toString()); if (m_project["FStyle"] != "GLOBAL") { for (TQMap<TQString, TQVariant>::iterator iter = m_project.begin();iter != m_project.end();iter++) { style.setAttribute(iter.key(),iter.data().toString()); } TQDomElement exten = domDoc.createElement ( "Extensions" ); exten.setAttribute ( "ext", m_projectExtensions.join(",").simplifyWhiteSpace() ); el->appendChild(exten); } el->appendChild(style); } void AStylePart::formatFilesSelect(){ m_urls.clear(); TQStringList filenames = KFileDialog::getOpenFileNames ( TQString(), getProjectExtensions(),0,"Select files to format" ); for(TQStringList::Iterator it = filenames.begin(); it != filenames.end();it++){ m_urls << *it; } formatFiles(); } /** * Format the selected files with the current style. */ void AStylePart::formatFiles() { KURL::List::iterator it = m_urls.begin(); while ( it != m_urls.end() ) { kdDebug ( 9009 ) << "Selected " << ( *it ).pathOrURL() << endl; ++it; } uint processed = 0; for ( uint fileCount = 0; fileCount < m_urls.size(); fileCount++ ) { TQString fileName = m_urls[fileCount].pathOrURL(); bool found = false; for ( TQMap<TQString, TQString>::Iterator it = m_searchExtensions.begin(); it != m_searchExtensions.end(); ++it ) { TQRegExp re ( it.data(), true, true ); if ( re.search ( fileName ) == 0 && ( uint ) re.matchedLength() == fileName.length() ) { found = true; break; } } if ( found ) { TQString backup = fileName + "#"; TQFile fin ( fileName ); TQFile fout ( backup ); if ( fin.open ( IO_ReadOnly ) ) { if ( fout.open ( IO_WriteOnly ) ) { TQString fileContents ( fin.readAll() ); fin.close(); TQTextStream outstream ( &fout ); outstream << formatSource ( fileContents ); fout.close(); TQDir().rename ( backup, fileName ); processed++; } else { KMessageBox::sorry ( 0, i18n ( "Not able to write %1" ).arg ( backup ) ); } } else { KMessageBox::sorry ( 0, i18n ( "Not able to read %1" ).arg ( fileName ) ); } } } if ( processed != 0 ) { KMessageBox::information ( 0, i18n ( "Processed %1 files ending with extensions %2" ).arg ( processed ).arg(getProjectExtensions().stripWhiteSpace()) ); } m_urls.clear(); } #include "astyle_part.moc"