diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 4aed2c8219774f5d797760606b8489a92ddc5163 (patch) | |
tree | 3f8c130f7d269626bf6a9447407ef6c35954426a /klipper | |
download | tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'klipper')
42 files changed, 7384 insertions, 0 deletions
diff --git a/klipper/Makefile.am b/klipper/Makefile.am new file mode 100644 index 000000000..114779287 --- /dev/null +++ b/klipper/Makefile.am @@ -0,0 +1,51 @@ +INCLUDES= $(all_includes) + +bin_PROGRAMS = +lib_LTLIBRARIES = +noinst_LTLIBRARIES = libklipper_common.la +kdeinit_LTLIBRARIES = klipper.la +kde_module_LTLIBRARIES = klipper_panelapplet.la + +CLEANFILES = dummy.cpp + +libklipper_common_la_LIBADD = $(LIB_KDEUI) $(LIB_KSYCOCA) $(LIB_XFIXES) +libklipper_common_la_SOURCES = main.cpp toplevel.cpp urlgrabber.cpp configdialog.cpp \ + toplevel.skel clipboardpoll.cpp history.cpp historyitem.cpp historystringitem.cpp klipperpopup.cpp popupproxy.cpp \ + historyimageitem.cpp historyurlitem.cpp + +libklipper_common_la_METASOURCES = AUTO +libklipper_common_la_LDFLAGS = $(all_libraries) + +klipper_la_LIBADD = libklipper_common.la +klipper_la_SOURCES = dummy.cpp +klipper_la_LDFLAGS = $(all_libraries) -module -avoid-version + +klipper_panelapplet_la_LIBADD = libklipper_common.la +klipper_panelapplet_la_SOURCES = applet.cpp applet.skel +klipper_panelapplet_la_LDFLAGS = $(all_libraries) -module -avoid-version + +xdg_apps_DATA = klipper.desktop + +autostartdir = $(datadir)/autostart +autostart_DATA = klipper.desktop + +appldir = $(kde_datadir)/kicker/applets +appl_DATA = klipper.desktop + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(kde_confdir) + $(INSTALL_DATA) $(srcdir)/klipperrc.desktop $(DESTDIR)$(kde_confdir)/klipperrc + +KDE_ICON = klipper + +messages: + $(XGETTEXT) *.cpp -o $(podir)/klipper.pot + +dummy.cpp: + echo > dummy.cpp + +# update script: +updatedir = $(kde_datadir)/kconf_update +update_DATA = klipperrc.upd klippershortcuts.upd +update_SCRIPTS = klipper-1-2.pl klipper-kde31.sh + diff --git a/klipper/applet.cpp b/klipper/applet.cpp new file mode 100644 index 000000000..298a00b3d --- /dev/null +++ b/klipper/applet.cpp @@ -0,0 +1,141 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) by Andrew Stanley-Jones + Copyright (C) 2000 by Carsten Pfeiffer <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "applet.h" + +#include <kaboutapplication.h> +#include <kglobal.h> +#include <klocale.h> +#include <dcopclient.h> +#include <kconfig.h> + +#include "toplevel.h" +#include "history.h" +#include "klipperpopup.h" + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("klipper"); + int actions = KPanelApplet::Preferences | KPanelApplet::About | KPanelApplet::Help; + return new KlipperApplet(configFile, KPanelApplet::Normal, actions, parent, "klipper"); + } +} + +KlipperApplet::KlipperApplet(const QString& configFile, Type t, int actions, + QWidget *parent, const char *name) + : KPanelApplet(configFile, t, actions, parent, name) +{ + KlipperWidget::createAboutData(); + move( 0, 0 ); + setBackgroundMode(QWidget::X11ParentRelative); + widget = new KlipperAppletWidget( this ); + setCustomMenu(widget->history()->popup()); + centerWidget(); + widget->show(); +} + +KlipperApplet::~KlipperApplet() +{ + widget->saveSession(); + delete widget; + KlipperWidget::destroyAboutData(); +} + +int KlipperApplet::widthForHeight(int) const +{ + return widget->width(); +} + +int KlipperApplet::heightForWidth(int) const +{ + return widget->height(); +} + +void KlipperApplet::resizeEvent( QResizeEvent* ev ) +{ + widget->adjustSize(); + KPanelApplet::resizeEvent( ev ); + centerWidget(); +} + +void KlipperApplet::centerWidget() +{ + int x = (width() - widget->width())/2; + int y = (height() - widget->height())/2; + widget->move( x, y ); +} + +void KlipperApplet::preferences() +{ + widget->slotConfigure(); +} + +void KlipperApplet::help() +{ + kapp->invokeHelp(QString::null, QString::fromLatin1("klipper")); +} + +void KlipperApplet::about() +{ + KAboutApplication about(this, 0); + about.exec(); +} + +KlipperAppletWidget::KlipperAppletWidget( QWidget* parent ) +// init() is called first, before KlipperWidget is called with ( parent, kconfig ) + : KlipperWidget( ( init(), parent ), new KConfig( "klipperrc" )) +{ +} + +// this needs to be called before KlipperWidget ctor, because it performs already some +// operations with the clipboard, and the other running instance could notice that +// and request data while this instance is waiting in the DCOP call +void KlipperAppletWidget::init() +{ + // if there's klipper process running, quit it + QByteArray arg1, arg2; + QCString str; + // call() - wait for finishing + kapp->dcopClient()->call("klipper", "klipper", "quitProcess()", arg1, str, arg2 ); + // register ourselves, so if klipper process is started, + // it will quit immediately (KUniqueApplication) + s_dcop = new DCOPClient; + s_dcop->registerAs( "klipper", false ); +} + +KlipperAppletWidget::~KlipperAppletWidget() +{ + delete s_dcop; + s_dcop = 0; +} + +DCOPClient* KlipperAppletWidget::s_dcop = 0; + +// this is just to make klipper process think we're KUniqueApplication +// (AKA ugly hack) +int KlipperAppletWidget::newInstance() +{ + return 0; +} + + +#include "applet.moc" diff --git a/klipper/applet.h b/klipper/applet.h new file mode 100644 index 000000000..c1a4bafcf --- /dev/null +++ b/klipper/applet.h @@ -0,0 +1,66 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + + Copyright (C) Andrew Stanley-Jones + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _APPLET_H_ +#define _APPLET_H_ + +#include <kpanelapplet.h> + +#include "toplevel.h" + +class KlipperAppletWidget; + +class KlipperApplet : public KPanelApplet +{ + Q_OBJECT +public: + KlipperApplet(const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0); + ~KlipperApplet(); + + int widthForHeight(int h) const; + int heightForWidth(int w) const; +protected: + void resizeEvent( QResizeEvent* ); + void preferences(); + void help(); + void about(); + +private: + void centerWidget(); + KlipperAppletWidget* widget; +}; + +class KlipperAppletWidget : public KlipperWidget +{ + Q_OBJECT + K_DCOP +k_dcop: + int newInstance(); +public: + KlipperAppletWidget( QWidget* parent = NULL ); + virtual ~KlipperAppletWidget(); +private: + void init(); + static DCOPClient* s_dcop; + +}; + +#endif diff --git a/klipper/clipboardpoll.cpp b/klipper/clipboardpoll.cpp new file mode 100644 index 000000000..20157a1a2 --- /dev/null +++ b/klipper/clipboardpoll.cpp @@ -0,0 +1,336 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + + Copyright (C) 2003 by Lubos Lunak <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "config.h" + +#include "clipboardpoll.h" + +#include <kapplication.h> +#include <qclipboard.h> +#include <kdebug.h> +#include <X11/Xatom.h> +#include <time.h> + +#ifdef HAVE_XFIXES +#include <X11/extensions/Xfixes.h> +#endif + +#include "toplevel.h" + +//#define NOISY_KLIPPER_ + +/* + + The polling magic: + + There's no way with X11 how to find out if the selection has changed (unless its ownership + is taken away from the current client). In the future, there will be hopefully such notification, + which will make this whole file more or less obsolete. But for now, Klipper has to poll. + In order to avoid transferring all the data on every time pulse, this file implements two + optimizations: The first one is checking whether the selection owner is Qt application (using + the _QT_SELECTION/CLIPBOARD_SENTINEL atoms on the root window of screen 0), and if yes, + Klipper can rely on QClipboard's signals. If the owner is not Qt app, and the ownership has changed, + it means the selection has changed as well. Otherwise, first only the timestamp + of the last selection change is requested using the TIMESTAMP selection target, and if it's + the same, it's assumed the contents haven't changed. Note that some applications (like XEmacs) does + not provide this information, so Klipper has to assume that the clipboard might have changed in this + case --- this is what is meant by REFUSED below. + + Update: Now there's also support for XFixes, so in case XFixes support is detected, only XFixes is + used for detecting changes, everything else is ignored, even Qt's clipboard signals. + +*/ + +extern Time qt_x_time; + +ClipboardPoll::ClipboardPoll( QWidget* parent ) + : QWidget( parent ) + , xfixes_event_base( -1 ) +{ + hide(); + const char* names[ 6 ] + = { "_QT_SELECTION_SENTINEL", + "_QT_CLIPBOARD_SENTINEL", + "CLIPBOARD", + "TIMESTAMP", + "KLIPPER_SELECTION_TIMESTAMP", + "KLIPPER_CLIPBOARD_TIMESTAMP" }; + Atom atoms[ 6 ]; + XInternAtoms( qt_xdisplay(), const_cast< char** >( names ), 6, False, atoms ); + selection.sentinel_atom = atoms[ 0 ]; + clipboard.sentinel_atom = atoms[ 1 ]; + xa_clipboard = atoms[ 2 ]; + xa_timestamp = atoms[ 3 ]; + selection.timestamp_atom = atoms[ 4 ]; + clipboard.timestamp_atom = atoms[ 5 ]; + bool use_polling = true; + kapp->installX11EventFilter( this ); +#ifdef HAVE_XFIXES + int dummy; + if( XFixesQueryExtension( qt_xdisplay(), &xfixes_event_base, &dummy )) + { + XFixesSelectSelectionInput( qt_xdisplay(), qt_xrootwin( 0 ), XA_PRIMARY, + XFixesSetSelectionOwnerNotifyMask | + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask ); + XFixesSelectSelectionInput( qt_xdisplay(), qt_xrootwin( 0 ), xa_clipboard, + XFixesSetSelectionOwnerNotifyMask | + XFixesSelectionWindowDestroyNotifyMask | + XFixesSelectionClientCloseNotifyMask ); + use_polling = false; +#ifdef NOISY_KLIPPER_ + kdDebug() << "Using XFIXES" << endl; +#endif + } +#endif + if( use_polling ) + { +#ifdef NOISY_KLIPPER_ + kdDebug() << "Using polling" << endl; +#endif + initPolling(); + } +} + +void ClipboardPoll::initPolling() +{ + connect( kapp->clipboard(), SIGNAL( selectionChanged() ), SLOT(qtSelectionChanged())); + connect( kapp->clipboard(), SIGNAL( dataChanged() ), SLOT( qtClipboardChanged() )); + connect( &timer, SIGNAL( timeout()), SLOT( timeout())); + timer.start( 1000, false ); + selection.atom = XA_PRIMARY; + clipboard.atom = xa_clipboard; + selection.last_change = clipboard.last_change = qt_x_time; // don't trigger right after startup + selection.last_owner = XGetSelectionOwner( qt_xdisplay(), XA_PRIMARY ); +#ifdef NOISY_KLIPPER_ + kdDebug() << "(1) Setting last_owner for =" << "selection" << ":" << selection.last_owner << endl; +#endif + clipboard.last_owner = XGetSelectionOwner( qt_xdisplay(), xa_clipboard ); +#ifdef NOISY_KLIPPER_ + kdDebug() << "(2) Setting last_owner for =" << "clipboard" << ":" << clipboard.last_owner << endl; +#endif + selection.waiting_for_timestamp = false; + clipboard.waiting_for_timestamp = false; + updateQtOwnership( selection ); + updateQtOwnership( clipboard ); +} + +void ClipboardPoll::qtSelectionChanged() +{ + emit clipboardChanged( true ); +} + +void ClipboardPoll::qtClipboardChanged() +{ + emit clipboardChanged( false ); +} + +bool ClipboardPoll::x11Event( XEvent* e ) +{ +// note that this is also installed as app-wide filter +#ifdef HAVE_XFIXES + if( xfixes_event_base != -1 && e->type == xfixes_event_base + XFixesSelectionNotify ) + { + XFixesSelectionNotifyEvent* ev = reinterpret_cast< XFixesSelectionNotifyEvent* >( e ); + if( ev->selection == XA_PRIMARY && !kapp->clipboard()->ownsSelection()) + { +#ifdef NOISY_KLIPPER_ + kdDebug() << "SELECTION CHANGED (XFIXES)" << endl; +#endif + qt_x_time = ev->timestamp; + emit clipboardChanged( true ); + } + else if( ev->selection == xa_clipboard && !kapp->clipboard()->ownsClipboard()) + { +#ifdef NOISY_KLIPPER_ + kdDebug() << "CLIPBOARD CHANGED (XFIXES)" << endl; +#endif + qt_x_time = ev->timestamp; + emit clipboardChanged( false ); + } + } +#endif + if( e->type == SelectionNotify && e->xselection.requestor == winId()) + { + if( changedTimestamp( selection, *e ) ) { +#ifdef NOISY_KLIPPER_ + kdDebug() << "SELECTION CHANGED (GOT TIMESTAMP)" << endl; +#endif + emit clipboardChanged( true ); + } + + if ( changedTimestamp( clipboard, *e ) ) + { +#ifdef NOISY_KLIPPER_ + kdDebug() << "CLIPBOARD CHANGED (GOT TIMESTAMP)" << endl; +#endif + emit clipboardChanged( false ); + } + return true; // filter out + } + return false; +} + +void ClipboardPoll::updateQtOwnership( SelectionData& data ) +{ + Atom type; + int format; + unsigned long nitems; + unsigned long after; + unsigned char* prop = NULL; + if( XGetWindowProperty( qt_xdisplay(), qt_xrootwin( 0 ), data.sentinel_atom, 0, 2, False, + XA_WINDOW, &type, &format, &nitems, &after, &prop ) != Success + || type != XA_WINDOW || format != 32 || nitems != 2 || prop == NULL ) + { +#ifdef REALLY_NOISY_KLIPPER_ + kdDebug() << "UPDATEQT BAD PROPERTY" << endl; +#endif + data.owner_is_qt = false; + if( prop != NULL ) + XFree( prop ); + return; + } + Window owner = reinterpret_cast< long* >( prop )[ 0 ]; // [0] is new owner, [1] is previous + XFree( prop ); + Window current_owner = XGetSelectionOwner( qt_xdisplay(), data.atom ); + data.owner_is_qt = ( owner == current_owner ); +#ifdef REALLY_NOISY_KLIPPER_ + kdDebug() << "owner=" << owner << "; current_owner=" << current_owner << endl; + kdDebug() << "UPDATEQT:" << ( &data == &selection ? "selection" : "clipboard" ) << ":" << data.owner_is_qt << endl; +#endif +} + +void ClipboardPoll::timeout() +{ + KlipperWidget::updateTimestamp(); + if( !kapp->clipboard()->ownsSelection() && checkTimestamp( selection ) ) { +#ifdef NOISY_KLIPPER_ + kdDebug() << "SELECTION CHANGED" << endl; +#endif + emit clipboardChanged( true ); + } + if( !kapp->clipboard()->ownsClipboard() && checkTimestamp( clipboard ) ) { +#ifdef NOISY_KLIPPER_ + kdDebug() << "CLIPBOARD CHANGED" << endl; +#endif + emit clipboardChanged( false ); + } + +} + +bool ClipboardPoll::checkTimestamp( SelectionData& data ) +{ + Window current_owner = XGetSelectionOwner( qt_xdisplay(), data.atom ); + bool signal = false; + updateQtOwnership( data ); + if( data.owner_is_qt ) + { + data.last_change = CurrentTime; +#ifdef REALLY_NOISY_KLIPPER_ + kdDebug() << "(3) Setting last_owner for =" << ( &data==&selection ?"selection":"clipboard" ) << ":" << current_owner << endl; +#endif + data.last_owner = current_owner; + data.waiting_for_timestamp = false; + return false; + } + if( current_owner != data.last_owner ) + { + signal = true; // owner has changed + data.last_owner = current_owner; +#ifdef REALLY_NOISY_KLIPPER_ + kdDebug() << "(4) Setting last_owner for =" << ( &data==&selection ?"selection":"clipboard" ) << ":" << current_owner << endl; +#endif + data.waiting_for_timestamp = false; + data.last_change = CurrentTime; +#ifdef REALLY_NOISY_KLIPPER_ + kdDebug() << "OWNER CHANGE:" << ( data.atom == XA_PRIMARY ) << ":" << current_owner << endl; +#endif + return true; + } + if( current_owner == None ) { + return false; // None also last_owner... + } + if( data.waiting_for_timestamp ) { + // We're already waiting for the timestamp of the last check + return false; + } + XDeleteProperty( qt_xdisplay(), winId(), data.timestamp_atom ); + XConvertSelection( qt_xdisplay(), data.atom, xa_timestamp, data.timestamp_atom, winId(), qt_x_time ); + data.waiting_for_timestamp = true; + data.waiting_x_time = qt_x_time; +#ifdef REALLY_NOISY_KLIPPER_ + kdDebug() << "WAITING TIMESTAMP:" << ( data.atom == XA_PRIMARY ) << endl; +#endif + return false; +} + +bool ClipboardPoll::changedTimestamp( SelectionData& data, const XEvent& ev ) +{ + if( ev.xselection.requestor != winId() + || ev.xselection.selection != data.atom + || ev.xselection.time != data.waiting_x_time ) + { + return false; + } + data.waiting_for_timestamp = false; + if( ev.xselection.property == None ) + { +#ifdef NOISY_KLIPPER_ + kdDebug() << "REFUSED:" << ( data.atom == XA_PRIMARY ) << endl; +#endif + return true; + } + Atom type; + int format; + unsigned long nitems; + unsigned long after; + unsigned char* prop = NULL; + if( XGetWindowProperty( qt_xdisplay(), winId(), ev.xselection.property, 0, 1, False, + AnyPropertyType, &type, &format, &nitems, &after, &prop ) != Success + || format != 32 || nitems != 1 || prop == NULL ) + { +#ifdef NOISY_KLIPPER_ + kdDebug() << "BAD PROPERTY:" << ( data.atom == XA_PRIMARY ) << endl; +#endif + if( prop != NULL ) + XFree( prop ); + return true; + } + Time timestamp = reinterpret_cast< long* >( prop )[ 0 ]; + XFree( prop ); +#ifdef NOISY_KLIPPER_ + kdDebug() << "GOT TIMESTAMP:" << ( data.atom == XA_PRIMARY ) << endl; + kdDebug() << "timestamp=" << timestamp + << "; CurrentTime=" << CurrentTime + << "; last_change=" << data.last_change + << endl; +#endif + if( timestamp != data.last_change || timestamp == CurrentTime ) + { +#ifdef NOISY_KLIPPER_ + kdDebug() << "TIMESTAMP CHANGE:" << ( data.atom == XA_PRIMARY ) << endl; +#endif + data.last_change = timestamp; + return true; + } + return false; // ok, same timestamp +} + +#include "clipboardpoll.moc" diff --git a/klipper/clipboardpoll.h b/klipper/clipboardpoll.h new file mode 100644 index 000000000..c7b3b266d --- /dev/null +++ b/klipper/clipboardpoll.h @@ -0,0 +1,66 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2003 by Lubos Lunak <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _CLIPBOARDPOLL_H_ +#define _CLIPBOARDPOLL_H_ + +#include <qwidget.h> +#include <qtimer.h> +#include <X11/Xlib.h> +#include <fixx11h.h> + +class ClipboardPoll + : public QWidget + { + Q_OBJECT + public: + ClipboardPoll( QWidget* parent ); + signals: + void clipboardChanged( bool selectionMode ); + protected: + virtual bool x11Event( XEvent* ); + private slots: + void timeout(); + void qtSelectionChanged(); + void qtClipboardChanged(); + private: + struct SelectionData + { + Atom atom; + Atom sentinel_atom; + Atom timestamp_atom; + Window last_owner; + bool owner_is_qt; + Time last_change; + bool waiting_for_timestamp; + Time waiting_x_time; + }; + void updateQtOwnership( SelectionData& data ); + bool checkTimestamp( SelectionData& data ); + bool changedTimestamp( SelectionData& data, const XEvent& e ); + void initPolling(); + QTimer timer; + SelectionData selection; + SelectionData clipboard; + Atom xa_clipboard; + Atom xa_timestamp; + int xfixes_event_base; + }; + +#endif diff --git a/klipper/configdialog.cpp b/klipper/configdialog.cpp new file mode 100644 index 000000000..a195a2332 --- /dev/null +++ b/klipper/configdialog.cpp @@ -0,0 +1,465 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2000 by Carsten Pfeiffer <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include <qlabel.h> +#include <qlayout.h> +#include <qlistview.h> +#include <qpushbutton.h> +#include <qtooltip.h> +#include <qwhatsthis.h> +#include <qvbuttongroup.h> +#include <assert.h> + +#include <kiconloader.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kwinmodule.h> +#include <kregexpeditorinterface.h> +#include <kparts/componentfactory.h> + +#include "configdialog.h" + +ConfigDialog::ConfigDialog( const ActionList *list, KGlobalAccel *accel, + bool isApplet ) + : KDialogBase( Tabbed, i18n("Configure"), + Ok | Cancel | Help, + Ok, 0L, "config dialog" ) +{ + if ( isApplet ) + setHelp( QString::null, "klipper" ); + + QFrame *w = 0L; // the parent for the widgets + + w = addVBoxPage( i18n("&General") ); + generalWidget = new GeneralWidget( w, "general widget" ); + + w = addVBoxPage( i18n("Ac&tions") ); + actionWidget = new ActionWidget( list, this, w, "actions widget" ); + + w = addVBoxPage( i18n("Global &Shortcuts") ); + keysWidget = new KKeyChooser( accel, w ); +} + + +ConfigDialog::~ConfigDialog() +{ +} + +// prevent huge size due to long regexps in the action-widget +void ConfigDialog::show() +{ + if ( !isVisible() ) { + KWinModule module(0, KWinModule::INFO_DESKTOP); + QSize s1 = sizeHint(); + QSize s2 = module.workArea().size(); + int w = s1.width(); + int h = s1.height(); + + if ( s1.width() >= s2.width() ) + w = s2.width(); + if ( s1.height() >= s2.height() ) + h = s2.height(); + + resize( w, h ); + } + + KDialogBase::show(); +} + +void ConfigDialog::commitShortcuts() +{ + keysWidget->commitChanges(); +} + +///////////////////////////////////////// +//// + + +GeneralWidget::GeneralWidget( QWidget *parent, const char *name ) + : QVBox( parent, name ) +{ + setSpacing(KDialog::spacingHint()); + + cbMousePos = new QCheckBox( i18n("&Popup menu at mouse-cursor position"), + this ); + cbSaveContents = new QCheckBox( i18n("Save clipboard contents on e&xit"), + this ); + cbStripWhitespace = new QCheckBox( i18n("Remove whitespace when executing actions"), this ); + QWhatsThis::add( cbStripWhitespace, + i18n("Sometimes, the selected text has some whitespace at the end, which, if loaded as URL in a browser would cause an error. Enabling this option removes any whitespace at the beginning or end of the selected string (the original clipboard contents will not be modified).") ); + + cbReplayAIH = new QCheckBox( i18n("&Replay actions on an item selected from history"), + this ); + + cbNoNull = new QCheckBox( i18n("Pre&vent empty clipboard"), this ); + QWhatsThis::add( cbNoNull, + i18n("Selecting this option has the effect, that the " + "clipboard can never be emptied. E.g. when an " + "application exits, the clipboard would usually be " + "emptied.") ); + + cbIgnoreSelection = new QCheckBox( i18n("&Ignore selection"), this ); + QWhatsThis::add( cbIgnoreSelection, + i18n("This option prevents the selection being recorded " + "in the clipboard history. Only explicit clipboard " + "changes are recorded.") ); + + QVButtonGroup *group = new QVButtonGroup( i18n("Clipboard/Selection Behavior"), this ); + group->setExclusive( true ); + + QWhatsThis::add( group, + i18n("<qt>There are two different clipboard buffers available:<br><br>" + "<b>Clipboard</b> is filled by selecting something " + "and pressing Ctrl+C, or by clicking \"Copy\" in a toolbar or " + "menubar.<br><br>" + "<b>Selection</b> is available immediately after " + "selecting some text. The only way to access the selection " + "is to press the middle mouse button.<br><br>" + "You can configure the relationship between Clipboard and Selection." + "</qt>" )); + + cbSynchronize = new QRadioButton( + i18n("Sy&nchronize contents of the clipboard and the selection"), + group ); + QWhatsThis::add( cbSynchronize, + i18n("Selecting this option synchronizes these two buffers, so they " + "work the same way as in KDE 1.x and 2.x.") ); + + cbSeparate = new QRadioButton( + i18n("Separate clipboard and selection"), group ); + QWhatsThis::add( + cbSeparate, + i18n("Using this option will only set the selection when highlighting " + "something and the clipboard when choosing e.g. \"Copy\" " + "in a menubar.") ); + + cbSeparate->setChecked( !cbSynchronize->isChecked() ); + + popupTimeout = new KIntNumInput( this ); + popupTimeout->setLabel( i18n( "Tim&eout for action popups:" ) ); + popupTimeout->setRange( 0, 200, 1, true ); + popupTimeout->setSuffix( i18n( " sec" ) ); + QToolTip::add( popupTimeout, i18n("A value of 0 disables the timeout") ); + + maxItems = new KIntNumInput( this ); + maxItems->setLabel( i18n( "C&lipboard history size:" ) ); + maxItems->setRange( 2, 2048, 1, true ); + connect( maxItems, SIGNAL( valueChanged( int )), + SLOT( historySizeChanged( int ) )); + + connect( group, SIGNAL( clicked( int )), + SLOT( slotClipConfigChanged() )); + slotClipConfigChanged(); + + // Add some spacing at the end + QWidget *dummy = new QWidget( this ); + setStretchFactor( dummy, 1 ); +} + +GeneralWidget::~GeneralWidget() +{ +} + +void GeneralWidget::historySizeChanged( int value ) +{ + // Note there is no %n in this string, because value is not supposed + // to be put into the suffix of the spinbox. + maxItems->setSuffix( i18n( " entry", " entries", value ) ); +} + +void GeneralWidget::slotClipConfigChanged() +{ + cbIgnoreSelection->setEnabled( !cbSynchronize->isChecked() ); +} + +///////////////////////////////////////// +//// + +void ListView::rename( QListViewItem* item, int c ) +{ + bool gui = false; + if ( item->childCount() != 0 && c == 0) { + // This is the regular expression + if ( _configWidget->useGUIRegExpEditor() ) { + gui = true; + } + } + + if ( gui ) { + if ( ! _regExpEditor ) + _regExpEditor = KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor", QString::null, this ); + KRegExpEditorInterface *iface = static_cast<KRegExpEditorInterface *>( _regExpEditor->qt_cast( "KRegExpEditorInterface" ) ); + assert( iface ); + iface->setRegExp( item->text( 0 ) ); + + bool ok = _regExpEditor->exec(); + if ( ok ) + item->setText( 0, iface->regExp() ); + + } + else + KListView::rename( item ,c ); +} + + +ActionWidget::ActionWidget( const ActionList *list, ConfigDialog* configWidget, QWidget *parent, + const char *name ) + : QVBox( parent, name ), + advancedWidget( 0L ) +{ + Q_ASSERT( list != 0L ); + + QLabel *lblAction = new QLabel( + i18n("Action &list (right click to add/remove commands):"), this ); + + listView = new ListView( configWidget, this, "list view" ); + lblAction->setBuddy( listView ); + listView->addColumn( i18n("Regular Expression (see http://doc.trolltech.com/qregexp.html#details)") ); + listView->addColumn( i18n("Description") ); + + listView->setRenameable(0); + listView->setRenameable(1); + listView->setItemsRenameable( true ); + listView->setItemsMovable( false ); +// listView->setAcceptDrops( true ); +// listView->setDropVisualizer( true ); +// listView->setDragEnabled( true ); + + listView->setRootIsDecorated( true ); + listView->setMultiSelection( false ); + listView->setAllColumnsShowFocus( true ); + listView->setSelectionMode( QListView::Single ); + connect( listView, SIGNAL(executed( QListViewItem*, const QPoint&, int )), + SLOT( slotItemChanged( QListViewItem*, const QPoint& , int ) )); + connect( listView, SIGNAL( selectionChanged ( QListViewItem * )), + SLOT(selectionChanged ( QListViewItem * ))); + connect(listView, + SIGNAL(contextMenu(KListView *, QListViewItem *, const QPoint&)), + SLOT( slotContextMenu(KListView*, QListViewItem*, const QPoint&))); + + ClipAction *action = 0L; + ClipCommand *command = 0L; + QListViewItem *item = 0L; + QListViewItem *child = 0L; + QListViewItem *after = 0L; // QListView's default inserting really sucks + ActionListIterator it( *list ); + + const QPixmap& doc = SmallIcon( "misc" ); + const QPixmap& exec = SmallIcon( "exec" ); + + for ( action = it.current(); action; action = ++it ) { + item = new QListViewItem( listView, after, + action->regExp(), action->description() ); + item->setPixmap( 0, doc ); + + QPtrListIterator<ClipCommand> it2( action->commands() ); + for ( command = it2.current(); command; command = ++it2 ) { + child = new QListViewItem( item, after, + command->command, command->description); + if ( command->pixmap.isEmpty() ) + child->setPixmap( 0, exec ); + else + child->setPixmap( 0, SmallIcon( command->pixmap ) ); + after = child; + } + after = item; + } + + listView->setSorting( -1 ); // newly inserted items just append unsorted + + cbUseGUIRegExpEditor = new QCheckBox( i18n("&Use graphical editor for editing regular expressions" ), this ); + if ( KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty() ) + { + cbUseGUIRegExpEditor->hide(); + cbUseGUIRegExpEditor->setChecked( false ); + } + + QHBox *box = new QHBox( this ); + box->setSpacing( KDialog::spacingHint() ); + QPushButton *button = new QPushButton( i18n("&Add Action"), box ); + connect( button, SIGNAL( clicked() ), SLOT( slotAddAction() )); + + delActionButton = new QPushButton( i18n("&Delete Action"), box ); + connect( delActionButton, SIGNAL( clicked() ), SLOT( slotDeleteAction() )); + + QLabel *label = new QLabel(i18n("Click on a highlighted item's column to change it. \"%s\" in a command will be replaced with the clipboard contents."), box); + label->setAlignment( WordBreak | AlignLeft | AlignVCenter ); + + box->setStretchFactor( label, 5 ); + + box = new QHBox( this ); + QPushButton *advanced = new QPushButton( i18n("Advanced..."), box ); + advanced->setFixedSize( advanced->sizeHint() ); + connect( advanced, SIGNAL( clicked() ), SLOT( slotAdvanced() )); + (void) new QWidget( box ); // spacer + + delActionButton->setEnabled(listView->currentItem () !=0); +} + +ActionWidget::~ActionWidget() +{ +} + +void ActionWidget::selectionChanged ( QListViewItem * item) +{ + delActionButton->setEnabled(item!=0); +} + +void ActionWidget::slotContextMenu( KListView *, QListViewItem *item, + const QPoint& pos ) +{ + if ( !item ) + return; + + int addCmd = 0, rmCmd = 0; + KPopupMenu *menu = new KPopupMenu; + addCmd = menu->insertItem( i18n("Add Command") ); + rmCmd = menu->insertItem( i18n("Remove Command") ); + if ( !item->parent() ) {// no "command" item + menu->setItemEnabled( rmCmd, false ); + item->setOpen( true ); + } + + int id = menu->exec( pos ); + if ( id == addCmd ) { + QListViewItem *p = item->parent() ? item->parent() : item; + QListViewItem *cmdItem = new QListViewItem( p, item, + i18n("Click here to set the command to be executed"), + i18n("<new command>") ); + cmdItem->setPixmap( 0, SmallIcon( "exec" ) ); + } + else if ( id == rmCmd ) + delete item; + + delete menu; +} + +void ActionWidget::slotItemChanged( QListViewItem *item, const QPoint&, int col ) +{ + if ( !item->parent() || col != 0 ) + return; + ClipCommand command( item->text(0), item->text(1) ); + item->setPixmap( 0, SmallIcon( command.pixmap.isEmpty() ? + "exec" : command.pixmap ) ); +} + +void ActionWidget::slotAddAction() +{ + QListViewItem *item = new QListViewItem( listView ); + item->setPixmap( 0, SmallIcon( "misc" )); + item->setText( 0, i18n("Click here to set the regexp")); + item->setText( 1, i18n("<new action>")); +} + + +void ActionWidget::slotDeleteAction() +{ + QListViewItem *item = listView->currentItem(); + if ( item && item->parent() ) + item = item->parent(); + delete item; +} + + +ActionList * ActionWidget::actionList() +{ + QListViewItem *item = listView->firstChild(); + QListViewItem *child = 0L; + ClipAction *action = 0L; + ActionList *list = new ActionList; + list->setAutoDelete( true ); + while ( item ) { + action = new ClipAction( item->text( 0 ), item->text( 1 ) ); + child = item->firstChild(); + + // add the commands + while ( child ) { + action->addCommand( child->text( 0 ), child->text( 1 ), true ); + child = child->nextSibling(); + } + + list->append( action ); + item = item->nextSibling(); + } + + return list; +} + +void ActionWidget::slotAdvanced() +{ + KDialogBase dlg( 0L, "advanced dlg", true, + i18n("Advanced Settings"), + KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok ); + QVBox *box = dlg.makeVBoxMainWidget(); + AdvancedWidget *widget = new AdvancedWidget( box ); + widget->setWMClasses( m_wmClasses ); + + dlg.resize( dlg.sizeHint().width(), + dlg.sizeHint().height() +40); // or we get an ugly scrollbar :( + + if ( dlg.exec() == QDialog::Accepted ) { + m_wmClasses = widget->wmClasses(); + } +} + +AdvancedWidget::AdvancedWidget( QWidget *parent, const char *name ) + : QVBox( parent, name ) +{ + editListBox = new KEditListBox( i18n("D&isable Actions for Windows of Type WM_CLASS"), this, "editlistbox", true, KEditListBox::Add | KEditListBox::Remove ); + + QWhatsThis::add( editListBox, + i18n("<qt>This lets you specify windows in which Klipper should " + "not invoke \"actions\". Use<br><br>" + "<center><b>xprop | grep WM_CLASS</b></center><br>" + "in a terminal to find out the WM_CLASS of a window. " + "Next, click on the window you want to examine. The " + "first string it outputs after the equal sign is the one " + "you need to enter here.</qt>")); + + editListBox->setFocus(); +} + +AdvancedWidget::~AdvancedWidget() +{ +} + +void AdvancedWidget::setWMClasses( const QStringList& items ) +{ + editListBox->clear(); + editListBox->insertStringList( items ); +} + + + +/////////////////////////////////////////////////////// +////////// + +/* +KeysWidget::KeysWidget( KAccelActions &keyMap, QWidget *parent, const char *name) + : QVBox( parent, name ) +{ + keyChooser = new KKeyChooser( keyMap, this ); +} + +KeysWidget::~KeysWidget() +{ +} +*/ + +#include "configdialog.moc" diff --git a/klipper/configdialog.h b/klipper/configdialog.h new file mode 100644 index 000000000..be64b8335 --- /dev/null +++ b/klipper/configdialog.h @@ -0,0 +1,263 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2000 by Carsten Pfeiffer <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef CONFIGDIALOG_H +#define CONFIGDIALOG_H + +#include <qcheckbox.h> +#include <qevent.h> +#include <qgroupbox.h> +#include <qheader.h> +#include <qradiobutton.h> +#include <qvbox.h> + +#include <kdialogbase.h> +#include <keditlistbox.h> +#include <kkeydialog.h> +#include <klistview.h> +#include <knuminput.h> + +#include "urlgrabber.h" + +class KGlobalAccel; +class KKeyChooser; +class KListView; +class QPushButton; +class QDialog; +class ConfigDialog; + +class GeneralWidget : public QVBox +{ + Q_OBJECT + + friend class ConfigDialog; + +public: + GeneralWidget( QWidget *parent, const char *name ); + ~GeneralWidget(); + +private slots: + void historySizeChanged( int value ); + void slotClipConfigChanged(); + +private: + QCheckBox *cbMousePos, *cbSaveContents, *cbReplayAIH, *cbNoNull; + QCheckBox *cbIgnoreSelection, *cbStripWhitespace; + QRadioButton *cbSynchronize, *cbImplicitSelection, *cbSeparate; + KIntNumInput *popupTimeout, *maxItems; + +}; + + +// only for use inside ActionWidget +class AdvancedWidget : public QVBox +{ + Q_OBJECT + +public: + AdvancedWidget( QWidget *parent = 0L, const char *name = 0L ); + ~AdvancedWidget(); + + void setWMClasses( const QStringList& items ); + QStringList wmClasses() const { return editListBox->items(); } + +private: + KEditListBox *editListBox; +}; + +class ActionWidget : public QVBox +{ + Q_OBJECT + + friend class ConfigDialog; + +public: + ActionWidget( const ActionList *list, ConfigDialog* configWidget, QWidget *parent, const char *name ); + ~ActionWidget(); + + /** + * Creates a list of actions from the listView and returns a pointer to + * the list. + * Make sure to free that pointer when you don't need it anymore. + */ + ActionList * actionList(); + + void setWMClasses( const QStringList& items ) { m_wmClasses = items; } + QStringList wmClasses() const { return m_wmClasses; } + +private slots: + void slotAddAction(); + void slotDeleteAction(); + void slotItemChanged( QListViewItem *, const QPoint& , int ); + void slotAdvanced(); + void slotContextMenu( KListView *, QListViewItem *, const QPoint& ); + void selectionChanged ( QListViewItem *); + +private: + KListView *listView; + QStringList m_wmClasses; + AdvancedWidget *advancedWidget; + QPushButton *delActionButton; + QCheckBox *cbUseGUIRegExpEditor; +}; + +/*class KeysWidget : public QVBox +{ + Q_OBJECT + + friend class ConfigDialog; + +public: + KeysWidget( KAccelActions &keyMap, QWidget *parent, const char *name ); + ~KeysWidget(); + +private: + KKeyChooser *keyChooser; +};*/ + + +class ConfigDialog : public KDialogBase +{ + Q_OBJECT + +public: + ConfigDialog( const ActionList *list, KGlobalAccel *accel, bool isApplet ); + ~ConfigDialog(); + + ActionList * actionList() const { return actionWidget->actionList(); } + + bool keepContents() const { + return generalWidget->cbSaveContents->isChecked(); + } + bool popupAtMousePos() const { + return generalWidget->cbMousePos->isChecked(); + } + bool stripWhiteSpace() const { + return generalWidget->cbStripWhitespace->isChecked(); + } + bool replayActionInHistory() const { + return generalWidget->cbReplayAIH->isChecked(); + } + bool noNullClipboard() const { + return generalWidget->cbNoNull->isChecked(); + } + + int popupTimeout() const { + return generalWidget->popupTimeout->value(); + } + int maxItems() const { + return generalWidget->maxItems->value(); + } + bool ignoreSelection() const + { + return generalWidget->cbIgnoreSelection->isChecked(); + } + QStringList noActionsFor() const { + return actionWidget->wmClasses(); + } + bool useGUIRegExpEditor() const + { + return actionWidget->cbUseGUIRegExpEditor->isChecked(); + } + + bool synchronize() const { + return generalWidget->cbSynchronize->isChecked(); + } + bool implicitSelection() const { + return generalWidget->cbImplicitSelection->isChecked(); + } + + void setKeepContents( bool enable ) { + generalWidget->cbSaveContents->setChecked( enable ); + } + void setPopupAtMousePos( bool enable ) { + generalWidget->cbMousePos->setChecked( enable ); + } + void setStripWhiteSpace( bool enable ) { + generalWidget->cbStripWhitespace->setChecked( enable ); + } + void setReplayActionInHistory( bool enable ) { + generalWidget->cbReplayAIH->setChecked( enable ); + } + void setNoNullClipboard( bool enable ) { + generalWidget->cbNoNull->setChecked( enable ); + } + void setPopupTimeout( int timeout ) { + generalWidget->popupTimeout->setValue( timeout ); + } + void setMaxItems( int items ) { + generalWidget->maxItems->setValue( items ); + } + void setIgnoreSelection( bool ignore ) { + generalWidget->cbIgnoreSelection->setChecked( ignore ); + } + void setSynchronize( bool synchronize ) { + generalWidget->cbSynchronize->setChecked( synchronize ); + } + void setNoActionsFor( const QStringList& items ) { + actionWidget->setWMClasses( items ); + } + void setUseGUIRegExpEditor( bool enabled ) + { + // the checkbox is only hidden explicitly when there's no + // regexp editor component available. + if ( !actionWidget->cbUseGUIRegExpEditor->isHidden() ) + actionWidget->cbUseGUIRegExpEditor->setChecked( enabled ); + } + + virtual void show(); + void commitShortcuts(); + +private: + GeneralWidget *generalWidget; + ActionWidget *actionWidget; + KKeyChooser *keysWidget; + +}; + +class ListView : public KListView +{ +public: + ListView( ConfigDialog* configWidget, QWidget *parent, const char *name ) + : KListView( parent, name ), _configWidget( configWidget ), + _regExpEditor(0L) {} + // QListView has a weird idea of a sizeHint... + virtual QSize sizeHint () const { + int w = minimumSizeHint().width(); + int h = header()->height(); + h += viewport()->sizeHint().height(); + h += horizontalScrollBar()->height(); + + QListViewItem *item = firstChild(); + while ( item ) { + h += item->totalHeight(); + item = item->nextSibling(); + } + + return QSize( w, h ); + } + +protected: + virtual void rename( QListViewItem* item, int c ); +private: + ConfigDialog* _configWidget; + QDialog* _regExpEditor; +}; + +#endif // CONFIGDIALOG_H diff --git a/klipper/configure.in.in b/klipper/configure.in.in new file mode 100644 index 000000000..c958872d0 --- /dev/null +++ b/klipper/configure.in.in @@ -0,0 +1,11 @@ +dnl XFixes check +KDE_CHECK_HEADER(X11/extensions/Xfixes.h, [xfixes_h=yes], [xfixes_h=no], [#include <X11/Xlib.h>]) +if test "$xfixes_h" = yes; then + KDE_CHECK_LIB(Xfixes, XFixesQueryExtension, [ + LIB_XFIXES=-lXfixes + AC_DEFINE_UNQUOTED(HAVE_XFIXES, 1, [Define if you have the XFixes extension]) + ], [], -lXext -lX11 $X_EXTRA_LIBS) +else + LIB_XFIXES= +fi +AC_SUBST(LIB_XFIXES) diff --git a/klipper/hi128-app-klipper.png b/klipper/hi128-app-klipper.png Binary files differnew file mode 100644 index 000000000..bff59bcae --- /dev/null +++ b/klipper/hi128-app-klipper.png diff --git a/klipper/hi16-app-klipper.png b/klipper/hi16-app-klipper.png Binary files differnew file mode 100644 index 000000000..2b1fbbafe --- /dev/null +++ b/klipper/hi16-app-klipper.png diff --git a/klipper/hi22-app-klipper.png b/klipper/hi22-app-klipper.png Binary files differnew file mode 100644 index 000000000..4044ab86f --- /dev/null +++ b/klipper/hi22-app-klipper.png diff --git a/klipper/hi32-app-klipper.png b/klipper/hi32-app-klipper.png Binary files differnew file mode 100644 index 000000000..e84fa7680 --- /dev/null +++ b/klipper/hi32-app-klipper.png diff --git a/klipper/hi48-app-klipper.png b/klipper/hi48-app-klipper.png Binary files differnew file mode 100644 index 000000000..e808a3c2d --- /dev/null +++ b/klipper/hi48-app-klipper.png diff --git a/klipper/hi64-app-klipper.png b/klipper/hi64-app-klipper.png Binary files differnew file mode 100644 index 000000000..8d966f25d --- /dev/null +++ b/klipper/hi64-app-klipper.png diff --git a/klipper/hisc-app-klipper.svgz b/klipper/hisc-app-klipper.svgz Binary files differnew file mode 100644 index 000000000..3f1f30d7d --- /dev/null +++ b/klipper/hisc-app-klipper.svgz diff --git a/klipper/history.cpp b/klipper/history.cpp new file mode 100644 index 000000000..068475214 --- /dev/null +++ b/klipper/history.cpp @@ -0,0 +1,132 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + Copyright (C) by Andrew Stanley-Jones + Copyright (C) 2000 by Carsten Pfeiffer <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include <kdebug.h> + +#include "history.h" +#include "historystringitem.h" +#include "klipperpopup.h" + +History::History( QWidget* parent, const char* name ) + : QObject( parent, name ), + m_popup( new KlipperPopup( this, parent, "main_widget" ) ), + m_topIsUserSelected( false ) +{ + connect( this, SIGNAL( changed() ), m_popup, SLOT( slotHistoryChanged() ) ); + itemList.setAutoDelete( true ); + +} + + +History::~History() { +} + +History::iterator History::youngest() { + return iterator( itemList ); +} + +void History::insert( const HistoryItem* item ) { + if ( !item ) + return; + + m_topIsUserSelected = false; + + // Optimisation: Compare with top item. + if ( !itemList.isEmpty() && *itemList.first() == *item ) { + delete item; + return; + } + + remove( item ); + forceInsert( item ); + + emit topChanged(); + +} + +void History::forceInsert( const HistoryItem* item ) { + if ( !item ) + return; + itemList.prepend( item ); + emit changed(); + trim(); +} + +void History::trim() { + int i = itemList.count() - max_size(); + if ( i <= 0 ) + return; + + while ( i-- ) { + itemList.removeLast(); + } + emit changed(); +} + +void History::remove( const HistoryItem* newItem ) { + if ( !newItem ) + return; + + for ( const HistoryItem* item = itemList.first(); item; item=next() ) { + if ( *item == *newItem ) { + itemList.remove(); + emit changed(); + return; + } + } + +} + + +void History::slotClear() { + itemList.clear(); + emit changed(); +} + +void History::slotMoveToTop(int pos ) { + if ( pos < 0 || static_cast<unsigned>( pos ) >= itemList.count() ) { + kdDebug() << "Argument pos out of range: " << pos << endl; + return; + } + + m_topIsUserSelected = true; + + itemList.first(); + for ( ; pos; pos-- ) { + itemList.next(); + } + HistoryItem* item = itemList.take(); + itemList.prepend( item ); + emit changed(); + emit topChanged(); +} + +void History::max_size( unsigned max_size ) { + m_max_size = max_size; + trim(); + +} + +KlipperPopup* History::popup() { + return m_popup; +} + +#include "history.moc" diff --git a/klipper/history.h b/klipper/history.h new file mode 100644 index 000000000..9c54cb387 --- /dev/null +++ b/klipper/history.h @@ -0,0 +1,169 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + Copyright (C) Andrew Stanley-Jones + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _HISTORY_H_ +#define _HISTORY_H_ + +#include <qobject.h> +#include <qptrlist.h> +#include "historyitem.h" + +class KlipperPopup; +class QPopupMenu; +class QWidget; +class QptrListIterator; + +class History : public QObject +{ + Q_OBJECT +public: + History( QWidget* parent, const char* name ); + ~History(); + /** + * Iterator for history + */ + typedef QPtrListIterator<HistoryItem> iterator; + + /** + * Return (toplevel) popup menu (or default view, of you like) + */ + KlipperPopup* popup(); + + /** + * Inserts item into clipboard history top + * if duplicate entry exist, the older duplicate is deleted. + * The duplicate concept is "deep", so that two text string + * are considerd duplicate if identical. + */ + void insert( const HistoryItem* item ); + + /** + * Inserts item into clipboard without any checks + * Used when restoring a saved history and internally. + * Don't use this unless you're reasonable certain + * that no duplicates are introduced + */ + void forceInsert( const HistoryItem* item ); + + /** + * Remove (first) history item equal to item from history + */ + void remove( const HistoryItem* item ); + + /** + * Traversal: Get first item + */ + const HistoryItem* first(); + + /** + * Traversal: Get current item + */ + const HistoryItem* next(); + + /** + * Get an iterator pointing to the first (most recent) item + * This iterator should probably be a constant iterator, but + * the QTL doesn't support this easily. + * + * Anyway, if you modify the items via. the iterator, call changed() + * when you're done. Calling changed() multiple times doesn't hurt. + * + * iterator could be made into a proxy class that did the right thing. + */ + iterator youngest(); + + /** + * True if no history items + */ + bool empty() const { return itemList.isEmpty(); } + + /** + * Set maximum history size + */ + void max_size( unsigned max_size ); + + /** + * Get the maximum history size + */ + unsigned max_size() const { return m_max_size; } + + /** + * returns true if the user has selected the top item + */ + bool topIsUserSelected() { + return m_topIsUserSelected; + } + +public slots: + /** + * move the history in position pos to top + */ + void slotMoveToTop(int pos ); + + /** + * Clear history + */ + void slotClear(); + +signals: + void changed(); + + /** + * Emitted when the first history item has changed. + */ + void topChanged(); + +private: + + /** + * The history + */ + QPtrList<HistoryItem> itemList; + + /** + * ensure that the number of items does not exceed max_size() + * Deletes items from the end as neccessary. + */ + void trim(); + +private: + /** + * "Default view" --- a popupmenu containing the clipboard history. + */ + KlipperPopup* m_popup; + + + /** + * The number of clipboard items stored. + */ + unsigned m_max_size; + + /** + * True if the top is selected by the user + */ + bool m_topIsUserSelected; + +}; + +inline const HistoryItem* History::first() { return itemList.first(); } + +inline const HistoryItem* History::next() { return itemList.next(); } + +#endif diff --git a/klipper/historyimageitem.cpp b/klipper/historyimageitem.cpp new file mode 100644 index 000000000..f95a76429 --- /dev/null +++ b/klipper/historyimageitem.cpp @@ -0,0 +1,46 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qstring.h> + +#include <kdebug.h> + +#include "historyimageitem.h" + +HistoryImageItem::HistoryImageItem( const QPixmap& data ) + : HistoryItem(), m_data( data ) +{ +} + +QString HistoryImageItem::text() const { + if ( m_text.isNull() ) { + m_text = QString( "%1x%2x%3 %4" ) + .arg( m_data.width() ) + .arg( m_data.height() ) + .arg( m_data.depth() ); + } + return m_text; + +} + +/* virtual */ +void HistoryImageItem::write( QDataStream& stream ) const { + stream << QString( "image" ) << m_data; +} diff --git a/klipper/historyimageitem.h b/klipper/historyimageitem.h new file mode 100644 index 000000000..2518e0a4c --- /dev/null +++ b/klipper/historyimageitem.h @@ -0,0 +1,61 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _HISTORYIMAGEITEM_H_ +#define _HISTORYIMAGEITEM_H_ + +#include "historyitem.h" +#include <qpixmap.h> +#include <qdragobject.h> + +/** + * A image entry in the clipboard history. + */ +class HistoryImageItem : public HistoryItem +{ +public: + HistoryImageItem( const QPixmap& data ); + virtual ~HistoryImageItem() {} + virtual QString text() const; + virtual bool operator==( const HistoryItem& rhs) const { + if ( const HistoryImageItem* casted_rhs = dynamic_cast<const HistoryImageItem*>( &rhs ) ) { + return &casted_rhs->m_data == &m_data; // Not perfect, but better than nothing. + } + return false; + } + virtual const QPixmap& image() const { return m_data; } + virtual QMimeSource* mimeSource() const { return new QImageDrag( m_data.convertToImage()) ; } + + virtual void write( QDataStream& stream ) const; + +private: + /** + * + */ + const QPixmap m_data; + /** + * Cache for m_data's string representation + */ + mutable QString m_text; +}; + + + +#endif diff --git a/klipper/historyitem.cpp b/klipper/historyitem.cpp new file mode 100644 index 000000000..680e6b700 --- /dev/null +++ b/klipper/historyitem.cpp @@ -0,0 +1,101 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include <qmime.h> +#include <qdragobject.h> +#include <qmap.h> +#include <qstring.h> +#include <qpixmap.h> +#include <kurldrag.h> + +#include <kdebug.h> + +#include "historyitem.h" +#include "historystringitem.h" +#include "historyimageitem.h" +#include "historyurlitem.h" + +HistoryItem::HistoryItem() { + +} + +HistoryItem::~HistoryItem() { + +} + +HistoryItem* HistoryItem::create( const QMimeSource& aSource ) +{ +#if 0 + int i=0; + while ( const char* f = aSource.format( i++ ) ) { + kdDebug() << "format(" << i <<"): " << f << endl; + } +#endif + if( KURLDrag::canDecode( &aSource )) { + KURL::List urls; + QMap<QString,QString> metaData; + if( KURLDrag::decode( &aSource, urls, metaData )) { + // this is from KonqDrag (libkonq) + QByteArray a = aSource.encodedData( "application/x-kde-cutselection" ); + bool cut = !a.isEmpty() && (a.at(0) == '1'); // true if 1 + return new HistoryURLItem( urls, metaData, cut ); + } + } + if ( QTextDrag::canDecode( &aSource ) ) { + QString text; + if( QTextDrag::decode( &aSource, text )) + return text.isNull() ? 0 : new HistoryStringItem( text ); + } + if ( QImageDrag::canDecode( &aSource ) ) { + QPixmap image; + if( QImageDrag::decode( &aSource, image )) + return image.isNull() ? 0 : new HistoryImageItem( image ); + } + return 0; // Failed. +} + +HistoryItem* HistoryItem::create( QDataStream& aSource ) { + if ( aSource.atEnd() ) { + return 0; + } + QString type; + aSource >> type; + if ( type == "url" ) { + KURL::List urls; + QMap< QString, QString > metaData; + int cut; + aSource >> urls; + aSource >> metaData; + aSource >> cut; + return new HistoryURLItem( urls, metaData, cut ); + } + if ( type == "string" ) { + QString text; + aSource >> text; + return new HistoryStringItem( text ); + } + if ( type == "image" ) { + QPixmap image; + aSource >> image; + return new HistoryImageItem( image ); + } + kdWarning() << "Failed to restore history item: Unknown type \"" << type << "\"" << endl; + return 0; +} + diff --git a/klipper/historyitem.h b/klipper/historyitem.h new file mode 100644 index 000000000..04ab542ed --- /dev/null +++ b/klipper/historyitem.h @@ -0,0 +1,95 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _HISTORYITEM_H_ +#define _HISTORYITEM_H_ +#include <qpixmap.h> + +class QString; +class QMimeSource; +class QDataStream; + +/** + * An entry in the clipboard history. + */ +class HistoryItem +{ +public: + HistoryItem( ); + virtual ~HistoryItem(); + + /** + * Return the current item as text + * An image would be returned as a descriptive + * text, such as 32x43 image. + */ + virtual QString text() const = 0; + + /** + * Return the current item as text + * A text would be returned as a null pixmap, + * which is also the default implementation + */ + inline virtual const QPixmap& image() const; + + /** + * Returns QMimeSource suitable for QClipboard::setData(). + */ + virtual QMimeSource* mimeSource() const = 0; + + /** + * Write object on datastream + */ + virtual void write( QDataStream& stream ) const = 0; + + /** + * Equality. + */ + virtual bool operator==(const HistoryItem& rhs) const = 0; + + /** + * Create an HistoryItem from MimeSources (i.e., clipboard data) + * returns null if create fails (e.g, unsupported mimetype) + */ + static HistoryItem* create( const QMimeSource& aSource ); + + /** + * Create an HistoryItem from MimeSources (i.e., clipboard data) + * returns null if creation fails. In this case, the datastream + * is left in an undefined state. + */ + static HistoryItem* create( QDataStream& aSource ); +}; + +inline +const QPixmap& HistoryItem::image() const { + static QPixmap nullPixmap; + return nullPixmap; +} + +inline +QDataStream& operator<<( QDataStream& lhs, HistoryItem const * const rhs ) { + if ( rhs ) { + rhs->write( lhs ); + } + return lhs; + +} + +#endif diff --git a/klipper/historystringitem.cpp b/klipper/historystringitem.cpp new file mode 100644 index 000000000..dcb7b8da9 --- /dev/null +++ b/klipper/historystringitem.cpp @@ -0,0 +1,31 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "historystringitem.h" + +HistoryStringItem::HistoryStringItem( const QString& data ) + : HistoryItem(), m_data( data ) +{ + +} + +/* virtual */ +void HistoryStringItem::write( QDataStream& stream ) const { + stream << QString( "string" ) << m_data; +} diff --git a/klipper/historystringitem.h b/klipper/historystringitem.h new file mode 100644 index 000000000..ccbaa2e63 --- /dev/null +++ b/klipper/historystringitem.h @@ -0,0 +1,57 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _HISTORYSTRINGITEM_H_ +#define _HISTORYSTRINGITEM_H_ + +#include <qstring.h> +#include <qdragobject.h> + +#include "historyitem.h" + +/** + * A string entry in the clipboard history. + */ +class HistoryStringItem : public HistoryItem +{ +public: + HistoryStringItem( const QString& data ); + virtual ~HistoryStringItem() {} + virtual QString text() const; + virtual bool operator==( const HistoryItem& rhs) const { + if ( const HistoryStringItem* casted_rhs = dynamic_cast<const HistoryStringItem*>( &rhs ) ) { + return casted_rhs->m_data == m_data; + } + return false; + } + virtual QMimeSource* mimeSource() const { return new QTextDrag( m_data ) ; } + + /** + * Write object on datastream + */ + virtual void write( QDataStream& stream ) const; + +private: + QString m_data; +}; + +inline QString HistoryStringItem::text() const { return m_data; } + + +#endif diff --git a/klipper/historyurlitem.cpp b/klipper/historyurlitem.cpp new file mode 100644 index 000000000..3e85a17c3 --- /dev/null +++ b/klipper/historyurlitem.cpp @@ -0,0 +1,63 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "historyurlitem.h" + +#include <kmultipledrag.h> +#include <kurldrag.h> + +HistoryURLItem::HistoryURLItem( const KURL::List &_urls, QMap<QString, QString> _metaData, bool _cut ) + : urls( _urls ), metaData( _metaData ), cut( _cut ) +{ +} + +/* virtual */ +void HistoryURLItem::write( QDataStream& stream ) const +{ + stream << QString( "url" ) << urls << metaData << (int)cut; +} + +QString HistoryURLItem::text() const { + return urls.toStringList().join( " " ); +} + +QMimeSource* HistoryURLItem::mimeSource() const { + KMultipleDrag* drag = new KMultipleDrag; + drag->addDragObject( new KURLDrag( urls, metaData )); + // from KonqDrag (libkonq) + QStoredDrag* cutdrag = new QStoredDrag( "application/x-kde-cutselection" ); + QByteArray a; + QCString s ( cut ? "1" : "0" ); + a.resize( s.length() + 1 ); // trailing zero + memcpy( a.data(), s.data(), s.length() + 1 ); + cutdrag->setEncodedData( a ); + drag->addDragObject( cutdrag ); + return drag; +} + +bool HistoryURLItem::operator==( const HistoryItem& rhs) const +{ + if ( const HistoryURLItem* casted_rhs = dynamic_cast<const HistoryURLItem*>( &rhs ) ) { + return casted_rhs->urls == urls + && casted_rhs->metaData.count() == metaData.count() + && qEqual( casted_rhs->metaData.begin(), casted_rhs->metaData.end(), metaData.begin()) + && casted_rhs->cut == cut; + } + return false; +} diff --git a/klipper/historyurlitem.h b/klipper/historyurlitem.h new file mode 100644 index 000000000..5619b6037 --- /dev/null +++ b/klipper/historyurlitem.h @@ -0,0 +1,49 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _HISTORYURLITEM_H_ +#define _HISTORYURLITEM_H_ + +#include "historyitem.h" + +#include <qmap.h> +#include <kurl.h> + +/** + * An URL entry in the clipboard history. + */ +class HistoryURLItem : public HistoryItem +{ +public: + HistoryURLItem( const KURL::List &urls, QMap<QString, QString> metaData, bool cut ); + virtual QString text() const; + virtual bool operator==( const HistoryItem& rhs) const; + virtual QMimeSource* mimeSource() const; + + /** + * Write object on datastream + */ + virtual void write( QDataStream& stream ) const; +private: + KURL::List urls; + QMap<QString, QString> metaData; + bool cut; +}; + +#endif diff --git a/klipper/klipper-1-2.pl b/klipper/klipper-1-2.pl new file mode 100644 index 000000000..b9cd337fb --- /dev/null +++ b/klipper/klipper-1-2.pl @@ -0,0 +1,63 @@ +#!/usr/bin/perl + +my ($section, %data); + +#read in all the data, split it up into hashes. Thanks again to malte for much input +while (<>) { + if (/\[(.*)\]/) { + $sections{$section} = {%data} if $section; + $section = $1; + undef %data; + next; + } + $data{$1} = $2 if /^([^=]*)=(.*)$/; +} + +$sections{$section} = {%data} if $section; + +# not used up to now +# $version = $sections{'General'}->{'Version'}; + +# if "Action description" is not available, we have a new, fresh configuration +# without any need for conversion. +if ( ! $sections{'Action_0'}->{'Action description'} ) { + exit; +} + +$numActions = $sections{'General'}->{'Number of Actions'}; +for my $i (0..($numActions - 1)) { + my $actionGroup = "Action_$i"; + my $numCommands = $sections{$actionGroup}->{'Number of commands'}; + + print "[$actionGroup]\n"; + # rename some keys + print "Description=$sections{$actionGroup}->{'Action description'}\n"; + print "Regexp=$sections{$actionGroup}->{'Action regexp'}\n"; + print "Number of commands=$numCommands\n"; + + # move the command entries from "Action_x" to "Action_x/Command_y" + for my $k (0..($numCommands - 1)) { + my $command = "Command_$k"; + my $commandGroup = "$actionGroup/$command"; + print "\n[$commandGroup]\n"; + my $value = $sections{$actionGroup}->{"$command: commandline"}; + print "Commandline=$value\n"; + $value = $sections{$actionGroup}->{"$command: description"}; + print "Description=$value\n"; + $value = $sections{$actionGroup}->{"$command: enabled"}; + print "Enabled=$value\n"; + } + print "\n"; +} + +©Section( "General" ); +©Section( "Global Keys" ); + +sub copySection() +{ + my ($group) = @_; + print "\n[$group]\n"; + while (($key,$value) = each(%{$sections{$group}})) { + print "$key=$value\n"; + } +} diff --git a/klipper/klipper-kde31.sh b/klipper/klipper-kde31.sh new file mode 100644 index 000000000..755915fdc --- /dev/null +++ b/klipper/klipper-kde31.sh @@ -0,0 +1,8 @@ +#!/bin/sh +filename=`kde-config --localprefix`share/autostart/klipper.desktop +if grep 'Hidden=true' "$filename" > /dev/null 2> /dev/null; then + echo AutoStart=false +else + echo AutoStart=true +fi +rm -f "$filename"
\ No newline at end of file diff --git a/klipper/klipper.desktop b/klipper/klipper.desktop new file mode 100644 index 000000000..4af6e5c9c --- /dev/null +++ b/klipper/klipper.desktop @@ -0,0 +1,165 @@ +[Desktop Entry] +Name=Klipper +Name[bn]=ক্লিপার +Name[eo]=Poŝilo +Name[hi]=क्लिपर +Name[kk]=Алмасу буфері +Name[lo]=ຄິບເປີ້ - K +Name[lv]=Klippers +Name[mk]=Клипер +Name[ne]=क्लिपर +Name[pa]=ਕੇਲਿਪਰ +Name[ru]=Монитор буфера обмена +Name[ta]=க்ளிப்பர் +Name[te]=క్లిప్పర్ +Name[th]=คลิปเปอร์ +Name[ven]=Kilipara +Name[vi]=Bảng nháp +Name[zh_TW]=剪貼薄(Klipper) +GenericName=Clipboard Tool +GenericName[af]=Klipbord Program +GenericName[ar]=أداة الحافظة +GenericName[be]=Буфер абмену +GenericName[bg]=Системен буфер +GenericName[bn]=ক্লিপবোর্ড টুল +GenericName[br]=Ostilh ar golver +GenericName[bs]=Spremnik +GenericName[ca]=Eina de portapapers +GenericName[cs]=Program pro práci se schránkou +GenericName[csb]=Nôrzãdze tacnika +GenericName[cy]=Offeryn Gludfwrdd +GenericName[da]=Klippebordsværktøj +GenericName[de]=Verwaltung der Zwischenablage +GenericName[el]=Εργαλείο πρόχειρου +GenericName[eo]=Ilo por la poŝo +GenericName[es]=Portapapeles +GenericName[et]=Lõikepuhvri haldamine +GenericName[eu]=Arbela +GenericName[fa]=ابزار تختهیادداشت +GenericName[fi]=Leikepöytätyökalu +GenericName[fr]=Presse-papiers +GenericName[fy]=Klamboerdbehear +GenericName[ga]=Uirlis Ghearrthaisce +GenericName[gl]=Ferramenta de Portarretallos +GenericName[he]=כלי לוח עריכה +GenericName[hi]=क्लिपबोर्ड औज़ार +GenericName[hr]=Alata međuspremnika +GenericName[hu]=Vágólapkezelő +GenericName[is]=Klippispjaldstól +GenericName[it]=Strumento per gli appunti +GenericName[ja]=クリップボードツール +GenericName[ka]=გაცვლის ბუფერის უტილიტა +GenericName[kk]=Алмасу буфер құралы +GenericName[km]=ឧបករណ៍ក្ដារតម្បៀតខ្ទាស់ +GenericName[ko]=클립보드 도구 +GenericName[lo]=ເຄື່ອງມືຄິບບອດ +GenericName[lt]=Laikinos talpyklės priemonė +GenericName[lv]=Starpliktuves Rīks +GenericName[mk]=Алатка за табла со исечоци +GenericName[mn]=Клипборд хэрэгсэл +GenericName[ms]=Alat Klipbod +GenericName[mt]=Għodda tal-klipbord +GenericName[nb]=Utklippstavle +GenericName[nds]=Warktüüch för de Twischenaflaag +GenericName[ne]=क्लिपबोर्ड उपकरण +GenericName[nl]=Klembordbeheer +GenericName[nn]=Utklippstavle +GenericName[nso]=Sebereka sa Clipboard +GenericName[pa]=ਕਲਿੱਪਬੋਰਡ ਸੰਦ +GenericName[pl]=Narzędzie schowka +GenericName[pt]=Ferramenta da Área de Transferência +GenericName[pt_BR]=Ferramenta de Área de Transferência +GenericName[ro]=Utilitar de clipboard +GenericName[ru]=Утилита для буфера обмена +GenericName[rw]=Igikoresho Ububikokoporora +GenericName[se]=Čuohpusgirjereaidu +GenericName[sk]=Nástroj pre schránku +GenericName[sl]=Orodje za odložišče +GenericName[sr]=Алат за клипборд +GenericName[sr@Latn]=Alat za klipbord +GenericName[sv]=Klippbordsverktyg +GenericName[ta]=தற்காலிக கருவி +GenericName[te]=క్లిప్ బోర్డ్ పనిముట్టు +GenericName[tg]=Обзори хотираи муваққат +GenericName[th]=เครื่องมือคลิปบอร์ด +GenericName[tr]=Pano Aracı +GenericName[tt]=Kesä Qoralı +GenericName[uk]=Утиліта кишені +GenericName[uz]=Klipbord vositasi +GenericName[uz@cyrillic]=Клипборд воситаси +GenericName[ven]=Tshishumiswa tsha bodo yau nambatedza +GenericName[vi]=Công cụ Bảng nháp +GenericName[wa]=Usteye di tchapea emacralé +GenericName[xh]=Isixhobo Sebhodi eqhoboshayo +GenericName[zh_CN]=剪贴板工具 +GenericName[zh_TW]=剪貼簿工具 +GenericName[zu]=Ithuluzi Lebhodi elibophayo +Exec=klipper +Icon=klipper +Type=Application +DocPath=klipper/index.html +Terminal=false +X-KDE-autostart-after=panel +X-KDE-StartupNotify=false +X-DCOP-ServiceType=Unique +X-KDE-Library=klipper_panelapplet +X-KDE-UniqueApplet=true +X-KDE-autostart-condition=klipperrc:General:AutoStart:true +OnlyShowIn=KDE; +Categories=Qt;KDE;Utility;X-KDE-Utilities-Desktop; +Comment=A cut & paste history utility +Comment[af]='n Knip & plak geskiedenis program +Comment[ar]=أدات الخط الزمني للقص و الإلصاق +Comment[be]=Службовая праграма працы з гісторыяй выразання/устаўкі +Comment[bg]=Инструмент за управление на операциите е по копиране и поставяне +Comment[bs]=Alat za historiju isijecanja i umetanja +Comment[ca]=Una utilitat de la historia de tallar i enganxar +Comment[cs]=Nástroj pro historii práce se schránkou +Comment[csb]=Nôrzãdze trzëmôjące historëjã tacnika +Comment[da]=Et værktøj med historik til at klippe ud og indsætte +Comment[de]=Programm zur Verwaltung der Zwischenablage +Comment[el]=Ένα εργαλείο ιστορικού αποκοπής & επικόλλησης +Comment[eo]=Eltonda kaj alglua historia ilo +Comment[es]=Una utilidad de cortar y pegar historial +Comment[et]=Lõikamiste ja asetamiste ajaloo rakendus +Comment[eu]=Ebaki eta itsasi-ren historian ibiltzeko tresna +Comment[fa]=برنامۀ سودمند تاریخچۀ برش و چسباندن +Comment[fi]=Työkalu leikkaamiseen ja liittämiseen +Comment[fr]=Un outil d'historique du copier / coller +Comment[fy]=Klamboerdhistoarje +Comment[gl]=Un historial de cortar & pegar +Comment[he]=שירותית היסטוריית גזירה והדבקה +Comment[hr]=Alat za izrezivanje i preljepljivanje iz povijesti +Comment[hu]=Kezelőprogram a vágólaphoz (naplózással) +Comment[is]=Klipp & lím sögutól +Comment[it]=Un programma che ricorda il copia e incolla +Comment[ja]=カット&ペースト履歴ユーティリティ +Comment[kk]=Қиып алу және орналастыру журналы +Comment[km]=ឧបករណ៍ប្រវត្តិកាត់ & បិទភ្ជាប់ +Comment[ko]=자르고 붙인 기록 도구 +Comment[lt]=„Iškirpti ir padėti“ istorijos pagalbinė programa +Comment[mk]=Алатка за историјат на сечење и вметнување +Comment[nb]=Et verktøy som viser tidligere utklipp +Comment[nds]=Vörgeschicht vun't Knippen un Infögen +Comment[ne]=काट्ने र टाँस्ने इतिहास युटिलिटी +Comment[nl]=Klembordgeschiedenis +Comment[nn]=Eit verktøy som viser eldre utklipp +Comment[pa]=ਕੱਟਣ ਅਤੇ ਚੇਪਣ ਅਤੀਤ ਸਹੂਲਤ +Comment[pl]=Narzędzie przechowujące historię schowka +Comment[pt]=Um utilitário de histórico de cópias e colagens +Comment[pt_BR]=Um utilitário com o histórico de cortar & colar +Comment[ro]=Utilitar cu istoria de tăiere și adăugare +Comment[ru]=История буфера обмена +Comment[sk]=Vystrihnúť a vložiť históriu +Comment[sl]=Orodje z zgodovino za izreži in prilepi +Comment[sr]=Алат за историјат исецања и преношења +Comment[sr@Latn]=Alat za istorijat isecanja i prenošenja +Comment[sv]=Ett verktyg med historik för klipp ut och klistra in +Comment[th]=โปรแกรมอรรถประโยชน์สำหรับดูประวัติการตัดและวาง +Comment[tr]=Kes & yapıştır geçmişine erişim +Comment[uk]=Утиліта історії кишені +Comment[vi]=Công cụ chứa lịch sử các cắt dán +Comment[wa]=Ene usteye po l' istwere des copyî/aclaper +Comment[zh_CN]=剪贴历史工具 +Comment[zh_TW]=剪貼紀錄公用程式 + diff --git a/klipper/klipperbindings.cpp b/klipper/klipperbindings.cpp new file mode 100644 index 000000000..b32c90410 --- /dev/null +++ b/klipper/klipperbindings.cpp @@ -0,0 +1,36 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) by Andrew Stanley-Jones + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef NOSLOTS +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), QString::null, key3, key4, this, SLOT(fnSlot) ) +#else +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), QString::null, key3, key4 ) +#endif +#define WIN KKey::QtWIN + + keys->insert( "Program:klipper", i18n("Clipboard") ); + + DEF( I18N_NOOP("Show Klipper Popup-Menu"), ALT+CTRL+Qt::Key_V, WIN+CTRL+Qt::Key_V, slotPopupMenu() ); + DEF( I18N_NOOP("Manually Invoke Action on Current Clipboard"), ALT+CTRL+Qt::Key_R, WIN+CTRL+Qt::Key_R, slotRepeatAction() ); + DEF( I18N_NOOP("Enable/Disable Clipboard Actions"), ALT+CTRL+Qt::Key_X, WIN+CTRL+Qt::Key_X, toggleURLGrabber() ); + +#undef DEF +#undef WIN diff --git a/klipper/klipperpopup.cpp b/klipper/klipperpopup.cpp new file mode 100644 index 000000000..7dfc0a3fd --- /dev/null +++ b/klipper/klipperpopup.cpp @@ -0,0 +1,304 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + Copyright (C) by Andrew Stanley-Jones + Copyright (C) 2000 by Carsten Pfeiffer <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include <kmessagebox.h> +#include <khelpmenu.h> +#include <kiconloader.h> +#include <klineedit.h> + +#include <klocale.h> +#include <kaction.h> +#include <kglobalsettings.h> +#include <kwin.h> +#include <kapplication.h> +#include <kglobalsettings.h> +#include <kdebug.h> + +#include "klipperpopup.h" +#include "history.h" +#include "toplevel.h" +#include "popupproxy.h" + +namespace { + static const int TOP_HISTORY_ITEM_INDEX = 2; +} + +// #define DEBUG_EVENTS__ + +#ifdef DEBUG_EVENTS__ +kdbgstream& operator<<( kdbgstream& stream, const QKeyEvent& e ) { + stream << "(QKeyEvent(text=" << e.text() << ",key=" << e.key() << ( e.isAccepted()?",accepted":",ignored)" ) << ",count=" << e.count(); + if ( e.state() & Qt::AltButton ) { + stream << ",ALT"; + } + if ( e.state() & Qt::ControlButton ) { + stream << ",CTRL"; + } + if ( e.state() & Qt::MetaButton ) { + stream << ",META"; + } + if ( e.state() & Qt::ShiftButton ) { + stream << ",SHIFT"; + } + if ( e.isAutoRepeat() ) { + stream << ",AUTOREPEAT"; + } + stream << ")"; + + return stream; +} +#endif + +/** + * Exactly the same as KLineEdit, except that ALL key events are swallowed. + * + * We need this to avoid infinite loop when sending events to the search widget + */ +class KLineEditBlackKey : public KLineEdit { +public: + KLineEditBlackKey(const QString& string, QWidget* parent, const char* name ) + : KLineEdit( string, parent, name ) + {} + + KLineEditBlackKey( QWidget* parent, const char* name ) + : KLineEdit( parent, name ) + {} + + ~KLineEditBlackKey() { + } +protected: + virtual void keyPressEvent( QKeyEvent* e ) { + KLineEdit::keyPressEvent( e ); + e->accept(); + + } + +}; + +KlipperPopup::KlipperPopup( History* history, QWidget* parent, const char* name ) + : KPopupMenu( parent, name ), + m_dirty( true ), + QSempty( i18n( "<empty clipboard>" ) ), + QSnomatch( i18n( "<no matches>" ) ), + m_history( history ), + helpmenu( new KHelpMenu( this, KlipperWidget::aboutData(), false ) ), + m_popupProxy( 0 ), + m_filterWidget( 0 ), + m_filterWidgetId( 10 ), + n_history_items( 0 ) +{ + KWin::WindowInfo i = KWin::windowInfo( winId(), NET::WMGeometry ); + QRect g = i.geometry(); + QRect screen = KGlobalSettings::desktopGeometry(g.center()); + int menu_height = ( screen.height() ) * 3/4; + int menu_width = ( screen.width() ) * 1/3; + + m_popupProxy = new PopupProxy( this, "popup_proxy", menu_height, menu_width ); + + connect( this, SIGNAL( aboutToShow() ), SLOT( slotAboutToShow() ) ); +} + +KlipperPopup::~KlipperPopup() { + +} + +void KlipperPopup::slotAboutToShow() { + if ( m_filterWidget ) { + if ( !m_filterWidget->text().isEmpty() ) { + m_dirty = true; + m_filterWidget->clear(); + setItemVisible( m_filterWidgetId, false ); + m_filterWidget->hide(); + } + } + ensureClean(); + +} + +void KlipperPopup::ensureClean() { + // If the history is unchanged since last menu build, the is no reason + // to rebuild it, + if ( m_dirty ) { + rebuild(); + } + +} + +void KlipperPopup::buildFromScratch() { + m_filterWidget = new KLineEditBlackKey( this, "Klipper filter widget" ); + insertTitle( SmallIcon( "klipper" ), i18n("Klipper - Clipboard Tool")); + m_filterWidgetId = insertItem( m_filterWidget, m_filterWidgetId, 1 ); + m_filterWidget->setFocusPolicy( QWidget::NoFocus ); + setItemVisible( m_filterWidgetId, false ); + m_filterWidget->hide(); + QString lastGroup; + + // Bit of a hack here. It would be better of KHelpMenu could be an action. + // Insert Help-menu at the butttom of the "default" group. + QString group; + QString defaultGroup( "default" ); + for ( KAction* action = m_actions.first(); action; action = m_actions.next() ) { + group = action->group(); + if ( group != lastGroup ) { + if ( lastGroup == defaultGroup ) { + insertItem( SmallIconSet("help"), KStdGuiItem::help().text(), helpmenu->menu() ); + } + insertSeparator(); + } + lastGroup = group; + action->plug( this, -1 ); + } + + if ( KGlobalSettings::insertTearOffHandle() ) { + insertTearOffHandle(); + } + +} + +void KlipperPopup::rebuild( const QString& filter ) { + + bool from_scratch = ( count() == 0 ); + if ( from_scratch ) { + buildFromScratch(); + } else { + for ( int i=0; i<n_history_items; i++ ) { + removeItemAt( TOP_HISTORY_ITEM_INDEX ); + } + } + + QRegExp filterexp( filter ); + if ( filterexp.isValid() ) { + m_filterWidget->setPaletteForegroundColor( paletteForegroundColor() ); + } else { + m_filterWidget->setPaletteForegroundColor( QColor( "red" ) ); + } + n_history_items = m_popupProxy->buildParent( TOP_HISTORY_ITEM_INDEX, filterexp ); + + if ( n_history_items == 0 ) { + if ( m_history->empty() ) { + insertItem( QSempty, -1, TOP_HISTORY_ITEM_INDEX ); + } else { + insertItem( QSnomatch, -1, TOP_HISTORY_ITEM_INDEX ); + } + n_history_items++; + } else { + if ( history()->topIsUserSelected() ) { + int id = idAt( TOP_HISTORY_ITEM_INDEX ); + if ( id != -1 ) { + setItemChecked( id,true ); + } + } + } + + + m_dirty = false; + +} + +void KlipperPopup::plugAction( KAction* action ) { + m_actions.append( action ); +} + + + + +/* virtual */ +void KlipperPopup::keyPressEvent( QKeyEvent* e ) { + // If alt-something is pressed, select a shortcut + // from the menu. Do this by sending a keyPress + // without the alt-modifier to the superobject. + if ( e->state() & Qt::AltButton ) { + QKeyEvent ke( QEvent::KeyPress, + e->key(), + e->ascii(), + e->state() ^ Qt::AltButton, + e->text(), + e->isAutoRepeat(), + e->count() ); + KPopupMenu::keyPressEvent( &ke ); +#ifdef DEBUG_EVENTS__ + kdDebug() << "Passing this event to ancestor (KPopupMenu): " << e "->" << ke << endl; +#endif + if ( ke.isAccepted() ) { + e->accept(); + return; + } else { + e->ignore(); + } + } + + // Otherwise, send most events to the search + // widget, except a few used for navigation: + // These go to the superobject. + switch( e->key() ) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Right: + case Qt::Key_Left: + case Qt::Key_Tab: + case Qt::Key_Backtab: + case Qt::Key_Escape: + case Qt::Key_Return: + case Qt::Key_Enter: + { +#ifdef DEBUG_EVENTS__ + kdDebug() << "Passing this event to ancestor (KPopupMenu): " << e << endl; +#endif + KPopupMenu::keyPressEvent( e ); + if ( isItemActive( m_filterWidgetId ) ) { + setActiveItem( TOP_HISTORY_ITEM_INDEX ); + } + break; + } + default: + { +#ifdef DEBUG_EVENTS__ + kdDebug() << "Passing this event down to child (KLineEdit): " << e << endl; +#endif + QString lastString = m_filterWidget->text(); + QApplication::sendEvent( m_filterWidget, e ); + if ( m_filterWidget->text().isEmpty() ) { + if ( isItemVisible( m_filterWidgetId ) ) + { + setItemVisible( m_filterWidgetId, false ); + m_filterWidget->hide(); + } + } + else if ( !isItemVisible( m_filterWidgetId ) ) + { + setItemVisible( m_filterWidgetId, true ); + m_filterWidget->show(); + + } + if ( m_filterWidget->text() != lastString) { + slotHistoryChanged(); + rebuild( m_filterWidget->text() ); + } + break; + + } //default: + } //case + +} + + + +#include "klipperpopup.moc" diff --git a/klipper/klipperpopup.h b/klipper/klipperpopup.h new file mode 100644 index 000000000..f77b87954 --- /dev/null +++ b/klipper/klipperpopup.h @@ -0,0 +1,127 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + Copytight (C) by Andrew Stanley-Jones + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _KLIPPERPOPUP_H_ +#define _KLIPPERPOPUP_H_ + +#include <kpopupmenu.h> +#include <qptrlist.h> +#include <qstring.h> + +class History; +class KlipperWidget; +class KHelpMenu; +class KAction; +class PopupProxy; +class KLineEdit; + +/** + * Default view of clipboard history. + * + */ +class KlipperPopup : public KPopupMenu +{ + Q_OBJECT + +public: + KlipperPopup( History* history, QWidget* parent=0, const char* name=0 ); + ~KlipperPopup(); + void plugAction( KAction* action ); + + /** + * Normally, the popupmenu is only rebuilt just before showing. + * If you need the pixel-size or similar of the this menu, call + * this beforehand. + */ + void ensureClean(); + + History* history() { return m_history; } + const History* history() const { return m_history; } + +public slots: + void slotHistoryChanged() { m_dirty = true; } + void slotAboutToShow(); + +private: + void rebuild( const QString& filter = QString::null ); + void buildFromScratch(); + + void insertSearchFilter(); + void removeSearchFilter(); + +protected: + virtual void keyPressEvent( QKeyEvent* e ); + +private: + bool m_dirty : 1; // true if menu contents needs to be rebuild. + + /** + * Contains the string shown if the menu is empty. + */ + QString QSempty; + + /** + * Contains the string shown if the search string has no + * matches and the menu is not empty. + */ + QString QSnomatch; + + /** + * The "document" (clipboard history) + */ + History* m_history; + + /** + * The help menu + */ + KHelpMenu* helpmenu; + + /** + * (unowned) actions to plug into the primary popup menu + */ + QPtrList<KAction> m_actions; + + /** + * Proxy helper object used to track history items + */ + PopupProxy* m_popupProxy; + + /** + * search filter widget + */ + KLineEdit* m_filterWidget; + + /** + * id of search widget, for convenience + */ + int m_filterWidgetId; + + /** + * The current number of history items in the clipboard + */ + int n_history_items; + +signals: + void clearHistory(); + void configure(); + void quit(); + +}; + +#endif diff --git a/klipper/klipperrc.desktop b/klipper/klipperrc.desktop new file mode 100644 index 000000000..ba04cf594 --- /dev/null +++ b/klipper/klipperrc.desktop @@ -0,0 +1,2066 @@ +[Action_0] +Description=Jpeg-Image +Description[af]=Jpeg Beeld +Description[ar]=صورة-Jpeg +Description[az]=Jpeg-Rəsmi +Description[be]=Відарыс JPEG +Description[bg]=Изображение Jpeg +Description[bn]=জেপেগ চিত্র +Description[br]=Skeudenn Jpeg +Description[bs]=Jpeg-slika +Description[ca]=Imatge jpeg +Description[cs]=Obrázek JPEG +Description[csb]=Òbrôz Jpeg +Description[cy]=Delwedd Jpeg +Description[da]=JPEG-billede +Description[de]=JPEG-Image +Description[el]=Εικόνα-Jpeg +Description[eo]=JPEG-grafiko +Description[es]=Imagen Jpeg +Description[et]=Jpeg pildifail +Description[eu]=Jpeg irudia +Description[fa]=تصویر Jpeg +Description[fi]=Jpeg-kuva +Description[fo]=JPEG-mynd +Description[fr]=Image JPEG +Description[fy]=Jpeg-ôfbylding +Description[ga]=Íomhá JPEG +Description[gl]=Imaxe Jpeg +Description[he]=תמונת JPEG +Description[hi]=जेपीईजी-छवि +Description[hr]=Jpeg slika +Description[hu]=JPEG kép +Description[is]=Jpeg mynd +Description[it]=Immagine JPEG +Description[ja]=Jpeg 画像 +Description[ka]=jpeg გამოსახულება +Description[kk]=Jpeg-кескіні +Description[km]=រូបភាព Jpeg +Description[ko]=Jpeg-그림 +Description[lo]=ແຟ້ມພາບ Jpeg +Description[lt]=Jpeg paveiksliukas +Description[lv]=Jpeg Attēls +Description[mk]=Jpeg-слика +Description[mn]=JPEG-Зураг +Description[ms]=Imej Jpeg +Description[mt]=Stampa JPEG +Description[nb]=Jpeg-bilde +Description[nds]=JPEG-Bild +Description[ne]=Jpeg-छवि +Description[nl]=Jpeg-afbeelding +Description[nn]=Jpeg-bilete +Description[nso]=Ponagalo ya Jpeg +Description[pa]=Jpeg-ਚਿੱਤਰ +Description[pl]=Obrazek Jpeg +Description[pt]=Imagem-JPEG +Description[pt_BR]=Imagem JPEG +Description[ro]=Imagine JPEG +Description[ru]=Рисунок JPEG +Description[rw]=Ishusho-jpeg +Description[se]=Jpeg-govva +Description[sk]=Obrázok Jpeg +Description[sl]=Slika JPEG +Description[sr]=Jpeg слика +Description[sr@Latn]=Jpeg slika +Description[sv]=Jpeg-bild +Description[ta]=Jpeg-பிம்பம் +Description[te]=జెపిఈజి చిత్రం +Description[tg]=Тасвири Jpeg +Description[th]=แฟ้มภาพ Jpeg +Description[tr]=Jpeg Resmi +Description[tt]=Jpeg-Sürät +Description[uk]=Зображення Jpeg +Description[uz]=Jpeg-rasm +Description[uz@cyrillic]=Jpeg-расм +Description[ven]=Tshifanyiso tsha jpeg +Description[vi]=Hình Jpeg +Description[wa]=Imådje JPEG +Description[xh]=Jpeg-Umfanekiso +Description[zh_CN]=JPEG 图像 +Description[zh_TW]=JPEG 影像 +Description[zu]=Isithombe se-Jpeg +Number of commands=1 +Regexp=^\\/.+\\.jpg$ + +[Action_0/Command_0] +Commandline=kview %s +Description=Launch K&View +Description[af]=Lanseer K-bekyk +Description[ar]=أ&طلق KView +Description[az]=K&View-u Başlat +Description[be]=Запусціць K&View +Description[bg]=&Стартиране на KView +Description[bn]=কে-ভি&উ চালাও +Description[br]=Lañsañ K&View +Description[bs]=Pokreni K&View +Description[ca]=Engega el K&View +Description[cs]=Spustit K&View +Description[csb]=Zrëszë &Przezérnik òbrôzów +Description[cy]=Cychwyn K&View +Description[da]=Start K&View +Description[de]=K&View starten +Description[el]=Εκκίνηση K&View +Description[eo]=Lanĉi &rigardilon +Description[es]=Iniciar K&View +Description[et]=K&View käivitamine +Description[eu]=Abiarazi K&View +Description[fa]=راهاندازیK&View +Description[fi]=Käynnistä K&View +Description[fr]=Démarrer K&View +Description[fy]=K&View útfiere +Description[ga]=Tosaigh K&View +Description[gl]=Executar K&View +Description[he]=הפעל את &KView +Description[hi]=के-व्यू चलाएँ (&V) +Description[hr]=Pokreni K&View +Description[hu]=A K&View indítása +Description[is]=Ræsa K&View +Description[it]=Avvia K&View +Description[ja]=KView を起動(&V) +Description[ka]=K&View გაშვება +Description[kk]=K&View жегу +Description[km]=បើក K&View +Description[lo]=ຮງກທຳງານ K&View +Description[lt]=Paleisti K&View +Description[lv]=Palaist K&Skatīt +Description[mk]=Стартувај K&View +Description[mn]=K&View эхлүүлэх +Description[ms]=Lancarkan K&View +Description[mt]=Ħaddem K&View +Description[nb]=Start K&View +Description[nds]=K&View starten +Description[ne]=के दृश्य सुरुआत गर्नुहोस् +Description[nl]=K&View opstarten +Description[nn]=Start K&View +Description[nso]=Ngwadisaleswa K&Pono +Description[pa]=ਕੇ-ਵਿਊ ਜਾਰੀ(&V) +Description[pl]=Uruchom &Przeglądarkę obrazków +Description[pt]=Lançar o K&View +Description[pt_BR]=Lançar K&View +Description[ro]=Execută K&View +Description[ru]=Запустить K&View +Description[rw]=Gutangiza K-Kugaragaza +Description[se]=Álggat K&View +Description[sk]=Spustiť K&View +Description[sl]=Zaženi K&View +Description[sr]=Покрени K&View +Description[sr@Latn]=Pokreni K&View +Description[sv]=Starta K&view +Description[ta]=KViewவை இறக்கு +Description[tg]=Ба кор даровардани K&View +Description[th]=เรียกใช้งาน K&View +Description[tr]=K&View Çalıştır +Description[tt]=K&View Cibärü +Description[uk]=Запустити K&View +Description[uz]=K&View'ni ishga tushirish +Description[uz@cyrillic]=K&View'ни ишга тушириш +Description[ven]=Thoma K&Vhona +Description[vi]=Chạy K&View +Description[wa]=Enonder K&View +Description[zh_CN]=调用 K&View +Description[zh_TW]=啟動 K&View +Description[zu]=Qalisa i-K&View +Enabled=true + +[Action_1] +Description=Web-URL +Description[ar]=عنوان مورد نظامي على الشبكة +Description[az]=Veb-URL +Description[be]=Спасылка Сеціва +Description[bg]=Уеб адрес +Description[bn]=ওয়েব ইউ-আর-এল +Description[br]=URL-gwiad +Description[ca]=URL web +Description[cs]=Webové URL +Description[csb]=Adresa WWW +Description[cy]=URL Gw? +Description[da]=Net-URL +Description[el]=URL-Ιστού +Description[eo]=TTT-URL +Description[es]=URL Web +Description[et]=Veebi-URL +Description[eu]=Web URLa +Description[fa]=نشانی وب وب +Description[fr]=URL web +Description[fy]=Web-URL-adres +Description[he]=כתובת אינטרנט +Description[hi]=वेब-यूआरएल +Description[hu]=Webes URL +Description[is]=Vefslóð +Description[it]=URL Web +Description[ja]=ウェブ URL +Description[ka]=ვებ ბმული +Description[kk]=URL сілтемесі +Description[km]=URL បណ្ដាញ +Description[lo]=ຕຳແຫນ່ງ URL ຂອງເວ໊ບ +Description[mk]=Веб-URL +Description[mn]=Вэб-URL +Description[ms]=URL Laman +Description[mt]=URL fuq il-web +Description[nb]=Internettadresse +Description[nds]=Nett-URL +Description[ne]=वेब-यूआरएल +Description[nl]=Web-URL-adres +Description[nn]=Internettadresse +Description[nso]=URL ya Web +Description[pa]=ਵੈੱਬ-URL +Description[pl]=Adres WWW +Description[pt]=URL da Web +Description[pt_BR]=URL +Description[ro]=URL web +Description[ru]=Ссылка на веб-страницу +Description[rw]=URL-Urubugamakuru +Description[se]=Fierpmádatčujuhus +Description[sl]=Spletni URL +Description[sr]=Веб URL +Description[sr@Latn]=Veb URL +Description[sv]=Webbadress +Description[ta]=வலை-வலைப்பின்னல் +Description[te]=వెబ్ యూ ఆర్ ఎల్ +Description[tg]=Нишони Вэб +Description[th]=ตำแหน่ง URL ของเว็บ +Description[uk]=URL Тенет +Description[uz]=Veb-URL +Description[uz@cyrillic]=Веб-URL +Description[ven]=Webe ya URL +Description[vi]=URL trên mạng +Description[wa]=Hårdêye waibe +Description[zh_CN]=Web URL +Description[zh_TW]=網頁 URL +Description[zu]=I-URL ye-Web +Number of commands=5 +Regexp=^https?://. + +[Action_1/Command_0] +Commandline=kfmclient exec %s +Description=Open with &Konqueror +Description[af]=Maak oop met Konqueror +Description[ar]=إفتح بــ &Konqueror +Description[az]=&Konqueror ilə Aç +Description[be]=Адкрыць у &Konqueror +Description[bg]=&Отваряне с Konqueror +Description[bn]=কনকরার দিয়ে খোলো (&ক) +Description[br]=Digeriñ gant &Konqueror +Description[bs]=Otvori u &Konqueroru +Description[ca]=Obre amb el &Konqueror +Description[cs]=Otevřít pomocí &Konqueroru +Description[csb]=Òtemknij w &Konquerorze +Description[cy]=Agor gyda &Konqueror +Description[da]=Åbn med &Konqueror +Description[de]=Mit &Konqueror öffnen +Description[el]=Άνοιγμα με το &Konqueror +Description[eo]=Malfermi per &Konkeranto +Description[es]=Abrir con &Konqueror +Description[et]=&Konqueroris avamine +Description[eu]= Ireki &Konquerorrekin +Description[fa]=باز کردن با&Konqueror +Description[fi]=Avaa &Konquerorissa +Description[fo]=Opna við &Konqueror +Description[fr]=Ouvrir dans &Konqueror +Description[fy]=Iepenje mei &Konqueror +Description[ga]=Oscail le &Konqueror +Description[gl]=Abrir con &Konqueror +Description[he]=פתח באמצעות &Konqueror +Description[hi]=कॉन्करर के साथ खोलें (&K) +Description[hr]=Otvori pomoću &Konquerora +Description[hu]=Megnyitás a &Konquerorral +Description[is]=Opna með &Konqueror +Description[it]=Apri con &Konqueror +Description[ja]=Konqueror で開く(&K) +Description[ka]=&Konqueror-ში გახსნა +Description[kk]=&Konqueror-де ашу +Description[km]=បើកជាមួយ &Konqueror +Description[ko]=Konqueror로 열기(&K) +Description[lo]=ເປີດດ້ວຍຄອນຄິວເຣ &Konquerorີ +Description[lt]=Atverti su &Konqueror +Description[lv]=Atvērt ar Ie&karotāju +Description[mk]=Отвори со &Konqueror +Description[mn]=&Конкюророор нээх +Description[ms]=Buka dengan &Konqueror +Description[mt]=Iftaħ b' &Konqueror +Description[nb]=Åpne med &Konqueror +Description[nds]=Mit &Konqueror opmaken +Description[ne]=कन्क्वेररसँग खोल्नुहोस् +Description[nl]=Openen met &Konqueror +Description[nn]=Opna med &Konqueror +Description[nso]=Bula ka &Konqueror +Description[pa]=ਕੋਨਕਿਉਰੋਰ ਨਾਲ ਖੋਲੋ(&K) +Description[pl]=Otwórz w &Konquerorze +Description[pt]=Abrir com o &Konqueror +Description[pt_BR]=Abrir com o &Konqueror +Description[ro]=Deschide cu &Konqueror +Description[ru]=Открыть в &Konqueror +Description[rw]=Gufungura ukoresheje Konqueror +Description[se]=Raba &Konquerorain +Description[sk]=Otvoriť pomocou &Konqueror +Description[sl]=Odpri s &Konquerorjem +Description[sr]=Отвори помоћу &Konqueror-а +Description[sr@Latn]=Otvori pomoću &Konqueror-a +Description[sv]=Öppna med &Konqueror +Description[ta]=கான்கொரர் உடன் திற +Description[te]=(&K) కాంకెరర్ తొ తెరువు +Description[tg]=Бо &Konqueror боз кунед +Description[th]=เปิดด้วย&คอนเควอร์เรอร์ +Description[tr]=&Konqueror ile Aç +Description[tt]=&Konqueror belän Aç +Description[uk]=Відкрити з &Konqueror +Description[uz]=Konqueror &bilan ochish +Description[uz@cyrillic]=Konqueror &билан очиш +Description[ven]=Vula nga &Konqueror +Description[vi]=Mở bằng &Konqueror +Description[wa]=Drovi avou &Konqueror +Description[xh]=Vula nge &Konqueror +Description[zh_CN]=用 &Konqueror 打开 +Description[zh_TW]=使用 &Konqueror 開啟 +Description[zu]=Vula nge-&Konqueror +Enabled=true + +[Action_1/Command_1] +Commandline=ps x |grep -q '[m]ozilla' && mozilla -remote openURL\(%s, new-window\) || mozilla %s +Icon=mozilla +Description=Open with &Mozilla +Description[af]=Maak oop met Mozilla +Description[ar]=إفتح بِــ &Mozilla +Description[az]=&Mozilla ilə Aç +Description[be]=Адкрыць у &Mozilla +Description[bg]=От&варяне с Mozilla +Description[bn]=মোজিলা দিয়ে খোলো (&ম) +Description[br]=Digeriñ gant &Mozilla +Description[bs]=Otvori u &Mozilla +Description[ca]=Obre amb el &Mozilla +Description[cs]=Otevřít pomocí &Mozilla +Description[csb]=Òtemknij w &Mozillë +Description[cy]=Agor gyda &Mozilla +Description[da]=Åbn med &Mozilla +Description[de]=Mit &Mozilla öffnen +Description[el]=Άνοιγμα με το &Mozilla +Description[eo]=Malfermi per &Mozilo +Description[es]=Abrir con &Mozilla +Description[et]=&Mozillas avamine +Description[eu]=Ireki &Mozillarekin +Description[fa]=باز کردن با &موزیلا +Description[fi]=Avaa &Mozillalla +Description[fo]=Opna við &Mozilla +Description[fr]=Ouvrir dans &Mozilla +Description[fy]=Iepenje mei &Mozilla +Description[ga]=Oscail le &Mozilla +Description[gl]=Abrir con &Mozilla +Description[he]=פתח באמצעות &Mozilla +Description[hi]=मोज़िला के साथ खोलें (&M) +Description[hr]=Otvori pomoću &Mozille +Description[hu]=Megnyitás a &Mozillával +Description[is]=Opna með &Mozilla +Description[it]=Apri con &Mozilla +Description[ja]=Mozilla で開く(&M) +Description[ka]=&Mozilla-ში გასნა +Description[kk]=&Mozilla-да ашу +Description[km]=បើកជាមួយ Mozilla +Description[ko]=Mozilla로 열기(&M) +Description[lo]=ເປີດດ້ວຍ &Mozilla +Description[lt]=Atverti su &Mozilla +Description[lv]=Atvērt ar &Mozilla +Description[mk]=Отвори со &Mozilla +Description[mn]=&Mозиллагаар нээх +Description[ms]=Buka dengan &Mozilla +Description[mt]=Iftaħ b' &Możilla +Description[nb]=Åpne med &Mozilla +Description[nds]=Mit &Mozilla opmaken +Description[ne]=मोजिलासँग खोल्नुहोस् +Description[nl]=Openen met &Mozilla +Description[nn]=Opna med &Mozilla +Description[nso]=Bula ka &Mozilla +Description[pa]=ਮੌਜੀਲਾ ਨਾਲ ਖੋਲੋ(&M) +Description[pl]=Otwórz w &Mozilli +Description[pt]=Abrir com o &Mozilla +Description[pt_BR]=Abrir com o &Mozilla +Description[ro]=Deschide cu &Mozilla +Description[ru]=Открыть в &Mozilla +Description[rw]=Gufungura ukoresheje Mozilla +Description[se]=Raba &Mozillain +Description[sk]=Otvoriť pomocou &Mozilla +Description[sl]=Odpri z &Mozillo +Description[sr]=Отвори помоћу &Mozilla-е +Description[sr@Latn]=Otvori pomoću &Mozilla-e +Description[sv]=Öppna med &Mozilla +Description[ta]=மொசில்லாவுடன் திற +Description[te]=(&M) మొజిల్లా తొ తెరువు +Description[tg]=Бо &Mozilla боз кунед +Description[th]=เปิดด้วย&มอซซิลลา +Description[tr]=&Mozilla ile Aç +Description[tt]=&Mozilla belän Aç +Description[uk]=Відкрити з &Mozilla +Description[uz]=Mozilla bilan &ochish +Description[uz@cyrillic]=Mozilla билан &очиш +Description[ven]=Vula nga &Mozilla +Description[vi]=Mở bằng &Mozilla +Description[wa]=Drovi avou &Mozilla +Description[xh]=Vula nge &Mozilla +Description[zh_CN]=用 &Mozilla 打开 +Description[zh_TW]=使用 &Mozilla 開啟 +Description[zu]=Vula nge-&Mozilla +Enabled=true + +[Action_1/Command_2] +Commandline=kmail --body %s +Description=Send &URL +Description[af]=Stuur URL +Description[ar]=ارسال &رابط +Description[az]=&URL Göndər +Description[be]=Даслаць &URL +Description[bg]=&Изпращане на адрес +Description[bn]=ই&উ-আর-এল পাঠাও +Description[br]=Kas an &URL +Description[bs]=Pošalji &URL +Description[ca]=Envia l'&URL +Description[cs]=Poslat &URL +Description[csb]=Wëslij adresã (&URL) +Description[cy]=Anfon &URL +Description[de]=&URL senden +Description[el]=Αποστολή &URL +Description[eo]=Sendi &URLon +Description[es]=Enviar &URL +Description[et]=&URL-i saatmine +Description[eu]=Bidali &URLa +Description[fa]=ارسال &نشانی وب +Description[fi]=Lähetä &URL +Description[fr]=Envoyer l'&URL +Description[fy]=&URL-adres stjoere +Description[ga]=Seol &URL +Description[gl]=Enviar &URL +Description[he]=שלח &כתובת +Description[hi]= यूआरएल भेजें (&U) +Description[hr]=Pošalji &URL +Description[hu]=Az &URL elküldése +Description[is]=Senda &slóð +Description[it]=Invia l'&URL +Description[ja]=URL を送信(&U) +Description[ka]=&URL-ს გაგზავნა +Description[kk]=&URL-ды жіберу +Description[km]=ផ្ញើ &URL +Description[lo]=ສັ່ງຕຳແຫນ່ງ &URL +Description[lt]=Siųsti &URL +Description[lv]=Sūtīt &URL +Description[mk]=Испрати &URL +Description[mn]=&URL илгээх +Description[ms]=Hantar &URL +Description[mt]=Ibgħat &URL +Description[nb]=Send &Nettadressen +Description[nds]=&URL schicken +Description[ne]=यूआरएल पठाउनुहोस् +Description[nl]=&URL-adres verzenden +Description[nso]=Romela &URL +Description[pa]=&URL ਭੇਜੋ +Description[pl]=Wyślij adres (&URL) +Description[pt]=Enviar o &URL +Description[pt_BR]=Enviar &URL +Description[ro]=Trimite &URL-ul +Description[ru]=Отправить &URL +Description[rw]=Kohereza URL +Description[se]=Sádde &URL:a +Description[sk]=Poslať &URL +Description[sl]=Pošlji &URL +Description[sr]=Пошаљи &URL +Description[sr@Latn]=Pošalji &URL +Description[sv]=Skicka &webbadress +Description[ta]=வலைப்பின்னலை அனுப்பு +Description[te]=(&U) యూ ఆర్ ఎల్ పంపించు +Description[tg]=Фиристодани &URL +Description[th]=ส่งตำแหน่ง &URL +Description[tr]=&URL Gönder +Description[tt]=&URL Künderü +Description[uk]=Відіслати &URL +Description[uz]=URL'ni &joʻnatish +Description[uz@cyrillic]=URL'ни &жўнатиш +Description[ven]=Rumele &URL +Description[vi]=Gửi&URL +Description[wa]=Evoyî &hårdêye +Description[xh]=Thumela i &URL +Description[zh_CN]=发送 &URL +Description[zh_TW]=送出 &URL +Description[zu]=Thumela i-&URL +Enabled=true + +[Action_1/Command_3] +Commandline=ps x |grep -q '[f]irefox' && firefox -remote openURL\(%s, new-window\) || firefox %s +Icon=firefox +Description=Open with &Firefox +Description[af]=Maak oop met Firefox +Description[ar]=إفتح بِــ &Firefox +Description[be]=Адкрыць у &Konqueror +Description[bg]=Отв&аряне с Firefox +Description[bn]=ফায়ার&ফক্স দিয়ে খোলো +Description[br]=Digeriñ gant &Firefox +Description[bs]=Otvori u &Firefoxu +Description[ca]=Obre amb el &Firefox +Description[cs]=Otevřít pomocí &Firefox +Description[csb]=Òtemknij w &Firefokse +Description[da]=Åbn med &Firefox +Description[de]=Mit &Firefox öffnen +Description[el]=Άνοιγμα με το &Firefox +Description[eo]=Malfermi per &Fajrovulpo +Description[es]=Abrir con &Firefox +Description[et]=&Firefoxis avamine +Description[eu]=Ireki &Firefox-ekin +Description[fa]=باز کردن با &فایرفاکس +Description[fi]=Avaa &Firefoxissa +Description[fr]=Ouvrir dans &Firefox +Description[fy]=Iepenje mei &Firefox +Description[ga]=Oscail le &Firefox +Description[gl]=Abrir con &Firefox +Description[he]=פתח באמצעות &Firefox +Description[hr]=Otvori pomoću &Firefoxa +Description[hu]=Megnyitás a &Firefoxszal +Description[is]=Opna með &Firefox +Description[it]=Apri con &Firefox +Description[ja]=Firefox で開く(&F) +Description[ka]=გახსნა &Firefox-ით +Description[kk]=&Firefox шолғышта ашу +Description[km]=បើកជាមួយ &Firefox +Description[ko]=Firefox로 열기(&F) +Description[lt]=Atverti su &Firefox +Description[mk]=Отвори со &Firefox +Description[nb]=Åpne med &Firefox +Description[nds]=Mit &Firefox opmaken +Description[ne]=फायरफक्ससँग खोल्नुहोस् +Description[nl]=Openen met &Firefox +Description[nn]=Opna med &Firefox +Description[pa]=ਫਾਇਰਫਾਕਸ ਨਾਲ ਖੋਲੋ(&F) +Description[pl]=Otwórz w &Firefoksie +Description[pt]=Abrir com o &Firefox +Description[pt_BR]=Abrir com o &Firefox +Description[ro]=Deschide cu &Firefox +Description[ru]=Открыть в &Firefox +Description[se]=Raba &Firefox:ain +Description[sk]=Otvoriť pomocou &Firefox +Description[sl]=Odpri s &Firefox +Description[sr]=Отвори помоћу &Firefox-а +Description[sr@Latn]=Otvori pomoću &Firefox-a +Description[sv]=Öppna med &Firefox +Description[te]=(&F) మొజిల్లా తొ తెరువు +Description[tg]=Кушодан бо &Firefox +Description[th]=เปิดด้วยไ&ฟร์ฟอกซ์ +Description[tr]=&Firefox ile Aç +Description[uk]=Відкрити з &Firefox +Description[uz]=Firefox b&ilan ochish +Description[uz@cyrillic]=Firefox б&илан очиш +Description[vi]=Mở bằng &Firefox +Description[wa]=Drovi avou &Firefox +Description[zh_CN]=用 &Firefox 打开 +Description[zh_TW]=使用 &Firefox 開啟 +Enabled=true + +[Action_1/Command_4] +Commandline=kmail --attach %s +Description=Send &Page +Description[af]=Stuur Bladsy +Description[ar]=ارسال ال&صفحة +Description[az]=&Səhifə Göndər +Description[be]=Даслаць &старонку +Description[bg]=И&зпращане на страница +Description[bn]=পৃষ্ঠা পাঠা&ও +Description[br]=Kas ar &pajenn +Description[bs]=Pošalji &Stranicu +Description[ca]=Envia la &pàgina +Description[cs]=&Poslat stránku +Description[csb]=Wëslij &starnã +Description[cy]=Anfon &Tudalen +Description[da]=Send &side +Description[de]=&Seite senden +Description[el]=Αποστολή &σελίδας +Description[eo]=Sendi &paĝon +Description[es]=Enviar &página +Description[et]=Lehek&ülje saatmine +Description[eu]=Bidali &Orria +Description[fa]=ارسال &صفحه +Description[fi]=Lähetä &Sivu +Description[fr]=Envoyer la &page +Description[fy]=&Side stjoere +Description[ga]=Seol &Leathanach +Description[gl]=Enviar &Páxina +Description[he]=&שלח עמוד +Description[hi]=पृष्ठ भेजें (&P) +Description[hr]=Pošalji &stranicu +Description[hu]=A &weboldal elküldése +Description[is]=Senda &síðu +Description[it]=Invia la &pagina +Description[ja]=ページを送信(&P) +Description[ka]=&გვერდის გაგზავნა +Description[kk]=&Парақты жіберу +Description[km]=ផ្ញើទំព័រ +Description[ko]=페이지 보내기(&P) +Description[lo]=ສົ່ງຫນ້າ &Page +Description[lt]=Siųsti &puslapį +Description[lv]=Sūtīt La&pu +Description[mk]=Испрати &страница +Description[mn]=&Хуудас илгээх +Description[ms]=Hantar &Halaman +Description[mt]=Ibgħat &Paġna +Description[nb]=Send &side +Description[nds]=Sie&t schicken +Description[ne]=पृष्ठ पठाउनुहोस् +Description[nl]=&Pagina verzenden +Description[nn]=Send &side +Description[nso]=Romela &Letlakala +Description[pa]=ਸਫ਼ਾ ਭੇਜੋ(&P) +Description[pl]=Wyślij &stronę +Description[pt]=Enviar a &Página +Description[pt_BR]=Enviar &Página +Description[ro]=Trimite &pagina +Description[ru]=Отправить &страницу +Description[rw]=Kohereza Ipaji +Description[se]=Sádde &siiddu +Description[sk]=Poslať &stránku +Description[sl]=Pošlji &stran +Description[sr]=Пошаљи &страну +Description[sr@Latn]=Pošalji &stranu +Description[sv]=Ski&cka sida +Description[ta]=பக்கத்தை அனுப்பு +Description[te]=(&P) పుట ను పంపించు +Description[tg]=Фиристодани &саҳифа +Description[th]=ส่ง&หน้าเว็บเพจ +Description[tr]=&Sayfayı Gönder +Description[tt]=&Bit Künderü +Description[uk]=Відіслати &сторінку +Description[uz]=&Betni joʻnatish +Description[uz@cyrillic]=&Бетни жўнатиш +Description[ven]=Rumele &Siatari +Description[vi]=Gửi &Trang +Description[wa]=Evoyî &pådje +Description[xh]=Thumela &Iphepha +Description[zh_CN]=发送页面(&P) +Description[zh_TW]=送出 &Page +Description[zu]=Thumela &Ikhasi +Enabled=true + +[Action_2] +Description=Mail-URL +Description[ar]=رابط بريد +Description[az]=Məktub-URL +Description[be]=Спасылка на паштовы адрас +Description[bg]=Изпращане на писмо с адрес +Description[bn]=মেইল ইউ-আর-এল +Description[br]=URL postel +Description[ca]=URL de correu +Description[cs]=Poštovní URL +Description[csb]=Wëslij pòcztą adresã (URL) +Description[cy]=URL Ebost +Description[da]=E-mail-URL +Description[de]=Mail-Adresse (URL) +Description[eo]=Retpoŝto-URL +Description[es]=URL de correo +Description[et]=E-posti aadressi URL +Description[eu]=Posta URLa +Description[fa]=نشانی وب نامه +Description[fi]=Postita-URL +Description[fr]=URL de courrier électronique +Description[fy]=E-post URL-adres +Description[gl]=Enviar URL por correo-e +Description[he]=כתובת דואר +Description[hi]=डाक-यूआरएल +Description[hr]=URL pošalji e-poštom +Description[hu]=Az URL elküldése levélben +Description[is]=Póstsenda slóð +Description[it]=URL di posta elettronica +Description[km]=URL សំបុត្រ +Description[lo]=ຕຳແຫນ່ງ URLຂອງເມລ +Description[lt]=Siųsti URL paštu +Description[lv]=Mailot URL +Description[mk]=Испрати URL по е-пошта +Description[mn]=Захиа-Хаяг (URL) +Description[ms]=URL Mel +Description[mt]=Url ta' imejl +Description[nb]=E-postadresse +Description[nds]=Nettpostadress +Description[ne]=मेल-यूआरएल +Description[nl]=URL-adres e-mailen +Description[nn]=E-postadresse +Description[nso]=Poso ya URL +Description[pa]=ਪੱਤਰ-URL +Description[pl]=Wyślij pocztą adres (URL) +Description[pt]=URL de E-mail +Description[pt_BR]=Enviar a URL por e-mail +Description[ro]=URL email +Description[rw]=URL-Ubutumwa +Description[se]=E-boastačujuhus +Description[sl]=Pošlji URL po pošti +Description[sr]=Пошаљи URL eлектронском поштом +Description[sr@Latn]=Pošalji URL elektronskom poštom +Description[sv]=Webbadress för e-post +Description[ta]=அஞ்சல்-வலைப்பின்னல் +Description[te]=యూ ఆర్ ఎల్ ను మెయిల్ చెయ్యి +Description[tg]=Адреси почтоӣ +Description[th]=ตำแหน่ง URL ของจดหมาย +Description[tr]=URL postala +Description[tt]=Xat-URL +Description[uk]=URL пошти +Description[uz]=Xat-URL +Description[uz@cyrillic]=Хат-URL +Description[ven]=Meili-URL +Description[vi]=Gửi thư-URL +Description[wa]=Hårdêye emile +Description[xh]=Iposi-URL +Description[zh_CN]=邮寄 URL +Description[zh_TW]=郵寄 URL +Description[zu]=I-URl yeposi +Number of commands=2 +Regexp=^mailto:. + +[Action_2/Command_0] +Commandline=kmail --composer `echo %s | sed 's/mailto://g'` +Description=Launch &Kmail +Description[af]=Lanseer Kpos +Description[ar]=تشغيل &Kmail +Description[az]=&Kmail-i Başlat +Description[be]=Запусціць &KMail +Description[bg]=Ст&артиране на Kmail +Description[bn]=কে-মেই&ল চালাও +Description[br]=Lañsañ &Kmail +Description[bs]=Pokreni &Kmail +Description[ca]=Engega el &Kmail +Description[cs]=Spustit &Kmail +Description[csb]=Zrëszë &KMail +Description[cy]=Cychwyn &KMail +Description[da]=Start &KMail +Description[de]=&Kmail starten +Description[el]=Εκκίνηση &Kmail +Description[en_GB]=Launch &KMail +Description[eo]=Lanĉi &Poŝtilon +Description[es]=Iniciar &Kmail +Description[et]=&Kmaili käivitamine +Description[eu]=Abiarazi &Kmail +Description[fa]=راهاندازی&Kmail +Description[fi]=Käynistä &Kmail +Description[fr]=Démarrer K&Mail +Description[fy]=&Kmail útfiere +Description[ga]=Tosaigh &Kmail +Description[gl]=Executar &Kmail +Description[he]=הפעל את &KMail +Description[hi]=के-मेल चालू करें (&K) +Description[hr]=Pokreni &Kmail poštu +Description[hu]=A &KMail elindítása +Description[is]=Ræsa &Kmail +Description[it]=Avvia &KMail +Description[ja]=Kmail を起動(&K) +Description[ka]=&Kmail-ის გაშვება +Description[kk]=&Kmail-ды жегу +Description[km]=បើក &Kmail +Description[ko]=KMail 실행(&K) +Description[lo]=ຮງກທຳງນ &Kmail +Description[lt]=Paleisti &Kmail +Description[lv]=Palaist &Kpastu +Description[mk]=Стартувај &Kmail +Description[mn]=&Kmail эхлүүлэх +Description[ms]=Lancar &Kmail +Description[mt]=Ħaddem &KMail +Description[nb]=Start &Kmail +Description[nds]=&KMail starten +Description[ne]=के मेल सुरुआत गर्नुहोस् +Description[nl]=&Kmail opstarten +Description[nn]=Start &KMail +Description[nso]=Ngwadisaleswa &Kposo +Description[pa]=ਕੇ-ਮੇਲ ਜਾਰੀ(&K) +Description[pl]=Uruchom KM&ail +Description[pt]=Lançar o &Kmail +Description[pt_BR]=Abrir &Kmail +Description[ro]=Execută &Kmail +Description[ru]=Запуск &Kmail +Description[rw]=Gutangiza K-ubutumwa +Description[se]=Álggat K&Mail +Description[sk]=Spustiť &Kmail +Description[sl]=Zaženi &Kmail +Description[sr]=Покрени &Kmail +Description[sr@Latn]=Pokreni &Kmail +Description[sv]=Starta &Kmail +Description[ta]=Kஅஞ்சலை இறக்கு +Description[tg]=Ба кор даровардани &Kmail +Description[th]=เรียกทำงานโปรแกรมรับส่งเมล์ &K +Description[tr]=KMail Ç&alıştır +Description[tt]=&Kmail Cibärü +Description[uk]=Запустити &Kmail +Description[uz]=&Kmail'ni ishga tushirish +Description[uz@cyrillic]=&Kmail'ни ишга тушириш +Description[ven]=Thoma &Kmeili +Description[vi]=Khởi động &Kmail +Description[wa]=Enonder &Kmail +Description[zh_CN]=调用 &Kmail +Description[zh_TW]=啟動 &Kmail +Description[zu]=Qalisa i-&Kmail +Enabled=true + +[Action_2/Command_1] +Commandline=konsole -e mutt `echo %s | sed 's/mailto://g'` +Description=Launch &mutt +Description[af]=Lanseer mutt +Description[ar]=تشغيل &Mutt +Description[az]=&mutt-u Başlat +Description[be]=Запусціць &mutt +Description[bg]=Ста&ртиране на mutt +Description[bn]=&mutt চালাও +Description[br]=Lañsañ &mutt +Description[bs]=Pokreni &mutt +Description[ca]=Engega el &mutt +Description[cs]=Spustit &mutt +Description[csb]=Zrëszë &mutt +Description[cy]=Cychwyn &mutt +Description[da]=Start &mutt +Description[de]=&Mutt starten +Description[el]=Εκκίνηση &mutt +Description[en_GB]=Launch &Mutt +Description[eo]=Lanĉi poŝtilon "&Mutt" +Description[es]=Iniciar &mutt +Description[et]=&Mutt'i käivitamine +Description[eu]=Abiarazi &mutt +Description[fa]=راهاندازی &mutt +Description[fi]=Käynistä &Mutt +Description[fr]=Démarrer &Mutt +Description[fy]=&Mutt útfiere +Description[ga]=Tosaigh &mutt +Description[gl]=Executar &mutt +Description[he]=הפעל את &mutt +Description[hi]=मट चालू करें (&m) +Description[hr]=Pokreni &mutt +Description[hu]=A &Mutt elindítása +Description[is]=Ræsa &mutt +Description[it]=Avvia &mutt +Description[ja]=mutt を起動(&M) +Description[ka]=&mutt-ის გაშვება +Description[kk]=&mutt-ті жегу +Description[km]=បើក &mutt +Description[ko]=mutt 실행(&M) +Description[lo]=ຮງກທຳງານ &mutt +Description[lt]=Paleisti &mutt +Description[lv]=Palaist &mutt +Description[mk]=Стартувај &mutt +Description[mn]=&Mutt эхлүүлэх +Description[ms]=Lancarkan &mutt +Description[mt]=Ħaddem &mutt +Description[nb]=Start &mutt +Description[nds]=&mutt starten +Description[ne]=मट सुरुआत गर्नुहोस् +Description[nl]=&Mutt opstarten +Description[nn]=Start &mutt +Description[nso]=Ngwadisaleswa &mutt +Description[pa]=ਮੂਟ ਜਾਰੀ(&m) +Description[pl]=Uruchom &mutt +Description[pt]=Lançar o &mutt +Description[pt_BR]=Abrir &mutt +Description[ro]=Execută &mutt +Description[ru]=Запуск &mutt +Description[rw]=Gutangiza mutt +Description[se]=Álggat &mutt +Description[sk]=Spustiť &mutt +Description[sl]=Zaženi &mutt +Description[sr]=Покрени &mutt +Description[sr@Latn]=Pokreni &mutt +Description[sv]=Starta &Mutt +Description[ta]=muttஐ இறக்கு +Description[tg]=Ба кор даровардани &mutt +Description[th]=เรียกใช้งาน &mutt +Description[tr]=&Mutt Çalıştır +Description[tt]=&mutt Cibärü +Description[uk]=Запустити &mutt +Description[uz]= &Mutt'ni ishga tushirish +Description[uz@cyrillic]= &Mutt'ни ишга тушириш +Description[ven]=Thoma &mutt +Description[vi]=Khởi động &mutt +Description[wa]=Enonder &mutt +Description[zh_CN]=调用 &mutt +Description[zh_TW]=啟動 &mutt +Description[zu]=Qalisa i-&mutt +Enabled=true + +[Action_3] +Description=Text File +Description[af]=Teks Lêer +Description[ar]=ملف نصي +Description[az]=Mətn Faylı +Description[be]=Тэкставы файл +Description[bg]=Текстов файл +Description[bn]=টেক্সট ফাইল +Description[br]=Restr testenn +Description[bs]=Tekst datoteka +Description[ca]=Fitxer de text +Description[cs]=Textový soubor +Description[csb]=Tekstowi lopk +Description[cy]=Ffeil Testun +Description[da]=Tekstfil +Description[de]=Textdatei +Description[el]=Αρχείο κειμένου +Description[eo]=Tekstdosiero +Description[es]=Archivo de texto +Description[et]=Tekstifail +Description[eu]=Testu fitxategia +Description[fa]=پروندۀ متنی +Description[fi]=Tekstitiedosto +Description[fo]=Tekstfíla +Description[fr]=Fichier texte +Description[fy]=Teksttriem +Description[ga]=Téacschomhad +Description[gl]=Ficheiro de Texto +Description[he]=קובץ טקסט +Description[hi]=पाठ फ़ाइल +Description[hr]=Tekstualna datoteka +Description[hu]=Szöveges fájl +Description[is]=Textaskrá +Description[it]=File di testo +Description[ja]=テキストファイル +Description[ka]=ტექსტური ფაილი +Description[kk]=Мәтін файлы +Description[km]=ឯកសារអត្ថបទ +Description[lo]=ແຟ້ມາຂໍ້ຄວາມ +Description[lt]=Tekstinė byla +Description[lv]=Teksta Fails +Description[mk]=Текстуална датотека +Description[mn]=Текст файл +Description[ms]=Fail Teks +Description[mt]=Fajl ta' test +Description[nb]=Tekstfil +Description[nds]=Textdatei +Description[ne]=पाठ फाइल +Description[nl]=Tekstbestand +Description[nn]=Tekstfil +Description[nso]=Faele ya Sengwalwana +Description[pa]=ਪਾਠ ਫਾਇਲਾਂ +Description[pl]=Plik tekstowy +Description[pt]=Ficheiro de Texto +Description[pt_BR]=Arquivo Texto +Description[ro]=Fișier Text +Description[ru]=Текстовый файл +Description[rw]=Idosiye Mwandiko +Description[se]=Teakstafiila +Description[sk]=Textový súbor +Description[sl]=Besedilna datoteka +Description[sr]=Текстуални фајл +Description[sr@Latn]=Tekstualni fajl +Description[sv]=Textfil +Description[ta]=உரை கோப்பு +Description[te]=వచన దస్త్రం +Description[tg]=Файли матнӣ +Description[th]=แฟ้มข้อความ +Description[tr]=Metin Dosyası +Description[tt]=Mäten Bireme +Description[uk]=Текстовий файл +Description[uz]=Matn fayli +Description[uz@cyrillic]=Матн файли +Description[ven]=Manwalwa a faela +Description[vi]=Tập tin Văn bản +Description[wa]=Fitchî tecse +Description[xh]=Ifayile Yombhalo +Description[zh_CN]=文本文件 +Description[zh_TW]=文字檔案 +Description[zu]=Ifayela Lombhalo +Number of commands=2 +Regexp=^\\/.+\\.txt$ + +[Action_3/Command_0] +Commandline=kedit %s +Description=Launch K&Edit +Description[af]=Lanseer Kredigeer +Description[ar]=تشغيل K&Edit +Description[az]=K&Edit-i Başlat +Description[be]=Запусціць K&Edit +Description[bg]=&Стартиране на KEdit +Description[bn]=কে-&এডিট চালাও +Description[br]=Lañsañ K&Edit +Description[bs]=Pokreni K&Edit +Description[ca]=Engega el K&Edit +Description[cs]=Spustit K&Edit +Description[csb]=Zrëszë editorã K&Edit +Description[cy]=Cychwyn K&Edit +Description[da]=Start K&Edit +Description[de]=K&Edit starten +Description[el]=Εκκίνηση K&Edit +Description[eo]=Lanĉi &Kodredaktilon +Description[es]=Iniciar K&Edit +Description[et]=K&Editi käivitamine +Description[eu]=Abiarazi K&Edit +Description[fa]=راهاندازیK&Edit +Description[fi]=Käynistä K&Edit +Description[fr]=Démarrer K&Edit +Description[fy]=Mei K&Edit begjinne +Description[ga]=Tosaigh K&Edit +Description[gl]=Executar K&Edit +Description[he]=הפעל את K&Edit +Description[hi]=के-एडिट चालू करें (&E) +Description[hr]=Pokreni K&Edit uređivač +Description[hu]=A K&Edit elindítása +Description[is]=Ræsa K&Edit +Description[it]=Avvia K&Edit +Description[ja]=KEdit を起動(&E) +Description[ka]=K&Edit-ის გაშვება +Description[kk]=K&Edit-ты жегу +Description[km]=បើក K&Edit +Description[lo]=ຮງກທຳງານ K&Edit +Description[lt]=Paleisti K&Edit +Description[lv]=Palaist K&Edit +Description[mk]=Стартувај K&Edit +Description[mn]=K&Edit эхлүүлэх +Description[ms]=Lancar K&Edit +Description[mt]=Ħaddem K&Edit +Description[nb]=Start K&Edit +Description[nds]=K&Edit starten +Description[ne]=के सम्पादन सुरुआत गर्नुहोस् +Description[nl]=K&Edit opstarten +Description[nn]=Start K&Edit +Description[nso]=Ngwadisaleswa K&Phetoso +Description[pa]=ਕੇ-ਸੰਪਾਦਕ ਸ਼ੁਰੂ(&E) +Description[pl]=Uruchom &Edytor +Description[pt]=Lançar o K&Edit +Description[pt_BR]=Abrir &Kedit +Description[ro]=Execută K&Edit +Description[ru]=Запуск K&Edit +Description[rw]=Gutangiza K-Guhindura +Description[se]=Álggat K&Edit +Description[sk]=Spustiť K&Edit +Description[sl]=Zaženi K&Edit +Description[sr]=Покрени K&Edit +Description[sr@Latn]=Pokreni K&Edit +Description[sv]=Starta K&edit +Description[ta]=K&Editஐ இறக்கு +Description[tg]=Ба кор даровардани K&Edit +Description[th]=เรียกใช้งาน ม&Edit K +Description[tr]=K&edit Çalıştır +Description[tt]=K&Edit Cibärü +Description[uk]=Запустити K&Edit +Description[uz]=K&Edit'ni ishga tushirish +Description[uz@cyrillic]=K&Edit'ни ишга тушириш +Description[ven]=Thoma K&Sengulusa +Description[vi]=Khởi động K&Edit +Description[wa]=Enonder K&Edit +Description[zh_CN]=调用 K&Edit +Description[zh_TW]=啟動 K&Edit +Description[zu]=Qalisa i-K&Edit +Enabled=true + +[Action_3/Command_1] +Commandline=kwrite %s +Description=Launch K&Write +Description[af]=Lanseer Kskryf +Description[ar]=تشغيل K&Write +Description[az]=K&Write-ı Başlat +Description[be]=Запусціць K&Write +Description[bg]=С&тартиране на KWrite +Description[bn]=কে-রাই&ট চালাও +Description[br]=Lañsañ K&Write +Description[bs]=Pokreni K&Write +Description[ca]=Engega el K&Write +Description[cs]=Spustit K&Write +Description[csb]=Zrëszë editorã K&Write +Description[cy]=Cychwyn K&Write +Description[da]=Start K&Write +Description[de]=K&Write starten +Description[el]=Εκκίνηση K&Write +Description[eo]=Lanĉas &simplan kodredaktilon +Description[es]=Inicar K&Write +Description[et]=K&Write'i käivitamine +Description[eu]=Abiarazi K&Write +Description[fa]=راهاندازی K&Write +Description[fi]=Käynistä K&Write +Description[fr]=Démarrer K&Write +Description[fy]=K&Write útfiere +Description[ga]=Tosaigh K&Write +Description[gl]=Executar K&Write +Description[he]=הפעל את K&Write +Description[hi]=के-राइट चालू करें (&W) +Description[hr]=Pokreni K&Write uređivač +Description[hu]=A K&Write elindítása +Description[is]=Ræsa K&Write +Description[it]=Avvia K&Write +Description[ja]=KWrite を起動(&W) +Description[ka]=K&Write-ის გაშვება +Description[kk]=K&Write-ты жегу +Description[km]=បើក KWrite +Description[ko]=KWrite 실행(&W) +Description[lo]=ຮງດທຳງານ K&Write +Description[lt]=Paleisti K&Write +Description[lv]=Palaist K&Write +Description[mk]=Стартувај K&Write +Description[mn]=K&Write эхлүүлэх +Description[ms]=Lancar K&Write +Description[mt]=Ħaddem K&Write +Description[nb]=Start K&Write +Description[nds]=K&Write starten +Description[ne]=के राइट सुरुआत गर्नुहोस् +Description[nl]=K&Write opstarten +Description[nn]=Start K&Write +Description[nso]=Ngwadisaleswa K&Ngwala +Description[pa]=ਕੇ-ਲੇਖਕ ਸ਼ੁਰੂ(&W) +Description[pl]=Uruchom &Zaawansowany edytor +Description[pt]=Lançar o K&Write +Description[pt_BR]=Abrir KW&rite +Description[ro]=Execută K&Write +Description[ru]=Запуск K&Write +Description[rw]=Gutangiza K-Kwandika +Description[se]=Álggat &KWrite +Description[sk]=Spustiť K&Write +Description[sl]=Zaženi K&Write +Description[sr]=Покрени K&Write +Description[sr@Latn]=Pokreni K&Write +Description[sv]=Starta K&write +Description[ta]= K&Writeஐ இறக்கு +Description[tg]=Ба кор даровардани K&Write +Description[th]=เรียกใช้งาน K&Write +Description[tr]=K&Write Çalıştır +Description[tt]=K&Write Cibärü +Description[uk]=Запустити K&Write +Description[uz]=K&Write'ni ishga tushirish +Description[uz@cyrillic]=K&Write'ни ишга тушириш +Description[ven]=Thoma K&Nwala +Description[vi]=Khởi động K&Write +Description[wa]=Enonder K&Write +Description[zh_CN]=调用 K&Write +Description[zh_TW]=啟動 K&Write +Description[zu]=Qalisa K&Write +Enabled=true + +[Action_4] +Description=Local file URL +Description[af]=Plaaslike lêer Url +Description[ar]=عنوان ملف محلي +Description[az]=Yerli fayl URL-si +Description[be]=URL мясцовага файла +Description[bg]=Локален файл/адрес +Description[bn]=স্থানীয় ফাইল ইউ-আর-এল +Description[br]=URL ur restr lec'hel +Description[bs]=URL lokalne datoteke +Description[ca]=URL d'un fitxer local +Description[cs]=Místní URL +Description[csb]=Lokalnô adresa lopkù +Description[cy]=URL ffeil lleol +Description[da]=Lokal fil-URL +Description[de]=Adresse einer lokalen Datei (URL) +Description[el]=URL τοπικού αρχείου +Description[eo]=URL al loka dosiero +Description[es]=URL de archivo local +Description[et]=Kohaliku faili URL +Description[eu]=Fitxategi lokalaren URLa +Description[fa]=نشانی وب پروندۀ محلی +Description[fi]=Paikallisen tiedoston URL +Description[fr]=URL locale +Description[fy]=Lokale triem-URL-adres +Description[gl]=URL do ficheiro local +Description[he]=כתובת של קובץ מקומי +Description[hi]=स्थानीय फ़ाइल यूआरएल +Description[hr]=URL lokalne datoteke +Description[hu]=helyi fájl URL-je +Description[is]=Staðbundin slóð +Description[it]=URL locale +Description[ja]=ローカルファイル URL +Description[ka]=ბმული ლოკალურ ფაილზე +Description[kk]=Жергілікті файлдың URL +Description[km]=URL ឯកសារមូលដ្ឋាន +Description[ko]=로컬 파일 URL +Description[lo]=ຕຳແຫນ່ງ URL ແຟ້ມພາບໃນ +Description[lt]=Vietinės bylos URL +Description[lv]=Lokāla faila URL +Description[mk]=URL на локалната датотека +Description[mn]=Дотоод файлын хаяг(URL) +Description[ms]=URL fail setempat +Description[mt]=URL ta' fajl lokali +Description[nb]=Lokal filadresse +Description[nds]=URL vun en lokale Datei +Description[ne]=स्थानीय फाइल यूआरएल +Description[nl]=Lokaal bestands-URL-adres +Description[nn]=Lokal filadresse +Description[nso]=URL ya Faele ya Selegae +Description[pa]=ਸਥਾਨਕ ਫਾਇਲ URL +Description[pl]=Adres lokalnego pliku +Description[pt]=URL de ficheiro local +Description[pt_BR]=URL de arquivo local +Description[ro]=URL de fișier local +Description[ru]=Ссылка на локальный файл +Description[rw]=URL y'idosiye ya hafi +Description[se]=Báikkalaš fiilačujuhus +Description[sk]=URL lokálneho súboru +Description[sl]=URL krajevne datoteke +Description[sr]=URL локалног фајла +Description[sr@Latn]=URL lokalnog fajla +Description[sv]=Webbadress för lokal fil +Description[ta]=உள் கோப்பு வலைப்பின்னல் +Description[te]=స్థానిక దస్త్ర యూ ఆర్ ఎల్ +Description[tg]=Нишони файли маҳлӣ +Description[th]=ตำแหน่งแฟ้มภายใน +Description[tr]=Yerel dosya URL'i +Description[tt]=Cirle birem URLı +Description[uk]=URL локального файла +Description[uz]=Lokal faylining URL'i +Description[uz@cyrillic]=Локал файлининг URL'и +Description[ven]=URL ya faela ya henefhano +Description[vi]=URL Tập tin Địa phương +Description[wa]=Hårdêye viè on fitchî locå +Description[xh]=Ifayile yobulali ye URL +Description[zh_CN]=本地文件 URL +Description[zh_TW]=本地檔案 URL +Description[zu]=I-URL yefayela eseduze +Number of commands=2 +Regexp=^file:. + +[Action_4/Command_0] +Commandline=kmail --body %s +Description=Send &URL +Description[af]=Stuur URL +Description[ar]=ارسال &رابط +Description[az]=&URL Göndər +Description[be]=Даслаць &URL +Description[bg]=&Изпращане на адрес +Description[bn]=ই&উ-আর-এল পাঠাও +Description[br]=Kas an &URL +Description[bs]=Pošalji &URL +Description[ca]=Envia l'&URL +Description[cs]=Poslat &URL +Description[csb]=Wëslij adresã (&URL) +Description[cy]=Anfon &URL +Description[de]=&URL senden +Description[el]=Αποστολή &URL +Description[eo]=Sendi &URLon +Description[es]=Enviar &URL +Description[et]=&URL-i saatmine +Description[eu]=Bidali &URLa +Description[fa]=ارسال &نشانی وب +Description[fi]=Lähetä &URL +Description[fr]=Envoyer l'&URL +Description[fy]=&URL-adres stjoere +Description[ga]=Seol &URL +Description[gl]=Enviar &URL +Description[he]=שלח &כתובת +Description[hi]= यूआरएल भेजें (&U) +Description[hr]=Pošalji &URL +Description[hu]=Az &URL elküldése +Description[is]=Senda &slóð +Description[it]=Invia l'&URL +Description[ja]=URL を送信(&U) +Description[ka]=&URL-ს გაგზავნა +Description[kk]=&URL-ды жіберу +Description[km]=ផ្ញើ &URL +Description[lo]=ສັ່ງຕຳແຫນ່ງ &URL +Description[lt]=Siųsti &URL +Description[lv]=Sūtīt &URL +Description[mk]=Испрати &URL +Description[mn]=&URL илгээх +Description[ms]=Hantar &URL +Description[mt]=Ibgħat &URL +Description[nb]=Send &Nettadressen +Description[nds]=&URL schicken +Description[ne]=यूआरएल पठाउनुहोस् +Description[nl]=&URL-adres verzenden +Description[nso]=Romela &URL +Description[pa]=&URL ਭੇਜੋ +Description[pl]=Wyślij adres (&URL) +Description[pt]=Enviar o &URL +Description[pt_BR]=Enviar &URL +Description[ro]=Trimite &URL-ul +Description[ru]=Отправить &URL +Description[rw]=Kohereza URL +Description[se]=Sádde &URL:a +Description[sk]=Poslať &URL +Description[sl]=Pošlji &URL +Description[sr]=Пошаљи &URL +Description[sr@Latn]=Pošalji &URL +Description[sv]=Skicka &webbadress +Description[ta]=வலைப்பின்னலை அனுப்பு +Description[te]=(&U) యూ ఆర్ ఎల్ పంపించు +Description[tg]=Фиристодани &URL +Description[th]=ส่งตำแหน่ง &URL +Description[tr]=&URL Gönder +Description[tt]=&URL Künderü +Description[uk]=Відіслати &URL +Description[uz]=URL'ni &joʻnatish +Description[uz@cyrillic]=URL'ни &жўнатиш +Description[ven]=Rumele &URL +Description[vi]=Gửi&URL +Description[wa]=Evoyî &hårdêye +Description[xh]=Thumela i &URL +Description[zh_CN]=发送 &URL +Description[zh_TW]=送出 &URL +Description[zu]=Thumela i-&URL +Enabled=true + +[Action_4/Command_1] +Commandline=kmail --attach %s +Description=Send &File +Description[af]=Stuur Lêer +Description[ar]=ارسال م&لف +Description[az]=&Fayl Göndər +Description[be]=Даслаць &файл +Description[bg]=Изпращане на &файл +Description[bn]=ফাই&ল পাঠাও +Description[br]=Kas ar &restr +Description[bs]=Pošalji &Datoteku +Description[ca]=Envia &fitxer +Description[cs]=Posl&at soubor +Description[csb]=Wëslij &lopk +Description[cy]=Anfon &Ffeil +Description[da]=Send &fil +Description[de]=&Datei senden +Description[el]=Αποστολή &αρχείου +Description[eo]=Sendi &dosieron +Description[es]=Enviar &archivo +Description[et]=&Faili saatmine +Description[eu]=Bidali &Fitxategia +Description[fa]=ارسال &پرونده +Description[fi]=Lähetä &tiedosto +Description[fr]=Envoyer le &fichier +Description[fy]=&Triem stjoere +Description[ga]=Seol &Comhad +Description[gl]=Enviar &Ficheiro +Description[he]=שלח &קובץ +Description[hi]=फ़ाइल भेजें (&F) +Description[hr]=Pošalji &datoteku +Description[hu]=&Fájl elküldése +Description[is]=Senda &skrá +Description[it]=Invia il &file +Description[ja]=ファイルを送信(&F) +Description[ka]=&ფაილის გაგზავნა +Description[kk]=&Файлды жіберу +Description[km]=ផ្ញើឯកសារ +Description[ko]=파일 보내기(&F) +Description[lo]=ສົ່ງແຟ້ມ &File +Description[lt]=Siųsti &bylą +Description[lv]=Sūtīt &Failu +Description[mk]=Испрати &датотека +Description[mn]=&Файл илгээх +Description[ms]=Hantar &Fail +Description[mt]=Ibgħat &fajl +Description[nb]=Send &Fil +Description[nds]=Datei &schicken +Description[ne]=फाइल पठाउनुहोस् +Description[nl]=&Bestand verzenden +Description[nn]=Send &fil +Description[nso]=Romela &Faele +Description[pa]=ਫਾਇਲ ਭੇਜੋ(&F) +Description[pl]=Wyślij &plik +Description[pt]=Enviar o &Ficheiro +Description[pt_BR]=Enviar &Arquivo +Description[ro]=Trimite &fișierul +Description[ru]=Отправить &файл +Description[rw]=Kohereza Idosiye +Description[se]=Sádde &fiilla +Description[sk]=Poslať &súbor +Description[sl]=Pošlji &datoteko +Description[sr]=Пошаљи &фајл +Description[sr@Latn]=Pošalji &fajl +Description[sv]=Skicka &fil +Description[ta]=கோப்பை அனுப்பு +Description[te]=(&F) దస్త్రాన్ని పంపించు +Description[tg]=Фиристодани &файл +Description[th]=ส่งแฟ้ม +Description[tr]=Dosya &Gönder +Description[tt]=Bir&em Cibärü +Description[uk]=Відіслати &File +Description[uz]=&Faylni joʻnatish +Description[uz@cyrillic]=&Файлни жўнатиш +Description[ven]=Rumela &Faela +Description[vi]=Gửi &Tập tin +Description[wa]=Evoyî &fitchî +Description[xh]=Thumela &Ifayile +Description[zh_CN]=发送文件(&F) +Description[zh_TW]=送出檔案(&F) +Description[zu]=Thumela &Ifayela +Enabled=true + +[Action_5] +Description=Gopher URL +Description[af]=Gopher Url +Description[ar]=رابط عنوان Gopher +Description[az]=Qofher URL +Description[bg]=Адрес Gopher +Description[br]=URL Gopher +Description[ca]=URL de Gopher +Description[csb]=Adresa Gophera +Description[cy]=URL Gopher +Description[da]=Gopher-URL +Description[de]=Gopher-Adresse (URL) +Description[eo]=Gofer-URL +Description[es]=URL de Gopher +Description[et]=Gopher'i URL +Description[eu]=Gopher URLa +Description[fa]=نشانی وب گوفر +Description[fr]=URL Gopher +Description[fy]=Gopher URL-adres +Description[ga]=URL Gopher +Description[gl]=URL Gopher +Description[he]=כתובת Gopher +Description[hi]=गोफर यूआरएल +Description[is]=Gopher slóð +Description[it]=URL Gopher +Description[ka]=Gopher ბმული +Description[kk]=Gopher сілтемесі +Description[lo]=ຕຳແຫນ່ງ URL ຂອງໂກເຟີ +Description[mn]=Gopher-Хаяг (URL) +Description[ms]=URL Gopher +Description[mt]=URL ta' gopher +Description[nb]=Gopheradresse +Description[nds]=Gopher-URL +Description[ne]=गोफर यूआरएल +Description[nl]=Gopher URL-adres +Description[nn]=Gopher-adresse +Description[nso]=URL ya Gopher +Description[pa]=ਗੋਫਰ URL +Description[pl]=Adres Gophera +Description[pt]=URL do Gopher +Description[pt_BR]=URL para o Gopher +Description[ro]=URL Gopher +Description[ru]=Ссылка Gopher +Description[se]=Gopher-čujuhus +Description[sk]=URL pre Gopher +Description[sl]=URL za gopher +Description[sv]=Gopher-webbadress +Description[ta]=கோப்பர் வலைப்பின்னல் +Description[te]=గొఫర్ యూ ఆర్ ఎల్ +Description[tg]=Гуфери URL +Description[th]=ตำแหน่ง URL ของโกเฟอร์ +Description[tr]=Gopher URL'i +Description[tt]=Gopher URLı +Description[uk]=URL від Gopher +Description[vi]=Gửi Gopher URL +Description[wa]=Hårdêye Gopher +Description[zu]=I-URL ye-Gopher +Number of commands=2 +Regexp=^gopher:. + +[Action_5/Command_0] +Commandline=kmail --body %s +Description=Send &URL +Description[af]=Stuur URL +Description[ar]=ارسال &رابط +Description[az]=&URL Göndər +Description[be]=Даслаць &URL +Description[bg]=&Изпращане на адрес +Description[bn]=ই&উ-আর-এল পাঠাও +Description[br]=Kas an &URL +Description[bs]=Pošalji &URL +Description[ca]=Envia l'&URL +Description[cs]=Poslat &URL +Description[csb]=Wëslij adresã (&URL) +Description[cy]=Anfon &URL +Description[de]=&URL senden +Description[el]=Αποστολή &URL +Description[eo]=Sendi &URLon +Description[es]=Enviar &URL +Description[et]=&URL-i saatmine +Description[eu]=Bidali &URLa +Description[fa]=ارسال &نشانی وب +Description[fi]=Lähetä &URL +Description[fr]=Envoyer l'&URL +Description[fy]=&URL-adres stjoere +Description[ga]=Seol &URL +Description[gl]=Enviar &URL +Description[he]=שלח &כתובת +Description[hi]= यूआरएल भेजें (&U) +Description[hr]=Pošalji &URL +Description[hu]=Az &URL elküldése +Description[is]=Senda &slóð +Description[it]=Invia l'&URL +Description[ja]=URL を送信(&U) +Description[ka]=&URL-ს გაგზავნა +Description[kk]=&URL-ды жіберу +Description[km]=ផ្ញើ &URL +Description[lo]=ສັ່ງຕຳແຫນ່ງ &URL +Description[lt]=Siųsti &URL +Description[lv]=Sūtīt &URL +Description[mk]=Испрати &URL +Description[mn]=&URL илгээх +Description[ms]=Hantar &URL +Description[mt]=Ibgħat &URL +Description[nb]=Send &Nettadressen +Description[nds]=&URL schicken +Description[ne]=यूआरएल पठाउनुहोस् +Description[nl]=&URL-adres verzenden +Description[nso]=Romela &URL +Description[pa]=&URL ਭੇਜੋ +Description[pl]=Wyślij adres (&URL) +Description[pt]=Enviar o &URL +Description[pt_BR]=Enviar &URL +Description[ro]=Trimite &URL-ul +Description[ru]=Отправить &URL +Description[rw]=Kohereza URL +Description[se]=Sádde &URL:a +Description[sk]=Poslať &URL +Description[sl]=Pošlji &URL +Description[sr]=Пошаљи &URL +Description[sr@Latn]=Pošalji &URL +Description[sv]=Skicka &webbadress +Description[ta]=வலைப்பின்னலை அனுப்பு +Description[te]=(&U) యూ ఆర్ ఎల్ పంపించు +Description[tg]=Фиристодани &URL +Description[th]=ส่งตำแหน่ง &URL +Description[tr]=&URL Gönder +Description[tt]=&URL Künderü +Description[uk]=Відіслати &URL +Description[uz]=URL'ni &joʻnatish +Description[uz@cyrillic]=URL'ни &жўнатиш +Description[ven]=Rumele &URL +Description[vi]=Gửi&URL +Description[wa]=Evoyî &hårdêye +Description[xh]=Thumela i &URL +Description[zh_CN]=发送 &URL +Description[zh_TW]=送出 &URL +Description[zu]=Thumela i-&URL +Enabled=true + +[Action_5/Command_1] +Commandline=kmail --attach %s +Description=Send &File +Description[af]=Stuur Lêer +Description[ar]=ارسال م&لف +Description[az]=&Fayl Göndər +Description[be]=Даслаць &файл +Description[bg]=Изпращане на &файл +Description[bn]=ফাই&ল পাঠাও +Description[br]=Kas ar &restr +Description[bs]=Pošalji &Datoteku +Description[ca]=Envia &fitxer +Description[cs]=Posl&at soubor +Description[csb]=Wëslij &lopk +Description[cy]=Anfon &Ffeil +Description[da]=Send &fil +Description[de]=&Datei senden +Description[el]=Αποστολή &αρχείου +Description[eo]=Sendi &dosieron +Description[es]=Enviar &archivo +Description[et]=&Faili saatmine +Description[eu]=Bidali &Fitxategia +Description[fa]=ارسال &پرونده +Description[fi]=Lähetä &tiedosto +Description[fr]=Envoyer le &fichier +Description[fy]=&Triem stjoere +Description[ga]=Seol &Comhad +Description[gl]=Enviar &Ficheiro +Description[he]=שלח &קובץ +Description[hi]=फ़ाइल भेजें (&F) +Description[hr]=Pošalji &datoteku +Description[hu]=&Fájl elküldése +Description[is]=Senda &skrá +Description[it]=Invia il &file +Description[ja]=ファイルを送信(&F) +Description[ka]=&ფაილის გაგზავნა +Description[kk]=&Файлды жіберу +Description[km]=ផ្ញើឯកសារ +Description[ko]=파일 보내기(&F) +Description[lo]=ສົ່ງແຟ້ມ &File +Description[lt]=Siųsti &bylą +Description[lv]=Sūtīt &Failu +Description[mk]=Испрати &датотека +Description[mn]=&Файл илгээх +Description[ms]=Hantar &Fail +Description[mt]=Ibgħat &fajl +Description[nb]=Send &Fil +Description[nds]=Datei &schicken +Description[ne]=फाइल पठाउनुहोस् +Description[nl]=&Bestand verzenden +Description[nn]=Send &fil +Description[nso]=Romela &Faele +Description[pa]=ਫਾਇਲ ਭੇਜੋ(&F) +Description[pl]=Wyślij &plik +Description[pt]=Enviar o &Ficheiro +Description[pt_BR]=Enviar &Arquivo +Description[ro]=Trimite &fișierul +Description[ru]=Отправить &файл +Description[rw]=Kohereza Idosiye +Description[se]=Sádde &fiilla +Description[sk]=Poslať &súbor +Description[sl]=Pošlji &datoteko +Description[sr]=Пошаљи &фајл +Description[sr@Latn]=Pošalji &fajl +Description[sv]=Skicka &fil +Description[ta]=கோப்பை அனுப்பு +Description[te]=(&F) దస్త్రాన్ని పంపించు +Description[tg]=Фиристодани &файл +Description[th]=ส่งแฟ้ม +Description[tr]=Dosya &Gönder +Description[tt]=Bir&em Cibärü +Description[uk]=Відіслати &File +Description[uz]=&Faylni joʻnatish +Description[uz@cyrillic]=&Файлни жўнатиш +Description[ven]=Rumela &Faela +Description[vi]=Gửi &Tập tin +Description[wa]=Evoyî &fitchî +Description[xh]=Thumela &Ifayile +Description[zh_CN]=发送文件(&F) +Description[zh_TW]=送出檔案(&F) +Description[zu]=Thumela &Ifayela +Enabled=true + +[Action_6] +Description=ftp URL +Description[af]=ftp Url +Description[ar]=رابط عنوان ftp ( URL ) +Description[be]=FTP URL +Description[bg]=Адрес ftp +Description[br]=URL FTP +Description[ca]=URL de ftp +Description[cs]=FTP URL +Description[csb]=Adresa FTP +Description[cy]=URL ftp +Description[da]=ftp-URL +Description[de]=FTP-Adresse (URL) +Description[en_GB]=FTP URL +Description[eo]=FTP-URL +Description[es]=URL de ftp +Description[et]=FTP URL +Description[eu]=ftp URLa +Description[fa]=نشانی وب قاپ +Description[fr]=URL FTP +Description[fy]=FTP URL-adres +Description[ga]=URL ftp +Description[gl]=URL ftp +Description[he]=כתובת FTP +Description[hi]=एफटीपी यूआरएल +Description[hu]=FTP URL +Description[is]=ftp slóð +Description[it]=URL FTP +Description[ka]=FTP ბმული +Description[kk]=ftp сілтемесі +Description[lo]=ຕຳແຫນ່ງ URL ຂອງ ftp +Description[mn]=FTP-Хаяг (URL) +Description[ms]=URL ftp +Description[mt]=URL ta' ftp +Description[nb]=Ftp-adressen +Description[nds]=FTP-URL +Description[ne]=ftp यूआरएल +Description[nl]=FTP URL-adres +Description[nn]=FTP-adresse +Description[nso]=URL ya ftp +Description[pl]=Adres FTP +Description[pt]=URL de FTP +Description[pt_BR]=URL para FTP +Description[ro]=URL FTP +Description[ru]=Ссылка FTP +Description[rw]=URL ya ftp +Description[se]=FTP-čujuhus +Description[sk]=URL pre FTP +Description[sl]=URL za FTP +Description[sv]=FTP-webbadress +Description[ta]=ftpவலைப்பின்னல் +Description[te]=ఎఫ్ టి పి యూ ఆర్ ఎల్ +Description[th]=ตำแหน่ง URL ของ ftp +Description[tr]=ftp URL'i +Description[tt]=ftp URLı +Description[uk]=URL ftp +Description[vi]=Gửi ftp URL +Description[wa]=Hårdêye FTP +Description[zh_TW]=FTP URL +Number of commands=5 +Regexp=^ftp://. + +[Action_6/Command_0] +Commandline=kfmclient exec %s +Description=Open with &Konqueror +Description[af]=Maak oop met Konqueror +Description[ar]=إفتح بــ &Konqueror +Description[az]=&Konqueror ilə Aç +Description[be]=Адкрыць у &Konqueror +Description[bg]=&Отваряне с Konqueror +Description[bn]=কনকরার দিয়ে খোলো (&ক) +Description[br]=Digeriñ gant &Konqueror +Description[bs]=Otvori u &Konqueroru +Description[ca]=Obre amb el &Konqueror +Description[cs]=Otevřít pomocí &Konqueroru +Description[csb]=Òtemknij w &Konquerorze +Description[cy]=Agor gyda &Konqueror +Description[da]=Åbn med &Konqueror +Description[de]=Mit &Konqueror öffnen +Description[el]=Άνοιγμα με το &Konqueror +Description[eo]=Malfermi per &Konkeranto +Description[es]=Abrir con &Konqueror +Description[et]=&Konqueroris avamine +Description[eu]= Ireki &Konquerorrekin +Description[fa]=باز کردن با&Konqueror +Description[fi]=Avaa &Konquerorissa +Description[fo]=Opna við &Konqueror +Description[fr]=Ouvrir dans &Konqueror +Description[fy]=Iepenje mei &Konqueror +Description[ga]=Oscail le &Konqueror +Description[gl]=Abrir con &Konqueror +Description[he]=פתח באמצעות &Konqueror +Description[hi]=कॉन्करर के साथ खोलें (&K) +Description[hr]=Otvori pomoću &Konquerora +Description[hu]=Megnyitás a &Konquerorral +Description[is]=Opna með &Konqueror +Description[it]=Apri con &Konqueror +Description[ja]=Konqueror で開く(&K) +Description[ka]=&Konqueror-ში გახსნა +Description[kk]=&Konqueror-де ашу +Description[km]=បើកជាមួយ &Konqueror +Description[ko]=Konqueror로 열기(&K) +Description[lo]=ເປີດດ້ວຍຄອນຄິວເຣ &Konquerorີ +Description[lt]=Atverti su &Konqueror +Description[lv]=Atvērt ar Ie&karotāju +Description[mk]=Отвори со &Konqueror +Description[mn]=&Конкюророор нээх +Description[ms]=Buka dengan &Konqueror +Description[mt]=Iftaħ b' &Konqueror +Description[nb]=Åpne med &Konqueror +Description[nds]=Mit &Konqueror opmaken +Description[ne]=कन्क्वेररसँग खोल्नुहोस् +Description[nl]=Openen met &Konqueror +Description[nn]=Opna med &Konqueror +Description[nso]=Bula ka &Konqueror +Description[pa]=ਕੋਨਕਿਉਰੋਰ ਨਾਲ ਖੋਲੋ(&K) +Description[pl]=Otwórz w &Konquerorze +Description[pt]=Abrir com o &Konqueror +Description[pt_BR]=Abrir com o &Konqueror +Description[ro]=Deschide cu &Konqueror +Description[ru]=Открыть в &Konqueror +Description[rw]=Gufungura ukoresheje Konqueror +Description[se]=Raba &Konquerorain +Description[sk]=Otvoriť pomocou &Konqueror +Description[sl]=Odpri s &Konquerorjem +Description[sr]=Отвори помоћу &Konqueror-а +Description[sr@Latn]=Otvori pomoću &Konqueror-a +Description[sv]=Öppna med &Konqueror +Description[ta]=கான்கொரர் உடன் திற +Description[te]=(&K) కాంకెరర్ తొ తెరువు +Description[tg]=Бо &Konqueror боз кунед +Description[th]=เปิดด้วย&คอนเควอร์เรอร์ +Description[tr]=&Konqueror ile Aç +Description[tt]=&Konqueror belän Aç +Description[uk]=Відкрити з &Konqueror +Description[uz]=Konqueror &bilan ochish +Description[uz@cyrillic]=Konqueror &билан очиш +Description[ven]=Vula nga &Konqueror +Description[vi]=Mở bằng &Konqueror +Description[wa]=Drovi avou &Konqueror +Description[xh]=Vula nge &Konqueror +Description[zh_CN]=用 &Konqueror 打开 +Description[zh_TW]=使用 &Konqueror 開啟 +Description[zu]=Vula nge-&Konqueror +Enabled=true + +[Action_6/Command_1] +Commandline=netscape -no-about-splash -remote openURL\(%s, new-window\) +Description=Open with &Netscape +Description[af]=Open met Netscape +Description[ar]=إفتح بواسطة &Netscape +Description[az]=&Netscape ilə Aç +Description[be]=Адкрыць у &Netscape +Description[bg]=О&тваряне с Netscape +Description[bn]=নেটস্কেপ দিয়ে খোলো (&ন) +Description[br]=Digeriñ gant &Netscape +Description[bs]=Otvori u &Netscape +Description[ca]=Obre amb el &Netscape +Description[cs]=Otevřít pomocí &Netscape +Description[csb]=Òtemknij w &Netscape +Description[cy]=Agor gyda &Netscape +Description[da]=Åbn med &Netscape +Description[de]=Mit &Netscape öffnen +Description[el]=Άνοιγμα με το &Netscape +Description[eo]=Malfermi per &Netskapo +Description[es]=Abrir con &Netscape +Description[et]=&Netscape'is avamine +Description[eu]= Ireki &Netscaperekin +Description[fa]=باز کردن با &نتاسکیپ +Description[fi]=Avaa &Netscapella +Description[fo]=Opna við &Netscape +Description[fr]=Ouvrir dans &Netscape +Description[fy]=Iepenje mei &Netscape +Description[ga]=Oscail le &Netscape +Description[gl]=Abrir con &Netscape +Description[he]=פתח באמצעות &Netscape +Description[hi]=नेटस्केप के साथ खोलें (&N) +Description[hr]=Otvori pomoću &Netscapea +Description[hu]=Megnyitás a &Netscape-pel +Description[is]=Opna með &Netscape +Description[it]=Apri con &Netscape +Description[ja]=Netscape で開く(&N) +Description[ka]=&Netscape-სი გახსნა +Description[kk]=&Netscape-те ашу +Description[km]=បើកជាមួយ &Netscape +Description[ko]=UADescription (윈도 XP의 넷스케이프 7.1) +Description[lo]=ເປີດດ້ວຍເນ໊ຕສະເຄ໊ບ &Netscape +Description[lt]=Atverti su &Netscape +Description[lv]=Atvērt ar &Netscape +Description[mk]=Отвори со &Netscape +Description[mn]=&Nэтскапаар нээх +Description[ms]=Buka dengan &Netscape +Description[mt]=Iftaħ b' &Netscape +Description[nb]=Åpne med &Netscape +Description[nds]=Mit &Netscape opmaken +Description[ne]=नेटस्केपसँग खोल्नुहोस् +Description[nl]=Openen met &Netscape +Description[nn]=Opna med &Netscape +Description[nso]=Bula ka &Netscape +Description[pa]=ਨੈੱਟਸਕੇਪ ਨਾਲ ਖੋਲੋ(&N) +Description[pl]=Otwórz w &Netscape +Description[pt]=Abrir com o &Netscape +Description[pt_BR]=Abrir com o &Netscape +Description[ro]=Deschide cu &Netscape +Description[ru]=Открыть в &Netscape +Description[rw]=Gufungura ukoresheje Netscape +Description[se]=Raba &Netscapeain +Description[sk]=Otvoriť pomocou &Netscape +Description[sl]=Odpri z &Netscape +Description[sr]=Отвори помоћу &Netscape-а +Description[sr@Latn]=Otvori pomoću &Netscape-a +Description[sv]=Öppna med &Netscape +Description[ta]=நெட்ஸ்கேப்புடன் திற +Description[te]=(&N) నెట్ స్కేప్ తొ తెరువు +Description[tg]=Бо &Netscape боз кунед +Description[th]=เปิดด้วยเน็ตสเคป +Description[tr]=&Netscape ile Aç +Description[tt]=&Netscape belän Aç +Description[uk]=Відкрити з &Netscape +Description[uz]=&Netscape bilan ochish +Description[uz@cyrillic]=&Netscape билан очиш +Description[ven]=Vula nga &Netscape +Description[vi]=Mở bằng &Netscape +Description[wa]=Drovi avou &Netscape +Description[xh]=Vula nge &Netscape +Description[zh_CN]=用 &Netscape 打开 +Description[zh_TW]=使用 &Netscape 開啟 +Description[zu]=Vula nge-&Netscape +Enabled=true + +[Action_6/Command_2] +Commandline=mozilla -remote openURL\(%s, new-window\) +Description=Open with &Mozilla +Description[af]=Maak oop met Mozilla +Description[ar]=إفتح بِــ &Mozilla +Description[az]=&Mozilla ilə Aç +Description[be]=Адкрыць у &Mozilla +Description[bg]=От&варяне с Mozilla +Description[bn]=মোজিলা দিয়ে খোলো (&ম) +Description[br]=Digeriñ gant &Mozilla +Description[bs]=Otvori u &Mozilla +Description[ca]=Obre amb el &Mozilla +Description[cs]=Otevřít pomocí &Mozilla +Description[csb]=Òtemknij w &Mozillë +Description[cy]=Agor gyda &Mozilla +Description[da]=Åbn med &Mozilla +Description[de]=Mit &Mozilla öffnen +Description[el]=Άνοιγμα με το &Mozilla +Description[eo]=Malfermi per &Mozilo +Description[es]=Abrir con &Mozilla +Description[et]=&Mozillas avamine +Description[eu]=Ireki &Mozillarekin +Description[fa]=باز کردن با &موزیلا +Description[fi]=Avaa &Mozillalla +Description[fo]=Opna við &Mozilla +Description[fr]=Ouvrir dans &Mozilla +Description[fy]=Iepenje mei &Mozilla +Description[ga]=Oscail le &Mozilla +Description[gl]=Abrir con &Mozilla +Description[he]=פתח באמצעות &Mozilla +Description[hi]=मोज़िला के साथ खोलें (&M) +Description[hr]=Otvori pomoću &Mozille +Description[hu]=Megnyitás a &Mozillával +Description[is]=Opna með &Mozilla +Description[it]=Apri con &Mozilla +Description[ja]=Mozilla で開く(&M) +Description[ka]=&Mozilla-ში გასნა +Description[kk]=&Mozilla-да ашу +Description[km]=បើកជាមួយ Mozilla +Description[ko]=Mozilla로 열기(&M) +Description[lo]=ເປີດດ້ວຍ &Mozilla +Description[lt]=Atverti su &Mozilla +Description[lv]=Atvērt ar &Mozilla +Description[mk]=Отвори со &Mozilla +Description[mn]=&Mозиллагаар нээх +Description[ms]=Buka dengan &Mozilla +Description[mt]=Iftaħ b' &Możilla +Description[nb]=Åpne med &Mozilla +Description[nds]=Mit &Mozilla opmaken +Description[ne]=मोजिलासँग खोल्नुहोस् +Description[nl]=Openen met &Mozilla +Description[nn]=Opna med &Mozilla +Description[nso]=Bula ka &Mozilla +Description[pa]=ਮੌਜੀਲਾ ਨਾਲ ਖੋਲੋ(&M) +Description[pl]=Otwórz w &Mozilli +Description[pt]=Abrir com o &Mozilla +Description[pt_BR]=Abrir com o &Mozilla +Description[ro]=Deschide cu &Mozilla +Description[ru]=Открыть в &Mozilla +Description[rw]=Gufungura ukoresheje Mozilla +Description[se]=Raba &Mozillain +Description[sk]=Otvoriť pomocou &Mozilla +Description[sl]=Odpri z &Mozillo +Description[sr]=Отвори помоћу &Mozilla-е +Description[sr@Latn]=Otvori pomoću &Mozilla-e +Description[sv]=Öppna med &Mozilla +Description[ta]=மொசில்லாவுடன் திற +Description[te]=(&M) మొజిల్లా తొ తెరువు +Description[tg]=Бо &Mozilla боз кунед +Description[th]=เปิดด้วย&มอซซิลลา +Description[tr]=&Mozilla ile Aç +Description[tt]=&Mozilla belän Aç +Description[uk]=Відкрити з &Mozilla +Description[uz]=Mozilla bilan &ochish +Description[uz@cyrillic]=Mozilla билан &очиш +Description[ven]=Vula nga &Mozilla +Description[vi]=Mở bằng &Mozilla +Description[wa]=Drovi avou &Mozilla +Description[xh]=Vula nge &Mozilla +Description[zh_CN]=用 &Mozilla 打开 +Description[zh_TW]=使用 &Mozilla 開啟 +Description[zu]=Vula nge-&Mozilla +Enabled=true + +[Action_6/Command_3] +Commandline=kmail --body %s +Description=Send &URL +Description[af]=Stuur URL +Description[ar]=ارسال &رابط +Description[az]=&URL Göndər +Description[be]=Даслаць &URL +Description[bg]=&Изпращане на адрес +Description[bn]=ই&উ-আর-এল পাঠাও +Description[br]=Kas an &URL +Description[bs]=Pošalji &URL +Description[ca]=Envia l'&URL +Description[cs]=Poslat &URL +Description[csb]=Wëslij adresã (&URL) +Description[cy]=Anfon &URL +Description[de]=&URL senden +Description[el]=Αποστολή &URL +Description[eo]=Sendi &URLon +Description[es]=Enviar &URL +Description[et]=&URL-i saatmine +Description[eu]=Bidali &URLa +Description[fa]=ارسال &نشانی وب +Description[fi]=Lähetä &URL +Description[fr]=Envoyer l'&URL +Description[fy]=&URL-adres stjoere +Description[ga]=Seol &URL +Description[gl]=Enviar &URL +Description[he]=שלח &כתובת +Description[hi]= यूआरएल भेजें (&U) +Description[hr]=Pošalji &URL +Description[hu]=Az &URL elküldése +Description[is]=Senda &slóð +Description[it]=Invia l'&URL +Description[ja]=URL を送信(&U) +Description[ka]=&URL-ს გაგზავნა +Description[kk]=&URL-ды жіберу +Description[km]=ផ្ញើ &URL +Description[lo]=ສັ່ງຕຳແຫນ່ງ &URL +Description[lt]=Siųsti &URL +Description[lv]=Sūtīt &URL +Description[mk]=Испрати &URL +Description[mn]=&URL илгээх +Description[ms]=Hantar &URL +Description[mt]=Ibgħat &URL +Description[nb]=Send &Nettadressen +Description[nds]=&URL schicken +Description[ne]=यूआरएल पठाउनुहोस् +Description[nl]=&URL-adres verzenden +Description[nso]=Romela &URL +Description[pa]=&URL ਭੇਜੋ +Description[pl]=Wyślij adres (&URL) +Description[pt]=Enviar o &URL +Description[pt_BR]=Enviar &URL +Description[ro]=Trimite &URL-ul +Description[ru]=Отправить &URL +Description[rw]=Kohereza URL +Description[se]=Sádde &URL:a +Description[sk]=Poslať &URL +Description[sl]=Pošlji &URL +Description[sr]=Пошаљи &URL +Description[sr@Latn]=Pošalji &URL +Description[sv]=Skicka &webbadress +Description[ta]=வலைப்பின்னலை அனுப்பு +Description[te]=(&U) యూ ఆర్ ఎల్ పంపించు +Description[tg]=Фиристодани &URL +Description[th]=ส่งตำแหน่ง &URL +Description[tr]=&URL Gönder +Description[tt]=&URL Künderü +Description[uk]=Відіслати &URL +Description[uz]=URL'ni &joʻnatish +Description[uz@cyrillic]=URL'ни &жўнатиш +Description[ven]=Rumele &URL +Description[vi]=Gửi&URL +Description[wa]=Evoyî &hårdêye +Description[xh]=Thumela i &URL +Description[zh_CN]=发送 &URL +Description[zh_TW]=送出 &URL +Description[zu]=Thumela i-&URL +Enabled=true + +[Action_6/Command_4] +Commandline=kmail --attach %s +Description=Send &File +Description[af]=Stuur Lêer +Description[ar]=ارسال م&لف +Description[az]=&Fayl Göndər +Description[be]=Даслаць &файл +Description[bg]=Изпращане на &файл +Description[bn]=ফাই&ল পাঠাও +Description[br]=Kas ar &restr +Description[bs]=Pošalji &Datoteku +Description[ca]=Envia &fitxer +Description[cs]=Posl&at soubor +Description[csb]=Wëslij &lopk +Description[cy]=Anfon &Ffeil +Description[da]=Send &fil +Description[de]=&Datei senden +Description[el]=Αποστολή &αρχείου +Description[eo]=Sendi &dosieron +Description[es]=Enviar &archivo +Description[et]=&Faili saatmine +Description[eu]=Bidali &Fitxategia +Description[fa]=ارسال &پرونده +Description[fi]=Lähetä &tiedosto +Description[fr]=Envoyer le &fichier +Description[fy]=&Triem stjoere +Description[ga]=Seol &Comhad +Description[gl]=Enviar &Ficheiro +Description[he]=שלח &קובץ +Description[hi]=फ़ाइल भेजें (&F) +Description[hr]=Pošalji &datoteku +Description[hu]=&Fájl elküldése +Description[is]=Senda &skrá +Description[it]=Invia il &file +Description[ja]=ファイルを送信(&F) +Description[ka]=&ფაილის გაგზავნა +Description[kk]=&Файлды жіберу +Description[km]=ផ្ញើឯកសារ +Description[ko]=파일 보내기(&F) +Description[lo]=ສົ່ງແຟ້ມ &File +Description[lt]=Siųsti &bylą +Description[lv]=Sūtīt &Failu +Description[mk]=Испрати &датотека +Description[mn]=&Файл илгээх +Description[ms]=Hantar &Fail +Description[mt]=Ibgħat &fajl +Description[nb]=Send &Fil +Description[nds]=Datei &schicken +Description[ne]=फाइल पठाउनुहोस् +Description[nl]=&Bestand verzenden +Description[nn]=Send &fil +Description[nso]=Romela &Faele +Description[pa]=ਫਾਇਲ ਭੇਜੋ(&F) +Description[pl]=Wyślij &plik +Description[pt]=Enviar o &Ficheiro +Description[pt_BR]=Enviar &Arquivo +Description[ro]=Trimite &fișierul +Description[ru]=Отправить &файл +Description[rw]=Kohereza Idosiye +Description[se]=Sádde &fiilla +Description[sk]=Poslať &súbor +Description[sl]=Pošlji &datoteko +Description[sr]=Пошаљи &фајл +Description[sr@Latn]=Pošalji &fajl +Description[sv]=Skicka &fil +Description[ta]=கோப்பை அனுப்பு +Description[te]=(&F) దస్త్రాన్ని పంపించు +Description[tg]=Фиристодани &файл +Description[th]=ส่งแฟ้ม +Description[tr]=Dosya &Gönder +Description[tt]=Bir&em Cibärü +Description[uk]=Відіслати &File +Description[uz]=&Faylni joʻnatish +Description[uz@cyrillic]=&Файлни жўнатиш +Description[ven]=Rumela &Faela +Description[vi]=Gửi &Tập tin +Description[wa]=Evoyî &fitchî +Description[xh]=Thumela &Ifayile +Description[zh_CN]=发送文件(&F) +Description[zh_TW]=送出檔案(&F) +Description[zu]=Thumela &Ifayela +Enabled=true + +[General] +KeepClipboardContents=true +No Actions for WM_CLASS=Navigator,navigator:browser,konqueror,keditbookmarks,mozilla-bin,Mozilla,Opera main window,opera,gnumeric,Gnumeric,Galeon,kcontrol,ksirc,MozillaFirebird-bin,firefox-bin,Firefox-bin,klipper,Gecko,gecko +Number of Actions=7 +PopupAtMousePosition=false +URLGrabberEnabled=false diff --git a/klipper/klipperrc.upd b/klipper/klipperrc.upd new file mode 100644 index 000000000..943ec9e20 --- /dev/null +++ b/klipper/klipperrc.upd @@ -0,0 +1,7 @@ +Id=25082001 +File=klipperrc +Script=klipper-1-2.pl,perl +Id=kde3.1 +File=klipperrc +Group=General +Script=klipper-kde31.sh,sh diff --git a/klipper/klippershortcuts.upd b/klipper/klippershortcuts.upd new file mode 100644 index 000000000..1088e054c --- /dev/null +++ b/klipper/klippershortcuts.upd @@ -0,0 +1,5 @@ +Id=04112002 +File=klipperrc,kdeglobals +Group=Global Shortcuts +AllKeys +RemoveGroup=Global Shortcuts diff --git a/klipper/main.cpp b/klipper/main.cpp new file mode 100644 index 000000000..cb54a0e68 --- /dev/null +++ b/klipper/main.cpp @@ -0,0 +1,67 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) Andrew Stanley-Jones + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <stdio.h> +#include <stdlib.h> + +#include <klocale.h> +#include <kcmdlineargs.h> +#include <kwin.h> +#include <kaboutdata.h> +#include <kuniqueapplication.h> + +#include "toplevel.h" +#include "version.h" + +#if defined Q_WS_X11 +#include <qxembed.h> // schroder +#endif + + +extern "C" int KDE_EXPORT kdemain(int argc, char *argv[]) +{ + Klipper::createAboutData(); + KCmdLineArgs::init( argc, argv, Klipper::aboutData()); + KUniqueApplication::addCmdLineOptions(); + + if (!KUniqueApplication::start()) { + fprintf(stderr, "Klipper is already running!\n"); + exit(0); + } + KUniqueApplication app; + app.disableSessionManagement(); + + Klipper *toplevel = new Klipper(); + + // Make Klipper conform to freedesktop system tray standard, see + // http://bugs.kde.org/show_bug.cgi?id=69119 +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + QXEmbed::initialize(); +#endif + + KWin::setSystemTrayWindowFor( toplevel->winId(), 0 ); + toplevel->setGeometry(-100, -100, 42, 42 ); + toplevel->show(); + + int ret = app.exec(); + delete toplevel; + Klipper::destroyAboutData(); + return ret; +} diff --git a/klipper/popupproxy.cpp b/klipper/popupproxy.cpp new file mode 100644 index 000000000..84a9e4ae6 --- /dev/null +++ b/klipper/popupproxy.cpp @@ -0,0 +1,163 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include <qregexp.h> +#include <qstyle.h> +#include <qpixmap.h> +#include <qimage.h> + +#include <kstringhandler.h> +#include <klocale.h> +#include <kdebug.h> + +#include "historyitem.h" +#include "popupproxy.h" +#include "history.h" +#include "klipperpopup.h" + + +PopupProxy::PopupProxy( KlipperPopup* parent, const char* name, int menu_height, int menu_width ) + : QObject( parent, name ), + proxy_for_menu( parent ), + spillPointer( parent->history()->youngest() ), + m_menu_height( menu_height ), + m_menu_width( menu_width ), + nextItemNumber( 0 ) +{ + connect( parent->history(), SIGNAL( changed() ), SLOT( slotHistoryChanged() ) ); +} + +void PopupProxy::slotHistoryChanged() { + deleteMoreMenus(); + +} + +void PopupProxy::deleteMoreMenus() { + const KPopupMenu* myParent = parent(); + if ( myParent != proxy_for_menu ) { + const KPopupMenu* delme = proxy_for_menu;; + proxy_for_menu = static_cast<KPopupMenu*>( proxy_for_menu->parent() ); + while ( proxy_for_menu != myParent ) { + delme = proxy_for_menu; + proxy_for_menu = static_cast<KPopupMenu*>( proxy_for_menu->parent() ); + } + delete delme; + } +} + +int PopupProxy::buildParent( int index, const QRegExp& filter ) { + deleteMoreMenus(); + // Start from top of history (again) + spillPointer = parent()->history()->youngest(); + nextItemNumber = 0; + if ( filter.isValid() ) { + m_filter = filter; + } + + return insertFromSpill( index ); + +} + +KlipperPopup* PopupProxy::parent() { + return static_cast<KlipperPopup*>( QObject::parent() ); +} + +void PopupProxy::slotAboutToShow() { + insertFromSpill(); +} + +void PopupProxy::tryInsertItem( HistoryItem const * const item, + int& remainingHeight, + const int index ) +{ + + // Insert item + int id = -1; + QPixmap image( item->image() ); + if ( image.isNull() ) { + // Squeeze text strings so that do not take up the entire screen (or more) + QString text( KStringHandler::cPixelSqueeze(item->text().simplifyWhiteSpace(), + proxy_for_menu->fontMetrics(), + m_menu_width).replace( "&", "&&" ) ); + id = proxy_for_menu->insertItem( text, -1, index ); + } else { + const QSize max_size( m_menu_width,m_menu_height/4 ); + if ( image.height() > max_size.height() || image.width() > max_size.width() ) { + image.convertFromImage( image.convertToImage().smoothScale( max_size, QImage::ScaleMin ) ); + } + id = proxy_for_menu->insertItem( image, -1, index ); + } + + + // Determine height of a menu item. + Q_ASSERT( id != -1 ); // Be sure that the item was inserted. + QMenuItem* mi = proxy_for_menu->findItem( id ); + int fontheight = QFontMetrics( proxy_for_menu->fontMetrics() ).height(); + int itemheight = proxy_for_menu->style().sizeFromContents(QStyle::CT_PopupMenuItem, + proxy_for_menu, + QSize( 0, fontheight ), + QStyleOption(mi,10,0) ).height(); + // Test if there was enough space + remainingHeight -= itemheight; + History* history = parent()->history(); + proxy_for_menu->connectItem( id, + history, + SLOT( slotMoveToTop( int ) ) ); + proxy_for_menu->setItemParameter( id, nextItemNumber ); + +} + +int PopupProxy::insertFromSpill( int index ) { + + // This menu is going to be filled, so we don't need the aboutToShow() + // signal anymore + disconnect( proxy_for_menu, 0, this, 0 ); + + // Insert history items into the current proxy_for_menu, + // discarding any that doesn't match the current filter. + // stop when the total number of items equal m_itemsPerMenu; + int count = 0; + int remainingHeight = m_menu_height - proxy_for_menu->sizeHint().height(); + // Force at least one item to be inserted. + remainingHeight = QMAX( remainingHeight, 0 ); + for ( const HistoryItem* item = spillPointer.current(); + item && remainingHeight >= 0; + nextItemNumber++, item = ++spillPointer ) + { + if ( m_filter.search( item->text() ) == -1) { + continue; + } + tryInsertItem( item, remainingHeight, index++ ); + count++; + } + + // If there is more items in the history, insert a new "More..." menu and + // make *this a proxy for that menu ('s content). + if ( spillPointer.current() ) { + KPopupMenu* moreMenu = new KPopupMenu( proxy_for_menu, "a more menu" ); + proxy_for_menu->insertItem( i18n( "&More" ), moreMenu, -1, index ); + connect( moreMenu, SIGNAL( aboutToShow() ), SLOT( slotAboutToShow() ) ); + proxy_for_menu = moreMenu; + } + + // Return the number of items inserted. + return count; + +} +#include "popupproxy.moc" diff --git a/klipper/popupproxy.h b/klipper/popupproxy.h new file mode 100644 index 000000000..689e97f2a --- /dev/null +++ b/klipper/popupproxy.h @@ -0,0 +1,93 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _POPUPPROXY_H_ +#define _POPUPPROXY_H_ + +#include <qptrlist.h> +#include <qobject.h> +#include <qstring.h> +#include <qregexp.h> + +#include <kpopupmenu.h> +#include <history.h> + +class HistoryItem; +class KlipperPopup; + +/** + * Proxy helper for the "more" menu item + * + */ +class PopupProxy : public QObject +{ + Q_OBJECT + +public: + /** + * Inserts up to itemsPerMenu into parent from parent->youngest(), + * and spills any remaining items into a more menu. + */ + PopupProxy( KlipperPopup* parent, const char* name, int menu_height, int menu_width ); + + KlipperPopup* parent(); + + /** + * Called when rebuilding the menu + * Deletes any More menus.. and start (re)inserting into the toplevel menu. + * @param index Items are inserted at index. + * @param filter If non-empty, only insert items that match filter as a regex + * @return number of items inserted. + */ + int buildParent( int index, const QRegExp& filter = QRegExp() ); + +public slots: + void slotAboutToShow(); + void slotHistoryChanged(); +private: + /** + * Insert up to m_itemsPerMenu items from spill and a new + * more-menu if neccessary. + * @param index Items are inserted at index + * @return number of items inserted. + */ + int insertFromSpill( int index = 0 ); + + /** + * Insert item into proxy_for_menu at index, + * subtracting the items height from remainingHeight + */ + void tryInsertItem( HistoryItem const * const item, int& remainingHeight, const int index ); + + /** + * Delete all "More..." menus current created. + */ + void deleteMoreMenus(); + +private: + KPopupMenu* proxy_for_menu; + History::iterator spillPointer; + QRegExp m_filter; + int m_menu_height; + int m_menu_width; + int nextItemNumber; + +}; + +#endif diff --git a/klipper/toplevel.cpp b/klipper/toplevel.cpp new file mode 100644 index 000000000..cc5de3f63 --- /dev/null +++ b/klipper/toplevel.cpp @@ -0,0 +1,1174 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + + Copyright (C) by Andrew Stanley-Jones + Copyright (C) 2000 by Carsten Pfeiffer <[email protected]> + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qclipboard.h> +#include <qcursor.h> +#include <qdatetime.h> +#include <qfile.h> +#include <qintdict.h> +#include <qpainter.h> +#include <qtooltip.h> +#include <qmime.h> +#include <qdragobject.h> + +#include <kaboutdata.h> +#include <kaction.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kipc.h> +#include <kkeydialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ksavefile.h> +#include <kstandarddirs.h> +#include <ksimpleconfig.h> +#include <kstringhandler.h> +#include <ksystemtray.h> +#include <kurldrag.h> +#include <kwin.h> +#include <kdebug.h> +#include <kglobalsettings.h> +#include <dcopclient.h> +#include <kiconloader.h> +#include <khelpmenu.h> +#include <kstdguiitem.h> + +#include "configdialog.h" +#include "toplevel.h" +#include "urlgrabber.h" +#include "version.h" +#include "clipboardpoll.h" +#include "history.h" +#include "historyitem.h" +#include "historystringitem.h" +#include "klipperpopup.h" + +#include <zlib.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +#include <kclipboard.h> + +namespace { + /** + * Use this when manipulating the clipboard + * from within clipboard-related signals. + * + * This avoids issues such as mouse-selections that immediately + * disappear. + * pattern: Resource Acqusition is Initialisation (RAII) + * + * (This is not threadsafe, so don't try to use such in threaded + * applications). + */ + struct Ignore { + Ignore(int& locklevel) : locklevelref(locklevel) { + locklevelref++; + } + ~Ignore() { + locklevelref--; + } + private: + int& locklevelref; + }; +} + +/** + * Helper class to save history upon session exit. + */ +class KlipperSessionManaged : public KSessionManaged +{ +public: + KlipperSessionManaged( KlipperWidget* k ) + : klipper( k ) + {} + + /** + * Save state upon session exit. + * + * Saving history on session save + */ + virtual bool commitData( QSessionManager& ) { + klipper->saveSession(); + return true; + } +private: + KlipperWidget* klipper; +}; + +extern bool qt_qclipboard_bailout_hack; +#if KDE_IS_VERSION( 3, 9, 0 ) +#error Check status of #80072 with Qt4. +#endif + +static void ensureGlobalSyncOff(KConfig* config); + +// config == kapp->config for process, otherwise applet +KlipperWidget::KlipperWidget( QWidget *parent, KConfig* config ) + : QWidget( parent ) + , DCOPObject( "klipper" ) + , m_overflowCounter( 0 ) + , locklevel( 0 ) + , m_config( config ) + , m_pendingContentsCheck( false ) + , session_managed( new KlipperSessionManaged( this )) +{ + qt_qclipboard_bailout_hack = true; + + // We don't use the clipboardsynchronizer anymore, and it confuses Klipper + ensureGlobalSyncOff(m_config); + + updateTimestamp(); // read initial X user time + setBackgroundMode( X11ParentRelative ); + clip = kapp->clipboard(); + + connect( &m_overflowClearTimer, SIGNAL( timeout()), SLOT( slotClearOverflow())); + m_overflowClearTimer.start( 1000 ); + connect( &m_pendingCheckTimer, SIGNAL( timeout()), SLOT( slotCheckPending())); + + m_history = new History( this, "main_history" ); + + // we need that collection, otherwise KToggleAction is not happy :} + QString defaultGroup( "default" ); + KActionCollection *collection = new KActionCollection( this, "my collection" ); + toggleURLGrabAction = new KToggleAction( collection, "toggleUrlGrabAction" ); + toggleURLGrabAction->setEnabled( true ); + toggleURLGrabAction->setGroup( defaultGroup ); + clearHistoryAction = new KAction( i18n("C&lear Clipboard History"), + "history_clear", + 0, + history(), + SLOT( slotClear() ), + collection, + "clearHistoryAction" ); + connect( clearHistoryAction, SIGNAL( activated() ), SLOT( slotClearClipboard() ) ); + clearHistoryAction->setGroup( defaultGroup ); + configureAction = new KAction( i18n("&Configure Klipper..."), + "configure", + 0, + this, + SLOT( slotConfigure() ), + collection, + "configureAction" ); + configureAction->setGroup( defaultGroup ); + quitAction = new KAction( i18n("&Quit"), + "exit", + 0, + this, + SLOT( slotQuit() ), + collection, + "quitAction" ); + quitAction->setGroup( "exit" ); + myURLGrabber = 0L; + KConfig *kc = m_config; + readConfiguration( kc ); + setURLGrabberEnabled( bURLGrabber ); + + hideTimer = new QTime(); + showTimer = new QTime(); + + readProperties(m_config); + connect(kapp, SIGNAL(settingsChanged(int)), SLOT(slotSettingsChanged(int))); + + poll = new ClipboardPoll( this ); + connect( poll, SIGNAL( clipboardChanged( bool ) ), + this, SLOT( newClipData( bool ) ) ); + + m_pixmap = KSystemTray::loadIcon( "klipper" ); + adjustSize(); + + globalKeys = new KGlobalAccel(this); + KGlobalAccel* keys = globalKeys; +#include "klipperbindings.cpp" + // the keys need to be read from kdeglobals, not kickerrc --ellis, 22/9/02 + globalKeys->readSettings(); + globalKeys->updateConnections(); + toggleURLGrabAction->setShortcut(globalKeys->shortcut("Enable/Disable Clipboard Actions")); + + connect( toggleURLGrabAction, SIGNAL( toggled( bool )), + this, SLOT( setURLGrabberEnabled( bool ))); + + KlipperPopup* popup = history()->popup(); + connect ( history(), SIGNAL( topChanged() ), SLOT( slotHistoryTopChanged() ) ); + connect( popup, SIGNAL( aboutToHide() ), SLOT( slotStartHideTimer() ) ); + connect( popup, SIGNAL( aboutToShow() ), SLOT( slotStartShowTimer() ) ); + + popup->plugAction( toggleURLGrabAction ); + popup->plugAction( clearHistoryAction ); + popup->plugAction( configureAction ); + if ( !isApplet() ) { + popup->plugAction( quitAction ); + } + + QToolTip::add( this, i18n("Klipper - clipboard tool") ); +} + +KlipperWidget::~KlipperWidget() +{ + delete session_managed; + delete showTimer; + delete hideTimer; + delete myURLGrabber; + if( m_config != kapp->config()) + delete m_config; + qt_qclipboard_bailout_hack = false; +} + +void KlipperWidget::adjustSize() +{ + resize( m_pixmap.size() ); +} + +// DCOP +QString KlipperWidget::getClipboardContents() +{ + return getClipboardHistoryItem(0); +} + +// DCOP - don't call from Klipper itself +void KlipperWidget::setClipboardContents(QString s) +{ + Ignore lock( locklevel ); + updateTimestamp(); + HistoryStringItem* item = new HistoryStringItem( s ); + setClipboard( *item, Clipboard | Selection); + history()->insert( item ); +} + +// DCOP - don't call from Klipper itself +void KlipperWidget::clearClipboardContents() +{ + updateTimestamp(); + slotClearClipboard(); +} + +// DCOP - don't call from Klipper itself +void KlipperWidget::clearClipboardHistory() +{ + updateTimestamp(); + slotClearClipboard(); + history()->slotClear(); + saveSession(); +} + + +void KlipperWidget::mousePressEvent(QMouseEvent *e) +{ + if ( e->button() != LeftButton && e->button() != RightButton ) + return; + + // if we only hid the menu less than a third of a second ago, + // it's probably because the user clicked on the klipper icon + // to hide it, and therefore won't want it shown again. + if ( hideTimer->elapsed() > 300 ) { + slotPopupMenu(); + } +} + +void KlipperWidget::paintEvent(QPaintEvent *) +{ + QPainter p(this); + int x = (width() - m_pixmap.width()) / 2; + int y = (height() - m_pixmap.height()) / 2; + if ( x < 0 ) x = 0; + if ( y < 0 ) y = 0; + p.drawPixmap(x , y, m_pixmap); + p.end(); +} + +void KlipperWidget::slotStartHideTimer() +{ + hideTimer->start(); +} + +void KlipperWidget::slotStartShowTimer() +{ + showTimer->start(); +} + +void KlipperWidget::showPopupMenu( QPopupMenu *menu ) +{ + Q_ASSERT( menu != 0L ); + + QSize size = menu->sizeHint(); // geometry is not valid until it's shown + if (bPopupAtMouse) { + QPoint g = QCursor::pos(); + if ( size.height() < g.y() ) + menu->popup(QPoint( g.x(), g.y() - size.height())); + else + menu->popup(QPoint(g.x(), g.y())); + } else { + KWin::WindowInfo i = KWin::windowInfo( winId(), NET::WMGeometry ); + QRect g = i.geometry(); + QRect screen = KGlobalSettings::desktopGeometry(g.center()); + + if ( g.x()-screen.x() > screen.width()/2 && + g.y()-screen.y() + size.height() > screen.height() ) + menu->popup(QPoint( g.x(), g.y() - size.height())); + else + menu->popup(QPoint( g.x() + width(), g.y() + height())); + + // menu->exec(mapToGlobal(QPoint( width()/2, height()/2 ))); + } +} + +bool KlipperWidget::loadHistory() { + static const char* const failed_load_warning = + "Failed to load history resource. Clipboard history cannot be read."; + // don't use "appdata", klipper is also a kicker applet + QString history_file_name = ::locateLocal( "data", "klipper/history2.lst" ); + QFile history_file( history_file_name ); + bool oldfile = false; + if ( !history_file.exists() ) { // backwards compatibility + oldfile = true; + history_file_name = ::locateLocal( "data", "klipper/history.lst" ); + history_file.setName( history_file_name ); + if ( !history_file.exists() ) { + history_file_name = ::locateLocal( "data", "kicker/history.lst" ); + history_file.setName( history_file_name ); + if ( !history_file.exists() ) { + return false; + } + } + } + if ( !history_file.open( IO_ReadOnly ) ) { + kdWarning() << failed_load_warning << ": " << history_file.errorString() << endl; + return false; + } + QDataStream file_stream( &history_file ); + if( file_stream.atEnd()) { + kdWarning() << failed_load_warning << endl; + return false; + } + QDataStream* history_stream = &file_stream; + QByteArray data; + if( !oldfile ) { + Q_UINT32 crc; + file_stream >> crc >> data; + if( crc32( 0, reinterpret_cast<unsigned char *>( data.data() ), data.size() ) != crc ) { + kdWarning() << failed_load_warning << ": " << history_file.errorString() << endl; + return false; + } + + history_stream = new QDataStream( data, IO_ReadOnly ); + } + char* version; + *history_stream >> version; + delete[] version; + + // The list needs to be reversed, as it is saved + // youngest-first to keep the most important clipboard + // items at the top, but the history is created oldest + // first. + QPtrList<HistoryItem> reverseList; + for ( HistoryItem* item = HistoryItem::create( *history_stream ); + item; + item = HistoryItem::create( *history_stream ) ) + { + reverseList.prepend( item ); + } + + for ( HistoryItem* item = reverseList.first(); + item; + item = reverseList.next() ) + { + history()->forceInsert( item ); + } + + if ( !history()->empty() ) { + m_lastSelection = -1; + m_lastClipboard = -1; + setClipboard( *history()->first(), Clipboard | Selection ); + } + + if( history_stream != &file_stream ) + delete history_stream; + + return true; +} + +void KlipperWidget::saveHistory() { + static const char* const failed_save_warning = + "Failed to save history. Clipboard history cannot be saved."; + // don't use "appdata", klipper is also a kicker applet + QString history_file_name( ::locateLocal( "data", "klipper/history2.lst" ) ); + if ( history_file_name.isNull() || history_file_name.isEmpty() ) { + kdWarning() << failed_save_warning << endl; + return; + } + KSaveFile history_file( history_file_name ); + if ( history_file.status() != 0 ) { + kdWarning() << failed_save_warning << endl; + return; + } + QByteArray data; + QDataStream history_stream( data, IO_WriteOnly ); + history_stream << klipper_version; // const char* + for ( const HistoryItem* item = history()->first(); item; item = history()->next() ) { + history_stream << item; + } + Q_UINT32 crc = crc32( 0, reinterpret_cast<unsigned char *>( data.data() ), data.size() ); + *history_file.dataStream() << crc << data; +} + +void KlipperWidget::readProperties(KConfig *kc) +{ + QStringList dataList; + + history()->slotClear(); + + if (bKeepContents) { // load old clipboard if configured + if ( !loadHistory() ) { + // Try to load from the old config file. + // Remove this at some point. + KConfigGroupSaver groupSaver(kc, "General"); + dataList = kc->readListEntry("ClipboardData"); + + for (QStringList::ConstIterator it = dataList.end(); + it != dataList.begin(); + ) + { + history()->forceInsert( new HistoryStringItem( *( --it ) ) ); + } + + if ( !dataList.isEmpty() ) + { + m_lastSelection = -1; + m_lastClipboard = -1; + setClipboard( *history()->first(), Clipboard | Selection ); + } + } + } + +} + + +void KlipperWidget::readConfiguration( KConfig *kc ) +{ + kc->setGroup("General"); + bPopupAtMouse = kc->readBoolEntry("PopupAtMousePosition", false); + bKeepContents = kc->readBoolEntry("KeepClipboardContents", true); + bURLGrabber = kc->readBoolEntry("URLGrabberEnabled", false); + bReplayActionInHistory = kc->readBoolEntry("ReplayActionInHistory", false); + bNoNullClipboard = kc->readBoolEntry("NoEmptyClipboard", true); + bUseGUIRegExpEditor = kc->readBoolEntry("UseGUIRegExpEditor", true ); + history()->max_size( kc->readNumEntry("MaxClipItems", 7) ); + bIgnoreSelection = kc->readBoolEntry("IgnoreSelection", false); + bSynchronize = kc->readBoolEntry("Synchronize", false); + bSelectionTextOnly = kc->readBoolEntry("SelectionTextOnly",true); + bIgnoreImages = kc->readBoolEntry("IgnoreImages",true); +} + +void KlipperWidget::writeConfiguration( KConfig *kc ) +{ + kc->setGroup("General"); + kc->writeEntry("PopupAtMousePosition", bPopupAtMouse); + kc->writeEntry("KeepClipboardContents", bKeepContents); + kc->writeEntry("ReplayActionInHistory", bReplayActionInHistory); + kc->writeEntry("NoEmptyClipboard", bNoNullClipboard); + kc->writeEntry("UseGUIRegExpEditor", bUseGUIRegExpEditor); + kc->writeEntry("MaxClipItems", history()->max_size() ); + kc->writeEntry("IgnoreSelection", bIgnoreSelection); + kc->writeEntry("Synchronize", bSynchronize ); + kc->writeEntry("SelectionTextOnly", bSelectionTextOnly); + kc->writeEntry("TrackImages", bIgnoreImages); + kc->writeEntry("Version", klipper_version ); + + if ( myURLGrabber ) + myURLGrabber->writeConfiguration( kc ); + + kc->sync(); +} + +// save session on shutdown. Don't simply use the c'tor, as that may not be called. +void KlipperWidget::saveSession() +{ + if ( bKeepContents ) { // save the clipboard eventually + saveHistory(); + } +} + +void KlipperWidget::slotSettingsChanged( int category ) +{ + if ( category == (int) KApplication::SETTINGS_SHORTCUTS ) { + globalKeys->readSettings(); + globalKeys->updateConnections(); + toggleURLGrabAction->setShortcut(globalKeys->shortcut("Enable/Disable Clipboard Actions")); + } +} + +void KlipperWidget::disableURLGrabber() +{ + KMessageBox::information( 0L, + i18n( "You can enable URL actions later by right-clicking on the " + "Klipper icon and selecting 'Enable Actions'" ) ); + + setURLGrabberEnabled( false ); +} + +void KlipperWidget::slotConfigure() +{ + bool haveURLGrabber = bURLGrabber; + if ( !myURLGrabber ) { // temporary, for the config-dialog + setURLGrabberEnabled( true ); + readConfiguration( m_config ); + } + + ConfigDialog *dlg = new ConfigDialog( myURLGrabber->actionList(), + globalKeys, isApplet() ); + dlg->setKeepContents( bKeepContents ); + dlg->setPopupAtMousePos( bPopupAtMouse ); + dlg->setStripWhiteSpace( myURLGrabber->stripWhiteSpace() ); + dlg->setReplayActionInHistory( bReplayActionInHistory ); + dlg->setNoNullClipboard( bNoNullClipboard ); + dlg->setUseGUIRegExpEditor( bUseGUIRegExpEditor ); + dlg->setPopupTimeout( myURLGrabber->popupTimeout() ); + dlg->setMaxItems( history()->max_size() ); + dlg->setIgnoreSelection( bIgnoreSelection ); + dlg->setSynchronize( bSynchronize ); + dlg->setNoActionsFor( myURLGrabber->avoidWindows() ); + + if ( dlg->exec() == QDialog::Accepted ) { + bKeepContents = dlg->keepContents(); + bPopupAtMouse = dlg->popupAtMousePos(); + bReplayActionInHistory = dlg->replayActionInHistory(); + bNoNullClipboard = dlg->noNullClipboard(); + bIgnoreSelection = dlg->ignoreSelection(); + bSynchronize = dlg->synchronize(); + bUseGUIRegExpEditor = dlg->useGUIRegExpEditor(); + dlg->commitShortcuts(); + // the keys need to be written to kdeglobals, not kickerrc --ellis, 22/9/02 + globalKeys->writeSettings(0, true); + globalKeys->updateConnections(); + toggleURLGrabAction->setShortcut(globalKeys->shortcut("Enable/Disable Clipboard Actions")); + + myURLGrabber->setActionList( dlg->actionList() ); + myURLGrabber->setPopupTimeout( dlg->popupTimeout() ); + myURLGrabber->setStripWhiteSpace( dlg->stripWhiteSpace() ); + myURLGrabber->setAvoidWindows( dlg->noActionsFor() ); + + history()->max_size( dlg->maxItems() ); + + writeConfiguration( m_config ); + + } + setURLGrabberEnabled( haveURLGrabber ); + + delete dlg; +} + +void KlipperWidget::slotQuit() +{ + // If the menu was just opened, likely the user + // selected quit by accident while attempting to + // click the Klipper icon. + if ( showTimer->elapsed() < 300 ) { + return; + } + + saveSession(); + int autoStart = KMessageBox::questionYesNoCancel( 0L, i18n("Should Klipper start automatically\nwhen you login?"), i18n("Automatically Start Klipper?"), i18n("Start"), i18n("Do Not Start") ); + + KConfig *config = KGlobal::config(); + config->setGroup("General"); + if ( autoStart == KMessageBox::Yes ) { + config->writeEntry("AutoStart", true); + } else if ( autoStart == KMessageBox::No) { + config->writeEntry("AutoStart", false); + } else // cancel chosen don't quit + return; + config->sync(); + + kapp->quit(); + +} + +void KlipperWidget::slotPopupMenu() { + KlipperPopup* popup = history()->popup(); + popup->ensureClean(); + showPopupMenu( popup ); +} + + +void KlipperWidget::slotRepeatAction() +{ + if ( !myURLGrabber ) { + myURLGrabber = new URLGrabber( m_config ); + connect( myURLGrabber, SIGNAL( sigPopup( QPopupMenu * )), + SLOT( showPopupMenu( QPopupMenu * )) ); + connect( myURLGrabber, SIGNAL( sigDisablePopup() ), + this, SLOT( disableURLGrabber() ) ); + } + + const HistoryStringItem* top = dynamic_cast<const HistoryStringItem*>( history()->first() ); + if ( top ) { + myURLGrabber->invokeAction( top->text() ); + } +} + +void KlipperWidget::setURLGrabberEnabled( bool enable ) +{ + if (enable != bURLGrabber) { + bURLGrabber = enable; + KConfig *kc = m_config; + kc->setGroup("General"); + kc->writeEntry("URLGrabberEnabled", bURLGrabber); + m_lastURLGrabberTextSelection = QString(); + m_lastURLGrabberTextClipboard = QString(); + } + + toggleURLGrabAction->setChecked( enable ); + + if ( !bURLGrabber ) { + delete myURLGrabber; + myURLGrabber = 0L; + toggleURLGrabAction->setText(i18n("Enable &Actions")); + } + + else { + toggleURLGrabAction->setText(i18n("&Actions Enabled")); + if ( !myURLGrabber ) { + myURLGrabber = new URLGrabber( m_config ); + connect( myURLGrabber, SIGNAL( sigPopup( QPopupMenu * )), + SLOT( showPopupMenu( QPopupMenu * )) ); + connect( myURLGrabber, SIGNAL( sigDisablePopup() ), + this, SLOT( disableURLGrabber() ) ); + } + } +} + +void KlipperWidget::toggleURLGrabber() +{ + setURLGrabberEnabled( !bURLGrabber ); +} + +void KlipperWidget::slotHistoryTopChanged() { + if ( locklevel ) { + return; + } + + const HistoryItem* topitem = history()->first(); + if ( topitem ) { + setClipboard( *topitem, Clipboard | Selection ); + } + if ( bReplayActionInHistory && bURLGrabber ) { + slotRepeatAction(); + } + +} + +void KlipperWidget::slotClearClipboard() +{ + Ignore lock( locklevel ); + + clip->clear(QClipboard::Selection); + clip->clear(QClipboard::Clipboard); + + +} + + +//XXX: Should die, and the DCOP signal handled sensible. +QString KlipperWidget::clipboardContents( bool * /*isSelection*/ ) +{ + kdWarning() << "Obsolete function called. Please fix" << endl; + +#if 0 + bool selection = true; + QMimeSource* data = clip->data(QClipboard::Selection); + + if ( data->serialNumber() == m_lastSelection ) + { + QString clipContents = clip->text(QClipboard::Clipboard); + if ( clipContents != m_lastClipboard ) + { + contents = clipContents; + selection = false; + } + else + selection = true; + } + + if ( isSelection ) + *isSelection = selection; + +#endif + + return 0; +} + +void KlipperWidget::applyClipChanges( const QMimeSource& clipData ) +{ + if ( locklevel ) + return; + Ignore lock( locklevel ); + history()->insert( HistoryItem::create( clipData ) ); + +} + +void KlipperWidget::newClipData( bool selectionMode ) +{ + if ( locklevel ) { + return; + } + + if( blockFetchingNewData()) + return; + + checkClipData( selectionMode ); + +} + +void KlipperWidget::clipboardSignalArrived( bool selectionMode ) +{ + if ( locklevel ) { + return; + } + if( blockFetchingNewData()) + return; + + updateTimestamp(); + checkClipData( selectionMode ); +} + +// Protection against too many clipboard data changes. Lyx responds to clipboard data +// requests with setting new clipboard data, so if Lyx takes over clipboard, +// Klipper notices, requests this data, this triggers "new" clipboard contents +// from Lyx, so Klipper notices again, requests this data, ... you get the idea. +const int MAX_CLIPBOARD_CHANGES = 10; // max changes per second + +bool KlipperWidget::blockFetchingNewData() +{ +// Hacks for #85198 and #80302. +// #85198 - block fetching new clipboard contents if Shift is pressed and mouse is not, +// this may mean the user is doing selection using the keyboard, in which case +// it's possible the app sets new clipboard contents after every change - Klipper's +// history would list them all. +// #80302 - OOo (v1.1.3 at least) has a bug that if Klipper requests its clipboard contents +// while the user is doing a selection using the mouse, OOo stops updating the clipboard +// contents, so in practice it's like the user has selected only the part which was +// selected when Klipper asked first. + ButtonState buttonstate = kapp->keyboardMouseState(); + if( ( buttonstate & ( ShiftButton | LeftButton )) == ShiftButton // #85198 + || ( buttonstate & LeftButton ) == LeftButton ) { // #80302 + m_pendingContentsCheck = true; + m_pendingCheckTimer.start( 100, true ); + return true; + } + m_pendingContentsCheck = false; + if( ++m_overflowCounter > MAX_CLIPBOARD_CHANGES ) + return true; + return false; +} + +void KlipperWidget::slotCheckPending() +{ + if( !m_pendingContentsCheck ) + return; + m_pendingContentsCheck = false; // blockFetchingNewData() will be called again + updateTimestamp(); + newClipData( true ); // always selection +} + +void KlipperWidget::checkClipData( bool selectionMode ) +{ + if ( ignoreClipboardChanges() ) // internal to klipper, ignoring QSpinBox selections + { + // keep our old clipboard, thanks + // This won't quite work, but it's close enough for now. + // The trouble is that the top selection =! top clipboard + // but we don't track that yet. We will.... + const HistoryItem* top = history()->first(); + if ( top ) { + setClipboard( *top, selectionMode ? Selection : Clipboard); + } + return; + } + +// debug code + +// debug code +#ifdef NOISY_KLIPPER + kdDebug() << "Checking clip data" << endl; +#endif +#if 0 + kdDebug() << "====== c h e c k C l i p D a t a ============================" + << kdBacktrace() + << "====== c h e c k C l i p D a t a ============================" + << endl;; +#endif +#if 0 + if ( sender() ) { + kdDebug() << "sender=" << sender()->name() << endl; + } else { + kdDebug() << "no sender" << endl; + } +#endif +#if 0 + kdDebug() << "\nselectionMode=" << selectionMode + << "\nserialNo=" << clip->data()->serialNumber() << " (sel,cli)=(" << m_lastSelection << "," << m_lastClipboard << ")" + << "\nowning (sel,cli)=(" << clip->ownsSelection() << "," << clip->ownsClipboard() << ")" + << "\ntext=" << clip->text( selectionMode ? QClipboard::Selection : QClipboard::Clipboard) << endl; + +#endif +#if 0 + const char *format; + int i = 0; + while ( (format = clip->data()->format( i++ )) ) + { + qDebug( " format: %s", format); + } +#endif + QMimeSource* data = clip->data( selectionMode ? QClipboard::Selection : QClipboard::Clipboard ); + if ( !data ) { + kdWarning("No data in clipboard. This not not supposed to happen." ); + return; + } + + int lastSerialNo = selectionMode ? m_lastSelection : m_lastClipboard; + bool changed = data->serialNumber() != lastSerialNo; + bool clipEmpty = ( data->format() == 0L ); + + if ( changed && clipEmpty && bNoNullClipboard ) { + const HistoryItem* top = history()->first(); + if ( top ) { + // keep old clipboard after someone set it to null +#ifdef NOISY_KLIPPER + kdDebug() << "Resetting clipboard (Prevent empty clipboard)" << endl; +#endif + setClipboard( *top, selectionMode ? Selection : Clipboard ); + } + return; + } + + // this must be below the "bNoNullClipboard" handling code! + // XXX: I want a better handling of selection/clipboard in general. + // XXX: Order sensitive code. Must die. + if ( selectionMode && bIgnoreSelection ) + return; + + if( selectionMode && bSelectionTextOnly && !QTextDrag::canDecode( data )) + return; + + if( KURLDrag::canDecode( data )) + ; // ok + else if( QTextDrag::canDecode( data )) + ; // ok + else if( QImageDrag::canDecode( data )) + { +// Limit mimetypes that are tracked by Klipper (this is basically a workaround +// for #109032). Can't add UI in 3.5 because of string freeze, and I'm not sure +// if this actually needs to be more configurable than only text vs all klipper knows. + if( bIgnoreImages ) + return; + else { + // ok + } + } + else // unknown, ignore + return; + + // store old contents: + if ( selectionMode ) + m_lastSelection = data->serialNumber(); + else + m_lastClipboard = data->serialNumber(); + + QString& lastURLGrabberText = selectionMode + ? m_lastURLGrabberTextSelection : m_lastURLGrabberTextClipboard; + if( QTextDrag::canDecode( data )) + { + if ( bURLGrabber && myURLGrabber ) + { + QString text; + QTextDrag::decode( data, text ); + // Make sure URLGrabber doesn't repeat all the time if klipper reads the same + // text all the time (e.g. because XFixes is not available and the application + // has broken TIMESTAMP target). Using most recent history item may not always + // work. + if ( text != lastURLGrabberText ) + { + lastURLGrabberText = text; + if ( myURLGrabber->checkNewData( text ) ) + { + return; // don't add into the history + } + } + } + else + lastURLGrabberText = QString(); + } + else + lastURLGrabberText = QString(); + + if (changed) { + applyClipChanges( *data ); +#ifdef NOISY_KLIPPER + kdDebug() << "Synchronize?" << ( bSynchronize ? "yes" : "no" ) << endl; +#endif + if ( bSynchronize ) { + const HistoryItem* topItem = history()->first(); + if ( topItem ) { + setClipboard( *topItem, selectionMode ? Clipboard : Selection ); + } + } + } + +} + +void KlipperWidget::setClipboard( const HistoryItem& item, int mode ) +{ + Ignore lock( locklevel ); + + Q_ASSERT( ( mode & 1 ) == 0 ); // Warn if trying to pass a boolean as a mode. + + if ( mode & Selection ) { +#ifdef NOSIY_KLIPPER + kdDebug() << "Setting selection to <" << item.text() << ">" << endl; +#endif + clip->setData( item.mimeSource(), QClipboard::Selection ); + m_lastSelection = clip->data()->serialNumber(); + } + if ( mode & Clipboard ) { +#ifdef NOSIY_KLIPPER + kdDebug() << "Setting clipboard to <" << item.text() << ">" << endl; +#endif + clip->setData( item.mimeSource(), QClipboard::Clipboard ); + m_lastClipboard = clip->data()->serialNumber(); + } + +} + +void KlipperWidget::slotClearOverflow() +{ + if( m_overflowCounter > MAX_CLIPBOARD_CHANGES ) { + kdDebug() << "App owning the clipboard/selection is lame" << endl; + // update to the latest data - this unfortunately may trigger the problem again + newClipData( true ); // Always the selection. + } + m_overflowCounter = 0; +} + +QStringList KlipperWidget::getClipboardHistoryMenu() +{ + QStringList menu; + for ( const HistoryItem* item = history()->first(); item; item = history()->next() ) { + menu << item->text(); + } + return menu; +} + +QString KlipperWidget::getClipboardHistoryItem(int i) +{ + for ( const HistoryItem* item = history()->first(); item; item = history()->next() , i-- ) { + if ( i == 0 ) { + return item->text(); + } + } + return QString::null; + +} + +// +// changing a spinbox in klipper's config-dialog causes the lineedit-contents +// of the spinbox to be selected and hence the clipboard changes. But we don't +// want all those items in klipper's history. See #41917 +// +bool KlipperWidget::ignoreClipboardChanges() const +{ + QWidget *focusWidget = qApp->focusWidget(); + if ( focusWidget ) + { + if ( focusWidget->inherits( "QSpinBox" ) || + (focusWidget->parentWidget() && + focusWidget->inherits("QLineEdit") && + focusWidget->parentWidget()->inherits("QSpinWidget")) ) + { + return true; + } + } + + return false; +} + +// QClipboard uses qt_x_time as the timestamp for selection operations. +// It is updated mainly from user actions, but Klipper polls the clipboard +// without any user action triggering it, so qt_x_time may be old, +// which could possibly lead to QClipboard reporting empty clipboard. +// Therefore, qt_x_time needs to be updated to current X server timestamp. + +// Call KApplication::updateUserTime() only from functions that are +// called from outside (DCOP), or from QTimer timeout ! + +extern Time qt_x_time; +extern Time qt_x_user_time; + +static Time next_x_time; +static Bool update_x_time_predicate( Display*, XEvent* event, XPointer ) +{ + if( next_x_time != CurrentTime ) + return False; + // from qapplication_x11.cpp + switch ( event->type ) { + case ButtonPress: + // fallthrough intended + case ButtonRelease: + next_x_time = event->xbutton.time; + break; + case MotionNotify: + next_x_time = event->xmotion.time; + break; + case KeyPress: + // fallthrough intended + case KeyRelease: + next_x_time = event->xkey.time; + break; + case PropertyNotify: + next_x_time = event->xproperty.time; + break; + case EnterNotify: + case LeaveNotify: + next_x_time = event->xcrossing.time; + break; + case SelectionClear: + next_x_time = event->xselectionclear.time; + break; + default: + break; + } + return False; +} + +void KlipperWidget::updateTimestamp() +{ // Qt3.3.0 and 3.3.1 use qt_x_user_time for clipboard operations + Time& time = ( strcmp( qVersion(), "3.3.1" ) == 0 + || strcmp( qVersion(), "3.3.0" ) == 0 ) + ? qt_x_user_time : qt_x_time; + static QWidget* w = 0; + if ( !w ) + w = new QWidget; + unsigned char data[ 1 ]; + XChangeProperty( qt_xdisplay(), w->winId(), XA_ATOM, XA_ATOM, 8, PropModeAppend, data, 1 ); + next_x_time = CurrentTime; + XEvent dummy; + XCheckIfEvent( qt_xdisplay(), &dummy, update_x_time_predicate, NULL ); + if( next_x_time == CurrentTime ) + { + XSync( qt_xdisplay(), False ); + XCheckIfEvent( qt_xdisplay(), &dummy, update_x_time_predicate, NULL ); + } + Q_ASSERT( next_x_time != CurrentTime ); + time = next_x_time; + XEvent ev; // remove the PropertyNotify event from the events queue + XWindowEvent( qt_xdisplay(), w->winId(), PropertyChangeMask, &ev ); +} + +static const char * const description = + I18N_NOOP("KDE cut & paste history utility"); + +void KlipperWidget::createAboutData() +{ + about_data = new KAboutData("klipper", I18N_NOOP("Klipper"), + klipper_version, description, KAboutData::License_GPL, + "(c) 1998, Andrew Stanley-Jones\n" + "1998-2002, Carsten Pfeiffer\n" + "2001, Patrick Dubroy"); + + about_data->addAuthor("Carsten Pfeiffer", + I18N_NOOP("Author"), + "[email protected]"); + + about_data->addAuthor("Andrew Stanley-Jones", + I18N_NOOP( "Original Author" ), + "[email protected]"); + + about_data->addAuthor("Patrick Dubroy", + I18N_NOOP("Contributor"), + "[email protected]"); + + about_data->addAuthor( "Luboš Luňák", + I18N_NOOP("Bugfixes and optimizations"), + "[email protected]"); + + about_data->addAuthor( "Esben Mose Hansen", + I18N_NOOP("Maintainer"), + "[email protected]"); +} + +void KlipperWidget::destroyAboutData() +{ + delete about_data; + about_data = NULL; +} + +KAboutData* KlipperWidget::about_data; + +KAboutData* KlipperWidget::aboutData() +{ + return about_data; +} + +Klipper::Klipper( QWidget* parent ) + : KlipperWidget( parent, kapp->config()) +{ +} + +// this sucks ... KUniqueApplication registers itself as 'klipper' +// for the unique-app detection calls (and it shouldn't use that name IMHO) +// but in Klipper it's not KUniqueApplication class who handles +// the DCOP calls, but an instance of class Klipper, registered as 'klipper' +// this below avoids a warning when KUniqueApplication wouldn't otherwise +// find newInstance() (which doesn't do anything in Klipper anyway) +int Klipper::newInstance() +{ + kapp->dcopClient()->setPriorityCall(false); // Allow other dcop calls + return 0; +} + +// this is used for quiting klipper process, if klipper is being started as an applet +// (AKA ugly hack) +void Klipper::quitProcess() +{ + kapp->dcopClient()->detach(); + kapp->quit(); +} + +static void ensureGlobalSyncOff(KConfig* config) { + config->setGroup("General"); + if ( config->readBoolEntry( "SynchronizeClipboardAndSelection" ) ) { + kdDebug() << "Shutting off global synchronization" << endl; + config->writeEntry("SynchronizeClipboardAndSelection", + false, + true, + true ); + config->sync(); + KClipboardSynchronizer::setSynchronizing( false ); + KClipboardSynchronizer::setReverseSynchronizing( false ); + KIPC::sendMessageAll( KIPC::ClipboardConfigChanged, 0 ); + } + +} + +#include "toplevel.moc" diff --git a/klipper/toplevel.h b/klipper/toplevel.h new file mode 100644 index 000000000..735698487 --- /dev/null +++ b/klipper/toplevel.h @@ -0,0 +1,221 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) by Andrew Stanley-Jones + Copyright (C) 2004 Esben Mose Hansen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _TOPLEVEL_H_ +#define _TOPLEVEL_H_ + +#include <kapplication.h> +#include <kglobalaccel.h> +#include <kpopupmenu.h> +#include <qmap.h> +#include <qpixmap.h> +#include <dcopobject.h> +#include <qtimer.h> + +class QClipboard; +class KToggleAction; +class KAboutData; +class URLGrabber; +class ClipboardPoll; +class QTime; +class History; +class KAction; +class QMimeSource; +class HistoryItem; +class KlipperSessionManaged; + +class KlipperWidget : public QWidget, public DCOPObject +{ + Q_OBJECT + K_DCOP + +k_dcop: + QString getClipboardContents(); + void setClipboardContents(QString s); + void clearClipboardContents(); + void clearClipboardHistory(); + QStringList getClipboardHistoryMenu(); + QString getClipboardHistoryItem(int i); + +public: + KlipperWidget( QWidget *parent, KConfig* config ); + ~KlipperWidget(); + + virtual void adjustSize(); + KGlobalAccel *globalKeys; + + /** + * Get clipboard history (the "document") + */ + History* history() { return m_history; } + + static void updateTimestamp(); + static void createAboutData(); + static void destroyAboutData(); + static KAboutData* aboutData(); + +public slots: + void saveSession(); + void slotSettingsChanged( int category ); + void slotHistoryTopChanged(); + void slotConfigure(); + +protected: + /** + * The selection modes + * + * Don't use 1, as I use that as a guard against passing + * a boolean true as a mode. + */ + enum SelectionMode { Clipboard = 2, Selection = 4 }; + + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + void readProperties(KConfig *); + void readConfiguration(KConfig *); + + /** + * Loads history from disk. + */ + bool loadHistory(); + + /** + * Save history to disk + */ + void saveHistory(); + + void writeConfiguration(KConfig *); + /** + * @returns the contents of the selection or, if empty, the contents of + * the clipboard. + */ + QString clipboardContents( bool *isSelection = 0L ); + + void removeFromHistory( const QString& text ); + void setEmptyClipboard(); + + void clipboardSignalArrived( bool selectionMode ); + + /** + * Check data in clipboard, and if it passes these checks, + * store the data in the clipboard history. + */ + void checkClipData( bool selectionMode ); + + /** + * Enter clipboard data in the history. + */ + void applyClipChanges( const QMimeSource& data ); + + void setClipboard( const HistoryItem& item, int mode ); + bool ignoreClipboardChanges() const; + + KConfig* config() const { return m_config; } + bool isApplet() const { return m_config != kapp->config(); } + +protected slots: + void slotPopupMenu(); + void showPopupMenu( QPopupMenu * ); + void slotRepeatAction(); + void setURLGrabberEnabled( bool ); + void toggleURLGrabber(); + void disableURLGrabber(); + +private slots: + void newClipData( bool selectionMode ); + void slotClearClipboard(); + + void slotSelectionChanged() { + clipboardSignalArrived( true ); + } + void slotClipboardChanged() { + clipboardSignalArrived( false ); + } + + void slotQuit(); + void slotStartHideTimer(); + void slotStartShowTimer(); + + void slotClearOverflow(); + void slotCheckPending(); + +private: + + QClipboard *clip; + + QTime *hideTimer; + QTime *showTimer; + + QMimeSource* m_lastClipdata; + int m_lastClipboard; + int m_lastSelection; + History* m_history; + int m_overflowCounter; + KToggleAction *toggleURLGrabAction; + KAction* clearHistoryAction; + KAction* configureAction; + KAction* quitAction; + QPixmap m_pixmap; + bool bPopupAtMouse :1; + bool bKeepContents :1; + bool bURLGrabber :1; + bool bReplayActionInHistory :1; + bool bUseGUIRegExpEditor :1; + bool bNoNullClipboard :1; + bool bTearOffHandle :1; + bool bIgnoreSelection :1; + bool bSynchronize :1; + bool bSelectionTextOnly :1; + bool bIgnoreImages :1; + + /** + * Avoid reacting to our own changes, using this + * lock. + * Don't manupulate this object directly... use the Ignore struct + * instead + */ + int locklevel; + + URLGrabber *myURLGrabber; + QString m_lastURLGrabberTextSelection; + QString m_lastURLGrabberTextClipboard; + KConfig* m_config; + QTimer m_overflowClearTimer; + QTimer m_pendingCheckTimer; + bool m_pendingContentsCheck; + ClipboardPoll* poll; + static KAboutData* about_data; + + bool blockFetchingNewData(); + KlipperSessionManaged* session_managed; +}; + +class Klipper : public KlipperWidget +{ + Q_OBJECT + K_DCOP +k_dcop: + int newInstance(); + void quitProcess(); // not ASYNC +public: + Klipper( QWidget* parent = NULL ); +}; + +#endif diff --git a/klipper/urlgrabber.cpp b/klipper/urlgrabber.cpp new file mode 100644 index 000000000..352f3ab74 --- /dev/null +++ b/klipper/urlgrabber.cpp @@ -0,0 +1,497 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) (C) 2000,2001,2002 by Carsten Pfeiffer <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qcursor.h> +#include <qtimer.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kdialogbase.h> +#include <ktextedit.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kservice.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <netwm.h> +#include <kstringhandler.h> +#include <kmacroexpander.h> + +#include <stdlib.h> // For getenv() + +#include "urlgrabber.h" + +// TODO: +// - script-interface? +// - Bug in KPopupMenu::clear() (insertTitle() doesn't go away sometimes) + +#define URL_EDIT_ITEM 10 +#define DO_NOTHING_ITEM 11 +#define DISABLE_POPUP 12 + +URLGrabber::URLGrabber( KConfig* config ) + : m_config( config ) +{ + if( m_config == NULL ) + m_config = kapp->config(); + myCurrentAction = 0L; + myMenu = 0L; + myPopupKillTimeout = 8; + m_stripWhiteSpace = true; + + myActions = new ActionList(); + myActions->setAutoDelete( true ); + myMatches.setAutoDelete( false ); + + readConfiguration( m_config ); + + myPopupKillTimer = new QTimer( this ); + connect( myPopupKillTimer, SIGNAL( timeout() ), + SLOT( slotKillPopupMenu() )); + + // testing + /* + ClipAction *action; + action = new ClipAction( "^http:\\/\\/", "Web-URL" ); + action->addCommand("kfmclient exec %s", "Open with Konqi", true); + action->addCommand("netscape -no-about-splash -remote \"openURL(%s, new-window)\"", "Open with Netscape", true); + myActions->append( action ); + + action = new ClipAction( "^mailto:", "Mail-URL" ); + action->addCommand("kmail --composer %s", "Launch kmail", true); + myActions->append( action ); + + action = new ClipAction( "^\\/.+\\.jpg$", "Jpeg-Image" ); + action->addCommand("kuickshow %s", "Launch KuickShow", true); + action->addCommand("kview %s", "Launch KView", true); + myActions->append( action ); + */ +} + + +URLGrabber::~URLGrabber() +{ + delete myActions; +} + +// +// Called from Klipper::slotRepeatAction, i.e. by pressing Ctrl-Alt-R +// shortcut. I.e. never from clipboard monitoring +// +void URLGrabber::invokeAction( const QString& clip ) +{ + if ( !clip.isEmpty() ) + myClipData = clip; + if ( m_stripWhiteSpace ) + myClipData = myClipData.stripWhiteSpace(); + + actionMenu( false ); +} + + +void URLGrabber::setActionList( ActionList *list ) +{ + delete myActions; + myActions = list; +} + + +const ActionList& URLGrabber::matchingActions( const QString& clipData ) +{ + myMatches.clear(); + ClipAction *action = 0L; + ActionListIterator it( *myActions ); + for ( action = it.current(); action; action = ++it ) { + if ( action->matches( clipData ) ) + myMatches.append( action ); + } + + return myMatches; +} + + +bool URLGrabber::checkNewData( const QString& clipData ) +{ + // kdDebug() << "** checking new data: " << clipData << endl; + myClipData = clipData; + if ( m_stripWhiteSpace ) + myClipData = myClipData.stripWhiteSpace(); + + if ( myActions->isEmpty() ) + return false; + + actionMenu( true ); // also creates myMatches + + return ( !myMatches.isEmpty() && + (!m_config->readBoolEntry("Put Matching URLs in history", true))); +} + + +void URLGrabber::actionMenu( bool wm_class_check ) +{ + if ( myClipData.isEmpty() ) + return; + + ActionListIterator it( matchingActions( myClipData ) ); + ClipAction *action = 0L; + ClipCommand *command = 0L; + + if ( it.count() > 0 ) { + // don't react on konqi's/netscape's urls... + if ( wm_class_check && isAvoidedWindow() ) + return; + + QString item; + myCommandMapper.clear(); + + myPopupKillTimer->stop(); + delete myMenu; + myMenu = new KPopupMenu; + connect( myMenu, SIGNAL( activated( int )), + SLOT( slotItemSelected( int ))); + + for ( action = it.current(); action; action = ++it ) { + QPtrListIterator<ClipCommand> it2( action->commands() ); + if ( it2.count() > 0 ) + myMenu->insertTitle( SmallIcon( "klipper" ), action->description() + + i18n(" - Actions For: ") + + KStringHandler::csqueeze(myClipData, 45)); + for ( command = it2.current(); command; command = ++it2 ) { + item = command->description; + if ( item.isEmpty() ) + item = command->command; + + int id; + if ( command->pixmap.isEmpty() ) + id = myMenu->insertItem( item ); + else + id = myMenu->insertItem( SmallIcon(command->pixmap), item); + myCommandMapper.insert( id, command ); + } + } + + // only insert this when invoked via clipboard monitoring, not from an + // explicit Ctrl-Alt-R + if ( wm_class_check ) + { + myMenu->insertSeparator(); + myMenu->insertItem( i18n( "Disable This Popup" ), DISABLE_POPUP ); + } + myMenu->insertSeparator(); + // add an edit-possibility + myMenu->insertItem( SmallIcon("edit"), i18n("&Edit Contents..."), + URL_EDIT_ITEM ); + myMenu->insertItem( SmallIconSet("cancel"), i18n("&Cancel"), DO_NOTHING_ITEM ); + + if ( myPopupKillTimeout > 0 ) + myPopupKillTimer->start( 1000 * myPopupKillTimeout, true ); + + emit sigPopup( myMenu ); + } +} + + +void URLGrabber::slotItemSelected( int id ) +{ + myMenu->hide(); // deleted by the timer or the next action + + switch ( id ) { + case -1: + case DO_NOTHING_ITEM: + break; + case URL_EDIT_ITEM: + editData(); + break; + case DISABLE_POPUP: + emit sigDisablePopup(); + break; + default: + ClipCommand *command = myCommandMapper.find( id ); + if ( !command ) + qWarning("Klipper: can't find associated action"); + else + execute( command ); + } +} + + +void URLGrabber::execute( const struct ClipCommand *command ) const +{ + if ( command->isEnabled ) { + QMap<QChar,QString> map; + map.insert( 's', myClipData ); + QString cmdLine = KMacroExpander::expandMacrosShellQuote( command->command, map ); + + if ( cmdLine.isEmpty() ) + return; + + KProcess proc; + const char *shell = getenv("KLIPPER_SHELL"); + if (shell==NULL) shell = getenv("SHELL"); + proc.setUseShell(true,shell); + + proc << cmdLine.stripWhiteSpace(); + + if ( !proc.start(KProcess::DontCare, KProcess::NoCommunication )) + qWarning("Klipper: Couldn't start process!"); + } +} + + +void URLGrabber::editData() +{ + myPopupKillTimer->stop(); + KDialogBase *dlg = new KDialogBase( 0, 0, true, + i18n("Edit Contents"), + KDialogBase::Ok | KDialogBase::Cancel); + KTextEdit *edit = new KTextEdit( dlg ); + edit->setText( myClipData ); + edit->setFocus(); + edit->setMinimumSize( 300, 40 ); + dlg->setMainWidget( edit ); + dlg->adjustSize(); + + if ( dlg->exec() == QDialog::Accepted ) { + myClipData = edit->text(); + delete dlg; + QTimer::singleShot( 0, this, SLOT( slotActionMenu() ) ); + } + else + { + delete dlg; + myMenu->deleteLater(); + myMenu = 0L; + } +} + + +void URLGrabber::readConfiguration( KConfig *kc ) +{ + myActions->clear(); + kc->setGroup( "General" ); + int num = kc->readNumEntry("Number of Actions", 0); + myAvoidWindows = kc->readListEntry("No Actions for WM_CLASS"); + myPopupKillTimeout = kc->readNumEntry( "Timeout for Action popups (seconds)", 8 ); + m_stripWhiteSpace = kc->readBoolEntry("Strip Whitespace before exec", true); + QString group; + for ( int i = 0; i < num; i++ ) { + group = QString("Action_%1").arg( i ); + kc->setGroup( group ); + myActions->append( new ClipAction( kc ) ); + } +} + + +void URLGrabber::writeConfiguration( KConfig *kc ) +{ + kc->setGroup( "General" ); + kc->writeEntry( "Number of Actions", myActions->count() ); + kc->writeEntry( "Timeout for Action popups (seconds)", myPopupKillTimeout); + kc->writeEntry( "No Actions for WM_CLASS", myAvoidWindows ); + kc->writeEntry( "Strip Whitespace before exec", m_stripWhiteSpace ); + + ActionListIterator it( *myActions ); + ClipAction *action; + + int i = 0; + QString group; + while ( (action = it.current()) ) { + group = QString("Action_%1").arg( i ); + kc->setGroup( group ); + action->save( kc ); + ++i; + ++it; + } +} + +// find out whether the active window's WM_CLASS is in our avoid-list +// digged a little bit in netwm.cpp +bool URLGrabber::isAvoidedWindow() const +{ + Display *d = qt_xdisplay(); + static Atom wm_class = XInternAtom( d, "WM_CLASS", true ); + static Atom active_window = XInternAtom( d, "_NET_ACTIVE_WINDOW", true ); + Atom type_ret; + int format_ret; + unsigned long nitems_ret, unused; + unsigned char *data_ret; + long BUFSIZE = 2048; + bool ret = false; + Window active = 0L; + QString wmClass; + + // get the active window + if (XGetWindowProperty(d, DefaultRootWindow( d ), active_window, 0l, 1l, + False, XA_WINDOW, &type_ret, &format_ret, + &nitems_ret, &unused, &data_ret) + == Success) { + if (type_ret == XA_WINDOW && format_ret == 32 && nitems_ret == 1) { + active = *((Window *) data_ret); + } + XFree(data_ret); + } + if ( !active ) + return false; + + // get the class of the active window + if ( XGetWindowProperty(d, active, wm_class, 0L, BUFSIZE, False, XA_STRING, + &type_ret, &format_ret, &nitems_ret, + &unused, &data_ret ) == Success) { + if ( type_ret == XA_STRING && format_ret == 8 && nitems_ret > 0 ) { + wmClass = QString::fromUtf8( (const char *) data_ret ); + ret = (myAvoidWindows.find( wmClass ) != myAvoidWindows.end()); + } + + XFree( data_ret ); + } + + return ret; +} + + +void URLGrabber::slotKillPopupMenu() +{ + if ( myMenu && myMenu->isVisible() ) + { + if ( myMenu->geometry().contains( QCursor::pos() ) && + myPopupKillTimeout > 0 ) + { + myPopupKillTimer->start( 1000 * myPopupKillTimeout, true ); + return; + } + } + + delete myMenu; + myMenu = 0L; +} + +/////////////////////////////////////////////////////////////////////////// +//////// + +ClipCommand::ClipCommand(const QString &_command, const QString &_description, + bool _isEnabled, const QString &_icon) + : command(_command), + description(_description), + isEnabled(_isEnabled) +{ + int len = command.find(" "); + if (len == -1) + len = command.length(); + + if (!_icon.isEmpty()) + pixmap = _icon; + else + { + KService::Ptr service= KService::serviceByDesktopName(command.left(len)); + if (service) + pixmap = service->icon(); + else + pixmap = QString::null; + } +} + + +ClipAction::ClipAction( const QString& regExp, const QString& description ) + : myRegExp( regExp ), myDescription( description ) +{ + myCommands.setAutoDelete( true ); +} + + +ClipAction::ClipAction( const ClipAction& action ) +{ + myCommands.setAutoDelete( true ); + myRegExp = action.myRegExp; + myDescription = action.myDescription; + + ClipCommand *command = 0L; + QPtrListIterator<ClipCommand> it( myCommands ); + for ( ; it.current(); ++it ) { + command = it.current(); + addCommand(command->command, command->description, command->isEnabled); + } +} + + +ClipAction::ClipAction( KConfig *kc ) + : myRegExp( kc->readEntry( "Regexp" ) ), + myDescription( kc->readEntry( "Description" ) ) +{ + myCommands.setAutoDelete( true ); + int num = kc->readNumEntry( "Number of commands" ); + + // read the commands + QString actionGroup = kc->group(); + for ( int i = 0; i < num; i++ ) { + QString group = actionGroup + "/Command_%1"; + kc->setGroup( group.arg( i ) ); + + addCommand( kc->readPathEntry( "Commandline" ), + kc->readEntry( "Description" ), // i18n'ed + kc->readBoolEntry( "Enabled" ), + kc->readEntry( "Icon") ); + } +} + + +ClipAction::~ClipAction() +{ +} + + +void ClipAction::addCommand( const QString& command, + const QString& description, bool enabled, const QString& icon ) +{ + if ( command.isEmpty() ) + return; + + struct ClipCommand *cmd = new ClipCommand( command, description, enabled, icon ); + // cmd->id = myCommands.count(); // superfluous, I think... + myCommands.append( cmd ); +} + + +// precondition: we're in the correct action's group of the KConfig object +void ClipAction::save( KConfig *kc ) const +{ + kc->writeEntry( "Description", description() ); + kc->writeEntry( "Regexp", regExp() ); + kc->writeEntry( "Number of commands", myCommands.count() ); + + QString actionGroup = kc->group(); + struct ClipCommand *cmd; + QPtrListIterator<struct ClipCommand> it( myCommands ); + + // now iterate over all commands of this action + int i = 0; + while ( (cmd = it.current()) ) { + QString group = actionGroup + "/Command_%1"; + kc->setGroup( group.arg( i ) ); + + kc->writePathEntry( "Commandline", cmd->command ); + kc->writeEntry( "Description", cmd->description ); + kc->writeEntry( "Enabled", cmd->isEnabled ); + + ++i; + ++it; + } +} + +#include "urlgrabber.moc" diff --git a/klipper/urlgrabber.h b/klipper/urlgrabber.h new file mode 100644 index 000000000..ae39d648d --- /dev/null +++ b/klipper/urlgrabber.h @@ -0,0 +1,159 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) 2000 by Carsten Pfeiffer <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef URLGRABBER_H +#define URLGRABBER_H + +#include <qptrlist.h> +#include <qintdict.h> +#include <qregexp.h> +#include <qstring.h> +#include <qstringlist.h> + +#include <kprocess.h> + + +class QTimer; + +class KConfig; +class KPopupMenu; + +class ClipAction; +struct ClipCommand; +typedef QPtrList<ClipAction> ActionList; +typedef QPtrListIterator<ClipAction> ActionListIterator; + +class URLGrabber : public QObject +{ + Q_OBJECT + +public: + URLGrabber( KConfig* config ); + ~URLGrabber(); + + /** + * Checks a given string whether it matches any of the user-defined criteria. + * If it does, the configured action will be executed. + * @returns false if the string should be put into the popupmenu or not, + * otherwise true. + */ + bool checkNewData( const QString& clipData ); + void invokeAction( const QString& clip = QString::null ); + + const ActionList * actionList() const { return myActions; } + void setActionList( ActionList * ); + void readConfiguration( KConfig * ); + void writeConfiguration( KConfig * ); + + int popupTimeout() const { return myPopupKillTimeout; } + void setPopupTimeout( int timeout ) { myPopupKillTimeout = timeout; } + + const QStringList& avoidWindows() const { return myAvoidWindows; } + void setAvoidWindows( const QStringList& list ) { myAvoidWindows = list; } + + bool stripWhiteSpace() const { return m_stripWhiteSpace; } + void setStripWhiteSpace( bool enable ) { m_stripWhiteSpace = enable; } + +private: + const ActionList& matchingActions( const QString& ); + void execute( const struct ClipCommand *command ) const; + void editData(); + bool isAvoidedWindow() const; + void actionMenu( bool wm_class_check ); + + ActionList *myActions; + ActionList myMatches; + QStringList myAvoidWindows; + QString myClipData; + ClipAction *myCurrentAction; + QIntDict<ClipCommand> myCommandMapper; + KPopupMenu *myMenu; + QTimer *myPopupKillTimer; + int myPopupKillTimeout; + bool m_stripWhiteSpace; + KConfig* m_config; + +private slots: + void slotActionMenu() { actionMenu( true ); } + void slotItemSelected( int ); + void slotKillPopupMenu(); + + +signals: + void sigPopup( QPopupMenu * ); + void sigDisablePopup(); + +}; + + +struct ClipCommand +{ + ClipCommand( const QString &, const QString &, bool = true, const QString & = "" ); + QString command; + QString description; + bool isEnabled; + QString pixmap; + // int id; // the index reflecting the position in the list of commands +}; + +/** + * Represents one configured action. An action consists of one regular + * expression, an (optional) description and a list of ClipCommands + * (a command to be executed, a description and an enabled/disabled flag). + */ +class ClipAction +{ +public: + ClipAction( const QString& regExp, const QString& description ); + ClipAction( const ClipAction& ); + ClipAction( KConfig *kc ); + ~ClipAction(); + + void setRegExp( const QString& r) { myRegExp = QRegExp( r ); } + QString regExp() const { return myRegExp.pattern(); } + inline bool matches( const QString& string ) const { + return ( myRegExp.search( string ) != -1 ); + } + + void setDescription( const QString& d) { myDescription = d; } + const QString& description() const { return myDescription; } + + /** + * Removes all ClipCommands associated with this ClipAction. + */ + void clearCommands() { myCommands.clear(); } + + void addCommand( const QString& command, const QString& description, bool, const QString& icon = "" ); + const QPtrList<ClipCommand>& commands() const { return myCommands; } + + /** + * Saves this action to a a given KConfig object + */ + void save( KConfig * ) const; + + +private: + QRegExp myRegExp; + QString myDescription; + QPtrList<ClipCommand> myCommands; + +}; + + +#endif // URLGRABBER_H diff --git a/klipper/version.h b/klipper/version.h new file mode 100644 index 000000000..758d59a6d --- /dev/null +++ b/klipper/version.h @@ -0,0 +1,26 @@ +// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- +/* This file is part of the KDE project + Copyright (C) Andrew Stanley-Jones + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef _VERSION_H_ +#define _VERSION_H_ + +static const char * const klipper_version = "v0.9.7"; + + +#endif |