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 <kfiledialog.h>
22 #include <ktempfile.h>
23 #include <kio/netaccess.h>
25 #include <kpopupmenu.h>
26 #include <kmessagebox.h>
28 #include <khtml_events.h>
29 #include <khtmlview.h>
34 #include "queryview.h"
35 #include <kapplication.h>
36 #include <kiconloader.h>
39 //******** SaveHelper *******************************************
41 QString
SaveHelper::lastPath
;
43 SaveHelper::SaveHelper(const QString
&saveName
, const QString
&filter
, QWidget
*parent
)
44 : p_arent(parent
), s_aveName(saveName
), f_ilter(filter
), file(0), tmpFile(0)
49 SaveHelper::~SaveHelper()
51 if (file
) { // local filesystem, just close the file
54 if (tmpFile
) { // network location, initiate transaction
56 if (KIO::NetAccess::upload(tmpFile
->name(),url
, p_arent
) == false)
57 KMessageBox::error(global
->topLevel
, i18n("Unable to save remote file."));
58 tmpFile
->unlink(); // delete temp file
64 QFile
* SaveHelper::getFile(const QString
&dialogTitle
)
66 url
= KFileDialog::getSaveURL(lastPath
+s_aveName
,f_ilter
,p_arent
,dialogTitle
);
71 lastPath
= url
.url(-1);
72 lastPath
.truncate(lastPath
.length()-url
.fileName().length());
74 if (url
.isLocalFile()) {
75 if (QFileInfo(url
.path()).exists() &&
76 (KMessageBox::warningContinueCancel(global
->topLevel
,
77 i18n("A file named %1 already exists.\nDo you want to replace it?").arg(url
.path()),
78 dialogTitle
, i18n("&Replace")) != KMessageBox::Continue
)) {
82 file
= new QFile(url
.path());
83 if(!file
->open(IO_WriteOnly
)) {
84 KMessageBox::error(global
->topLevel
, i18n("Unable to save file."));
90 tmpFile
= new KTempFile();
91 if (tmpFile
->status()!=0)
92 KMessageBox::error(global
->topLevel
, i18n("Unable to create temporary file.")); {
97 return tmpFile
->file();
102 //**** BrowseData ****************************************************
105 BrowseData::BrowseData(const QString
&Nhtml
, const QString
&NqueryText
)
106 : html(Nhtml
),queryText(NqueryText
),xPos(0),yPos(0)
110 //********* DictHTMLPart ******************************************
112 DictHTMLPart::DictHTMLPart(QWidget
*parentWidget
, const char *widgetname
)
113 : KHTMLPart(parentWidget
,widgetname
)
117 DictHTMLPart::~DictHTMLPart()
121 void DictHTMLPart::khtmlMouseReleaseEvent(khtml::MouseReleaseEvent
*event
)
123 if (event
->qmouseEvent()->button()==MidButton
)
124 emit(middleButtonClicked());
126 KHTMLPart::khtmlMouseReleaseEvent(event
);
130 //********* QueryView ******************************************
133 QueryView::QueryView(QWidget
*_parent
)
134 : QVBox( _parent
), actBack(0L), actForward(0L), actQueryCombo(0L), browsePos(0), isRendering(false)
136 browseList
.setAutoDelete(true);
138 part
=new DictHTMLPart(this);
139 part
->setDNDEnabled(false);
140 part
->setJScriptEnabled(false);
141 part
->setJavaEnabled(false);
142 part
->setURLCursor(KCursor::handCursor());
143 setFocusPolicy(QWidget::NoFocus
);
144 connect(part
, SIGNAL(completed()), SLOT(partCompleted()));
145 connect(part
, SIGNAL(middleButtonClicked()), SLOT(middleButtonClicked()));
146 rightBtnMenu
= new KPopupMenu(this);
147 connect(part
,SIGNAL(popupMenu(const QString
&, const QPoint
&)),this,SLOT(buildPopupMenu(const QString
&, const QPoint
&)));
148 connect(part
->browserExtension(),SIGNAL(openURLRequest(const KURL
&,const KParts::URLArgs
&)),
149 this,SLOT(slotURLRequest(const KURL
&,const KParts::URLArgs
&)));
150 connect(part
->browserExtension(),SIGNAL(enableAction(const char *,bool)),this,SLOT(enableAction(const char *,bool)));
151 QTimer::singleShot( 0, this, SLOT(optionsChanged()) );
152 connect(interface
,SIGNAL(resultReady(const QString
&, const QString
&)), SLOT(resultReady(const QString
&, const QString
&)));
156 QueryView::~QueryView()
160 void QueryView::setActions(KToolBarPopupAction
* NactBack
, KToolBarPopupAction
* NactForward
, DictComboAction
* NactQueryCombo
)
163 connect(actBack
->popupMenu(),SIGNAL(activated(int)),SLOT(browseBack(int)));
164 actForward
= NactForward
;
165 connect(actForward
->popupMenu(),SIGNAL(activated(int)),SLOT(browseForward(int)));
166 actQueryCombo
= NactQueryCombo
;
170 bool QueryView::browseBackPossible() const
172 return (browsePos
> 0)? true:false;
176 bool QueryView::browseForwardPossible() const
178 return (browsePos
+1 < browseList
.count()) ? true:false;
182 void QueryView::optionsChanged()
184 saveCurrentResultPos();
186 currentHTMLHeader
= QString("<html><head><style type=\"text/css\">\n");
187 currentHTMLHeader
+= QString("body { background-color:%1; color:%2; }\n").arg(global
->backgroundColor().name()).arg(global
->textColor().name());
188 currentHTMLHeader
+= QString("a:link, a:active { color:%1; text-decoration:none; }\n").arg(global
->linksColor().name());
189 currentHTMLHeader
+= QString("a:visited { color:%1; text-decoration:none; }\n").arg(global
->visitedLinksColor().name());
190 currentHTMLHeader
+= QString("a:hover { color:%1; text-decoration:underline; }\n").arg(global
->linksColor().name());
191 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());
192 if (global
->headingsFont().italic())
193 currentHTMLHeader
+= QString("font-style:italic; ");
194 if (global
->headingsFont().bold())
195 currentHTMLHeader
+= QString("font-weight:bold; ");
196 currentHTMLHeader
+= QString("}\n");
197 currentHTMLHeader
+= QString("p.definition { margin-left:1.5mm; margin-top:2mm; margin-bottom:2mm;}\n");
198 currentHTMLHeader
+= QString("table { margin-left:1.5mm; margin-top:2mm; margin-bottom:2mm;}\n");
199 currentHTMLHeader
+= QString("pre { font-family:%1; font-size:%2pt; ").arg(global
->textFont().family()).arg(global
->textFont().pointSize());
200 if (global
->textFont().italic())
201 currentHTMLHeader
+= QString("font-style:italic; ");
202 if (global
->textFont().bold())
203 currentHTMLHeader
+= QString("font-weight:bold; ");
204 currentHTMLHeader
+= QString("}\n");
205 currentHTMLHeader
+= QString("</style></head>\n");
207 showResult(); // apply changes directly
211 void QueryView::stop()
213 if (isRendering
== true) {
216 emit(renderingStopped());
221 // print current result
222 void QueryView::printQuery()
224 part
->view()->print();
228 // save the current result in an .html file
229 void QueryView::saveQuery()
231 if (!browseList
.isEmpty()) {
232 BrowseData
* brw
= browseList
.at(browsePos
);
233 QString fName
= brw
->queryText
+".html";
234 fName
.replace(QRegExp("[\\s/]"),"_");
235 SaveHelper
helper(fName
,"*.html",global
->topLevel
);
236 QFile
*file
= helper
.getFile(QString::null
);
239 QTextStream
stream(file
);
240 stream
.setEncoding(QTextStream::Locale
);
241 stream
<< currentHTMLHeader
+brw
->html
;
247 void QueryView::browseBack()
249 if (browseBackPossible()) {
250 saveCurrentResultPos();
252 actQueryCombo
->setEditText(browseList
.at(browsePos
)->queryText
);
254 updateBrowseActions();
259 void QueryView::browseForward()
261 if (browseForwardPossible()) {
262 saveCurrentResultPos();
264 actQueryCombo
->setEditText(browseList
.at(browsePos
)->queryText
);
266 updateBrowseActions();
271 void QueryView::selectAll()
277 void QueryView::copySelection()
279 kapp
->clipboard()->setText(part
->selectedText());
283 void QueryView::showFindDialog()
285 KAction
*act
= part
->actionCollection()->action("find");
291 void QueryView::paletteChange ( const QPalette
& )
298 void QueryView::fontChange ( const QFont
& )
304 void QueryView::saveCurrentResultPos()
306 if (!browseList
.isEmpty()) {
307 browseList
.at(browsePos
)->xPos
= part
->view()->contentsX();
308 browseList
.at(browsePos
)->yPos
= part
->view()->contentsY();
313 void QueryView::showResult()
317 emit(renderingStarted());
321 if (browseList
.isEmpty()) {
322 part
->write(currentHTMLHeader
+"<body></body></html>");
325 BrowseData
* brw
= browseList
.at(browsePos
);
326 emit(newCaption(getShortString(brw
->queryText
.simplifyWhiteSpace(),70)));
327 part
->write(currentHTMLHeader
+brw
->html
);
329 part
->view()->setFocus();
334 void QueryView::resultReady(const QString
&result
, const QString
&query
)
336 BrowseData
* brw
= new BrowseData(result
,query
);
338 if (browseList
.isEmpty()) {
340 browseList
.append(brw
);
342 saveCurrentResultPos();
343 while (browseList
.count()>browsePos
+1)
344 browseList
.removeLast();
345 browseList
.append(brw
);
347 while (browseList
.count()>global
->maxBrowseListEntrys
) {
348 browseList
.removeFirst();
354 emit(enablePrintSave());
355 actQueryCombo
->selectAll();
356 updateBrowseActions();
360 void QueryView::partCompleted()
362 if (!browseList
.isEmpty())
363 part
->view()->setContentsPos(browseList
.at(browsePos
)->xPos
,browseList
.at(browsePos
)->yPos
);
365 emit(renderingStopped());
371 void QueryView::slotURLRequest (const KURL
&url
, const KParts::URLArgs
&)
373 QString type
= url
.host();
374 QString urlText
= url
.prettyURL();
375 urlText
.remove(0,8+type
.length());
377 if (type
.length()) { // valid url
379 emit(defineRequested(urlText
));
381 interface
->showDbInfo(urlText
.utf8());
383 kapp
->invokeBrowser("http://"+urlText
);
385 kapp
->invokeBrowser("ftp://"+urlText
);
390 void QueryView::middleButtonClicked()
392 emit(clipboardRequested());
396 // construct the right-mouse-button-popup-menu on demand
397 void QueryView::buildPopupMenu(const QString
&url
, const QPoint
&point
)
399 rightBtnMenu
->clear();
401 if (!url
.isEmpty()) { // menuitem if mouse is over link
403 QString type
= u
.host();
404 popupLink
= u
.prettyURL();
405 popupLink
.remove(0,8+type
.length());
407 if (type
.length()) { // valid url
409 rightBtnMenu
->insertItem(i18n("Define &Synonym"),
410 this,SLOT(popupDefineLink()));
411 rightBtnMenu
->insertItem(i18n("M&atch Synonym"),
412 this,SLOT(popupMatchLink()));
413 rightBtnMenu
->insertSeparator();
416 rightBtnMenu
->insertItem(i18n("D&atabase Information"),this,SLOT(popupDbInfo()));
417 rightBtnMenu
->insertSeparator();
419 if(type
=="realhttp") {
420 popupLink
.prepend("http://");
421 rightBtnMenu
->insertItem(SmallIcon("fileopen"),
423 this,SLOT(popupOpenLink()));
424 rightBtnMenu
->insertSeparator();
426 if(type
=="realftp") {
427 popupLink
.prepend("ftp://");
428 rightBtnMenu
->insertItem(SmallIcon("fileopen"),
430 this,SLOT(popupOpenLink()));
431 rightBtnMenu
->insertSeparator();
436 if (part
->hasSelection()) {
437 popupSelect
= part
->selectedText();
438 rightBtnMenu
->insertItem(i18n("&Define Selection"),
439 this,SLOT(popupDefineSelect()));
440 rightBtnMenu
->insertItem(i18n("&Match Selection"),
441 this,SLOT(popupMatchSelect()));
442 rightBtnMenu
->insertSeparator();
444 kapp
->clipboard()->setSelectionMode(false);
445 QString text
= kapp
->clipboard()->text();
446 if (text
.isEmpty()) {
447 kapp
->clipboard()->setSelectionMode(true);
448 text
= kapp
->clipboard()->text();
450 if (!text
.isEmpty()) {
451 popupSelect
= QApplication::clipboard()->text();
452 rightBtnMenu
->insertItem(SmallIcon("define_clip"),
453 i18n("&Define Clipboard Content"),
454 this,SLOT(popupDefineSelect()));
455 rightBtnMenu
->insertItem(i18n("&Match Clipboard Content"),
456 this,SLOT(popupMatchSelect()));
457 rightBtnMenu
->insertSeparator();
463 if (browseBackPossible()) { // if possible, show string
464 if (browseList
.at(browsePos
-1)->queryText
.isEmpty())
465 rightBtnMenu
->insertItem(SmallIcon("back"),
466 i18n("&Back: Information"),
467 this,SLOT(browseBack()));
469 rightBtnMenu
->insertItem(SmallIcon("back"),
470 i18n("&Back: '%1'").arg(getShortString(browseList
.at(browsePos
-1)->queryText
,25)),
471 this,SLOT(browseBack()));
473 ID
= rightBtnMenu
->insertItem(SmallIcon("back"), i18n("&Back"), this, SLOT(browseBack()));
474 rightBtnMenu
->setItemEnabled(ID
,false);
477 if (browseForwardPossible()) { // if possible, show string
478 if (browseList
.at(browsePos
+1)->queryText
.isEmpty())
479 rightBtnMenu
->insertItem(SmallIcon("forward"),
480 i18n("&Forward: Information"),
481 this,SLOT(browseForward()));
483 rightBtnMenu
->insertItem(SmallIcon("forward"),
484 i18n("&Forward: '%1'").arg(getShortString(browseList
.at(browsePos
+1)->queryText
,25)),
485 this,SLOT(browseForward()));
487 ID
= rightBtnMenu
->insertItem(SmallIcon("forward"),i18n("&Forward"),this,SLOT(browseForward()));
488 rightBtnMenu
->setItemEnabled(ID
,false);
491 rightBtnMenu
->popup(point
);
495 void QueryView::popupDefineLink()
497 emit(defineRequested(popupLink
));
501 void QueryView::popupMatchLink()
503 emit(matchRequested(popupLink
));
507 void QueryView::popupOpenLink()
509 kapp
->invokeBrowser(popupLink
);
513 void QueryView::popupDefineSelect()
515 emit(defineRequested(popupSelect
));
519 void QueryView::popupMatchSelect()
521 emit(matchRequested(popupSelect
));
525 void QueryView::popupDbInfo()
528 interface
->showDbInfo(popupLink
.utf8());
532 void QueryView::enableAction(const char * name
, bool enabled
)
534 if (!strcmp(name
,"copy"))
535 emit(enableCopy(enabled
));
539 void QueryView::browseBack(int index
)
541 int x
= browsePos
-index
;
543 saveCurrentResultPos();
545 actQueryCombo
->setEditText(browseList
.at(browsePos
)->queryText
);
547 QTimer::singleShot(0, this, SLOT(updateBrowseActions())); // don't clear the menu in this slot
552 void QueryView::browseForward(int index
)
554 int x
= browsePos
+index
;
555 if (x
< (int)(browseList
.count())) {
556 saveCurrentResultPos();
558 actQueryCombo
->setEditText(browseList
.at(browsePos
)->queryText
);
560 QTimer::singleShot(0, this, SLOT(updateBrowseActions())); // don't clear the menu in this slot
565 void QueryView::updateBrowseActions()
567 if (browseBackPossible()) {
568 actBack
->setEnabled(true);
569 if (browseList
.at(browsePos
-1)->queryText
.isEmpty())
570 actBack
->setText(i18n("&Back: Information"));
572 actBack
->setText(i18n("&Back: '%1'").arg(getShortString(browseList
.at(browsePos
-1)->queryText
,25)));
574 actBack
->popupMenu()->clear();
578 while ((i
>=0)&&(num
<=10)) {
579 s
= browseList
.at(i
)->queryText
;
580 if (s
.isEmpty()) s
= i18n("Information");
581 actBack
->popupMenu()->insertItem(s
,num
);
586 actBack
->setEnabled(false);
587 actBack
->setText(i18n("&Back"));
588 actBack
->popupMenu()->clear();
591 if (browseForwardPossible()) {
592 actForward
->setEnabled(true);
593 if (browseList
.at(browsePos
+1)->queryText
.isEmpty())
594 actForward
->setText(i18n("&Forward: Information"));
596 actForward
->setText(i18n("&Forward: '%1'").arg(getShortString(browseList
.at(browsePos
+1)->queryText
,25)));
598 actForward
->popupMenu()->clear();
602 while ((i
<(int)(browseList
.count()))&&(num
<=10)) {
603 s
= browseList
.at(i
)->queryText
;
604 if (s
.isEmpty()) s
= i18n("Information");
605 actForward
->popupMenu()->insertItem(s
,num
);
610 actForward
->setEnabled(false);
611 actForward
->setText(i18n("&Forward"));
612 actForward
->popupMenu()->clear();
616 //--------------------------------
618 #include "queryview.moc"