1 /* -------------------------------------------------------------
3 queryview.cpp (part of The KDE Dictionary Client)
5 Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
7 This file is distributed under the Artistic License.
8 See LICENSE for details.
10 -------------------------------------------------------------
12 BrowseData data structure used for caching definitions
13 DictHTMLPart handling of middle mouse button clicks
14 QueryView widget that displays the definitions
16 ------------------------------------------------------------- */
18 #include <qclipboard.h>
21 #include <QTextStream>
23 #include <kfiledialog.h>
24 #include <ktempfile.h>
25 #include <kio/netaccess.h>
28 #include <kmessagebox.h>
30 #include <khtml_events.h>
31 #include <khtmlview.h>
32 #include <kactioncollection.h>
33 #include <ktoolbarpopupaction.h>
37 #include "queryview.h"
38 #include <kapplication.h>
39 #include <kiconloader.h>
40 #include <ktoolinvocation.h>
43 //******** SaveHelper *******************************************
45 QString
SaveHelper::lastPath
;
47 SaveHelper::SaveHelper(const QString
&saveName
, const QString
&filter
, QWidget
*parent
)
48 : p_arent(parent
), s_aveName(saveName
), f_ilter(filter
), file(0), tmpFile(0)
53 SaveHelper::~SaveHelper()
55 if (file
) { // local filesystem, just close the file
58 if (tmpFile
) { // network location, initiate transaction
60 if (KIO::NetAccess::upload(tmpFile
->name(),url
, p_arent
) == false)
61 KMessageBox::error(global
->topLevel
, i18n("Unable to save remote file."));
62 tmpFile
->unlink(); // delete temp file
68 QFile
* SaveHelper::getFile(const QString
&dialogTitle
)
70 url
= KFileDialog::getSaveURL(lastPath
+s_aveName
,f_ilter
,p_arent
,dialogTitle
);
75 lastPath
= url
.url(KUrl::RemoveTrailingSlash
);
76 lastPath
.truncate(lastPath
.length()-url
.fileName().length());
78 if (url
.isLocalFile()) {
79 if (QFileInfo(url
.path()).exists() &&
80 (KMessageBox::warningContinueCancel(global
->topLevel
,
81 i18n("A file named %1 already exists.\nDo you want to replace it?", url
.path()),
82 dialogTitle
, i18n("&Replace")) != KMessageBox::Continue
)) {
86 file
= new QFile(url
.path());
87 if(!file
->open(QIODevice::WriteOnly
)) {
88 KMessageBox::error(global
->topLevel
, i18n("Unable to save file."));
94 tmpFile
= new KTempFile();
95 if (tmpFile
->status()!=0)
96 KMessageBox::error(global
->topLevel
, i18n("Unable to create temporary file.")); {
101 return tmpFile
->file();
106 //**** BrowseData ****************************************************
109 BrowseData::BrowseData(const QString
&Nhtml
, const QString
&NqueryText
)
110 : html(Nhtml
),queryText(NqueryText
),xPos(0),yPos(0)
114 //********* DictHTMLPart ******************************************
116 DictHTMLPart::DictHTMLPart(QWidget
*parentWidget
, const char *widgetname
)
117 : KHTMLPart(parentWidget
/*,widgetname*/)
121 DictHTMLPart::~DictHTMLPart()
125 void DictHTMLPart::khtmlMouseReleaseEvent(khtml::MouseReleaseEvent
*event
)
127 if (event
->qmouseEvent()->button()==Qt::MidButton
)
128 emit(middleButtonClicked());
130 KHTMLPart::khtmlMouseReleaseEvent(event
);
134 //********* QueryView ******************************************
137 QueryView::QueryView(QWidget
*_parent
)
138 : Q3VBox( _parent
), actBack(0L), actForward(0L), actQueryCombo(0L), browsePos(0), isRendering(false)
140 browseList
.setAutoDelete(true);
142 part
=new DictHTMLPart(this);
143 part
->setDNDEnabled(false);
144 part
->setJScriptEnabled(false);
145 part
->setJavaEnabled(false);
146 part
->setURLCursor(KCursor::handCursor());
147 setFocusPolicy(Qt::NoFocus
);
148 connect(part
, SIGNAL(completed()), SLOT(partCompleted()));
149 connect(part
, SIGNAL(middleButtonClicked()), SLOT(middleButtonClicked()));
150 rightBtnMenu
= new KMenu(this);
151 connect(part
,SIGNAL(popupMenu(const QString
&, const QPoint
&)),this,SLOT(buildPopupMenu(const QString
&, const QPoint
&)));
152 connect(part
->browserExtension(),SIGNAL(openURLRequest(const KUrl
&,const KParts::URLArgs
&)),
153 this,SLOT(slotURLRequest(const KUrl
&,const KParts::URLArgs
&)));
154 connect(part
->browserExtension(),SIGNAL(enableAction(const char *,bool)),this,SLOT(enableAction(const char *,bool)));
155 QTimer::singleShot( 0, this, SLOT(optionsChanged()) );
156 connect(interface
,SIGNAL(resultReady(const QString
&, const QString
&)), SLOT(resultReady(const QString
&, const QString
&)));
160 QueryView::~QueryView()
164 void QueryView::setActions(KToolBarPopupAction
* NactBack
, KToolBarPopupAction
* NactForward
, DictComboAction
* NactQueryCombo
)
167 connect(actBack
->popupMenu(),SIGNAL(activated(int)),SLOT(browseBack(int)));
168 actForward
= NactForward
;
169 connect(actForward
->popupMenu(),SIGNAL(activated(int)),SLOT(browseForward(int)));
170 actQueryCombo
= NactQueryCombo
;
174 bool QueryView::browseBackPossible() const
176 return (browsePos
> 0)? true:false;
180 bool QueryView::browseForwardPossible() const
182 return (browsePos
+1 < browseList
.count()) ? true:false;
186 void QueryView::optionsChanged()
188 saveCurrentResultPos();
190 currentHTMLHeader
= QString("<html><head><style type=\"text/css\">\n");
191 currentHTMLHeader
+= QString("body { background-color:%1; color:%2; }\n").arg(global
->backgroundColor().name()).arg(global
->textColor().name());
192 currentHTMLHeader
+= QString("a:link, a:active { color:%1; text-decoration:none; }\n").arg(global
->linksColor().name());
193 currentHTMLHeader
+= QString("a:visited { color:%1; text-decoration:none; }\n").arg(global
->visitedLinksColor().name());
194 currentHTMLHeader
+= QString("a:hover { color:%1; text-decoration:underline; }\n").arg(global
->linksColor().name());
195 currentHTMLHeader
+= QString("p.heading { margin-left:0mm; margin-top:2mm; margin-bottom:2mm; padding:1mm; background-color:%1; color:%2; font-family:%3; font-size:%4pt; ").arg(global
->headingsBackgroundColor().name()).arg(global
->headingsTextColor().name()).arg(global
->headingsFont().family()).arg(global
->headingsFont().pointSize());
196 if (global
->headingsFont().italic())
197 currentHTMLHeader
+= QString("font-style:italic; ");
198 if (global
->headingsFont().bold())
199 currentHTMLHeader
+= QString("font-weight:bold; ");
200 currentHTMLHeader
+= QString("}\n");
201 currentHTMLHeader
+= QString("p.definition { margin-left:1.5mm; margin-top:2mm; margin-bottom:2mm;}\n");
202 currentHTMLHeader
+= QString("table { margin-left:1.5mm; margin-top:2mm; margin-bottom:2mm;}\n");
203 currentHTMLHeader
+= QString("pre { font-family:%1; font-size:%2pt; ").arg(global
->textFont().family()).arg(global
->textFont().pointSize());
204 if (global
->textFont().italic())
205 currentHTMLHeader
+= QString("font-style:italic; ");
206 if (global
->textFont().bold())
207 currentHTMLHeader
+= QString("font-weight:bold; ");
208 currentHTMLHeader
+= QString("}\n");
209 currentHTMLHeader
+= QString("</style></head>\n");
211 showResult(); // apply changes directly
215 void QueryView::stop()
217 if (isRendering
== true) {
220 emit(renderingStopped());
225 // print current result
226 void QueryView::printQuery()
228 part
->view()->print();
232 // save the current result in an .html file
233 void QueryView::saveQuery()
235 if (!browseList
.isEmpty()) {
236 BrowseData
* brw
= browseList
.at(browsePos
);
237 QString fName
= brw
->queryText
+".html";
238 fName
.replace(QRegExp("[\\s/]"),"_");
239 SaveHelper
helper(fName
,"*.html",global
->topLevel
);
240 QFile
*file
= helper
.getFile(QString::null
);
243 QTextStream
stream(file
);
244 stream
.setEncoding(QTextStream::Locale
);
245 stream
<< currentHTMLHeader
+brw
->html
;
251 void QueryView::browseBack()
253 if (browseBackPossible()) {
254 saveCurrentResultPos();
256 actQueryCombo
->setEditText(browseList
.at(browsePos
)->queryText
);
258 updateBrowseActions();
263 void QueryView::browseForward()
265 if (browseForwardPossible()) {
266 saveCurrentResultPos();
268 actQueryCombo
->setEditText(browseList
.at(browsePos
)->queryText
);
270 updateBrowseActions();
275 void QueryView::selectAll()
281 void QueryView::copySelection()
283 kapp
->clipboard()->setText(part
->selectedText());
287 void QueryView::showFindDialog()
289 KAction
*act
= part
->actionCollection()->action("find");
295 void QueryView::paletteChange ( const QPalette
& )
302 void QueryView::fontChange ( const QFont
& )
308 void QueryView::saveCurrentResultPos()
310 if (!browseList
.isEmpty()) {
311 browseList
.at(browsePos
)->xPos
= part
->view()->contentsX();
312 browseList
.at(browsePos
)->yPos
= part
->view()->contentsY();
317 void QueryView::showResult()
321 emit(renderingStarted());
325 if (browseList
.isEmpty()) {
326 part
->write(currentHTMLHeader
+"<body></body></html>");
329 BrowseData
* brw
= browseList
.at(browsePos
);
330 emit(newCaption(getShortString(brw
->queryText
.simplified(),70)));
331 part
->write(currentHTMLHeader
+brw
->html
);
333 part
->view()->setFocus();
338 void QueryView::resultReady(const QString
&result
, const QString
&query
)
340 BrowseData
* brw
= new BrowseData(result
,query
);
342 if (browseList
.isEmpty()) {
344 browseList
.append(brw
);
346 saveCurrentResultPos();
347 while (browseList
.count()>browsePos
+1)
348 browseList
.removeLast();
349 browseList
.append(brw
);
351 while (browseList
.count()>global
->maxBrowseListEntrys
) {
352 browseList
.removeFirst();
358 emit(enablePrintSave());
359 actQueryCombo
->selectAll();
360 updateBrowseActions();
364 void QueryView::partCompleted()
366 if (!browseList
.isEmpty())
367 part
->view()->setContentsPos(browseList
.at(browsePos
)->xPos
,browseList
.at(browsePos
)->yPos
);
369 emit(renderingStopped());
375 void QueryView::slotURLRequest (const KUrl
&url
, const KParts::URLArgs
&)
377 QString type
= url
.host();
378 QString urlText
= url
.prettyUrl();
379 urlText
.remove(0,8+type
.length());
381 if (type
.length()) { // valid url
383 emit(defineRequested(urlText
));
385 interface
->showDbInfo(urlText
.utf8());
387 KToolInvocation::invokeBrowser("http://"+urlText
);
389 KToolInvocation::invokeBrowser("ftp://"+urlText
);
394 void QueryView::middleButtonClicked()
396 emit(clipboardRequested());
400 // construct the right-mouse-button-popup-menu on demand
401 void QueryView::buildPopupMenu(const QString
&url
, const QPoint
&point
)
403 rightBtnMenu
->clear();
405 if (!url
.isEmpty()) { // menuitem if mouse is over link
407 QString type
= u
.host();
408 popupLink
= u
.prettyUrl();
409 popupLink
.remove(0,8+type
.length());
411 if (type
.length()) { // valid url
413 rightBtnMenu
->insertItem(i18n("Define &Synonym"),
414 this,SLOT(popupDefineLink()));
415 rightBtnMenu
->insertItem(i18n("M&atch Synonym"),
416 this,SLOT(popupMatchLink()));
417 rightBtnMenu
->insertSeparator();
420 rightBtnMenu
->insertItem(i18n("D&atabase Information"),this,SLOT(popupDbInfo()));
421 rightBtnMenu
->insertSeparator();
423 if(type
=="realhttp") {
424 popupLink
.prepend("http://");
425 rightBtnMenu
->insertItem(SmallIcon("fileopen"),
427 this,SLOT(popupOpenLink()));
428 rightBtnMenu
->insertSeparator();
430 if(type
=="realftp") {
431 popupLink
.prepend("ftp://");
432 rightBtnMenu
->insertItem(SmallIcon("fileopen"),
434 this,SLOT(popupOpenLink()));
435 rightBtnMenu
->insertSeparator();
440 if (part
->hasSelection()) {
441 popupSelect
= part
->selectedText();
442 rightBtnMenu
->insertItem(i18n("&Define Selection"),
443 this,SLOT(popupDefineSelect()));
444 rightBtnMenu
->insertItem(i18n("&Match Selection"),
445 this,SLOT(popupMatchSelect()));
446 rightBtnMenu
->insertSeparator();
448 QString text
= kapp
->clipboard()->text(QClipboard::Clipboard
);
449 if (text
.isEmpty()) {
450 text
= kapp
->clipboard()->text(QClipboard::Selection
);
452 if (!text
.isEmpty()) {
453 popupSelect
= QApplication::clipboard()->text();
454 rightBtnMenu
->insertItem(SmallIcon("define_clip"),
455 i18n("&Define Clipboard Content"),
456 this,SLOT(popupDefineSelect()));
457 rightBtnMenu
->insertItem(i18n("&Match Clipboard Content"),
458 this,SLOT(popupMatchSelect()));
459 rightBtnMenu
->insertSeparator();
465 if (browseBackPossible()) { // if possible, show string
466 if (browseList
.at(browsePos
-1)->queryText
.isEmpty())
467 rightBtnMenu
->insertItem(SmallIcon("back"),
468 i18n("&Back: Information"),
469 this,SLOT(browseBack()));
471 rightBtnMenu
->insertItem(SmallIcon("back"),
472 i18n("&Back: '%1'", getShortString(browseList
.at(browsePos
-1)->queryText
,25)),
473 this,SLOT(browseBack()));
475 ID
= rightBtnMenu
->insertItem(SmallIcon("back"), i18n("&Back"), this, SLOT(browseBack()));
476 rightBtnMenu
->setItemEnabled(ID
,false);
479 if (browseForwardPossible()) { // if possible, show string
480 if (browseList
.at(browsePos
+1)->queryText
.isEmpty())
481 rightBtnMenu
->insertItem(SmallIcon("forward"),
482 i18n("&Forward: Information"),
483 this,SLOT(browseForward()));
485 rightBtnMenu
->insertItem(SmallIcon("forward"),
486 i18n("&Forward: '%1'", getShortString(browseList
.at(browsePos
+1)->queryText
,25)),
487 this,SLOT(browseForward()));
489 ID
= rightBtnMenu
->insertItem(SmallIcon("forward"),i18n("&Forward"),this,SLOT(browseForward()));
490 rightBtnMenu
->setItemEnabled(ID
,false);
493 rightBtnMenu
->popup(point
);
497 void QueryView::popupDefineLink()
499 emit(defineRequested(popupLink
));
503 void QueryView::popupMatchLink()
505 emit(matchRequested(popupLink
));
509 void QueryView::popupOpenLink()
511 KToolInvocation::invokeBrowser(popupLink
);
515 void QueryView::popupDefineSelect()
517 emit(defineRequested(popupSelect
));
521 void QueryView::popupMatchSelect()
523 emit(matchRequested(popupSelect
));
527 void QueryView::popupDbInfo()
530 interface
->showDbInfo(popupLink
.utf8());
534 void QueryView::enableAction(const char * name
, bool enabled
)
536 if (!strcmp(name
,"copy"))
537 emit(enableCopy(enabled
));
541 void QueryView::browseBack(int index
)
543 int x
= browsePos
-index
;
545 saveCurrentResultPos();
547 actQueryCombo
->setEditText(browseList
.at(browsePos
)->queryText
);
549 QTimer::singleShot(0, this, SLOT(updateBrowseActions())); // don't clear the menu in this slot
554 void QueryView::browseForward(int index
)
556 int x
= browsePos
+index
;
557 if (x
< (int)(browseList
.count())) {
558 saveCurrentResultPos();
560 actQueryCombo
->setEditText(browseList
.at(browsePos
)->queryText
);
562 QTimer::singleShot(0, this, SLOT(updateBrowseActions())); // don't clear the menu in this slot
567 void QueryView::updateBrowseActions()
569 if (browseBackPossible()) {
570 actBack
->setEnabled(true);
571 if (browseList
.at(browsePos
-1)->queryText
.isEmpty())
572 actBack
->setText(i18n("&Back: Information"));
574 actBack
->setText(i18n("&Back: '%1'", getShortString(browseList
.at(browsePos
-1)->queryText
,25)));
576 actBack
->popupMenu()->clear();
580 while ((i
>=0)&&(num
<=10)) {
581 s
= browseList
.at(i
)->queryText
;
582 if (s
.isEmpty()) s
= i18n("Information");
583 actBack
->popupMenu()->insertItem(s
,num
);
588 actBack
->setEnabled(false);
589 actBack
->setText(i18n("&Back"));
590 actBack
->popupMenu()->clear();
593 if (browseForwardPossible()) {
594 actForward
->setEnabled(true);
595 if (browseList
.at(browsePos
+1)->queryText
.isEmpty())
596 actForward
->setText(i18n("&Forward: Information"));
598 actForward
->setText(i18n("&Forward: '%1'", getShortString(browseList
.at(browsePos
+1)->queryText
,25)));
600 actForward
->popupMenu()->clear();
604 while ((i
<(int)(browseList
.count()))&&(num
<=10)) {
605 s
= browseList
.at(i
)->queryText
;
606 if (s
.isEmpty()) s
= i18n("Information");
607 actForward
->popupMenu()->insertItem(s
,num
);
612 actForward
->setEnabled(false);
613 actForward
->setText(i18n("&Forward"));
614 actForward
->popupMenu()->clear();
618 //--------------------------------
620 #include "queryview.moc"