3
* This file is part of BibleTime's source code, http://www.bibletime.info/.
5
* Copyright 1999-2006 by the BibleTime developers.
6
* The BibleTime source code is licensed under the GNU General Public License version 2.0.
12
#include "csearchanalysis.h"
13
#include "csearchdialog.h"
15
#include "backend/cswordkey.h"
16
#include "backend/cswordversekey.h"
18
#include "frontend/cbtconfig.h"
20
#include "util/cresmgr.h"
21
#include "util/ctoolclass.h"
30
#include <qlineedit.h>
31
#include <qtextedit.h>
33
#include <qsizepolicy.h>
34
#include <qpushbutton.h>
37
#include <qmessagebox.h>
40
#include <kapplication.h>
41
#include <kfiledialog.h>
43
#include <kiconloader.h>
48
const int SPACE_BETWEEN_PARTS = 5;
49
const int RIGHT_BORDER = 15;
50
const int LEFT_BORDER = 15;
51
const int LOWER_BORDER = 10;
52
const int UPPER_BORDER = 10;
54
const int ITEM_TEXT_SIZE = 8;
55
const int LABEL_TEXT_SIZE = 6;
57
//used for the shift between the bars
58
const int BAR_DELTAX = 4;
59
const int BAR_DELTAY = 2;
60
const int BAR_WIDTH = 2 + (2*BAR_DELTAX); //should be equal or bigger than the label font size
61
// Used for the text below the bars
62
const int BAR_LOWER_BORDER = 100;
64
const int LEGEND_INNER_BORDER = 5;
65
const int LEGEND_DELTAY = 4;
66
const int LEGEND_WIDTH = 85;
68
/****************************/
70
CSearchAnalysisDialog::CSearchAnalysisDialog( ListCSwordModuleInfo modules, QWidget* parentDialog ) : KDialogBase(Plain, i18n("Search analysis"), Close, Close, parentDialog, 0, true) {
74
m_analysis->analyse(modules);
78
CSearchAnalysisDialog::~CSearchAnalysisDialog() {}
81
/** Initializes this dialog. */
82
void CSearchAnalysisDialog::initView() {
83
QVBoxLayout* layout = new QVBoxLayout(plainPage(),0);
85
QPushButton* button = new QPushButton(plainPage(), "button");
86
button->setIconSet(SmallIconSet("filesave"));
87
button->setText(i18n("Save search analysis as HTML"));
88
button->setFixedSize(button->sizeHint());
89
layout->addWidget(button);
90
layout->addSpacing(10);
92
m_analysis = new CSearchAnalysis(plainPage());
93
m_analysisView = new CSearchAnalysisView(m_analysis, plainPage());
94
m_analysisView->show();
95
layout->addWidget(m_analysisView);
97
connect(button, SIGNAL(clicked()), m_analysis, SLOT(saveAsHTML()));
100
/** Initializes the widgets SIGNAL and SLOT connections,. */
101
void CSearchAnalysisDialog::initConnections() {}
103
/****************************/
104
/* CSearchAnalysis */
105
/****************************/
107
CSearchAnalysis::CSearchAnalysis(QObject *parent, const char *name )
108
: QCanvas(parent,name) {
112
setBackgroundColor(Qt::white);
113
m_canvasItemList.resize(67);
114
m_canvasItemList.setAutoDelete(true);
116
connect(this, SIGNAL(resized()), SLOT(slotResized()));
119
CSearchAnalysis::~CSearchAnalysis() {}
121
QDict<CSearchAnalysisItem>* CSearchAnalysis::getSearchAnalysisItemList() {
122
// Returns pointer to the search analysis items
123
return &m_canvasItemList;
126
/** Starts the analysis of the search result. This should be called only once because QCanvas handles the updates automatically. */
127
void CSearchAnalysis::analyse(ListCSwordModuleInfo modules) {
129
* Steps of analysing our search result;
130
* -Create the items for all available books ("Genesis" - "Revelation")
131
* -Iterate through all modules we analyse
132
* -Go through all books of this module
133
* -Find out how many times we found the book
134
* -Set the count to the items which belongs to the book
138
m_lastPosList.clear();
139
const int numberOfModules = m_moduleList.count();
140
if (!numberOfModules)
142
m_legend = new CSearchAnalysisLegendItem(this, &m_moduleList);
143
m_legend->setX(LEFT_BORDER);
144
m_legend->setY(UPPER_BORDER);
145
m_legend->setSize(LEGEND_WIDTH,
146
LEGEND_INNER_BORDER*2 + ITEM_TEXT_SIZE*numberOfModules + LEGEND_DELTAY*(numberOfModules-1));
149
int xPos = LEFT_BORDER + m_legend->width() + SPACE_BETWEEN_PARTS;
153
CSwordVerseKey key(0);
154
key.key("Genesis 1:1");
156
CSearchAnalysisItem* analysisItem = m_canvasItemList[key.book()];
158
while (ok && analysisItem) {
159
// for (moduleIndex = 0,m_moduleList.first(); m_moduleList.current(); m_moduleList.next(),++moduleIndex) {
161
ListCSwordModuleInfo::iterator end_it = m_moduleList.end();
162
for (ListCSwordModuleInfo::iterator it(m_moduleList.begin()); it != end_it; ++it) {
163
KApplication::kApplication()->processEvents( 10 ); //10 ms only
164
if (!m_lastPosList.contains(*it)) {
165
m_lastPosList.insert(*it,0);
168
analysisItem->setCountForModule(moduleIndex, (count = getCount(key.book(), *it)));
169
m_maxCount = (count > m_maxCount) ? count : m_maxCount;
173
analysisItem->setX(xPos);
174
analysisItem->setY(UPPER_BORDER);
175
analysisItem->show();
177
xPos += (int)analysisItem->width() + SPACE_BETWEEN_PARTS;
178
ok = key.next(CSwordVerseKey::UseBook);
179
analysisItem = m_canvasItemList[key.book()];
181
resize(xPos+BAR_WIDTH+(m_moduleList.count()-1)*BAR_DELTAX+RIGHT_BORDER, height() );
185
/** Sets te module list used for the analysis. */
186
void CSearchAnalysis::setModules(ListCSwordModuleInfo modules) {
187
m_moduleList.clear();
188
// for (modules.first(); modules.current(); modules.next()) {
189
ListCSwordModuleInfo::iterator end_it = modules.end();
190
for (ListCSwordModuleInfo::iterator it(modules.begin()); it != end_it; ++it) {
191
if ( ((*it)->type() == CSwordModuleInfo::Bible) || ((*it)->type() == CSwordModuleInfo::Commentary) ) { //a Bible or an commentary
192
m_moduleList.append(*it);
196
m_canvasItemList.clear();
197
CSearchAnalysisItem* analysisItem = 0;
198
CSwordVerseKey key(0);
199
key.key("Genesis 1:1");
201
analysisItem = new CSearchAnalysisItem(this, m_moduleList.count(), key.book(), &m_scaleFactor, &m_moduleList);
202
analysisItem->hide();
203
m_canvasItemList.insert(key.book(), analysisItem);
205
while (key.next(CSwordVerseKey::UseBook));
209
/** Sets back the items and deletes things to cleanup */
210
void CSearchAnalysis::reset() {
213
QDictIterator<CSearchAnalysisItem> it( m_canvasItemList ); // iterator for items
214
while ( it.current() ) {
215
it.current()->hide();
218
m_lastPosList.clear();
230
/** No descriptions */
231
void CSearchAnalysis::slotResized() {
232
m_scaleFactor = (double)( (double)(height()-UPPER_BORDER-LOWER_BORDER-BAR_LOWER_BORDER-(m_moduleList.count()-1)*BAR_DELTAY)
233
/(double)m_maxCount);
234
QDictIterator<CSearchAnalysisItem> it( m_canvasItemList );
235
while ( it.current() ) {
236
it.current()->setSize(BAR_WIDTH + (m_moduleList.count()-1)*BAR_DELTAX, height()-UPPER_BORDER-LOWER_BORDER);
237
it.current()->setY(UPPER_BORDER);
243
/** This function returns a color for each module */
244
QColor CSearchAnalysis::getColor(int index) {
249
return Qt::darkGreen;
265
return Qt::darkMagenta;
271
/** Returns the count of the book in the module */
272
const unsigned int CSearchAnalysis::getCount( const QString book, CSwordModuleInfo* module ) {
273
sword::ListKey& result = module->searchResult();
274
const int length = book.length();
275
unsigned int i = m_lastPosList[module];
276
unsigned int count = 0;
277
const unsigned int resultCount = result.Count();
278
while (i < resultCount) {
279
if ( strncmp(book.utf8(), (const char*)*result.GetElement(i), length) )
284
m_lastPosList.contains(module) ? m_lastPosList.replace(module,i) : m_lastPosList.insert(module,i);
290
//------------------------------------------------------------------
291
//------------------------------------------------------------------
293
CSearchAnalysisItem::CSearchAnalysisItem(QCanvas *parent, const int moduleCount, const QString &bookname, double *scaleFactor, ListCSwordModuleInfo* modules)
294
: QCanvasRectangle(parent),
295
m_moduleList( modules ),
296
m_scaleFactor(scaleFactor),
297
m_bookName(bookname),
298
m_moduleCount(moduleCount),
300
m_resultCountArray.resize(m_moduleCount);
302
for (index = 0; index < m_moduleCount; ++index)
303
m_resultCountArray[index] = 0;
306
CSearchAnalysisItem::~CSearchAnalysisItem() {
307
delete m_bufferPixmap;
310
/** Sets the resultcount of this item for the given module */
311
void CSearchAnalysisItem::setCountForModule( const int moduleIndex, const int count) {
312
m_resultCountArray[moduleIndex] = count;
315
/** Returns the resultcount of this item for the given module */
316
int CSearchAnalysisItem::getCountForModule( const int moduleIndex) {
317
return m_resultCountArray[moduleIndex];
320
/** Reimplementation. Draws the content of this item. */
321
void CSearchAnalysisItem::draw(QPainter& painter) {
322
QFont f = painter.font();
323
f.setPointSize(ITEM_TEXT_SIZE);
326
setPen(QPen(black,1));
329
* We have to paint so many bars as we have modules available (we use m_moduleCount)
330
* We paint inside the area which is given by height and width of this rectangle item
336
//find out the biggest value
337
for (index=0;index < m_moduleCount; index++) {
338
if (m_resultCountArray[index] > Value) {
339
Value = m_resultCountArray[index];
343
while (drawn < m_moduleCount) {
344
for (index = 0; index < m_moduleCount; index++) {
345
if (m_resultCountArray[index] == Value) {
346
QPoint p1((int)x() + (m_moduleCount-drawn-1)*BAR_DELTAX,
347
(int)height() + (int)y() - BAR_LOWER_BORDER - (m_moduleCount-drawn)*BAR_DELTAY);
348
QPoint p2(p1.x() + BAR_WIDTH,
349
p1.y() - (int)( !m_resultCountArray[index] ? 0 : ((m_resultCountArray[index])*(*m_scaleFactor))) );
351
painter.fillRect(r, QBrush(CSearchAnalysis::getColor(index)) );
356
//finds the next smaller value
358
for (index=0;index < m_moduleCount; index++)
359
if (m_resultCountArray[index] < Value && m_resultCountArray[index] >= newValue)
360
newValue = m_resultCountArray[index];
363
if (!m_bufferPixmap) {
364
m_bufferPixmap = new QPixmap();
365
m_bufferPixmap->resize(width(),BAR_LOWER_BORDER);
366
m_bufferPixmap->fill();
367
QPainter p(m_bufferPixmap);
369
f.setPointSize(ITEM_TEXT_SIZE);
372
p.drawText(QPoint(5,0), m_bookName);
374
painter.drawPixmap(QPoint(int(x()),int(height()+y()-BAR_LOWER_BORDER)), *m_bufferPixmap);
377
/** Returns the width of this item. */
378
int CSearchAnalysisItem::width() {
379
return m_moduleCount*(m_moduleCount>1 ? BAR_DELTAX : 0) + BAR_WIDTH;
382
/** Returns the tooltip for this item. */
383
const QString CSearchAnalysisItem::getToolTip() {
384
QString ret = QString("<center><b>%1</b></center><hr/>").arg(m_bookName);
385
ret += "<table cellspacing=\"0\" cellpadding=\"3\" width=\"100%\" height=\"100%\" align=\"center\">";
387
//ToDo: Fix that loop
389
ListCSwordModuleInfo::iterator end_it = m_moduleList->end();
391
for (ListCSwordModuleInfo::iterator it(m_moduleList->begin()); it != end_it; ++it) {
392
// for (int i = 0; i < m_moduleCount; ++i) {
393
CSwordModuleInfo* info = (*it);
394
const QColor c = CSearchAnalysis::getColor(i);
397
QString("<tr bgcolor=\"white\"><td><b><font color=\"#%1\">%2</font></b></td><td>%3 (%4%)</td></tr>")
398
.arg(QString().sprintf("%02X%02X%02X",c.red(),c.green(),c.blue()))
399
.arg(info ? info->name() : QString::null)
400
.arg( m_resultCountArray[i] )
401
.arg( (info && m_resultCountArray[i])? ((double)m_resultCountArray[i] / (double)info->searchResult().Count())*(double)100 : 0.0, 0, 'g', 2)
411
//------------------------------------------------------------------
412
//------------------------------------------------------------------
414
CSearchAnalysisView::CSearchAnalysisView(QCanvas* canvas, QWidget* parent)
415
: QCanvasView(canvas, parent) {
416
setFocusPolicy(QWidget::WheelFocus);
417
m_toolTip = new ToolTip(this);
421
/** Returns the sizeHint for this view */
422
QSize CSearchAnalysisView::sizeHint() {
423
if ( parentWidget() )
424
return parentWidget()->sizeHint();
425
return QCanvasView::sizeHint();
428
/** No descriptions */
429
void CSearchAnalysisView::resizeEvent( QResizeEvent* e) {
430
QCanvasView::resizeEvent(e);
431
canvas()->resize( canvas()->width(), viewport()->height() );
434
CSearchAnalysisView::ToolTip::ToolTip(QWidget* parent) : QToolTip(parent) {}
436
void CSearchAnalysisView::ToolTip::maybeTip(const QPoint& p) {
437
CSearchAnalysisView* view = dynamic_cast<CSearchAnalysisView*>(parentWidget());
441
point = view->viewport()->mapFrom(view, point);
442
CSearchAnalysisItem* i = view->itemAt( view->viewportToContents(point) );
446
//get type of item and display correct text
447
QString text = i->getToolTip();
451
QPoint p1 = view->viewport()->mapTo(view, view->contentsToViewport(i->rect().topLeft()));
453
QPoint p2 = view->viewport()->mapTo(view, view->contentsToViewport(i->rect().bottomRight()));
454
p2.setY(view->height());
455
QRect r = QRect( p1, p2 );
461
/** Returns the item at position p. If there no item at that point return 0. */
462
CSearchAnalysisItem* CSearchAnalysisView::itemAt( const QPoint& p ) {
463
QCanvasItemList l = canvas()->collisions(p);
466
return dynamic_cast<CSearchAnalysisItem*>(l.first());
469
//------------------------------------------------------------------
470
//------------------------------------------------------------------
472
CSearchAnalysisLegendItem::CSearchAnalysisLegendItem(QCanvas *parent, ListCSwordModuleInfo *list )
473
: QCanvasRectangle(parent) {
477
/** Reimplementation. Draws the content of this item. */
478
void CSearchAnalysisLegendItem::draw (QPainter& painter) {
481
setPen( QPen(black,2) );
482
setBrush( Qt::white );
483
//the outer rectangle
484
QPoint p1( (int)x(), (int)y() );
485
QPoint p2( (int)x()+width(), (int)y() + height() );
490
QFont f = painter.font();
491
f.setPointSize(ITEM_TEXT_SIZE);
494
// for (unsigned int index=0; index < m_moduleList->count(); index++){
496
ListCSwordModuleInfo::iterator end_it = m_moduleList->end();
497
for (ListCSwordModuleInfo::iterator it(m_moduleList->begin()); it != end_it; ++it) {
498
// the module color indicators
499
QPoint p1( (int)x() + LEGEND_INNER_BORDER, (int)y() + LEGEND_INNER_BORDER + moduleIndex*(LEGEND_DELTAY + ITEM_TEXT_SIZE) );
500
QPoint p2(p1.x() + ITEM_TEXT_SIZE, p1.y() + ITEM_TEXT_SIZE);
502
painter.fillRect(r, QBrush(CSearchAnalysis::getColor(moduleIndex)) );
506
QPoint p3( p2.x() + LEGEND_INNER_BORDER, p2.y() );
507
painter.drawText(p3, (*it)->name() );
514
/** No descriptions */
515
void CSearchAnalysis::saveAsHTML() {
516
const QString file = KFileDialog::getSaveFileName(QString::null,
517
QString("*.html | %1").arg(i18n("HTML files")),
519
i18n("Save Search Analysis"));
526
QString countStr = "";
527
QString m_searchAnalysisHTML = "";
528
QString tableTitle = "";
529
QString tableTotals = "";
530
QString VerseRange = "";
531
const QString txtCSS = QString("<style type=\"text/css\">\ntd {border:1px solid black;}\nth {font-size: 130%; text-align:left; vertical-align:top;}\n</style>\n");
532
const QString metaEncoding = QString("<META http-equiv=Content-Type content=\"text/html; charset=utf-8\">");
533
CSwordVerseKey key(0);
534
sword::ListKey searchResult;
536
key.key("Genesis 1:1");
538
CSearchAnalysisItem* analysisItem = m_canvasItemList.find( key.book() );
540
QString text = "<html>\n<head>\n<title>" + i18n("BibleTime Search Analysis") + "</title>\n" + txtCSS + metaEncoding + "</head>\n<body>\n";
541
text += "<table>\n<tr><th>" + i18n("Search text :") + "</th><th>" + CSearchDialog::getSearchDialog()->searchText() + "</th></tr>\n";
543
tableTitle = "<tr><th align=\"left\">" + i18n("Book") + "</th>";
544
tableTotals = "<tr><td align=\"left\">" + i18n("Total hits") + "</td>";
545
// for (moduleIndex = 0,m_moduleList.first(); m_moduleList.current(); m_moduleList.next(),++moduleIndex) {
547
ListCSwordModuleInfo::iterator end_it = m_moduleList.end();
548
for (ListCSwordModuleInfo::iterator it(m_moduleList.begin()); it != end_it; ++it) {
549
tableTitle += QString("<th align=\"left\">") + (*it)->name() + QString("</th>");
550
searchResult = (*it)->searchResult();
551
countStr.setNum(searchResult.Count());
553
tableTotals += QString("<td align=\"right\">") + countStr + QString("</td>");
556
tableTitle += QString("</tr>\n");
557
tableTotals += QString("</tr>\n");
559
m_searchAnalysisHTML = "";
562
m_searchAnalysisHTML += QString("<tr><td>") + key.book() + QString("</td>");
563
analysisItem = m_canvasItemList.find( key.book() );
565
// for (moduleIndex = 0, m_moduleList.first(); m_moduleList.current(); m_moduleList.next(), ++moduleIndex) {
567
ListCSwordModuleInfo::iterator end_it = m_moduleList.end();
568
for (ListCSwordModuleInfo::iterator it(m_moduleList.begin()); it != end_it; ++it) {
569
count = analysisItem->getCountForModule(moduleIndex);
570
countStr.setNum(count);
571
m_searchAnalysisHTML += QString("<td align=\"right\">") + countStr + QString("</td>");
575
m_searchAnalysisHTML += QString("</tr>\n");
576
ok = key.next(CSwordVerseKey::UseBook);
579
text += QString("<table>\n") + tableTitle + tableTotals + m_searchAnalysisHTML + QString("</table>\n");
580
text += QString("<center>") + i18n("Created by") + QString(" <a href=\"http://www.bibletime.info/\">BibleTime</a></center>");
581
text += QString("</body></html>");
583
CToolClass::savePlainFile(file, text, false, QTextStream::UnicodeUTF8);
586
} //end of namespace Search::Analysis
587
} //end of namespace Search