From ae262e92d29c5d944380712b95b7e01fe3dc366b Mon Sep 17 00:00:00 2001 From: Kurt Granroth Date: Sun, 7 Nov 1999 08:46:36 +0000 Subject: [PATCH] "ported" over the essential minicli from kwm (yipee! now i can use kwin as my normal window manager!!) unfortunately, i'm not 100% that the way i did it was the "proper" way. it works, but it might not be the best way to do it. matthias, et al, definitely need to take a look at it! svn path=/trunk/kdebase/kwin/; revision=33085 --- Makefile.am | 2 +- minicli.cpp | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++ minicli.h | 55 +++++++ workspace.cpp | 32 +++- workspace.h | 10 ++ 5 files changed, 493 insertions(+), 2 deletions(-) create mode 100644 minicli.cpp create mode 100644 minicli.h diff --git a/Makefile.am b/Makefile.am index 3eb39f17fe..5f4b207217 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,7 +4,7 @@ LDFLAGS = $(all_libraries) $(KDE_RPATH) bin_PROGRAMS = kwin -kwin_SOURCES = atoms.cpp beclient.cpp client.cpp main.cpp stdclient.cpp workspace.cpp tabbox.cpp options.cpp systemclient.cpp +kwin_SOURCES = atoms.cpp beclient.cpp client.cpp main.cpp stdclient.cpp workspace.cpp tabbox.cpp options.cpp systemclient.cpp minicli.cpp kwin_LDADD = $(LIB_KDECORE) $(LIB_KDEUI) diff --git a/minicli.cpp b/minicli.cpp new file mode 100644 index 0000000000..178b62a611 --- /dev/null +++ b/minicli.cpp @@ -0,0 +1,396 @@ +// minicli +// Copyright (C) 1997 Matthias Ettrich +// Copyright (c) 1999 Preston Brown + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "client.h" +#include "main.h" + + +#include "minicli.moc" + +#ifdef KeyPress +#undef KeyPress +#endif + +//KURT: not sure what this variable does +//extern bool do_not_draw; + +// global history list +QList *history = 0; +QListIterator *it; + +/* + Function determines whether what the user entered in the minicli + box is executable or not. This code is eniterly based on the C + version of the 'which' command in csh/bash. (Dawit A.) +*/ +bool isExecutable ( const char *name ) +{ + QString test; + char *path = getenv( "PATH" ); + char *pc = path; + bool found = false; + + while ( *pc != '\0' && found == false ) { + int len = 0; + while ( *pc != ':' && *pc != '\0' ) { + len++; + pc++; + } + char save = *pc; + *pc = '\0'; + test = QString(pc-len) + "/" + name; + *pc = save; + if (*pc) { pc++; } + found = ( access(test.data(), 01) == 0 ); /* is it executable ? */ + } + return found; +} + +// Check for the existance of a local file/directory. (Dawit A.) +bool isLocalResource ( const char * cmd ) +{ + struct stat buff; + return ( stat( cmd, &buff ) == 0 && ( S_ISREG( buff.st_mode ) || S_ISDIR( buff.st_mode ) ) ) ? true :false; +} + +bool isValidShortURL ( const char * cmd ) +{ + // NOTE : By design, this check disqualifies some valid + // URL's that contain queries and *nix command characters. + // This is an intentional trade off to best much the URL + // with a local resource first. This also allows minicli + // to behave consistently with the way it does now. (Dawit A.) + char lastchr = *( cmd + strlen (cmd) - 1 ); + //debug ( "%s : %c", "The last charater of the URL is", lastchr ); + if ( strchr ( cmd, ' ' ) != 0L || strchr ( cmd, ';' ) != 0L ) + return false; + if ( *cmd == '/' || lastchr == '&' ) + return false; + if ( strchr ( cmd, '<' ) != 0L || strchr ( cmd, '>' ) != 0L ) + return false; + if ( strstr ( cmd, "||" ) != 0L || strstr ( cmd, "&&" ) != 0L ) + return false; + return true; +} + +void execute( const char* cmd, bool inTerminal) +{ + QString tmp; + + // Torben + // WWW Adress ? + if ( strnicmp( cmd, "www.", 4 ) == 0 ) { + tmp = "kfmclient exec http://"; + tmp += cmd; + cmd = tmp.data(); + } + // FTP Adress ? + else if ( strnicmp( cmd, "ftp.", 4 ) == 0 ) { + tmp = "kfmclient exec ftp://"; + tmp += cmd; + cmd = tmp.data(); + } + // Looks like a KDEHelp thing ? + else if ( strstr( cmd, "man:" ) != 0L || + strstr( cmd, "MAN:" ) != 0L || cmd[0] == '#' ) { + tmp = "kdehelp \""; + if ( cmd[0] == '#' ) { + tmp += "man:"; + tmp += cmd + 1; + } else + tmp += cmd; + tmp += "\""; + cmd = tmp.data(); + } + // Looks like an URL ? + else if ( strstr( cmd, "://" ) != 0L || + strnicmp ( cmd, "news:", 5) == 0 || + strnicmp ( cmd, "mailto:", 7) == 0 ) { + tmp = "kfmclient exec "; + tmp += cmd; + cmd = tmp.data(); + } + // Usual file or directory + else { + const char *p = cmd; + QString tst; + + if ( strnicmp( p, "file:", 5 ) == 0 ) + p = p + 5; + // Replace '~' with the user's home directory. + if ( *p == '~' ) + { + p = tst.append( p ).replace( 0, 1, QDir::homeDirPath() ).data(); + cmd = p; + } + bool isLocal = isLocalResource ( cmd ); // Am I locally available ? + bool isExec = isExecutable ( cmd ); // Am I locally executable ? + + // Is this a non-executable local resource ? + if ( !isExec && isLocal ) { + // Tell KFM to open the document + tmp += cmd; + // Quote the URL in case there is a space in it ; so + // one can for example type : ~/Desktop/CD ROM.kdelnk + // to mount the CD ROM device. ( Dawit A. ) + tmp.prepend ( "kfmclient exec \"" ); + tmp.append ( "\""); + cmd = tmp.data(); + } + // if URL is not a local resource and does not contain any *nix shell + // command characters, append "http://" as the default protocol. (Dawit A.) + else if ( !isExec && isValidShortURL ( cmd ) ) { + tmp = cmd; + tmp.prepend ("kfmclient exec http://"); + cmd = tmp.data(); + } else if (isExec && inTerminal) { + tmp += cmd; + tmp.prepend("konsole -e \""); + tmp.append("\""); + cmd = tmp.data(); + qDebug("cmd is now %s",cmd); + } + } + + KShellProcess proc; + proc << cmd; + proc.start(KShellProcess::DontCare); +} + +Minicli::Minicli( Workspace *ws, QWidget *parent, const char *name, WFlags f) + : QVBox(parent, name, f & WStyle_StaysOnTop) +{ + setFrameStyle(QFrame::WinPanel | QFrame:: Raised); + + setSpacing(7); + setMargin(7); + + QHBox *hBox = new QHBox(this); + hBox->setSpacing(7); + + QLabel *label = new QLabel(hBox); + label->setPixmap(KGlobal::iconLoader()->loadApplicationIcon("go")); + + label = new QLabel(i18n("Type in the name of the program or command you wish to execute.\n" + "Check the \"run in terminal\" box if the program is not graphical."), hBox); + + hBox = new QHBox(this); + hBox->setSpacing(7); + + label = new QLabel(i18n("Command:"), hBox); + label->setFixedSize(label->sizeHint()); + runCombo = new QComboBox(true, hBox); + runCombo->setAutoCompletion(true); + + // populate run combo with history. + KConfig *config = KGlobal::config(); + config->setGroup("MiniCli"); + QStrList hist; + config->readListEntry("History", hist); + + //CT 15Jan1999 - limit in-memory history too + unsigned int max_hist = config->readNumEntry("HistoryLength", 50); + for (unsigned int i=0; i< QMIN(hist.count(), max_hist); i++) + if (strcmp(hist.at(i),"") != 0) + runCombo->insertItem(hist.at(i)); + + // insert blank entry at end and make it active + runCombo->insertItem(""); + + hBox = new QHBox(this); + hBox->setSpacing(7); + terminalBox = new QCheckBox(i18n("Run in terminal"), hBox); + + KButtonBox *bbox = new KButtonBox(hBox); + bbox->addStretch(); + QPushButton *runButton = bbox->addButton(i18n("&Run"), false); + runButton->setDefault(true); + connect(runButton, SIGNAL(clicked()), + this, SLOT(run_command())); + + QPushButton *cancelButton = bbox->addButton(i18n("&Cancel"), false); + connect(cancelButton, SIGNAL(clicked()), + this, SLOT(cleanup())); + + adjustSize(); + + workspace = ws; + + move(QApplication::desktop()->width()/2 - width()/2, + QApplication::desktop()->height()/2 - height()/2); +} + +void Minicli::keyPressEvent(QKeyEvent *kev) +{ + int a = ((QKeyEvent*)kev)->ascii(); + if (a == 3 || a == 7 || a == 27) { + cleanup(); + kev->accept(); + return; + } + if (kev->key() == Key_Return || + kev->key() == Key_Enter) { + kev->accept(); + run_command(); + return; + } + + kev->ignore(); +} + +bool Minicli::do_grabbing() +{ + runCombo->setCurrentItem(runCombo->count() - 1); + terminalBox->setChecked(false); + show(); + + reactive = workspace->activeClient(); + if (reactive) + reactive->setActive(false); + XSetInputFocus (qt_xdisplay(), winId(), RevertToParent, CurrentTime); + + // commenting out the following makes "dead keys" (@, ~, ...) work + // if (XGrabKeyboard(qt_xdisplay(), runCombo->winId(),True,GrabModeAsync, + // GrabModeAsync,CurrentTime) != GrabSuccess) + // return False; + +// KURT: not sure what this does +// do_not_draw = true; + raise(); + runCombo->setFocus(); + return True; +} + +void Minicli::run_command(){ + QString s = runCombo->currentText(); + s = s.stripWhiteSpace(); + + if (s.simplifyWhiteSpace().stripWhiteSpace().isEmpty()) { + cleanup(); + return; + } + + KGlobal::config()->setGroup("MiniCli"); + int max_hist = KGlobal::config()->readNumEntry("HistoryLength",50); + + runCombo->removeItem(runCombo->count() - 1); + + if (runCombo->text(runCombo->count() - 1).simplifyWhiteSpace().isEmpty()) { + runCombo->removeItem(runCombo->count() - 1); // remove empty item + } + if (s != runCombo->text(runCombo->count() - 1)) { // don't save duplicates + runCombo->insertItem(s); + } + + runCombo->insertItem(""); + while (runCombo->count() > max_hist) + runCombo->removeItem(0); // take out first item + + bool term = terminalBox->isChecked(); + cleanup(); + +// if (s == "logout") +// logout(); +// else + execute(s.data(), term); +} + +void Minicli::cleanup() +{ + hide(); + terminalBox->setChecked(false); + runCombo->setCurrentItem(runCombo->count() - 1); + +// KURT: not sure what this does +// do_not_draw = false; + if (reactive && workspace->hasClient(reactive)){ + reactive->setActive(true); + XSetInputFocus (qt_xdisplay(), reactive->window(), + RevertToPointerRoot, CurrentTime); + } + XSync(qt_xdisplay(), false); + + // save history list (M.H.) + KConfig *config = KGlobal::config(); + config->setGroup("MiniCli"); + + QStrList hist; + for (int i = 0; i < runCombo->count(); i++) { + if (runCombo->text(i).simplifyWhiteSpace() != "") + hist.append(runCombo->text(i).utf8()); + } + + config->writeEntry("History", hist); + config->sync(); +} + +void Minicli::commandCompletion(){ + QListIterator *it; + it = new QListIterator (*history); + QString s,t,u; + s = runCombo->text(runCombo->currentItem()); + bool abort = True; + + if (!it->isEmpty()){ + it->toLast(); + while (!it->atFirst()){ + --(*it); + t = it->current(); + if (t.length()>=s.length() && t.left(s.length()) == s){ + if (t.length()>s.length()){ + u = t.left(s.length()+1); + abort = False; + } + it->toFirst(); + } + } + if (!abort){ + it->toLast(); + while (!it->atFirst()){ + --(*it); + t = it->current(); + if (t.length()>=s.length() && t.left(s.length()) == s){ + if (t.length()toFirst(); + } + } + } + } + + if (!abort){ + runCombo->insertItem(u.data()); + commandCompletion(); + } + } + delete it; + return; +} diff --git a/minicli.h b/minicli.h new file mode 100644 index 0000000000..240e11e77f --- /dev/null +++ b/minicli.h @@ -0,0 +1,55 @@ +// minicli +// Copyright (C) 1997 Matthias Ettrich +// Copyright (c) 1999 Preston Brown +// +// Torben added command completion +// 09.11.97 +// +// Dawit added short URL support +// 02.26.99 + +#ifndef MINICLI_H +#define MINICLI_H + +#include +#include + +void execute ( const char*, bool ); +bool isExecutable ( const char* ); +bool isLocalResource ( const char* ); +bool isValidShortURL ( const char * ); + +class QComboBox; +class QLabel; +class QCheckBox; + +class Workspace; + +class Minicli : public QVBox { + Q_OBJECT +public: + Minicli( Workspace *ws = 0, QWidget *parent=0, const char *name=0, WFlags f=0); + bool do_grabbing(); + +public slots: + void cleanup(); + +protected: + void keyPressEvent(QKeyEvent *); + +private slots: + void run_command(); + +private: + QComboBox* runCombo; + QCheckBox *terminalBox; + Client* reactive; + void commandCompletion(); + + Workspace *workspace; + + KURLCompletion kurlcompletion; +}; + +#endif + diff --git a/workspace.cpp b/workspace.cpp index 09c027e8e0..665480a8d1 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -5,6 +5,7 @@ #include "systemclient.h" #include "tabbox.h" #include "atoms.h" +#include "minicli.h" #include #include #include @@ -12,6 +13,8 @@ #include #include +#include +#include static Client* clientFactory( Workspace *ws, WId w ) @@ -63,8 +66,14 @@ Workspace::Workspace() grabKey(XK_Tab, Mod1Mask | ShiftMask); grabKey(XK_Tab, ControlMask); grabKey(XK_Tab, ControlMask | ShiftMask); +} - +void Workspace::slotExecuteCommand() +{ + if (!minicli){ + minicli = new Minicli(this, 0, 0, Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool); + } + while (!minicli->do_grabbing()); } Workspace::Workspace( WId rootwin ) @@ -93,6 +102,7 @@ Workspace::Workspace( WId rootwin ) void Workspace::init() { + minicli = 0; tab_box = 0; active_client = 0; should_get_focus = 0; @@ -134,6 +144,11 @@ void Workspace::init() XUngrabServer( qt_xdisplay() ); popup = 0; propagateClients(); + + // KURT: Not sure where to put these.. + keys = new KGlobalAccel(); + keys->insertItem(i18n("Execute command"), "Execute command", "ALT+F2"); + keys->connectItem("Execute command", this, SLOT(slotExecuteCommand())); } Workspace::~Workspace() @@ -162,6 +177,11 @@ bool Workspace::workspaceEvent( XEvent * e ) if ( c ) return c->windowEvent( e ); + if (!tab_grab && ! control_grab) { // don't process accelerators in tab or control mode + if (keys->x11EventFilter(e)) + return true; + } + switch (e->type) { case ButtonPress: case ButtonRelease: @@ -263,6 +283,16 @@ bool Workspace::workspaceEvent( XEvent * e ) return FALSE; } +bool Workspace::hasClient(Client* c) +{ + for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { + if ( (*it) == c ) + return TRUE; + } + + return FALSE; +} + /*! Finds the client that embedds the window \a w */ diff --git a/workspace.h b/workspace.h index fb11c3282c..d8201fc842 100644 --- a/workspace.h +++ b/workspace.h @@ -9,6 +9,9 @@ class Client; class TabBox; +class Minicli; + +class KGlobalAccel; typedef QValueList ClientList; @@ -22,6 +25,8 @@ public: virtual bool workspaceEvent( XEvent * ); + bool hasClient(Client *); + Client* findClient( WId w ) const; QRect geometry() const; @@ -64,12 +69,17 @@ public: void makeFullScreen( Client* ); +protected slots: + void slotExecuteCommand(); + protected: bool keyPress( XKeyEvent key ); bool keyRelease( XKeyEvent key ); bool clientMessage( XClientMessageEvent msg ); private: + KGlobalAccel *keys; + Minicli *minicli; void init(); WId root; ClientList clients;