00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "texttoolswidget.h"
00013
00014 #include <qheader.h>
00015 #include <qregexp.h>
00016 #include <qtimer.h>
00017 #include <kdebug.h>
00018 #include <klocale.h>
00019 #include <kparts/part.h>
00020 #include <kpopupmenu.h>
00021 #include <ktexteditor/viewcursorinterface.h>
00022 #include <ktexteditor/selectioninterface.h>
00023 #include <ktexteditor/editinterface.h>
00024
00025 #include "kdevmainwindow.h"
00026 #include "kdevpartcontroller.h"
00027 #include "texttoolspart.h"
00028
00029
00030 class TextStructItem : public QListViewItem
00031 {
00032 public:
00033 TextStructItem(QListView *parent)
00034 : QListViewItem(parent)
00035 {}
00036 TextStructItem(QListViewItem *parent)
00037 : QListViewItem(parent)
00038 {
00039 QListViewItem *item = this;
00040 while (item->nextSibling())
00041 item = item->nextSibling();
00042 if (item != this)
00043 moveItem(item);
00044 }
00045
00046 QString text(int) const
00047 {
00048 return extra.isNull()? tag : QString("%1: %2").arg(tag).arg(extra);
00049 }
00050 TextStructItem *parentStructItem()
00051 { return static_cast<TextStructItem*>(parent()); }
00052
00053 QString tag;
00054 QString extra;
00055 int pos;
00056 int endpos;
00057 };
00058
00059
00060 TextToolsWidget::TextToolsWidget(TextToolsPart *part, QWidget *parent, const char *name)
00061 : KListView(parent, name)
00062 {
00063 setResizeMode(QListView::LastColumn);
00064 setSorting(-1);
00065 header()->hide();
00066 addColumn(QString::null);
00067
00068 m_part = part;
00069
00070 m_timer = new QTimer(this);
00071 connect( this, SIGNAL(mouseButtonPressed(int, QListViewItem*, const QPoint&, int)),
00072 this, SLOT(slotItemPressed(int,QListViewItem*)) );
00073
00074
00075 connect( this, SIGNAL(returnPressed(QListViewItem*)),
00076 this, SLOT(slotReturnPressed(QListViewItem*)) );
00077 connect( this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
00078 this, SLOT(slotContextMenu(KListView*, QListViewItem*, const QPoint&)) );
00079 }
00080
00081
00082 TextToolsWidget::~TextToolsWidget()
00083 {}
00084
00085
00086 void TextToolsWidget::slotItemPressed(int button, QListViewItem *item)
00087 {
00088 if (!item)
00089 return;
00090
00091 TextStructItem *tsitem = static_cast<TextStructItem*>(item);
00092 int searchedPos = tsitem->pos;
00093 int searchedEndpos = tsitem->endpos;
00094 kdDebug(9030) << "Searched pos " << searchedPos << ", " << searchedEndpos << endl;
00095
00096 int endline = 0;
00097 int endcol = 0;
00098 int line = 0;
00099 int col = 0;
00100
00101 int len = m_cachedText.length();
00102 int pos = 0;
00103 while (pos < len) {
00104 if (pos == searchedPos) {
00105 line = endline;
00106 col = endcol;
00107 }
00108 if (pos == searchedEndpos)
00109 break;
00110 QChar ch = m_cachedText[pos];
00111 if (ch == '\n') {
00112 ++endline;
00113 endcol = 0;
00114 } else {
00115 ++endcol;
00116 }
00117 ++pos;
00118 }
00119
00120 KParts::Part *rwpart
00121 = dynamic_cast<KParts::Part*>(m_part->partController()->activePart());
00122 QWidget *view = m_part->partController()->activeWidget();
00123
00124 KTextEditor::ViewCursorInterface *cursorIface
00125 = dynamic_cast<KTextEditor::ViewCursorInterface*>(view);
00126 if (cursorIface) {
00127 kdDebug(9030) << "set cursor " << line << ", " << col << endl;
00128 cursorIface->setCursorPosition(line, col);
00129 }
00130
00131 if (button == MidButton) {
00132 KTextEditor::SelectionInterface *selectionIface
00133 = dynamic_cast<KTextEditor::SelectionInterface*>(rwpart);
00134 if (selectionIface) {
00135 kdDebug(9030) << "set selection " << line << ", " << col
00136 << ", " << endline << ", " << endcol << endl;
00137 selectionIface->setSelection((int)line, (int)col, (int)endline, (int)endcol+1);
00138 }
00139 }
00140
00141 m_part->mainWindow()->lowerView(this);
00142 }
00143
00144
00145 void TextToolsWidget::slotReturnPressed(QListViewItem *item)
00146 {
00147 slotItemPressed(LeftButton, item);
00148 }
00149
00150
00151 void TextToolsWidget::slotContextMenu(KListView *, QListViewItem *item, const QPoint &)
00152 {
00153 if (!item)
00154 return;
00155
00156 #if 0
00157 KPopupMenu popup(i18n("Text Structure"), this);
00158 popup.exec(p);
00159 #endif
00160 }
00161
00162
00163 void TextToolsWidget::stop()
00164 {
00165 disconnect( m_timer );
00166 m_relevantTags.clear();
00167 m_emptyTags.clear();
00168 m_cachedText = QString::null;
00169 }
00170
00171
00172 void TextToolsWidget::setMode(Mode mode, KParts::Part *part)
00173 {
00174 connect( part, SIGNAL(textChanged()),
00175 this, SLOT(startTimer()) );
00176 m_editIface = dynamic_cast<KTextEditor::EditInterface*>(part);
00177
00178 switch (mode) {
00179 case HTML:
00180 m_relevantTags << "h1" << "h2" << "h3" << "h4"
00181 << "table" << "tr";
00182 m_emptyTags << "br" << "hr" << "img" << "input" << "p" << "meta";
00183
00184 connect( m_timer, SIGNAL(timeout()),
00185 this, SLOT(parseXML()) );
00186 break;
00187 case Docbook:
00188 m_relevantTags << "chapter" << "sect1" << "sect2"
00189 << "para" << "formalpara";
00190 connect( m_timer, SIGNAL(timeout()),
00191 this, SLOT(parseXML()) );
00192 break;
00193 case LaTeX:
00194 connect( m_timer, SIGNAL(timeout()),
00195 this, SLOT(parseLaTeX()) );
00196 break;
00197 default: ;
00198 }
00199
00200 m_timer->start(0, true);
00201 }
00202
00203
00204 void TextToolsWidget::startTimer()
00205 {
00206 kdDebug(9030) << "Starting parse timer" << endl;
00207 m_timer->start(1000, true);
00208 }
00209
00210
00211 void TextToolsWidget::parseXML()
00212 {
00213 kdDebug(9030) << "Starting to parse XML" << endl;
00214 clear();
00215 QString text = m_editIface->text();
00216 m_cachedText = text;
00217
00218 TextStructItem *currentItem = new TextStructItem(this);
00219 currentItem->tag = "Root";
00220 currentItem->pos = -1;
00221 currentItem->endpos = -1;
00222
00223 int len = text.length();
00224 for (int pos=0; pos+1 < len; ++pos) {
00225 QChar ch1 = text[pos];
00226 QChar ch2 = text[pos+1];
00227
00228 if (ch1 == '<' && ch2 == '?') {
00229
00230
00231 QString tag;
00232 int endpos = pos+2;
00233 bool foundspace = false;
00234 while (endpos+1 < len) {
00235 QChar ch3 = text[endpos];
00236 QChar ch4 = text[endpos+1];
00237 if ((ch3 == ' ' || ch3 == '\t' || ch3 == '\n') && !foundspace) {
00238 tag = text.mid(pos+2, endpos-pos-2).lower();
00239 foundspace = true;
00240 } else if (ch3 == '?' && ch4 == '>') {
00241 if (!foundspace)
00242 tag = text.mid(pos+2, endpos-pos-2).lower();
00243 break;
00244 }
00245 ++endpos;
00246 }
00247
00248 TextStructItem *item = new TextStructItem(currentItem);
00249 item->tag = "<?" + tag + "?>";
00250 item->pos = pos;
00251 item->endpos = endpos+1;
00252
00253 pos = endpos+1;
00254
00255 } else if (ch1 == '<' && ch2 == '!') {
00256
00257
00258 QString tag;
00259 int endpos = pos+2;
00260 bool foundspace = false;
00261 while (endpos+1 < len) {
00262 QChar ch3 = text[endpos];
00263 if ((ch3 == ' ' || ch3 == '\t' || ch3 == '\n') && !foundspace) {
00264 tag = text.mid(pos+2, endpos-pos-2).lower();
00265 foundspace = true;
00266 } else if (ch3 == '>') {
00267 if (!foundspace)
00268 tag = text.mid(pos+2, endpos-pos-2).lower();
00269 break;
00270 }
00271 ++endpos;
00272 }
00273
00274 TextStructItem *item = new TextStructItem(currentItem);
00275 item->tag = "<!" + tag + ">";
00276 item->pos = pos;
00277 item->endpos = endpos+1;
00278
00279 pos = endpos+1;
00280
00281 } else if (ch1 == '<' && ch2 == '/') {
00282
00283 QString tag;
00284 int endpos = pos+2;
00285 while (endpos < len) {
00286 QChar ch3 = text[endpos];
00287 if (ch3 == '>') {
00288 tag = text.mid(pos+2, endpos-pos-2).lower();
00289 break;
00290 }
00291 ++endpos;
00292 }
00293
00294 if (!m_relevantTags.contains(tag)) {
00295 pos = endpos;
00296 continue;
00297 }
00298
00299 TextStructItem *closingItem = currentItem;
00300 while (closingItem->parent() && closingItem->tag != tag)
00301 closingItem = closingItem->parentStructItem();
00302 if (closingItem->parent()) {
00303 closingItem->endpos = endpos;
00304 currentItem = closingItem->parentStructItem();
00305 } else {
00306 kdDebug(9030) << "found no opening tag " << tag << "." << endl;
00307 }
00308
00309 pos = endpos;
00310
00311 } else if (ch1 == '<') {
00312
00313 QString tag;
00314 int endpos = pos+1;
00315 bool foundspace = false;
00316 while (endpos < len) {
00317 QChar ch3 = text[endpos];
00318 if ((ch3 == ' ' || ch3 == '\t' || ch3 == '\n') && !foundspace) {
00319 tag = text.mid(pos+1, endpos-pos-1).lower();
00320 foundspace = true;
00321 } else if (ch3 == '>') {
00322 if (!foundspace) {
00323 tag = text.mid(pos+1, endpos-pos-1).lower();
00324 }
00325 break;
00326 }
00327 ++endpos;
00328 }
00329
00330 if (!m_relevantTags.contains(tag)) {
00331 pos = endpos;
00332 continue;
00333 }
00334
00335 TextStructItem *item = new TextStructItem(currentItem);
00336 item->tag = tag;
00337 item->pos = pos;
00338 item->endpos = -1;
00339
00340 if (m_emptyTags.contains(tag))
00341 item->endpos = endpos;
00342 else
00343 currentItem = item;
00344 pos = endpos;
00345
00346 }
00347 }
00348
00349
00350 QListViewItemIterator it(this);
00351 for (; it.current(); ++it)
00352 it.current()->setOpen(true);
00353 }
00354
00355
00356 void TextToolsWidget::parseLaTeX()
00357 {
00358 kdDebug(9030) << "Starting to parse LaTeX" << endl;
00359 clear();
00360 QString text = m_editIface->text();
00361 m_cachedText = text;
00362
00363 TextStructItem *currentItem = new TextStructItem(this);
00364 currentItem->tag = "Root";
00365 currentItem->pos = -1;
00366 currentItem->endpos = -1;
00367
00368 QString hierarchyLevels = "Root,chapter,section,subsection,subsubsection";
00369 QRegExp re("\n[ \t]*s*\\\\(chapter|section|subsection|subsubsection)\\{([^}]*)\\}");
00370
00371 int pos=0;
00372 for (;;) {
00373 pos = re.search(text, pos);
00374 if (pos == -1)
00375 break;
00376 QString tag = re.cap(1);
00377 QString title = re.cap(2);
00378 kdDebug(9030) << "Match with " << tag << " and title " << title << endl;
00379 int level = hierarchyLevels.find(tag);
00380 while (currentItem->parent() && level <= hierarchyLevels.find(currentItem->tag))
00381 currentItem = currentItem->parentStructItem();
00382
00383 TextStructItem *item = new TextStructItem(currentItem);
00384 item->tag = tag;
00385 item->extra = title;
00386 item->pos = pos+1;
00387 item->endpos = pos+re.matchedLength()-1;
00388
00389 if (level > hierarchyLevels.find(currentItem->tag))
00390 currentItem = item;
00391
00392 pos = pos+re.matchedLength();
00393 }
00394
00395 QListViewItemIterator it(this);
00396 for (; it.current(); ++it)
00397 it.current()->setOpen(true);
00398 }
00399
00400 #include "texttoolswidget.moc"