diff options
Diffstat (limited to 'ksystraycmd')
-rw-r--r-- | ksystraycmd/CMakeLists.txt | 29 | ||||
-rw-r--r-- | ksystraycmd/Makefile.am | 18 | ||||
-rw-r--r-- | ksystraycmd/README | 29 | ||||
-rw-r--r-- | ksystraycmd/ksystraycmd.cpp | 341 | ||||
-rw-r--r-- | ksystraycmd/ksystraycmd.h | 89 | ||||
-rw-r--r-- | ksystraycmd/main.cpp | 143 |
6 files changed, 649 insertions, 0 deletions
diff --git a/ksystraycmd/CMakeLists.txt b/ksystraycmd/CMakeLists.txt new file mode 100644 index 000000000..02a271f0d --- /dev/null +++ b/ksystraycmd/CMakeLists.txt @@ -0,0 +1,29 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### ksystraycmd (executable) ################## + +tde_add_executable( ksystraycmd AUTOMOC + SOURCES ksystraycmd.cpp main.cpp + LINK tdeui-shared + DESTINATION ${BIN_INSTALL_DIR} +) diff --git a/ksystraycmd/Makefile.am b/ksystraycmd/Makefile.am new file mode 100644 index 000000000..897fcc81d --- /dev/null +++ b/ksystraycmd/Makefile.am @@ -0,0 +1,18 @@ +####### Fiddle here + +INCLUDES = $(all_includes) +LDADD = $(LIB_TDEUI) + +####### Files + +bin_PROGRAMS = ksystraycmd +METASOURCES = ksystraycmd.moc +noinst_HEADERS = ksystraycmd.h + +ksystraycmd_SOURCES = ksystraycmd.cpp main.cpp +ksystraycmd_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +messages: + $(XGETTEXT) $(ksystraycmd_SOURCES) -o $(podir)/ksystraycmd.pot + + diff --git a/ksystraycmd/README b/ksystraycmd/README new file mode 100644 index 000000000..8e41777a0 --- /dev/null +++ b/ksystraycmd/README @@ -0,0 +1,29 @@ + README for KSysTrayCmd + ====================== + +Introduction +============ + +KSysTrayCmd is a utility that allows you to run any application you +like in the system tray, not just those designed to use it. + +Examples +======== + +ksystraycmd --window 'kmail' kmail + +ksystraycmd --startonshow --icon logfile --tooltip 'X Log' --hidden --window 'X Log' \ + konsole --icon logfile --caption 'X Log' -e 'less -M ~/.xsession-errors' + +More Information +================ + +I'm currently writing an article for the dot which will describe the +usage of both kstart and ksystraycmd in more detail, the text of the +article will be used to improve this document. + +Richard Moore + + + diff --git a/ksystraycmd/ksystraycmd.cpp b/ksystraycmd/ksystraycmd.cpp new file mode 100644 index 000000000..5f9264c8c --- /dev/null +++ b/ksystraycmd/ksystraycmd.cpp @@ -0,0 +1,341 @@ +#include <tqtooltip.h> +#include <tqtextstream.h> +#include <tqimage.h> +#include <tqregexp.h> + +#include <kdebug.h> +#include <tdeapplication.h> +#include <tdeglobal.h> +#include <kiconloader.h> +#include <tdelocale.h> +#include <tdepopupmenu.h> +#include <kprocess.h> +#include <twinmodule.h> +#include <tdeconfig.h> +#include <ksystemtray.h> + +#include <netwm.h> + +#include "ksystraycmd.h" +#include "ksystraycmd.moc" + + +KSysTrayCmd::KSysTrayCmd() + : TQLabel( 0, "systray_cmd" ), + isVisible(true), lazyStart( false ), noquit( false ), quitOnHide( false ), onTop(false), ownIcon(false), + win(0), client(0), twinmodule(0), top(0), left(0) +{ + setAlignment( AlignCenter ); + twinmodule = new KWinModule( TQT_TQOBJECT(this) ); + refresh(); +} + +KSysTrayCmd::~KSysTrayCmd() +{ + delete client; +} + +// +// Main entry point to the class +// + +bool KSysTrayCmd::start() +{ + // If we have no command we must catching an existing window + if ( !command ) { + if ( win ) { + setTargetWindow( win ); + return true; + } + + checkExistingWindows(); + if ( win ) { + // Window always on top + if (onTop) { + KWin::setState(win, NET::StaysOnTop); + } + return true; + } + + errStr = i18n( "No window matching pattern '%1' and no command specified.\n" ) + .arg( window ); + return false; + } + + // Run the command and watch for its window + if ( !startClient() ) { + errStr = i18n( "KSysTrayCmd: KShellProcess cannot find a shell." ); + clientExited(); + return false; + } + + return true; +} + +// +// Window related functions. +// + +void KSysTrayCmd::showWindow() +{ + isVisible = true; + if ( !win ) + return; + XMapWindow( tqt_xdisplay(), win ); + // We move the window to the memorized position + XMoveWindow( tqt_xdisplay(), win, left, top); + + // Window always on top + if (onTop) + { + KWin::setState(win, NET::StaysOnTop); + } + + KWin::activateWindow( win ); + +} + +void KSysTrayCmd::hideWindow() +{ + isVisible = false; + if ( !win ) + return; + //We memorize the position of the window + left = KWin::windowInfo(win).frameGeometry().left(); + top=KWin::windowInfo(win).frameGeometry().top(); + + XUnmapWindow( tqt_xdisplay(), win ); +} + +void KSysTrayCmd::setTargetWindow( WId w ) +{ + setTargetWindow( KWin::windowInfo( w ) ); +} + +void KSysTrayCmd::setTargetWindow( const KWin::WindowInfo &info ) +{ + disconnect( twinmodule, TQT_SIGNAL(windowAdded(WId)), this, TQT_SLOT(windowAdded(WId)) ); + connect( twinmodule, TQT_SIGNAL(windowChanged(WId)), TQT_SLOT(windowChanged(WId)) ); + win = info.win(); + KWin::setSystemTrayWindowFor( winId(), win ); + refresh(); + show(); + + if ( isVisible ) + KWin::activateWindow( win ); + else + hideWindow(); + + // Always on top ? + if (onTop) + { + KWin::setState(win, NET::StaysOnTop); + } +} + +// +// Refresh the tray icon +// + +void KSysTrayCmd::refresh() +{ + KWin::setSystemTrayWindowFor( winId(), win ? win : winId() ); + + TQToolTip::remove( this ); + if ( win ) { + TDEConfig *appCfg = kapp->config(); + TDEConfigGroupSaver configSaver(appCfg, "System Tray"); + int iconWidth = appCfg->readNumEntry("systrayIconWidth", 22); + + // ksystraycmd's icon or app's icon + if (ownIcon) + { + setPixmap( KSystemTray::loadIcon( kapp->iconName() ) ); + } + else + { + setPixmap( KWin::icon( win, iconWidth, iconWidth, true ) ); + } + + TQToolTip::add( this, KWin::windowInfo( win ).name() ); + } + else { + if ( !tooltip.isEmpty() ) + TQToolTip::add( this, tooltip ); + else if ( !command.isEmpty() ) + TQToolTip::add( this, command ); + else + TQToolTip::add( this, window ); + + setPixmap( KSystemTray::loadIcon( kapp->iconName() ) ); + } +} + +// +// Client related functions. +// + +bool KSysTrayCmd::startClient() +{ + client = new KShellProcess(); + *client << command; + connect( twinmodule, TQT_SIGNAL(windowAdded(WId)), TQT_SLOT(windowAdded(WId)) ); + connect( client, TQT_SIGNAL( processExited(TDEProcess *) ), + this, TQT_SLOT( clientExited() ) ); + + return client->start(); +} + +void KSysTrayCmd::clientExited() +{ + delete client; + client = 0; + win = 0; + + if ( lazyStart && noquit ) + refresh(); + else + tqApp->quit(); +} + +void KSysTrayCmd::quitClient() +{ + if ( win ) { + // Before sending the close request we have to show the window + XMapWindow( tqt_xdisplay(), win ); + NETRootInfo ri( tqt_xdisplay(), NET::CloseWindow ); + ri.closeWindowRequest( win ); + win=0; + noquit = false; + + // We didn't give command, so we didn't open an application. + // That's why when the application is closed we aren't informed. + // So we quit now. + if ( !command ) { + tqApp->quit(); + } + } + else { + tqApp->quit(); + } +} + +void KSysTrayCmd::quit() +{ + if ( !isVisible ) { + showWindow(); + } + tqApp->quit(); +} + +void KSysTrayCmd::execContextMenu( const TQPoint &pos ) +{ + TDEPopupMenu *menu = new TDEPopupMenu(); + menu->insertTitle( *pixmap(), i18n( "KSysTrayCmd" ) ); + int hideShowId = menu->insertItem( isVisible ? i18n( "&Hide" ) : i18n( "&Restore" ) ); + int undockId = menu->insertItem( SmallIcon("close"), i18n( "&Undock" ) ); + int quitId = menu->insertItem( SmallIcon("system-log-out"), i18n( "&Quit" ) ); + + int cmd = menu->exec( pos ); + + if ( cmd == quitId ) + quitClient(); + else if ( cmd == undockId ) + quit(); + else if ( cmd == hideShowId ) + { + if ( lazyStart && ( !hasRunningClient() ) ) + { + start(); + isVisible=true; + } + else if ( quitOnHide && ( hasRunningClient() ) && isVisible ) + { + NETRootInfo ri( tqt_xdisplay(), NET::CloseWindow ); + ri.closeWindowRequest( win ); + isVisible=false; + } + else + toggleWindow(); + } + + delete menu; +} + +void KSysTrayCmd::checkExistingWindows() +{ + TQValueList<WId>::ConstIterator it; + for ( it = twinmodule->windows().begin(); it != twinmodule->windows().end(); ++it ) { + windowAdded( *it ); + if ( win ) + break; + } +} + +void KSysTrayCmd::windowAdded(WId w) +{ + if ( !window.isEmpty() && ( TQRegExp( window ).search( KWin::windowInfo(w).name() ) == -1 ) ) + return; // no match + setTargetWindow( w ); +} + +void KSysTrayCmd::windowChanged( WId w ) +{ + if ( w != win ) + return; + refresh(); +} + +// +// Tray icon event handlers +// + +void KSysTrayCmd::mousePressEvent( TQMouseEvent *e ) +{ + if ( e->button() == Qt::RightButton ) + execContextMenu( e->globalPos() ); + else if ( lazyStart && ( !hasRunningClient() ) ) + { + start(); + isVisible=true; + } + else if ( quitOnHide && ( hasRunningClient() ) && isVisible ) + { + NETRootInfo ri( tqt_xdisplay(), NET::CloseWindow ); + ri.closeWindowRequest( win ); + isVisible=false; + } + else + toggleWindow(); +} + +WId KSysTrayCmd::findRealWindow( WId w, int depth ) +{ + if( depth > 5 ) + return None; + static Atom wm_state = XInternAtom( tqt_xdisplay(), "WM_STATE", False ); + Atom type; + int format; + unsigned long nitems, after; + unsigned char* prop; + if( XGetWindowProperty( tqt_xdisplay(), w, wm_state, 0, 0, False, AnyPropertyType, + &type, &format, &nitems, &after, &prop ) == Success ) { + if( prop != NULL ) + XFree( prop ); + if( type != None ) + return w; + } + Window root, parent; + Window* children; + unsigned int nchildren; + Window ret = None; + if( XQueryTree( tqt_xdisplay(), w, &root, &parent, &children, &nchildren ) != 0 ) { + for( unsigned int i = 0; + i < nchildren && ret == None; + ++i ) + ret = findRealWindow( children[ i ], depth + 1 ); + if( children != NULL ) + XFree( children ); + } + return ret; +} diff --git a/ksystraycmd/ksystraycmd.h b/ksystraycmd/ksystraycmd.h new file mode 100644 index 000000000..abad3ec57 --- /dev/null +++ b/ksystraycmd/ksystraycmd.h @@ -0,0 +1,89 @@ +// -*- c++ -*- + +#ifndef KSYSTRAYCMD_H +#define KSYSTRAYCMD_H + +#include <tqlabel.h> +#include <twin.h> + +class KShellProcess; +class KWinModule; + +/** + * Provides a system tray icon for a normal window. + * + * @author Richard Moore, [email protected] + */ +class KSysTrayCmd : public TQLabel +{ + Q_OBJECT +public: + KSysTrayCmd(); + ~KSysTrayCmd(); + + void setWindow( WId w ) { win = w; } + void setCommand( const TQString &cmd ) { command = cmd; } + void setPattern( const TQString ®exp ) { window = regexp; } + void setStartOnShow( bool enable ) { lazyStart = enable; isVisible = !enable; } + void setNoQuit( bool enable ) { noquit = enable; } + void setQuitOnHide( bool enable ) { quitOnHide = enable; } + void setOnTop( bool enable ) { onTop = enable; } + void setOwnIcon( bool enable ) { ownIcon = enable; } + void setDefaultTip( const TQString &tip ) { tooltip = tip; } + bool hasTargetWindow() const { return (win != 0); } + bool hasRunningClient() const { return (client != 0); } + const TQString &errorMsg() const { return errStr; } + + bool start(); + + static WId findRealWindow( WId w, int depth = 0 ); + +public slots: + void refresh(); + + void showWindow(); + void hideWindow(); + void toggleWindow() { if ( isVisible ) hideWindow(); else showWindow(); } + + void setTargetWindow( WId w ); + void execContextMenu( const TQPoint &pos ); + + void quit(); + void quitClient(); + +protected slots: + void clientExited(); + + void windowAdded(WId w); + void windowChanged(WId w); + +protected: + bool startClient(); + void checkExistingWindows(); + void setTargetWindow( const KWin::WindowInfo &info ); + + void mousePressEvent( TQMouseEvent *e ); + +private: + TQString command; + TQString window; + TQString tooltip; + bool isVisible; + bool lazyStart; + bool noquit; + bool quitOnHide; + bool onTop; ///< tells if window must stay on top or not + bool ownIcon; ///< tells if the ksystraycmd icon must be used in systray + + WId win; + KShellProcess *client; + KWinModule *twinmodule; + TQString errStr; + + /** Memorized 'top' position of the window*/ + int top; + /** Memorized 'left' position of the window*/ + int left; +}; + +#endif // KSYSTRAYCMD_H diff --git a/ksystraycmd/main.cpp b/ksystraycmd/main.cpp new file mode 100644 index 000000000..e322f2cf9 --- /dev/null +++ b/ksystraycmd/main.cpp @@ -0,0 +1,143 @@ +#include <fcntl.h> + +#include <tdeapplication.h> +#include <tdeaboutdata.h> +#include <tdecmdlineargs.h> +#include <kdebug.h> +#include <tdelocale.h> +#include <kprocess.h> + +#include "ksystraycmd.h" + +#include <X11/Xlib.h> +#ifndef KDE_USE_FINAL +const int XFocusOut = FocusOut; +const int XFocusIn = FocusIn; +#endif +#undef FocusOut +#undef FocusIn +#undef KeyPress +#undef KeyRelease + + +static TDECmdLineOptions options[] = +{ + { "!+command", I18N_NOOP("Command to execute"), 0 }, + // "!" means: all options after command are treated as arguments to the command + { "window <regexp>", I18N_NOOP("A regular expression matching the window title\n" + "If you do not specify one, then the very first window\n" + "to appear will be taken - not recommended."), 0 }, + { "wid <int>", I18N_NOOP("The window id of the target window\n" + "Specifies the id of the window to use. If the id starts with 0x\n" + "it is assumed to be in hex."), 0 }, + { "hidden", I18N_NOOP( "Hide the window to the tray on startup" ), 0 }, + { "startonshow", I18N_NOOP( "Wait until we are told to show the window before\n" + "executing the command" ), 0 }, + { "tooltip <text>", I18N_NOOP( "Sets the initial tooltip for the tray icon" ), 0 }, + { "keeprunning", I18N_NOOP( "Keep the tray icon even if the client exits. This option\n" + "has no effect unless startonshow is specified." ), 0 }, + { "ownicon", I18N_NOOP( "Use ksystraycmd's icon instead of window's icon in systray\n" + "(should be used with --icon to specify ksystraycmd icon)" ), 0 }, + { "ontop", I18N_NOOP( "Try to keep the window above other windows"), 0 }, + { "quitonhide", I18N_NOOP( "Quit the client when we are told to hide the window.\n" + "This has no effect unless startonshow is specified and implies keeprunning." ), 0 }, + /* { "menuitem <item>", I18N_NOOP( "Adds a custom entry to the tray icon menu\n" + "The item should have the form text:command." ), 0 },*/ + TDECmdLineLastOption +}; + +int main( int argc, char *argv[] ) +{ + TDEAboutData aboutData( "ksystraycmd", I18N_NOOP( "KSysTrayCmd" ), + "KSysTrayCmd 0.1", + I18N_NOOP( "Allows any application to be kept in the system tray" ), + TDEAboutData::License_GPL, + "(C) 2001-2002 Richard Moore ([email protected])" ); + aboutData.addAuthor( "Richard Moore", 0, "[email protected]" ); + + TDECmdLineArgs::init( argc, argv, &aboutData ); + TDECmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + TDEApplication app; + + // + // Setup the tray icon from the arguments. + // + TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); + KSysTrayCmd cmd; + + // Read the window id + TQString wid = args->getOption( "wid" ); + if ( !wid.isEmpty() ) { + int base = 10; + if ( wid.startsWith( "0x" ) ) { + base = 16; + wid = wid.right( wid.length() - 2 ); + } + + bool ok=true; + ulong w = wid.toULong( &ok, base ); + if ( ok ) + cmd.setTargetWindow( w ); + else { + kdWarning() << "KSysTrayCmd: Got bad win id" << endl; + } + } + + // Read window title regexp + TQString title = args->getOption( "window" ); + if ( !title.isEmpty() ) + cmd.setPattern( title ); + + if ( !title && !wid && (args->count() == 0) ) + TDECmdLineArgs::usage(i18n("No command or window specified")); + + // Read the command + TQString command; + for ( int i = 0; i < args->count(); i++ ) + command += TDEProcess::quote(TQString::fromLocal8Bit( args->arg(i) )) + " "; + if ( !command.isEmpty() ) + cmd.setCommand( command ); + + // Tooltip + TQString tip = args->getOption( "tooltip" ); + if ( !tip.isEmpty() ) + cmd.setDefaultTip( tip ); + + // Keep running flag + if ( args->isSet( "keeprunning" ) ) + cmd.setNoQuit( true ); + + if ( args->isSet( "quitonhide" ) ) { + cmd.setNoQuit( true ); + cmd.setQuitOnHide( true ); + } + + // Start hidden + if ( args->isSet( "hidden" ) ) + cmd.hideWindow(); + + // On top + if ( args->isSet( "ontop" ) ) + cmd.setOnTop(true); + + // Use ksystraycmd icon + if ( args->isSet( "ownicon" ) ) + cmd.setOwnIcon(true); + + // Lazy invocation flag + if ( args->isSet( "startonshow" ) ) { + cmd.setStartOnShow( true ); + cmd.show(); + } + else { + if ( !cmd.start() ) + return 1; + } + + fcntl(ConnectionNumber(tqt_xdisplay()), F_SETFD, 1); + args->clear(); + + return app.exec(); +} + |