📄 queryview.cpp
字号:
/*************************************************************************** * Copyright (C) 2007-2009 by Elad Lahav * elad_lahav@users.sourceforge.net * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ***************************************************************************/#include "queryview.h"#include "locationlistmodel.h"#include "locationtreemodel.h"#include "exception.h"namespace KScope{namespace Core{/** * Class constructor. * @param parent The parent widget * @param type Whether the view works in list or tree modes */QueryView::QueryView(QWidget* parent, Type type) : QTreeView(parent), type_(type), progBar_(NULL), autoSelectSingleResult_(false){ // Set tree view properties. setRootIsDecorated(type_ == Tree); setUniformRowHeights(true); setExpandsOnDoubleClick(false); // Create a location model. switch (type_) { case List: setModel(new LocationListModel(this)); break; case Tree: setModel(new LocationTreeModel(this)); break; } // Emit requests for locations when an item is double-clicked. connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(requestLocation(const QModelIndex&))); // Query child items when expanded (in a tree view). if (type_ == Tree) { connect(this, SIGNAL(expanded(const QModelIndex&)), this, SLOT(queryTreeItem(const QModelIndex&))); }}/** * Class destructor. */QueryView::~QueryView(){}/** * @param query The query to run */void QueryView::query(const Query& query){ // Reset the model. model()->clear(); try { // Get an engine for running the query. Engine* eng; if ((eng = engine()) != NULL) { // Run the query. query_ = query; model()->setColumns(eng->queryFields(query_.type_)); eng->query(this, query_); } } catch (Exception* e) { e->showMessage(); delete e; }}/** * Adjusts all columns to fit the their contents. */void QueryView::resizeColumns(){ for (int i = 0; i < model()->columnCount(); i++) resizeColumnToContents(i);}/** * Creates an XML representation of the view, which can be used for storing the * model's data in a file. * The representation is rooted at a <QueryView> element, which holds a <Query> * element for the query information, a <Columns> element with a list of * columns, and a <LocationList> element that describes the list or tree of * locations. * @param doc The XML document object to use * @return The root element of the view's representation */QDomElement QueryView::toXML(QDomDocument& doc) const{ // Create an element for storing the view. QDomElement viewElem = doc.createElement("QueryView"); viewElem.setAttribute("name", windowTitle()); viewElem.setAttribute("type", QString::number(type_)); // Store query information. QDomElement queryElem = doc.createElement("Query"); queryElem.setAttribute("type", QString::number(query_.type_)); queryElem.setAttribute("flags", QString::number(query_.flags_)); queryElem.appendChild(doc.createCDATASection(query_.pattern_)); viewElem.appendChild(queryElem); // Create a "Columns" element. QDomElement colsElem = doc.createElement("Columns"); viewElem.appendChild(colsElem); // Add an element for each column. foreach (Location::Fields field, model()->columns()) { QDomElement colElem = doc.createElement("Column"); colElem.setAttribute("field", QString::number(field)); colsElem.appendChild(colElem); } // Add locations. locationToXML(doc, viewElem, QModelIndex()); return viewElem;}/** * Loads a query view from an XML representation. * @param root The root element for the query's XML representation */void QueryView::fromXML(const QDomElement& viewElem){ // Get query information. QDomElement queryElem = viewElem.elementsByTagName("Query").at(0).toElement(); if (queryElem.isNull()) return; query_.type_ = static_cast<Core::Query::Type> (queryElem.attribute("type").toUInt()); query_.flags_ = queryElem.attribute("flags").toUInt(); query_.pattern_ = queryElem.childNodes().at(0).toCDATASection().data(); // Reset the model. model()->clear(); // TODO: Is there a guarantee of order? QDomNodeList columnNodes = viewElem.elementsByTagName("Column"); QList<Location::Fields> colList; for (int i = 0; i < columnNodes.size(); i++) { QDomElement elem = columnNodes.at(i).toElement(); if (elem.isNull()) continue; colList.append(static_cast<Location::Fields> (elem.attribute("field").toUInt())); } model()->setColumns(colList); // Find the <LocationList> element that is a child of the root element. QDomNodeList childNodes = viewElem.childNodes(); for (int i = 0; i < childNodes.size(); i++) { QDomElement elem = childNodes.at(i).toElement(); if (elem.isNull() || elem.tagName() != "LocationList") continue; // Load locations. locationFromXML(elem, QModelIndex()); }#ifndef QT_NO_DEBUG model()->verify();#endif}/** * Called by the engine when results are available. * Adds the list of locations to the model. * @param locList Query results */void QueryView::onDataReady(const LocationList& locList){ model()->add(locList, queryIndex_);}/** * Displays progress information in a progress-bar at the top of the view. * @param text Progress message * @param cur Current value * @param total Expected final value */void QueryView::onProgress(const QString& text, uint cur, uint total){ // Create the progress-bar widget, if it does not exist. if (!progBar_) { progBar_ = new ProgressBar(this); connect(progBar_, SIGNAL(cancelled()), this, SLOT(stopQuery())); progBar_->show(); } // Update progress information in the progress bar. progBar_->setLabel(text); progBar_->setProgress(cur, total); if (!isVisible()) emit needToShow();}/** * Called by the engine when a query terminates normally. */void QueryView::onFinished(){ // Handle an empty result set. if (model()->rowCount(queryIndex_) == 0) model()->add(LocationList(), queryIndex_); // Destroy the progress-bar, if it exists. if (progBar_) { delete progBar_; progBar_ = NULL; } // Adjust column sizes. resizeColumns(); // Auto-select a single result, if required. Location loc; if (autoSelectSingleResult_ && model()->rowCount(queryIndex_) == 1 && model()->firstLocation(loc)) { emit locationRequested(loc); } else if (!isVisible()) { emit needToShow(); }}/** * Called by the engine when a query terminates abnormally. */void QueryView::onAborted(){ // Destroy the progress-bar, if it exists. if (progBar_) { delete progBar_; progBar_ = NULL; }}/** * Selects the next location in the list. */void QueryView::selectNext(){ QModelIndex selIndex = model()->nextIndex(currentIndex()); if (selIndex.isValid()) setCurrentIndex(selIndex); Location loc; if (model()->locationFromIndex(selIndex, loc)) emit locationRequested(loc);}/** * Selects the previous location in the list. */void QueryView::selectPrev(){ QModelIndex selIndex = model()->prevIndex(currentIndex()); if (selIndex.isValid()) setCurrentIndex(selIndex); Location loc; if (model()->locationFromIndex(selIndex, loc)) emit locationRequested(loc);}/** * Recursively transforms the location hierarchy stored in the model to an XML * sub-tree. * The XML representation of locations is composed of a <LocationList> element * holding a list of <Location> elements. For a tree model, <Location> elements * may in turn hold a <LocationList> sub-element, and so on. * @param doc The XML document object to use * @param parentElem XML element under which new location elements should be * created * @param idx The index to store (along with its children) */void QueryView::locationToXML(QDomDocument& doc, QDomElement& parentElem, const QModelIndex& idx) const{ QDomElement elem; if (idx.isValid()) { // A non-root index. // Translate the index into a location information structure. Location loc; if (!model()->locationFromIndex(idx, loc)) return; // Create an XML element for the location. elem = doc.createElement("Location"); parentElem.appendChild(elem); // Add a text node for each structure member. const QList<Location::Fields>& colList = model()->columns(); foreach (Location::Fields field, colList) { QString name; QDomNode node; switch (field) { case Location::File: name = "File"; node = doc.createTextNode(loc.file_); break; case Location::Line: name = "Line"; node = doc.createTextNode(QString::number(loc.line_)); break; case Location::Column: name = "Column"; node = doc.createTextNode(QString::number(loc.column_)); break; case Location::TagName: name = "TagName"; node = doc.createTextNode(loc.tag_.name_); break; case Location::TagType: name = "TagType"; node = doc.createTextNode(QString::number(loc.tag_.type_)); break; case Location::Scope: name = "Scope"; node = doc.createTextNode(loc.tag_.scope_); break; case Location::Text: name = "Text"; node = doc.createCDATASection(loc.text_); break; } QDomElement child = doc.createElement(name); child.appendChild(node); elem.appendChild(child); } } else { // For the root index, use the given parent element as the parent of // the location list created by top-level items. elem = parentElem; } // Create an element list using the index's children. // A <LocationList> element is created for queried items (whether there // were any results or not). An element is not created for non-queried // items, so that locationFromXML() does not call add() for such items, // correctly restoring their status. if (model()->isEmpty(idx) != LocationModel::Unknown) { // Create the element. QDomElement locListElem = doc.createElement("LocationList"); locListElem.setAttribute("expanded", isExpanded(idx) ? "1" : "0"); elem.appendChild(locListElem); // Add child locations. for (int i = 0; i < model()->rowCount(idx); i++) locationToXML(doc, locListElem, model()->index(i, 0, idx)); }}/** * Loads a hierarchy of locations from an XML document into the model. * See locationToXML() for the XML format. * @param locListElem A <LocationList> XML element * @param parentIndex The model index under which locations should be added */void QueryView::locationFromXML(const QDomElement& locListElem, const QModelIndex& parentIndex){ // Get a list of location elements. QDomNodeList nodes = locListElem.childNodes(); // Translate elements into a list of location objects. // The list is used to store sub-lists encountered inside the location // element. These will be loaded later. It's better to first construct the // list of locations for the current level, rather than follow the XML // tree depth-first, due to the behaviour of the add() method. LocationList locList; QList< QPair<int, QDomElement> > childLists; for (int i = 0; i < nodes.size(); i++) { // Get the current location element. QDomElement elem = nodes.at(i).toElement(); if (elem.isNull() || elem.tagName() != "Location") continue; // Iterate over the sub-elements, which represent either location // properties, or nested location lists. We expect at most one of the // latter. Location loc; QDomNodeList childNodes = elem.childNodes(); for (int j = 0; j < childNodes.size(); j++) { // Has to be an element. QDomElement child = childNodes.at(j).toElement(); if (child.isNull()) continue; // Extract location data from the element. if (child.tagName() == "File") loc.file_ = child.text(); else if (child.tagName() == "Line") loc.line_ = child.text().toUInt(); else if (child.tagName() == "Column") loc.column_ = child.text().toUInt(); else if (child.tagName() == "TagName") loc.tag_.name_ = child.text(); else if (child.tagName() == "TagType") loc.tag_.type_ = static_cast<Tag::Type>(child.text().toUInt()); else if (child.tagName() == "Scope") loc.tag_.scope_ = child.text(); else if (child.tagName() == "Text") loc.text_ = child.firstChild().toCDATASection().data(); else if (child.tagName() == "LocationList") childLists.append(QPair<int, QDomElement>(i, child)); } // Add to the location list. locList.append(loc); } // Store locations in the model. model()->add(locList, parentIndex); // Load any sub-lists encountered earlier. QList< QPair<int, QDomElement> >::Iterator itr; for (itr = childLists.begin(); itr != childLists.end(); ++itr) { locationFromXML((*itr).second, model()->index((*itr).first, 0, parentIndex)); } // Expand the item if required. if (locListElem.attribute("expanded").toUInt()) expand(parentIndex);}/** * Called when the user double-clicks a location item in the list. * Emits the locationRequested() signal for this location. * @param index Identifies the clicked item */void QueryView::requestLocation(const QModelIndex& index){ Location loc; if (model()->locationFromIndex(index, loc)) emit locationRequested(loc);}/** * Called when the "Cancel" button is clicked in the progress-bar. * Informs the engine that the query process should be stopped. */void QueryView::stopQuery(){ stop();}/** * Called when a tree item is expanded. * If this item was not queried before, a query is performed. * @param idx The expanded item */void QueryView::queryTreeItem(const QModelIndex& idx){ // Do not query if the item already has children. // An item has no children if it was not yet queried, or if the query // returned no results. The assumption is that the "expand" button is // removed in the latter case, so that this method is called for a // child-less item only if it was never queried. if (model()->rowCount(idx) > 0) return; // Get the location information from the index. Location loc; if (!model()->locationFromIndex(idx, loc)) return; // Run a query on this location. try { Engine* eng; if ((eng = engine()) != NULL) { queryIndex_ = idx; eng->query(this, Query(query_.type_, loc.tag_.scope_)); } } catch (Exception* e) { e->showMessage(); delete e; }}} // namespace Core} // namespace KScope
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -