You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
619 lines
17 KiB
619 lines
17 KiB
/* -------------------------------------------------------------
|
|
|
|
queryview.cpp (part of The KDE Dictionary Client)
|
|
|
|
Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
|
|
|
|
This file is distributed under the Artistic License.
|
|
See LICENSE for details.
|
|
|
|
-------------------------------------------------------------
|
|
|
|
BrowseData data structure used for caching definitions
|
|
DictHTMLPart handling of middle mouse button clicks
|
|
QueryView widget that displays the definitions
|
|
|
|
------------------------------------------------------------- */
|
|
|
|
#include <qclipboard.h>
|
|
#include <qtimer.h>
|
|
|
|
#include <kfiledialog.h>
|
|
#include <ktempfile.h>
|
|
#include <kio/netaccess.h>
|
|
#include <kcursor.h>
|
|
#include <kpopupmenu.h>
|
|
#include <kmessagebox.h>
|
|
#include <klocale.h>
|
|
#include <khtml_events.h>
|
|
#include <khtmlview.h>
|
|
|
|
#include "actions.h"
|
|
#include "options.h"
|
|
#include "dict.h"
|
|
#include "queryview.h"
|
|
#include <kapplication.h>
|
|
#include <kiconloader.h>
|
|
|
|
|
|
//******** SaveHelper *******************************************
|
|
|
|
QString SaveHelper::lastPath;
|
|
|
|
SaveHelper::SaveHelper(const QString &saveName, const QString &filter, QWidget *parent)
|
|
: p_arent(parent), s_aveName(saveName), f_ilter(filter), file(0), tmpFile(0)
|
|
{
|
|
}
|
|
|
|
|
|
SaveHelper::~SaveHelper()
|
|
{
|
|
if (file) { // local filesystem, just close the file
|
|
delete file;
|
|
} else
|
|
if (tmpFile) { // network location, initiate transaction
|
|
tmpFile->close();
|
|
if (KIO::NetAccess::upload(tmpFile->name(),url, p_arent) == false)
|
|
KMessageBox::error(global->topLevel, i18n("Unable to save remote file."));
|
|
tmpFile->unlink(); // delete temp file
|
|
delete tmpFile;
|
|
}
|
|
}
|
|
|
|
|
|
QFile* SaveHelper::getFile(const QString &dialogTitle)
|
|
{
|
|
url = KFileDialog::getSaveURL(lastPath+s_aveName,f_ilter,p_arent,dialogTitle);
|
|
|
|
if (url.isEmpty())
|
|
return 0;
|
|
|
|
lastPath = url.url(-1);
|
|
lastPath.truncate(lastPath.length()-url.fileName().length());
|
|
|
|
if (url.isLocalFile()) {
|
|
if (QFileInfo(url.path()).exists() &&
|
|
(KMessageBox::warningContinueCancel(global->topLevel,
|
|
i18n("A file named %1 already exists.\nDo you want to replace it?").arg(url.path()),
|
|
dialogTitle, i18n("&Replace")) != KMessageBox::Continue)) {
|
|
return 0;
|
|
}
|
|
|
|
file = new QFile(url.path());
|
|
if(!file->open(IO_WriteOnly)) {
|
|
KMessageBox::error(global->topLevel, i18n("Unable to save file."));
|
|
delete file;
|
|
file = 0;
|
|
}
|
|
return file;
|
|
} else {
|
|
tmpFile = new KTempFile();
|
|
if (tmpFile->status()!=0)
|
|
KMessageBox::error(global->topLevel, i18n("Unable to create temporary file.")); {
|
|
delete tmpFile;
|
|
tmpFile = 0;
|
|
return 0;
|
|
}
|
|
return tmpFile->file();
|
|
}
|
|
}
|
|
|
|
|
|
//**** BrowseData ****************************************************
|
|
|
|
|
|
BrowseData::BrowseData(const QString &Nhtml, const QString &NqueryText)
|
|
: html(Nhtml),queryText(NqueryText),xPos(0),yPos(0)
|
|
{}
|
|
|
|
|
|
//********* DictHTMLPart ******************************************
|
|
|
|
DictHTMLPart::DictHTMLPart(QWidget *parentWidget, const char *widgetname)
|
|
: KHTMLPart(parentWidget,widgetname)
|
|
{}
|
|
|
|
|
|
DictHTMLPart::~DictHTMLPart()
|
|
{}
|
|
|
|
|
|
void DictHTMLPart::khtmlMouseReleaseEvent(khtml::MouseReleaseEvent *event)
|
|
{
|
|
if (event->qmouseEvent()->button()==MidButton)
|
|
emit(middleButtonClicked());
|
|
else
|
|
KHTMLPart::khtmlMouseReleaseEvent(event);
|
|
}
|
|
|
|
|
|
//********* QueryView ******************************************
|
|
|
|
|
|
QueryView::QueryView(QWidget *_parent)
|
|
: QVBox( _parent), actBack(0L), actForward(0L), actQueryCombo(0L), browsePos(0), isRendering(false)
|
|
{
|
|
browseList.setAutoDelete(true);
|
|
|
|
part=new DictHTMLPart(this);
|
|
part->setDNDEnabled(false);
|
|
part->setJScriptEnabled(false);
|
|
part->setJavaEnabled(false);
|
|
part->setURLCursor(KCursor::handCursor());
|
|
setFocusPolicy(QWidget::NoFocus);
|
|
connect(part, SIGNAL(completed()), SLOT(partCompleted()));
|
|
connect(part, SIGNAL(middleButtonClicked()), SLOT(middleButtonClicked()));
|
|
rightBtnMenu = new KPopupMenu(this);
|
|
connect(part,SIGNAL(popupMenu(const QString &, const QPoint &)),this,SLOT(buildPopupMenu(const QString &, const QPoint &)));
|
|
connect(part->browserExtension(),SIGNAL(openURLRequest(const KURL &,const KParts::URLArgs &)),
|
|
this,SLOT(slotURLRequest(const KURL &,const KParts::URLArgs &)));
|
|
connect(part->browserExtension(),SIGNAL(enableAction(const char *,bool)),this,SLOT(enableAction(const char *,bool)));
|
|
QTimer::singleShot( 0, this, SLOT(optionsChanged()) );
|
|
connect(interface,SIGNAL(resultReady(const QString &, const QString &)), SLOT(resultReady(const QString &, const QString &)));
|
|
}
|
|
|
|
|
|
QueryView::~QueryView()
|
|
{}
|
|
|
|
|
|
void QueryView::setActions(KToolBarPopupAction* NactBack, KToolBarPopupAction* NactForward, DictComboAction* NactQueryCombo)
|
|
{
|
|
actBack = NactBack;
|
|
connect(actBack->popupMenu(),SIGNAL(activated(int)),SLOT(browseBack(int)));
|
|
actForward = NactForward;
|
|
connect(actForward->popupMenu(),SIGNAL(activated(int)),SLOT(browseForward(int)));
|
|
actQueryCombo = NactQueryCombo;
|
|
}
|
|
|
|
|
|
bool QueryView::browseBackPossible() const
|
|
{
|
|
return (browsePos > 0)? true:false;
|
|
}
|
|
|
|
|
|
bool QueryView::browseForwardPossible() const
|
|
{
|
|
return (browsePos+1 < browseList.count()) ? true:false;
|
|
}
|
|
|
|
|
|
void QueryView::optionsChanged()
|
|
{
|
|
saveCurrentResultPos();
|
|
|
|
currentHTMLHeader = QString("<html><head><style type=\"text/css\">\n");
|
|
currentHTMLHeader += QString("body { background-color:%1; color:%2; }\n").arg(global->backgroundColor().name()).arg(global->textColor().name());
|
|
currentHTMLHeader += QString("a:link, a:active { color:%1; text-decoration:none; }\n").arg(global->linksColor().name());
|
|
currentHTMLHeader += QString("a:visited { color:%1; text-decoration:none; }\n").arg(global->visitedLinksColor().name());
|
|
currentHTMLHeader += QString("a:hover { color:%1; text-decoration:underline; }\n").arg(global->linksColor().name());
|
|
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());
|
|
if (global->headingsFont().italic())
|
|
currentHTMLHeader += QString("font-style:italic; ");
|
|
if (global->headingsFont().bold())
|
|
currentHTMLHeader += QString("font-weight:bold; ");
|
|
currentHTMLHeader += QString("}\n");
|
|
currentHTMLHeader += QString("p.definition { margin-left:1.5mm; margin-top:2mm; margin-bottom:2mm;}\n");
|
|
currentHTMLHeader += QString("table { margin-left:1.5mm; margin-top:2mm; margin-bottom:2mm;}\n");
|
|
currentHTMLHeader += QString("pre { font-family:%1; font-size:%2pt; ").arg(global->textFont().family()).arg(global->textFont().pointSize());
|
|
if (global->textFont().italic())
|
|
currentHTMLHeader += QString("font-style:italic; ");
|
|
if (global->textFont().bold())
|
|
currentHTMLHeader += QString("font-weight:bold; ");
|
|
currentHTMLHeader += QString("}\n");
|
|
currentHTMLHeader += QString("</style></head>\n");
|
|
|
|
showResult(); // apply changes directly
|
|
}
|
|
|
|
|
|
void QueryView::stop()
|
|
{
|
|
if (isRendering == true) {
|
|
part->closeURL();
|
|
isRendering = false;
|
|
emit(renderingStopped());
|
|
}
|
|
}
|
|
|
|
|
|
// print current result
|
|
void QueryView::printQuery()
|
|
{
|
|
part->view()->print();
|
|
}
|
|
|
|
|
|
// save the current result in an .html file
|
|
void QueryView::saveQuery()
|
|
{
|
|
if (!browseList.isEmpty()) {
|
|
BrowseData* brw = browseList.at(browsePos);
|
|
QString fName = brw->queryText+".html";
|
|
fName.replace(QRegExp("[\\s/]"),"_");
|
|
SaveHelper helper(fName,"*.html",global->topLevel);
|
|
QFile *file = helper.getFile(QString::null);
|
|
|
|
if (file) {
|
|
QTextStream stream(file);
|
|
stream.setEncoding(QTextStream::Locale);
|
|
stream << currentHTMLHeader+brw->html;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::browseBack()
|
|
{
|
|
if (browseBackPossible()) {
|
|
saveCurrentResultPos();
|
|
browsePos--;
|
|
actQueryCombo->setEditText(browseList.at(browsePos)->queryText);
|
|
showResult();
|
|
updateBrowseActions();
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::browseForward()
|
|
{
|
|
if (browseForwardPossible()) {
|
|
saveCurrentResultPos();
|
|
browsePos++;
|
|
actQueryCombo->setEditText(browseList.at(browsePos)->queryText);
|
|
showResult();
|
|
updateBrowseActions();
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::selectAll()
|
|
{
|
|
part->selectAll();
|
|
}
|
|
|
|
|
|
void QueryView::copySelection()
|
|
{
|
|
kapp->clipboard()->setText(part->selectedText());
|
|
}
|
|
|
|
|
|
void QueryView::showFindDialog()
|
|
{
|
|
KAction *act = part->actionCollection()->action("find");
|
|
if (act)
|
|
act->activate();
|
|
}
|
|
|
|
|
|
void QueryView::paletteChange ( const QPalette & )
|
|
{
|
|
|
|
optionsChanged();
|
|
}
|
|
|
|
|
|
void QueryView::fontChange ( const QFont & )
|
|
{
|
|
optionsChanged();
|
|
}
|
|
|
|
|
|
void QueryView::saveCurrentResultPos()
|
|
{
|
|
if (!browseList.isEmpty()) {
|
|
browseList.at(browsePos)->xPos = part->view()->contentsX();
|
|
browseList.at(browsePos)->yPos = part->view()->contentsY();
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::showResult()
|
|
{
|
|
if (!isRendering) {
|
|
isRendering = true;
|
|
emit(renderingStarted());
|
|
}
|
|
|
|
part->begin();
|
|
if (browseList.isEmpty()) {
|
|
part->write(currentHTMLHeader+"<body></body></html>");
|
|
part->end();
|
|
} else {
|
|
BrowseData* brw = browseList.at(browsePos);
|
|
emit(newCaption(getShortString(brw->queryText.simplifyWhiteSpace(),70)));
|
|
part->write(currentHTMLHeader+brw->html);
|
|
part->end();
|
|
part->view()->setFocus();
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::resultReady(const QString &result, const QString &query)
|
|
{
|
|
BrowseData* brw = new BrowseData(result,query);
|
|
|
|
if (browseList.isEmpty()) {
|
|
browsePos = 0;
|
|
browseList.append(brw);
|
|
} else {
|
|
saveCurrentResultPos();
|
|
while (browseList.count()>browsePos+1)
|
|
browseList.removeLast();
|
|
browseList.append(brw);
|
|
browsePos++;
|
|
while (browseList.count()>global->maxBrowseListEntrys) {
|
|
browseList.removeFirst();
|
|
browsePos--;
|
|
}
|
|
}
|
|
|
|
showResult();
|
|
emit(enablePrintSave());
|
|
actQueryCombo->selectAll();
|
|
updateBrowseActions();
|
|
}
|
|
|
|
|
|
void QueryView::partCompleted()
|
|
{
|
|
if (!browseList.isEmpty())
|
|
part->view()->setContentsPos(browseList.at(browsePos)->xPos,browseList.at(browsePos)->yPos);
|
|
if (isRendering) {
|
|
emit(renderingStopped());
|
|
isRendering = false;
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::slotURLRequest (const KURL &url, const KParts::URLArgs &)
|
|
{
|
|
QString type = url.host();
|
|
QString urlText = url.prettyURL();
|
|
urlText.remove(0,8+type.length());
|
|
|
|
if (type.length()) { // valid url
|
|
if(type=="define")
|
|
emit(defineRequested(urlText));
|
|
if(type=="dbinfo")
|
|
interface->showDbInfo(urlText.utf8());
|
|
if(type=="realhttp")
|
|
kapp->invokeBrowser("http://"+urlText);
|
|
if(type=="realftp")
|
|
kapp->invokeBrowser("ftp://"+urlText);
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::middleButtonClicked()
|
|
{
|
|
emit(clipboardRequested());
|
|
}
|
|
|
|
|
|
// construct the right-mouse-button-popup-menu on demand
|
|
void QueryView::buildPopupMenu(const QString &url, const QPoint &point)
|
|
{
|
|
rightBtnMenu->clear();
|
|
|
|
if (!url.isEmpty()) { // menuitem if mouse is over link
|
|
KURL u(url);
|
|
QString type = u.host();
|
|
popupLink = u.prettyURL();
|
|
popupLink.remove(0,8+type.length());
|
|
|
|
if (type.length()) { // valid url
|
|
if(type=="define") {
|
|
rightBtnMenu->insertItem(i18n("Define &Synonym"),
|
|
this,SLOT(popupDefineLink()));
|
|
rightBtnMenu->insertItem(i18n("M&atch Synonym"),
|
|
this,SLOT(popupMatchLink()));
|
|
rightBtnMenu->insertSeparator();
|
|
}
|
|
if(type=="dbinfo") {
|
|
rightBtnMenu->insertItem(i18n("D&atabase Information"),this,SLOT(popupDbInfo()));
|
|
rightBtnMenu->insertSeparator();
|
|
}
|
|
if(type=="realhttp") {
|
|
popupLink.prepend("http://");
|
|
rightBtnMenu->insertItem(SmallIcon("fileopen"),
|
|
i18n("&Open Link"),
|
|
this,SLOT(popupOpenLink()));
|
|
rightBtnMenu->insertSeparator();
|
|
}
|
|
if(type=="realftp") {
|
|
popupLink.prepend("ftp://");
|
|
rightBtnMenu->insertItem(SmallIcon("fileopen"),
|
|
i18n("&Open Link"),
|
|
this,SLOT(popupOpenLink()));
|
|
rightBtnMenu->insertSeparator();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (part->hasSelection()) {
|
|
popupSelect = part->selectedText();
|
|
rightBtnMenu->insertItem(i18n("&Define Selection"),
|
|
this,SLOT(popupDefineSelect()));
|
|
rightBtnMenu->insertItem(i18n("&Match Selection"),
|
|
this,SLOT(popupMatchSelect()));
|
|
rightBtnMenu->insertSeparator();
|
|
} else {
|
|
kapp->clipboard()->setSelectionMode(false);
|
|
QString text = kapp->clipboard()->text();
|
|
if (text.isEmpty()) {
|
|
kapp->clipboard()->setSelectionMode(true);
|
|
text = kapp->clipboard()->text();
|
|
}
|
|
if (!text.isEmpty()) {
|
|
popupSelect = QApplication::clipboard()->text();
|
|
rightBtnMenu->insertItem(SmallIcon("define_clip"),
|
|
i18n("&Define Clipboard Content"),
|
|
this,SLOT(popupDefineSelect()));
|
|
rightBtnMenu->insertItem(i18n("&Match Clipboard Content"),
|
|
this,SLOT(popupMatchSelect()));
|
|
rightBtnMenu->insertSeparator();
|
|
}
|
|
}
|
|
|
|
int ID;
|
|
|
|
if (browseBackPossible()) { // if possible, show string
|
|
if (browseList.at(browsePos-1)->queryText.isEmpty())
|
|
rightBtnMenu->insertItem(SmallIcon("back"),
|
|
i18n("&Back: Information"),
|
|
this,SLOT(browseBack()));
|
|
else
|
|
rightBtnMenu->insertItem(SmallIcon("back"),
|
|
i18n("&Back: '%1'").arg(getShortString(browseList.at(browsePos-1)->queryText,25)),
|
|
this,SLOT(browseBack()));
|
|
} else {
|
|
ID = rightBtnMenu->insertItem(SmallIcon("back"), i18n("&Back"), this, SLOT(browseBack()));
|
|
rightBtnMenu->setItemEnabled(ID,false);
|
|
}
|
|
|
|
if (browseForwardPossible()) { // if possible, show string
|
|
if (browseList.at(browsePos+1)->queryText.isEmpty())
|
|
rightBtnMenu->insertItem(SmallIcon("forward"),
|
|
i18n("&Forward: Information"),
|
|
this,SLOT(browseForward()));
|
|
else
|
|
rightBtnMenu->insertItem(SmallIcon("forward"),
|
|
i18n("&Forward: '%1'").arg(getShortString(browseList.at(browsePos+1)->queryText,25)),
|
|
this,SLOT(browseForward()));
|
|
} else {
|
|
ID = rightBtnMenu->insertItem(SmallIcon("forward"),i18n("&Forward"),this,SLOT(browseForward()));
|
|
rightBtnMenu->setItemEnabled(ID,false);
|
|
}
|
|
|
|
rightBtnMenu->popup(point);
|
|
}
|
|
|
|
|
|
void QueryView::popupDefineLink()
|
|
{
|
|
emit(defineRequested(popupLink));
|
|
}
|
|
|
|
|
|
void QueryView::popupMatchLink()
|
|
{
|
|
emit(matchRequested(popupLink));
|
|
}
|
|
|
|
|
|
void QueryView::popupOpenLink()
|
|
{
|
|
kapp->invokeBrowser(popupLink);
|
|
}
|
|
|
|
|
|
void QueryView::popupDefineSelect()
|
|
{
|
|
emit(defineRequested(popupSelect));
|
|
}
|
|
|
|
|
|
void QueryView::popupMatchSelect()
|
|
{
|
|
emit(matchRequested(popupSelect));
|
|
}
|
|
|
|
|
|
void QueryView::popupDbInfo()
|
|
{
|
|
|
|
interface->showDbInfo(popupLink.utf8());
|
|
}
|
|
|
|
|
|
void QueryView::enableAction(const char * name, bool enabled)
|
|
{
|
|
if (!strcmp(name,"copy"))
|
|
emit(enableCopy(enabled));
|
|
}
|
|
|
|
|
|
void QueryView::browseBack(int index)
|
|
{
|
|
int x = browsePos-index;
|
|
if (x>=0) {
|
|
saveCurrentResultPos();
|
|
browsePos = x;
|
|
actQueryCombo->setEditText(browseList.at(browsePos)->queryText);
|
|
showResult();
|
|
QTimer::singleShot(0, this, SLOT(updateBrowseActions())); // don't clear the menu in this slot
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::browseForward(int index)
|
|
{
|
|
int x = browsePos+index;
|
|
if (x < (int)(browseList.count())) {
|
|
saveCurrentResultPos();
|
|
browsePos = x;
|
|
actQueryCombo->setEditText(browseList.at(browsePos)->queryText);
|
|
showResult();
|
|
QTimer::singleShot(0, this, SLOT(updateBrowseActions())); // don't clear the menu in this slot
|
|
}
|
|
}
|
|
|
|
|
|
void QueryView::updateBrowseActions()
|
|
{
|
|
if (browseBackPossible()) {
|
|
actBack->setEnabled(true);
|
|
if (browseList.at(browsePos-1)->queryText.isEmpty())
|
|
actBack->setText(i18n("&Back: Information"));
|
|
else
|
|
actBack->setText(i18n("&Back: '%1'").arg(getShortString(browseList.at(browsePos-1)->queryText,25)));
|
|
|
|
actBack->popupMenu()->clear();
|
|
int i = browsePos-1;
|
|
int num = 1;
|
|
QString s;
|
|
while ((i>=0)&&(num<=10)) {
|
|
s = browseList.at(i)->queryText;
|
|
if (s.isEmpty()) s = i18n("Information");
|
|
actBack->popupMenu()->insertItem(s,num);
|
|
num++;
|
|
i--;
|
|
}
|
|
} else {
|
|
actBack->setEnabled(false);
|
|
actBack->setText(i18n("&Back"));
|
|
actBack->popupMenu()->clear();
|
|
}
|
|
|
|
if (browseForwardPossible()) {
|
|
actForward->setEnabled(true);
|
|
if (browseList.at(browsePos+1)->queryText.isEmpty())
|
|
actForward->setText(i18n("&Forward: Information"));
|
|
else
|
|
actForward->setText(i18n("&Forward: '%1'").arg(getShortString(browseList.at(browsePos+1)->queryText,25)));
|
|
|
|
actForward->popupMenu()->clear();
|
|
int i = browsePos+1;
|
|
int num = 1;
|
|
QString s;
|
|
while ((i<(int)(browseList.count()))&&(num<=10)) {
|
|
s = browseList.at(i)->queryText;
|
|
if (s.isEmpty()) s = i18n("Information");
|
|
actForward->popupMenu()->insertItem(s,num);
|
|
num++;
|
|
i++;
|
|
}
|
|
} else {
|
|
actForward->setEnabled(false);
|
|
actForward->setText(i18n("&Forward"));
|
|
actForward->popupMenu()->clear();
|
|
}
|
|
}
|
|
|
|
//--------------------------------
|
|
|
|
#include "queryview.moc"
|