• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Kate

insanehtmlplugin_le.cpp

Go to the documentation of this file.
00001 /* 
00002 Copyright (C) 2010 Joseph Wenninger <jowenn@kde.org>
00003 
00004 Redistribution and use in source and binary forms, with or without
00005 modification, are permitted provided that the following conditions
00006 are met:
00007 
00008 1. Redistributions of source code must retain the above copyright
00009    notice, this list of conditions and the following disclaimer.
00010 2. Redistributions in binary form must reproduce the above copyright
00011    notice, this list of conditions and the following disclaimer in the
00012    documentation and/or other materials provided with the distribution.
00013 
00014 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00015 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00016 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00017 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00018 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00019 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00020 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00021 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00022 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00023 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00024 */
00025 
00026 #include "insanehtmlplugin_le.h"
00027 #include "insanehtmlplugin_le.moc"
00028 #include <kpluginfactory.h>
00029 #include <kaction.h>
00030 #include <kactioncollection.h>
00031 #include <kpassivepopup.h>
00032 #include <ktexteditor/document.h>
00033 #include <kstandarddirs.h>
00034 #include <kconfiggroup.h>
00035 
00036 #undef IHP_DEBUG
00037 
00038 K_PLUGIN_FACTORY_DECLARATION(InsaneHTMLPluginLEFactory)
00039 K_PLUGIN_FACTORY_DEFINITION(InsaneHTMLPluginLEFactory,
00040         registerPlugin<InsaneHTMLPluginLE>();
00041         )
00042 K_EXPORT_PLUGIN(InsaneHTMLPluginLEFactory("ktexteditor_insanehtml_le", "ktexteditor_plugins"))
00043 
00044 InsaneHTMLPluginLE::InsaneHTMLPluginLE(QObject *parent, const QList<QVariant> data):
00045   KTextEditor::Plugin(parent) {
00046     Q_UNUSED(data);
00047 
00048 }
00049 
00050 
00051 void InsaneHTMLPluginLE::addView (KTextEditor::View *view) {
00052   m_map.insert(view,new InsaneHTMLPluginLEView(this,view));
00053 }
00054 
00055 void InsaneHTMLPluginLE::removeView (KTextEditor::View *view) {
00056   delete m_map.take(view);
00057 }
00058 
00059 
00060 InsaneHTMLPluginLEView::InsaneHTMLPluginLEView(QObject* parent,KTextEditor::View* view):
00061   QObject(parent),KXMLGUIClient(),m_view(view) {
00062     
00063     setComponentData(InsaneHTMLPluginLEFactory::componentData());
00064 
00065     KAction *a=actionCollection()->addAction( "tools_insanehtml_le", this,SLOT(expand()) );
00066     a->setText(i18n("Insane HTML (LE) Expansion"));
00067     a->setShortcut( Qt::CTRL + Qt::Key_Period );
00068              
00069     setXMLFile( "insanehtml_le_ui.rc" );
00070     
00071     m_view->insertChildClient(this);
00072 
00073     m_emptyTags<<"br"<<"hr"<<"img"<<"input"<<"meta"<<"link";
00074     QStringList cfgFiles=KGlobal::dirs()->findAllResources("data", "ktexteditor_insanehtml_le/xhtml.cfg",KStandardDirs::NoDuplicates);
00075     if (cfgFiles.count()>0) {
00076       KConfig attribConfig(cfgFiles[0],KConfig::SimpleConfig);
00077       KConfigGroup group(&attribConfig,"Default Attributes");
00078       foreach (const QString& tag, group.keyList()) {
00079         QStringList attribs=group.readEntry(tag,QStringList());
00080         foreach (const QString& attrib,attribs) {
00081           m_defaultAttributes.insert(tag,attrib);
00082         }
00083       }
00084     }
00085 }
00086 
00087 InsaneHTMLPluginLEView::~InsaneHTMLPluginLEView() {
00088   m_view->removeChildClient(this);
00089 }
00090 
00091 
00092 /*
00093  We expect the cursor to be in them middle or at the end of a tag and don't allow attribute definitions on the right hand side, only quantifiers
00094 */
00095 
00096 int InsaneHTMLPluginLEView::find_region_end(int cursor_x, const QString& line, int *filtercount) {
00097   int end_x=cursor_x;
00098   const int len=line.length();
00099   while (end_x<len) {
00100     QChar c=line.at(end_x);
00101     if (c.isLetter() || c.isDigit() || (c==QChar('*')) || (c==QChar('_')) || (c==QChar('-')) || (c==QChar(':')) || (c==QChar('.')) || (c==QChar('#')) || (c==QChar(')'))  )
00102       end_x++;
00103     else if (c==QChar('|')) {
00104       end_x++;
00105       (*filtercount)++;
00106     } else
00107       break;
00108   }
00109   int tmp=end_x-1;
00110   if ((tmp>=0) && (tmp<len))
00111     if (line.at(tmp)==QChar('>')) return -1;
00112   return end_x;  
00113 }
00114 
00115 
00116 /* everything is allowed in the front*/
00117 int InsaneHTMLPluginLEView::find_region_start(int cursor_x, const QString& line, int *filtercount) {
00118   int len=line.length();
00119   int start_x=cursor_x;
00120   bool in_attrib=false;
00121   bool in_string=false;
00122   while (start_x>0) {
00123     int tmp_x=start_x-1;
00124     if (tmp_x==-1) break;
00125     QChar c=line.at(tmp_x);
00126     if (c==QChar('"')) {
00127       if (!in_attrib) break;
00128       in_string=!in_string;
00129       start_x=tmp_x;
00130       continue;
00131     }
00132     if (in_string) {
00133       start_x=tmp_x;
00134       continue;
00135     }
00136     
00137     if (c==QChar(']')) {
00138       in_attrib=true;
00139       start_x=tmp_x;
00140       continue;
00141     }
00142     
00143     if (c==QChar('[')) {
00144       if (in_attrib) {
00145       in_attrib=false;
00146       start_x=tmp_x;
00147       continue;
00148       } else {
00149     break;
00150       }
00151     }
00152     
00153     if (in_attrib) {
00154       start_x=tmp_x;
00155       continue;
00156     }
00157     
00158     if ( (c.isSpace() || c==QChar('=')) && (!in_attrib))
00159       break;
00160     
00161     
00162     if (c.isLetter() || c.isDigit() || (c==QChar('*')) || (c==QChar('_')) ||
00163       (c==QChar('-')) || (c==QChar(':')) || (c==QChar('.')) || (c==QChar('#')) || 
00164       (c==QChar('>')) || (c==QChar('$')) || (c==QChar('+')) || (c==QChar('(')) ||
00165       (c==QChar(')'))) {
00166       start_x=tmp_x;
00167       continue;
00168     }
00169     
00170     if (c==QChar('|')) {
00171       (*filtercount)++;
00172       start_x=tmp_x;
00173       continue;
00174     }
00175     
00176     break;
00177   }
00178   
00179   if (in_attrib || in_string) return -1;
00180   if (start_x>=len) return -1;
00181   if (start_x>=0) {
00182     if (!( (line.at(start_x).isLetter()) || (line.at(start_x)==QChar('(')) ) ) return -1;
00183   }
00184   return start_x;
00185 }
00186 
00187 
00188 QString InsaneHTMLPluginLEView::parseIdentifier(const QString& input, int *offset,bool firstDigit) {
00189   int offset_tmp=*offset;
00190   int len=input.length();
00191   QString identifier;
00192   if (!firstDigit) {
00193     if (offset_tmp<input.length()) {     
00194       if (input.at(offset_tmp).isDigit()) return QString();
00195     }
00196   }
00197   while (offset_tmp<len) {
00198     QChar c=input.at(offset_tmp);
00199     if (! (
00200       c.isDigit() || c.isLetter() || (c==QChar(':')) || (c==QChar('_')) || (c==QChar('-')) 
00201     )) break;
00202     identifier+=c;
00203     offset_tmp++;
00204   }
00205   *offset=offset_tmp;
00206   return identifier;
00207   
00208 }
00209 
00210 int InsaneHTMLPluginLEView::parseNumber(const QString& input, int *offset) {
00211   int offset_tmp=*offset;
00212   int len=input.length();
00213   QString number;
00214   if (offset_tmp<input.length()) {
00215     if (!input.at(offset_tmp).isDigit()) return 1;
00216   }
00217   while (offset_tmp<len) {
00218     QChar c=input.at(offset_tmp);
00219     if (! (
00220       c.isDigit()
00221     )) break;
00222     number+=c;
00223     offset_tmp++;
00224   }
00225   *offset=offset_tmp;
00226   return number.toInt();
00227   
00228 }
00229 
00230 QStringList InsaneHTMLPluginLEView::parse(const QString& input, int offset,int *newOffset) {
00231   QString tag;
00232   QStringList classes;
00233   QStringList sub;
00234   QStringList relatives;
00235   QString id;
00236   bool compound=false;
00237   QStringList compoundSub;
00238   QMap<QString,QString> attributes;
00239   QString attributesString;
00240   int multiply=1;
00241   bool error=false;
00242   tag=parseIdentifier(input,&offset);
00243   QStringList defAttribs=m_defaultAttributes.values(tag);
00244   foreach (const QString& defAttr,defAttribs)
00245     attributes.insert(defAttr,"");
00246   while (offset<input.length()) {
00247     QChar c=input.at(offset);
00248     if (c==QChar(')')) {
00249       offset++;
00250       break;
00251     } else if (c==QChar('(')) {
00252       offset++;
00253 #ifdef IHP_DEBUG
00254       KPassivePopup::message(i18n("offset1 %1",offset),m_view);
00255 #endif
00256       compoundSub=parse(input,offset,&offset);
00257       compound=true;
00258 #ifdef IHP_DEBUG
00259       KPassivePopup::message(i18n("offset2 %1",offset),m_view);
00260 #endif
00261       
00262     } else if (c==QChar('.')) {
00263       offset++;
00264       classes << parseIdentifier(input,&offset);
00265     } else if (c==QChar('>')) {
00266       offset++;
00267       sub=parse(input,offset,&offset);
00268       break;
00269     } else if (c==QChar('+')) {
00270       offset++;
00271       relatives=parse(input,offset,&offset);
00272       break;
00273     } else if (c==QChar('*')) {
00274       offset++;
00275       multiply=parseNumber(input,&offset);
00276     } else if (c==QChar('#')) {
00277       offset++;
00278       id=parseIdentifier(input,&offset);
00279     } else if (c==QChar('[')) {
00280       offset++;
00281       while (offset<input.length()) {
00282         c=input.at(offset);
00283         if (! ( (c==QChar(' ')) || (c==QChar('\t')) || (c==QChar(',')) ) ) {
00284           break;
00285         }
00286         offset++;
00287       }
00288       if (offset>=input.length()) {
00289         error=true;
00290         break;
00291       }
00292       while (offset<input.length()) {
00293         if (input.at(offset)==QChar(']')) {
00294           offset++;
00295           break;
00296         }
00297         QString attr=parseIdentifier(input,&offset);
00298         QString value;
00299         if (attr.isEmpty() || offset>=input.length()) {
00300           error=true;
00301           break;
00302         }
00303         c=input.at(offset);
00304         if (c==QChar('=')) {
00305           offset++;
00306           //PARSE PARAMETER
00307           if (offset>=input.length()) {
00308             error=true;
00309             break;
00310           }
00311           if (input.at(offset)==QChar('"')) {
00312             // parse quoted string
00313             offset++;
00314             int stringStart=offset;
00315             while (offset<input.length()) {
00316               if (input.at(offset)==QChar('"')) {
00317                 attributes.insert(attr,input.mid(stringStart,offset-stringStart));
00318                 offset++;
00319                 break;
00320               }
00321               offset++;
00322             }
00323             if (offset>=input.length()) {
00324               error=true;
00325               break;
00326             }
00327             //skip whitespace and ,
00328             while (offset<input.length()) {
00329               c=input.at(offset);
00330               if (! ( (c==QChar(' ')) || (c==QChar('\t')) || (c==QChar(',')) ) ) {
00331                 break;
00332               }
00333               offset++;
00334             }   
00335           } else {
00336             //no "
00337             value=parseIdentifier(input,&offset,true);
00338             attributes.insert(attr,value);
00339           }
00340 
00341 
00342           if (offset>=input.length()) {
00343             error=true;
00344             break;
00345           }
00346           //skip whitespace and ,
00347           while (offset<input.length()) {
00348             c=input.at(offset);
00349             if (! ( (c==QChar(' ')) || (c==QChar('\t')) || (c==QChar(',')) ) ) {
00350               break;
00351             }
00352             offset++;
00353           }
00354         } else { //no parameter for attribute specified
00355           if (offset>=input.length()) {
00356             error=true;
00357             break;
00358           }
00359           //skip whitespace and ,
00360           while (offset<input.length()) {
00361             c=input.at(offset);
00362             if (! ( (c==QChar(' ')) || (c==QChar('\t')) || (c==QChar(',')) ) ) {
00363               break;
00364             }
00365             offset++;
00366           }
00367           attributes.insert(attr,QString());
00368         }
00369         //offset++;
00370       }
00371       if (error) break;
00372     } else {
00373 #ifdef IHP_DEBUG
00374   KPassivePopup::message(i18n("error %1",c),m_view);
00375 #endif
00376       
00377       error=true;
00378       break;
00379     }
00380   }
00381   
00382   if (newOffset) *newOffset=offset;
00383   
00384   if (!error) {
00385       
00386       
00387     for(QMap<QString,QString>::const_iterator it=attributes.constBegin();it!=attributes.constEnd();++it) {
00388       attributesString+=" "+it.key();
00389       if (!it.value().isNull()) attributesString+="=\""+it.value()+"\"";
00390     }
00391     QStringList result;
00392     if (!compound) {
00393       QString idAttrib;
00394       if (!id.isEmpty()) idAttrib=QString(" id=\"%1\"").arg(id);
00395       QString classAttrib=classes.join(" ");
00396       QString classComment;
00397       if (!classAttrib.isEmpty()) classComment="."+classes.join(" .");
00398       if (!classAttrib.isEmpty()) classAttrib=QString(" class=\"%1\"").arg(classAttrib);
00399 
00400       if (!sub.isEmpty())
00401         sub=sub.replaceInStrings(QRegExp("^"),"  ");
00402       
00403       for (int i=1;i<=multiply;i++) {
00404         bool done=false;
00405         if (!idAttrib.isEmpty()) result<<QString("|c-#%1-c|").arg(id);
00406         if (!classComment.isEmpty()) result<<QString("|c-%1-c|").arg(classComment);
00407         if (sub.isEmpty()) {        
00408           if (m_emptyTags.contains(tag)) {
00409             result<<QString("<%1%2%3%4/>").arg(tag).arg(idAttrib).arg(classAttrib).arg(attributesString);
00410             done=true;
00411           }
00412         }   
00413         if (!done){
00414           if (!sub.isEmpty()) {
00415             result<<QString("<%1%2%3%4>").arg(tag).arg(idAttrib).arg(classAttrib).arg(attributesString);
00416             result<<sub;
00417             result<<QString("</%1>").arg(tag);
00418           } else
00419             result<<QString("<%1%2%3%4></%1>").arg(tag).arg(idAttrib).arg(classAttrib).arg(attributesString);
00420         }
00421         
00422         if (!idAttrib.isEmpty()) result<<QString("|c-/#%1-c|").arg(id);
00423         if (!classComment.isEmpty()) result<<QString("|c-/%1-c|").arg(classComment);
00424       }
00425     } else {
00426       for (int i=1;i<=multiply;i++) {
00427         QStringList  tmp=compoundSub;
00428         result<<tmp;
00429       }
00430     }
00431     if (!relatives.isEmpty())
00432       result<<relatives;
00433     return result;
00434   }
00435   return QStringList();
00436 }
00437 
00438 void InsaneHTMLPluginLEView::apply_filter_e(QStringList *lines) {
00439   lines->replaceInStrings("&","&amp;");
00440   lines->replaceInStrings("<","&lt;");
00441   lines->replaceInStrings(">","&gt;");
00442 }
00443 
00444 void InsaneHTMLPluginLEView::apply_filter_c(QStringList *lines) {
00445   lines->replaceInStrings("|c-","<!-- ");
00446   lines->replaceInStrings("-c|"," -->");
00447 }
00448 
00449 void InsaneHTMLPluginLEView::expand() {
00450  KTextEditor::Cursor c=m_view->cursorPosition();
00451  QString line=m_view->document()->line(c.line());
00452  int filtercount=0;
00453  int start_x=find_region_start(c.column(),line,&filtercount);
00454  int end_x=find_region_end(c.column(),line,&filtercount);
00455  if ( (start_x<0) || (end_x<0) || (start_x==end_x) ) {
00456    KPassivePopup::message(i18n("No valid Insane HTML markup detected at current cursor position"),m_view);
00457    return;
00458  }
00459 #ifdef IHP_DEBUG
00460   KPassivePopup::message(i18n("This looks like valid Insane HTML markup: %1",line.mid(start_x,end_x-start_x)),m_view);
00461 #endif
00462   QString region_text=line.mid(start_x,end_x-start_x);
00463   QStringList filters;
00464   while(filtercount>0) {
00465     int li=region_text.lastIndexOf("|");
00466     filters<<region_text.mid(li+1);
00467     region_text=region_text.left(li);
00468     filtercount--;
00469   }
00470   QStringList result_list=parse(region_text,0);
00471   while(!filters.isEmpty()) {
00472     //built_in_filters
00473     QString filter=filters.takeLast();
00474     if (filter=="e")
00475       apply_filter_e(&result_list);
00476     else if (filter=="c") apply_filter_c(&result_list);
00477   }
00478   //remove unwanted comment marks
00479   QRegExp rcm("\\|c\\-.*\\-c\\|");
00480   for (int i=result_list.count()-1;i>=0;i--) {
00481     QString tmp=result_list[i];
00482     tmp.remove(rcm);
00483     if (tmp.trimmed().isEmpty())
00484       result_list.takeAt(i);
00485     else
00486         result_list[i]=tmp;
00487   }
00488   //prefix with indentation
00489   QString line_prefix=line.left(start_x);
00490   line_prefix.replace(QRegExp("\\S")," ");
00491   for (int i=1;i<result_list.count();i++)
00492     result_list[i]=line_prefix+result_list[i];
00493   QString result=result_list.join("\n");
00494   KTextEditor::Document *doc=m_view->document();
00495   KTextEditor::Range r(c.line(),start_x,c.line(),end_x);
00496   doc->replaceText(r,result);
00497 }
00498 
00499 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal