diff options
Diffstat (limited to 'kdict')
41 files changed, 7570 insertions, 0 deletions
diff --git a/kdict/AUTHORS b/kdict/AUTHORS new file mode 100644 index 00000000..b81599fe --- /dev/null +++ b/kdict/AUTHORS @@ -0,0 +1,25 @@ +Developers: +Christian Gebauer <gebauer@kde.org> +Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + +Documentation: +Christian Gebauer <gebauer@kde.org> + +Translators: +Wolfram Diestel <wolfram@steloj.de> +Otto Bruggeman <otto.bruggeman@home.nl> +Mattias Newzella <newzella@linux.nu> +Andris Maziks <andris.m@delfi.lv> +Jaime Robles <jaime@kde.org> +Pedro Morais <morais@kde.org> +Andrey S. Cherepanov <andrey_tiger@i.am> +Claudiu Costin <claudiuc@geocities.com> +Erik Kjær Pedersen <erik@binghamton.edu> +Stanislav Visnovsky <visnovsky@nenya.ms.mff.cuni.cz> +Andy Rysin <arysin@yahoo.com> +Dimitris Kamenopoulos <el97146@mail.ntua.gr> +Meni Livne <meni@mail.com> +Matthias Kiefer <kiefer@kde.org> +Sinohara <shinobo@leo.bekkoame.ne.jp> +Tamas Szanto <tszanto@mol.hu> +Görkem Çetin <gorkem@kde.org>
\ No newline at end of file diff --git a/kdict/ChangeLog b/kdict/ChangeLog new file mode 100644 index 00000000..d9513c75 --- /dev/null +++ b/kdict/ChangeLog @@ -0,0 +1,79 @@ +0.5.3 + + recognize http and ftp urls in cross references + + convert characters that are incompatible with html + like <,>,& into &, etc. + +0.5.2 + + Kdict is now part of the offical kdenetwork package + + support for IPv6 and socks-proxies + + up-to-date docbook-handbook + + code cleanup + +0.5 + + dcop interface + + panel applet + + bugfixes: session management, small problems + with the toolbar editor + +0.4.1 + + fixed CSS-issues with KDE 2.0.x + + fixed saving of results as html file + +0.4 + + ported to KDE2 + + full unicode support + + improved portability, should work in non-linux environments now + + the german gui-translation is missing in this release, + I will add it later on + + documentation is not yet converted to docbook + +0.3.1 + + essential bugfix for older, libc5-based linux systems + (Thanks to Osvaldo Fornaro for testing) + + mini-howto for installing a local DICT-server + +0.3 + + integrated implementation of the DICT-protocol, + dict(1) is no longer needed. + + match mode: At first all matches for a query are + displayed in a separate list, the user can then + choose the most interesting definitions. + + database sets: The user can create individual + compilations of the available databases and use + them in the same way as a single database. + + nicer/more configurable html-layout + + find dialog for locating text in the definitions + + the user can paste text into Kdict with the middle + mouse button. Kdict starts a new query with the pasted text + (analogue to Netscape). + + command to show detailed database information is + implemented ("show info") + + speed improvements: + * faster html-generation + * results are stored in a local cache, so they + are displayed instantly when the users wants to + browse back. + * Kdict holds the connection to the server + for a configurable amount of time. + + various gui improvements: + * improved statusbar + * unified preferences dialog + * all toolbars are now removable and remember + their position. + * new toolbar icons + + ... + +0.2 + fixed some bugs (and introduced new ones ;-) + added various features: + + saving/printing of the queryresult + + query history + + improved html output + + improved command line interface + + html help + + Preferences dialogbox for fonts, colors, etc. + + Synonyms get displayed and handled like hyperlinks + + Automatically lookup of a word in the X clipboard + + ... + +0.1 Initial release diff --git a/kdict/LICENSE b/kdict/LICENSE new file mode 100644 index 00000000..4def90fc --- /dev/null +++ b/kdict/LICENSE @@ -0,0 +1,125 @@ +kdict - The KDE Dictionary Client + +Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org> +copyright (C) 1998 by Matthias Hoelzer <hoelzer@kde.org> + + + Preamble + + The intent of this document is to state the conditions under which a + Package may be copied, such that the Copyright Holder maintains some + semblance of artistic control over the development of the package, + while giving the users of the package the right to use and + distribute the Package in a more-or-less customary fashion, plus the + right to make reasonable modifications. + + Definitions: + + * "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + * "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes of + the Copyright Holder. + + * "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + * "You" is you, if you're thinking about copying or distributing + this Package. + + * "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people + involved, and so on. (You will not be required to justify it to + the Copyright Holder, but only to the computing community at + large as a market that must bear the fee.) + + * "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + + 1. You may make and give away verbatim copies of the source form of + the Standard Version of this Package without restriction, provided + that you duplicate all of the original copyright notices and + associated disclaimers. + + 2. You may apply bug fixes, portability fixes and other + modifications derived from the Public Domain or from the Copyright + Holder. A Package modified in such a way shall still be considered + the Standard Version. + + 3. You may otherwise modify your copy of this Package in any way, + provided that you insert a prominent notice in each changed file + stating how and when you changed that file, and provided that you do + at least ONE of the following: + + a) place your modifications in the Public Domain or + otherwise make them Freely Available, such as by posting + said modifications to Usenet or an equivalent medium, or + placing the modifications on a major archive site such as + ftp.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation + or organization. + + c) rename any non-standard executables so the names do not + conflict with standard executables, which must also be + provided, and provide a separate manual page for each + non-standard executable that clearly documents how it + differs from the Standard Version. + + d) make other distribution arrangements with the Copyright + Holder. + + 4. You may distribute the programs of this Package in object code or + executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and + library files, together with instructions (in the manual + page or equivalent) on where to get the Standard Version. + + b) accompany the distribution with the machine-readable + source of the Package with your modifications. + + c) accompany any non-standard executables with their + corresponding Standard Version executables, giving the + non-standard executables non-standard names, and clearly + documenting the differences in manual pages (or + equivalent), together with instructions on where to get + the Standard Version. + + d) make other distribution arrangements with the Copyright + Holder. + + 5. You may charge a reasonable copying fee for any distribution of + this Package. You may charge any fee you choose for support of this + Package. You may not charge a fee for this Package itself. However, + you may distribute this Package in aggregate with other (possibly + commercial) programs as part of a larger (possibly commercial) + software distribution provided that you do not advertise this + Package as a product of your own. + + 6. The scripts and library files supplied as input to or produced as + output from the programs of this Package do not automatically fall + under the copyright of this Package, but belong to whomever + generated them, and may be sold commercially, and may be aggregated + with this Package. + + 7. C or perl subroutines supplied by you and linked into this + Package shall not be considered part of this Package. + + 8. The name of the Copyright Holder may not be used to endorse or + promote products derived from this software without specific prior + written permission. + + 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End + + + diff --git a/kdict/Makefile.am b/kdict/Makefile.am new file mode 100644 index 00000000..5cb60755 --- /dev/null +++ b/kdict/Makefile.am @@ -0,0 +1,33 @@ +## Makefile.am for kdict + +KDE_CXXFLAGS = $(USE_THREADS) + +SUBDIRS = applet pics + +bin_PROGRAMS = +lib_LTLIBRARIES = +kdeinit_LTLIBRARIES = kdict.la + +# set the include path for X, qt and KDE +AM_CPPFLAGS = $(all_includes) + +kdict_la_LDFLAGS = $(KDE_RPATH) $(all_libraries) -module $(KDE_PLUGIN) +kdict_la_LIBADD = $(LIB_KFILE) $(LIB_KHTML) $(LIBPTHREAD) $(LIBRESOLV) +kdict_la_SOURCES = dcopinterface.skel main.cpp actions.cpp dict.cpp options.cpp \ + queryview.cpp toplevel.cpp sets.cpp matchview.cpp application.cpp + +# these are the headers for your project +noinst_HEADERS = actions.h dict.h options.h queryview.h toplevel.h sets.h matchview.h application.h + +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/kdict.pot + +KDE_ICON = AUTO + +# this is where the kdelnk file will go +xdg_apps_DATA = kdict.desktop + +rcdir = $(kde_datadir)/kdict +rc_DATA = kdictui.rc diff --git a/kdict/README b/kdict/README new file mode 100644 index 00000000..894983ab --- /dev/null +++ b/kdict/README @@ -0,0 +1,47 @@ +Kdict - The KDE Dictionary Client +Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org> +copyright (c) 1998 by Matthias Hoelzer <hoelzer@kde.org> + +maintained by Christian Gebauer <gebauer@kde.org> + +Homepage: http://www.rhrk.uni-kl.de/~gebauerc/kdict/ + +See LICENSE for the license of this programm. + +************************************************************************** + +Kdict is a graphical client for the DICT Protocol. +It enables you to search through dictionary-like databases +for a word or phrase, then displays suitable definitions. + +Kdict trys to ease basic as well as advanced queries. A separate list +offers a convenient way to deal with the enormous number of matching +words that a advanced query can return. + +The remainder of Kdict's user interface resembles a web browser: +For instance, you can jump to the definition of a synonym by +simply clicking on the highlighted word. The back/forward functionality +is also implemented, enabling you to quickly go back to the result of +previous queries. + +Kdict is able to process the content of the X clipboard, so it's easy +to combine Kdict with your web browser or text editor. + +Kdict is now a real client, no additional software is needed. + +If your machine is behind a firewall, has no permanent internet +connection or the server of dict.org is too slow for you, you can +set up your own local server, all you need is available at +http://www.dict.org. +The advantages of a local server are optimal performance and +the ability to install additional databases of your choice. +The handbook contains a small tutorial for installation +and links to databases. + +************************************************************************** + +For installing Kdict you need: + +- KDE 3.x +- POSIX Threads + diff --git a/kdict/TODO b/kdict/TODO new file mode 100644 index 00000000..5a1d2446 --- /dev/null +++ b/kdict/TODO @@ -0,0 +1,2 @@ +* offline-support: + accessing local database-files without running a dictd-server
\ No newline at end of file diff --git a/kdict/actions.cpp b/kdict/actions.cpp new file mode 100644 index 00000000..5ca1aade --- /dev/null +++ b/kdict/actions.cpp @@ -0,0 +1,335 @@ +/* ------------------------------------------------------------- + + actions.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. + + ------------------------------------------------------------- + + DictComboAction, special KAction subclasses used + DictLabelAction, in the toolbar + DictButtonAction + + ------------------------------------------------------------- */ + +#include "actions.h" + +#include <qlabel.h> +#include <qpushbutton.h> + +#include <kcombobox.h> +#include <ktoolbar.h> + + +DictComboAction::DictComboAction( const QString &text, QObject *parent, const char *name, + bool editable, bool autoSized ) + : KAction( text, 0, parent, name ), m_editable(editable), m_autoSized(autoSized), m_compMode(KGlobalSettings::completionMode()) +{ +} + + +DictComboAction::~DictComboAction() +{ +} + + +int DictComboAction::plug( QWidget *widget, int index ) +{ + if ( widget->inherits( "KToolBar" ) ) + { + KToolBar* bar = static_cast<KToolBar*>( widget ); + int id_ = KAction::getToolButtonID(); + + m_combo = new KComboBox(m_editable,bar); + m_combo->setCompletionMode(m_compMode); + + bar->insertWidget( id_, m_combo->sizeHint().width(), m_combo, index ); + bar->setItemAutoSized(id_,m_autoSized); + + if ( m_combo ) { + connect(bar->getCombo(id_), SIGNAL(activated(const QString&)), SLOT(slotComboActivated(const QString&))); + connect(bar->getCombo(id_), SIGNAL(activated(int)), SLOT(slotComboActivated(int))); + + if (m_editable) + m_combo->setInsertionPolicy( QComboBox::NoInsertion ); + } + + addContainer( bar, id_ ); + connect( bar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) ); + return containerCount() - 1; + } + + return -1; +} + + +void DictComboAction::unplug( QWidget *widget ) +{ + if ( widget->inherits( "KToolBar" ) ) + { + KToolBar *bar = (KToolBar *)widget; + + int idx = findContainer( bar ); + + if ( idx != -1 ) + { + bar->removeItem( itemId( idx ) ); + removeContainer( idx ); + } + + return; + } +} + + +QWidget* DictComboAction::widget() +{ + return m_combo; +} + + +void DictComboAction::setFocus() +{ + if (m_combo) + m_combo->setFocus(); +} + + +QString DictComboAction::currentText() const +{ + if (m_combo) + return m_combo->currentText(); + else + return QString::null; +} + +void DictComboAction::selectAll() +{ + if (m_combo) + { + m_combo->lineEdit()->selectAll(); + m_combo->lineEdit()->setFocus(); + } +} + + +void DictComboAction::setEditText(const QString &s) +{ + if (m_combo && m_editable) + m_combo->setEditText(s); +} + + +void DictComboAction::setCurrentItem(int index) +{ + if (m_combo) + m_combo->setCurrentItem(index); +} + + +void DictComboAction::clearEdit() +{ + if (m_combo && m_editable) + m_combo->clearEdit(); +} + + +void DictComboAction::clear() +{ + if (m_combo) { + m_combo->clear(); + if (m_editable && m_combo->completionObject()) + m_combo->completionObject()->clear(); + } +} + + +void DictComboAction::setList(QStringList items) +{ + if (m_combo) { + m_combo->clear(); + m_combo->insertStringList(items); + if (m_editable && m_combo->completionObject()) + m_combo->completionObject()->setItems(items); + if (!m_autoSized) + m_combo->setFixedWidth(m_combo->sizeHint().width()); + } +} + + +KGlobalSettings::Completion DictComboAction::completionMode() +{ + if (m_combo) + return m_combo->completionMode(); + else + return m_compMode; + } + + +void DictComboAction::setCompletionMode(KGlobalSettings::Completion mode) +{ + if (m_combo) + m_combo->setCompletionMode(mode); + else + m_compMode = mode; +} + + +void DictComboAction::slotComboActivated(int i) +{ + emit(activated(i)); +} + + +void DictComboAction::slotComboActivated(const QString &s) +{ + emit(activated(s)); +} + + +//********************************************************************************* + + +DictLabelAction::DictLabelAction( const QString &text, QObject *parent, const char *name ) + : KAction( text, 0, parent, name ) +{ +} + + +DictLabelAction::~DictLabelAction() +{ +} + + +int DictLabelAction::plug( QWidget *widget, int index ) +{ + if ( widget->inherits( "KToolBar" ) ) + { + KToolBar *tb = (KToolBar *)widget; + + int id = KAction::getToolButtonID(); + + QLabel *label = new QLabel( text(), widget, "kde toolbar widget" ); + label->setMinimumWidth(label->sizeHint().width()); + label->setBackgroundMode( Qt::PaletteButton ); + label->setAlignment(AlignCenter | AlignVCenter); + label->adjustSize(); + + tb->insertWidget( id, label->width(), label, index ); + + addContainer( tb, id ); + + connect( tb, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) ); + + m_label = label; + + return containerCount() - 1; + } + + return -1; +} + + +void DictLabelAction::unplug( QWidget *widget ) +{ + if ( widget->inherits( "KToolBar" ) ) + { + KToolBar *bar = (KToolBar *)widget; + + int idx = findContainer( bar ); + + if ( idx != -1 ) + { + bar->removeItem( itemId( idx ) ); + removeContainer( idx ); + } + + return; + } +} + + +void DictLabelAction::setBuddy(QWidget *buddy) +{ + if (m_label && buddy) + m_label->setBuddy(buddy); +} + + +//********************************************************************************* + + +DictButtonAction::DictButtonAction( const QString& text, QObject* receiver, + const char* slot, QObject* parent, const char* name ) + : KAction( text, 0, receiver, slot, parent, name ) +{ +} + + +DictButtonAction::~DictButtonAction() +{ +} + + +int DictButtonAction::plug( QWidget *widget, int index ) +{ + if ( widget->inherits( "KToolBar" ) ) + { + KToolBar *tb = (KToolBar *)widget; + + int id = KAction::getToolButtonID(); + + QPushButton *button = new QPushButton( text(), widget ); + button->adjustSize(); + connect(button,SIGNAL(clicked()),this,SLOT(activate())); + tb->insertWidget( id, button->width(), button, index ); + + addContainer( tb, id ); + + connect( tb, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) ); + + m_button = button; + + return containerCount() - 1; + } + + return -1; +} + + +void DictButtonAction::unplug( QWidget *widget ) +{ + if ( widget->inherits( "KToolBar" ) ) + { + KToolBar *bar = (KToolBar *)widget; + + int idx = findContainer( bar ); + + if ( idx != -1 ) + { + bar->removeItem( itemId( idx ) ); + removeContainer( idx ); + } + } +} + + +int DictButtonAction::widthHint() +{ + if (m_button) + return m_button->sizeHint().width(); + else + return 0; +} + + +void DictButtonAction::setWidth(int width) +{ + if (m_button) + m_button->setFixedWidth(width); +} + +#include "actions.moc" diff --git a/kdict/actions.h b/kdict/actions.h new file mode 100644 index 00000000..568a9f7c --- /dev/null +++ b/kdict/actions.h @@ -0,0 +1,111 @@ +/* ------------------------------------------------------------- + + actions.h (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. + + ------------------------------------------------------------- + + DictComboAction, special KAction subclasses used + DictLabelAction, in the toolbar + DictButtonAction + + ------------------------------------------------------------- */ + +#ifndef _ACTIONS_H_ +#define _ACTIONS_H_ + +#include <qguardedptr.h> +#include <qptrlist.h> +#include <kaction.h> +#include <kglobalsettings.h> + +class KComboBox; +class QLabel; +class QPushButton; + + +class DictComboAction : public KAction +{ + Q_OBJECT + + public: + DictComboAction( const QString& text, QObject* parent, + const char* name, bool editable, bool autoSized ); + ~DictComboAction(); + + virtual int plug( QWidget *w, int index = -1 ); + virtual void unplug( QWidget *w ); + + QWidget* widget(); + void setFocus(); + + QString currentText() const; + void selectAll(); + void setEditText(const QString &s); + void setCurrentItem(int index); + void clearEdit(); + + void clear(); + void setList(QStringList items); + + KGlobalSettings::Completion completionMode(); + void setCompletionMode(KGlobalSettings::Completion mode); + + signals: + void activated(int); + void activated(const QString&); + + private slots: + void slotComboActivated(int); + void slotComboActivated(const QString&); + + private: + QGuardedPtr<KComboBox> m_combo; + bool m_editable, m_autoSized; + KGlobalSettings::Completion m_compMode; +}; + + +class DictLabelAction : public KAction +{ + Q_OBJECT + + public: + DictLabelAction( const QString &text, QObject *parent = 0, const char *name = 0 ); + ~DictLabelAction(); + + virtual int plug( QWidget *widget, int index = -1 ); + virtual void unplug( QWidget *widget ); + + void setBuddy(QWidget *buddy); + + private: + QGuardedPtr<QLabel> m_label; + +}; + + +class DictButtonAction : public KAction +{ + Q_OBJECT + + public: + DictButtonAction( const QString& text, QObject* receiver, + const char* slot, QObject* parent, const char* name ); + ~DictButtonAction(); + + virtual int plug( QWidget *w, int index = -1 ); + virtual void unplug( QWidget *w ); + + int widthHint(); + void setWidth(int width); + + private: + QGuardedPtr<QPushButton> m_button; +}; + +#endif diff --git a/kdict/applet/Makefile.am b/kdict/applet/Makefile.am new file mode 100644 index 00000000..db1f4bc2 --- /dev/null +++ b/kdict/applet/Makefile.am @@ -0,0 +1,19 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kdict_panelapplet.la + +kdict_panelapplet_la_SOURCES = kdictapplet.cpp + +METASOURCES = AUTO +noinst_HEADERS = kdictapplet.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = kdictapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +kdict_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kdict_panelapplet_la_LIBADD = $(LIB_KSYCOCA) $(LIB_KDEUI) + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/kdictapplet.pot diff --git a/kdict/applet/kdictapplet.cpp b/kdict/applet/kdictapplet.cpp new file mode 100644 index 00000000..ad907864 --- /dev/null +++ b/kdict/applet/kdictapplet.cpp @@ -0,0 +1,405 @@ +/* ------------------------------------------------------------- + + kdictapplet.h (part of The KDE Dictionary Client) + + Copyright (C) 2001 Christian Gebauer <gebauer@kde.org> + + The applet is loosely based on the "Run" applet included in KDE. + Copyright (c) 2000 Matthias Elter <elter@kde.org> (Artistic License) + + This file is distributed under the Artistic License. + See LICENSE for details. + + ------------------------------------------------------------- + + PopupBox helper class + DictApplet a small kicker-applet + + ------------------------------------------------------------- */ + +#include <qlabel.h> +#include <qpushbutton.h> +#include <qtimer.h> +#include <qlayout.h> +#include <qtooltip.h> + +#include <kconfig.h> +#include <kcombobox.h> +#include <kiconloader.h> +#include <klocale.h> +#include <dcopclient.h> +#include <kapplication.h> +#include <kprocess.h> + +#include "kdictapplet.h" + +//********* PopupBox ******************************************** + +PopupBox::PopupBox() + : QHBox(0, 0, WStyle_Customize | WType_Popup ), popupEnabled(true) +{ +} + + +PopupBox::~PopupBox() +{} + + +bool PopupBox::showBox() +{ + if (!popupEnabled) // prevents that the popup is shown again immediatly + return false; + else { + show(); + return true; + } +} + + +void PopupBox::hideEvent(QHideEvent *) +{ + emit(hidden()); + popupEnabled = false; + QTimer::singleShot(100, this, SLOT(enablePopup())); +} + + +void PopupBox::enablePopup() +{ + popupEnabled = true; +} + + +//********* DictApplet ******************************************** + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("kdictapplet"); + return new DictApplet(configFile, KPanelApplet::Stretch, 0, parent, "kdictapplet"); + } +} + + +DictApplet::DictApplet(const QString& configFile, Type type, int actions, QWidget *parent, const char *name) + : KPanelApplet(configFile, type, actions, parent, name), waiting(0) +{ + // first the widgets for a horizontal panel + baseWidget = new QWidget(this); + QGridLayout *baseLay = new QGridLayout(baseWidget,2,6,0,1); + + textLabel = new QLabel(i18n("Dictionary:"), baseWidget); + textLabel->setBackgroundOrigin(AncestorOrigin); + QFont f(textLabel->font()); + f.setPixelSize(12); + textLabel->setFont(f); + baseLay->addWidget(textLabel,0,1); + QToolTip::add(textLabel,i18n("Look up a word or phrase with Kdict")); + + iconLabel = new QLabel(baseWidget); + iconLabel->setBackgroundOrigin(AncestorOrigin); + QPixmap pm = KGlobal::iconLoader()->loadIcon("kdict", KIcon::Panel, KIcon::SizeSmall, KIcon::DefaultState, 0L, true); + iconLabel->setPixmap(pm); + baseLay->addWidget(iconLabel,1,0); + iconLabel->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); + iconLabel->setFixedWidth(pm.width()+4); + QToolTip::add(iconLabel,i18n("Look up a word or phrase with Kdict")); + + f.setPixelSize(10); + clipboardBtn = new QPushButton(i18n("C"),baseWidget); + clipboardBtn->setBackgroundOrigin(AncestorOrigin); + clipboardBtn->setFont(f); + clipboardBtn->setFixedSize(16,16); + connect(clipboardBtn, SIGNAL(clicked()), SLOT(queryClipboard())); + baseLay->addWidget(clipboardBtn,0,3); + QToolTip::add(clipboardBtn,i18n("Define selected text")); + + defineBtn = new QPushButton(i18n("D"),baseWidget); + defineBtn->setBackgroundOrigin(AncestorOrigin); + defineBtn->setFont(f); + defineBtn->setFixedSize(16,16); + defineBtn->setEnabled(false); + connect(defineBtn, SIGNAL(clicked()), SLOT(startDefine())); + baseLay->addWidget(defineBtn,0,4); + QToolTip::add(defineBtn,i18n("Define word/phrase")); + + matchBtn = new QPushButton(i18n("M"),baseWidget); + matchBtn->setBackgroundOrigin(AncestorOrigin); + matchBtn->setFont(f); + matchBtn->setFixedSize(16,16); + matchBtn->setEnabled(false); + connect(matchBtn, SIGNAL(clicked()), SLOT(startMatch())); + baseLay->addWidget(matchBtn,0,5); + QToolTip::add(matchBtn,i18n("Find matching definitions")); + + completionObject = new KCompletion(); + + internalCombo = new KHistoryCombo(baseWidget); + internalCombo->setBackgroundOrigin(AncestorOrigin); + internalCombo->setCompletionObject(completionObject); + internalCombo->setFocus(); + internalCombo->clearEdit(); + internalCombo->lineEdit()->installEventFilter( this ); + connect(internalCombo, SIGNAL(returnPressed(const QString&)), SLOT(startQuery(const QString&))); + connect(internalCombo, SIGNAL(textChanged(const QString&)), SLOT(comboTextChanged(const QString&))); + QToolTip::add(internalCombo,i18n("Look up a word or phrase with Kdict")); + + baseLay->addMultiCellWidget(internalCombo,1,1,1,5); + + baseLay->setColStretch(2,1); + + // widgets for a vertical panel + verticalBtn = new QPushButton(this); + connect(verticalBtn, SIGNAL(pressed()), SLOT(showExternalCombo())); + QToolTip::add(verticalBtn,i18n("Look up a word or phrase with Kdict")); + + popupBox = new PopupBox(); + popupBox->setFixedSize(160, 22); + connect(popupBox, SIGNAL(hidden()), SLOT(externalComboHidden())); + externalCombo = new KHistoryCombo(popupBox); + externalCombo->setCompletionObject(completionObject); + connect(externalCombo, SIGNAL(returnPressed(const QString&)), SLOT(startQuery(const QString&))); + externalCombo->setFixedSize(160, externalCombo->sizeHint().height()); + + connect(internalCombo, SIGNAL(completionModeChanged(KGlobalSettings::Completion)), + this, SLOT(updateCompletionMode(KGlobalSettings::Completion))); + connect(externalCombo, SIGNAL(completionModeChanged(KGlobalSettings::Completion)), + this, SLOT(updateCompletionMode(KGlobalSettings::Completion))); + + // restore history and completion list + KConfig *c = config(); + c->setGroup("General"); + + QStringList list = c->readListEntry("Completion list"); + completionObject->setItems(list); + int mode = c->readNumEntry("Completion mode", + KGlobalSettings::completionMode()); + internalCombo->setCompletionMode((KGlobalSettings::Completion)mode); + externalCombo->setCompletionMode((KGlobalSettings::Completion)mode); + + list = c->readListEntry("History list"); + internalCombo->setHistoryItems(list); + externalCombo->setHistoryItems(list); +} + + +DictApplet::~DictApplet() +{ + // save history and completion list + KConfig *c = config(); + c->setGroup("General"); + + QStringList list = completionObject->items(); + c->writeEntry("Completion list", list); + c->writeEntry("Completion mode", (int) internalCombo->completionMode()); + + list = internalCombo->historyItems(); + c->writeEntry("History list", list); + c->sync(); + + delete completionObject; +} + + +int DictApplet::widthForHeight(int height) const +{ + if (height >= 38) + return textLabel->sizeHint().width()+55; + else + return textLabel->sizeHint().width()+25; +} + + +int DictApplet::heightForWidth(int width) const +{ + return width; +} + + +void DictApplet::resizeEvent(QResizeEvent*) +{ + if (orientation() == Horizontal) { + verticalBtn->hide(); + baseWidget->show(); + baseWidget->setFixedSize(width(),height()); + + if (height() < internalCombo->sizeHint().height()) + internalCombo->setFixedHeight(height()); + else + internalCombo->setFixedHeight(internalCombo->sizeHint().height()); + + if (height() >= 38) { + textLabel->show(); + clipboardBtn->show(); + defineBtn->show(); + matchBtn->show(); + iconLabel->hide(); + internalCombo->setFixedWidth(width()); + } else { + textLabel->hide(); + clipboardBtn->hide(); + defineBtn->hide(); + matchBtn->hide(); + iconLabel->show(); + internalCombo->setFixedWidth(width()-iconLabel->width()-1); + } + + baseWidget->updateGeometry(); + } else { // orientation() == Vertical + verticalBtn->show(); + baseWidget->hide(); + verticalBtn->setFixedSize(width(),width()); + + KIcon::StdSizes sz = width() < 32 ? KIcon::SizeSmall : (width() < 48 ? KIcon::SizeMedium : KIcon::SizeLarge); + QPixmap pm = KGlobal::iconLoader()->loadIcon("kdict", KIcon::Panel, sz, KIcon::DefaultState, 0L, true); + verticalBtn->setPixmap(pm); + } +} + + +bool DictApplet::eventFilter( QObject *o, QEvent * e) +{ + if (e->type() == QEvent::MouseButtonRelease) + emit requestFocus(); + + return KPanelApplet::eventFilter(o, e); +} + + +void DictApplet::sendCommand(const QCString &fun, const QString &data) +{ + if (waiting > 0) { + waiting = 1; + delayedFunc = fun.copy(); + delayedData = data; + return; + } + + DCOPClient *client = kapp->dcopClient(); + if (!client->isApplicationRegistered("kdict")) { + KApplication::startServiceByDesktopName("kdict"); + waiting = 1; + delayedFunc = fun.copy(); + delayedData = data; + QTimer::singleShot(100, this, SLOT(sendDelayedCommand())); + return; + } else { + QCStringList list = client->remoteObjects("kdict"); + if (list.findIndex("KDictIface")==-1) { + waiting = 1; + delayedFunc = fun.copy(); + delayedData = data; + QTimer::singleShot(100, this, SLOT(sendDelayedCommand())); + return; + } + } + + client->send("kdict","default",fun,data); +} + + +void DictApplet::sendDelayedCommand() +{ + if (waiting > 100) { // timeout after ten seconds + waiting = 0; + return; + } + + DCOPClient *client = kapp->dcopClient(); + if (!client->isApplicationRegistered("kdict")) { + waiting++; + QTimer::singleShot(100, this, SLOT(sendDelayedCommand())); + return; + } else { + QCStringList list = client->remoteObjects("kdict"); + if (list.findIndex("KDictIface")==-1) { + waiting++; + QTimer::singleShot(100, this, SLOT(sendDelayedCommand())); + return; + } + } + + client->send("kdict","default",delayedFunc,delayedData); + waiting = 0; +} + + +void DictApplet::startQuery(const QString &s) +{ + QString query = s.stripWhiteSpace(); + if (query.isEmpty()) + return; + + internalCombo->addToHistory(query); + externalCombo->addToHistory(query); + internalCombo->clearEdit(); + externalCombo->clearEdit(); + + sendCommand("definePhrase(QString)",query); + + if (orientation() == Vertical) + popupBox->hide(); +} + + +void DictApplet::comboTextChanged(const QString &s) +{ + defineBtn->setEnabled(!s.isEmpty()); + matchBtn->setEnabled(!s.isEmpty()); +} + + +void DictApplet::queryClipboard() +{ + sendCommand("defineClipboardContent()",QString::null); +} + + +void DictApplet::startDefine() +{ + startQuery(internalCombo->currentText()); +} + + +void DictApplet::startMatch() +{ + QString query = internalCombo->currentText().stripWhiteSpace(); + internalCombo->addToHistory(query); + externalCombo->addToHistory(query); + internalCombo->clearEdit(); + externalCombo->clearEdit(); + + sendCommand("matchPhrase(QString)",query); +} + + +void DictApplet::showExternalCombo() +{ + QPoint p; + if (position() == pLeft) + p = mapToGlobal(QPoint(-popupBox->width()-1, 0)); + else + p = mapToGlobal(QPoint(width()+1, 0)); + popupBox->move(p); + if (popupBox->showBox()) + externalCombo->setFocus(); + else + verticalBtn->setDown(false); +} + + +void DictApplet::externalComboHidden() +{ + verticalBtn->setDown(false); +} + +void DictApplet::updateCompletionMode(KGlobalSettings::Completion mode) +{ + internalCombo->setCompletionMode(mode); + externalCombo->setCompletionMode(mode); +} + +//-------------------------------- + +#include "kdictapplet.moc" diff --git a/kdict/applet/kdictapplet.desktop b/kdict/applet/kdictapplet.desktop new file mode 100644 index 00000000..23d26f3f --- /dev/null +++ b/kdict/applet/kdictapplet.desktop @@ -0,0 +1,147 @@ +[Desktop Entry] +Comment=Lookup phrases in a dictionary +Comment[af]=Opkyk frases in 'n woordeboek +Comment[ar]=ابØØ« عن الكلمات ÙÙŠ القاموس +Comment[az]=LüğətdÉ™n kÉ™limÉ™lÉ™rÉ™ baxın +Comment[be]=Пошук выразаў у Ñлоўніку +Comment[bg]=ТърÑене на фрази в речника +Comment[bn]=à¦à¦•à¦Ÿà¦¿ অà¦à¦¿à¦§à¦¾à¦¨à§‡ শবà§à¦¦à¦¸à¦®à¦·à§à¦Ÿà¦¿à¦° খোà¦à¦œ করো +Comment[bs]=Potražite fraze u rjeÄniku +Comment[ca]=Cerca expressions en un diccionari +Comment[cs]=VyhledávaÄ pojmů ve slovnÃku +Comment[cy]=Edrych am ddywediadau mewn geiriadur +Comment[da]=SlÃ¥ sætninger op i en ordbog +Comment[de]=Ausdrücke in einem Lexikon nachschlagen +Comment[el]=Αναζήτηση φÏάσεων σε λεξικό +Comment[eo]=Serĉi kapvortojn en vortaroj +Comment[es]=Busca expresiones en un diccionario +Comment[et]=Fraaside otsimine sõnaraamatust +Comment[eu]=Bilatu esaldiak hiztegi batean +Comment[fa]=مراجعه به واژه‌نامه برای عبارتها +Comment[fi]=Hae lauseita sanakirjasta +Comment[fr]=Recherche de phrases dans un dictionnaire +Comment[ga]=Cuardaigh frásaà i bhfoclóir +Comment[gl]=Buscar expresións no diccionario +Comment[he]=חיפוש ×‘×™×˜×•×™×™× ×‘×ž×™×œ×•×Ÿ +Comment[hi]=शबà¥à¤¦à¤•à¥‹à¤¶ में वाकà¥à¤¯à¤¾à¤‚शों को देखे +Comment[hr]=Potraži fraze u rjeÄniku +Comment[hu]=SzótárkezelÅ‘ alkalmazás +Comment[is]=Fletta upp à orðabók +Comment[it]=Cerca frasi in un dizionario +Comment[ja]=辞書ã§èªžå¥ã‚’検索 +Comment[ka]=ფრáƒáƒ–ების ლექსიკáƒáƒœáƒ¨áƒ˜ ძებნრ+Comment[kk]=Сөздікте іздеу +Comment[km]=រក​មើល​ប្រយោគ​នៅ​ក្នុង​វចនានុក្រម​មួយ +Comment[ko]=ì‚¬ì „ì—ì„œ 글귀를 찾아ì¤ë‹ˆë‹¤ +Comment[lt]=IeÅ¡koti frazių žodyne +Comment[lv]=SkatÄ«t frÄzes vÄrdnÄ«cÄ +Comment[mk]=Барајте за изрази во речник +Comment[mn]=Толь бичигÑÑÑ Ò¯Ð³ харах +Comment[ms]=Mencari frasa di dalam kamus +Comment[mt]=Fittex frażijiet fid-dizzjunarju +Comment[nb]=Finn fraser i en ordbok +Comment[nds]=Begrepen in en Wöörbook nakieken +Comment[ne]=शबà¥à¤¦à¤•à¥‹à¤¶à¤®à¤¾ वाकà¥à¤¯à¤¾à¤‚श खोजी गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Zoek bepaalde uitdrukkingen op in een woordenboek +Comment[nn]=SlÃ¥ opp ord i ei ordboka +Comment[nso]=Nyaka mantsu ka gare ga pukuntsu +Comment[pl]=Szukanie zwrotów w sÅ‚owniku +Comment[pt]=Procura por frases num dicionário +Comment[pt_BR]=Busca frases em um dicionário +Comment[ro]=Caută fraze într-un dicÅ£ionar +Comment[ru]=ПоиÑк Ñлов в Ñловаре +Comment[se]=Oza sániid sátnegirjjis +Comment[sk]=Hľadanie fráz v slovnÃku +Comment[sl]=Iskanje Izrazov v slovarju +Comment[sr]=Потражите фразе у речнику +Comment[sr@Latn]=Potražite fraze u reÄniku +Comment[sv]=Leta upp fraser i en ordlista +Comment[ta]=ஒர௠அகராதியிலà¯à®³à¯à®³ தேடறà¯à®šà¯Šà®±à¯à®¤à¯Šà®Ÿà®°à¯à®•à®³à¯ +Comment[tg]=ҶуÑтуҷӯи ибораҳо дар луғат +Comment[th]=ค้นหาวลีในพจนานุà¸à¸£à¸¡ +Comment[tr]=Sözlükten kelimelere bakın +Comment[uk]=Пошук фраз у Ñловнику +Comment[ven]=Todani fhungo kha bugu talutshedza maipfi +Comment[wa]=Cweri des frÃ¥zes dins on diccionaire +Comment[xh]=Jonga amabinzana kwincwadi enekcazelo zamagama +Comment[zh_CN]=在å—å…¸ä¸æŸ¥æ‰¾çŸè¯ +Comment[zh_HK]=在å—å…¸ä¸å°‹æ‰¾ç‰‡èªž +Comment[zh_TW]=在å—å…¸ä¸å°‹æ‰¾ç‰‡èªž +Comment[zu]=Bheka amagama kwisichazamagama +Icon= +Name=Dictionary +Name[af]=Woordeboek +Name[ar]=القاموس +Name[az]=Lüğət +Name[be]=Слоўнік +Name[bg]=Речник +Name[bn]=অà¦à¦¿à¦§à¦¾à¦¨ +Name[br]=Geriadur +Name[bs]=RjeÄnik +Name[ca]=Diccionari +Name[cs]=SlovnÃk +Name[cy]=Geiriadur +Name[da]=Ordbog +Name[de]=Lexikon +Name[el]=Λεξικό +Name[eo]=Vortaro +Name[es]=Diccionario +Name[et]=Sõnaraamat +Name[eu]=Hiztegia +Name[fa]=واژه‌نامه +Name[fi]=Sanakirja +Name[fr]=Dictionnaire +Name[ga]=Foclóir +Name[gl]=Diccionario +Name[he]=מילון +Name[hi]=शबà¥à¤¦à¤•à¥‹à¤¶ +Name[hr]=RjeÄnik +Name[hu]=Szótár +Name[is]=Orðabók +Name[it]=Dizionario +Name[ja]=辞書 +Name[ka]=ლექსიკáƒáƒœáƒ˜ +Name[kk]=Сөздік +Name[km]=វចនានុក្រម +Name[ko]=ì‚¬ì „ +Name[lt]=Žodynas +Name[lv]=VÄrdnÄ«ca +Name[mk]=Речник +Name[mn]=Толь бичиг +Name[ms]=Kamus +Name[mt]=Dizzjunarju +Name[nb]=Ordbok +Name[nds]=Wöörbook +Name[ne]=शबà¥à¤¦à¤•à¥‹à¤¶ +Name[nl]=Woordenboek +Name[nn]=Ordbok +Name[nso]=Pukuntsu +Name[pa]=ਸ਼ਬਦ-ਕੋਸ਼ +Name[pl]=SÅ‚ownik +Name[pt]=Dicionário +Name[pt_BR]=Dicionário +Name[ro]=DicÅ£ionar +Name[ru]=Словарь +Name[se]=Sátnegirji +Name[sk]=SlovnÃk +Name[sl]=Slovar +Name[sr]=Речник +Name[sr@Latn]=ReÄnik +Name[sv]=Ordlista +Name[ta]=அகராதி +Name[tg]=Луғат +Name[th]=พจนานุà¸à¸£à¸¡ +Name[tr]=Sözlük +Name[uk]=Словник +Name[uz]=LugÊ»at +Name[uz@cyrillic]=Луғат +Name[ven]=Bugu yau talutshedza maipfi +Name[wa]=Motî +Name[xh]=Incwadi eneenkcazelo zamagama +Name[zh_CN]=å—å…¸ +Name[zh_HK]=å—å…¸ +Name[zh_TW]=å—å…¸ +Name[zu]=Isichazamagama +Icon=kdict +X-KDE-Library=kdict_panelapplet +X-KDE-UniqueApplet=true diff --git a/kdict/applet/kdictapplet.h b/kdict/applet/kdictapplet.h new file mode 100644 index 00000000..a9738148 --- /dev/null +++ b/kdict/applet/kdictapplet.h @@ -0,0 +1,101 @@ +/* ------------------------------------------------------------- + + kdictapplet.h (part of The KDE Dictionary Client) + + Copyright (C) 2001 Christian Gebauer <gebauer@kde.org> + + The applet is loosely based on the "Run" applet included in KDE. + Copyright (c) 2000 Matthias Elter <elter@kde.org> (Artistic License) + + This file is distributed under the Artistic License. + See LICENSE for details. + + ------------------------------------------------------------- + + PopupBox helper class + DictApplet a small kicker-applet + + ------------------------------------------------------------- */ + +#ifndef _DICTAPPLET_H_ +#define _DICTAPPLET_H_ + +#include <qhbox.h> +#include <kpanelapplet.h> + +class QLabel; +class QPushButton; +class KHistoryCombo; + + +//********* PopupBox ******************************************** + +class PopupBox : public QHBox +{ + Q_OBJECT + +public: + PopupBox(); + ~PopupBox(); + + bool showBox(); + +signals: + void hidden(); + +public slots: + void enablePopup(); + +protected: + void hideEvent(QHideEvent *); + +private: + bool popupEnabled; + +}; + +//********* DictApplet ******************************************** + +class DictApplet : public KPanelApplet +{ + Q_OBJECT + +public: + DictApplet(const QString& configFile, Type t = Stretch, int actions = 0, QWidget *parent = 0, const char *name = 0); + virtual ~DictApplet(); + + int widthForHeight(int height) const; + int heightForWidth(int width) const; + +protected: + void resizeEvent(QResizeEvent*); + bool eventFilter( QObject *, QEvent * ); + + void sendCommand(const QCString &fun, const QString &data); + +protected slots: + void sendDelayedCommand(); + void startQuery(const QString&); + void comboTextChanged(const QString&); + void queryClipboard(); + void startDefine(); + void startMatch(); + void showExternalCombo(); + void externalComboHidden(); + void updateCompletionMode(KGlobalSettings::Completion mode); + +private: + KHistoryCombo *internalCombo, *externalCombo; + KCompletion *completionObject; + QLabel *textLabel, *iconLabel; + QPushButton *verticalBtn, *clipboardBtn, *defineBtn, *matchBtn; + QWidget *baseWidget; + PopupBox *popupBox; + + int waiting; + QCString delayedFunc; + QString delayedData; + +}; + +#endif diff --git a/kdict/application.cpp b/kdict/application.cpp new file mode 100644 index 00000000..28e3a398 --- /dev/null +++ b/kdict/application.cpp @@ -0,0 +1,71 @@ +/* ------------------------------------------------------------- + + application.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. + + ------------------------------------------------------------- */ + +#include <kwin.h> +#include <kdebug.h> +#include <kcmdlineargs.h> + +#include "application.h" +#include "toplevel.h" + + +Application::Application() + : KUniqueApplication() +{ + m_mainWindow = new TopLevel( 0, "mainWindow" ); +} + + +Application::~Application() +{ + delete m_mainWindow; +} + + +int Application::newInstance() +{ + kdDebug(5004) << "Application::newInstance()" << endl; + KUniqueApplication::newInstance(); + + // process parameters... + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + m_mainWindow->show(); + + if (args->isSet("clipboard")) + { + m_mainWindow->defineClipboard(); + } + else + { + if (args->count()>0) + { + QString phrase; + for (int i=0;i<args->count();i++) + { + phrase += QString::fromLocal8Bit(args->arg(i)); + if (i+1 < args->count()) + phrase += " "; + } + m_mainWindow->define(phrase); + } + else + { + m_mainWindow->normalStartup(); + } + } + + return 0; +} + +//-------------------------------- + +#include "application.moc" diff --git a/kdict/application.h b/kdict/application.h new file mode 100644 index 00000000..eddb7f44 --- /dev/null +++ b/kdict/application.h @@ -0,0 +1,39 @@ +/* ------------------------------------------------------------- + + application.h (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. + + ------------------------------------------------------------- */ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include <kuniqueapplication.h> +#include <qguardedptr.h> + +#define KDICT_VERSION "0.6" + +class TopLevel; + +class Application : public KUniqueApplication +{ + Q_OBJECT + + public: + Application(); + ~Application(); + + /** Create new instance of Kdict. Make the existing + main window active if Kdict is already running */ + int newInstance(); + + private: + QGuardedPtr<TopLevel> m_mainWindow; + +}; + +#endif diff --git a/kdict/dcopinterface.h b/kdict/dcopinterface.h new file mode 100644 index 00000000..f088a842 --- /dev/null +++ b/kdict/dcopinterface.h @@ -0,0 +1,54 @@ +/* ------------------------------------------------------------- + + dcopinterface.h (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. + + ------------------------------------------------------------- + + KDictDCOPInterface abstract base class that defines the + DCOP interface of Kdict + + ------------------------------------------------------------- */ + +#ifndef _DCOPINTERFACE_H +#define _DCOPINTERFACE_H + +#include <dcopobject.h> +#include <qstringlist.h> + +class KDictIface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + + /** Quit Kdict **/ + virtual void quit() = 0; + virtual void makeActiveWindow() = 0; + + /** define/match a word/phrase **/ + virtual void definePhrase(QString phrase) = 0; + virtual void matchPhrase(QString phrase) = 0; + virtual void defineClipboardContent() = 0; + virtual void matchClipboardContent() = 0; + + /** get info **/ + virtual QStringList getDatabases() = 0; + virtual QString currentDatabase() = 0; + virtual QStringList getStrategies() = 0; + virtual QString currentStrategy() = 0; + + /** set current database/strategy (returns true on success) **/ + virtual bool setDatabase(QString db) = 0; + virtual bool setStrategy(QString strategy) = 0; + + /** navigate in history **/ + virtual bool historyGoBack() = 0; + virtual bool historyGoForward() = 0; + +}; + +#endif diff --git a/kdict/dict.cpp b/kdict/dict.cpp new file mode 100644 index 00000000..b36f1ac1 --- /dev/null +++ b/kdict/dict.cpp @@ -0,0 +1,1632 @@ +/* ------------------------------------------------------------- + + dict.cpp (part of The KDE Dictionary Client) + + Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org> + (C) by Matthias Hölzer 1998 + + This file is distributed under the Artistic License. + See LICENSE for details. + + ------------------------------------------------------------- + + JobData used for data transfer between Client and Interface + DictAsyncClient all network related stuff happens here in asynchrous thread + DictInterface interface for DictAsyncClient, job management + + ------------------------------------------------------------- */ + +#include <config.h> + +#include "application.h" +#include "options.h" +#include "dict.h" + +#include <qregexp.h> +#include <qtextcodec.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kmessagebox.h> +#include <kmdcodec.h> +#include <kextsock.h> +#include <ksocks.h> + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> + +//********* JobData ****************************************** + + +JobData::JobData(QueryType Ntype,bool NnewServer,QString const& Nserver,int Nport, + int NidleHold, int Ntimeout, int NpipeSize, QString const& Nencoding, bool NAuthEnabled, + QString const& Nuser, QString const& Nsecret, unsigned int NheadLayout) + : type(Ntype), error(ErrNoErr), canceled(false), numFetched(0), newServer(NnewServer),server(Nserver), port(Nport), + timeout(Ntimeout), pipeSize(NpipeSize), idleHold(NidleHold), encoding(Nencoding), authEnabled(NAuthEnabled), + user(Nuser), secret(Nsecret), headLayout(NheadLayout) +{} + + +//********* DictAsyncClient ************************************* + +DictAsyncClient::DictAsyncClient(int NfdPipeIn, int NfdPipeOut) +: job(0L), inputSize(10000), fdPipeIn(NfdPipeIn), + fdPipeOut(NfdPipeOut), tcpSocket(-1), idleHold(0) +{ + input = new char[inputSize]; +} + + +DictAsyncClient::~DictAsyncClient() +{ + if (-1!=tcpSocket) + doQuit(); + delete [] input; +} + + +void* DictAsyncClient::startThread(void* pseudoThis) +{ + DictAsyncClient* newthis = (DictAsyncClient*) (pseudoThis); + + if (0!=pthread_setcanceltype(PTHREAD_CANCEL_ENABLE,NULL)) + qWarning("pthread_setcanceltype failed!"); + if (0!= pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL)) + qWarning("pthread_setcanceltype failed!"); + + signal(SIGPIPE,SIG_IGN); // ignore sigpipe + + newthis->waitForWork(); + return NULL; +} + + +void DictAsyncClient::insertJob(JobData *newJob) +{ + if (!job) // don't overwrite existing job pointer + job = newJob; +} + + +void DictAsyncClient::removeJob() +{ + job = 0L; +} + + +void DictAsyncClient::waitForWork() +{ + fd_set fdsR,fdsE; + timeval tv; + int selectRet; + char buf; + + while (true) { + if (tcpSocket != -1) { // we are connected, hold the connection for xx secs + FD_ZERO(&fdsR); + FD_SET(fdPipeIn, &fdsR); + FD_SET(tcpSocket, &fdsR); + FD_ZERO(&fdsE); + FD_SET(tcpSocket, &fdsE); + tv.tv_sec = idleHold; + tv.tv_usec = 0; + selectRet = KSocks::self()->select(FD_SETSIZE, &fdsR, NULL, &fdsE, &tv); + if (selectRet == 0) { + doQuit(); // nothing happend... + } else { + if (((selectRet > 0)&&(!FD_ISSET(fdPipeIn,&fdsR)))||(selectRet == -1)) + closeSocket(); + } + } + + do { + FD_ZERO(&fdsR); + FD_SET(fdPipeIn, &fdsR); + } while (select(FD_SETSIZE, &fdsR, NULL, NULL, NULL)<0); // don't get tricked by signals + + clearPipe(); + + if (job) { + if ((tcpSocket!=-1)&&(job->newServer)) + doQuit(); + + codec = QTextCodec::codecForName(job->encoding.latin1()); + input[0] = 0; //terminate string + thisLine = input; + nextLine = input; + inputEnd = input; + timeout = job->timeout; + idleHold = job->idleHold; + + if (tcpSocket==-1) + openConnection(); + + if (tcpSocket!=-1) { // connection is ready + switch (job->type) { + case JobData::TDefine : + define(); + break; + case JobData::TGetDefinitions : + getDefinitions(); + break; + case JobData::TMatch : + match(); + break; + case JobData::TShowDatabases : + showDatabases(); + break; + case JobData::TShowDbInfo : + showDbInfo(); + break; + case JobData::TShowStrategies : + showStrategies(); + break; + case JobData::TShowInfo : + showInfo(); + break; + case JobData::TUpdate : + update(); + } + } + clearPipe(); + } + if (write(fdPipeOut,&buf,1) == -1) // emit stopped signal + ::perror( "waitForJobs()" ); + } +} + + +void DictAsyncClient::define() +{ + QString command; + + job->defines.clear(); + QStringList::iterator it; + for (it = job->databases.begin(); it != job->databases.end(); ++it) { + command = "define "; + command += *it; + command += " \""; + command += job->query; + command += "\"\r\n"; + job->defines.append(command); + } + + if (!getDefinitions()) + return; + + if (job->numFetched == 0) { + job->strategy = "."; + if (!match()) + return; + job->result = QString::null; + if (job->numFetched == 0) { + resultAppend("<body>\n<p class=\"heading\">\n"); + resultAppend(i18n("No definitions found for \'%1'.").arg(job->query)); + resultAppend("</p>\n</html></body>"); + } else { + // html header... + resultAppend("<body>\n<p class=\"heading\">\n"); + resultAppend(i18n("No definitions found for \'%1\'. Perhaps you mean:").arg(job->query)); + resultAppend("</p>\n<table width=\"100%\" cols=2>\n"); + + QString lastDb; + QStringList::iterator it; + for (it = job->matches.begin(); it != job->matches.end(); ++it) { + int pos = (*it).find(' '); + if (pos != -1) { + if (lastDb != (*it).left(pos)) { + if (lastDb.length() > 0) + resultAppend("</pre></td></tr>\n"); + lastDb = (*it).left(pos); + resultAppend("<tr valign=top><td width=25%><pre><b>"); + resultAppend(lastDb); + resultAppend(":</b></pre></td><td width=75%><pre>"); + } + if ((*it).length() > (unsigned int)pos+2) { + resultAppend("<a href=\"http://define/"); + resultAppend((*it).mid(pos+2, (*it).length()-pos-3)); + resultAppend("\">"); + resultAppend((*it).mid(pos+2, (*it).length()-pos-3)); + resultAppend("</a> "); + } + } + } + resultAppend("\n</pre></td></tr></table>\n</body></html>"); + job->numFetched = 0; + } + } +} + + +QString htmlString(const QString &raw) +{ + unsigned int len=raw.length(); + QString ret; + + for (unsigned int i=0; i<len; i++) { + switch (raw[i]) { + case '&' : ret += "&"; break; + case '<' : ret+="<"; break; + case '>' : ret+=">"; break; + default : ret+=raw[i]; + } + } + + return ret; +} + + +QString generateDefineLink(const QString &raw) +{ + QRegExp http("http://[^\\s<>()\"|\\[\\]{}]+"); + QRegExp ftp("ftp://[^\\s<>()\"|\\[\\]{}]+"); + int matchPos=0, matchLen=0; + bool httpMatch=false; + QString ret; + + matchPos = http.search(raw); + matchLen = http.matchedLength(); + if (-1 != matchPos) { + httpMatch = true; + } else { + matchPos = ftp.search(raw); + matchLen = ftp.matchedLength(); + httpMatch = false; + } + + if (-1 != matchPos) { + ret = htmlString(raw.left(matchPos)); + ret += "<a href=\"http://"; + if (httpMatch) { + ret += "realhttp/"; + ret += raw.mid(matchPos+7, matchLen-7); + } else { + ret += "realftp/"; + ret += raw.mid(matchPos+6, matchLen-6); + } + ret += "\">"; + ret += htmlString(raw.mid(matchPos, matchLen)); + ret += "</a>"; + ret += htmlString(raw.right(raw.length()-matchLen-matchPos)); + } else { + ret = "<a href=\"http://define/"; + ret += raw; + ret += "\">"; + ret += htmlString(raw); + ret += "</a>"; + } + + return ret; +} + + +bool DictAsyncClient::getDefinitions() +{ + QCString lastDb,bracketBuff; + QStrList hashList; + char *s; + int defCount,response; + + // html header... + resultAppend("<body>\n"); + + while (job->defines.count()>0) { + defCount = 0; + cmdBuffer = ""; + do { + QStringList::iterator it = job->defines.begin(); + cmdBuffer += codec->fromUnicode(*it); + defCount++; + job->defines.remove(it); + } while ((job->defines.count()>0)&&((int)cmdBuffer.length()<job->pipeSize)); + + if (!sendBuffer()) + return false; + + for (;defCount > 0;defCount--) { + if (!getNextResponse(response)) + return false; + switch (response) { + case 552: // define: 552 No match + break; + case 150: { // define: 150 n definitions retrieved - definitions follow + bool defineDone = false; + while (!defineDone) { + if (!getNextResponse(response)) + return false; + switch (response) { + case 151: { // define: 151 word database name - text follows + char *db = strchr(thisLine, '\"'); + if (db) + db = strchr(db+1, '\"'); + char *dbdes = 0; + if (db) { + db+=2; // db points now on database name + dbdes = strchr(db,' '); + if (dbdes) { + dbdes[0] = 0; // terminate database name + dbdes+=2; // dbdes points now on database description + } + } else { + job->error = JobData::ErrServerError; + job->result = QString::null; + resultAppend(thisLine); + doQuit(); + return false; + } + + int oldResPos = job->result.length(); + + if (((job->headLayout<2)&&(lastDb!=db))||(job->headLayout==2)) { + lastDb = db; + resultAppend("<p class=\"heading\">\n"); + if (dbdes) + resultAppend(codec->toUnicode(dbdes,strlen(dbdes)-1)); + resultAppend(" [<a href=\"http://dbinfo/"); + resultAppend(db); + resultAppend("\">"); + resultAppend(db); + resultAppend("</a>]</p>\n"); + } else + if (job->headLayout==1) + resultAppend("<hr>\n"); + + resultAppend("<pre><p class=\"definition\">\n"); + + KMD5 context; + bool bodyDone = false; + while (!bodyDone) { + if (!getNextLine()) + return false; + char *line = thisLine; + if (line[0]=='.') { + if (line[1]=='.') + line++; // collapse double periode into one + else + if (line[1]==0) + bodyDone = true; + } + if (!bodyDone) { + context.update(QCString(line)); + if (!bracketBuff.isEmpty()) { + s = strchr(line,'}'); + if (!s) + resultAppend(bracketBuff.data()); + else { + s[0] = 0; + bracketBuff.remove(0,1); // remove '{' + bracketBuff += line; + line = s+1; + resultAppend(generateDefineLink(codec->toUnicode(bracketBuff))); + } + bracketBuff = ""; + } + s = strchr(line,'{'); + while (s) { + resultAppend(htmlString(codec->toUnicode(line,s-line))); + line = s; + s = strchr(line,'}'); + if (s) { + s[0] = 0; + line++; + resultAppend(generateDefineLink(codec->toUnicode(line))); + line = s+1; + s = strchr(line,'{'); + } else { + bracketBuff = line; + bracketBuff += "\n"; + line = 0; + s = 0; + } + } + if (line) { + resultAppend(htmlString(codec->toUnicode(line))); + resultAppend("\n"); + } + } + } + resultAppend("</p></pre>\n"); + + if (hashList.find(context.hexDigest())>=0) // duplicate?? + job->result.truncate(oldResPos); // delete the whole definition + else { + hashList.append(context.hexDigest()); + job->numFetched++; + } + + break; } + case 250: { // define: 250 ok (optional timing information here) + defineDone = true; + break; } + default: { + handleErrors(); + return false; } + } + } + break; } + default: + handleErrors(); + return false; + } + } + } + + resultAppend("</body></html>\n"); + return true; +} + + +bool DictAsyncClient::match() +{ + QStringList::iterator it = job->databases.begin(); + int response; + cmdBuffer = ""; + + while (it != job->databases.end()) { + int send = 0; + do { + cmdBuffer += "match "; + cmdBuffer += codec->fromUnicode(*(it)); + cmdBuffer += " "; + cmdBuffer += codec->fromUnicode(job->strategy); + cmdBuffer += " \""; + cmdBuffer += codec->fromUnicode(job->query); + cmdBuffer += "\"\r\n"; + send++; + ++it; + } while ((it != job->databases.end())&&((int)cmdBuffer.length()<job->pipeSize)); + + if (!sendBuffer()) + return false; + + for (;send > 0;send--) { + if (!getNextResponse(response)) + return false; + switch (response) { + case 552: // match: 552 No match + break; + case 152: { // match: 152 n matches found - text follows + bool matchDone = false; + while (!matchDone) { + if (!getNextLine()) + return false; + char *line = thisLine; + if (line[0]=='.') { + if (line[1]=='.') + line++; // collapse double period into one + else + if (line[1]==0) + matchDone = true; + } + if (!matchDone) { + job->numFetched++; + job->matches.append(codec->toUnicode(line)); + } + } + if (!nextResponseOk(250)) // match: "250 ok ..." + return false; + break; } + default: + handleErrors(); + return false; + } + } + } + + return true; +} + + +void DictAsyncClient::showDatabases() +{ + cmdBuffer = "show db\r\n"; + + if (!sendBuffer()) + return; + + if (!nextResponseOk(110)) // show db: "110 n databases present - text follows " + return; + + // html header... + resultAppend("<body>\n<p class=\"heading\">\n"); + resultAppend(i18n("Available Databases:")); + resultAppend("\n</p>\n<table width=\"100%\" cols=2>\n"); + + bool done(false); + char *line; + while (!done) { + if (!getNextLine()) + return; + line = thisLine; + if (line[0]=='.') { + if (line[1]=='.') + line++; // collapse double periode into one + else + if (line[1]==0) + done = true; + } + if (!done) { + resultAppend("<tr valign=top><td width=25%><pre><a href=\"http://dbinfo/"); + char *space = strchr(line,' '); + if (space) { + resultAppend(codec->toUnicode(line,space-line)); + resultAppend("\">"); + resultAppend(codec->toUnicode(line,space-line)); + resultAppend("</a></pre></td><td width=75%><pre>"); + line = space+1; + if (line[0]=='"') { + line++; // remove double quote + char *quote = strchr(line, '\"'); + if (quote) + quote[0]=0; + } + } else { // hmmm, malformated line... + resultAppend("\"></a></pre></td><td width=75%>"); + } + resultAppend(line); + resultAppend("</pre></td></tr>\n"); + } + } + resultAppend("</table>\n</body></html>"); + + if (!nextResponseOk(250)) // end of transmission: "250 ok ..." + return; +} + + +void DictAsyncClient::showDbInfo() +{ + cmdBuffer = "show info "; + cmdBuffer += codec->fromUnicode(job->query); + cmdBuffer += "\r\n"; + + if (!sendBuffer()) + return; + + if (!nextResponseOk(112)) // show info db: "112 database information follows" + return; + + // html header... + resultAppend("<body>\n<p class=\"heading\">\n"); + resultAppend(i18n("Database Information [%1]:").arg(job->query)); + resultAppend("</p>\n<pre><p class=\"definition\">\n"); + + bool done(false); + char *line; + while (!done) { + if (!getNextLine()) + return; + line = thisLine; + if (line[0]=='.') { + if (line[1]=='.') + line++; // collapse double periode into one + else + if (line[1]==0) + done = true; + } + if (!done) { + resultAppend(line); + resultAppend("\n"); + } + } + + resultAppend("</p></pre>\n</body></html>"); + + if (!nextResponseOk(250)) // end of transmission: "250 ok ..." + return; +} + + +void DictAsyncClient::showStrategies() +{ + cmdBuffer = "show strat\r\n"; + + if (!sendBuffer()) + return; + + if (!nextResponseOk(111)) // show strat: "111 n strategies present - text follows " + return; + + // html header... + resultAppend("<body>\n<p class=\"heading\">\n"); + resultAppend(i18n("Available Strategies:")); + resultAppend("\n</p>\n<table width=\"100%\" cols=2>\n"); + + bool done(false); + char *line; + while (!done) { + if (!getNextLine()) + return; + line = thisLine; + if (line[0]=='.') { + if (line[1]=='.') + line++; // collapse double periode into one + else + if (line[1]==0) + done = true; + } + if (!done) { + resultAppend("<tr valign=top><td width=25%><pre>"); + char *space = strchr(line,' '); + if (space) { + resultAppend(codec->toUnicode(line,space-line)); + resultAppend("</pre></td><td width=75%><pre>"); + line = space+1; + if (line[0]=='"') { + line++; // remove double quote + char *quote = strchr(line, '\"'); + if (quote) + quote[0]=0; + } + } else { // hmmm, malformated line... + resultAppend("</pre></td><td width=75%><pre>"); + } + resultAppend(line); + resultAppend("</pre></td></tr>\n"); + } + } + resultAppend("</table>\n</body></html>"); + + if (!nextResponseOk(250)) // end of transmission: "250 ok ..." + return; +} + + +void DictAsyncClient::showInfo() +{ + cmdBuffer = "show server\r\n"; + + if (!sendBuffer()) + return; + + if (!nextResponseOk(114)) // show server: "114 server information follows" + return; + + // html header... + resultAppend("<body>\n<p class=\"heading\">\n"); + resultAppend(i18n("Server Information:")); + resultAppend("\n</p>\n<pre><p class=\"definition\">\n"); + + bool done(false); + char *line; + while (!done) { + if (!getNextLine()) + return; + line = thisLine; + if (line[0]=='.') { + if (line[1]=='.') + line++; // collapse double periode into one + else + if (line[1]==0) + done = true; + } + if (!done) { + resultAppend(line); + resultAppend("\n"); + } + } + + resultAppend("</p></pre>\n</body></html>"); + + if (!nextResponseOk(250)) // end of transmission: "250 ok ..." + return; +} + + +void DictAsyncClient::update() +{ + cmdBuffer = "show strat\r\nshow db\r\n"; + + if (!sendBuffer()) + return; + + if (!nextResponseOk(111)) // show strat: "111 n strategies present - text follows " + return; + + bool done(false); + char *line; + while (!done) { + if (!getNextLine()) + return; + line = thisLine; + if (line[0]=='.') { + if (line[1]=='.') + line++; // collapse double periode into one + else + if (line[1]==0) + done = true; + } + if (!done) { + char *space = strchr(line,' '); + if (space) space[0] = 0; // terminate string, hack ;-) + job->strategies.append(codec->toUnicode(line)); + } + } + + if (!nextResponseOk(250)) // end of transmission: "250 ok ..." + return; + + if (!nextResponseOk(110)) // show db: "110 n databases present - text follows " + return; + + done = false; + while (!done) { + if (!getNextLine()) + return; + line = thisLine; + if (line[0]=='.') { + if (line[1]=='.') + line++; // collapse double periode into one + else + if (line[1]==0) + done = true; + } + if (!done) { + char *space = strchr(line,' '); + if (space) space[0] = 0; // terminate string, hack ;-) + job->databases.append(codec->toUnicode(line)); + } + } + + if (!nextResponseOk(250)) // end of transmission: "250 ok ..." + return; +} + + +// connect, handshake and authorization +void DictAsyncClient::openConnection() +{ + if (job->server.isEmpty()) { + job->error = JobData::ErrBadHost; + return; + } + + KExtendedSocket ks; + + ks.setAddress(job->server, job->port); + ks.setTimeout(job->timeout); + if (ks.connect() < 0) { + if (ks.status() == IO_LookupError) + job->error = JobData::ErrBadHost; + else if (ks.status() == IO_ConnectError) { + job->result = QString::null; + resultAppend(KExtendedSocket::strError(ks.status(), errno)); + job->error = JobData::ErrConnect; + } else if (ks.status() == IO_TimeOutError) + job->error = JobData::ErrTimeout; + else { + job->result = QString::null; + resultAppend(KExtendedSocket::strError(ks.status(), errno)); + job->error = JobData::ErrCommunication; + } + + closeSocket(); + return; + } + tcpSocket = ks.fd(); + ks.release(); + + if (!nextResponseOk(220)) // connect: "220 text capabilities msg-id" + return; + + cmdBuffer = "client \"Kdict "; + cmdBuffer += KDICT_VERSION; + cmdBuffer += "\"\r\n"; + + if (job->authEnabled) + if (strstr(thisLine,"auth")) { // skip auth if not supported + char *msgId = strrchr(thisLine,'<'); + + if ((!msgId)||(!job->user.length())) { + job->error = JobData::ErrAuthFailed; + closeSocket(); + return; + } + + KMD5 context; + context.update(QCString(msgId)); + context.update(job->secret.local8Bit()); + + cmdBuffer += "auth " + job->user.local8Bit() + " "; + cmdBuffer += context.hexDigest(); + cmdBuffer += "\r\n"; + } + + if (!sendBuffer()) + return; + + if (!nextResponseOk(250)) // client: "250 ok ..." + return; + + if (job->authEnabled) + if (!nextResponseOk(230)) // auth: "230 Authentication successful" + return; +} + + +void DictAsyncClient::closeSocket() +{ + if (-1 != tcpSocket) { + ::close(tcpSocket); + tcpSocket = -1; + } +} + + +// send "quit" without timeout, without checks, close connection +void DictAsyncClient::doQuit() +{ + fd_set fdsW; + timeval tv; + + FD_ZERO(&fdsW); + FD_SET(tcpSocket, &fdsW); + tv.tv_sec = 0; + tv.tv_usec = 0; + int ret = KSocks::self()->select(FD_SETSIZE, NULL, &fdsW, NULL, &tv); + + if (ret > 0) { // we can write... + cmdBuffer = "quit\r\n"; + int todo = cmdBuffer.length(); + KSocks::self()->write(tcpSocket,&cmdBuffer.data()[0],todo); + } + closeSocket(); +} + + +// used by getNextLine() +bool DictAsyncClient::waitForRead() +{ + fd_set fdsR,fdsE; + timeval tv; + + int ret; + do { + FD_ZERO(&fdsR); + FD_SET(fdPipeIn, &fdsR); + FD_SET(tcpSocket, &fdsR); + FD_ZERO(&fdsE); + FD_SET(tcpSocket, &fdsE); + FD_SET(fdPipeIn, &fdsE); + tv.tv_sec = timeout; + tv.tv_usec = 0; + ret = KSocks::self()->select(FD_SETSIZE, &fdsR, NULL, &fdsE, &tv); + } while ((ret<0)&&(errno==EINTR)); // don't get tricked by signals + + if (ret == -1) { // select failed + if (job) { + job->result = QString::null; + resultAppend(strerror(errno)); + job->error = JobData::ErrCommunication; + } + closeSocket(); + return false; + } + if (ret == 0) { // Nothing happend, timeout + if (job) + job->error = JobData::ErrTimeout; + doQuit(); + return false; + } + if (ret > 0) { + if (FD_ISSET(fdPipeIn,&fdsR)) { // stop signal + doQuit(); + return false; + } + if (FD_ISSET(tcpSocket,&fdsE)||FD_ISSET(fdPipeIn,&fdsE)) { // broken pipe, etc + if (job) { + job->result = QString::null; + resultAppend(i18n("The connection is broken.")); + job->error = JobData::ErrCommunication; + } + closeSocket(); + return false; + } + if (FD_ISSET(tcpSocket,&fdsR)) // all ok + return true; + } + + if (job) { + job->result = QString::null; + job->error = JobData::ErrCommunication; + } + closeSocket(); + return false; +} + + +// used by sendBuffer() & connect() +bool DictAsyncClient::waitForWrite() +{ + fd_set fdsR,fdsW,fdsE; + timeval tv; + + int ret; + do { + FD_ZERO(&fdsR); + FD_SET(fdPipeIn, &fdsR); + FD_SET(tcpSocket, &fdsR); + FD_ZERO(&fdsW); + FD_SET(tcpSocket, &fdsW); + FD_ZERO(&fdsE); + FD_SET(tcpSocket, &fdsE); + FD_SET(fdPipeIn, &fdsE); + tv.tv_sec = timeout; + tv.tv_usec = 0; + ret = KSocks::self()->select(FD_SETSIZE, &fdsR, &fdsW, &fdsE, &tv); + } while ((ret<0)&&(errno==EINTR)); // don't get tricked by signals + + if (ret == -1) { // select failed + if (job) { + job->result = QString::null; + resultAppend(strerror(errno)); + job->error = JobData::ErrCommunication; + } + closeSocket(); + return false; + } + if (ret == 0) { // nothing happend, timeout + if (job) + job->error = JobData::ErrTimeout; + closeSocket(); + return false; + } + if (ret > 0) { + if (FD_ISSET(fdPipeIn,&fdsR)) { // stop signal + doQuit(); + return false; + } + if (FD_ISSET(tcpSocket,&fdsR)||FD_ISSET(tcpSocket,&fdsE)||FD_ISSET(fdPipeIn,&fdsE)) { // broken pipe, etc + if (job) { + job->result = QString::null; + resultAppend(i18n("The connection is broken.")); + job->error = JobData::ErrCommunication; + } + closeSocket(); + return false; + } + if (FD_ISSET(tcpSocket,&fdsW)) // all ok + return true; + } + if (job) { + job->result = QString::null; + job->error = JobData::ErrCommunication; + } + closeSocket(); + return false; +} + + +// remove start/stop signal +void DictAsyncClient::clearPipe() +{ + fd_set fdsR; + timeval tv; + int selectRet; + char buf; + + tv.tv_sec = 0; + tv.tv_usec = 0; + do { + FD_ZERO(&fdsR); + FD_SET(fdPipeIn,&fdsR); + if (1==(selectRet=select(FD_SETSIZE,&fdsR,NULL,NULL,&tv))) + if ( ::read(fdPipeIn, &buf, 1 ) == -1 ) + ::perror( "clearPipe()" ); + } while (selectRet == 1); +} + + +bool DictAsyncClient::sendBuffer() +{ + int ret; + int todo = cmdBuffer.length(); + int done = 0; + + while (todo > 0) { + if (!waitForWrite()) + return false; + ret = KSocks::self()->write(tcpSocket,&cmdBuffer.data()[done],todo); + if (ret <= 0) { + if (job) { + job->result = QString::null; + resultAppend(strerror(errno)); + job->error = JobData::ErrCommunication; + } + closeSocket(); + return false; + } else { + done += ret; + todo -= ret; + } + } + return true; +} + + +// set thisLine to next complete line of input +bool DictAsyncClient::getNextLine() +{ + thisLine = nextLine; + nextLine = strstr(thisLine,"\r\n"); + if (nextLine) { // there is another full line in the inputbuffer + nextLine[0] = 0; // terminate string + nextLine[1] = 0; + nextLine+=2; + return true; + } + unsigned int div = inputEnd-thisLine+1; // hmmm, I need to fetch more input from the server... + memmove(input,thisLine,div); // save last, incomplete line + thisLine = input; + inputEnd = input+div-1; + do { + if ((inputEnd-input) > 9000) { + job->error = JobData::ErrMsgTooLong; + closeSocket(); + return false; + } + if (!waitForRead()) + return false; + + int received; + do { + received = KSocks::self()->read(tcpSocket, inputEnd, inputSize-(inputEnd-input)-1); + } while ((received<0)&&(errno==EINTR)); // don't get tricked by signals + + if (received <= 0) { + job->result = QString::null; + resultAppend(i18n("The connection is broken.")); + job->error = JobData::ErrCommunication; + closeSocket(); + return false; + } + inputEnd += received; + inputEnd[0] = 0; // terminate *char + } while (!(nextLine = strstr(thisLine,"\r\n"))); + nextLine[0] = 0; // terminate string + nextLine[1] = 0; + nextLine+=2; + return true; +} + + +// reads next line and checks the response code +bool DictAsyncClient::nextResponseOk(int code) +{ + if (!getNextLine()) + return false; + if (strtol(thisLine,0L,0)!=code) { + handleErrors(); + return false; + } + return true; +} + + +// reads next line and returns the response code +bool DictAsyncClient::getNextResponse(int &code) +{ + if (!getNextLine()) + return false; + code = strtol(thisLine,0L,0); + return true; +} + + +void DictAsyncClient::handleErrors() +{ + int len = strlen(thisLine); + if (len>80) + len = 80; + job->result = QString::null; + resultAppend(codec->toUnicode(thisLine,len)); + + switch (strtol(thisLine,0L,0)) { + case 420: + case 421: + job->error = JobData::ErrNotAvailable; // server unavailable + break; + case 500: + case 501: + job->error = JobData::ErrSyntax; // syntax error + break; + case 502: + case 503: + job->error = JobData::ErrCommandNotImplemented; // command not implemented + break; + case 530: + job->error = JobData::ErrAccessDenied; // access denied + break; + case 531: + job->error = JobData::ErrAuthFailed; // authentication failed + break; + case 550: + case 551: + job->error = JobData::ErrInvalidDbStrat; // invalid strategy/database + break; + case 554: + job->error = JobData::ErrNoDatabases; // no databases + break; + case 555: + job->error = JobData::ErrNoStrategies; // no strategies + break; + default: + job->error = JobData::ErrServerError; + } + doQuit(); +} + + +void DictAsyncClient::resultAppend(const char* str) +{ + if (job) + job->result += codec->toUnicode(str); +} + + +void DictAsyncClient::resultAppend(QString str) +{ + if (job) + job->result += str; +} + + + +//********* DictInterface ****************************************** + +DictInterface::DictInterface() +: newServer(false), clientDoneInProgress(false) +{ + if (::pipe(fdPipeIn ) == -1 ) { + perror( "Creating in pipe" ); + KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication.")); + kapp->exit(1); + } + if (::pipe(fdPipeOut ) == -1 ) { + perror( "Creating out pipe" ); + KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication.")); + kapp->exit(1); + } + + if (-1 == fcntl(fdPipeIn[0],F_SETFL,O_NONBLOCK)) { // make socket non-blocking + perror("fcntl()"); + KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication.")); + kapp->exit(1); + } + + if (-1 == fcntl(fdPipeOut[0],F_SETFL,O_NONBLOCK)) { // make socket non-blocking + perror("fcntl()"); + KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication.")); + kapp->exit(1); + } + + notifier = new QSocketNotifier(fdPipeIn[0],QSocketNotifier::Read,this); + connect(notifier,SIGNAL(activated(int)),this,SLOT(clientDone())); + + // initialize the KSocks stuff in the main thread, otherwise we get + // strange effects on FreeBSD + (void) KSocks::self(); + + client = new DictAsyncClient(fdPipeOut[0],fdPipeIn[1]); + if (0!=pthread_create(&threadID,0,&(client->startThread),client)) { + KMessageBox::error(global->topLevel, i18n("Internal error:\nUnable to create thread.")); + kapp->exit(1); + } + + jobList.setAutoDelete(true); +} + + +DictInterface::~DictInterface() +{ + disconnect(notifier,SIGNAL(activated(int)),this,SLOT(clientDone())); + + if (0!=pthread_cancel(threadID)) + kdWarning() << "pthread_cancel failed!" << endl; + if (0!=pthread_join(threadID,NULL)) + kdWarning() << "pthread_join failed!" << endl; + delete client; + + if ( ::close( fdPipeIn[0] ) == -1 ) { + perror( "Closing fdPipeIn[0]" ); + } + if ( ::close( fdPipeIn[1] ) == -1 ) { + perror( "Closing fdPipeIn[1]" ); + } + if ( ::close( fdPipeOut[0] ) == -1 ) { + perror( "Closing fdPipeOut[0]" ); + } + if ( ::close( fdPipeOut[1] ) == -1 ) { + perror( "Closing fdPipeOut[1]" ); + } +} + + +// inform the client when server settings get changed +void DictInterface::serverChanged() +{ + newServer = true; +} + + +// cancel all pending jobs +void DictInterface::stop() +{ + if (jobList.isEmpty()) { + return; + } else { + while (jobList.count()>1) // not yet started jobs can be deleted directly + jobList.removeLast(); + + if (!clientDoneInProgress) { + jobList.getFirst()->canceled = true; // clientDone() now ignores the results of this job + char buf; // write one char in the pipe to the async thread + if (::write(fdPipeOut[1],&buf,1) == -1) + ::perror( "stop()" ); + } + } +} + + +void DictInterface::define(const QString &query) +{ + JobData *newJob = generateQuery(JobData::TDefine,query); + if (newJob) + insertJob(newJob); +} + + +void DictInterface::getDefinitions(QStringList query) +{ + JobData *newjob = new JobData(JobData::TGetDefinitions,newServer,global->server,global->port, + global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled, + global->user,global->secret,global->headLayout); + newjob->defines = query; + newServer = false; + insertJob(newjob); +} + + +void DictInterface::match(const QString &query) +{ + JobData *newJob = generateQuery(JobData::TMatch,query); + + if (newJob) { + if (global->currentStrategy == 0) + newJob->strategy = "."; // spell check strategy + else + newJob->strategy = global->strategies[global->currentStrategy].utf8(); + + insertJob(newJob); + } +} + + +// fetch detailed db info +void DictInterface::showDbInfo(const QString &db) +{ + QString ndb = db.simplifyWhiteSpace(); // cleanup query string + if (ndb.isEmpty()) + return; + if (ndb.length()>100) // shorten if necessary + ndb.truncate(100); + JobData *newjob = new JobData(JobData::TShowDbInfo,newServer,global->server,global->port, + global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled, + global->user,global->secret,global->headLayout); + newServer = false; + newjob->query = ndb; // construct job... + insertJob(newjob); +} + + +void DictInterface::showDatabases() +{ + insertJob( new JobData(JobData::TShowDatabases,newServer,global->server,global->port, + global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled, + global->user,global->secret,global->headLayout)); + newServer = false; +} + + +void DictInterface::showStrategies() +{ + insertJob( new JobData(JobData::TShowStrategies,newServer,global->server,global->port, + global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled, + global->user,global->secret,global->headLayout)); + newServer = false; +} + + +void DictInterface::showInfo() +{ + insertJob( new JobData(JobData::TShowInfo,newServer,global->server,global->port, + global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled, + global->user,global->secret,global->headLayout)); + newServer = false; +} + + +// get info about databases & stratgies the server knows +void DictInterface::updateServer() +{ + insertJob( new JobData(JobData::TUpdate,newServer,global->server,global->port, + global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled, + global->user,global->secret,global->headLayout)); + newServer = false; +} + + +// client-thread ended +void DictInterface::clientDone() +{ + QString message; + + cleanPipes(); // read from pipe so that notifier doesn´t fire again + + if (jobList.isEmpty()) { + kdDebug(5004) << "This shouldn´t happen, the client-thread signaled termination, but the job list is empty" << endl; + return; // strange.. + } + + clientDoneInProgress = true; + QStringList::iterator it; + JobData* job = jobList.getFirst(); + if (!job->canceled) { // non-interupted job? + if (JobData::ErrNoErr == job->error) { + switch (job->type) { + case JobData::TUpdate : + global->serverDatabases.clear(); + for (it = job->databases.begin(); it != job->databases.end(); ++it) + global->serverDatabases.append(*it); + global->databases = global->serverDatabases; + for (int i = global->databaseSets.count()-1;i>=0;i--) + global->databases.prepend(global->databaseSets.at(i)->first()); + global->databases.prepend(i18n("All Databases")); + global->currentDatabase = 0; + + global->strategies.clear(); + for (it = job->strategies.begin(); it != job->strategies.end(); ++it) + global->strategies.append(*it); + global->strategies.prepend(i18n("Spell Check")); + global->currentStrategy = 0; + message = i18n(" Received database/strategy list "); + emit stopped(message); + emit infoReady(); + break; + case JobData::TDefine: + case JobData::TGetDefinitions: + if (job->type == JobData::TDefine) { + switch (job->numFetched) { + case 0: + message = i18n("No definitions found"); + break; + case 1: + message = i18n("One definition found"); + break; + default: + message = i18n("%1 definitions found").arg(job->numFetched); + } + } else { + switch (job->numFetched) { + case 0: + message = i18n(" No definitions fetched "); + break; + case 1: + message = i18n(" One definition fetched "); + break; + default: + message = i18n(" %1 definitions fetched ").arg(job->numFetched); + } + } + emit stopped(message); + emit resultReady(job->result, job->query); + break; + case JobData::TMatch: + switch (job->numFetched) { + case 0: + message = i18n(" No matching definitions found "); + break; + case 1: + message = i18n(" One matching definition found "); + break; + default: + message = i18n(" %1 matching definitions found ").arg(job->numFetched); + } + emit stopped(message); + emit matchReady(job->matches); + break; + default : + message = i18n(" Received information "); + emit stopped(message); + emit resultReady(job->result, job->query); + } + } else { + QString errMsg; + switch (job->error) { + case JobData::ErrCommunication: + errMsg = i18n("Communication error:\n\n"); + errMsg += job->result; + break; + case JobData::ErrTimeout: + errMsg = i18n("A delay occurred which exceeded the\ncurrent timeout limit of %1 seconds.\nYou can modify this limit in the Preferences Dialog.").arg(global->timeout); + break; + case JobData::ErrBadHost: + errMsg = i18n("Unable to connect to:\n%1:%2\n\nCannot resolve hostname.").arg(job->server).arg(job->port); + break; + case JobData::ErrConnect: + errMsg = i18n("Unable to connect to:\n%1:%2\n\n").arg(job->server).arg(job->port); + errMsg += job->result; + break; + case JobData::ErrRefused: + errMsg = i18n("Unable to connect to:\n%1:%2\n\nThe server refused the connection.").arg(job->server).arg(job->port); + break; + case JobData::ErrNotAvailable: + errMsg = i18n("The server is temporarily unavailable."); + break; + case JobData::ErrSyntax: + errMsg = i18n("The server reported a syntax error.\nThis shouldn't happen -- please consider\nwriting a bug report."); + break; + case JobData::ErrCommandNotImplemented: + errMsg = i18n("A command that Kdict needs isn't\nimplemented on the server."); + break; + case JobData::ErrAccessDenied: + errMsg = i18n("Access denied.\nThis host is not allowed to connect."); + break; + case JobData::ErrAuthFailed: + errMsg = i18n("Authentication failed.\nPlease enter a valid username and password."); + break; + case JobData::ErrInvalidDbStrat: + errMsg = i18n("Invalid database/strategy.\nYou probably need to use Server->Get Capabilities."); + break; + case JobData::ErrNoDatabases: + errMsg = i18n("No databases available.\nIt is possible that you need to authenticate\nwith a valid username/password combination to\ngain access to any databases."); + break; + case JobData::ErrNoStrategies: + errMsg = i18n("No strategies available."); + break; + case JobData::ErrServerError: + errMsg = i18n("The server sent an unexpected reply:\n\"%1\"\nThis shouldn't happen, please consider\nwriting a bug report").arg(job->result); + break; + case JobData::ErrMsgTooLong: + errMsg = i18n("The server sent a response with a text line\nthat was too long.\n(RFC 2229: max. 1024 characters/6144 octets)"); + break; + case JobData::ErrNoErr: // make compiler happy + errMsg = i18n("No Errors"); + } + message = i18n(" Error "); + emit stopped(message); + KMessageBox::error(global->topLevel, errMsg); + } + } else { + message = i18n(" Stopped "); + emit stopped(message); + } + + clientDoneInProgress = false; + + client->removeJob(); + jobList.removeFirst(); // this job is now history + if (!jobList.isEmpty()) // work to be done? + startClient(); // => restart client +} + + +JobData* DictInterface::generateQuery(JobData::QueryType type, QString query) +{ + query = query.simplifyWhiteSpace(); // cleanup query string + if (query.isEmpty()) + return 0L; + if (query.length()>300) // shorten if necessary + query.truncate(300); + query = query.replace(QRegExp("[\"\\]"), ""); // remove remaining illegal chars... + if (query.isEmpty()) + return 0L; + + JobData *newjob = new JobData(type,newServer,global->server,global->port, + global->idleHold,global->timeout,global->pipeSize, global->encoding, global->authEnabled, + global->user,global->secret,global->headLayout); + newServer = false; + newjob->query = query; // construct job... + + if (global->currentDatabase == 0) // all databases + newjob->databases.append("*"); + else { + if ((global->currentDatabase > 0)&& // database set + (global->currentDatabase < global->databaseSets.count()+1)) { + for (int i = 0;i<(int)global->serverDatabases.count();i++) + if ((global->databaseSets.at(global->currentDatabase-1))->findIndex(global->serverDatabases[i])>0) + newjob->databases.append(global->serverDatabases[i].utf8().data()); + if (newjob->databases.count()==0) { + KMessageBox::sorry(global->topLevel, i18n("Please select at least one database.")); + delete newjob; + return 0L; + } + } else { // one database + newjob->databases.append(global->databases[global->currentDatabase].utf8().data()); + } + } + + return newjob; +} + + +void DictInterface::insertJob(JobData* job) +{ + if (jobList.isEmpty()) { // Client has nothing to do, start directly + jobList.append(job); + startClient(); + } else { // there are other pending jobs... + stop(); + jobList.append(job); + } +} + + +// start client-thread +void DictInterface::startClient() +{ + cleanPipes(); + if (jobList.isEmpty()) { + kdDebug(5004) << "This shouldn´t happen, startClient called, but clientList is empty" << endl; + return; + } + + client->insertJob(jobList.getFirst()); + char buf; // write one char in the pipe to the async thread + if (::write(fdPipeOut[1],&buf,1) == -1) + ::perror( "startClient()" ); + + QString message; + switch (jobList.getFirst()->type) { + case JobData::TDefine: + case JobData::TGetDefinitions: + case JobData::TMatch: + message = i18n(" Querying server... "); + break; + case JobData::TShowDatabases: + case JobData::TShowStrategies: + case JobData::TShowInfo: + case JobData::TShowDbInfo: + message = i18n(" Fetching information... "); + break; + case JobData::TUpdate: + message = i18n(" Updating server information... "); + break; + } + emit started(message); +} + + +// empty the pipes, so that notifier stops firing +void DictInterface::cleanPipes() +{ + fd_set rfds; + struct timeval tv; + int ret; + char buf; + tv.tv_sec = 0; + tv.tv_usec = 0; + + do { + FD_ZERO(&rfds); + FD_SET(fdPipeIn[0],&rfds); + if (1==(ret=select(FD_SETSIZE,&rfds,NULL,NULL,&tv))) + if ( ::read(fdPipeIn[0], &buf, 1 ) == -1 ) + ::perror( "cleanPipes" ); + } while (ret == 1); + + do { + FD_ZERO(&rfds); + FD_SET(fdPipeOut[0],&rfds); + if (1==(ret=select(FD_SETSIZE,&rfds,NULL,NULL,&tv))) + if ( ::read(fdPipeOut[0], &buf, 1 ) == -1 ) + ::perror( "cleanPipes" ); + } while (ret == 1); +} + +//-------------------------------- + +#include "dict.moc" diff --git a/kdict/dict.h b/kdict/dict.h new file mode 100644 index 00000000..adb87ed9 --- /dev/null +++ b/kdict/dict.h @@ -0,0 +1,204 @@ +/* ------------------------------------------------------------- + + dict.h (part of The KDE Dictionary Client) + + Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org> + (C) by Matthias Hölzer 1998 + + This file is distributed under the Artistic License. + See LICENSE for details. + + ------------------------------------------------------------- + + JobData used for data transfer between Client and Interface + DictAsyncClient all network related stuff happens here in an asynchrous thread + DictInterface interface for DictAsyncClient, job management + + -------------------------------------------------------------*/ + +#ifndef _DICT_H_ +#define _DICT_H_ + +#include <pthread.h> +#include <qptrlist.h> +#include <qsocketnotifier.h> + +class QSocketNotifier; +struct in_addr; + + +//********* JobData ****************************************** + + +class JobData +{ + +public: + + enum QueryType { //type of transaction + TDefine=0, + TGetDefinitions, + TMatch, + TShowDatabases, + TShowDbInfo, + TShowStrategies, + TShowInfo, + TUpdate + }; + + enum ErrType { // error codes + ErrNoErr=0, + ErrCommunication, // display result! + ErrTimeout, + ErrBadHost, + ErrConnect, // display result! + ErrRefused, + ErrNotAvailable, + ErrSyntax, + ErrCommandNotImplemented, + ErrAccessDenied, + ErrAuthFailed, + ErrInvalidDbStrat, + ErrNoDatabases, + ErrNoStrategies, + ErrServerError, // display result! + ErrMsgTooLong + }; + + JobData(QueryType Ntype,bool NnewServer,QString const& Nserver,int Nport, + int NidleHold, int Ntimeout, int NpipeSize, QString const& Nencoding, bool NAuthEnabled, + QString const& Nuser, QString const& Nsecret, unsigned int NheadLayout); + + QueryType type; + ErrType error; + + bool canceled; + int numFetched; + QString result; + QStringList matches; + + QString query; + QStringList defines; + + bool newServer; + QString server; + int port, timeout, pipeSize, idleHold; + QString encoding; + bool authEnabled; + QString user, secret; + QStringList databases,strategies; + QString strategy; + unsigned int headLayout; +}; + + +//********* DictAsyncClient ****************************************** + + +class DictAsyncClient +{ + +public: + + DictAsyncClient(int NfdPipeIn, int NfdPipeOut); + ~DictAsyncClient(); + + static void* startThread(void* pseudoThis); + + void insertJob(JobData *newJob); + void removeJob(); + +private: + + void waitForWork(); // main loop + void define(); + bool getDefinitions(); + bool match(); + void showDatabases(); + void showDbInfo(); + void showStrategies(); + void showInfo(); + void update(); + + void openConnection(); // connect, handshake and authorization + void closeSocket(); + void doQuit(); // send "quit" without timeout, without checks, close connection + bool waitForRead(); // used by getNextIntoBuffer() + bool waitForWrite(); // used by sendBuffer() & connect() + void clearPipe(); // remove start/stop signal + + bool sendBuffer(); // send cmdBuffer to the server + bool getNextLine(); // set thisLine to next complete line of input + bool nextResponseOk(int code); // reads next line and checks the response code + bool getNextResponse(int &code); // reads next line and returns the response code + void handleErrors(); + + void resultAppend(const char* str); + void resultAppend(QString str); + + JobData *job; + char *input; + QCString cmdBuffer; + const unsigned int inputSize; + char *thisLine, *nextLine, *inputEnd; + int fdPipeIn,fdPipeOut; //IPC-Pipes to/from async thread + int tcpSocket,timeout,idleHold; + QTextCodec *codec; +}; + + +//********* DictInterface ************************************************* + +class DictInterface : public QObject +{ + Q_OBJECT + +public: + + DictInterface(); + ~DictInterface(); + +public slots: + + void serverChanged(); // inform the client when server settings get changed + void stop(); // cancel all pending jobs + + void define(const QString &query); + void getDefinitions(QStringList query); + void match(const QString &query); + void showDbInfo(const QString &db); // fetch detailed db info + void showDatabases(); // fetch misc. info... + void showStrategies(); + void showInfo(); + void updateServer(); // get info about databases & strategies the server knows + +signals: + + void infoReady(); // updateServer done + void resultReady(const QString &result, const QString &query); // define done + void matchReady(const QStringList &result); // match done + void started(const QString &message); // Client is active now, activate indicator + void stopped(const QString &message); // Client is now halted, deactivate indicator + +private slots: + + void clientDone(); + +private: + + JobData* generateQuery(JobData::QueryType type, QString query); + void insertJob(JobData* job); // insert in job list, if nesscary cancel/remove previous jobs + void startClient(); // send start signal + void cleanPipes(); // empty the pipes, so that notifier stops firing + + QSocketNotifier *notifier; + int fdPipeIn[2],fdPipeOut[2]; //IPC-Pipes to/from async thread + pthread_t threadID; + DictAsyncClient *client; + QPtrList<JobData> jobList; + bool newServer,clientDoneInProgress; +}; + +extern DictInterface *interface; + +#endif diff --git a/kdict/hi128-app-kdict.png b/kdict/hi128-app-kdict.png Binary files differnew file mode 100644 index 00000000..fd57f9ab --- /dev/null +++ b/kdict/hi128-app-kdict.png diff --git a/kdict/hi16-app-kdict.png b/kdict/hi16-app-kdict.png Binary files differnew file mode 100644 index 00000000..643500fc --- /dev/null +++ b/kdict/hi16-app-kdict.png diff --git a/kdict/hi32-app-kdict.png b/kdict/hi32-app-kdict.png Binary files differnew file mode 100644 index 00000000..fa3a0537 --- /dev/null +++ b/kdict/hi32-app-kdict.png diff --git a/kdict/hi48-app-kdict.png b/kdict/hi48-app-kdict.png Binary files differnew file mode 100644 index 00000000..40efecd0 --- /dev/null +++ b/kdict/hi48-app-kdict.png diff --git a/kdict/hi64-app-kdict.png b/kdict/hi64-app-kdict.png Binary files differnew file mode 100644 index 00000000..ba6ed634 --- /dev/null +++ b/kdict/hi64-app-kdict.png diff --git a/kdict/hisc-app-kdict.svgz b/kdict/hisc-app-kdict.svgz Binary files differnew file mode 100644 index 00000000..584e1968 --- /dev/null +++ b/kdict/hisc-app-kdict.svgz diff --git a/kdict/kdict.desktop b/kdict/kdict.desktop new file mode 100644 index 00000000..d274d233 --- /dev/null +++ b/kdict/kdict.desktop @@ -0,0 +1,153 @@ +[Desktop Entry] +Type=Application +Exec=kdict -caption "%c" %i %m +Icon=kdict +Terminal=false +Name=Dictionary +Name[af]=Woordeboek +Name[ar]=القاموس +Name[az]=Lüğət +Name[be]=Слоўнік +Name[bg]=Речник +Name[bn]=অà¦à¦¿à¦§à¦¾à¦¨ +Name[br]=Geriadur +Name[bs]=RjeÄnik +Name[ca]=Diccionari +Name[cs]=SlovnÃk +Name[cy]=Geiriadur +Name[da]=Ordbog +Name[de]=Lexikon +Name[el]=Λεξικό +Name[eo]=Vortaro +Name[es]=Diccionario +Name[et]=Sõnaraamat +Name[eu]=Hiztegia +Name[fa]=واژه‌نامه +Name[fi]=Sanakirja +Name[fr]=Dictionnaire +Name[ga]=Foclóir +Name[gl]=Diccionario +Name[he]=מילון +Name[hi]=शबà¥à¤¦à¤•à¥‹à¤¶ +Name[hr]=RjeÄnik +Name[hu]=Szótár +Name[is]=Orðabók +Name[it]=Dizionario +Name[ja]=辞書 +Name[ka]=ლექსიკáƒáƒœáƒ˜ +Name[kk]=Сөздік +Name[km]=វចនានុក្រម +Name[ko]=ì‚¬ì „ +Name[lt]=Žodynas +Name[lv]=VÄrdnÄ«ca +Name[mk]=Речник +Name[mn]=Толь бичиг +Name[ms]=Kamus +Name[mt]=Dizzjunarju +Name[nb]=Ordbok +Name[nds]=Wöörbook +Name[ne]=शबà¥à¤¦à¤•à¥‹à¤¶ +Name[nl]=Woordenboek +Name[nn]=Ordbok +Name[nso]=Pukuntsu +Name[pa]=ਸ਼ਬਦ-ਕੋਸ਼ +Name[pl]=SÅ‚ownik +Name[pt]=Dicionário +Name[pt_BR]=Dicionário +Name[ro]=DicÅ£ionar +Name[ru]=Словарь +Name[se]=Sátnegirji +Name[sk]=SlovnÃk +Name[sl]=Slovar +Name[sr]=Речник +Name[sr@Latn]=ReÄnik +Name[sv]=Ordlista +Name[ta]=அகராதி +Name[tg]=Луғат +Name[th]=พจนานุà¸à¸£à¸¡ +Name[tr]=Sözlük +Name[uk]=Словник +Name[uz]=LugÊ»at +Name[uz@cyrillic]=Луғат +Name[ven]=Bugu yau talutshedza maipfi +Name[wa]=Motî +Name[xh]=Incwadi eneenkcazelo zamagama +Name[zh_CN]=å—å…¸ +Name[zh_HK]=å—å…¸ +Name[zh_TW]=å—å…¸ +Name[zu]=Isichazamagama +GenericName=Online Dictionary +GenericName[af]=Aan-lyn Woordeboek +GenericName[ar]=قاموس على الإنترنت +GenericName[be]=Сеткавы Ñлоўнік +GenericName[bg]=Мрежови речник +GenericName[bn]=অনলাইন অà¦à¦¿à¦§à¦¾à¦¨ +GenericName[br]=Geriaoueg enlinenn +GenericName[bs]=Online rjeÄnik +GenericName[ca]=Diccionari en lÃnia +GenericName[cs]=Online slovnÃk +GenericName[cy]=Geiriadur Ar-lein +GenericName[da]=Online-ordbog +GenericName[de]=Online-Lexikon +GenericName[el]=Διαδικτυακό λεξικό +GenericName[eo]=Vortaro +GenericName[es]=Diccionario en la red +GenericName[et]=Võrgusõnaraamat +GenericName[eu]=On line hiztegia +GenericName[fa]=واژه‌نامۀ برخط +GenericName[fi]=Sanakirja +GenericName[fr]=Dictionnaire électronique +GenericName[ga]=Foclóir ar lÃne +GenericName[gl]=Diccionario en liña +GenericName[he]=מילון מקוון +GenericName[hi]=ऑनलाइन शबà¥à¤¦à¤•à¥‹à¤¶ +GenericName[hr]=Online rjeÄnik +GenericName[hu]=SzótárkezelÅ‘ +GenericName[is]=Orðabók á Netinu +GenericName[it]=Dizionario in linea +GenericName[ja]=オンライン辞書 +GenericName[ka]=ხáƒáƒ–ის ლექსიკáƒáƒœáƒ˜ +GenericName[kk]=Онлайн Ñөздік +GenericName[km]=វចនានុក្រម​លើ​បណ្ដាញ +GenericName[lt]=Žodynas tinkle +GenericName[lv]=TieÅ¡saites VÄrdnÄ«ca +GenericName[mk]=Речник на линија +GenericName[mn]=Онлайн-Толь бичиг +GenericName[ms]=Kamus Talian +GenericName[mt]=Dizzjunarju online +GenericName[nb]=Ordbok pÃ¥ nettet +GenericName[nds]=Online-Wöörbook +GenericName[ne]=अनलाइन शबà¥à¤¦à¤•à¥‹à¤¶ +GenericName[nl]=Online woordenboek +GenericName[nn]=Internettordbok +GenericName[nso]=Pukuntsu ya Online +GenericName[pa]=ਆਨਲਾਇਨ ਡਿਕਸ਼ਨਰੀ +GenericName[pl]=SÅ‚ownik w sieci +GenericName[pt]=Dicionário na Rede +GenericName[pt_BR]= Dicionário On-line +GenericName[ro]=DicÅ£ionar on-line +GenericName[ru]=Онлайн-Ñловарь +GenericName[se]=Fierpmádatsátnegirji +GenericName[sk]=On-line slovnÃk +GenericName[sl]=Spletni slovar +GenericName[sr]=Онлајн речник +GenericName[sr@Latn]=Onlajn reÄnik +GenericName[sv]=Online-ordlista +GenericName[ta]=இணைய அகராதி +GenericName[tg]=Луғати Шабакавӣ +GenericName[th]=พจนานุà¸à¸£à¸¡à¹à¸šà¸šà¸à¸à¸™à¹„ลน์ +GenericName[tr]=Çevrimiçi Sözlük +GenericName[uk]=Словник в мережі +GenericName[uz]=Internet lugÊ»at +GenericName[uz@cyrillic]=Интернет луғат +GenericName[ven]=Bugu talutshedza maipfi ine yavha kha mutevhe wau tshimbila +GenericName[wa]=Motî so les fyis +GenericName[xh]=Incwadi eneenkcazelo zamagama Esemgceni +GenericName[zh_CN]=在线å—å…¸ +GenericName[zh_HK]=線上å—å…¸ +GenericName[zh_TW]=線上å—å…¸ +GenericName[zu]=Isichaza magama esixhumekile +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Unique +DocPath=kdict/index.html +Categories=Qt;KDE;Network;X-KDE-More;Office;Dictionary; diff --git a/kdict/kdictui.rc b/kdict/kdictui.rc new file mode 100644 index 00000000..15b3c414 --- /dev/null +++ b/kdict/kdictui.rc @@ -0,0 +1,59 @@ +<!DOCTYPE kpartgui> +<kpartgui name="Kdict" version="4"> + +<MenuBar> + <Menu noMerge="1" name="file"><text>&File</text> + <Action name="file_save"/> + <Action name="file_print"/> + <Separator/> + <Action name="start_query"/> + <Action name="stop_query"/> + <Separator/> + <Action name="file_quit"/> + </Menu> + <Menu noMerge="1" name="edit"><text>&Edit</text> + <Action name="edit_copy"/> + <Action name="edit_select_all"/> + <Separator/> + <Action name="define_clipboard"/> + <Action name="match_clipboard"/> + <Separator/> + <Action name="edit_find"/> + </Menu> + <Menu name="history"><text>Hist&ory</text> + <Action name="browse_back"/> + <Action name="browse_forward"/> + <Separator /> + <Action name="clear_history"/> + <Separator /> + <ActionList name="history_items"/> + </Menu> + <Menu name="server"><text>Ser&ver</text> + <Action name="get_capabilities"/> + <Action name="edit_sets"/> + <Separator /> + <Menu name="database_sub"><text>Database &Information</text> + <Action name="db_summary"/> + <Separator /> + <ActionList name="db_detail"/> + </Menu> + <Action name="strategy_info"/> + <Action name="server_info"/> + </Menu> + <Menu name="settings"><text>&Settings</text> + <Action name="show_match" append="show_merge"/> + </Menu> +</MenuBar> + +<ToolBar fullWidth="true" newline="true" noMerge="1" name="mainToolBar"><text>Main Toolbar</text> + <Action name="clear_query"/> + <Action name="look_label"/> + <Action name="query_combo"/> + <Separator/> + <Action name="define_btn"/> + <Action name="match_btn"/> +</ToolBar> + +<StatusBar/> + +</kpartgui> diff --git a/kdict/main.cpp b/kdict/main.cpp new file mode 100644 index 00000000..235f9048 --- /dev/null +++ b/kdict/main.cpp @@ -0,0 +1,56 @@ +/* ------------------------------------------------------------- + + main.cpp (part of The KDE Dictionary Client) + + Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org> + (C) by Matthias H�zer 1998 + + This file is distributed under the Artistic License. + See LICENSE for details. + + ------------------------------------------------------------- */ + +#include <config.h> + +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <klocale.h> +#include <kdelibs_export.h> + +#include "application.h" +#include "toplevel.h" + +static KCmdLineOptions knoptions[] = +{ + { "c", 0, 0 }, + { "clipboard", I18N_NOOP("Define X11-clipboard content (selected text)"), 0 }, + { "+[word/phrase]", I18N_NOOP("Lookup the given word/phrase"), 0 }, + KCmdLineLastOption +}; + + +extern "C" KDE_EXPORT int kdemain(int argc, char* argv[]) +{ + KAboutData aboutData("kdict", + I18N_NOOP("Dictionary"), + KDICT_VERSION, + I18N_NOOP("The KDE Dictionary Client"), + KAboutData:: License_Artistic, + "Copyright (c) 1999-2001, Christian Gebauer\nCopyright (c) 1998, Matthias Hoelzer", + 0, + 0); + + aboutData.addAuthor("Christian Gebauer",I18N_NOOP("Maintainer"),"gebauer@kde.org"); + aboutData.addAuthor("Matthias Hoelzer",I18N_NOOP("Original Author"),"hoelzer@kde.org"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( knoptions ); + KUniqueApplication::addCmdLineOptions(); + + if (!Application::start()) + return 0; + + Application app; + + return app.exec(); +} diff --git a/kdict/matchview.cpp b/kdict/matchview.cpp new file mode 100644 index 00000000..32cd04dd --- /dev/null +++ b/kdict/matchview.cpp @@ -0,0 +1,473 @@ +/* ------------------------------------------------------------- + + matchview.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. + + ------------------------------------------------------------- + + MatchView This widget contains the list of matching definitions + + ------------------------------------------------------------- */ + +#include <qclipboard.h> +#include <qcombobox.h> +#include <qpushbutton.h> +#include <qheader.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qregexp.h> + +#include <kpopupmenu.h> +#include <klocale.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <kmessagebox.h> + +#include "dict.h" +#include "options.h" +#include "matchview.h" + + +//********* MatchViewItem ******************************************** + + +MatchViewItem::MatchViewItem(QListView *view, const QString &text) + : QListViewItem(view,text) +{ +} + + +MatchViewItem::MatchViewItem(QListView *view,QListViewItem *after,const QString &text) + : QListViewItem(view,after,text) +{ +} + + +MatchViewItem::MatchViewItem(QListViewItem *item,const QString &text,const QString &commandStr) +: QListViewItem(item,text), command(commandStr) +{ +} + + +MatchViewItem::MatchViewItem(QListViewItem *item,QListViewItem *after,const QString &text,const QString &commandStr) +: QListViewItem(item,after,text), command(commandStr) +{ +} + + +MatchViewItem::~MatchViewItem() +{ +} + + +void MatchViewItem::setOpen(bool o) +{ + if (o && !childCount()) { + listView()->setUpdatesEnabled(false); + + MatchViewItem *sub=0; + QString command, label; + QRegExp exp("\"*\"", true, true); + QStringList::iterator it; + for (it = subEntrys.begin(); it != subEntrys.end(); ++it) { + command = "define "; + command += (*it); + command += "\r\n"; + exp.search((*it)); + label = exp.cap(); + label = label.mid(1, label.length()-2); // remove quotes + if (sub) + sub = new MatchViewItem(this, sub, label, command); + else + sub = new MatchViewItem(this, label, command); + } + + subEntrys.clear(); + + listView()->setUpdatesEnabled(true); + } + + if (childCount()) + QListViewItem::setOpen(o); +} + + +void MatchViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment) +{ + if(command.isEmpty()) { + QFont font=p->font(); + font.setBold(true); + p->setFont(font); + } + QListViewItem::paintCell(p,cg,column,width,alignment); +} + + +//********* MatchView ****************************************** + + +MatchView::MatchView(QWidget *parent, const char *name) + : QWidget(parent,name),getOn(false),getAllOn(false) +{ + setCaption(kapp->makeStdCaption(i18n("Match List"))); + + QVBoxLayout * boxLayout = new QVBoxLayout(this, 1, 0); + + boxLayout->addSpacing(1); + w_strat = new QComboBox(false,this); + w_strat->setFixedHeight(w_strat->sizeHint().height()); + connect(w_strat,SIGNAL(activated(int)),this,SLOT(strategySelected(int))); + boxLayout->addWidget(w_strat,0); + boxLayout->addSpacing(1); + + w_list = new QListView(this); + w_list->setFocusPolicy(QWidget::StrongFocus); + w_list->header()->hide(); + w_list->addColumn("foo"); + w_list->setColumnWidthMode(0,QListView::Maximum); + w_list->setColumnWidth(0,0); + w_list->setSelectionMode(QListView::Extended); + w_list->setTreeStepSize(18); + w_list->setSorting(-1); // disable sorting + w_list->setMinimumHeight(w_strat->sizeHint().height()); + connect(w_list,SIGNAL(selectionChanged()),SLOT(enableGetButton())); + connect(w_list,SIGNAL(returnPressed(QListViewItem *)),SLOT(returnPressed(QListViewItem *))); + connect(w_list,SIGNAL(doubleClicked(QListViewItem *)),SLOT(getOneItem(QListViewItem *))); + connect(w_list,SIGNAL(mouseButtonPressed(int, QListViewItem *, const QPoint &, int)), + SLOT(mouseButtonPressed(int, QListViewItem *, const QPoint &, int))); + connect(w_list,SIGNAL(rightButtonPressed(QListViewItem *,const QPoint &,int)),SLOT(buildPopupMenu(QListViewItem *,const QPoint &,int))); + boxLayout->addWidget(w_list,1); + + boxLayout->addSpacing(1); + w_get = new QPushButton(i18n("&Get Selected"),this); + w_get->setFixedHeight(w_get->sizeHint().height()-3); + w_get->setMinimumWidth(w_get->sizeHint().width()-20); + w_get->setEnabled(false); + connect(w_get, SIGNAL(clicked()), this, SLOT(getSelected())); + boxLayout->addWidget(w_get,0); + + w_getAll = new QPushButton(i18n("Get &All"),this); + w_getAll->setFixedHeight(w_getAll->sizeHint().height()-3); + w_getAll->setMinimumWidth(w_getAll->sizeHint().width()-20); + w_getAll->setEnabled(false); + connect(w_getAll, SIGNAL(clicked()), this, SLOT(getAll())); + boxLayout->addWidget(w_getAll,0); + connect(interface,SIGNAL(matchReady(const QStringList &)),this,SLOT(newList(const QStringList &))); + rightBtnMenu = new KPopupMenu(); +} + + +MatchView::~MatchView() +{ +} + + +void MatchView::updateStrategyCombo() +{ + w_strat->clear(); + w_strat->insertStringList(global->strategies); + w_strat->setCurrentItem(global->currentStrategy); +} + + +bool MatchView::selectStrategy(const QString &strategy) const +{ + int newCurrent = global->strategies.findIndex(strategy); + if (newCurrent == -1) + return false; + else { + global->currentStrategy = newCurrent; + w_strat->setCurrentItem(global->currentStrategy); + return true; + } +} + + +void MatchView::match(const QString &query) +{ + interface->match(query.utf8()); +} + + +void MatchView::closeEvent ( QCloseEvent * e ) +{ + e->accept(); // hides the widget + emit(windowClosed()); +} + + +void MatchView::strategySelected(int num) +{ + global->currentStrategy = num; +} + + +void MatchView::enableGetButton() +{ + if (w_getAll->isEnabled()) { + w_get->setEnabled(true); + getOn = true; + } +} + + +void MatchView::mouseButtonPressed(int button, QListViewItem *, const QPoint &, int) +{ + if (button == MidButton) + emit(clipboardRequested()); +} + + +void MatchView::returnPressed(QListViewItem *) +{ + getSelected(); +} + + +void MatchView::getOneItem(QListViewItem *i) +{ + QStringList defines; + + if ((!i->childCount())&&(i->parent())) + defines.append(((MatchViewItem *)(i))->command); + else { + i = i->firstChild(); + while (i) { + defines.append(((MatchViewItem *)(i))->command); + i = i->nextSibling(); + } + } + + doGet(defines); +} + + +void MatchView::getSelected() +{ + QStringList defines; + MatchViewItem *top = static_cast<MatchViewItem*>(w_list->firstChild()); + MatchViewItem *sub; + + while (top) { + if (top->isSelected()&&(!top->subEntrys.isEmpty())) { + QString command; + QStringList::iterator it; + for (it = top->subEntrys.begin(); it != top->subEntrys.end(); ++it) { + command = "define "; + command += (*it); + command += "\r\n"; + defines.append(command); + } + } else { + sub = static_cast<MatchViewItem*>(top->firstChild()); + while (sub) { + if (top->isSelected()||sub->isSelected()) + defines.append(sub->command); + sub = static_cast<MatchViewItem*>(sub->nextSibling()); + } + } + top = static_cast<MatchViewItem*>(top->nextSibling()); + } + doGet(defines); +} + + +void MatchView::getAll() +{ + QStringList defines; + MatchViewItem *top = static_cast<MatchViewItem*>(w_list->firstChild()); + MatchViewItem *sub; + + while (top) { + if (!top->subEntrys.isEmpty()) { + QString command; + QStringList::iterator it; + for (it = top->subEntrys.begin(); it != top->subEntrys.end(); ++it) { + command = "define "; + command += (*it); + command += "\r\n"; + defines.append(command); + } + } else { + sub = static_cast<MatchViewItem*>(top->firstChild()); + while (sub) { + defines.append(sub->command); + sub = static_cast<MatchViewItem*>(sub->nextSibling()); + } + } + top = static_cast<MatchViewItem*>(top->nextSibling()); + } + doGet(defines); +} + + +void MatchView::doGet(QStringList &defines) +{ + if (defines.count() > 0) { + if (defines.count() > global->maxDefinitions) { + KMessageBox::sorry(global->topLevel,i18n("You have selected %1 definitions,\nbut Kdict will fetch only the first %2 definitions.\nYou can modify this limit in the Preferences Dialog.") + .arg(defines.count()).arg(global->maxDefinitions)); + while (defines.count()>global->maxDefinitions) + defines.pop_back(); + } + interface->getDefinitions(defines); + } +} + + +void MatchView::newList(const QStringList &matches) +{ + MatchViewItem *top=0; + bool initialOpen = (matches.count()<200); + int numDb = 0; + + rightBtnMenu->hide(); + w_list->clear(); + w_list->setColumnWidth(0,0); + w_list->setUpdatesEnabled(false); + w_get->setEnabled(false); + getOn = false; + + if (matches.isEmpty()) { + w_list->setColumnWidth(0,w_get->width()-5); + w_list->setRootIsDecorated(false); + w_getAll->setEnabled(false); + getAllOn = false; + top = new MatchViewItem(w_list,top,i18n(" No Hits")); + } else { + w_list->setRootIsDecorated(true); + w_getAll->setEnabled(true); + getAllOn = true; + QString lastDb, db, match; + + QStringList::const_iterator it; + for (it = matches.begin(); it != matches.end(); ++it) { + db = (*it).section(' ', 0, 0); + + if (db != lastDb) { + numDb++; + if (top) { + top->setOpen(initialOpen); + top = new MatchViewItem(w_list, top, db); + } else + top = new MatchViewItem(w_list, db); + top->setExpandable(true); + lastDb = db; + } + + if (top) + top->subEntrys.append(*it); + } + + if ((numDb == 1)||(initialOpen)) + top->setOpen(true); + } + + w_list->setUpdatesEnabled(true); + w_list->repaint(); + w_list->setFocus(); +} + + +// construct the right-mouse-button-popup-menu on demand +void MatchView::buildPopupMenu(QListViewItem *i, const QPoint &_point, int) +{ + rightBtnMenu->clear(); + + if ((i!=0L)&&(i->isExpandable()||i->parent())) { + popupCurrent = (MatchViewItem *)(i); + rightBtnMenu->insertItem(i18n("&Get"),this,SLOT(popupGetCurrent())); + if (!i->isExpandable()) { // toplevel item -> only "get" + rightBtnMenu->insertItem(i18n("&Match"),this,SLOT(popupMatchCurrent())); + rightBtnMenu->insertItem(i18n("&Define"),this,SLOT(popupDefineCurrent())); + } + rightBtnMenu->insertSeparator(); + } + + kapp->clipboard()->setSelectionMode(false); + QString text = kapp->clipboard()->text(); + if (text.isEmpty()) { + kapp->clipboard()->setSelectionMode(true); + text = kapp->clipboard()->text(); + } + if (!text.isEmpty()) { + popupClip = kapp->clipboard()->text(); + rightBtnMenu->insertItem(i18n("Match &Clipboard Content"),this,SLOT(popupMatchClip())); + rightBtnMenu->insertItem(SmallIcon("define_clip"),i18n("D&efine Clipboard Content"),this,SLOT(popupDefineClip())); + rightBtnMenu->insertSeparator(); + } + + int ID = rightBtnMenu->insertItem(i18n("Get &Selected"),this,SLOT(getSelected())); + rightBtnMenu->setItemEnabled(ID,getOn); + ID = rightBtnMenu->insertItem(i18n("Get &All"),this,SLOT(getAll())); + rightBtnMenu->setItemEnabled(ID,getAllOn); + + if (w_list->childCount()) { + rightBtnMenu->insertSeparator(); + rightBtnMenu->insertItem(i18n("E&xpand List"),this,SLOT(expandList())); + rightBtnMenu->insertItem(i18n("C&ollapse List"),this,SLOT(collapseList())); + } + + rightBtnMenu->popup(_point); +} + + +void MatchView::popupGetCurrent() +{ + getOneItem(popupCurrent); +} + + +void MatchView::popupDefineCurrent() +{ + emit(defineRequested(popupCurrent->text(0))); +} + + +void MatchView::popupMatchCurrent() +{ + emit(matchRequested(popupCurrent->text(0))); +} + + +void MatchView::popupDefineClip() +{ + emit(defineRequested(popupClip)); +} + + +void MatchView::popupMatchClip() +{ + emit(matchRequested(popupClip)); +} + + +void MatchView::expandList() +{ + QListViewItem *top = w_list->firstChild(); + + while (top) { + w_list->setOpen(top,true); + top = top->nextSibling(); + } +} + + +void MatchView::collapseList() +{ + w_list->setCurrentItem(w_list->firstChild()); + QListViewItem *top = w_list->firstChild(); + + while (top) { + w_list->setOpen(top,false); + top = top->nextSibling(); + } +} + +//-------------------------------- + +#include "matchview.moc" diff --git a/kdict/matchview.h b/kdict/matchview.h new file mode 100644 index 00000000..f619820e --- /dev/null +++ b/kdict/matchview.h @@ -0,0 +1,105 @@ +/* ------------------------------------------------------------- + + matchview.h (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. + + ------------------------------------------------------------- + + MatchView This widget contains the list of matching definitions + + ------------------------------------------------------------- */ + + +#ifndef _MATCHVIEW_H_ +#define _MATCHVIEW_H_ + +#include <qlistview.h> +class KPopupMenu; + + +//********* MatchViewItem ******************************************** + +class MatchViewItem : public QListViewItem +{ + +public: + + MatchViewItem(QListView *view,const QString &text); + MatchViewItem(QListView *view,QListViewItem *after,const QString &text); + MatchViewItem(QListViewItem *item,const QString &text,const QString &commandStr); + MatchViewItem(QListViewItem *item,QListViewItem *after,const QString &text,const QString &commandStr); + ~MatchViewItem(); + + void setOpen(bool o); + void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment); + + QString command; + QStringList subEntrys; +}; + + +//********* MatchView ****************************************** + + +class MatchView : public QWidget +{ + Q_OBJECT + +public: + + MatchView(QWidget *parent=0,const char *name=0); + ~MatchView(); + + void updateStrategyCombo(); + bool selectStrategy(const QString &strategy) const; + void match(const QString &query); + +signals: + + void defineRequested(const QString &query); + void matchRequested(const QString &query); + void clipboardRequested(); + void windowClosed(); + +protected: + + void closeEvent ( QCloseEvent * e ); + +private slots: + + void strategySelected(int num); + void enableGetButton(); + void mouseButtonPressed(int, QListViewItem *, const QPoint &, int); + void returnPressed(QListViewItem *i); + void getOneItem(QListViewItem *i); + void getSelected(); + void getAll(); + void doGet(QStringList &defines); + void newList(const QStringList &matches); + void buildPopupMenu(QListViewItem *, const QPoint &, int); + void popupGetCurrent(); + void popupDefineCurrent(); + void popupMatchCurrent(); + void popupDefineClip(); + void popupMatchClip(); + void expandList(); + void collapseList(); + +private: + + QComboBox *w_strat; + QListView *w_list; + QPushButton *w_get,*w_getAll; + + bool getOn, getAllOn; + + KPopupMenu *rightBtnMenu; + MatchViewItem *popupCurrent; + QString popupClip; // needed for rightbtn-popup menu +}; + +#endif diff --git a/kdict/options.cpp b/kdict/options.cpp new file mode 100644 index 00000000..a157fbf5 --- /dev/null +++ b/kdict/options.cpp @@ -0,0 +1,947 @@ +/* ------------------------------------------------------------- + + options.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. + + ------------------------------------------------------------- + + GlobalData manages all global data of Kdict + + OptionsDialog the "Preferences" dialog + + DbSetsDialog dialog for editing the user defined database sets + + ------------------------------------------------------------- */ + +#include <qcheckbox.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <qbuttongroup.h> +#include <qpainter.h> + +#include <kcolordialog.h> +#include <kconfig.h> +#include <klocale.h> +#include <knumvalidator.h> +#include <knuminput.h> +#include <klineedit.h> +#include <kcharsets.h> +#include <kfontdialog.h> +#include <kiconloader.h> + +#include "options.h" + + +//********* DictOptions ****************************************** + + +void GlobalData::read() +{ + KConfig *config=KGlobal::config(); + + // general... + config->setGroup("General"); + defineClipboard = config->readBoolEntry("Lookup_Clipboard",false); + headLayout = config->readNumEntry("Heading_Layout",0); + if ((headLayout > 2)||(headLayout < 0)) + headLayout = 0; + saveHistory = config->readBoolEntry("Save_History",true); + maxHistEntrys = config->readNumEntry("Max_History_Entrys",500); + if ((maxHistEntrys < 10)||(maxHistEntrys >5000)) + maxHistEntrys = 200; + maxBrowseListEntrys = config->readNumEntry("Max_Browse_Entrys",15); + if ((maxBrowseListEntrys < 1)||(maxBrowseListEntrys > 50)) + maxBrowseListEntrys = 15; + maxDefinitions = config->readNumEntry("Max_Definitions",2000); + if ((maxDefinitions < 100)||(maxDefinitions > 10000)) + maxDefinitions = 2000; + + //colors + config->setGroup("Colors"); + useCustomColors=config->readBoolEntry("customColors", false); + + QColor defCol=KGlobalSettings::textColor(); + c_olors[Ctext]=config->readColorEntry("textColor",&defCol); + c_olorNames[Ctext]=i18n("Text"); + + defCol=KGlobalSettings::baseColor(); + c_olors[Cbackground]=config->readColorEntry("backgroundColor",&defCol); + c_olorNames[Cbackground]=i18n("Background"); + + defCol=KGlobalSettings::highlightedTextColor(); + c_olors[CheadingsText]=config->readColorEntry("headingsTextColor",&defCol); + c_olorNames[CheadingsText]=i18n("Heading Text"); + + defCol=KGlobalSettings::highlightColor(); + c_olors[CheadingsBackground]=config->readColorEntry("headingsBackgroundColor",&defCol); + c_olorNames[CheadingsBackground]=i18n("Heading Background"); + + defCol=KGlobalSettings::linkColor(); + c_olors[Clinks]=config->readColorEntry("linksColor",&defCol); + c_olorNames[Clinks]=i18n("Link"); + + defCol=KGlobalSettings::visitedLinkColor(); + c_olors[CvisitedLinks]=config->readColorEntry("linksColor",&defCol); + c_olorNames[CvisitedLinks]=i18n("Followed Link"); + + //fonts + config->setGroup("Fonts"); + useCustomFonts=config->readBoolEntry("customFonts", false); + + QFont defFont=KGlobalSettings::generalFont(); + f_onts[Ftext]=config->readFontEntry("textFont",&defFont); + f_ontNames[Ftext]=i18n("Text"); + + defFont.setBold( true ); + defFont.setPointSize(defFont.pointSize()+2); + f_onts[Fheadings]=config->readFontEntry("headingsFont",&defFont); + f_ontNames[Fheadings]=i18n("Headings"); + + // geometry... + config->setGroup("Geometry"); + QSize invalid(-1,-1); + optSize = config->readSizeEntry("Opt_Size",&invalid); + setsSize = config->readSizeEntry("Sets_Size",&invalid); + matchSize = config->readSizeEntry("Match_Size",&invalid); + showMatchList = config->readBoolEntry("Show_MatchList",false); + splitterSizes = config->readIntListEntry("Splitter_Sizes"); + + config->setGroup("Query Combo"); + queryComboCompletionMode = (KGlobalSettings::Completion)config->readNumEntry("Completion_mode", + KGlobalSettings::completionMode()); + + config->setGroup("Query_History"); + queryHistory = config->readListEntry("History"); + + config->setGroup("DictServer"); + server = config->readEntry("Server", "dict.org"); + port = config->readNumEntry("Port", 2628); + if (port < 0) + port = 2628; + timeout = config->readNumEntry("Timeout",60); + if (timeout < 0) + timeout = 60; + pipeSize = config->readNumEntry("Pipe_Size",256); + if (pipeSize < 0) + pipeSize = 256; + idleHold = config->readNumEntry("Idle_Hold",30); + if (idleHold < 0) + idleHold = 30; + encoding=config->readEntry("encoding", "utf8"); + authEnabled = config->readBoolEntry("Auth_Enabled",false); + user = config->readEntry("User"); + secret = encryptStr(config->readEntry("Secret")); + serverDatabases = config->readListEntry("Server_Databases"); + currentDatabase = config->readNumEntry("Current_Database",0); + strategies = config->readListEntry("Strategies"); + if (strategies.isEmpty()) { + strategies.append(i18n("Spell Check")); + strategies.append(i18n("Exact")); + strategies.append(i18n("Prefix")); + } else { + strategies.remove(strategies.begin()); + strategies.prepend(i18n("Spell Check")); + } + + currentStrategy = config->readNumEntry("Current_Strategy",0); + if (currentStrategy >= strategies.count()) + currentStrategy = 0; + + config->setGroup("Database_Sets"); + databaseSets.setAutoDelete(true); + int num = config->readNumEntry("Num_Sets",0); + QStringList* temp; + QString strNum; + while (num > 0) { + temp = new QStringList(); + strNum.setNum(num); + *temp = config->readListEntry(strNum); + if (!temp->isEmpty()) { + databaseSets.prepend(temp); + num--; + } else { + delete temp; + num = 0; // stop reading... + } + } + databases = serverDatabases; + for (int i = databaseSets.count()-1;i>=0;i--) + databases.prepend(databaseSets.at(i)->first()); + databases.prepend(i18n("All Databases")); + if (currentDatabase >= databases.count()) + currentDatabase = 0; +} + + +void GlobalData::write() +{ + KConfig *config=KGlobal::config(); + + config->setGroup("General"); + config->writeEntry("Lookup_Clipboard",defineClipboard); + config->writeEntry("Heading_Layout",headLayout); + config->writeEntry("Save_History",saveHistory); + config->writeEntry("Max_History_Entrys",maxHistEntrys); + config->writeEntry("Max_Browse_Entrys",maxBrowseListEntrys); + config->writeEntry("Max_Definitions",maxDefinitions); + + config->setGroup("Colors"); + config->writeEntry("customColors",useCustomColors); + config->writeEntry("textColor", c_olors[Ctext]); + config->writeEntry("backgroundColor", c_olors[Cbackground]); + config->writeEntry("headingsTextColor", c_olors[CheadingsText]); + config->writeEntry("headingsBackgroundColor", c_olors[CheadingsBackground]); + config->writeEntry("linksColor", c_olors[Clinks]); + config->writeEntry("visitedLinksColor", c_olors[CvisitedLinks]); + + config->setGroup("Fonts"); + config->writeEntry("customFonts", useCustomFonts); + config->writeEntry("textFont", f_onts[Ftext]); + config->writeEntry("headingsFont", f_onts[Fheadings]); + + config->setGroup("Geometry"); + config->writeEntry("Opt_Size",optSize); + config->writeEntry("Sets_Size",setsSize); + config->writeEntry("Match_Size",matchSize); + config->writeEntry("Show_MatchList",showMatchList); + config->writeEntry("Splitter_Sizes",splitterSizes); + + config->setGroup("Query Combo"); + config->writeEntry("Completion_mode", (int)queryComboCompletionMode); + + config->setGroup("Query_History"); + QStringList copy; + if (saveHistory) + copy = queryHistory; + config->writeEntry("History",copy); + + config->setGroup("DictServer"); + config->writeEntry("Server", server); + config->writeEntry("Port", port); + config->writeEntry("Timeout",timeout); + config->writeEntry("Pipe_Size",pipeSize); + config->writeEntry("Idle_Hold",idleHold); + config->writeEntry("encoding", encoding); + config->writeEntry("Auth_Enabled",authEnabled); + config->writeEntry("User", user); + config->writeEntry("Secret", encryptStr(secret)); + config->writeEntry("Server_Databases",serverDatabases); + config->writeEntry("Current_Database",currentDatabase); + config->writeEntry("Strategies",strategies); + config->writeEntry("Current_Strategy",currentStrategy); + + config->setGroup("Database_Sets"); + config->writeEntry("Num_Sets",databaseSets.count()); + QString strNum; + for (unsigned int i = 0;i<databaseSets.count();i++) + config->writeEntry(strNum.setNum(i+1),*databaseSets.at(i)); +} + + +QColor GlobalData::defaultColor(int i) +{ + switch(i) { + case Ctext: + return KGlobalSettings::textColor(); + break; + + case Cbackground: + return KGlobalSettings::baseColor(); + break; + + case CheadingsText: + return KGlobalSettings::highlightedTextColor(); + break; + + case CheadingsBackground: + return KGlobalSettings::highlightColor(); + break; + + case Clinks: + return KGlobalSettings::linkColor(); + break; + + case CvisitedLinks: + return KGlobalSettings::visitedLinkColor(); + break; + + } + + return KGlobalSettings::baseColor(); +} + + +QColor GlobalData::textColor() +{ + if(useCustomColors) + return c_olors[Ctext]; + else + return defaultColor(Ctext); +} + + +QColor GlobalData::backgroundColor() +{ + if(useCustomColors) + return c_olors[Cbackground]; + else + return defaultColor(Cbackground); +} + + +QColor GlobalData::headingsTextColor() +{ + if(useCustomColors) + return c_olors[CheadingsText]; + else + return defaultColor(CheadingsText); +} + + +QColor GlobalData::headingsBackgroundColor() +{ + if(useCustomColors) + return c_olors[CheadingsBackground]; + else + return defaultColor(CheadingsBackground); +} + + +QColor GlobalData::linksColor() +{ + if(useCustomColors) + return c_olors[Clinks]; + else + return defaultColor(Clinks); +} + + +QColor GlobalData::visitedLinksColor() +{ + if(useCustomColors) + return c_olors[CvisitedLinks]; + else + return defaultColor(CvisitedLinks); +} + + +QFont GlobalData::defaultFont(int i) +{ + QFont font = KGlobalSettings::generalFont(); + + if (font.pointSize() < 5) + font.setPointSize(12); + + if (i==Fheadings) + font.setPointSize(font.pointSize()+5); + + return font; +} + + +QFont GlobalData::textFont() +{ + if(useCustomFonts) + return f_onts[Ftext]; + else + return defaultFont(Ftext); +} + + +QFont GlobalData::headingsFont() +{ + if(useCustomFonts) + return f_onts[Fheadings]; + else + return defaultFont(Fheadings); +} + + +// simple password scrambling... +QString GlobalData::encryptStr(const QString& aStr) +{ + uint i,val,len = aStr.length(); + QString result; + + for (i=0; i<len; i++) + { + val = aStr[i] - ' '; + val = (255-' ') - val; + result += (char)(val + ' '); + } + + return result; +} + + +//********* OptionsDialog::DialogListBox ***************************** + + +OptionsDialog::DialogListBox::DialogListBox(bool alwaysIgnore, QWidget * parent, const char * name) + : QListBox(parent, name), a_lwaysIgnore(alwaysIgnore) +{ +} + + +OptionsDialog::DialogListBox::~DialogListBox() +{ +} + + +void OptionsDialog::DialogListBox::keyPressEvent(QKeyEvent *e) +{ + if ((a_lwaysIgnore || !(hasFocus()&&isVisible()))&&((e->key()==Key_Enter)||(e->key()==Key_Return))) + e->ignore(); + else + QListBox::keyPressEvent(e); +} + + +//********* OptionsDialog::ColorListItem ***************************** + + +OptionsDialog::ColorListItem::ColorListItem( const QString &text, const QColor &color ) + : QListBoxText(text), mColor( color ) +{ +} + + +OptionsDialog::ColorListItem::~ColorListItem() +{ +} + + +void OptionsDialog::ColorListItem::paint( QPainter *p ) +{ + QFontMetrics fm = p->fontMetrics(); + int h = fm.height(); + + p->drawText( 30+3*2, fm.ascent() + fm.leading()/2, text() ); + + p->setPen( Qt::black ); + p->drawRect( 3, 1, 30, h-1 ); + p->fillRect( 4, 2, 28, h-3, mColor ); +} + + +int OptionsDialog::ColorListItem::height(const QListBox *lb ) const +{ + return( lb->fontMetrics().lineSpacing()+1 ); +} + + +int OptionsDialog::ColorListItem::width(const QListBox *lb ) const +{ + return( 30 + lb->fontMetrics().width( text() ) + 6 ); +} + + +//********* OptionsDialog::FontListItem ***************************** + + +OptionsDialog::FontListItem::FontListItem( const QString &name, const QFont &font ) + : QListBoxText(name), f_ont(font) +{ + fontInfo = QString("[%1 %2]").arg(f_ont.family()).arg(f_ont.pointSize()); +} + + +OptionsDialog::FontListItem::~FontListItem() +{ +} + + +void OptionsDialog::FontListItem::setFont(const QFont &font) +{ + f_ont = font; + fontInfo = QString("[%1 %2]").arg(f_ont.family()).arg(f_ont.pointSize()); +} + + +void OptionsDialog::FontListItem::paint( QPainter *p ) +{ + QFont fnt = p->font(); + fnt.setWeight(QFont::Bold); + p->setFont(fnt); + int fontInfoWidth = p->fontMetrics().width(fontInfo); + int h = p->fontMetrics().ascent() + p->fontMetrics().leading()/2; + p->drawText(2, h, fontInfo ); + fnt.setWeight(QFont::Normal); + p->setFont(fnt); + p->drawText(5 + fontInfoWidth, h, text() ); +} + + +int OptionsDialog::FontListItem::width(const QListBox *lb ) const +{ + return( lb->fontMetrics().width(fontInfo) + lb->fontMetrics().width(text()) + 20 ); +} + + +//********* OptionsDialog ****************************************** + + +OptionsDialog::OptionsDialog(QWidget *parent, const char *name) + : KDialogBase(IconList, i18n("Configure"), Help|Default|Ok|Apply|Cancel, Ok, parent, name, false, true) +{ + + //******** Server ************************************ + serverTab = addPage(i18n("Server"),i18n("DICT Server Configuration"), BarIcon("network", KIcon::SizeMedium )); + QGridLayout* grid = new QGridLayout(serverTab,10,3,0,spacingHint()); + + w_server = new KLineEdit(serverTab); + w_server->setText(global->server); + QLabel *l = new QLabel(w_server, i18n("Host&name:"), serverTab); + grid->addWidget(l,0,0); + grid->addMultiCellWidget(w_server,0,0,1,2); + connect( w_server, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) ); + + w_port = new KLineEdit(serverTab); + w_port->setValidator(new KIntValidator(0,65536,this)); + w_port->setText(QString::number(global->port)); + l = new QLabel(w_port, i18n("&Port:"), serverTab); + grid->addWidget(l,1,0); + grid->addWidget(w_port,1,1); + connect( w_port, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) ); + + w_idleHold = new KIntSpinBox(0,300,5,0,10,serverTab); + w_idleHold->setSuffix(i18n(" sec")); + w_idleHold->setValue(global->idleHold); + l = new QLabel(w_idleHold, i18n("Hold conn&ection for:"), serverTab); + grid->addWidget(l,2,0); + grid->addWidget(w_idleHold,2,1); + connect( w_idleHold, SIGNAL( valueChanged(int) ), this, SLOT( slotChanged() ) ); + + w_timeout = new KIntSpinBox(5,600,5,5,10,serverTab); + w_timeout->setSuffix(i18n(" sec")); + w_timeout->setValue(global->timeout); + l = new QLabel(w_timeout, i18n("T&imeout:"), serverTab); + grid->addWidget(l,3,0); + grid->addWidget(w_timeout,3,1); + connect( w_timeout, SIGNAL( valueChanged(int) ), this, SLOT( slotChanged() ) ); + + w_pipesize = new KIntSpinBox(100,5000,2,2,10,serverTab); + w_pipesize->setSuffix(i18n(" bytes")); + w_pipesize->setValue(global->pipeSize); + l = new QLabel(w_pipesize, i18n("Command &buffer:"), serverTab); + grid->addWidget(l,4,0); + grid->addWidget(w_pipesize,4,1); + connect( w_pipesize, SIGNAL( valueChanged(int) ), this, SLOT( slotChanged() ) ); + + QStringList encodingNames = KGlobal::charsets()->descriptiveEncodingNames(); + int i=0,x=0; + for ( QStringList::Iterator it = encodingNames.begin(); it != encodingNames.end(); ++it ) { + if (KGlobal::charsets()->encodingForName(*it)==global->encoding) { + x = i; + break; + } + i++; + } + w_encoding = new QComboBox(serverTab); + w_encoding->insertStringList(encodingNames); + w_encoding->setCurrentItem(x); + l = new QLabel(w_encoding, i18n("Encod&ing:"), serverTab); + grid->addWidget(l,5,0); + grid->addMultiCellWidget(w_encoding,5,5,1,2); + connect( w_encoding, SIGNAL( activated(int) ), this, SLOT( slotChanged() ) ); + + w_auth = new QCheckBox(i18n("Server requires a&uthentication"),serverTab); + w_auth->setChecked(global->authEnabled); + grid->addMultiCellWidget(w_auth,6,6,0,2); + connect( w_auth, SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) ); + connect(w_auth,SIGNAL(toggled(bool)),SLOT(slotAuthRequiredToggled(bool))); + + w_user = new KLineEdit(serverTab); + w_user->setText(global->user); + l_user = new QLabel(w_user, i18n("U&ser:"),serverTab); + grid->addWidget(l_user,7,0); + grid->addMultiCellWidget(w_user,7,7,1,2); + connect( w_user, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) ); + + w_secret = new KLineEdit(serverTab); + w_secret->setEchoMode(QLineEdit::Password); + w_secret->setText(global->secret); + l_secret = new QLabel(w_secret, i18n("Pass&word:"), serverTab); + grid->addWidget(l_secret,8,0); + grid->addMultiCellWidget(w_secret,8,8,1,2); + connect( w_secret, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) ); + + slotAuthRequiredToggled( w_auth->isChecked() ); + + grid->setColStretch(1,2); + grid->setColStretch(2,2); + + //************ Appearance *************************** + appTab = addPage(i18n("Appearance"),i18n("Customize Visual Appearance"), BarIcon("appearance", KIcon::SizeMedium )); + + QGridLayout *topL=new QGridLayout(appTab, 8, 3, 0, spacingHint()); + + //color-list + c_List = new DialogListBox(false, appTab); + topL->addMultiCellWidget(c_List,1,3,0,1); + connect(c_List, SIGNAL(selected(QListBoxItem*)),SLOT(slotColItemSelected(QListBoxItem*))); + connect(c_List, SIGNAL(selectionChanged()), SLOT(slotColSelectionChanged())); + + c_olorCB = new QCheckBox(i18n("&Use custom colors"),appTab); + topL->addWidget(c_olorCB,0,0); + connect(c_olorCB, SIGNAL(toggled(bool)), this, SLOT(slotColCheckBoxToggled(bool))); + connect(c_olorCB, SIGNAL(toggled(bool) ), this, SLOT( slotChanged())); + + c_olChngBtn=new QPushButton(i18n("Cha&nge..."), appTab); + connect(c_olChngBtn, SIGNAL(clicked()), SLOT(slotColChangeBtnClicked())); + topL->addWidget(c_olChngBtn,1,2); + + c_olDefBtn=new QPushButton(i18n("Default&s"), appTab); + connect(c_olDefBtn, SIGNAL(clicked()), SLOT(slotColDefaultBtnClicked())); + topL->addWidget(c_olDefBtn,2,2); + connect(c_olDefBtn, SIGNAL(clicked()), SLOT(slotChanged())); + + //font-list + f_List = new DialogListBox(false, appTab); + topL->addMultiCellWidget(f_List,5,7,0,1); + connect(f_List, SIGNAL(selected(QListBoxItem*)),SLOT(slotFontItemSelected(QListBoxItem*))); + connect(f_List, SIGNAL(selectionChanged()),SLOT(slotFontSelectionChanged())); + + f_ontCB = new QCheckBox(i18n("Use custom &fonts"),appTab); + topL->addWidget(f_ontCB,4,0); + connect(f_ontCB, SIGNAL(toggled(bool)), SLOT(slotFontCheckBoxToggled(bool))); + connect(f_ontCB, SIGNAL(toggled(bool)), SLOT(slotChanged())); + + f_ntChngBtn=new QPushButton(i18n("Chang&e..."), appTab); + connect(f_ntChngBtn, SIGNAL(clicked()), SLOT(slotFontChangeBtnClicked())); + topL->addWidget(f_ntChngBtn,5,2); + + f_ntDefBtn=new QPushButton(i18n("Defaul&ts"), appTab); + connect(f_ntDefBtn, SIGNAL(clicked()), SLOT(slotFontDefaultBtnClicked())); + topL->addWidget(f_ntDefBtn,6,2); + connect(f_ntDefBtn, SIGNAL(clicked()), SLOT(slotChanged())); + + topL->setColStretch(1,2); + topL->setColStretch(2,0); + topL->setRowStretch(3,1); + topL->setRowStretch(7,1); + topL->setResizeMode(QLayout::Minimum); + + //init + c_olorCB->setChecked(global->useCustomColors); + slotColCheckBoxToggled(global->useCustomColors); + for(int i=0; i<global->colorCount(); i++) + c_List->insertItem(new ColorListItem(global->colorName(i), global->color(i))); + + f_ontCB->setChecked(global->useCustomFonts); + slotFontCheckBoxToggled(global->useCustomFonts); + for(int i=0; i<global->fontCount(); i++) + f_List->insertItem(new FontListItem(global->fontName(i), global->font(i))); + + //************ Layout *************************** + layoutTab = addPage(i18n("Layout"),i18n("Customize Output Format"), BarIcon("text_left", KIcon::SizeMedium )); + + QVBoxLayout *vbox = new QVBoxLayout(layoutTab, 0, spacingHint()); + + QButtonGroup *bGroup = new QButtonGroup(i18n("Headings"),layoutTab); + QVBoxLayout *bvbox = new QVBoxLayout(bGroup,8,5); + + bvbox->addSpacing(fontMetrics().lineSpacing()-4); + w_layout[0] = new QRadioButton(i18n("O&ne heading for each database"),bGroup); + w_layout[0]->setChecked(global->headLayout == 0); + bvbox->addWidget(w_layout[0],1); + w_layout[1] = new QRadioButton(i18n("A&s above, with separators between the definitions"),bGroup); + w_layout[1]->setChecked(global->headLayout == 1); + bvbox->addWidget(w_layout[1],1); + w_layout[2] = new QRadioButton(i18n("A separate heading for &each definition"),bGroup); + w_layout[2]->setChecked(global->headLayout == 2); + bvbox->addWidget(w_layout[2],1); + connect(w_layout[0], SIGNAL(toggled(bool)), SLOT(slotChanged())); + connect(w_layout[1], SIGNAL(toggled(bool)), SLOT(slotChanged())); + connect(w_layout[2], SIGNAL(toggled(bool)), SLOT(slotChanged())); + + vbox->addWidget(bGroup,0); + vbox->addStretch(1); + + //************ Other *************************** + otherTab = addPage(i18n("Miscellaneous"),i18n("Various Settings"), BarIcon("misc", KIcon::SizeMedium )); + + vbox = new QVBoxLayout(otherTab, 0, spacingHint()); + + QGroupBox *group = new QGroupBox(i18n("Limits"),otherTab); + + grid = new QGridLayout(group,4,2,8,5); + grid->addRowSpacing(0, fontMetrics().lineSpacing()-4); + + w_MaxDefinitions = new KIntSpinBox(100,10000,100,100,10,group); + w_MaxDefinitions->setValue(global->maxDefinitions); + l = new QLabel(w_MaxDefinitions, i18n("De&finitions:"), group); + grid->addWidget(l,1,0); + grid->addWidget(w_MaxDefinitions,1,1); + connect(w_MaxDefinitions, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + + w_Maxbrowse = new KIntSpinBox(1,100,1,1,10,group); + w_Maxbrowse->setValue(global->maxBrowseListEntrys); + l = new QLabel(w_Maxbrowse, i18n("Cached &results:"), group); + grid->addWidget(l,2,0); + grid->addWidget(w_Maxbrowse,2,1); + connect(w_Maxbrowse, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + + w_Maxhist = new KIntSpinBox(10,5000,10,10,10,group); + w_Maxhist->setValue(global->maxHistEntrys); + l = new QLabel(w_Maxhist, i18n("Hi&story entries:"), group); + grid->addWidget(l,3,0); + grid->addWidget(w_Maxhist,3,1); + connect(w_Maxhist, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + + grid->setColStretch(1,1); + + vbox->addWidget(group,0); + + group = new QGroupBox(i18n("Other"),otherTab); + + QVBoxLayout *vbox2 = new QVBoxLayout(group, 8, 5); + + vbox2->addSpacing(fontMetrics().lineSpacing()-4); + + w_Savehist = new QCheckBox(i18n("Sa&ve history on exit"),group); + w_Savehist->setChecked(global->saveHistory); + vbox2->addWidget(w_Savehist,0); + connect(w_Savehist, SIGNAL(toggled(bool)), SLOT(slotChanged())); + + w_Clipboard = new QCheckBox(i18n("D&efine selected text on start"),group); + w_Clipboard->setChecked(global->defineClipboard); + vbox2->addWidget(w_Clipboard,1); + connect(w_Clipboard, SIGNAL(toggled(bool)), SLOT(slotChanged())); + + vbox->addWidget(group,0); + vbox->addStretch(2); + + setHelp("preferences"); + + if (global->optSize.isValid()) + resize(global->optSize); + else + resize(300,200); + enableButton( Apply, false ); + configChanged = false; +} + + +OptionsDialog::~OptionsDialog() +{ + global->optSize = size(); +} + + +void OptionsDialog::slotApply() +{ + global->server = w_server->text(); + global->port = w_port->text().toInt(); + global->timeout = w_timeout->value(); + global->idleHold = w_idleHold->value(); + global->pipeSize = w_pipesize->value(); + global->encoding = KGlobal::charsets()->encodingForName(w_encoding->currentText()); + global->authEnabled = w_auth->isChecked(); + global->user = w_user->text(); + global->secret = w_secret->text(); + global->useCustomColors=c_olorCB->isChecked(); + for(int i=0; i<global->colorCount(); i++) + global->c_olors[i] = (static_cast<ColorListItem*>(c_List->item(i)))->color(); + + global->useCustomFonts=f_ontCB->isChecked(); + for(int i=0; i<global->fontCount(); i++) + global->f_onts[i] = (static_cast<FontListItem*>(f_List->item(i)))->font(); + if (w_layout[0]->isChecked()) + global->headLayout = 0; + else + if (w_layout[1]->isChecked()) + global->headLayout = 1; + else + global->headLayout = 2; + global->maxDefinitions = w_MaxDefinitions->value(); + global->maxBrowseListEntrys = w_Maxbrowse->value(); + global->maxHistEntrys = w_Maxhist->value(); + global->defineClipboard = w_Clipboard->isChecked(); + global->saveHistory = w_Savehist->isChecked(); + emit(optionsChanged()); + enableButton( Apply, false ); + configChanged = false; +} + + +void OptionsDialog::slotOk() +{ + if( configChanged ) + slotApply(); + KDialogBase::slotOk(); +} + + +void OptionsDialog::slotDefault() +{ + QStringList encodingNames; + int i=0,x=0; + + switch(activePageIndex()) { + case 0: + w_server->setText("dict.org"); + w_port->setText("2628"); + w_idleHold->setValue(30); + w_timeout->setValue(60); + w_pipesize->setValue(256); + encodingNames = KGlobal::charsets()->descriptiveEncodingNames(); + for ( QStringList::Iterator it = encodingNames.begin(); it != encodingNames.end(); ++it ) { + if (KGlobal::charsets()->encodingForName(*it)=="utf8") + x = i; + i++; + } + w_encoding->setCurrentItem(x); + w_auth->setChecked(false); + w_user->clear(); + w_user->setEnabled(false); + w_secret->clear(); + w_secret->setEnabled(false); + break; + case 1: + c_olorCB->setChecked(false); + slotColCheckBoxToggled(false); + slotColDefaultBtnClicked(); + f_ontCB->setChecked(false); + slotFontCheckBoxToggled(false); + slotFontDefaultBtnClicked(); + break; + case 2: + w_layout[0]->setChecked(true); + break; + case 3: + w_MaxDefinitions->setValue(2000); + w_Maxbrowse->setValue(15); + w_Maxhist->setValue(500); + w_Savehist->setChecked(true); + w_Clipboard->setChecked(false); + } +} + + +void OptionsDialog::slotAuthRequiredToggled( bool enable ) +{ + l_user->setEnabled( enable ); + l_secret->setEnabled( enable ); + w_user->setEnabled( enable ); + w_secret->setEnabled( enable ); +} + + +void OptionsDialog::slotColCheckBoxToggled(bool b) +{ + c_List->setEnabled(b); + c_olDefBtn->setEnabled(b); + c_olChngBtn->setEnabled(b && (c_List->currentItem()!=-1)); + if (b) c_List->setFocus(); +} + + +// show color dialog for the entry +void OptionsDialog::slotColItemSelected(QListBoxItem *it) +{ + if (it) { + ColorListItem *colorItem = static_cast<ColorListItem*>(it); + QColor col = colorItem->color(); + int result = KColorDialog::getColor(col,this); + + if (result == KColorDialog::Accepted) { + colorItem->setColor(col); + c_List->triggerUpdate(false); + slotChanged(); + } + } +} + + +void OptionsDialog::slotColDefaultBtnClicked() +{ + ColorListItem *colorItem; + for(int i=0; i < global->colorCount(); i++) { + colorItem=static_cast<ColorListItem*>(c_List->item(i)); + colorItem->setColor(global->defaultColor(i)); + } + c_List->triggerUpdate(true); + c_List->repaint(true); +} + + +void OptionsDialog::slotColChangeBtnClicked() +{ + if(c_List->currentItem()!=-1) + slotColItemSelected(c_List->item(c_List->currentItem())); +} + + +void OptionsDialog::slotColSelectionChanged() +{ + c_olChngBtn->setEnabled(c_List->currentItem()!=-1); +} + + +void OptionsDialog::slotFontCheckBoxToggled(bool b) +{ + f_List->setEnabled(b); + f_ntDefBtn->setEnabled(b); + f_ntChngBtn->setEnabled(b && (f_List->currentItem()!=-1)); + if (b) f_List->setFocus(); +} + + +// show font dialog for the entry +void OptionsDialog::slotFontItemSelected(QListBoxItem *it) +{ + if (it) { + FontListItem *fontItem = static_cast<FontListItem*>(it); + QFont font = fontItem->font(); + int result = KFontDialog::getFont(font,false,this); + + if (result == KFontDialog::Accepted) { + fontItem->setFont(font); + f_List->triggerUpdate(false); + slotChanged(); + } + } +} + + +void OptionsDialog::slotFontDefaultBtnClicked() +{ + FontListItem *fontItem; + for(int i=0; i < global->fontCount(); i++) { + fontItem=static_cast<FontListItem*>(f_List->item(i)); + fontItem->setFont(global->defaultFont(i)); + } + f_List->triggerUpdate(false); +} + + +void OptionsDialog::slotFontChangeBtnClicked() +{ + if(f_List->currentItem()!=-1) + slotFontItemSelected(f_List->item(f_List->currentItem())); +} + + +void OptionsDialog::slotFontSelectionChanged() +{ + f_ntChngBtn->setEnabled(f_List->currentItem()!=-1); +} + +void OptionsDialog::slotChanged() +{ + enableButton( Apply, true ); + configChanged = true; +} + + +//-------------------------------- + +#include "options.moc" diff --git a/kdict/options.h b/kdict/options.h new file mode 100644 index 00000000..ecef7606 --- /dev/null +++ b/kdict/options.h @@ -0,0 +1,229 @@ +/* ------------------------------------------------------------- + + options.h (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. + + ------------------------------------------------------------- + + GlobalData manages all global data of Kdict + DialgoListBox a list box which ignores Enter, usefull for dialogs + OptionsDialog the "Preferences" dialog + + ------------------------------------------------------------- */ + +#ifndef _KDICT_OPTIONS_H_ +#define _KDICT_OPTIONS_H_ + +#include <qlistbox.h> +#include <kdialogbase.h> +#include <kglobalsettings.h> + +class QLineEdit; +class QCheckBox; +class QComboBox; +class QRadioButton; + +class KColorButton; +class KLineEdit; +class KIntSpinBox; + + +//********* GlobalData ****************************************** + +#define COL_CNT 6 +#define FNT_CNT 2 + +class GlobalData +{ + +public: + + enum ColorIndex { Ctext=0, Cbackground=1, CheadingsText=2, CheadingsBackground=3, Clinks=4, CvisitedLinks=5 }; + enum FontIndex { Ftext=0, Fheadings=1 }; + + void read(); + void write(); + + // colors... + const QColor& color(int i) { return c_olors[i]; } + const QString& colorName(int i) { return c_olorNames[i]; } + int colorCount() const { return COL_CNT; } + QColor defaultColor(int i); + bool useCustomColors; + QColor textColor(); + QColor backgroundColor(); + QColor headingsTextColor(); + QColor headingsBackgroundColor(); + QColor linksColor(); + QColor visitedLinksColor(); + + // fonts... + const QFont& font(int i) { return f_onts[i]; } + const QString& fontName(int i) { return f_ontNames[i]; } + int fontCount() const { return FNT_CNT; } + QFont defaultFont(int); + bool useCustomFonts; + QFont textFont(); + QFont headingsFont(); + + QString encryptStr(const QString& aStr); + + bool defineClipboard; // define clipboard content on startup? + + QSize optSize,setsSize,matchSize; // window geometry + bool showMatchList; + QValueList<int> splitterSizes; + + KGlobalSettings::Completion queryComboCompletionMode; + + QStringList queryHistory; + bool saveHistory; // save query history to disk on exit? + unsigned int maxHistEntrys, maxBrowseListEntrys, maxDefinitions; + int headLayout; + + QString server; // network client... + int port,timeout,pipeSize,idleHold; + QString encoding; + bool authEnabled; + QString user, secret; + QStringList serverDatabases, databases, strategies; + QPtrList<QStringList> databaseSets; + unsigned int currentDatabase, currentStrategy; + + QColor c_olors[COL_CNT]; + QString c_olorNames[COL_CNT]; + QFont f_onts[FNT_CNT]; + QString f_ontNames[FNT_CNT]; + + QWidget *topLevel; +}; + +extern GlobalData *global; + + +//********* OptionsDialog ****************************************** + + +class OptionsDialog : public KDialogBase +{ + Q_OBJECT + +public: + + OptionsDialog(QWidget *parent=0, const char *name=0); + ~OptionsDialog(); + + //=================================================================================== + + class DialogListBox : public QListBox { + + public: + // alwaysIgnore==false: enter is ignored when the widget isn't visible/out of focus + DialogListBox(bool alwaysIgnore=false, QWidget * parent=0, const char * name=0); + ~DialogListBox(); + + protected: + void keyPressEvent( QKeyEvent *e ); + + bool a_lwaysIgnore; + }; + + //=================================================================================== + + class ColorListItem : public QListBoxText { + + public: + ColorListItem( const QString &text, const QColor &color=Qt::black ); + ~ColorListItem(); + const QColor& color() { return mColor; } + void setColor( const QColor &color ) { mColor = color; } + + protected: + virtual void paint( QPainter * ); + virtual int height( const QListBox * ) const; + virtual int width( const QListBox * ) const; + + private: + QColor mColor; + }; + + //=================================================================================== + + class FontListItem : public QListBoxText { + + public: + FontListItem( const QString &name, const QFont & ); + ~FontListItem(); + const QFont& font() { return f_ont; } + void setFont( const QFont &); + protected: + virtual void paint( QPainter * ); + virtual int width( const QListBox * ) const; + + private: + QFont f_ont; + QString fontInfo; + }; + + //=================================================================================== + +signals: + + void optionsChanged(); + +protected slots: + void slotApply(); + void slotOk(); + void slotDefault(); + void slotChanged(); + + //server + void slotAuthRequiredToggled( bool ); + + //colors + void slotColCheckBoxToggled(bool b); + void slotColItemSelected(QListBoxItem *); // show color dialog for the entry + void slotColDefaultBtnClicked(); + void slotColChangeBtnClicked(); + void slotColSelectionChanged(); + + //fonts + void slotFontCheckBoxToggled(bool b); + void slotFontItemSelected(QListBoxItem *); // show font dialog for the entry + void slotFontDefaultBtnClicked(); + void slotFontChangeBtnClicked(); + void slotFontSelectionChanged(); + +private: + + QFrame *serverTab; + QLabel *l_user, *l_secret; + KLineEdit *w_server, *w_user, *w_secret, *w_port; + QComboBox *w_encoding; + QCheckBox *w_auth; + KIntSpinBox *w_idleHold,*w_timeout,*w_pipesize; + + QFrame *appTab; + DialogListBox *c_List, + *f_List; + QCheckBox *c_olorCB, + *f_ontCB; + QPushButton *c_olDefBtn, + *c_olChngBtn, + *f_ntDefBtn, + *f_ntChngBtn; + + QFrame *layoutTab; + QRadioButton *w_layout[3]; + + QFrame *otherTab; + QCheckBox *w_Clipboard, *w_Savehist; + KIntSpinBox *w_Maxhist, *w_Maxbrowse, *w_MaxDefinitions; + bool configChanged; +}; + +#endif diff --git a/kdict/pics/Makefile.am b/kdict/pics/Makefile.am new file mode 100644 index 00000000..dfb183ec --- /dev/null +++ b/kdict/pics/Makefile.am @@ -0,0 +1,2 @@ +kdicticondir = $(kde_datadir)/kdict/icons +kdicticon_ICON = define_clip query_erase diff --git a/kdict/pics/cr16-action-define_clip.png b/kdict/pics/cr16-action-define_clip.png Binary files differnew file mode 100644 index 00000000..dd83280b --- /dev/null +++ b/kdict/pics/cr16-action-define_clip.png diff --git a/kdict/pics/cr16-action-query_erase.png b/kdict/pics/cr16-action-query_erase.png Binary files differnew file mode 100644 index 00000000..0fb00f91 --- /dev/null +++ b/kdict/pics/cr16-action-query_erase.png diff --git a/kdict/pics/cr22-action-define_clip.png b/kdict/pics/cr22-action-define_clip.png Binary files differnew file mode 100644 index 00000000..3ab1b0cc --- /dev/null +++ b/kdict/pics/cr22-action-define_clip.png diff --git a/kdict/pics/cr32-action-define_clip.png b/kdict/pics/cr32-action-define_clip.png Binary files differnew file mode 100644 index 00000000..f0c56ca4 --- /dev/null +++ b/kdict/pics/cr32-action-define_clip.png diff --git a/kdict/queryview.cpp b/kdict/queryview.cpp new file mode 100644 index 00000000..a2674d68 --- /dev/null +++ b/kdict/queryview.cpp @@ -0,0 +1,618 @@ +/* ------------------------------------------------------------- + + 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" diff --git a/kdict/queryview.h b/kdict/queryview.h new file mode 100644 index 00000000..e942d297 --- /dev/null +++ b/kdict/queryview.h @@ -0,0 +1,178 @@ +/* ------------------------------------------------------------- + + queryview.h (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. + + ------------------------------------------------------------- + + SaveHelper network transparent file saving + BrowseData data structure used for caching definitions + DictHTMLPart handling of middle mouse button clicks + QueryView widget that displays the definitions + + ------------------------------------------------------------- */ + +#ifndef _QUERYVIEW_H_ +#define _QUERYVIEW_H_ + +#include <qvbox.h> +#include <khtml_part.h> + +class QFile; +class KTempFile; +class KPopupMenu; +class DictComboAction; + + +QString getShortString(QString str,unsigned int length); + + +//******** SaveHelper ******************************************* + + +class SaveHelper { + +public: + + SaveHelper(const QString &saveName, const QString &filter, QWidget *parent); + ~SaveHelper(); + + // returns a file open for writing + QFile* getFile(const QString &dialogTitle); + +private: + + QWidget *p_arent; + QString s_aveName, f_ilter; + KURL url; + QFile* file; + KTempFile* tmpFile; + static QString lastPath; + +}; + + +//******** BrowseData ****************************************** + + +class BrowseData +{ + +public: + + BrowseData(const QString &Nhtml, const QString &NqueryText); + + QString html; + QString queryText; + int xPos,yPos; +}; + + +//********* DictHTMLPart *************************************** + +class DictHTMLPart : public KHTMLPart +{ + Q_OBJECT + +public: + + DictHTMLPart(QWidget *parentWidget = 0, const char *widgetname = 0); + ~DictHTMLPart(); + +signals: + void middleButtonClicked(); + +protected: + + virtual void khtmlMouseReleaseEvent(khtml::MouseReleaseEvent *event); + +}; + +//********* QueryView ****************************************** + + +class QueryView : public QVBox +{ + Q_OBJECT + +public: + + QueryView(QWidget *_parent = 0L); + ~QueryView(); + + void setActions(KToolBarPopupAction* NactBack, KToolBarPopupAction* NactForward, DictComboAction* NactQueryCombo); + + bool browseBackPossible() const; + bool browseForwardPossible() const; + + void stop(); + +public slots: + void optionsChanged(); + void printQuery(); + void saveQuery(); + void browseBack(); + void browseForward(); + void selectAll(); + void copySelection(); + void showFindDialog(); + +signals: + + void defineRequested(const QString &query); + void matchRequested(const QString &query); + void clipboardRequested(); + void enableCopy(bool selected); // emited when the user selects/deselects text + void enablePrintSave(); + void renderingStarted(); + void renderingStopped(); + void newCaption(const QString&); + +protected: + + void paletteChange ( const QPalette & ); + void fontChange ( const QFont & ); + + void saveCurrentResultPos(); + void showResult(); + +protected slots: + + void resultReady(const QString &result, const QString &query); + void partCompleted(); + void slotURLRequest (const KURL &url, const KParts::URLArgs &args); + void middleButtonClicked(); + void buildPopupMenu(const QString &url, const QPoint &point); + void popupDefineLink(); + void popupMatchLink(); + void popupOpenLink(); + void popupDefineSelect(); + void popupMatchSelect(); + void popupDbInfo(); + void enableAction(const char *, bool); + void browseBack(int); + void browseForward(int); + void updateBrowseActions(); + +private: + + DictHTMLPart *part; // Widgets + + KToolBarPopupAction *actBack, *actForward; + DictComboAction *actQueryCombo; + + KPopupMenu *rightBtnMenu; + QString popupLink,popupSelect; // needed for rightbtn-popup menu + + QPtrList<BrowseData> browseList; + unsigned int browsePos; // position in browseList + QString currentHTMLHeader; + + bool isRendering; +}; + +#endif diff --git a/kdict/sets.cpp b/kdict/sets.cpp new file mode 100644 index 00000000..0a252cec --- /dev/null +++ b/kdict/sets.cpp @@ -0,0 +1,326 @@ +/* ------------------------------------------------------------- + + sets.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. + + ------------------------------------------------------------- + + DbSetsDialog dialog for editing the user defined database sets + + ------------------------------------------------------------- */ + +#include <qpushbutton.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qcombobox.h> + +#include <kiconloader.h> +#include <kseparator.h> +#include <klocale.h> + +#include "options.h" +#include "sets.h" + + +//********* DbSetsDialog ****************************************** + + +DbSetsDialog::DbSetsDialog(QWidget *parent, const char *name) + : KDialogBase(Plain, i18n("Database Sets"),Close | Help, Close, parent, name, false, true) +{ + QFrame* page=plainPage(); + + QStringList sets; + for(unsigned int i=1;i<global->databaseSets.count()+1;i++) + sets.append(global->databases[i]); + + QVBoxLayout * topLayout = new QVBoxLayout(page, 0, 0); + + QHBoxLayout * subLayout1 = new QHBoxLayout(5); + topLayout->addLayout(subLayout1,0); + + w_set = new QComboBox(true,page); + w_set->setFixedHeight(w_set->sizeHint().height()); + w_set->setInsertionPolicy (QComboBox::NoInsertion); + w_set->insertStringList(sets); + connect(w_set, SIGNAL(activated(int)),this, SLOT(activateSet(int))); + QLabel *l = new QLabel(w_set, i18n("&Set:"),page); + l->setMinimumSize(l->sizeHint()); + subLayout1->addWidget(l,0); + subLayout1->addWidget(w_set,1); + + subLayout1->addSpacing(8); + + w_save = new QPushButton(i18n("S&ave"),page); + connect(w_save,SIGNAL(clicked()),this, SLOT(transferSet())); + subLayout1->addWidget(w_save,0); + + QPushButton *btn = new QPushButton(i18n("&New"),page); + btn->setMinimumSize(btn->sizeHint()); + connect(btn, SIGNAL(clicked()),this, SLOT(newPressed())); + subLayout1->addWidget(btn,0); + + w_delete = new QPushButton(i18n("&Delete"),page); + w_delete->setMinimumSize(w_delete->sizeHint()); + connect(w_delete, SIGNAL(clicked()),this, SLOT(deletePressed())); + subLayout1->addWidget(w_delete,0); + + topLayout->addSpacing(8); + + KSeparator *sep = new KSeparator(page); + topLayout->addWidget(sep,0); + + topLayout->addSpacing(8); + + QGridLayout * subLayout2 = new QGridLayout(7,3,6); + topLayout->addLayout(subLayout2,1); + + w_leftBox = new QListBox(page); + connect(w_leftBox, SIGNAL(selected(int)),this, SLOT(leftSelected(int))); + connect(w_leftBox, SIGNAL(highlighted(int)),this, SLOT(leftHighlighted(int))); + QLabel *leftLabel = new QLabel(w_leftBox, i18n("S&elected databases:"),page); + leftLabel->setMinimumSize(leftLabel->sizeHint()); + subLayout2->addWidget(leftLabel,0,0); + subLayout2->addMultiCellWidget(w_leftBox,1,6,0,0); + + w_allLeft = new QPushButton(page); + w_allLeft->setIconSet(BarIconSet("2leftarrow")); + connect(w_allLeft, SIGNAL(clicked()),this, SLOT(allLeftPressed())); + subLayout2->addWidget(w_allLeft,2,1); + + w_left = new QPushButton(page); + w_left->setIconSet(BarIconSet("1leftarrow")); + connect(w_left, SIGNAL(clicked()),this, SLOT(leftPressed())); + subLayout2->addWidget(w_left,3,1); + + w_right = new QPushButton(page); + w_right->setIconSet(BarIconSet("1rightarrow")); + connect(w_right, SIGNAL(clicked()),this, SLOT(rightPressed())); + subLayout2->addWidget(w_right,4,1); + + w_allRight = new QPushButton(page); + w_allRight->setIconSet(BarIconSet("2rightarrow")); + connect(w_allRight, SIGNAL(clicked()),this, SLOT(allRightPressed())); + subLayout2->addWidget(w_allRight,5,1); + + w_rightBox = new QListBox(page); + connect(w_rightBox, SIGNAL(selected(int)),this, SLOT(rightSelected(int))); + connect(w_rightBox, SIGNAL(highlighted(int)),this, SLOT(rightHighlighted(int))); + QLabel *rightLabel = new QLabel(w_rightBox, i18n("A&vailable databases:"),page); + rightLabel->setMinimumSize(rightLabel->sizeHint()); + subLayout2->addWidget(rightLabel,0,2); + subLayout2->addMultiCellWidget(w_rightBox,1,6,2,2); + + subLayout2->setRowStretch(1,1); + subLayout2->setRowStretch(6,1); + subLayout2->setColStretch(0,1); + subLayout2->setColStretch(2,1); + + setHelp("database-sets"); + + if (global->setsSize.isValid()) + resize(global->setsSize); + else + resize(300,200); + + if ((global->currentDatabase>=1)&&(global->currentDatabase<=global->databaseSets.count())) + activateSet(global->currentDatabase-1); + else + activateSet(0); + w_set->setFocus(); +} + + +void DbSetsDialog::hideEvent(QHideEvent *) +{ + global->setsSize = size(); + emit(dialogClosed()); +} + + +void DbSetsDialog::newPressed() +{ + QStringList *temp = new QStringList; + temp->append(i18n("New Set")); + global->databaseSets.append(temp); + global->databases.insert(global->databases.at(global->databaseSets.count()),i18n("New Set")); + if (global->currentDatabase >= global->databaseSets.count()) + global->currentDatabase++; + + QStringList sets; // reread sets, because w_sets internal list is not correct in all cases + for(unsigned int i=1;i<global->databaseSets.count()+1;i++) + sets.append(global->databases[i]); + w_set->clear(); + w_set->insertStringList(sets); + emit(setsChanged()); + activateSet(global->databaseSets.count()-1); + w_set->setFocus(); +} + + +void DbSetsDialog::deletePressed() +{ + int pos = w_set->currentItem(); + if (pos>=0) { + global->databaseSets.remove(global->databaseSets.at(pos)); + global->databases.remove(global->databases.at(pos+1)); + if ((int)global->currentDatabase >= pos+1) + global->currentDatabase--; + w_set->removeItem(pos); + if (pos >= w_set->count()) + pos--; + emit(setsChanged()); + activateSet(pos); + w_set->setFocus(); + } +} + + +void DbSetsDialog::allLeftPressed() +{ + while (w_rightBox->count()) { + w_leftBox->insertItem(w_rightBox->text(0)); + w_rightBox->removeItem(0); + } + w_leftBox->sort(); + checkButtons(); +} + + +void DbSetsDialog::leftPressed() +{ + int pos = w_rightBox->currentItem(); + if (pos>=0) { + w_leftBox->insertItem(w_rightBox->text(pos)); + w_leftBox->sort(); + w_rightBox->removeItem(pos); + if (pos >= (int)w_rightBox->count()) + pos--; + if (pos>=0) + w_rightBox->setCurrentItem(pos); + checkButtons(); + } +} + + +void DbSetsDialog::rightPressed() +{ + int pos = w_leftBox->currentItem(); + if (pos>=0) { + w_rightBox->insertItem(w_leftBox->text(pos)); + w_rightBox->sort(); + w_leftBox->removeItem(pos); + if (pos >= (int)w_leftBox->count()) + pos--; + if (pos>=0) + w_leftBox->setCurrentItem(pos); + checkButtons(); + } +} + + +void DbSetsDialog::allRightPressed() +{ + while (w_leftBox->count()) { + w_rightBox->insertItem(w_leftBox->text(0)); + w_leftBox->removeItem(0); + } + w_rightBox->sort(); + checkButtons(); +} + + +void DbSetsDialog::closePressed() +{ + accept(); + global->setsSize = size(); + emit(dialogClosed()); +} + + +void DbSetsDialog::transferSet() +{ + global->databaseSets.at(w_set->currentItem())->clear(); + global->databaseSets.at(w_set->currentItem())->append(w_set->currentText()); + for (unsigned int i = 0;i<w_leftBox->count();i++) + global->databaseSets.at(w_set->currentItem())->append(w_leftBox->text(i)); + global->databases.remove(global->databases.at(w_set->currentItem()+1)); + global->databases.insert(global->databases.at(w_set->currentItem()+1),w_set->currentText()); + w_set->changeItem(w_set->currentText(),w_set->currentItem()); + emit(setsChanged()); +} + + +void DbSetsDialog::activateSet(int num) +{ + w_leftBox->clear(); + w_rightBox->clear(); + + if ((num < 0)||(num>=(int)global->databaseSets.count())) { + w_set->clearEdit(); + w_delete->setEnabled(false); + w_save->setEnabled(false); + w_rightBox->repaint(true); // Workaround for repaint-bug + w_leftBox->repaint(true); // Workaround for repaint-bug + } else { + w_set->setCurrentItem(num); + for (unsigned int i=0;i<global->serverDatabases.count();i++) + if (global->databaseSets.at(num)->findIndex(global->serverDatabases[i])>0) + w_leftBox->insertItem(global->serverDatabases[i]); + else + w_rightBox->insertItem(global->serverDatabases[i]); + w_leftBox->sort(); + w_rightBox->sort(); + w_delete->setEnabled(true); + w_save->setEnabled(true); + if (w_rightBox->count()==0) + w_rightBox->repaint(true); // Workaround for repaint-bug + if (w_leftBox->count()==0) + w_leftBox->repaint(true); // Workaround for repaint-bug + w_leftBox->clearSelection(); + w_leftBox->centerCurrentItem(); + w_rightBox->clearSelection(); + w_rightBox->centerCurrentItem(); + } + checkButtons(); +} + + +void DbSetsDialog::leftSelected(int) +{ + rightPressed(); +} + + +void DbSetsDialog::rightSelected(int) +{ + leftPressed(); +} + + +void DbSetsDialog::leftHighlighted(int) +{ + w_right->setEnabled(true); +} + + +void DbSetsDialog::rightHighlighted(int) +{ + w_left->setEnabled(true); +} + +void DbSetsDialog::checkButtons() +{ + w_allLeft->setEnabled((w_rightBox->count()>0)); + w_allRight->setEnabled((w_leftBox->count()>0)); + w_right->setEnabled((w_leftBox->currentItem()>=0)); + w_left->setEnabled((w_rightBox->currentItem()>=0)); +} + +//-------------------------------- + +#include "sets.moc" diff --git a/kdict/sets.h b/kdict/sets.h new file mode 100644 index 00000000..3e874a6e --- /dev/null +++ b/kdict/sets.h @@ -0,0 +1,68 @@ +/* ------------------------------------------------------------- + + sets.h (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. + + ------------------------------------------------------------- + + dbSetsDialog dialog for editing the user defined database sets + + ------------------------------------------------------------- */ + +#ifndef _KDICT_SETS_H_ +#define _KDICT_SETS_H_ + +#include <kdialogbase.h> + +class QListBox; + + +//********* DbSetsDialog ****************************************** + + +class DbSetsDialog : public KDialogBase +{ + Q_OBJECT + +public: + + DbSetsDialog(QWidget *parent=0, const char *name=0); + +signals: + + void setsChanged(); + void dialogClosed(); + +protected: + void hideEvent(QHideEvent *); + +private slots: + + void newPressed(); + void deletePressed(); + void allLeftPressed(); + void leftPressed(); + void rightPressed(); + void allRightPressed(); + void closePressed(); + void transferSet(); + void activateSet(int num); + void leftSelected(int index); + void rightSelected(int index); + void leftHighlighted(int index); + void rightHighlighted(int index); + +private: + + void checkButtons(); + + QComboBox *w_set; + QListBox *w_leftBox, *w_rightBox; + QPushButton *w_delete,*w_save,*w_allLeft,*w_left,*w_right,*w_allRight; +}; + +#endif diff --git a/kdict/toplevel.cpp b/kdict/toplevel.cpp new file mode 100644 index 00000000..ee334cbd --- /dev/null +++ b/kdict/toplevel.cpp @@ -0,0 +1,778 @@ +/* ------------------------------------------------------------- + + toplevel.cpp (part of The KDE Dictionary Client) + + Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org> + (C) by Matthias Hölzer 1998 + + This file is distributed under the Artistic License. + See LICENSE for details. + + ------------------------------------------------------------- + + TopLevel The toplevel widget of Kdict. + + ------------------------------------------------------------- */ + +#include <qclipboard.h> + +#include <kstdaccel.h> +#include <kstdaction.h> +#include <kapplication.h> +#include <kstatusbar.h> +#include <klocale.h> +#include <kwin.h> +#include <kedittoolbar.h> +#include <kdebug.h> +#include <dcopclient.h> + +#include "actions.h" +#include "dict.h" +#include "options.h" +#include "queryview.h" +#include "matchview.h" +#include "sets.h" +#include "toplevel.h" + + +// cut a QString and add "..." +QString getShortString(QString str,unsigned int length) +{ + if (str.length()>length) { + str.truncate(length-3); + str.append("..."); + } + return str; +} + + +DictInterface *interface; +GlobalData *global; + + +TopLevel::TopLevel(QWidget* parent, const char* name) + : DCOPObject("KDictIface"), KMainWindow(parent, name, WType_TopLevel), + optDlg(0L), setsDlg(0L), stopRef(0) +{ + kapp->dcopClient()->setDefaultObject(objId()); + kapp->setMainWidget(this); + + global = new GlobalData(); + global->topLevel = this; + global->read(); + interface = new DictInterface(); + connect(interface,SIGNAL(infoReady()),SLOT(stratDbChanged())); + connect(interface,SIGNAL(started(const QString&)),SLOT(clientStarted(const QString&))); + connect(interface,SIGNAL(stopped(const QString&)),SLOT(clientStopped(const QString&))); + + queryView = new QueryView(this); + connect(queryView,SIGNAL(defineRequested(const QString&)),SLOT(define(const QString&))); + connect(queryView,SIGNAL(matchRequested(const QString&)),SLOT(match(const QString&))); + connect(queryView,SIGNAL(clipboardRequested()),SLOT(defineClipboard())); + connect(queryView,SIGNAL(enableCopy(bool)),SLOT(enableCopy(bool))); + connect(queryView,SIGNAL(enablePrintSave()),SLOT(enablePrintSave())); + connect(queryView,SIGNAL(renderingStarted()),SLOT(renderingStarted())); + connect(queryView,SIGNAL(renderingStopped()),SLOT(renderingStopped())); + connect(queryView,SIGNAL(newCaption(const QString&)),SLOT(newCaption(const QString&))); + + matchView = new MatchView(); + connect(matchView,SIGNAL(defineRequested(const QString&)),SLOT(define(const QString&))); + connect(matchView,SIGNAL(matchRequested(const QString&)),SLOT(match(const QString&))); + connect(matchView,SIGNAL(clipboardRequested()),SLOT(matchClipboard())); + connect(matchView,SIGNAL(windowClosed()),SLOT(toggleMatchListShow())); + connect(&resetStatusbarTimer,SIGNAL(timeout()),SLOT(resetStatusbar())); + + setupStatusBar(); + setupActions(); + recreateGUI(); + buildHistMenu(); + + if (global->showMatchList) + { // show splitter, html view & match list + splitter = new QSplitter(QSplitter::Horizontal,this); + splitter->setOpaqueResize( KGlobalSettings::opaqueResize() ); + queryView->reparent(splitter,0,queryView->pos(),true); + matchView->reparent(splitter,0,matchView->pos(),true); + setCentralWidget(splitter); + splitter->setResizeMode(matchView,QSplitter::KeepSize); + adjustMatchViewSize(); + } + else + { // show only html view + setCentralWidget(queryView); + matchView->hide(); + } + + //apply settings + resize(600,390); + applyMainWindowSettings(KGlobal::config(),"toplevel_options"); + stratDbChanged(); // fill combos, build menus + + actQueryCombo->setFocus(); // place cursor in combobox +} + + +TopLevel::~TopLevel() +{ +} + + +void TopLevel::normalStartup() +{ + if (global->defineClipboard) + defineClipboard(); +} + +// ******* DCOP Interface ******************************************************** + +void TopLevel::quit() +{ + kdDebug(5004) << "*DCOP call* TopLevel::quit()" << endl; + kapp->closeAllWindows(); +} + + +void TopLevel::makeActiveWindow() +{ + kdDebug(5004) << "*DCOP call* TopLevel::makeActiveWindow()" << endl; + raiseWindow(); +} + + +void TopLevel::definePhrase(QString phrase) +{ + kdDebug(5004) << "*DCOP call* TopLevel::definePhrase()" << endl; + define(phrase); + raiseWindow(); +} + + +void TopLevel::matchPhrase(QString phrase) +{ + kdDebug(5004) << "*DCOP call* TopLevel::matchPhrase()" << endl; + match(phrase); + raiseWindow(); +} + + +void TopLevel::defineClipboardContent() +{ + kdDebug(5004) << "*DCOP call* TopLevel::defineClipboardContent()" << endl; + defineClipboard(); + raiseWindow(); +} + + +void TopLevel::matchClipboardContent() +{ + kdDebug(5004) << "*DCOP call* TopLevel::matchClipboardContent()" << endl; + matchClipboard(); + raiseWindow(); +} + + +QStringList TopLevel::getDatabases() +{ + kdDebug(5004) << "*DCOP call* TopLevel::getDatabases()" << endl; + return global->databases; +} + + +QString TopLevel::currentDatabase() +{ + kdDebug(5004) << "*DCOP call* TopLevel::currentDatabase()" << endl; + return global->databases[global->currentDatabase]; +} + + +QStringList TopLevel::getStrategies() +{ + kdDebug(5004) << "*DCOP call* TopLevel::getStrategies()" << endl; + return global->strategies; +} + + +QString TopLevel::currentStrategy() +{ + kdDebug(5004) << "*DCOP call* TopLevel::currentStrategy()" << endl; + return global->strategies[global->currentStrategy]; +} + + +bool TopLevel::setDatabase(QString db) +{ + kdDebug(5004) << "*DCOP call* TopLevel::setDatabase()" << endl; + + int newCurrent = global->databases.findIndex(db); + if (newCurrent == -1) + return false; + else { + global->currentDatabase = newCurrent; + actDbCombo->setCurrentItem(global->currentDatabase); + return true; + } +} + + +bool TopLevel::setStrategy(QString strategy) +{ + kdDebug(5004) << "*DCOP call* TopLevel::setStrategy()" << endl; + + return matchView->selectStrategy(strategy); +} + + +bool TopLevel::historyGoBack() +{ + kdDebug(5004) << "*DCOP call* TopLevel::historyGoBack()" << endl; + + if (!queryView->browseBackPossible()) + return false; + else { + queryView->browseBack(); + return true; + } +} + + +bool TopLevel::historyGoForward() +{ + kdDebug(5004) << "*DCOP call* TopLevel::historyGoForward()" << endl; + + if (!queryView->browseForwardPossible()) + return false; + else { + queryView->browseForward(); + return true; + } +} + +// ******************************************************************************* + +void TopLevel::define(const QString &query) +{ + kdDebug(5004) << "TopLevel::define()" << endl; + actQueryCombo->setEditText(query); + doDefine(); +} + + +void TopLevel::defineClipboard() +{ + kdDebug(5004) << "TopLevel::defineClipboard()" << endl; + kapp->clipboard()->setSelectionMode(true); + QString text = kapp->clipboard()->text(); + if (text.isEmpty()) { + kapp->clipboard()->setSelectionMode(false); + text = kapp->clipboard()->text(); + } + define(text); +} + + +void TopLevel::match(const QString &query) +{ + kdDebug(5004) << "TopLevel::match()" << endl; + actQueryCombo->setEditText(query); + doMatch(); +} + + +void TopLevel::matchClipboard() +{ + kdDebug(5004) << "TopLevel::matchClipboard()" << endl; + kapp->clipboard()->setSelectionMode(true); + QString text = kapp->clipboard()->text(); + if (text.isEmpty()) { + kapp->clipboard()->setSelectionMode(false); + text = kapp->clipboard()->text(); + } + match(text); +} + + +bool TopLevel::queryClose() +{ + kdDebug(5004) << "TopLevel::queryClose()" << endl; + + saveMainWindowSettings(KGlobal::config(),"toplevel_options"); + saveMatchViewSize(); + global->queryComboCompletionMode = actQueryCombo->completionMode(); + + global->write(); + + return true; +} + + +void TopLevel::setupActions() +{ + // file menu... + actSave = KStdAction::save(queryView, SLOT(saveQuery()), actionCollection()); + actSave->setText(i18n("&Save As...")); + actSave->setEnabled(false); + actPrint = KStdAction::print(queryView, SLOT(printQuery()), actionCollection()); + actPrint->setEnabled(false); + actStartQuery = new KAction(i18n("St&art Query"),"reload", 0 , this, + SLOT(doDefine()), actionCollection(), "start_query"); + actStopQuery = new KAction(i18n("St&op Query"),"stop", 0 , this, + SLOT(stopClients()), actionCollection(), "stop_query"); + actStopQuery->setEnabled(false); + KStdAction::quit(kapp, SLOT(closeAllWindows()), actionCollection()); + + // edit menu... + actCopy = KStdAction::copy(queryView, SLOT(copySelection()), actionCollection()); + actCopy->setEnabled(false); + KStdAction::selectAll(queryView, SLOT(selectAll()), actionCollection()); + new KAction(i18n("&Define Clipboard Content"), "define_clip", 0 , this, + SLOT(defineClipboard()), actionCollection(), "define_clipboard"); + new KAction(i18n("&Match Clipboard Content"), 0 , this, + SLOT(matchClipboard()), actionCollection(), "match_clipboard"); + KStdAction::find(queryView, SLOT(showFindDialog()), actionCollection()); + + // history menu... + actBack = new KToolBarPopupAction(i18n("&Back"), "back", KStdAccel::shortcut(KStdAccel::Back), + queryView, SLOT(browseBack()), actionCollection(),"browse_back"); + actBack->setDelayed(true); + actBack->setStickyMenu(false); + actBack->setEnabled(false); + actForward = new KToolBarPopupAction(i18n("&Forward"), "forward", KStdAccel::shortcut(KStdAccel::Forward), + queryView, SLOT(browseForward()), actionCollection(),"browse_forward"); + actForward->setDelayed(true); + actForward->setStickyMenu(false); + actForward->setEnabled(false); + new KAction(i18n("&Clear History"), 0 , this, + SLOT(clearQueryHistory()), actionCollection(), "clear_history"); + + // server menu... + new KAction(i18n("&Get Capabilities"), 0 , interface, + SLOT(updateServer()), actionCollection(), "get_capabilities"); + new KAction(i18n("Edit &Database Sets..."), "edit", 0 , this, + SLOT(showSetsDialog()), actionCollection(), "edit_sets"); + new KAction(i18n("&Summary"), 0 , interface, + SLOT(showDatabases()), actionCollection(), "db_summary"); + new KAction(i18n("S&trategy Information"), 0 , interface, + SLOT(showStrategies()), actionCollection(), "strategy_info"); + new KAction(i18n("&Server Information"), 0 , interface, + SLOT(showInfo()), actionCollection(), "server_info"); + + // settings menu... + createStandardStatusBarAction(); + setStandardToolBarMenuEnabled(true); + + actShowMatchList = new KToggleAction(i18n("Show &Match List"), 0 , this, + SLOT(toggleMatchListShow()), actionCollection(), "show_match"); + actShowMatchList->setCheckedState(i18n("Hide &Match List")); + actShowMatchList->setChecked(global->showMatchList); + KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), +actionCollection()); + KStdAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection()); + KStdAction::preferences(this, SLOT(showOptionsDialog()), actionCollection()); + + // toolbar... + new KAction(i18n("Clear Input Field"), "query_erase", 0 , this, + SLOT(clearInput()), actionCollection(), "clear_query"); + + actQueryLabel = new DictLabelAction(i18n("&Look for:"), actionCollection(), "look_label"); + actQueryCombo = new DictComboAction(i18n("Query"), actionCollection(), "query_combo",true,true); + connect(actQueryCombo,SIGNAL(activated(const QString &)), SLOT(define(const QString&))); + actQueryCombo->setCompletionMode(global->queryComboCompletionMode); + actDbLabel = new DictLabelAction(i18n("&in"), actionCollection(), "in_label"); + actDbCombo = new DictComboAction(i18n("Databases"), actionCollection(), "db_combo",false,false); + connect(actDbCombo,SIGNAL(activated(int)),SLOT(databaseSelected(int))); + actDefineBtn = new DictButtonAction(i18n("&Define"), this, SLOT(doDefine()), actionCollection(), "define_btn"); + actMatchBtn = new DictButtonAction(i18n("&Match"), this, SLOT(doMatch()), actionCollection(), "match_btn"); + + queryView->setActions(actBack,actForward,actQueryCombo); +} + + +void TopLevel::setupStatusBar() +{ + statusBar()->insertItem(i18n(" Ready "),0,2); + statusBar()->setItemAlignment(0,AlignLeft | AlignVCenter); + + QString serverInfo; + if (global->authEnabled) + serverInfo = QString(" %1@%2:%3 ").arg(getShortString(global->user,50)) + .arg(getShortString(global->server,50)) + .arg(global->port); + else + serverInfo = QString(" %1:%3 ").arg(getShortString(global->server,50)) + .arg(global->port); + statusBar()->insertItem(serverInfo, 1,3); + statusBar()->setItemAlignment(1,AlignLeft | AlignVCenter); +} + + +void TopLevel::recreateGUI() +{ + kdDebug(5004) << "TopLevel::recreateGUI()" << endl; + createGUI("kdictui.rc", false); + actQueryCombo->setList(global->queryHistory); + actQueryCombo->clearEdit(); + actQueryLabel->setBuddy(actQueryCombo->widget()); + + actDbCombo->setList(global->databases); + actDbCombo->setCurrentItem(global->currentDatabase); + actDbLabel->setBuddy(actDbCombo->widget()); + int bwidth; + if (actDefineBtn->widthHint() > actMatchBtn->widthHint()) + bwidth = actDefineBtn->widthHint(); + else + bwidth = actMatchBtn->widthHint(); + actDefineBtn->setWidth(bwidth); + actMatchBtn->setWidth(bwidth); +} + + +// add text in the query-combobox to the history +void TopLevel::addCurrentInputToHistory() +{ + QString text(actQueryCombo->currentText()); + + // maintain queryHistory + global->queryHistory.remove(text); // no double entrys + global->queryHistory.prepend(text); // prepend new item + while (global->queryHistory.count()>global->maxHistEntrys) // shorten list + global->queryHistory.remove(global->queryHistory.fromLast()); + + actQueryCombo->setList(global->queryHistory); + actQueryCombo->setCurrentItem(0); + buildHistMenu(); +} + + + +// erase text in query-combobox +void TopLevel::clearInput() +{ + actQueryCombo->clearEdit(); + actQueryCombo->setFocus(); // place cursor in combobox +} + + +// define text in the combobox +void TopLevel::doDefine() +{ + QString text(actQueryCombo->currentText()); + + if (!text.isEmpty()) + { + addCurrentInputToHistory(); + actQueryCombo->selectAll(); + interface->define(text); + } +} + + +void TopLevel::doMatch() +{ + QString text(actQueryCombo->currentText()); + + if (!text.isEmpty()) + { + addCurrentInputToHistory(); + actQueryCombo->selectAll(); + + if (!global->showMatchList) + { + toggleMatchListShow(); + } + + matchView->match(text); + setCaption(getShortString(text.simplifyWhiteSpace(),70)); + } +} + + +void TopLevel::stopClients() +{ + interface->stop(); + queryView->stop(); +} + + +// rebuild history menu on demand +void TopLevel::buildHistMenu() +{ + unplugActionList("history_items"); + + historyActionList.setAutoDelete(true); + historyActionList.clear(); + + unsigned int i = 0; + while ((i<10)&&(i<global->queryHistory.count())) { + historyActionList.append( new KAction(getShortString(global->queryHistory[i],70), 0, this, SLOT(queryHistMenu()), + (QObject*)0, global->queryHistory[i].utf8().data()) ); + i++; + } + + plugActionList("history_items", historyActionList); +} + + +// process a query via the history menu +void TopLevel::queryHistMenu() +{ + QCString name = sender()->name(); + if (!name.isEmpty()) + define(QString::fromUtf8(name)); +} + + +void TopLevel::clearQueryHistory() +{ + global->queryHistory.clear(); + actQueryCombo->clear(); + buildHistMenu(); +} + + +// fill combos, rebuild menus +void TopLevel::stratDbChanged() +{ + actDbCombo->setList(global->databases); + actDbCombo->setCurrentItem(global->currentDatabase); + matchView->updateStrategyCombo(); + + unplugActionList("db_detail"); + + dbActionList.setAutoDelete(true); + dbActionList.clear(); + + for (unsigned int i=0;i<global->serverDatabases.count();i++) + dbActionList.append( new KAction(global->serverDatabases[i], 0, this, SLOT(dbInfoMenuClicked()), + (QObject*)0, global->serverDatabases[i].utf8().data()) ); + + plugActionList("db_detail", dbActionList); +} + + +void TopLevel::dbInfoMenuClicked() +{ + QCString name = sender()->name(); + if (!name.isEmpty()) + interface->showDbInfo(name); +} + + +void TopLevel::databaseSelected(int num) +{ + global->currentDatabase = num; +} + + +void TopLevel::enableCopy(bool selected) +{ + actCopy->setEnabled(selected); +} + + +void TopLevel::enablePrintSave() +{ + actSave->setEnabled(true); + actPrint->setEnabled(true); +} + + +void TopLevel::clientStarted(const QString &message) +{ + statusBar()->changeItem(message,0); + resetStatusbarTimer.stop(); + stopRef++; + actStopQuery->setEnabled(stopRef>0); // enable stop-icon + kapp->setOverrideCursor(waitCursor); +} + + +void TopLevel::clientStopped(const QString &message) +{ + statusBar()->changeItem(message,0); + resetStatusbarTimer.start(4000); + if (stopRef > 0) + stopRef--; + actStopQuery->setEnabled(stopRef>0); // disable stop-icon + kapp->restoreOverrideCursor(); +} + + +void TopLevel::resetStatusbar() +{ + resetStatusbarTimer.stop(); + statusBar()->changeItem(i18n(" Ready "),0); +} + + +void TopLevel::renderingStarted() +{ + stopRef++; + actStopQuery->setEnabled(stopRef>0); // disable stop-icon + kapp->setOverrideCursor(waitCursor); +} + + +void TopLevel::renderingStopped() +{ + if (stopRef > 0) + stopRef--; + actStopQuery->setEnabled(stopRef>0); // disable stop-icon + kapp->restoreOverrideCursor(); +} + + +void TopLevel::newCaption(const QString &s) +{ + setCaption(s); +} + +void TopLevel::toggleMatchListShow() +{ + saveMatchViewSize(); + if (global->showMatchList) // list is visible -> hide it + { + global->showMatchList = false; + queryView->reparent(this,0,queryView->pos(),true); + matchView->reparent(this,0,matchView->pos(),true); + matchView->hide(); + delete splitter; + setCentralWidget(queryView); + } + else // list is not visible -> show it + { + global->showMatchList = true; + splitter = new QSplitter(QSplitter::Horizontal,this); + splitter->setOpaqueResize( KGlobalSettings::opaqueResize() ); + setCentralWidget(splitter); + splitter->show(); + queryView->reparent(splitter,0,queryView->pos(),true); + matchView->reparent(splitter,0,matchView->pos(),true); + splitter->setResizeMode(matchView,QSplitter::KeepSize); + adjustMatchViewSize(); + } + + actShowMatchList->setChecked(global->showMatchList); +} + + +void TopLevel::saveMatchViewSize() +{ + if (global->showMatchList) + { + global->splitterSizes = splitter->sizes(); + } +} + + +void TopLevel::adjustMatchViewSize() +{ + if (global->splitterSizes.count()==2) + { + splitter->setSizes(global->splitterSizes); + } +} + + +void TopLevel::slotConfToolbar() +{ + saveMainWindowSettings(KGlobal::config(),"toplevel_options"); + KEditToolbar dlg(actionCollection(), "kdictui.rc"); + connect(&dlg,SIGNAL( newToolbarConfig() ), this, SLOT( slotNewToolbarConfig() )); + dlg.exec(); +} + + +void TopLevel::slotNewToolbarConfig() +{ + recreateGUI(); + applyMainWindowSettings(KGlobal::config(),"toplevel_options"); + buildHistMenu(); // actionlists must be inserted + stratDbChanged(); +} + + +void TopLevel::showSetsDialog() +{ + if (!setsDlg) { + setsDlg = new DbSetsDialog(this); + connect(setsDlg,SIGNAL(setsChanged()),this,SLOT(setsChanged())); + connect(setsDlg,SIGNAL(dialogClosed()),this,SLOT(hideSetsDialog())); + setsDlg->show(); + } else { + KWin::activateWindow(setsDlg->winId()); + } +} + + +void TopLevel::hideSetsDialog() +{ + if (setsDlg) { + setsDlg->delayedDestruct(); + setsDlg = 0L; + } +} + + +void TopLevel::setsChanged() +{ + actDbCombo->setList(global->databases); + actDbCombo->setCurrentItem(global->currentDatabase); +} + + +void TopLevel::showOptionsDialog() +{ + if (!optDlg) { + optDlg = new OptionsDialog(this); + connect(optDlg,SIGNAL(optionsChanged()),this,SLOT(optionsChanged())); + connect(optDlg,SIGNAL(finished()),this,SLOT(hideOptionsDialog())); + optDlg->show(); + } else { + KWin::activateWindow(optDlg->winId()); + } +} + + +void TopLevel::hideOptionsDialog() +{ + if (optDlg) { + optDlg->delayedDestruct(); + optDlg=0; + } +} + + +void TopLevel::optionsChanged() +{ + QString serverInfo; + if (global->authEnabled) + serverInfo = QString(" %1@%2:%3 ").arg(getShortString(global->user,50)) + .arg(getShortString(global->server,50)) + .arg(global->port); + else + serverInfo = QString(" %1:%3 ").arg(getShortString(global->server,50)) + .arg(global->port); + statusBar()->changeItem(serverInfo,1); + interface->serverChanged(); // inform client + queryView->optionsChanged(); // inform html-view +} + +void TopLevel::raiseWindow() +{ + // Bypass focus stealing prevention + kapp->updateUserTimestamp(); + + KWin::WindowInfo info = KWin::windowInfo( winId() ); + + if ( !info.isOnCurrentDesktop() ) + { + KWin::setOnDesktop( winId(), KWin::currentDesktop() ); + } + + KWin::activateWindow(winId()); +} + + +//-------------------------------- + +#include "toplevel.moc" diff --git a/kdict/toplevel.h b/kdict/toplevel.h new file mode 100644 index 00000000..cd8fa25d --- /dev/null +++ b/kdict/toplevel.h @@ -0,0 +1,149 @@ +/* ------------------------------------------------------------- + + toplevel.h (part of The KDE Dictionary Client) + + Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org> + (C) by Matthias Hölzer 1998 + + This file is distributed under the Artistic License. + See LICENSE for details. + + ------------------------------------------------------------- + + TopLevel The toplevel widget of Kdict. + + ------------------------------------------------------------- */ + +#ifndef _TOPLEVEL_H_ +#define _TOPLEVEL_H_ + +#include <qtimer.h> +#include <kmainwindow.h> +#include "dcopinterface.h" + +class QSplitter; + +class KToggleAction; +class KToolBarPopupAction; + +class DictLabelAction; +class DictComboAction; +class DictButtonAction; +class MatchView; +class QueryView; +class OptionsDialog; +class DbSetsDialog; + + +class TopLevel : public KMainWindow, virtual public KDictIface +{ + Q_OBJECT + + friend class QueryView; + +public: + + TopLevel(QWidget* parent = 0, const char* name = 0); + ~TopLevel(); + + void normalStartup(); // called when started without commandline parameters + + // DCOP-Interface... + void quit(); + void makeActiveWindow(); + void definePhrase(QString phrase); + void matchPhrase(QString phrase); + void defineClipboardContent(); + void matchClipboardContent(); + QStringList getDatabases(); + QString currentDatabase(); + QStringList getStrategies(); + QString currentStrategy(); + bool setDatabase(QString db); + bool setStrategy(QString strategy); + bool historyGoBack(); + bool historyGoForward(); + +public slots: + + void define(const QString &query); + void defineClipboard(); + + void match(const QString &query); + void matchClipboard(); + +protected: + bool queryClose(); + +private: + + void setupActions(); + void setupStatusBar(); + void recreateGUI(); + void raiseWindow(); + + void addCurrentInputToHistory(); // add text in the query-combobox to the history + +private slots: + void clearInput(); // erase text in query-combobox + + void doDefine(); // define text in the combobox + void doMatch(); // match text in the combobox + + void stopClients(); + + void buildHistMenu(); + void queryHistMenu(); // process a query via the history menu + void clearQueryHistory(); + + void stratDbChanged(); + void dbInfoMenuClicked(); + void databaseSelected(int num); + + void enableCopy(bool selected); + void enablePrintSave(); + + void clientStarted(const QString &message); + void clientStopped(const QString &message); + void resetStatusbar(); + void renderingStarted(); + void renderingStopped(); + + void newCaption(const QString&); + + void toggleMatchListShow(); + void saveMatchViewSize(); + void adjustMatchViewSize(); + + void slotConfToolbar(); + void slotNewToolbarConfig(); + + void showSetsDialog(); + void hideSetsDialog(); + void setsChanged(); + + void showOptionsDialog(); + void hideOptionsDialog(); + void optionsChanged(); + +private: + + KAction *actSave, *actPrint, *actStartQuery, *actStopQuery, *actCopy; + KToggleAction *actShowMatchList; + DictLabelAction *actQueryLabel, *actDbLabel; + DictComboAction *actQueryCombo, *actDbCombo; + DictButtonAction *actDefineBtn, *actMatchBtn; + QPtrList<KAction> historyActionList, dbActionList; + KToolBarPopupAction *actBack, *actForward; + + QSplitter *splitter; // widgets.... + QueryView *queryView; + MatchView *matchView; + OptionsDialog *optDlg; + DbSetsDialog *setsDlg; + + QTimer resetStatusbarTimer; + int stopRef; // remember how many "clients" are running +}; + +#endif |