PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/qt/semantic_engine/src/results.cpp

http://semantic-engine.googlecode.com/
C++ | 668 lines | 458 code | 160 blank | 50 comment | 51 complexity | 04460a71868e31fdcedf8bf6ff4c0ef6 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. #include "results.h"
  2. #include <iostream>
  3. #include <string>
  4. #include <vector>
  5. // defines for display
  6. #define D_RANK_BAR_WIDTH 50 // pixels
  7. #define D_RANK_BAR_HEIGHT 10 // pixels
  8. #define D_LEFT_SPACING 10 // pixels
  9. #define D_TOP_SPACING 5 // pixels
  10. #define D_RIGHT_SPACING 10 // pixels
  11. /* ***************************************************************** *
  12. * SearchResults implementation
  13. * ***************************************************************** */
  14. SearchResults::SearchResults(search<Graph> *g, QWidget *parent)
  15. : QWidget(parent), searchEngine(g)
  16. {
  17. setupLayout();
  18. setupConnections();
  19. }
  20. // for use with the clustering code
  21. QPair<WeightingTraits::edge_weight_map,
  22. WeightingTraits::vertex_weight_map> SearchResults::get_weight_map(){
  23. std::pair<WeightingTraits::edge_weight_map,
  24. WeightingTraits::vertex_weight_map> maps = searchEngine->get_weight_map();
  25. return QPair<WeightingTraits::edge_weight_map,
  26. WeightingTraits::vertex_weight_map>(maps.first, maps.second);
  27. }
  28. QStringList SearchResults::getTopTerms(){
  29. return topTerms;
  30. }
  31. void SearchResults::displayItem(const QModelIndex &index){
  32. QString title = index.data(ResultsModel::TitleRole).toString();
  33. if( QFile::exists( title ) ){
  34. QDesktopServices::openUrl(QUrl::fromLocalFile(title));
  35. } else {
  36. /* emit */ displaySingleResultWindow(title);
  37. }
  38. }
  39. void SearchResults::setupLayout(){
  40. // display the related terms
  41. related_label = new QLabel();
  42. related_terms = new QLabel();
  43. related_terms->setAlignment(Qt::AlignLeft);
  44. related_terms->setWordWrap(true);
  45. QGridLayout *related = new QGridLayout();
  46. related->addWidget(related_label,0,0);
  47. related->addWidget(related_terms,0,1);
  48. related->setColumnStretch(0,0);
  49. related->setColumnStretch(1,1);
  50. // display the result list
  51. search_result_list = new ResultsView();
  52. search_result_list->setDragEnabled(true);
  53. search_result_list->setAlternatingRowColors(true);
  54. search_result_list->setSelectionMode(QAbstractItemView::ExtendedSelection);
  55. search_result_list->setModel(new ResultsModel(searchEngine, resultList));
  56. searchStatus = new SearchStatus();
  57. searchStatus->setAlignment(Qt::AlignCenter);
  58. searchStatus->setVisible(false);
  59. QVBoxLayout *layout = new QVBoxLayout();
  60. layout->setAlignment(Qt::AlignCenter);
  61. layout->addLayout(related);
  62. layout->addWidget(search_result_list);
  63. layout->addWidget(searchStatus);
  64. setLayout(layout);
  65. }
  66. void SearchResults::setupConnections(){
  67. connect(search_result_list,SIGNAL(doubleClicked(const QModelIndex &)),this,SLOT(displayItem(const QModelIndex &)));
  68. // connect(related_terms,SIGNAL(linkActivated(related_terms->text())),this,SLOT(doSemanticSearch(related_terms->text())));
  69. }
  70. void SearchResults::searchStarted(){
  71. resultList.clear();
  72. topTerms.clear();
  73. search_result_list->setVisible(false);
  74. searchStatus->setStatus("Searching ...");
  75. searchStatus->setVisible(true);
  76. related_label->clear();
  77. related_terms->clear();
  78. }
  79. void SearchResults::searchCompleted(){
  80. topTerms = m_searchThread->getRelatedTerms();
  81. if( topTerms.count() ){
  82. QFont relatedFont("Lucida Grande");
  83. relatedFont.setBold(true);
  84. relatedFont.setPixelSize(12);
  85. related_label->setFont(relatedFont);
  86. related_label->setText(tr("Related Terms: "));
  87. related_terms->setText(topTerms.join(", "));
  88. }
  89. resultList = resultList = m_searchThread->getSearchResults();
  90. if( resultList.count() ){
  91. ResultsModel *model = new ResultsModel(searchEngine, resultList);
  92. search_result_list->setModel(model);
  93. searchStatus->setVisible(false);
  94. search_result_list->setVisible(true);
  95. } else {
  96. searchStatus->setStatus("No Results");
  97. // search_result_list->setModel(new ResultsModel(searchEngine, resultList));
  98. }
  99. searchFinished();
  100. }
  101. QList<QPair<QString,double> > SearchResults::getSearchResults(){
  102. return resultList;
  103. }
  104. void SearchResults::startSearch(QStringList query, searchType type=SemanticSearch) {
  105. m_searchThread = new SearchThread(searchEngine, query, type);
  106. connect(m_searchThread, SIGNAL(startedSearching()), this, SLOT(searchStarted()));
  107. connect(m_searchThread, SIGNAL(finishedSearching()), this, SLOT(searchCompleted()));
  108. m_searchThread->start();
  109. }
  110. /* ******************************************************************** *
  111. * Search Thread Implementation
  112. * ******************************************************************** */
  113. SearchThread::SearchThread(search<Graph> *s, QStringList query, searchType type, QObject *parent)
  114. : QThread(parent), searchEngine(s)
  115. {
  116. m_query = query;
  117. m_type = type;
  118. }
  119. QList<QPair<QString,double> > SearchThread::getSearchResults(){
  120. return m_resultList;
  121. }
  122. QStringList SearchThread::getRelatedTerms(){
  123. return m_topTerms;
  124. }
  125. void SearchThread::run() {
  126. startedSearching();
  127. sorted_results docs, terms;
  128. m_resultList.clear();
  129. m_topTerms.clear();
  130. if( m_query.size() > 0 ){
  131. // add threading here!
  132. docs_and_terms results;
  133. switch( m_type ){
  134. case SemanticSearch:
  135. results = searchEngine->semantic(m_query.at(0).toStdString());
  136. // results = searchEngine->do_better_search(m_query.at(0).toStdString());
  137. break;
  138. case KeywordSearch:
  139. results = searchEngine->keyword(m_query.at(0).toStdString());
  140. break;
  141. case SimilarSearch:
  142. std::vector<std::string> documents;
  143. for( int i=0;i<m_query.size(); ++i){
  144. documents.push_back( m_query.at(i).toStdString());
  145. }
  146. results = searchEngine->similar(documents.begin(), documents.end());
  147. break;
  148. }
  149. docs = results.first;
  150. terms = results.second;
  151. double min = 0;
  152. double range = 1;
  153. int resultCount = docs.size();
  154. if( resultCount > 1 ){
  155. range = docs[0].second - docs[resultCount-1].second;
  156. min = docs[resultCount-1].second;
  157. }
  158. for( unsigned i = 0; i < docs.size(); ++i){
  159. QString title = QString::fromStdString(docs[i].first);
  160. double normalizedWeight = (docs[i].second - min ) / range;
  161. m_resultList.push_back(QPair<QString,double>(title,normalizedWeight));
  162. }
  163. for( unsigned i = 0; i < 10 && i < terms.size(); i++){
  164. m_topTerms << QString::fromStdString(terms[i].first);
  165. }
  166. }
  167. finishedSearching();
  168. }
  169. /* ***************************************************************** *
  170. * ResultsModel implementation
  171. * ***************************************************************** */
  172. ResultsModel::ResultsModel(search<Graph> *s, QList<QPair<QString,double> > &resultList, QObject *parent)
  173. : QAbstractListModel(parent), m_searchEngine(s)
  174. {
  175. m_resultList = resultList;
  176. m_summaryList.clear();
  177. }
  178. int ResultsModel::rowCount(const QModelIndex &) const
  179. {
  180. return m_resultList.size();
  181. }
  182. /*
  183. double ResultsModel::max() const
  184. {
  185. int size = m_resultList.size();
  186. if( size > 0 ){
  187. return m_resultList[0].second;
  188. } else {
  189. return 0;
  190. }
  191. }
  192. double ResultsModel::min() const
  193. {
  194. int size = m_resultList.size();
  195. if( size > 0 ){
  196. return m_resultList[size-1].second;
  197. } else {
  198. return 0;
  199. }
  200. }
  201. */
  202. QVariant ResultsModel::data(const QModelIndex &index, int role) const
  203. {
  204. if( !index.isValid() || index.row() >= m_resultList.size())
  205. return QVariant();
  206. QPair<QString,double> singleResult = m_resultList[index.row()];
  207. QString title = singleResult.first;
  208. switch(role) {
  209. case TitleRole:
  210. return QString(title);
  211. break;
  212. case DetailRole:
  213. if(!m_summaryList.count(index.row())) {
  214. std::string summary;
  215. try {
  216. summary = m_searchEngine->summarize_document(title.toStdString(),2);
  217. } catch (...){
  218. std::cerr << "Error getting summary from: " << title.toStdString() << std::endl;
  219. }
  220. m_summaryList.insert(index.row(),QString::fromStdString(summary));
  221. }
  222. return QString(m_summaryList[index.row()]);
  223. break;
  224. case IdRole:
  225. return 101;
  226. break;
  227. case RankRole:
  228. return singleResult.second;
  229. break;
  230. case OriginalTitleRole:
  231. return QString(singleResult.first);
  232. break;
  233. default:
  234. return QVariant();
  235. }
  236. return QVariant();
  237. }
  238. QMap<int, QVariant>
  239. ResultsModel::itemData(const QModelIndex &index) const
  240. {
  241. int row = index.row();
  242. int col = index.column();
  243. if (! index.isValid() || col != 0 || row < 0 || row > rowCount())
  244. return QMap<int, QVariant>();
  245. QMap<int, QVariant> data_map = QAbstractListModel::itemData(index);
  246. data_map.insert(TitleRole, QString("Vertex Content"));
  247. data_map.insert(DetailRole, QString("Summary"));
  248. data_map.insert(IdRole, 101);
  249. data_map.insert(RankRole, row);
  250. data_map.insert(OriginalTitleRole, QString("Original Title"));
  251. return data_map;
  252. }
  253. /* ********************************************************************** *
  254. * ResultsView implementation
  255. * ********************************************************************** */
  256. ResultsView::ResultsView(QWidget *parent)
  257. : QAbstractItemView(parent)
  258. {
  259. setDragDropMode(QAbstractItemView::DragOnly);
  260. setDragEnabled(true);
  261. setDropIndicatorShown(true);
  262. };
  263. QRect ResultsView::visualRect(const QModelIndex &index) const
  264. {
  265. QRect rect = QRect(0, rowHeight() * index.row(), width(), rowHeight());
  266. if (rect.isValid())
  267. return QRect(rect.left() - horizontalScrollBar()->value(),
  268. rect.top() - verticalScrollBar()->value(),
  269. rect.width(), rect.height());
  270. else
  271. return rect;
  272. }
  273. int ResultsView::rowHeight() const {
  274. #ifdef WIN32
  275. return 60;
  276. #else
  277. return 50;
  278. #endif
  279. }
  280. void ResultsView::scrollTo(const QModelIndex &index, ScrollHint)
  281. {
  282. QRect area = viewport()->rect();
  283. QRect rect = visualRect(index);
  284. if (rect.left() < area.left())
  285. horizontalScrollBar()->setValue(
  286. horizontalScrollBar()->value() + rect.left() - area.left());
  287. else if (rect.right() > area.right())
  288. horizontalScrollBar()->setValue(
  289. horizontalScrollBar()->value() + qMin(
  290. rect.right() - area.right(), rect.left() - area.left()));
  291. if (rect.top() < area.top())
  292. verticalScrollBar()->setValue(
  293. verticalScrollBar()->value() + rect.top() - area.top());
  294. else if (rect.bottom() > area.bottom())
  295. verticalScrollBar()->setValue(
  296. verticalScrollBar()->value() + qMin(
  297. rect.bottom() - area.bottom(), rect.top() - area.top()));
  298. update();
  299. }
  300. QModelIndex ResultsView::moveCursor(QAbstractItemView::CursorAction action, Qt::KeyboardModifiers /*modifiers*/)
  301. {
  302. QModelIndex index = currentIndex();
  303. switch(action) {
  304. case MoveLeft:
  305. case MoveUp:
  306. if (index.row() > 0)
  307. index = model()->index(index.row() - 1, index.column(), rootIndex());
  308. else
  309. index = model()->index(0, index.column(), rootIndex());
  310. break;
  311. case MoveRight:
  312. case MoveDown:
  313. if (index.row() < rows(index) - 1)
  314. index = model()->index(index.row() + 1, index.column(), rootIndex());
  315. else
  316. index = model()->index(rows(index) - 1, index.column(), rootIndex());
  317. break;
  318. default:
  319. break;
  320. }
  321. viewport()->update();
  322. return index;
  323. }
  324. int ResultsView::rows(const QModelIndex &index) const
  325. {
  326. return model()->rowCount(model()->parent(index));
  327. }
  328. int ResultsView::horizontalOffset() const {
  329. return horizontalScrollBar()->value();
  330. }
  331. int ResultsView::verticalOffset() const {
  332. return verticalScrollBar()->value();
  333. }
  334. bool ResultsView::isIndexHidden(const QModelIndex &index) const {
  335. int row = index.row();
  336. // if we're off the top
  337. if ((row + 1) * rowHeight() - verticalOffset() < 0) return true;
  338. if (row * rowHeight() - verticalOffset() > height()) return true;
  339. return false;
  340. }
  341. void ResultsView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) {
  342. QRect contentsRect = rect.translated(horizontalScrollBar()->value(),
  343. verticalScrollBar()->value()).normalized();
  344. int rows = model()->rowCount(rootIndex());
  345. int columns = model()->columnCount(rootIndex());
  346. QModelIndexList indexes;
  347. for (int row = 0; row < rows; ++row) {
  348. for (int column = 0; column < columns; ++column) {
  349. QModelIndex index = model()->index(row, column, rootIndex());
  350. QRegion region = itemRegion(index);
  351. if (!region.intersect(contentsRect).isEmpty())
  352. indexes.append(index);
  353. }
  354. }
  355. if (indexes.size() > 0) {
  356. int firstRow = indexes[0].row();
  357. int lastRow = indexes[0].row();
  358. int firstColumn = indexes[0].column();
  359. int lastColumn = indexes[0].column();
  360. for (int i = 1; i < indexes.size(); ++i) {
  361. firstRow = qMin(firstRow, indexes[i].row());
  362. lastRow = qMax(lastRow, indexes[i].row());
  363. firstColumn = qMin(firstColumn, indexes[i].column());
  364. lastColumn = qMax(lastColumn, indexes[i].column());
  365. }
  366. QItemSelection selection(
  367. model()->index(firstRow, firstColumn, rootIndex()),
  368. model()->index(lastRow, lastColumn, rootIndex()));
  369. selectionModel()->select(selection, command);
  370. } else {
  371. QModelIndex noIndex;
  372. QItemSelection selection(noIndex, noIndex);
  373. selectionModel()->select(selection, command);
  374. }
  375. m_selectionRect = rect;
  376. update();
  377. }
  378. QRegion ResultsView::itemRegion(const QModelIndex &index) const {
  379. if (!index.isValid()) return QRegion();
  380. return QRegion(0, rowHeight()*index.row(), width(), rowHeight());
  381. }
  382. QRegion ResultsView::visualRegionForSelection(const QItemSelection &selection) const
  383. {
  384. int ranges = selection.count();
  385. if (ranges == 0)
  386. return QRect();
  387. // Note that we use the top and bottom functions of the selection range
  388. // since the data is stored in rows.
  389. int firstRow = selection.at(0).top();
  390. int lastRow = selection.at(0).bottom();
  391. for (int i = 0; i < ranges; ++i) {
  392. firstRow = qMin(firstRow, selection.at(i).top());
  393. lastRow = qMax(lastRow, selection.at(i).bottom());
  394. }
  395. QModelIndex firstItem = model()->index(qMin(firstRow, lastRow), 0, rootIndex());
  396. QModelIndex lastItem = model()->index(qMax(firstRow, lastRow), 0, rootIndex());
  397. QRect firstRect = visualRect(firstItem);
  398. QRect lastRect = visualRect(lastItem);
  399. return firstRect.unite(lastRect);
  400. }
  401. QModelIndex ResultsView::indexAt(const QPoint &point) const {
  402. if (model()->rowCount() == 0) return QModelIndex();
  403. // int wx = point.x() + horizontalScrollBar()->value();
  404. int wy = point.y() + verticalOffset();
  405. int w = wy/rowHeight(); // we want this floored, which conversion to "int" will do
  406. return model()->index(w, 0, rootIndex());
  407. }
  408. QRect ResultsView::itemRect(const QModelIndex &index) const {
  409. if (!index.isValid()) return QRect();
  410. return QRect(0, rowHeight()*index.row(), width(), rowHeight());
  411. }
  412. void ResultsView::paintEvent(QPaintEvent *event) {
  413. QItemSelectionModel *selections = selectionModel();
  414. QStyleOptionViewItem option = viewOptions();
  415. QStyle::State state = option.state;
  416. QBrush background = option.palette.base();
  417. QPen foreground(option.palette.color(QPalette::Foreground));
  418. QPen textPen(option.palette.color(QPalette::Text));
  419. QColor highlightedColor = option.palette.color(QPalette::HighlightedText);
  420. QPen highlightedPen(highlightedColor);
  421. QPen collectionPen(QColor(100,100,100));
  422. QColor highlightBackground = option.palette.color(QPalette::Highlight);
  423. QColor rankBarColor(200, 70, 70);
  424. QFont font("Verdana");
  425. font.setPixelSize(12);
  426. QFont small_font("Verdana");
  427. small_font.setPixelSize(9);
  428. QFontMetrics metrics(font);
  429. QFontMetrics small_metrics(small_font);
  430. QPainter painter(viewport());
  431. painter.setRenderHint(QPainter::Antialiasing);
  432. painter.setFont(font);
  433. painter.fillRect(event->rect(), background);
  434. painter.setPen(foreground);
  435. if (model()->rowCount() > 0) {
  436. painter.save();
  437. painter.translate(-1 * horizontalScrollBar()->value(), -1 * verticalScrollBar()->value());
  438. // go through each result item and draw it, if it's visible
  439. for(int i = 0; i < model()->rowCount(); i++) {
  440. QModelIndex index = model()->index(i, 0, rootIndex());
  441. if (!isIndexHidden(index)) {
  442. bool isH = (/*currentIndex()==index||*/selections->isSelected(index))?true:false;
  443. // the background color
  444. if (isH) {painter.setPen(highlightedPen); painter.fillRect(itemRect(index), highlightBackground); }
  445. else painter.setPen(foreground);
  446. // the title
  447. QString titleString = QVariant(i+1).toString() + ". " +
  448. model()->data(index, ResultsModel::TitleRole).toString();
  449. QString elidedTitle = metrics.elidedText(titleString, Qt::ElideMiddle, viewport()->width() - D_LEFT_SPACING - D_RIGHT_SPACING - D_RANK_BAR_WIDTH - 10 );
  450. QRect titleRect = metrics.boundingRect(elidedTitle);
  451. painter.drawText(D_LEFT_SPACING, i*rowHeight() + titleRect.height() + D_TOP_SPACING - 1, elidedTitle);
  452. // the rank bar
  453. double rank = model()->data(index, ResultsModel::RankRole).toDouble();
  454. int width = int(rank * D_RANK_BAR_WIDTH);
  455. if (width < 1) width = 1;
  456. if( width > D_RANK_BAR_WIDTH * 1)
  457. width = D_RANK_BAR_WIDTH;
  458. QRect barRect(viewport()->width() - D_RANK_BAR_WIDTH - D_RIGHT_SPACING, D_TOP_SPACING + i*rowHeight(), width, D_RANK_BAR_HEIGHT);
  459. painter.fillRect(barRect, isH?highlightedColor:rankBarColor);
  460. // the summary -- just below the title
  461. painter.save();
  462. painter.setFont(small_font);
  463. isH?painter.setPen(Qt::lightGray):painter.setPen(Qt::darkGray);
  464. QString summaryString = model()->data(index, ResultsModel::DetailRole).toString();
  465. summaryString.replace(QRegExp("\\s+"), " ");
  466. QString elidedSummary = small_metrics.elidedText(summaryString, Qt::ElideRight, (viewport()->width() - D_LEFT_SPACING - D_RIGHT_SPACING - D_RANK_BAR_WIDTH - 20)*2 );
  467. painter.drawText( D_LEFT_SPACING + 10,
  468. i*rowHeight() + titleRect.height() + D_TOP_SPACING + 4,
  469. viewport()->width() - D_LEFT_SPACING - D_RIGHT_SPACING - D_RANK_BAR_WIDTH - 10,
  470. small_metrics.boundingRect(summaryString).height()*2 + 2,
  471. Qt::TextWordWrap, elidedSummary);
  472. painter.restore();
  473. }
  474. }
  475. painter.restore();
  476. }
  477. }
  478. void ResultsView::resizeEvent(QResizeEvent * /* event */) {
  479. updateGeometries();
  480. }
  481. void ResultsView::scrollContentsBy(int dx, int dy)
  482. {
  483. viewport()->scroll(dx, dy);
  484. }
  485. void ResultsView::updateGeometries()
  486. {
  487. horizontalScrollBar()->setPageStep(viewport()->width());
  488. horizontalScrollBar()->setRange(0, 0);
  489. verticalScrollBar()->setPageStep(viewport()->height());
  490. verticalScrollBar()->setRange(0, qMax(0, model()->rowCount()*rowHeight() - viewport()->height()));
  491. }
  492. void ResultsView::dragEnterEvent(QDragEnterEvent *event) {
  493. event->setDropAction(Qt::CopyAction);
  494. event->accept();
  495. }
  496. void ResultsView::mouseMoveEvent(QMouseEvent *){
  497. QItemSelectionModel *selections = this->selectionModel();
  498. QModelIndexList list = selections->selectedIndexes();
  499. for( int i = 0; i < list.size(); ++i){
  500. QDrag *drag = new QDrag(this);
  501. QModelIndex index = list.at(i);
  502. QMimeData *mimeData = new QMimeData;
  503. mimeData->setHtml(index.data(Qt::DisplayRole).toString());
  504. drag->setMimeData(mimeData);
  505. drag->start();
  506. }
  507. }