File indexing completed on 2024-04-28 16:26:35

0001 /*************************************************************************************************
0002    Copyright (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net
0003                  2005-2007 by Holger Danielsson (holger.danielsson@versanet.de)
0004                  2008-2022 by Michel Ludwig (michel.ludwig@kdemail.net)
0005  *************************************************************************************************/
0006 
0007 /***************************************************************************
0008  *                                                                         *
0009  *   This program is free software; you can redistribute it and/or modify  *
0010  *   it under the terms of the GNU General Public License as published by  *
0011  *   the Free Software Foundation; either version 2 of the License, or     *
0012  *   (at your option) any later version.                                   *
0013  *                                                                         *
0014  ***************************************************************************/
0015 
0016 // 2005-11-02: dani
0017 //  - cleaning up source (local variables etc.)
0018 //  - added different QToolTips for each item
0019 //  - add more types of KilelistViewItems
0020 //  - KileStruct::Sect and KileStruct::BeginFloat will remember assigned labels,
0021 //    which are displayed as QToolTips, if these labels are defined in the
0022 //    same or the next line
0023 //  - Caption for type KileStruct::BeginFloat are displayed in the title
0024 //    of this item
0025 //  - \includegraphics and float environment items are displayed
0026 //  - if an item has a companion label, you can use the context menu (right mouse)
0027 //    to insert this label as reference, as a page reference or only the keyword
0028 //    into the text or copy it to the clipboard.
0029 //  - graphics files have also a context menu to open them with a special program
0030 
0031 // 2005-12-08: dani
0032 //  - make some items like labels, bibitems, graphics and float environments
0033 //    configurable for the user
0034 
0035 // 2005-12-16: dani
0036 //  - add listview item for undefined references
0037 
0038 // 2007-02-15: dani
0039 // - class StructureViewItem gets two new members to
0040 //   save the real cursor position of the command
0041 
0042 // 2007-03-12 dani
0043 //  - use KileDocument::Extensions
0044 
0045 // 2007-03-17 dani
0046 //  - remember how document structure is collapsed, when structure view is refreshed
0047 
0048 // 2007-03-24: dani
0049 // - preliminary minimal support for Beamer class
0050 // - \begin{frame}...\end{frame} and \frame{...} are shown in the structure view
0051 // - if a \frametitle command follows as next LaTeX command, its parameter
0052 //   is taken to replace the standard title of this entry in the structure view
0053 // - \begin{block}...\end{block} is taken as child of a frame
0054 
0055 // 2007-04-06 dani
0056 // - add TODO/FIXME section to structure view
0057 
0058 #include "widgets/structurewidget.h"
0059 
0060 #include <QClipboard>
0061 #include <QFileInfo>
0062 #include <QIcon>
0063 #include <QHeaderView>
0064 #include <QMimeDatabase>
0065 #include <QMimeType>
0066 #include <QRegExp>
0067 #include <QScrollBar>
0068 #include <QUrl>
0069 
0070 #include <KApplicationTrader>
0071 #include <KIO/ApplicationLauncherJob>
0072 #include <KIO/JobUiDelegateFactory>
0073 #include <KIO/OpenUrlJob>
0074 #include <KJobUiDelegate>
0075 #include <KLocalizedString>
0076 #include <KMessageBox>
0077 
0078 #include "documentinfo.h"
0079 #include "errorhandler.h"
0080 #include "kileconfig.h"
0081 #include "kiledebug.h"
0082 #include "kiledocmanager.h"
0083 #include "kileinfo.h"
0084 #include "kileproject.h"
0085 #include "kiletool_enums.h"
0086 #include "parser/parsermanager.h"
0087 #include "widgets/logwidget.h"
0088 
0089 namespace KileWidget
0090 {
0091 ////////////////////// StructureViewItem with all info //////////////////////
0092 
0093 StructureViewItem::StructureViewItem(QTreeWidgetItem* parent, const QString &title, const QUrl &url, uint line, uint column, int type, int level, uint startline, uint startcol) :
0094     QTreeWidgetItem(parent),
0095     m_title(title), m_url(url), m_line(line), m_column(column), m_type(type), m_level(level),
0096     m_startline(startline), m_startcol(startcol)
0097 {
0098     setItemEntry();
0099 }
0100 
0101 StructureViewItem::StructureViewItem(QTreeWidget* parent, const QString& label) :
0102     QTreeWidgetItem(parent, QStringList(label)),
0103     m_title(label), m_url(QUrl()), m_line(0),  m_column(0), m_type(KileStruct::None), m_level(0)
0104 {
0105     setToolTip(0, i18n("Click left to jump to the line. A double click will open\n a text file or a graphics file. When a label is assigned\nto this item, it will be shown when the mouse is over\nthis item. Items for a graphics file or an assigned label\nalso offer a context menu (right mouse button)."));
0106 }
0107 
0108 StructureViewItem::StructureViewItem(const QString& label, QTreeWidgetItem* parent) :
0109     QTreeWidgetItem(parent, QStringList(label)),
0110     m_title(label), m_url(QUrl()), m_line(0),  m_column(0), m_type(KileStruct::None), m_level(0)
0111 {}
0112 
0113 void StructureViewItem::setTitle(const QString &title)
0114 {
0115     m_title = title;
0116     setItemEntry();
0117 }
0118 
0119 void StructureViewItem::setItemEntry()
0120 {
0121     setText(0, i18nc("structure view entry: title (line)", "%1 (line %2)", m_title, QString::number(m_line)));
0122     setToolTip(0, text(0));
0123 }
0124 
0125 void StructureViewItem::setLabel(const QString &label)
0126 {
0127     m_label = label;
0128     if(!m_label.isEmpty()) {
0129         setToolTip(0, i18n("Label: %1", m_label));
0130     }
0131 }
0132 
0133 ////////////////////// StructureView tree widget //////////////////////
0134 
0135 StructureView::StructureView(StructureWidget *stack, KileDocument::Info *docinfo) :
0136     QTreeWidget(stack),
0137     m_stack(stack), m_docinfo(docinfo)
0138 {
0139     stack->addWidget(this);
0140     setColumnCount(1);
0141     QStringList labelList;
0142     labelList << i18n("Structure");
0143     setHeaderLabels(labelList);
0144 
0145     header()->hide();
0146     header()->setSectionResizeMode(QHeaderView::ResizeToContents);
0147     setAllColumnsShowFocus(true);
0148     setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
0149 
0150     //connect(this, SIGNAL(clicked(QListViewItem*)), m_stack, SLOT(slotClicked(QListViewItem*)));
0151     connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), m_stack, SLOT(slotDoubleClicked(QTreeWidgetItem*)));
0152 
0153     connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), m_stack, SLOT(slotClicked(QTreeWidgetItem*)));
0154     connect(m_stack, SIGNAL(configChanged()), this, SLOT(slotConfigChanged()));
0155 
0156     init();
0157 }
0158 
0159 StructureView::~StructureView() {}
0160 
0161 void StructureView::init()
0162 {
0163     QString title = (!m_docinfo) ? i18n("No \"structure data\" to display.") : m_docinfo->url().fileName();
0164     m_root =  new StructureViewItem(this, title);
0165     if(m_docinfo) {
0166         m_root->setURL(m_docinfo->url());
0167         m_root->setExpanded(true);
0168         m_root->setIcon(0, QIcon::fromTheme("contents"));
0169         connect(m_docinfo, SIGNAL(foundItem(QString,uint,uint,int,int,uint,uint,QString,QString)),
0170                 this, SLOT(addItem(QString,uint,uint,int,int,uint,uint,QString,QString)));
0171     }
0172 
0173     m_parent[0]=m_parent[1]=m_parent[2]=m_parent[3]=m_parent[4]=m_parent[5]=m_parent[6]=m_root;
0174     m_lastType = KileStruct::None;
0175     m_lastSectioning = Q_NULLPTR;
0176     m_lastFloat = Q_NULLPTR;
0177     m_lastFrame = Q_NULLPTR;
0178     m_lastFrameEnv = Q_NULLPTR;
0179     m_stop = false;
0180 
0181     m_folders.clear();
0182     m_references.clear();
0183 
0184     if(m_docinfo) {
0185         m_openStructureLabels = m_docinfo->openStructureLabels();
0186         m_openStructureReferences = m_docinfo->openStructureReferences();
0187         m_openStructureBibitems = m_docinfo->openStructureBibitems();
0188         m_openStructureTodo = m_docinfo->openStructureTodo();
0189         m_showStructureLabels = m_docinfo->showStructureLabels();
0190     }
0191     else {
0192         m_openStructureLabels = false;
0193         m_openStructureReferences = false;
0194         m_openStructureBibitems = false;
0195         m_openStructureTodo = false;
0196         m_showStructureLabels = false;
0197     }
0198 }
0199 
0200 void StructureView::updateRoot()
0201 {
0202     m_root->setURL( m_docinfo->url() );
0203     m_root->setText(0, m_docinfo->url().fileName() );
0204 }
0205 
0206 void StructureView::cleanUp(bool preserveState/* = true */)
0207 {
0208     KILE_DEBUG_MAIN << "==void StructureView::cleanUp()========";
0209     if(preserveState) {
0210         saveState();
0211     }
0212     clear();
0213     if(m_docinfo) {
0214         disconnect(m_docinfo, 0, this, 0);
0215     }
0216     init();
0217 }
0218 
0219 void StructureView::slotConfigChanged() {
0220     QWidget *current = m_stack->currentWidget();
0221     if(!current) {
0222         return;
0223     }
0224     cleanUp(false);
0225     m_stack->update(m_docinfo, true);
0226 }
0227 
0228 void StructureView::contextMenuEvent(QContextMenuEvent *event)
0229 {
0230     m_stack->viewContextMenuEvent(this, event);
0231 }
0232 
0233 void StructureView::saveState()
0234 {
0235     KILE_DEBUG_MAIN << "===void StructureView::saveState()";
0236     m_openByTitle.clear();
0237     m_openByLine.clear();
0238     m_openByFolders.clear();
0239 
0240     QTreeWidgetItemIterator it(this);
0241     StructureViewItem *item = Q_NULLPTR;
0242     while(*it) {
0243         item = dynamic_cast<StructureViewItem*>(*it);
0244         if(item && item->child(0)) {
0245             //we don't accept duplicate entries in the map, remove this item alltogether
0246             //and rely on the openByLine map instead
0247             if(m_openByTitle.contains(item->title())) {
0248                 m_openByTitle.remove(item->title());
0249             }
0250             else {
0251                 m_openByTitle[item->title()] = item->isExpanded();
0252             }
0253 
0254             m_openByLine[item->line()] = item->isExpanded();
0255         }
0256         ++it;
0257     }
0258 
0259     if(m_folders.contains("labels")) {
0260         m_openByFolders["labels"] = m_folders["labels"]->isExpanded();
0261     }
0262     if(m_folders.contains("refs")) {
0263         m_openByFolders["refs"] = m_folders["refs"]->isExpanded();
0264     }
0265     if(m_folders.contains("bibs")) {
0266         m_openByFolders["bibs"] = m_folders["bibs"]->isExpanded();
0267     }
0268     if(m_folders.contains("todo")) {
0269         m_openByFolders["todo"] = m_folders["todo"]->isExpanded();
0270     }
0271     if(m_folders.contains("fixme")) {
0272         m_openByFolders["fixme"] = m_folders["fixme"]->isExpanded();
0273     }
0274 }
0275 
0276 bool StructureView::shouldBeOpen(StructureViewItem *item, const QString & folder, int level)
0277 {
0278     if(!item->parent()) {
0279         return true;
0280     }
0281     if(folder == "labels") {
0282         if(m_openByFolders.contains("labels")) {
0283             return m_openByFolders["labels"];
0284         }
0285         else {
0286             return m_openStructureLabels;
0287         }
0288     }
0289     else if(folder == "refs") {
0290         if(m_openByFolders.contains("refs")) {
0291             return m_openByFolders["refs"];
0292         }
0293         else {
0294             return m_openStructureReferences;
0295         }
0296     }
0297     else if(folder == "bibs") {
0298         if(m_openByFolders.contains("bibs")) {
0299             return m_openByFolders["bibs"];
0300         }
0301         else {
0302             return m_openStructureBibitems;
0303         }
0304     }
0305     else if(folder=="todo" || folder=="fixme") {
0306         if(m_openByFolders.contains(folder)) {
0307             return m_openByFolders[folder];
0308         }
0309         else {
0310             return m_openStructureTodo;
0311         }
0312     }
0313 
0314     if(m_openByTitle.contains(item->title())) {
0315         return m_openByTitle[item->title()];
0316     }
0317     else if(m_openByLine.contains(item->line())) {
0318         return m_openByLine[item->line()]; //TODO check surrounding lines as well
0319     }
0320     else {
0321         return ((folder == "root") && level <= m_stack->level());
0322     }
0323 }
0324 
0325 StructureViewItem* StructureView::createFolder(const QString &folder)
0326 {
0327     StructureViewItem *fldr = new StructureViewItem(folder);
0328     // add it as a top-level child
0329     m_root->insertChild(0, fldr);
0330     fldr->setExpanded(false);
0331     if(folder == "labels") {
0332         fldr->setText(0, i18n("Labels"));
0333         fldr->setIcon(0, QIcon::fromTheme("label"));
0334     }
0335     else if(folder == "bibs") {
0336         fldr->setText(0, i18n("BibTeX References"));
0337         fldr->setIcon(0, QIcon::fromTheme("viewbib"));
0338     }
0339     else if(folder == "refs") {
0340         fldr->setText(0, i18n("Undefined References"));
0341         fldr->setIcon(0, QIcon::fromTheme("dialog-error"));
0342     }
0343     else if(folder == "todo") {
0344         fldr->setText(0, i18n("TODO"));
0345         fldr->setIcon(0, QIcon::fromTheme("bookmarks"));
0346     }
0347     else if(folder == "fixme") {
0348         fldr->setText(0, i18n("FIXME"));
0349         fldr->setIcon(0, QIcon::fromTheme("bookmarks"));
0350     }
0351 
0352     m_folders[folder] = fldr;
0353 
0354     return m_folders[folder];
0355 }
0356 
0357 StructureViewItem* StructureView::folder(const QString &folder)
0358 {
0359     StructureViewItem *item = m_folders[folder];
0360     if(!item) {
0361         item = createFolder(folder);
0362     }
0363     return item;
0364 }
0365 
0366 void StructureView::activate()
0367 {
0368     if(m_stack->indexOf(this) >= 0) {
0369         m_stack->setCurrentWidget(this);
0370     }
0371 }
0372 
0373 StructureViewItem *StructureView::parentFor(int lev, const QString & fldr)
0374 {
0375     StructureViewItem *par = Q_NULLPTR;
0376 
0377     if(fldr == "root") {
0378         switch(lev) {
0379         case KileStruct::Object:
0380         case KileStruct::File:
0381             par = (!m_lastSectioning) ? m_root : m_lastSectioning;
0382             break;
0383 
0384         case 0:
0385         case 1:
0386             par = m_root;
0387             break;
0388 
0389         default:
0390             par = m_parent[lev - 2];
0391             break;
0392         }
0393     }
0394     else {
0395         par = folder(fldr);
0396     }
0397 
0398     return par;
0399 }
0400 
0401 ////////////////////// add a new item to the tree widget //////////////////////
0402 
0403 /* some items have a special action:
0404     - KileStruct::Sect:
0405           The new item is saved in m_lastSectioning, so that all following entries
0406           can be inserted as children. \part will drop back to level 0 of the Listview,
0407           all other sectioning commands will be children of the last sectioning item.
0408           If a \label command follows in the same or the next line, it is assigned
0409           to this item.
0410     - KileStruct::BeginFloat:
0411           The new item is saved in m_lastFloat. If a \caption command follows before
0412           the floating environment is closed, it is inserted into the title of this item.
0413           If a \label command follows, it is assigned to this float item.
0414     - KileStruct::EndFloat
0415           Reset m_lastFloat to Q_NULLPTR to close this environment. No more \caption or \label
0416           commands are assigned to this float after this.
0417     - KileStruct::Caption
0418           If a float environment is opened, the caption is assigned to the float item.
0419           A caption item has hidden attribute, so that no other action is performed and
0420           function addItem() will return immediately.
0421     - KileStruct::Label
0422           If we are inside a float, this label is assigned to this environment. If the last
0423           type was a sectioning command on the current line or the line before, the label is
0424           assigned to this sectioning item. Assigning means that a popup menu will open,
0425           when the mouse is over this item.
0426     - KileStruct::BeamerBeginFrame
0427           The new item is saved in m_lastFrameEnv. If a \frametitle command follows before
0428           the frame environment is closed, it is inserted into the title of this item.
0429           If a \label command follows, it is assigned to this float item.
0430     - KileStruct::BeamerEndFrame
0431           Reset m_lastFloatEnv to Q_NULLPTR to close this environment. No more \frametitle
0432           or \label commands are assigned to this frame after this.
0433     - KileStruct::BeamerBeginBlock
0434           Inside a beamer frame this environment is taken as child of this frame
0435     - KileStruct::BeamerFrame
0436           The new item is saved in m_lastFrame. If a \frametitle command follows
0437           immediately as next command, it is inserted into the title of this item.
0438     */
0439 
0440 void StructureView::addItem(const QString &title, uint line, uint column, int type, int lev,
0441                             uint startline, uint startcol,
0442                             const QString &pix, const QString &fldr /* = "root" */)
0443 {
0444 //          KILE_DEBUG_MAIN << "\t\taddItem: " << title << ", with type " <<  type;
0445     if(m_stop) {
0446         return;
0447     }
0448 
0449     // some types need a special action
0450     if(type == KileStruct::Reference) {
0451         m_references.prepend(KileReferenceData(title, line, column));
0452     }
0453     else if(type==KileStruct::Caption && m_lastFloat) {
0454         QString floattitle = m_lastFloat->title();
0455         if(floattitle == "figure" || floattitle == "table") {
0456             m_lastFloat->setTitle(floattitle+": "+title);
0457         }
0458     }
0459     else if(type == KileStruct::EndFloat) {
0460         m_lastFloat = Q_NULLPTR;
0461     }
0462     else if(type == KileStruct::BeamerFrametitle) {
0463         if(m_lastFrameEnv) {
0464             m_lastFrameEnv->setTitle(title);
0465         }
0466         else if(m_lastFrame) {
0467             m_lastFrame->setTitle(title);
0468         }
0469     }
0470     else if(type == KileStruct::BeamerEndFrame) {
0471         m_lastFrameEnv = Q_NULLPTR;
0472     }
0473     m_lastFrame = Q_NULLPTR;
0474 
0475     // that's all for hidden types: we must immediately return
0476     if(lev == KileStruct::Hidden) {
0477         //KILE_DEBUG_MAIN << "\t\thidden item: not created";
0478         return;
0479     }
0480 
0481     //KILE_DEBUG_MAIN << "\t\tcreate new item";
0482     // check if we have to update a label before loosing this item
0483     if(type==KileStruct::Label) {
0484         if(m_lastFloat) {
0485             m_lastFloat->setLabel(title);
0486         }
0487         else if(m_lastType == KileStruct::Sect) {
0488             // check if this is a follow up label for the last sectioning item
0489             if(m_lastSectioning && (m_lastLine == line-1 || m_lastLine==line)) {
0490                 m_lastSectioning->setLabel(title);
0491             }
0492         }
0493         else if(m_lastType == KileStruct::BeamerBeginFrame && m_lastFrameEnv) {
0494             m_lastFrameEnv->setLabel(title);
0495         }
0496 
0497         if(!m_showStructureLabels) { // if we don't want to have it displayed return here
0498             return;
0499         }
0500     }
0501 
0502     // remember current type and line for the next call of addItem()
0503     m_lastType = type;
0504     m_lastLine = line;
0505 
0506     //find the parent for the new element
0507     StructureViewItem *parentItem = (type == KileStruct::BeamerBeginBlock && m_lastFrameEnv) ? m_lastFrameEnv : parentFor(lev, fldr);
0508     if(!parentItem) {
0509         KMessageBox::error(m_stack->info()->mainWindow(), i18n("Cannot create a list view item: no parent found."));
0510         return;
0511     }
0512 
0513     // create a new item
0514     StructureViewItem *newChild = new StructureViewItem(parentItem, title, m_docinfo->url(), line, column, type, lev, startline, startcol);
0515     if(!pix.isEmpty()) {
0516         newChild->setIcon(0, QIcon::fromTheme(pix));
0517     }
0518     //m_stop = true;
0519 
0520     //if the level is not greater than the defaultLevel
0521     //open the parentItem to make this item visible
0522     parentItem->setExpanded(shouldBeOpen(parentItem, fldr, lev));
0523 
0524     //update the m_parent levels, such that section etc. get inserted at the correct level
0525     //m_current = newChild;
0526     if(lev > 0) {
0527         m_parent[lev - 1] = newChild;
0528         for(int l = lev; l < 7; ++l) {
0529             m_parent[l] = newChild;
0530         }
0531     }
0532     else if(lev == 0) {
0533         m_lastSectioning = Q_NULLPTR;
0534         for(int l = 0; l < 7; ++l) {
0535             m_parent[l] = m_root;
0536         }
0537     }
0538 
0539     // check if we have to remember the new item for setting a label or caption
0540     if(type == KileStruct::Sect) {
0541         m_lastSectioning = newChild;
0542     }
0543     else if(type == KileStruct::BeginFloat) {
0544         m_lastFloat = newChild;
0545     }
0546     else if(type == KileStruct::BeamerBeginFrame) {
0547         m_lastFrameEnv = newChild;
0548     }
0549     else if(type == KileStruct::BeamerFrame) {
0550         m_lastFrame = newChild;
0551     }
0552 }
0553 
0554 void StructureView::showReferences(KileInfo *ki)
0555 {
0556     // remove old listview item for references, if it exists
0557     if(m_folders.contains("refs")) {
0558         StructureViewItem *refitem = m_folders["refs"];
0559         m_root->removeChild(refitem);
0560         delete refitem;
0561 
0562         m_folders.remove("refs");
0563     }
0564 
0565     //KILE_DEBUG_MAIN << "==void StructureView::showReferences()========";
0566     //KILE_DEBUG_MAIN << "\tfound " << m_references.count() << " references";
0567     if(m_references.count() == 0) {
0568         return;
0569     }
0570 
0571     // read list with all labels
0572     QStringList list = ki->allLabels();
0573     //KILE_DEBUG_MAIN << "\tfound " << list.count() << " labels";
0574     QMap<QString,bool> labelmap;
0575     for (QStringList::const_iterator itmap = list.constBegin(); itmap != list.constEnd(); ++itmap) {
0576         labelmap[(*itmap)] = true;
0577     }
0578 
0579     // now check if there are unsolved references
0580     for (QList<KileReferenceData>::const_iterator it = m_references.constBegin(); it!=m_references.constEnd(); ++it) {
0581         if(!labelmap.contains((*it).name())) {
0582             StructureViewItem *refitem = folder("refs");
0583             refitem->setExpanded(shouldBeOpen(refitem, "refs", 0));
0584             new StructureViewItem(refitem, (*it).name(), m_docinfo->url(), (*it).line(), (*it).column(), KileStruct::Reference, KileStruct::NotSpecified, 0, 0);
0585         }
0586     }
0587 }
0588 
0589 ////////////////////// StructureWidget: QWidgetStack //////////////////////
0590 
0591 StructureWidget::StructureWidget(KileInfo *ki, QWidget * parent, const char* name) :
0592     QStackedWidget(parent),
0593     m_ki(ki),
0594     m_docinfo(Q_NULLPTR),
0595     m_showingContextMenu(Q_NULLPTR)
0596 {
0597     setObjectName(name);
0598     KILE_DEBUG_MAIN << "==KileWidget::StructureWidget::StructureWidget()===========";
0599     setLineWidth(0);
0600     setMidLineWidth(0);
0601     setContentsMargins(0, 0, 0, 0);
0602     setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
0603 
0604     m_default = new StructureView(this, Q_NULLPTR);
0605     m_default->activate();
0606 
0607     connect(m_ki->parserManager(), SIGNAL(documentParsingStarted()), this, SLOT(handleDocumentParsingStarted()));
0608     connect(m_ki->parserManager(), SIGNAL(documentParsingComplete()), this, SLOT(handleDocumentParsingCompleted()));
0609 }
0610 
0611 StructureWidget::~StructureWidget()
0612 {
0613 }
0614 
0615 int StructureWidget::level()
0616 {
0617     return KileConfig::defaultLevel();
0618 }
0619 
0620 void StructureWidget::addDocumentInfo(KileDocument::Info *docinfo)
0621 {
0622     StructureView *view = new StructureView(this, docinfo);
0623     addWidget(view);
0624     m_map.insert(docinfo, view);
0625 }
0626 
0627 void StructureWidget::slotClicked(QTreeWidgetItem * itm)
0628 {
0629     KILE_DEBUG_MAIN << "\tStructureWidget::slotClicked";
0630 
0631     StructureViewItem *item = dynamic_cast<StructureViewItem*>(itm);
0632     //return if user didn't click on an item
0633     if(!item) {
0634         return;
0635     }
0636 
0637     if(!(item->type() & KileStruct::None)) {
0638         emit(setCursor(item->url(), item->line()-1, item->column()));
0639     }
0640     else if(!item->parent()) { //root item
0641         emit(setCursor(item->url(), 0, 0));
0642     }
0643 }
0644 
0645 void StructureWidget::slotDoubleClicked(QTreeWidgetItem * itm)
0646 {
0647     KILE_DEBUG_MAIN << "\tStructureWidget::slotDoubleClicked";
0648     StructureViewItem *item = dynamic_cast<StructureViewItem*>(itm);
0649     static QRegExp suffix("\\.[\\d\\w]*$");
0650 
0651     if (!item) {
0652         return;
0653     }
0654 
0655     KILE_DEBUG_MAIN <<"item->url() is " << item->url() << ", item->title() is " << item->title();
0656 
0657     if(item->type() & (KileStruct::Input | KileStruct::Bibliography | KileStruct::Graphics)) {
0658         QString fname = item->title();
0659 
0660 
0661         if(fname.indexOf(suffix) != -1) { // check if we have a suffix, if not add standard suffixes
0662             KILE_DEBUG_MAIN << "Suffix found: " << suffix.cap(0);
0663         }
0664         else {
0665             // filename in structureview entry has no extension: this shouldn't happen anymore,
0666             // because all have got one in function updateStruct(). But who knows?
0667             if(item->type() == KileStruct::Input) {
0668                 fname += m_ki->extensions()->latexDocumentDefault();
0669             }
0670             else if(item->type() == KileStruct::Bibliography) {
0671                 fname += m_ki->extensions()->bibtexDefault();
0672             }
0673             else if(item->type() == KileStruct::Graphics) {
0674 
0675                 KileProjectItem *kiItem = m_ki->docManager()->itemFor(item->url());
0676                 KileProject *proj;
0677                 QString extToAdd;
0678                 bool fromProject = true;
0679 
0680                 if(kiItem && (proj = kiItem->project())) {
0681                     extToAdd = proj->defaultGraphicExt();
0682                 }
0683                 if(extToAdd.isEmpty()) {
0684                     extToAdd = KileConfig::svDefaultGraphicExt();
0685                     fromProject = false;
0686                 }
0687 
0688                 m_ki->errorHandler()->printMessage(KileTool::Info,
0689                                                    (fromProject ?
0690                                                     i18n("No extension specified for graphic file.  Using .%1 from Project settings.", extToAdd) :
0691                                                     i18n("No extension specified for graphic file.  Using .%1 from global Structure View settings.", extToAdd)),
0692                                                    i18n("File extension not specified"));
0693 
0694                 fname += '.' + extToAdd;
0695 
0696             }
0697             else {
0698                 KILE_DEBUG_MAIN << "Suffixless item with unknown type found";
0699             }
0700 
0701         }
0702 
0703         if(QDir::isRelativePath(fname)) { // no absolute path
0704             QString fn = m_ki->getCompileName();
0705             fname= QFileInfo(fn).path() + QDir::separator() + fname;
0706         }
0707 
0708         QFileInfo fi(fname);
0709 
0710         if(fi.isReadable()) {
0711             QUrl url = QUrl::fromLocalFile(fname);
0712             if(item->type() == KileStruct::Graphics) {
0713                 QMimeDatabase db;
0714                 QMimeType pMime = db.mimeTypeForUrl(url);
0715                 auto *job = new KIO::OpenUrlJob(url, pMime.name());
0716                 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0717                 job->start();
0718             }
0719             else {
0720                 emit(fileOpen(url, QString()));
0721             }
0722         }
0723         else {
0724             QString otherFilename;
0725 
0726             if(item->type() == KileStruct::Bibliography) {
0727                 otherFilename = m_ki->checkOtherPaths(fi.path(), fi.fileName(), KileInfo::bibinputs);
0728             }
0729             else if(item->type() == KileStruct::Input) {
0730                 otherFilename = m_ki->checkOtherPaths(fi.path(), fi.fileName(), KileInfo::texinputs);
0731             }
0732 
0733             fi.setFile(otherFilename);
0734 
0735             if(fi.isReadable()) {
0736                 emit(fileOpen(QUrl::fromLocalFile(otherFilename), QString()));
0737             }
0738             else {
0739                 if(KMessageBox::warningTwoActions(this,
0740                                                   i18n("Cannot find the included file. The file does not exist, is not readable or Kile is unable to determine the correct path to it. The filename causing this error was: %1.\nDo you want to create this file?", fname),
0741                                                   i18n("Cannot Find File"),
0742                                                   KStandardGuiItem::ok(),
0743                                                   KStandardGuiItem::cancel())
0744                         == KMessageBox::PrimaryAction) {
0745                     emit(fileNew(QUrl::fromLocalFile(fname)));
0746                 }
0747             }
0748         }
0749     }
0750 }
0751 
0752 // all popup items get different id's, so that we can see, what item is activated
0753 //  - label:       1 -  6
0754 //  - sectioning: 10 - 16
0755 //  - graphics:   100ff
0756 void StructureWidget::viewContextMenuEvent(StructureView *view, QContextMenuEvent *event)
0757 {
0758     KILE_DEBUG_MAIN << "\tcalled";
0759 
0760     QMenu popup;
0761     m_showingContextMenu = Q_NULLPTR;
0762 
0763     m_popupItem = dynamic_cast<StructureViewItem*>(view->itemAt(event->pos()));
0764     if(!m_popupItem) {
0765         KILE_DEBUG_MAIN << "not a pointer to a StructureViewItem object.";
0766         return;
0767     }
0768 
0769     bool hasLabel = !(m_popupItem->label().isEmpty());
0770 
0771     if(m_popupItem->type() == KileStruct::Sect) {
0772         if(hasLabel) {
0773             popup.addSection(i18n("Sectioning"));
0774         }
0775         popup.addAction(i18n("Cu&t"), this, [this] { slotPopupSectioning(SectioningCut); });
0776         popup.addAction(i18n("&Copy"), this, [this] { slotPopupSectioning(SectioningCopy); });
0777         popup.addAction(i18n("&Paste below"), this, [this] { slotPopupSectioning(SectioningPaste); });
0778         popup.addSeparator();
0779         popup.addAction(i18n("&Select"), this, [this] { slotPopupSectioning(SectioningSelect); });
0780         popup.addAction(i18n("&Delete"), this, [this] { slotPopupSectioning(SectioningDelete); });
0781         popup.addSeparator();
0782         popup.addAction(i18n("C&omment"), this, [this] { slotPopupSectioning(SectioningComment); });
0783         popup.addSeparator();
0784         popup.addAction(i18n("Run QuickPreview"), this, [this] { slotPopupSectioning(SectioningPreview); });
0785     }
0786     else if(m_popupItem->type() == KileStruct::Graphics) {
0787         m_popupInfo = m_popupItem->title();
0788 
0789         if(!QDir::isAbsolutePath(m_popupInfo)) {
0790             QString fn = m_ki->getCompileName();
0791             m_popupInfo = QFileInfo(fn).path() + '/' + m_popupInfo;
0792         }
0793 
0794         QFileInfo fi(m_popupInfo);
0795         if(fi.isReadable()) {
0796             QUrl url;
0797             url.setPath(m_popupInfo);
0798 
0799             QMimeDatabase db;
0800             m_offerList = KApplicationTrader::queryByMimeType(db.mimeTypeForUrl(url).name());
0801             for(int i = 0; i < m_offerList.count(); ++i) {
0802                 popup.addAction(QIcon::fromTheme(m_offerList[i]->icon()), m_offerList[i]->name(),
0803                                          this, [this, i] { slotPopupGraphics(i + SectioningGraphicsOfferlist); });
0804             }
0805             popup.addSeparator();
0806             popup.addAction(i18n("Other..."), this, [this] { slotPopupGraphics(SectioningGraphicsOther); });
0807         }
0808     }
0809 
0810     if(hasLabel) {
0811         popup.addSection(i18n("Insert Label"));
0812         popup.addAction(i18n("As &reference"), this, [this] { emit(sendText("\\ref{" + m_popupItem->label() + '}')); });
0813         popup.addAction(i18n("As &page reference"), this, [this] { emit(sendText("\\pageref{" + m_popupItem->label() + '}')); });
0814         popup.addAction(i18n("Only the &label"), this, [this] { emit(sendText(m_popupItem->label())); });
0815         popup.addSeparator();
0816         popup.addSection(i18n("Copy Label to Clipboard"));
0817         popup.addAction(i18n("As reference"), this, [this] { QApplication::clipboard()->setText("\\ref{" + m_popupItem->label() + '}'); });
0818         popup.addAction(i18n("As page reference"), this, [this] { QApplication::clipboard()->setText("\\pageref{" + m_popupItem->label() + '}'); });
0819         popup.addAction(i18n("Only the label"), this, [this] { QApplication::clipboard()->setText(m_popupItem->label()); });
0820     }
0821 
0822     if(!popup.isEmpty()) {
0823         m_showingContextMenu = &popup;
0824         popup.exec(event->globalPos());
0825         m_showingContextMenu = Q_NULLPTR;
0826     }
0827 }
0828 
0829 // id's 10..16 (already checked)
0830 void StructureWidget::slotPopupSectioning(int id)
0831 {
0832     KILE_DEBUG_MAIN << "\tStructureWidget::slotPopupSectioning (" << id << ")"<< Qt::endl;
0833     if(m_popupItem->level() >= 1 && m_popupItem->level() <= 7) {
0834         emit(sectioningPopup(m_popupItem, id));
0835     }
0836 }
0837 
0838 // id's 100ff (already checked)
0839 void StructureWidget::slotPopupGraphics(int id)
0840 {
0841     KILE_DEBUG_MAIN << "\tStructureWidget::slotPopupGraphics (" << id << ")"<< Qt::endl;
0842 
0843     QUrl url;
0844     url.setPath(m_popupInfo);
0845 
0846     if(id == SectioningGraphicsOther) {
0847         // open with dialog
0848         auto *job = new KIO::ApplicationLauncherJob();
0849         job->setUrls(QList<QUrl>() << url);
0850         job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0851         job->start();
0852     }
0853     else {
0854         auto *job = new KIO::ApplicationLauncherJob(m_offerList[id-SectioningGraphicsOfferlist]);
0855         job->setUrls(QList<QUrl>() << url);
0856         job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0857         job->start();
0858     }
0859 }
0860 
0861 StructureView* StructureWidget::viewFor(KileDocument::Info *info)
0862 {
0863     if(!info) {
0864         return Q_NULLPTR;
0865     }
0866 
0867     if(!viewExistsFor(info)) {
0868         m_map.insert(info, new StructureView(this, info));
0869     }
0870 
0871     return m_map[info];
0872 }
0873 
0874 bool StructureWidget::viewExistsFor(KileDocument::Info *info)
0875 {
0876     if(!info) {
0877         return false;
0878     }
0879     else {
0880         return m_map.contains(info);
0881     }
0882 }
0883 
0884 void StructureWidget::closeDocumentInfo(KileDocument::Info *docinfo)
0885 {
0886     m_docinfo = Q_NULLPTR;
0887     if(m_map.contains(docinfo)) {
0888         StructureView *data = m_map[docinfo];
0889         m_map.remove(docinfo);
0890         delete data;
0891     }
0892 
0893     if(m_map.isEmpty()) {
0894         m_default->activate();
0895     }
0896 }
0897 
0898 void StructureWidget::clear()
0899 {
0900     QMap<KileDocument::Info *, StructureView *>::iterator it;
0901     QMap<KileDocument::Info *, StructureView *>::iterator itend(m_map.end());
0902     for (it = m_map.begin(); it != itend; ++it) {
0903         if(it.value()) {
0904             delete it.value();
0905         }
0906     }
0907 
0908     m_map.clear();
0909     m_docinfo = Q_NULLPTR;
0910 
0911     m_default->activate();
0912 }
0913 
0914 void StructureWidget::updateUrl(KileDocument::Info *docinfo)
0915 {
0916     StructureView *view = viewFor(docinfo);
0917     if(view) {
0918         view->updateRoot();
0919     }
0920 }
0921 
0922 void StructureWidget::update(KileDocument::Info *docinfo)
0923 {
0924     update(docinfo, false);
0925 }
0926 
0927 void StructureWidget::update(KileDocument::Info *docinfo, bool forceParsing)
0928 {
0929     KILE_DEBUG_MAIN << "==KileWidget::StructureWidget::update(" << docinfo << ")=============";
0930 
0931     if(!docinfo) {
0932         m_default->activate();
0933         return;
0934     }
0935 
0936     m_docinfo = docinfo;
0937     bool needParsing = forceParsing || m_docinfo->isDirty() || !viewExistsFor(docinfo);
0938 
0939     //find structview-item for this docinfo
0940     StructureView *view = viewFor(m_docinfo);
0941     if(!view) {
0942         m_default->activate();
0943         return;
0944     }
0945 
0946     if(needParsing) { //need to reparse the doc
0947         m_docinfo->updateStruct();
0948     }
0949 
0950     KILE_DEBUG_MAIN << "activating view";
0951     view->activate();
0952 }
0953 
0954 void StructureWidget::updateAfterParsing(KileDocument::Info *info, const std::list<KileParser::StructureViewItem*>& items)
0955 {
0956     KILE_DEBUG_MAIN;
0957     StructureView *view = viewFor(info);
0958     if(!view) {
0959         m_default->activate();
0960         return;
0961     }
0962 
0963     int xtop = view->horizontalScrollBar()->value();
0964     int ytop = view->verticalScrollBar()->value();
0965     // avoid flickering when parsing
0966     view->setUpdatesEnabled(false);
0967     view->cleanUp();
0968     for(KileParser::StructureViewItem *item : items) {
0969         view->addItem(item->title, item->line, item->column, item->type, item->level, item->startline, item->startcol, item->pix, item->folder);
0970     }
0971     view->setUpdatesEnabled(true);
0972     view->showReferences(m_ki);
0973     view->horizontalScrollBar()->setValue(xtop);
0974     view->verticalScrollBar()->setValue(ytop);
0975 }
0976 
0977 void StructureWidget::clean(KileDocument::Info *docinfo)
0978 {
0979     KILE_DEBUG_MAIN << "==void StructureWidget::clean()========";
0980     StructureView *view = viewFor(docinfo);
0981     if(view) {
0982         view->cleanUp();
0983     }
0984 }
0985 
0986 void StructureWidget::updateReferences(KileDocument::Info *docinfo)
0987 {
0988     KILE_DEBUG_MAIN << "==void StructureView::updateReferences()========";
0989     StructureView *view = viewFor(docinfo);
0990     if(view) {
0991         view->showReferences(m_ki);
0992     }
0993 }
0994 
0995 ////////////////////// StructureWidget: find sectioning //////////////////////
0996 
0997 bool StructureWidget::findSectioning(StructureViewItem *refItem, KTextEditor::Document *doc, int row, int col, bool backwards, bool checkLevel, int &sectRow, int &sectCol)
0998 {
0999     KileDocument::TextInfo *docinfo = m_ki->docManager()->textInfoFor(doc);
1000     if(!docinfo) {
1001         return false;
1002     }
1003 
1004     if( checkLevel && !refItem ) { // only allow a refItem == Q_NULLPTR if checkLevel is false
1005         return false;
1006     }
1007 
1008     bool found = false;
1009     int foundRow, foundCol;
1010     StructureView *structurelist = viewFor(docinfo);
1011     QTreeWidgetItemIterator it(structurelist);
1012     while(*it) {
1013         StructureViewItem *item = dynamic_cast<StructureViewItem*>(*it);
1014         if  (item && item->type() == KileStruct::Sect && (!checkLevel || item->level() <= refItem->level())) {
1015             foundRow = item->startline() - 1;
1016             foundCol = item->startcol() - 1;
1017             if(backwards) {
1018                 if(foundRow < row || (foundRow==row && foundCol < col)) {
1019                     sectRow = foundRow;
1020                     sectCol = foundCol;
1021                     found = true;
1022                 }
1023                 else {
1024                     return found;
1025                 }
1026 
1027             }
1028             else if(!backwards && (foundRow > row || (foundRow == row && foundCol > col))) {
1029                 sectRow = foundRow;
1030                 sectCol = foundCol;
1031                 return true;
1032             }
1033         }
1034         ++it;
1035     }
1036 
1037     return found;
1038 }
1039 
1040 void StructureWidget::handleDocumentParsingStarted()
1041 {
1042     setEnabled(false);
1043     // if a context menu is showing, we better close it
1044     // as the StructureViewItems it operates on will be deleted
1045     if(m_showingContextMenu) {
1046         m_showingContextMenu->close();
1047     }
1048 }
1049 
1050 void StructureWidget::handleDocumentParsingCompleted()
1051 {
1052     setEnabled(true);
1053 }
1054 }
1055