You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdevelop/parts/astyle/astyle_part.cpp

542 lines
16 KiB

#include "astyle_part.h"
#include <qwhatsthis.h>
#include <qvbox.h>
#include <qtextstream.h>
#include <qpopupmenu.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 <qlineedit.h>
#include <qregexp.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(QObject *parent, const char *name, const QStringList &)
: KDevSourceFormatter(&data, parent, name ? name : "AStylePart")
{
setInstance(AStyleFactory::instance());
setXMLFile("kdevpart_astyle.rc");
formatTextAction = new KAction(i18n("&Reformat Source"), 0, this, 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, 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, SIGNAL(insertConfigWidget(const KDialogBase* ,QWidget*,unsigned int)), this, SLOT(insertConfigWidget(const KDialogBase*,QWidget*,unsigned int)));
connect(partController(), SIGNAL(activePartChanged(KParts::Part*)), this, SLOT(activePartChanged(KParts::Part*)));
connect( core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)), this, SLOT(contextMenu(QPopupMenu *, 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");
QString 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=QStringList::split(",",config->readEntry("Extensions",defaultFormatExtensions));
QStringList pairs = QStringList::split( ",", options);
QStringList::Iterator it;
for ( it = pairs.begin(); it != pairs.end(); ++it ) {
QStringList bits = QStringList::split( "=", (*it) );
m_global[bits[0]] = bits[1];
}
// for (QMap<QString, QVariant>::iterator iter = m_global.begin();iter != m_global.end();iter++)
// {
// kdDebug(9009) << "load: " <<iter.key() << "="<< iter.data() << endl;
// }
}
void AStylePart::saveGlobal()
{
QString options;
for (QMap<QString, QVariant>::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 (QMap<QString, QVariant>::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 (QMap<QString, QVariant>::iterator iter = m_global.begin();iter != m_global.end();iter++)
// {
// kdDebug(9009) << "global after: " <<iter.key() << "="<< iter.data() << endl;
// }
// for (QMap<QString, QVariant>::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);
QString output;
QTextStream os(&output, IO_WriteOnly);
// put the selection back to the same indent level.
// taking note of the config options.
unsigned int indentCount=0;
QString indentWith("");
if ( has_selection){
QString original = sel_iface->selection();
for (;indentCount<original.length();indentCount++){
QChar ch = original[indentCount];
if ( ch.isSpace()){
if ( ch == QChar('\n') || ch == QChar('\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
QString replace;
for (int i =0;i<wsCount;i++)
replace+=' ';
indentWith=indentWith.replace(replace, QChar('\t'));
indentWith=indentWith.remove(' ');
} else
{
if ( m_project["FillForce"].toBool()){
//convert tabs to spaces
QString replace;
for (int i =0;i<wsCount;i++)
replace+=' ';
indentWith=indentWith.replace(QChar('\t'),replace);
}
}
}
while (formatter.hasMoreLines()){
if ( has_selection ){
os << indentWith;
}
os << QString::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, QWidget *page, unsigned int pageNo)
{
switch (pageNo)
{
case GLOBALDOC_OPTIONS:
{
AStyleWidget *w = new AStyleWidget(this, true, page, "astyle config widget");
connect(dlg, SIGNAL(okClicked()), w, SLOT(accept()));
break;
}
case PROJECTDOC_OPTIONS:
{
AStyleWidget *w = new AStyleWidget(this, false, page, "astyle config widget");
connect(dlg, SIGNAL(okClicked()), w, SLOT(accept()));
break;
}
}
}
QString AStylePart::getGlobalExtensions(){
QString values = m_globalExtensions.join("\n");
return values.stripWhiteSpace();
}
QString AStylePart::getProjectExtensions(){
QString 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 ( QString ext, bool global )
{
kdDebug(9009) << "setExtensions " << ext<<endl;
if ( global){
m_globalExtensions.clear();
m_globalExtensions=QStringList::split ( QRegExp("\n"), ext );
}
else{
m_searchExtensions.clear();
m_projectExtensions.clear();
m_projectExtensions = QStringList::split ( QRegExp("\n"), ext );
QStringList bits = QStringList::split(QRegExp("\\s+"),ext);
for ( QStringList::Iterator iter = bits.begin(); iter != bits.end(); iter++ )
{
QString 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() )
{
QString 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 );
}
QString AStylePart::formatSource( const QString text, AStyleWidget * widget, const QMap<QString, QVariant>& options )
{
ASStringIterator is(text);
KDevFormatter * formatter = ( widget)? new KDevFormatter( widget ) : new KDevFormatter(options);
formatter->init(&is);
QString output;
QTextStream os(&output, IO_WriteOnly);
while ( formatter->hasMoreLines() )
os << QString::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 );
}
}
QString AStylePart::formatSource( const QString text )
{
return formatSource(text, 0, m_project);
}
QString AStylePart::indentString( ) const
{
KDevFormatter formatter(m_project);
return formatter.indentString();
}
void AStylePart::contextMenu(QPopupMenu *popup, const Context *context)
{
if (context->hasType( Context::EditorContext ))
{
popup->insertSeparator();
int id = popup->insertItem( i18n("Format selection"), this, SLOT(beautifySource()) );
popup->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, SLOT(formatFiles()) );
popup->setWhatsThis(id, i18n("<b>Format files</b><p>Formats selected files if possible"));
}
}
void AStylePart::restorePartialProjectSession(const QDomElement * el)
{
kdDebug(9009) << "Load project" << endl;
QDomElement 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 (QMap<QString, QVariant>::iterator iter = m_global.begin();iter != m_global.end();iter++)
{
m_project[iter.key()] = style.attribute(iter.key(),iter.data().toString());
}
QDomElement exten = el->namedItem("Extensions").toElement();
QString ext = exten.attribute("ext").simplifyWhiteSpace();
if ( ext.isEmpty()){
ext=defaultFormatExtensions;
}
setExtensions(ext.replace(QChar(','), QChar('\n')),false);
}
}
void AStylePart::savePartialProjectSession(QDomElement * el)
{
QDomDocument domDoc = el->ownerDocument();
if (domDoc.isNull())
return;
QDomElement style = domDoc.createElement("AStyle");
style.setAttribute("FStyle", m_project["FStyle"].toString());
if (m_project["FStyle"] != "GLOBAL")
{
for (QMap<QString, QVariant>::iterator iter = m_project.begin();iter != m_project.end();iter++)
{
style.setAttribute(iter.key(),iter.data().toString());
}
QDomElement exten = domDoc.createElement ( "Extensions" );
exten.setAttribute ( "ext", m_projectExtensions.join(",").simplifyWhiteSpace() );
el->appendChild(exten);
}
el->appendChild(style);
}
void AStylePart::formatFilesSelect(){
m_urls.clear();
QStringList filenames = KFileDialog::getOpenFileNames ( QString::null, getProjectExtensions(),0,"Select files to format" );
for(QStringList::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++ )
{
QString fileName = m_urls[fileCount].pathOrURL();
bool found = false;
for ( QMap<QString, QString>::Iterator it = m_searchExtensions.begin(); it != m_searchExtensions.end(); ++it )
{
QRegExp re ( it.data(), true, true );
if ( re.search ( fileName ) == 0 && ( uint ) re.matchedLength() == fileName.length() )
{
found = true;
break;
}
}
if ( found )
{
QString backup = fileName + "#";
QFile fin ( fileName );
QFile fout ( backup );
if ( fin.open ( IO_ReadOnly ) )
{
if ( fout.open ( IO_WriteOnly ) )
{
QString fileContents ( fin.readAll() );
fin.close();
QTextStream outstream ( &fout );
outstream << formatSource ( fileContents );
fout.close();
QDir().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"