/********* * * This file is part of BibleTime's source code, http://www.bibletime.info/. * * Copyright 1999-2006 by the BibleTime developers. * The BibleTime source code is licensed under the GNU General Public License version 2.0. * **********/ #include "csearchanalysis.h" #include "csearchdialog.h" #include "backend/cswordkey.h" #include "backend/cswordversekey.h" #include "frontend/cbtconfig.h" #include "util/cresmgr.h" #include "util/ctoolclass.h" //TQt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include //KDE includes #include #include #include #include namespace Search { namespace Analysis { const int SPACE_BETWEEN_PARTS = 5; const int RIGHT_BORDER = 15; const int LEFT_BORDER = 15; const int LOWER_BORDER = 10; const int UPPER_BORDER = 10; const int ITEM_TEXT_SIZE = 8; const int LABEL_TEXT_SIZE = 6; //used for the shift between the bars const int BAR_DELTAX = 4; const int BAR_DELTAY = 2; const int BAR_WIDTH = 2 + (2*BAR_DELTAX); //should be equal or bigger than the label font size // Used for the text below the bars const int BAR_LOWER_BORDER = 100; const int LEGEND_INNER_BORDER = 5; const int LEGEND_DELTAY = 4; const int LEGEND_WIDTH = 85; /****************************/ CSearchAnalysisDialog::CSearchAnalysisDialog( ListCSwordModuleInfo modules, TQWidget* parentDialog ) : KDialogBase(Plain, i18n("Search analysis"), Close, Close, parentDialog, 0, true) { initView(); initConnections(); m_analysis->reset(); m_analysis->analyse(modules); showMaximized(); }; CSearchAnalysisDialog::~CSearchAnalysisDialog() {} ; /** Initializes this dialog. */ void CSearchAnalysisDialog::initView() { TQVBoxLayout* layout = new TQVBoxLayout(plainPage(),0); TQPushButton* button = new TQPushButton(plainPage(), "button"); button->setIconSet(SmallIconSet("filesave")); button->setText(i18n("Save search analysis as HTML")); button->setFixedSize(button->sizeHint()); layout->addWidget(button); layout->addSpacing(10); m_analysis = new CSearchAnalysis(TQT_TQOBJECT(plainPage())); m_analysisView = new CSearchAnalysisView(m_analysis, plainPage()); m_analysisView->show(); layout->addWidget(m_analysisView); connect(button, TQT_SIGNAL(clicked()), m_analysis, TQT_SLOT(saveAsHTML())); } /** Initializes the widgets TQT_SIGNAL and TQT_SLOT connections,. */ void CSearchAnalysisDialog::initConnections() {} /****************************/ /* CSearchAnalysis */ /****************************/ CSearchAnalysis::CSearchAnalysis(TQObject *parent, const char *name ) : TQCanvas(parent,name) { m_scaleFactor = 0.0; m_legend = 0; setBackgroundColor(TQt::white); m_canvasItemList.resize(67); m_canvasItemList.setAutoDelete(true); resize(1,1); connect(this, TQT_SIGNAL(resized()), TQT_SLOT(slotResized())); } CSearchAnalysis::~CSearchAnalysis() {} TQDict* CSearchAnalysis::getSearchAnalysisItemList() { // Returns pointer to the search analysis items return &m_canvasItemList; } /** Starts the analysis of the search result. This should be called only once because TQCanvas handles the updates automatically. */ void CSearchAnalysis::analyse(ListCSwordModuleInfo modules) { /** * Steps of analysing our search result; * -Create the items for all available books ("Genesis" - "Revelation") * -Iterate through all modules we analyse * -Go through all books of this module * -Find out how many times we found the book * -Set the count to the items which belongs to the book */ setModules(modules); m_lastPosList.clear(); const int numberOfModules = m_moduleList.count(); if (!numberOfModules) return; m_legend = new CSearchAnalysisLegendItem(this, &m_moduleList); m_legend->setX(LEFT_BORDER); m_legend->setY(UPPER_BORDER); m_legend->setSize(LEGEND_WIDTH, LEGEND_INNER_BORDER*2 + ITEM_TEXT_SIZE*numberOfModules + LEGEND_DELTAY*(numberOfModules-1)); m_legend->show(); int xPos = LEFT_BORDER + m_legend->width() + SPACE_BETWEEN_PARTS; int moduleIndex = 0; m_maxCount = 0; int count = 0; CSwordVerseKey key(0); key.key("Genesis 1:1"); CSearchAnalysisItem* analysisItem = m_canvasItemList[key.book()]; bool ok = true; while (ok && analysisItem) { // for (moduleIndex = 0,m_moduleList.first(); m_moduleList.current(); m_moduleList.next(),++moduleIndex) { moduleIndex = 0; ListCSwordModuleInfo::iterator end_it = m_moduleList.end(); for (ListCSwordModuleInfo::iterator it(m_moduleList.begin()); it != end_it; ++it) { TDEApplication::kApplication()->processEvents( 10 ); //10 ms only if (!m_lastPosList.contains(*it)) { m_lastPosList.insert(*it,0); } analysisItem->setCountForModule(moduleIndex, (count = getCount(key.book(), *it))); m_maxCount = (count > m_maxCount) ? count : m_maxCount; ++moduleIndex; } analysisItem->setX(xPos); analysisItem->setY(UPPER_BORDER); analysisItem->show(); xPos += (int)analysisItem->width() + SPACE_BETWEEN_PARTS; ok = key.next(CSwordVerseKey::UseBook); analysisItem = m_canvasItemList[key.book()]; } resize(xPos+BAR_WIDTH+(m_moduleList.count()-1)*BAR_DELTAX+RIGHT_BORDER, height() ); slotResized(); } /** Sets te module list used for the analysis. */ void CSearchAnalysis::setModules(ListCSwordModuleInfo modules) { m_moduleList.clear(); // for (modules.first(); modules.current(); modules.next()) { ListCSwordModuleInfo::iterator end_it = modules.end(); for (ListCSwordModuleInfo::iterator it(modules.begin()); it != end_it; ++it) { if ( ((*it)->type() == CSwordModuleInfo::Bible) || ((*it)->type() == CSwordModuleInfo::Commentary) ) { //a Bible or an commentary m_moduleList.append(*it); } } m_canvasItemList.clear(); CSearchAnalysisItem* analysisItem = 0; CSwordVerseKey key(0); key.key("Genesis 1:1"); do { analysisItem = new CSearchAnalysisItem(this, m_moduleList.count(), key.book(), &m_scaleFactor, &m_moduleList); analysisItem->hide(); m_canvasItemList.insert(key.book(), analysisItem); } while (key.next(CSwordVerseKey::UseBook)); update(); } /** Sets back the items and deletes things to cleanup */ void CSearchAnalysis::reset() { m_scaleFactor = 0.0; TQDictIterator it( m_canvasItemList ); // iterator for items while ( it.current() ) { it.current()->hide(); ++it; } m_lastPosList.clear(); if (m_legend) { m_legend->hide(); } delete m_legend; m_legend = 0; update(); } /** No descriptions */ void CSearchAnalysis::slotResized() { m_scaleFactor = (double)( (double)(height()-UPPER_BORDER-LOWER_BORDER-BAR_LOWER_BORDER-(m_moduleList.count()-1)*BAR_DELTAY) /(double)m_maxCount); TQDictIterator it( m_canvasItemList ); while ( it.current() ) { it.current()->setSize(BAR_WIDTH + (m_moduleList.count()-1)*BAR_DELTAX, height()-UPPER_BORDER-LOWER_BORDER); it.current()->setY(UPPER_BORDER); ++it; } update(); } /** This function returns a color for each module */ TQColor CSearchAnalysis::getColor(int index) { switch (index) { case 0: return TQt::red; case 1: return TQt::darkGreen; case 2: return TQt::blue; case 3: return TQt::cyan; case 4: return TQt::magenta; case 5: return TQt::darkRed; case 6: return TQt::darkGray; case 7: return TQt::black; case 8: return TQt::darkCyan; case 9: return TQt::darkMagenta; default: return TQt::red; } } /** Returns the count of the book in the module */ const unsigned int CSearchAnalysis::getCount( const TQString book, CSwordModuleInfo* module ) { sword::ListKey& result = module->searchResult(); const int length = book.length(); unsigned int i = m_lastPosList[module]; unsigned int count = 0; const unsigned int resultCount = result.Count(); while (i < resultCount) { if ( strncmp(book.utf8(), (const char*)*result.GetElement(i), length) ) break; i++; ++count; } m_lastPosList.contains(module) ? m_lastPosList.replace(module,i) : m_lastPosList.insert(module,i); return count; } //------------------------------------------------------------------ //------------------------------------------------------------------ CSearchAnalysisItem::CSearchAnalysisItem(TQCanvas *parent, const int moduleCount, const TQString &bookname, double *scaleFactor, ListCSwordModuleInfo* modules) : TQCanvasRectangle(parent), m_moduleList( modules ), m_scaleFactor(scaleFactor), m_bookName(bookname), m_moduleCount(moduleCount), m_bufferPixmap(0) { m_resultCountArray.resize(m_moduleCount); int index = 0; for (index = 0; index < m_moduleCount; ++index) m_resultCountArray[index] = 0; } CSearchAnalysisItem::~CSearchAnalysisItem() { delete m_bufferPixmap; } /** Sets the resultcount of this item for the given module */ void CSearchAnalysisItem::setCountForModule( const int moduleIndex, const int count) { m_resultCountArray[moduleIndex] = count; } /** Returns the resultcount of this item for the given module */ int CSearchAnalysisItem::getCountForModule( const int moduleIndex) { return m_resultCountArray[moduleIndex]; } /** Reimplementation. Draws the content of this item. */ void CSearchAnalysisItem::draw(TQPainter& painter) { TQFont f = painter.font(); f.setPointSize(ITEM_TEXT_SIZE); painter.setFont(f); setPen(TQPen(black,1)); setBrush(TQt::red); /** * We have to paint so many bars as we have modules available (we use m_moduleCount) * We paint inside the area which is given by height and width of this rectangle item */ int index = 0; int drawn = 0; int Value = 0; //find out the biggest value for (index=0;index < m_moduleCount; index++) { if (m_resultCountArray[index] > Value) { Value = m_resultCountArray[index]; } }; while (drawn < m_moduleCount) { for (index = 0; index < m_moduleCount; index++) { if (m_resultCountArray[index] == Value) { TQPoint p1((int)x() + (m_moduleCount-drawn-1)*BAR_DELTAX, (int)height() + (int)y() - BAR_LOWER_BORDER - (m_moduleCount-drawn)*BAR_DELTAY); TQPoint p2(p1.x() + BAR_WIDTH, p1.y() - (int)( !m_resultCountArray[index] ? 0 : ((m_resultCountArray[index])*(*m_scaleFactor))) ); TQRect r(p1, p2); painter.fillRect(r, TQBrush(CSearchAnalysis::getColor(index)) ); painter.drawRect(r); drawn++; } } //finds the next smaller value int newValue = 0; for (index=0;index < m_moduleCount; index++) if (m_resultCountArray[index] < Value && m_resultCountArray[index] >= newValue) newValue = m_resultCountArray[index]; Value = newValue; } if (!m_bufferPixmap) { m_bufferPixmap = new TQPixmap(); m_bufferPixmap->resize(width(),BAR_LOWER_BORDER); m_bufferPixmap->fill(); TQPainter p(m_bufferPixmap); f = p.font(); f.setPointSize(ITEM_TEXT_SIZE); p.setFont(f); p.rotate(90); p.drawText(TQPoint(5,0), m_bookName); } painter.drawPixmap(TQPoint(int(x()),int(height()+y()-BAR_LOWER_BORDER)), *m_bufferPixmap); } /** Returns the width of this item. */ int CSearchAnalysisItem::width() { return m_moduleCount*(m_moduleCount>1 ? BAR_DELTAX : 0) + BAR_WIDTH; } /** Returns the tooltip for this item. */ const TQString CSearchAnalysisItem::getToolTip() { TQString ret = TQString("
%1

