/*************************************************************************** * Copyright (C) 2002 by Bernd Gehrmann * * bernd@kdevelop.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "texttoolswidget.h" #include #include #include #include #include #include #include #include #include #include #include "kdevmainwindow.h" #include "kdevpartcontroller.h" #include "texttoolspart.h" class TextStructItem : public TQListViewItem { public: TextStructItem(TQListView *tqparent) : TQListViewItem(tqparent) {} TextStructItem(TQListViewItem *tqparent) : TQListViewItem(tqparent) { TQListViewItem *item = this; while (item->nextSibling()) item = item->nextSibling(); if (item != this) moveItem(item); } TQString text(int) const { return extra.isNull()? tag : TQString("%1: %2").tqarg(tag).tqarg(extra); } TextStructItem *parentStructItem() { return static_cast(tqparent()); } TQString tag; TQString extra; int pos; int endpos; }; TextToolsWidget::TextToolsWidget(TextToolsPart *part, TQWidget *tqparent, const char *name) : KListView(tqparent, name) { setResizeMode(TQListView::LastColumn); setSorting(-1); header()->hide(); addColumn(TQString()); m_part = part; m_timer = new TQTimer(this); connect( this, TQT_SIGNAL(mouseButtonPressed(int, TQListViewItem*, const TQPoint&, int)), this, TQT_SLOT(slotItemPressed(int,TQListViewItem*)) ); // connect( this, TQT_SIGNAL(doubleClicked(TQListViewItem*)), // this, TQT_SLOT(slotItemPressed(int,TQListViewItem*)) ); connect( this, TQT_SIGNAL(returnPressed(TQListViewItem*)), this, TQT_SLOT(slotReturnPressed(TQListViewItem*)) ); connect( this, TQT_SIGNAL(contextMenu(KListView*, TQListViewItem*, const TQPoint&)), this, TQT_SLOT(slotContextMenu(KListView*, TQListViewItem*, const TQPoint&)) ); } TextToolsWidget::~TextToolsWidget() {} void TextToolsWidget::slotItemPressed(int button, TQListViewItem *item) { if (!item) return; TextStructItem *tsitem = static_cast(item); int searchedPos = tsitem->pos; int searchedEndpos = tsitem->endpos; kdDebug(9030) << "Searched pos " << searchedPos << ", " << searchedEndpos << endl; int endline = 0; int endcol = 0; int line = 0; int col = 0; int len = m_cachedText.length(); int pos = 0; while (pos < len) { if (pos == searchedPos) { line = endline; col = endcol; } if (pos == searchedEndpos) break; TQChar ch = m_cachedText[pos]; if (ch == '\n') { ++endline; endcol = 0; } else { ++endcol; } ++pos; } KParts::Part *rwpart = dynamic_cast(m_part->partController()->activePart()); TQWidget *view = m_part->partController()->activeWidget(); KTextEditor::ViewCursorInterface *cursorIface = dynamic_cast(view); if (cursorIface) { kdDebug(9030) << "set cursor " << line << ", " << col << endl; cursorIface->setCursorPosition(line, col); } if (button == Qt::MidButton) { KTextEditor::SelectionInterface *selectionIface = dynamic_cast(rwpart); if (selectionIface) { kdDebug(9030) << "set selection " << line << ", " << col << ", " << endline << ", " << endcol << endl; selectionIface->setSelection((int)line, (int)col, (int)endline, (int)endcol+1); } } m_part->mainWindow()->lowerView(this); } void TextToolsWidget::slotReturnPressed(TQListViewItem *item) { slotItemPressed(Qt::LeftButton, item); } void TextToolsWidget::slotContextMenu(KListView *, TQListViewItem *item, const TQPoint &) { if (!item) return; #if 0 KPopupMenu popup(i18n("Text Structure"), this); popup.exec(p); #endif } void TextToolsWidget::stop() { disconnect( m_timer ); m_relevantTags.clear(); m_emptyTags.clear(); m_cachedText = TQString(); } void TextToolsWidget::setMode(Mode mode, KParts::Part *part) { connect( part, TQT_SIGNAL(textChanged()), this, TQT_SLOT(startTimer()) ); m_editIface = dynamic_cast(part); switch (mode) { case HTML: m_relevantTags << "h1" << "h2" << "h3" << "h4" << "table" << "tr"; m_emptyTags << "br" << "hr" << "img" << "input" << "p" << "meta"; connect( m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(parseXML()) ); break; case Docbook: m_relevantTags << "chapter" << "sect1" << "sect2" << "para" << "formalpara"; connect( m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(parseXML()) ); break; case LaTeX: connect( m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(parseLaTeX()) ); break; default: ; } m_timer->start(0, true); } void TextToolsWidget::startTimer() { kdDebug(9030) << "Starting parse timer" << endl; m_timer->start(1000, true); } void TextToolsWidget::parseXML() { kdDebug(9030) << "Starting to parse XML" << endl; clear(); TQString text = m_editIface->text(); m_cachedText = text; TextStructItem *currentItem = new TextStructItem(this); currentItem->tag = "Root"; currentItem->pos = -1; currentItem->endpos = -1; int len = text.length(); for (int pos=0; pos+1 < len; ++pos) { TQChar ch1 = text[pos]; TQChar ch2 = text[pos+1]; if (ch1 == '<' && ch2 == '?') { // PHP and other similar stuff TQString tag; int endpos = pos+2; bool foundspace = false; while (endpos+1 < len) { TQChar ch3 = text[endpos]; TQChar ch4 = text[endpos+1]; if ((ch3 == ' ' || ch3 == '\t' || ch3 == '\n') && !foundspace) { tag = text.mid(pos+2, endpos-pos-2).lower(); foundspace = true; } else if (ch3 == '?' && ch4 == '>') { if (!foundspace) tag = text.mid(pos+2, endpos-pos-2).lower(); break; } ++endpos; } TextStructItem *item = new TextStructItem(currentItem); item->tag = ""; item->pos = pos; item->endpos = endpos+1; pos = endpos+1; } else if (ch1 == '<' && ch2 == '!') { // Processing instructions like !DOCTYPE TQString tag; int endpos = pos+2; bool foundspace = false; while (endpos+1 < len) { TQChar ch3 = text[endpos]; if ((ch3 == ' ' || ch3 == '\t' || ch3 == '\n') && !foundspace) { tag = text.mid(pos+2, endpos-pos-2).lower(); foundspace = true; } else if (ch3 == '>') { if (!foundspace) tag = text.mid(pos+2, endpos-pos-2).lower(); break; } ++endpos; } TextStructItem *item = new TextStructItem(currentItem); item->tag = ""; item->pos = pos; item->endpos = endpos+1; pos = endpos+1; } else if (ch1 == '<' && ch2 == '/') { TQString tag; int endpos = pos+2; while (endpos < len) { TQChar ch3 = text[endpos]; if (ch3 == '>') { tag = text.mid(pos+2, endpos-pos-2).lower(); break; } ++endpos; } if (!m_relevantTags.tqcontains(tag)) { pos = endpos; continue; } TextStructItem *closingItem = currentItem; while (closingItem->tqparent() && closingItem->tag != tag) closingItem = closingItem->parentStructItem(); if (closingItem->tqparent()) { closingItem->endpos = endpos; currentItem = closingItem->parentStructItem(); } else { kdDebug(9030) << "found no opening tag " << tag << "." << endl; } pos = endpos; } else if (ch1 == '<') { TQString tag; int endpos = pos+1; bool foundspace = false; while (endpos < len) { TQChar ch3 = text[endpos]; if ((ch3 == ' ' || ch3 == '\t' || ch3 == '\n') && !foundspace) { tag = text.mid(pos+1, endpos-pos-1).lower(); foundspace = true; } else if (ch3 == '>') { if (!foundspace) { tag = text.mid(pos+1, endpos-pos-1).lower(); } break; } ++endpos; } if (!m_relevantTags.tqcontains(tag)) { pos = endpos; continue; } TextStructItem *item = new TextStructItem(currentItem); item->tag = tag; item->pos = pos; item->endpos = -1; if (m_emptyTags.tqcontains(tag)) item->endpos = endpos; else currentItem = item; pos = endpos; } } // firstChild()->setOpen(true); TQListViewItemIterator it(this); for (; it.current(); ++it) it.current()->setOpen(true); } void TextToolsWidget::parseLaTeX() { kdDebug(9030) << "Starting to parse LaTeX" << endl; clear(); TQString text = m_editIface->text(); m_cachedText = text; TextStructItem *currentItem = new TextStructItem(this); currentItem->tag = "Root"; currentItem->pos = -1; currentItem->endpos = -1; TQString hierarchyLevels = "Root,chapter,section,subsection,subsubsection"; TQRegExp re("\n[ \t]*s*\\\\(chapter|section|subsection|subsubsection)\\{([^}]*)\\}"); int pos=0; for (;;) { pos = re.search(text, pos); if (pos == -1) break; TQString tag = re.cap(1); TQString title = re.cap(2); kdDebug(9030) << "Match with " << tag << " and title " << title << endl; int level = hierarchyLevels.tqfind(tag); while (currentItem->tqparent() && level <= hierarchyLevels.tqfind(currentItem->tag)) currentItem = currentItem->parentStructItem(); TextStructItem *item = new TextStructItem(currentItem); item->tag = tag; item->extra = title; item->pos = pos+1; item->endpos = pos+re.matchedLength()-1; // lie if (level > hierarchyLevels.tqfind(currentItem->tag)) currentItem = item; pos = pos+re.matchedLength(); } TQListViewItemIterator it(this); for (; it.current(); ++it) it.current()->setOpen(true); } #include "texttoolswidget.moc"