[QGIS Commit] r10664 - trunk/qgis/src/plugins/grass
svn_qgis at osgeo.org
svn_qgis at osgeo.org
Mon Apr 27 03:04:32 EDT 2009
Author: pcav
Date: 2009-04-27 03:04:32 -0400 (Mon, 27 Apr 2009)
New Revision: 10664
Modified:
trunk/qgis/src/plugins/grass/CMakeLists.txt
trunk/qgis/src/plugins/grass/qgsgrassshell.cpp
trunk/qgis/src/plugins/grass/qgsgrassshell.h
trunk/qgis/src/plugins/grass/qgsgrassshellbase.ui
trunk/qgis/src/plugins/grass/qgsgrasstools.cpp
Log:
New GRASS shell - first commit - dev Lorenzo Masini
Modified: trunk/qgis/src/plugins/grass/CMakeLists.txt
===================================================================
--- trunk/qgis/src/plugins/grass/CMakeLists.txt 2009-04-27 05:39:32 UTC (rev 10663)
+++ trunk/qgis/src/plugins/grass/CMakeLists.txt 2009-04-27 07:04:32 UTC (rev 10664)
@@ -16,9 +16,9 @@
ENDIF (NOT MSVC)
IF (WIN32)
- ADD_DEFINITIONS("\"-DGRASS_EXPORT=__declspec(dllimport)\"")
+ ADD_DEFINITIONS("\"-DHAVE_GETPT -DGRASS_EXPORT=__declspec(dllimport)\"")
ELSE (WIN32)
- ADD_DEFINITIONS("-DGRASS_EXPORT=")
+ ADD_DEFINITIONS("-DHAVE_POSIX_OPENPT -DGRASS_EXPORT=")
ENDIF (WIN32)
########################################################
@@ -39,6 +39,24 @@
qgsgrassattributes.cpp
qgsgrassregion.cpp
qgsgrassutils.cpp
+ qtermwidget/BlockArray.cpp
+ qtermwidget/Emulation.cpp
+ qtermwidget/Filter.cpp
+ qtermwidget/History.cpp
+ qtermwidget/KeyboardTranslator.cpp
+ qtermwidget/Pty.cpp
+ qtermwidget/Screen.cpp
+ qtermwidget/ScreenWindow.cpp
+ qtermwidget/Session.cpp
+ qtermwidget/ShellCommand.cpp
+ qtermwidget/TerminalCharacterDecoder.cpp
+ qtermwidget/TerminalDisplay.cpp
+ qtermwidget/Vt102Emulation.cpp
+ qtermwidget/k3process.cpp
+ qtermwidget/k3processcontroller.cpp
+ qtermwidget/konsole_wcwidth.cpp
+ qtermwidget/kpty.cpp
+ qtermwidget/qtermwidget.cpp
)
SET (GRASS_PLUGIN_UIS
@@ -48,7 +66,6 @@
qgsgrassmapcalcbase.ui
qgsgrassmodulebase.ui
qgsgrassnewmapsetbase.ui
- qgsgrassshellbase.ui
qgsgrassattributesbase.ui
qgsgrassregionbase.ui
../../ui/qgsprojectionselectorbase.ui
@@ -69,6 +86,16 @@
qgsgrassregion.h
qgsgrassutils.h
qgsgrassedittools.h
+ qtermwidget/Emulation.h
+ qtermwidget/Filter.h
+ qtermwidget/Pty.h
+ qtermwidget/ScreenWindow.h
+ qtermwidget/Session.h
+ qtermwidget/TerminalDisplay.h
+ qtermwidget/Vt102Emulation.h
+ qtermwidget/k3process.h
+ qtermwidget/k3processcontroller.h
+ qtermwidget/qtermwidget.h
)
SET (GRASS_PLUGIN_RCCS qgsgrass_plugin.qrc)
@@ -106,9 +133,9 @@
${GRASS_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${PROJ_INCLUDE_DIR}
+ qtermwidget
)
-SET( QT_USE_QT3SUPPORT TRUE )
INCLUDE( ${QT_USE_FILE} )
TARGET_LINK_LIBRARIES(grassplugin
Modified: trunk/qgis/src/plugins/grass/qgsgrassshell.cpp
===================================================================
--- trunk/qgis/src/plugins/grass/qgsgrassshell.cpp 2009-04-27 05:39:32 UTC (rev 10663)
+++ trunk/qgis/src/plugins/grass/qgsgrassshell.cpp 2009-04-27 07:04:32 UTC (rev 10664)
@@ -1,9 +1,9 @@
/***************************************************************************
qgsgrassshell.cpp
--------------------------------------
- Date : Sun Sep 16 12:06:10 AKDT 2007
- Copyright : (C) 2007 by Gary E. Sherman
- Email : sherman at mrcc dot com
+ Date : Thu Apr 23 08:35:43 CEST 2009
+ Copyright : (C) 2009 by Lorenzo "Il Rugginoso" Masini
+ Email : lorenxo86 at gmail.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
@@ -12,944 +12,102 @@
* (at your option) any later version. *
* *
***************************************************************************/
-#include <vector>
+#include <QTabWidget>
+#include <QVBoxLayout>
+#include <QPushButton>
+#include <QShortcut>
+#include <QKeySequence>
+#include <QSizePolicy>
-#include <qstring.h>
-#include <qapplication.h>
-#include <qpushbutton.h>
-#include <qwidget.h>
-#include <q3textedit.h>
-#include <q3process.h>
-#include <qmessagebox.h>
-#include <q3cstring.h>
-#include <qfile.h>
-#include <qdatastream.h>
-#include <qstringlist.h>
-#include <qsocketnotifier.h>
-#include <q3socket.h>
-#include <q3socketdevice.h>
-#include <qevent.h>
-#include <q3textbrowser.h>
-#include <qregexp.h>
-#include <qcursor.h>
-#include <qlayout.h>
-#include <qclipboard.h>
-#include <qfontmetrics.h>
-#include <q3progressbar.h>
-
+#include "qgsgrasstools.h"
+#include "qtermwidget/qtermwidget.h"
#include "qgsapplication.h"
#include "qgsgrassshell.h"
-//Added by qt3to4:
-#include <QGridLayout>
-#include <QKeyEvent>
-#include <QResizeEvent>
-#include <QMouseEvent>
-#include "qgslogger.h"
-extern "C"
-{
-#include <stdio.h>
+extern "C" {
#include <stdlib.h>
-#ifndef _MSC_VER
-#include <unistd.h>
-#else
-#include <io.h>
-#endif
-
-#ifndef WIN32
-#ifdef Q_OS_MACX
-#include <util.h>
-#else
-#ifdef __NetBSD__
-#include <util.h>
-#else
-#ifdef __FreeBSD__
-#include <termios.h>
-#include <libutil.h>
-#else
-#include <pty.h>
-#endif
-#endif
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/wait.h>
-#endif //!WIN32
}
-QgsGrassShell::QgsGrassShell( QgsGrassTools *tools,
- QTabWidget * parent, const char * name ):
- QDialog( parent ), QgsGrassShellBase(), mTools( tools )
+QgsGrassShell::QgsGrassShell(QgsGrassTools *tools, QTabWidget *parent, const char *name)
+ : QFrame(parent)
{
- mValid = false;
- mSkipLines = 2;
- mTabWidget = parent;
+ mTools = tools;
+ mTabWidget = parent;
-#ifdef WIN32
- QMessageBox::warning( 0, "Warning",
- "GRASS Shell is not supported on Windows." );
- return;
-#else
+ QVBoxLayout *mainLayout = new QVBoxLayout(this);
+ QTermWidget *mTerminal = new QTermWidget(0, this);
+ initTerminal(mTerminal);
+ QPushButton *closeButton = new QPushButton(tr("Close"), this);
+ QShortcut *pasteShortcut = new QShortcut(QKeySequence(tr("Ctrl+Shift+V")), mTerminal);
+ QShortcut *copyShortcut = new QShortcut(QKeySequence(tr("Ctrl+Shift+C")), mTerminal);
- setupUi( this );
+ mainLayout->addWidget(mTerminal);
+ mainLayout->addWidget(closeButton);
+ setLayout(mainLayout);
- QGridLayout *layout = new QGridLayout( mTextFrame, 1, 1 );
- mText = new QgsGrassShellText( this, mTextFrame );
- layout->addWidget( mText, 0, 0 );
- mText->show();
+ connect(closeButton, SIGNAL(clicked()), this, SLOT(closeShell()));
+ connect(mTerminal, SIGNAL(finished()), this, SLOT(closeShell()));
+ connect(pasteShortcut, SIGNAL(activated()), mTerminal, SLOT(pasteClipboard()));
+ connect(copyShortcut, SIGNAL(activated()), mTerminal, SLOT(copyClipboard()));
- connect( mCloseButton, SIGNAL( clicked() ), this, SLOT( closeShell() ) );
-
- mFont = QFont( "Courier", 10 );
-
- mAppDir = mTools->appDir();
-
-#ifndef Q_WS_MAC
- // Qt4.3.2/Mac Q3TextEdit readOnly property causes keys to be processed as keyboard actions
- mText->setReadOnly( TRUE );
-#endif
- //mText->setFocusPolicy ( QWidget::NoFocus ); // To get key press directly
-
-#ifndef HAVE_OPENPTY
- mText->append( "GRASS shell is not supported" );
- return;
-#endif
-
- // TODO set cursor IbeamCursor
- // This does not work - the cursor is used for scrollbars -> disabled
- //mText->setCursor ( QCursor(Qt::IbeamCursor) );
- mParagraph = -1; // first will be 0
- mIndex = -1;
-
- mNewLine = true;
-
- for ( int i = 0; i < ModeCount; i++ )
- {
- resetMode( i );
- }
-
- int uid;
- seteuid( uid = getuid() ); /* Run unprivileged */
-
- // Get and open pseudo terminal
- // Note: 0 (stdin), 1 (stdout) or 2 (stderr)
- int fdSlave; // slave file descriptor
- seteuid( 0 );
- int ret = openpty( &mFdMaster, &fdSlave, NULL, NULL, NULL );
- if ( ret != 0 )
- {
- QMessageBox::warning( 0, "Warning", "Cannot open pseudo terminal" );
- return;
- }
- fchown( fdSlave, uid, ( gid_t ) - 1 );
- fchmod( fdSlave, S_IRUSR | S_IWUSR );
- seteuid( uid );
-
- QgsDebugMsg( QString( "mFdMaster = %1" ).arg( mFdMaster ) );
- QgsDebugMsg( QString( "fdSlave = %1" ).arg( fdSlave ) );
-
- fcntl( mFdMaster, F_SETFL, O_NDELAY );
- //fcntl( fdSlave, F_SETFL, O_NDELAY); // enable?
-
- QString slaveName = ttyname( fdSlave );
- QgsDebugMsg( QString( "master ttyname = %1" ).arg( ttyname( mFdMaster ) ) );
- QgsDebugMsg( QString( "slave ttyname = %1" ).arg( ttyname( fdSlave ) ) );
-
- //::close( fdSlave ); // -> crash
-
- // Fork slave and open shell
- int pid = fork();
- QgsDebugMsg( QString( "pid = %1" ).arg( pid ) );
- if ( pid == -1 )
- {
- QMessageBox::warning( 0, "Warning", "Cannot fork shell" );
- return;
- }
-
- // Child - slave
- if ( pid == 0 )
- {
- QgsDebugMsg( "child ->" );
-
- // TODO close all opened file descriptors - close(0)???
- ::close( mFdMaster );
-
- //::close( fdSlave ); // -> freeze
-
- setsid();
- seteuid( 0 );
-
- int fd = ::open(( char* ) slaveName.ascii(), O_RDWR );
- if ( fd < 0 )
- {
- QMessageBox::warning( 0, "Warning", "Cannot open slave file "
- "in child process" );
- return;
- }
-
- fchown( fd, uid, ( gid_t ) - 1 );
- fchmod( fd, S_IRUSR | S_IWUSR );
- setuid( uid );
-
- dup2( fd, 0 ); /* stdin */
- dup2( fd, 1 ); /* stdout */
- dup2( fd, 2 ); /* stderr */
-
- // TODO: test if shell is available
- QString shell = ( getenv( "SHELL" ) );
- if ( shell.isEmpty() )
- {
- shell = "/bin/bash";
- }
-
- const char *norc = "";
- QFileInfo si( shell );
- if ( si.fileName() == "bash" || si.fileName() == "sh" )
- {
- norc = "--norc";
- }
- else if ( si.fileName() == "tcsh" || si.fileName() == "csh" )
- {
- norc = "-f";
- }
-
- // Warning: execle + --norc will not inherit not given variables
- // -> overwrite here
- const char *env = "GRASS_MESSAGE_FORMAT=gui";
- char *envstr = new char[strlen( env )+1];
- strcpy( envstr, env );
- putenv( envstr );
-
- putenv(( char * ) "GISRC_MODE_MEMORY" ); // unset
-
- env = "PS1=GRASS > ";
- envstr = new char[strlen( env )+1];
- strcpy( envstr, env );
- putenv( envstr );
-
- env = "TERM=vt100";
- envstr = new char[strlen( env )+1];
- strcpy( envstr, env );
- putenv( envstr );
-
- //char *envar[] = { "PS1=GRASS > ", "TERM=vt100", "GISRC_MODE_MEMORY=",
- // "GRASS_MESSAGE_FORMAT=gui", (char *)0 };
-
- //execle ( (char*)shell.ascii(), (char *)si.fileName().ascii(),
- // norc, (char *) 0, envar);
- execl(( char* )shell.ascii(), ( char * )si.fileName().ascii(),
- norc, ( char * ) 0 );
-
- // Failed (QMessageBox here does not work)
- fprintf( stderr, "GRASS_INFO_ERROR(1,1): Cannot start shell %s\n",
- ( char* )shell.ascii() );
- exit( 1 );
- }
-
- mPid = pid;
-
- // Create socket notifier
- mOutNotifier = new QSocketNotifier( mFdMaster, QSocketNotifier::Read, this );
-
- QObject::connect( mOutNotifier, SIGNAL( activated( int ) ),
- this, SLOT( readStdout( int ) ) );
-
- // Set tab stops ???
- mTabStop.resize( 200 );
- for ( int i = 0 ; i * 8 < ( int )mTabStop.size(); i++ )
- {
- mTabStop[i*8] = true;
- }
-
- // Set trap to write history on SIGUSR1
- //QString trap = "trap 'history -w' SIGUSR1\015\012";
- QString trap = "trap 'history -w' SIGUSR1\015";
- write( mFdMaster, trap.ascii(), trap.length() );
- mText->clear();
-
- resizeTerminal();
- mValid = true;
-#endif // !WIN32
+ mTerminal->startShellProgram();
+ mTerminal->setFocus(Qt::MouseFocusReason);
}
QgsGrassShell::~QgsGrassShell()
{
- QgsDebugMsg( "entered." );
-
-#ifndef WIN32
- // This was old trick to write history
- /*
- write( mFdMaster, "exit\015\012", 6);
- while ( 1 )
- {
- readStdout(0);
-
- int status;
- if ( waitpid ( mPid, &status, WNOHANG ) > 0 ) break;
-
- struct timespec t, r;
- t.tv_sec = 0;
- t.tv_nsec = 10000000; // 0.01 s
- nanosleep ( &t, &r );
- }
- */
-
- // Write history
- if ( kill( mPid, SIGUSR1 ) == -1 )
- {
- QgsDebugMsg( QString( "cannot write history (signal SIGUSR1 to pid = %1)" ).arg( mPid ) );
- }
-
- QgsDebugMsg( QString( "kill shell pid = %1" ).arg( mPid ) );
- if ( kill( mPid, SIGTERM ) == -1 )
- {
- QgsDebugMsg( QString( "cannot kill shell pid = %1" ).arg( mPid ) );
- }
-#endif
}
-void QgsGrassShell::keyPressEvent( QKeyEvent * e )
-{
- QgsDebugMsg( "entered." );
- char s[10];
- int length = 0;
- int ret = 0;
-
- if ( !mValid ) return;
-
- mProgressBar->setProgress( 0, 100 );
-
- char c = ( char ) e->ascii();
- QgsDebugMsg( QString( "c = %1 key = %2 text = %3" ).arg(( int )c ).arg( e->key() ).arg( e->text().local8Bit().data() ) );
- s[0] = c;
- length = 1;
-
- // Set key down
- if ( e->key() == Qt::Key_Control ) mKeyDown[DownControl] = true;
- else if ( e->key() == Qt::Key_Shift ) mKeyDown[DownShift] = true;
- else if ( e->key() == Qt::Key_Alt ) mKeyDown[DownAlt] = true;
- else if ( e->key() == Qt::Key_Meta ) mKeyDown[DownMeta] = true;
-
- if ( c == 0 )
- {
- switch ( e->key() )
- {
- case Qt::Key_Up :
- strcpy( s, "\033[A" );
- length = 3;
- break;
-
- case Qt::Key_Down :
- strcpy( s, "\033[B" );
- length = 3;
- break;
-
- case Qt::Key_Right :
- strcpy( s, "\033[C" );
- length = 3;
- break;
-
- case Qt::Key_Left :
- strcpy( s, "\033[D" );
- length = 3;
- break;
- }
- }
-
- ret = write( mFdMaster, s, length );
- QgsDebugMsg( QString( "write ret = %1" ).arg( ret ) );
-}
-
-void QgsGrassShell::keyReleaseEvent( QKeyEvent * e )
+/* TODO: Implement something that resizes the terminal without
+ * crashes.
+void QgsGrassShell::resizeTerminal()
{
-#ifdef QGISDEBUG
- // QgsDebugMsg("entered.");
-#endif
-
- // Reset key down
- if ( e->key() == Qt::Key_Control ) mKeyDown[DownControl] = false;
- else if ( e->key() == Qt::Key_Shift ) mKeyDown[DownShift] = false;
- else if ( e->key() == Qt::Key_Alt ) mKeyDown[DownAlt] = false;
- else if ( e->key() == Qt::Key_Meta ) mKeyDown[DownMeta] = false;
+ //mTerminal->setSize(80, 25);
+ //mTerminal->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
}
+*/
-void QgsGrassShell::readStdout( int socket )
+void QgsGrassShell::closeShell()
{
-#ifdef QGISDEBUG
-// QgsDebugMsg("entered.");
-#endif
-
- char buf[4097];
- int len;
- while (( len = read( mFdMaster, buf, 4096 ) ) > 0 )
- {
- // Terminate string
- buf[len] = '\0';
-
- mStdoutBuffer.append( buf );
- }
-
- printStdout();
+ int index = mTabWidget->indexOf(this);
+ mTabWidget->removeTab(index);
+ delete this;
}
-void QgsGrassShell::printStdout()
+void QgsGrassShell::initTerminal(QTermWidget *terminal)
{
- // Debug
-#ifdef QGISDEBUG
- QString str;
- for ( int i = 0; i < ( int )mStdoutBuffer.length(); i++ )
- {
- int c = mStdoutBuffer[i];
- QString s = "";
- if ( c > '\037' && c != '\177' ) // control characters
+ QStringList args("");
+ QStringList env("");
+ // Set the shell program
+ QString shell = ::getenv("SHELL");
+ if (shell.isEmpty() || shell.isNull())
{
- str += c;
+ // if the shell isn't specified use the default one (/bin/bash)
+ terminal->setShellProgram(shell);
}
- else
- {
- str += "(c=" + QString::number( c, 8 ) + ")";
- }
- }
- QgsDebugMsg( "****** buffer ******" );
- QgsDebugMsg( QString( "-->%1<--" ).arg( str.toLocal8Bit().constData() ) );
-#endif
- eraseCursor();
- // To make it faster we want to print maximum lenght blocks from buffer
- while ( mStdoutBuffer.length() > 0 )
- {
- QgsDebugMsg( "------ cycle ------" );
-
- // Search control character
- int control = -1;
- for ( int i = 0; i < ( int )mStdoutBuffer.length(); i++ )
+ // Set shell program arguments
+ QFileInfo shellInfo(shell);
+ if (shellInfo.fileName() == "bash" || shellInfo.fileName() == "sh")
{
- int c = mStdoutBuffer[i];
- if ( c < '\037' || c == '\177' )
- {
- control = i;
- break;
- }
+ args << "--norc";
}
- QgsDebugMsg( QString( "control = %1" ).arg( control ) );
-
- // Process control character if found at index 0
- if ( control == 0 )
+ else if (shellInfo.fileName() == "tcsh" || shellInfo.fileName() == "csh")
{
- int c = mStdoutBuffer[0];
- QgsDebugMsg( QString( "c = %1" ).arg( QString::number( c, 8 ).local8Bit().data() ) );
-
- // control sequence
- if ( c == '\033' )
- {
-// QgsDebugMsg("control sequence");
-
- bool found = false;
-
- // It is sequence, so it should be at least one more character
- // wait for more data
- if ( mStdoutBuffer.length() < 2 ) break;
- if ( mStdoutBuffer[1] == ']' && mStdoutBuffer.length() < 3 ) break;
-
- // ESC ] Ps ; Pt BEL (xterm title hack)
- QRegExp rx( "\\](\\d+);([^\\a]+)\\a" );
- if ( rx.search( mStdoutBuffer, 1 ) == 1 )
- {
- int mlen = rx.matchedLength();
- QgsDebugMsg( QString( "ESC(set title): %1" ).arg( rx.cap( 2 ).local8Bit().data() ) );
- mStdoutBuffer.remove( 0, mlen + 1 );
- found = true;
- }
-
- if ( !found )
- {
- // ESC [ Pn ; Pn FINAL
- // or ESC [ = Pn ; Pn FINAL
- // or ESC [ = Pn ; Pn FINAL
- // TODO: QRegExp captures only last of repeated patterns
- // ( ; separated nums - (;\\d+)* )
- rx.setPattern( "\\[([?=])*(\\d+)*(;\\d+)*([A-z])" );
- if ( rx.search( mStdoutBuffer, 1 ) == 1 )
- {
- int mlen = rx.matchedLength();
- char final = rx.cap( 4 ).at( 0 ).latin1();
-
- QgsDebugMsg( QString( "final = %1" ).arg( final ) );
-// QgsDebugMsg(QString("ESC: %1").arg(rx.cap(0)));
-
- switch ( final )
- {
- case 'l' : // RM - Reset Mode
- case 'h' : // SM - Set Mode
- {
- int mode = -1;
- switch ( rx.cap( 2 ).toInt() )
- {
- case 4 :
- mode = Insert;
- break;
-
- default:
- QgsDebugMsg( QString( "ESC ignored: %1" ).arg( rx.cap( 0 ).local8Bit().data() ) );
- break;
- }
- if ( mode >= 0 )
- {
- if ( final == 'l' )
- resetMode( mode );
- else
- setMode( mode );
- }
- break;
- }
-
- case 'm' : // SGR - Select Graphic Rendition
- if ( rx.cap( 2 ).isEmpty() || rx.cap( 2 ).toInt() == 0 )
- {
- for ( int i = 0; i < RendetionCount; i++ )
- {
- mRendetion[i] = false;
- }
- }
- else
- {
- QgsDebugMsg( QString( "ESC SGR ignored: %1" ).arg( rx.cap( 0 ).local8Bit().data() ) );
- }
- break;
-
- case 'P' : // DCH - Delete Character
- {
- int n = rx.cap( 2 ).toInt();
- mText->setSelection( mParagraph, mIndex, mParagraph, mIndex + n, 0 );
- mText->removeSelectedText( 0 );
- break;
- }
-
- case 'K' : // EL - Erase In Line
- if ( rx.cap( 2 ).isEmpty() || rx.cap( 2 ).toInt() == 0 )
- {
- //mText->setSelectionAttributes ( 1, QColor(255,255,255), true );
- mText->setSelection( mParagraph, mIndex, mParagraph,
- mText->paragraphLength( mParagraph ), 0 );
- mText->removeSelectedText( 0 );
- }
- break;
-
- // TODO: multiple tab stops
- case 'H' : // Horizontal Tabulation Set (HTS)
- mTabStop[mIndex] = true;
- QgsDebugMsg( QString( "TAB set on %1" ).arg( mIndex ) );
- break;
-
- case 'g' : // Tabulation Clear (TBC)
- // ESC [ g Clears tab stop at the cursor
- // ESC [ 2 g Clears all tab stops in the line
- // ESC [ 3 g Clears all tab stops in the Page
- QgsDebugMsg( "TAB reset" );
- if ( rx.cap( 2 ).isEmpty() || rx.cap( 2 ).toInt() == 0 )
- {
- mTabStop[mIndex] = false;
- }
- else
- {
- for ( int i = 0; i < ( int )mTabStop.size(); i++ )
- mTabStop[mIndex] = false;
- }
- break;
-
- default:
- QgsDebugMsg( QString( "ESC ignored: %1" ).arg( rx.cap( 0 ).local8Bit().data() ) );
- break;
- }
-
- mStdoutBuffer.remove( 0, mlen + 1 );
- found = true;
- }
- }
-
- if ( !found )
- {
- // ESC # DIGIT
- rx.setPattern( "#(\\d)" );
- if ( rx.search( mStdoutBuffer, 1 ) == 1 )
- {
- QgsDebugMsg( QString( "ESC ignored: %1" ).arg( rx.cap( 0 ).local8Bit().data() ) );
- mStdoutBuffer.remove( 0, 3 );
- found = true;
- }
- }
-
- if ( !found )
- {
- // ESC CHARACTER
- rx.setPattern( "[A-z<>=]" );
- if ( rx.search( mStdoutBuffer, 1 ) == 1 )
- {
- QgsDebugMsg( QString( "ESC ignored: %1" ).arg( rx.cap( 0 ).local8Bit().data() ) );
- mStdoutBuffer.remove( 0, 2 );
- found = true;
- }
- }
-
- // TODO: it can happen that the sequence is not complete ->
- // no match -> how to distinguish unknown sequence from
- // missing characters
- if ( !found )
- {
- // For now move forward
- QgsDebugMsg( QString( "Unknown ESC ignored: %1" ).arg( mStdoutBuffer.mid( 1, 5 ).data() ) );
- mStdoutBuffer.remove( 0, 1 );
- }
- }
- else
- {
- // control character
- switch ( c )
- {
- case '\015' : // CR
-// QgsDebugMsg("CR");
- mStdoutBuffer.remove( 0, 1 );
- // TODO : back tab stops?
- mIndex = 0;
- break;
-
- case '\012' : // NL
-// QgsDebugMsg("NL");
- newLine();
- mStdoutBuffer.remove( 0, 1 );
- break;
-
- case '\010' : // BS
-// QgsDebugMsg("BS");
- mIndex--;
- mStdoutBuffer.remove( 0, 1 );
- break;
-
- case '\011' : // HT (tabulator)
- {
-// QgsDebugMsg("HT");
- QString space;
- for ( int i = mIndex; i < ( int )mTabStop.size(); i++ )
- {
- space.append( " " );
- if ( mTabStop[i] ) break;
- }
- insert( space );
- mStdoutBuffer.remove( 0, 1 );
- break;
- }
-
- case '>' : // Keypad Numeric Mode
- QgsDebugMsg( QString( "Keypad Numeric Mode ignored: %1" ).arg( QString::number( c, 8 ).local8Bit().data() ) );
- mStdoutBuffer.remove( 0, 2 );
- break;
-
- default : // unknown control, do nothing
- QgsDebugMsg( QString( "Unknown control char ignored: %1" ).arg( QString::number( c, 8 ).local8Bit().data() ) );
- mStdoutBuffer.remove( 0, 1 );
- break;
- }
- }
- continue;
+ args << "-f";
}
+ terminal->setArgs(args);
- // GRASS messages. GRASS messages start with GRASS_INFO_
- // and stop with \015\012 (\n)
+ // Set shell program enviroment variables
+ env << "GRASS_MESSAGE_FORMAT=";
+ env << "GRASS_UI_TERM=1";
+ env << "GISRC_MODE_MEMORY";
+ env << "PS1=GRASS > ";
+ env << "TERM=vt100";
+ terminal->setEnvironment(env);
- // first info
- QRegExp rxinfo( "GRASS_INFO_" );
- int message = rxinfo.search( mStdoutBuffer );
-
- if ( message == 0 ) // Info found at index 0
- {
- // First try percent
- QRegExp rxpercent( "GRASS_INFO_PERCENT: (\\d+)\\015\\012" );
- if ( rxpercent.search( mStdoutBuffer ) == 0 )
- {
- int mlen = rxpercent.matchedLength();
- int progress = rxpercent.cap( 1 ).toInt();
- mProgressBar->setProgress( progress, 100 );
- mStdoutBuffer.remove( 0, mlen );
- continue;
- }
-
- QRegExp rxwarning( "GRASS_INFO_WARNING\\(\\d+,\\d+\\): ([^\\015]*)\\015\\012" );
- QRegExp rxerror( "GRASS_INFO_ERROR\\(\\d+,\\d+\\): ([^\\015]*)\\015\\012" );
- QRegExp rxend( "GRASS_INFO_END\\(\\d+,\\d+\\)\\015\\012" );
-
- int mlen = 0;
- QString msg;
- QString img;
- if ( rxwarning.search( mStdoutBuffer ) == 0 )
- {
- mlen = rxwarning.matchedLength();
- msg = rxwarning.cap( 1 );
- img = QgsApplication::pkgDataPath() + "/themes/default/grass/grass_module_warning.png";
- }
- else if ( rxerror.search( mStdoutBuffer ) == 0 )
- {
- mlen = rxerror.matchedLength();
- msg = rxerror.cap( 1 );
- img = QgsApplication::pkgDataPath() + "/themes/default/grass/grass_module_error.png";
- }
-
- if ( mlen > 0 ) // found error or warning
- {
- QgsDebugMsg( QString( "MSG: %1" ).arg( msg.local8Bit().data() ) );
-
- // Delete all previous empty paragraphs.
- // Messages starts with \n (\015\012) which is previously interpreted
- // as new line, so it is OK delete it, but it is not quite correct
- // to delete more because it can be regular module output
- // but it does not look nice to have empty rows before
- removeEmptyParagraphs();
-
- msg.replace( "&", "&" );
- msg.replace( "<", "<" );
- msg.replace( ">", ">" );
- msg.replace( " ", " " );
-
- mText->setTextFormat( Qt::RichText );
- mText->append( "<img src=\"" + img + "\">" + msg );
- mParagraph++;
- mNewLine = true;
- mStdoutBuffer.remove( 0, mlen );
- continue;
- }
-
- if ( rxend.search( mStdoutBuffer ) == 0 )
- {
- mlen = rxend.matchedLength();
- mStdoutBuffer.remove( 0, mlen );
- continue;
- }
-
- // No complete message found => wait for input
- // TODO: 1) Sleep for a moment because GRASS writes
- // 1 character in loop
- // 2) Fix GRASS to write longer strings
- break;
- }
-
- // Print plain text
- int length = mStdoutBuffer.length();
- if ( control >= 0 ) length = control;
- if ( message >= 0 && ( control == -1 || control > message ) )
- {
- length = message;
- }
-
- if ( length > 0 )
- {
- QString out = mStdoutBuffer.left( length ) ;
- QgsDebugMsg( QString( "TXT: '%1'" ).arg( out.local8Bit().data() ) );
-
- insert( out );
-
- mStdoutBuffer.remove( 0, length );
- }
- }
-
- showCursor();
- mText->ensureCursorVisible();
+ // Look & Feel
+ terminal->setScrollBarPosition(QTermWidget::ScrollBarRight);
}
-
-void QgsGrassShell::removeEmptyParagraphs()
-{
- while ( mParagraph >= 0
- && mText->text( mParagraph ).stripWhiteSpace().length() <= 0 )
- {
- mText->removeParagraph( mParagraph );
- mParagraph--;
- }
- mIndex = mText->paragraphLength( mParagraph );
-}
-
-void QgsGrassShell::insert( QString s )
-{
- QgsDebugMsg( "insert()" );
-
- if ( s.isEmpty() ) return;
-
- // In theory mParagraph == mText->paragrephs()-1
- // but if something goes wrong (more paragraphs) we want to write
- // at the end
- if ( mParagraph > -1 && mParagraph != mText->paragraphs() - 1 )
- {
- QgsDebugMsg( "WRONG mParagraph!" );
- mNewLine = true;
- }
-
- // Bug?: QTextEdit::setOverwriteMode does not work, always 'insert'
- // -> if Insert mode is not set, delete first the string
- // to the right
- // mText->setOverwriteMode ( !mMode[Insert] ); // does not work
- if ( !mMode[Insert] && !mNewLine && mParagraph >= 0 &&
- mText->paragraphLength( mParagraph ) > mIndex )
- {
- QgsDebugMsg( QString( "erase old %1 chars " ).arg( mIndex + s.length() ) );
- mText->setSelection( mParagraph, mIndex, mParagraph, mIndex + s.length(), 0 );
- mText->removeSelectedText( 0 );
- }
-
- if ( mNewLine )
- {
- // Start new paragraph
- mText->setTextFormat( Qt::PlainText );
- mText->setCurrentFont( mFont );
- mText->append( s );
- mIndex = s.length();
- //mParagraph++;
- mParagraph = mText->paragraphs() - 1;
- mNewLine = false;
- }
- else
- {
- // Append to existing paragraph
- mText->setCursorPosition( mParagraph, mIndex );
- mText->setTextFormat( Qt::PlainText );
- mText->setCurrentFont( mFont );
- mText->insert( s );
-
- mIndex += s.length();
- }
-}
-
-void QgsGrassShell::newLine()
-{
- if ( mSkipLines > 0 )
- {
- mText->clear();
- mSkipLines--;
- }
- if ( mNewLine )
- {
- mText->setTextFormat( Qt::PlainText );
- mText->setCurrentFont( mFont );
- mText->append( " " );
- //mParagraph++;
- // To be sure that we are at the end
- mParagraph = mText->paragraphs() - 1;
- mIndex = 0;
- }
- mNewLine = true;
-}
-
-void QgsGrassShell::eraseCursor()
-{
- // Remove space representing cursor from the end of current paragraph
- if ( !mNewLine && mCursorSpace && mParagraph >= 0 )
- {
- mText->setSelection( mParagraph, mIndex, mParagraph, mIndex + 1, 0 );
- mText->removeSelectedText( 0 );
- }
- mCursorSpace = false;
-}
-
-void QgsGrassShell::showCursor()
-{
- // Do not highlite cursor if last printed paragraph was GRASS message
- if ( mNewLine ) return;
-
- // If cursor is at the end of paragraph add space
- if ( mParagraph >= 0 && mIndex > mText->paragraphLength( mParagraph ) - 1 )
- {
- mText->setCursorPosition( mParagraph, mIndex );
- mText->setCursorPosition( mParagraph, mIndex );
- mText->insert( " " );
- // Warning: do not increase mIndex,
- // the space if after current position
- mCursorSpace = true;
- }
-
- // Selection 1 is used as cursor highlite
- mText->setSelection( mParagraph, mIndex, mParagraph, mIndex + 1, 1 );
- mText->setSelectionAttributes( 1, QColor( 0, 0, 0 ), true );
-}
-
-void QgsGrassShell::mousePressEvent( QMouseEvent* e )
-{
- QgsDebugMsg( "mousePressEvent()" );
-
- if ( !mValid ) return;
-
- // paste clipboard
- if ( e->button() == Qt::MidButton )
- {
- QClipboard *cb = QApplication::clipboard();
- QString text = cb->text( QClipboard::Selection );
- write( mFdMaster, ( char* ) text.ascii(), text.length() );
- }
-}
-
-void QgsGrassShell::resizeTerminal()
-{
-#ifndef WIN32
- int width = mText->visibleWidth();
- int height = mText->visibleHeight();
-
- QFontMetrics fm( mFont );
- int col = ( int )( width / fm.width( "x" ) );
- int row = ( int )( height / fm.height() );
-
- struct winsize winSize;
- memset( &winSize, 0, sizeof( winSize ) );
- winSize.ws_row = row;
- winSize.ws_col = col;
-
- ioctl( mFdMaster, TIOCSWINSZ, ( char * )&winSize );
-#endif
-}
-
-void QgsGrassShell::readStderr()
-{
-}
-
-void QgsGrassShell::closeShell()
-{
- QgsDebugMsg( "entered." );
-
- mTabWidget->removePage( this );
- delete this;
-}
-
-
-QgsGrassShellText::QgsGrassShellText( QgsGrassShell *gs,
- QWidget * parent, const char *name )
- : Q3TextEdit( parent, name ),
- mShell( gs )
-{
-}
-
-QgsGrassShellText::~QgsGrassShellText() {}
-
-void QgsGrassShellText::contentsMousePressEvent( QMouseEvent* e )
-{
- QgsDebugMsg( "entered." );
- mShell->mousePressEvent( e );
- Q3TextEdit::contentsMousePressEvent( e );
-}
-
-void QgsGrassShellText::keyPressEvent( QKeyEvent * e )
-{
- QgsDebugMsg( "entered." );
- mShell->keyPressEvent( e );
-}
-
-void QgsGrassShellText::keyReleaseEvent( QKeyEvent * e )
-{
- QgsDebugMsg( "entered." );
- mShell->keyReleaseEvent( e );
-}
-
-void QgsGrassShellText::resizeEvent( QResizeEvent *e )
-{
- QgsDebugMsg( "resizeEvent()" );
- mShell->resizeTerminal();
- Q3TextEdit::resizeEvent( e );
-}
Modified: trunk/qgis/src/plugins/grass/qgsgrassshell.h
===================================================================
--- trunk/qgis/src/plugins/grass/qgsgrassshell.h 2009-04-27 05:39:32 UTC (rev 10663)
+++ trunk/qgis/src/plugins/grass/qgsgrassshell.h 2009-04-27 07:04:32 UTC (rev 10664)
@@ -1,9 +1,10 @@
/***************************************************************************
qgsgrassshell.h
--------------------------------------
- Date : Sun Sep 16 12:06:16 AKDT 2007
- Copyright : (C) 2007 by Gary E. Sherman
- Email : sherman at mrcc dot com
+ Date : Thu Apr 23 08:35:43 CEST 2009
+ Copyright : (C) 2009 by Lorenzo "Il Rugginoso" Masini
+ Email : lorenxo86 at gmail.com
+
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
@@ -12,228 +13,33 @@
* (at your option) any later version. *
* *
***************************************************************************/
+#include <QFrame>
-#include <Q3CString>
-#include <Q3Process>
-#include <Q3TextEdit>
-#include <QDialog>
-#include <QKeyEvent>
-#include <QMessageBox>
-#include <QMouseEvent>
-#include <QResizeEvent>
-#include <QSocketNotifier>
-#include <QString>
-
-#include "qgsgrasstools.h"
-#include "ui_qgsgrassshellbase.h"
-
+class QgsGrassTools;
class QTabWidget;
+class QTermWidget;
-class QgsGrassShellText;
-
-extern "C"
+class QgsGrassShell : public QFrame
{
-#include <stdio.h>
-}
-
-/* QgsGrassShellBase is simple pseudo terminal implementing
- * very limited subset of VT100. OTOH it parses GUI format of
- * messages printed by GRASS modules and adds icons for
- * WARNING and ERROR and it also resets progress bar if a module
- * uses G_percent() function.
- *
- * On UNIX pseudo terminal is represented by a pair of files
- * called master and slave which are connected.
- * On LINUX the files are called /dev/ptmx (master)
- * and /dev/pts/<number> (slave). The application emulating terminal
- * is connected master and the program running in terminal
- * (usually shell like bash,tcsh,sh) is connected to slave.
- *
- * There is no support for pseudo terminals in Qt. Everything
- * must be written using relatively low level C functions
- *
- * Input (LINUX):
- * keyboard -> X -> Qt -> QgsGrassShellBase::keyPressEvent()
- * -> /dev/ptmx -> /dev/pts/<number> -> shell
- *
- * Output (LINUX):
- * shell -> /dev/pts/<number> -> /dev/ptmx
- * -> QgsGrassShell::readStdout() -> QCString ->
- * -> QgsGrassShell::printStdout() -> QTextEdit
- *
- * Reading/printing of shel output is bease on two prerrequisites:
- * 1) Lines printed by shell are not endless
- * 2) GRASS GUI messages are always available complete on output
- * thus we read everything on output to buffer in cycle untill
- * we reach either EOF (no more data available) or NL (new line).
- * Then the data from buffer are printed to output QTextEdit.
- * Control characters, control sequences and GRASS GUI messages
- * are transformed to appropriate output written to QTextEdit.
- */
-
-class QgsGrassShell: public QDialog, private Ui::QgsGrassShellBase
-{
Q_OBJECT
- public:
- QgsGrassShell( QgsGrassTools *tools,
- QTabWidget * parent = 0, const char * name = 0 );
- ~QgsGrassShell();
+public:
+ QgsGrassShell(QgsGrassTools *tools, QTabWidget *parent = 0, const char *name = 0);
+ virtual ~QgsGrassShell();
- // Modes
- enum Mode
- {
- Origin = 0,
- Wrap,
- Insert,
- Screen,
- Cursor,
- NewLine,
- ModeCount
- };
-
- // Graphic Rendition
- enum Rendition
- {
- Bold = 0,
- Underline,
- Blink,
- Reverse,
- RendetionCount
- };
-
- // Pressed key
- enum KeyDown
- {
- DownControl = 0,
- DownShift,
- DownAlt,
- DownMeta,
- KeyDownCount
- };
-
- // Print maximum form standard input buffer
- void printStdout();
-
- // Print maximum form standard error buffer
- void printStderr();
-
- // Set / reset mode
- void setMode( int m ) { mMode[m] = true; }
- void resetMode( int m ) { mMode[m] = false; }
-
- // Insert text on current cursor position according to Insert mode
- void insert( QString );
-
- // Start new line
- void newLine();
-
- // Erase cursor highlight (space)
- void eraseCursor( void );
-
- // Highlite cursor
- void showCursor( void );
-
- // Reset pseudoterminal size
+/* TODO: Implement something that resizes the terminal without
+ * crashes.
+public slots:
void resizeTerminal();
+*/
- // Remove empty (or with whitespaces) paragraphs
- void removeEmptyParagraphs();
-
- // Reset cursor to current position
- //void setCursorPosition();
-
- public slots:
- void readStdout( int socket );
- void readStderr();
- void keyPressEvent( QKeyEvent * e );
- void keyReleaseEvent( QKeyEvent * e );
- void mousePressEvent( QMouseEvent* e );
+private slots:
void closeShell();
- signals:
+private:
+ void initTerminal(QTermWidget *terminal);
- private:
- //QProcess *mProcess;
- // Master file descriptor
- int mFdMaster;
-
- /**
- * * The socket descriptors for stdout.
- * */
- int out[2];
-
- QSocketNotifier *mOutNotifier;
-
- // Buffer for data read form shell stdout
- Q3CString mStdoutBuffer;
-
- // Buffer for data read form shell stderr
- Q3CString mStderrBuffer;
-
- // Shell process PID
- int mPid;
-
- // Modes
- bool mMode[ModeCount];
-
- // Flag for space if it was added at the end of paragraph to
- // visualize the cursor
- bool mCursorSpace;
-
- // Current cursor position
- int mParagraph;
- int mIndex;
-
- // Text widget
- QgsGrassShellText *mText;
-
- // Default text font
- QFont mFont;
-
- // Rendetion
- bool mRendetion[RendetionCount];
-
- // Pressed keys
- bool mKeyDown[KeyDownCount];
-
- // Application directory
- QString mAppDir;
-
- // GRASS Tools
+ QTermWidget *mTerminal;
QgsGrassTools *mTools;
-
- // Create new line in nex insert
- // This must be used because QTextEdit will remove all empty
- // paragraphs
- bool mNewLine;
-
- // Is it successfuly opened
- bool mValid;
-
- // Horizontal tabulator stop
- std::vector<bool> mTabStop;
-
- // How many lines to skip, used to skip output of first commands sent to shell
- int mSkipLines;
-
- // pointer to tab widget in which is the shell
- QTabWidget* mTabWidget;
+ QTabWidget *mTabWidget;
};
-
-class QgsGrassShellText : public Q3TextEdit
-{
- Q_OBJECT
- public:
- QgsGrassShellText( QgsGrassShell *,
- QWidget * parent = 0, const char * name = 0 );
- ~QgsGrassShellText();
- public slots:
- void contentsMousePressEvent( QMouseEvent * );
- void resizeEvent( QResizeEvent * );
- void keyPressEvent( QKeyEvent * e );
- void keyReleaseEvent( QKeyEvent * e );
- private:
- QgsGrassShell *mShell;
-};
-
Modified: trunk/qgis/src/plugins/grass/qgsgrassshellbase.ui
===================================================================
--- trunk/qgis/src/plugins/grass/qgsgrassshellbase.ui 2009-04-27 05:39:32 UTC (rev 10663)
+++ trunk/qgis/src/plugins/grass/qgsgrassshellbase.ui 2009-04-27 07:04:32 UTC (rev 10664)
@@ -1,75 +0,0 @@
-<ui version="4.0" >
- <class>QgsGrassShellBase</class>
- <widget class="QWidget" name="QgsGrassShellBase" >
- <property name="geometry" >
- <rect>
- <x>0</x>
- <y>0</y>
- <width>600</width>
- <height>434</height>
- </rect>
- </property>
- <property name="minimumSize" >
- <size>
- <width>600</width>
- <height>400</height>
- </size>
- </property>
- <property name="windowTitle" >
- <string>GRASS Shell</string>
- </property>
- <layout class="QVBoxLayout" >
- <item>
- <widget class="Q3Frame" name="mTextFrame" >
- <property name="sizePolicy" >
- <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShape" >
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow" >
- <enum>QFrame::Raised</enum>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QHBoxLayout" >
- <item>
- <widget class="Q3ProgressBar" name="mProgressBar" />
- </item>
- <item>
- <widget class="QPushButton" name="mCloseButton" >
- <property name="focusPolicy" >
- <enum>Qt::ClickFocus</enum>
- </property>
- <property name="text" >
- <string>Close</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <layoutdefault spacing="6" margin="11" />
- <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
- <customwidgets>
- <customwidget>
- <class>Q3Frame</class>
- <extends>QFrame</extends>
- <header>Qt3Support/Q3Frame</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>Q3ProgressBar</class>
- <extends>QFrame</extends>
- <header>Qt3Support/Q3ProgressBar</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
Modified: trunk/qgis/src/plugins/grass/qgsgrasstools.cpp
===================================================================
--- trunk/qgis/src/plugins/grass/qgsgrasstools.cpp 2009-04-27 05:39:32 UTC (rev 10663)
+++ trunk/qgis/src/plugins/grass/qgsgrasstools.cpp 2009-04-27 07:04:32 UTC (rev 10664)
@@ -214,9 +214,13 @@
// We must call resize to reset COLUMNS enviroment variable
// used by bash !!!
+
+/* TODO: Implement something that resizes the terminal without
+ * crashes.
#ifndef WIN32
if ( sh ) sh->resizeTerminal();
#endif
+*/
}
bool QgsGrassTools::loadConfig( QString filePath )
More information about the QGIS-commit
mailing list