").arg(m_bookName); ret += ""; //ToDo: Fix that loop int i = 0; ListCSwordModuleInfo::iterator end_it = m_moduleList->end(); for (ListCSwordModuleInfo::iterator it(m_moduleList->begin()); it != end_it; ++it) { // for (int i = 0; i < m_moduleCount; ++i) { CSwordModuleInfo* info = (*it); const TQColor c = CSearchAnalysis::getColor(i); ret.append( TQString("") .arg(TQString().sprintf("%02X%02X%02X",c.red(),c.green(),c.blue())) .arg(info ? info->name() : TQString()) .arg( m_resultCountArray[i] ) .arg( (info && m_resultCountArray[i])? ((double)m_resultCountArray[i] / (double)info->searchResult().Count())*(double)100 : 0.0, 0, 'g', 2) ); ++i; } ret += "
%2%3 (%4%)
"; return ret; } //------------------------------------------------------------------ //------------------------------------------------------------------ CSearchAnalysisView::CSearchAnalysisView(TQCanvas* canvas, TQWidget* parent) : TQCanvasView(canvas, parent) { setFocusPolicy(TQ_WheelFocus); m_toolTip = new ToolTip(this); resize(sizeHint()); } /** Returns the sizeHint for this view */ TQSize CSearchAnalysisView::sizeHint() { if ( parentWidget() ) return parentWidget()->sizeHint(); return TQCanvasView::sizeHint(); } /** No descriptions */ void CSearchAnalysisView::resizeEvent( TQResizeEvent* e) { TQCanvasView::resizeEvent(e); canvas()->resize( canvas()->width(), viewport()->height() ); } CSearchAnalysisView::ToolTip::ToolTip(TQWidget* parent) : TQToolTip(parent) {} void CSearchAnalysisView::ToolTip::maybeTip(const TQPoint& p) { CSearchAnalysisView* view = dynamic_cast(parentWidget()); if (!view) return; TQPoint point(p); point = view->viewport()->mapFrom(view, point); CSearchAnalysisItem* i = view->itemAt( view->viewportToContents(point) ); if (!i) return; //get type of item and display correct text TQString text = i->getToolTip(); if (text.isEmpty()) return; TQPoint p1 = view->viewport()->mapTo(view, view->contentsToViewport(i->rect().topLeft())); p1.setY(0); TQPoint p2 = view->viewport()->mapTo(view, view->contentsToViewport(i->rect().bottomRight())); p2.setY(view->height()); TQRect r = TQRect( p1, p2 ); if (r.contains(p)) tip(r, text); } /** Returns the item at position p. If there no item at that point return 0. */ CSearchAnalysisItem* CSearchAnalysisView::itemAt( const TQPoint& p ) { TQCanvasItemList l = canvas()->collisions(p); if (!l.count()) return 0; return dynamic_cast(l.first()); } //------------------------------------------------------------------ //------------------------------------------------------------------ CSearchAnalysisLegendItem::CSearchAnalysisLegendItem(TQCanvas *parent, ListCSwordModuleInfo *list ) : TQCanvasRectangle(parent) { m_moduleList = list; } /** Reimplementation. Draws the content of this item. */ void CSearchAnalysisLegendItem::draw (TQPainter& painter) { painter.save(); setPen( TQPen(black,2) ); setBrush( TQt::white ); //the outer rectangle TQPoint p1( (int)x(), (int)y() ); TQPoint p2( (int)x()+width(), (int)y() + height() ); TQRect r(p1, p2); r.normalize(); painter.drawRect(r); TQFont f = painter.font(); f.setPointSize(ITEM_TEXT_SIZE); painter.setFont(f); // for (unsigned int index=0; index < m_moduleList->count(); index++){ int moduleIndex = 0; ListCSwordModuleInfo::iterator end_it = m_moduleList->end(); for (ListCSwordModuleInfo::iterator it(m_moduleList->begin()); it != end_it; ++it) { // the module color indicators TQPoint p1( (int)x() + LEGEND_INNER_BORDER, (int)y() + LEGEND_INNER_BORDER + moduleIndex*(LEGEND_DELTAY + ITEM_TEXT_SIZE) ); TQPoint p2(p1.x() + ITEM_TEXT_SIZE, p1.y() + ITEM_TEXT_SIZE); TQRect r(p1,p2); painter.fillRect(r, TQBrush(CSearchAnalysis::getColor(moduleIndex)) ); r.normalize(); painter.drawRect(r); TQPoint p3( p2.x() + LEGEND_INNER_BORDER, p2.y() ); painter.drawText(p3, (*it)->name() ); ++moduleIndex; } painter.restore(); } /** No descriptions */ void CSearchAnalysis::saveAsHTML() { const TQString file = KFileDialog::getSaveFileName(TQString(), TQString("*.html | %1").arg(i18n("HTML files")), 0, i18n("Save Search Analysis")); if (file.isNull()) { return; } int moduleIndex = 0; int count = 0; TQString countStr = ""; TQString m_searchAnalysisHTML = ""; TQString tableTitle = ""; TQString tableTotals = ""; TQString VerseRange = ""; const TQString txtCSS = TQString("\n"); const TQString metaEncoding = TQString(""); CSwordVerseKey key(0); sword::ListKey searchResult; key.key("Genesis 1:1"); CSearchAnalysisItem* analysisItem = m_canvasItemList.find( key.book() ); TQString text = "\n\n" + i18n("BibleTime Search Analysis") + "\n" + txtCSS + metaEncoding + "\n\n"; text += "\n\n"; tableTitle = ""; tableTotals = ""; // for (moduleIndex = 0,m_moduleList.first(); m_moduleList.current(); m_moduleList.next(),++moduleIndex) { moduleIndex = 0; ListCSwordModuleInfo::iterator end_it = m_moduleList.end(); for (ListCSwordModuleInfo::iterator it(m_moduleList.begin()); it != end_it; ++it) { tableTitle += TQString(""); searchResult = (*it)->searchResult(); countStr.setNum(searchResult.Count()); tableTotals += TQString(""); ++moduleIndex; } tableTitle += TQString("\n"); tableTotals += TQString("\n"); m_searchAnalysisHTML = ""; bool ok = true; while (ok) { m_searchAnalysisHTML += TQString(""); analysisItem = m_canvasItemList.find( key.book() ); // for (moduleIndex = 0, m_moduleList.first(); m_moduleList.current(); m_moduleList.next(), ++moduleIndex) { moduleIndex = 0; ListCSwordModuleInfo::iterator end_it = m_moduleList.end(); for (ListCSwordModuleInfo::iterator it(m_moduleList.begin()); it != end_it; ++it) { count = analysisItem->getCountForModule(moduleIndex); countStr.setNum(count); m_searchAnalysisHTML += TQString(""); ++moduleIndex; } m_searchAnalysisHTML += TQString("\n"); ok = key.next(CSwordVerseKey::UseBook); } text += TQString("
" + i18n("Search text :") + "" + CSearchDialog::getSearchDialog()->searchText() + "
" + i18n("Book") + "
" + i18n("Total hits") + "") + (*it)->name() + TQString("") + countStr + TQString("
") + key.book() + TQString("") + countStr + TQString("
\n") + tableTitle + tableTotals + m_searchAnalysisHTML + TQString("
\n"); text += TQString("
") + i18n("Created by") + TQString(" BibleTime
"); text += TQString(""); CToolClass::savePlainFile(file, text, false, TQTextStream::UnicodeUTF8); } } //end of namespace Search::Analysis } //end of namespace Search