Back to index

salome-gui  6.5.0
PyConsole_Editor.cxx
Go to the documentation of this file.
00001 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
00002 //
00003 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
00004 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
00005 //
00006 // This library is free software; you can redistribute it and/or
00007 // modify it under the terms of the GNU Lesser General Public
00008 // License as published by the Free Software Foundation; either
00009 // version 2.1 of the License.
00010 //
00011 // This library is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014 // Lesser General Public License for more details.
00015 //
00016 // You should have received a copy of the GNU Lesser General Public
00017 // License along with this library; if not, write to the Free Software
00018 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00019 //
00020 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
00021 //
00022 
00023 //  SALOME SALOMEGUI : implementation of desktop and GUI kernel
00024 // File   : PyConsole_Editor.cxx
00025 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
00026 //
00096 #include "PyConsole_Interp.h"   // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
00097 #include "PyConsole_Editor.h"
00098 #include <PyInterp_Dispatcher.h>
00099 
00100 #include <SUIT_Tools.h>
00101 #include <SUIT_FileDlg.h>
00102 #include <SUIT_MessageBox.h>
00103 #include <SUIT_FileValidator.h>
00104 
00105 #include <QApplication>
00106 #include <QClipboard>
00107 #include <QDropEvent>
00108 #include <QEvent>
00109 #include <QKeyEvent>
00110 #include <QMouseEvent>
00111 #include <QScrollBar>
00112 #include <QTextBlock>
00113 #include <QTextCursor>
00114 #include <QTextDocument>
00115 #include <QTextStream>
00116 
00117 static QString READY_PROMPT = ">>> ";
00118 static QString DOTS_PROMPT  = "... ";
00119 #define PROMPT_SIZE myPrompt.length()
00120 
00121 #define PRINT_EVENT 65432
00122 
00123 
00124 class DumpCommandsFileValidator : public SUIT_FileValidator
00125 {
00126  public:
00127   DumpCommandsFileValidator( QWidget* parent = 0 ) : SUIT_FileValidator ( parent ) {};
00128   virtual ~DumpCommandsFileValidator() {};
00129   virtual bool canSave( const QString& file, bool permissions );
00130 };
00131 
00132 bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions)
00133 {
00134   QFileInfo fi( file );
00135   if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) {
00136     SUIT_MessageBox::critical( parent(),
00137                                QObject::tr("WRN_WARNING"),
00138                                QObject::tr("WRN_FILE_NAME_BAD") );
00139     return false;
00140   }
00141   return SUIT_FileValidator::canSave( file, permissions);
00142 }
00143 
00150 class ExecCommand : public PyInterp_LockRequest
00151 {
00152 public:
00162   ExecCommand( PyInterp_Interp*        theInterp, 
00163                const QString&          theCommand,
00164                PyConsole_Editor*       theListener, 
00165                bool                    sync = false )
00166     : PyInterp_LockRequest( theInterp, theListener, sync ),
00167       myCommand( theCommand ), myState( PyInterp_Event::ES_OK )
00168   {}
00169 
00170 protected:
00175   virtual void execute()
00176   {
00177     if ( myCommand != "" )
00178     {
00179       int ret = getInterp()->run( myCommand.toUtf8().data() );
00180       if ( ret < 0 )
00181         myState = PyInterp_Event::ES_ERROR;
00182       else if ( ret > 0 )
00183         myState = PyInterp_Event::ES_INCOMPLETE;
00184     } 
00185   }
00186 
00191   virtual QEvent* createEvent() const
00192   {
00193     if ( IsSync() )
00194       QCoreApplication::sendPostedEvents( listener(), PRINT_EVENT );
00195     return new PyInterp_Event( myState, (PyInterp_Request*)this );    
00196   }
00197 
00198 private:
00199   QString myCommand;   
00200   int     myState;     
00201 };
00202 
00209 class PrintEvent : public QEvent
00210 {
00211 public:
00216   PrintEvent( const char* c ) : QEvent( (QEvent::Type)PRINT_EVENT ), myText( c ) {}
00221   QString text() const { return myText; }
00222 
00223 private:
00224   QString myText; 
00225 };
00226 
00227 void staticCallback( void* data, char* c )
00228 {
00229   if(!((PyConsole_Editor*)data)->isSuppressOutput())
00230     QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c ) ); 
00231 }
00232 
00240 PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, 
00241                                     QWidget*          theParent )
00242 : QTextEdit( theParent ),
00243   myInterp( 0 ),
00244   myCmdInHistory( -1 ),
00245   myEventLoop( 0 ),
00246   myShowBanner( true ),
00247   myIsSync( false ),
00248   myIsSuppressOutput( false )
00249 {
00250   QString fntSet( "" );
00251   QFont aFont = SUIT_Tools::stringToFont( fntSet );
00252   setFont( aFont );
00253   setUndoRedoEnabled( false );
00254 
00255   myPrompt = READY_PROMPT;
00256   setLineWrapMode( QTextEdit::WidgetWidth );
00257   setWordWrapMode( QTextOption::WrapAnywhere );
00258   setAcceptRichText( false );
00259 
00260   theInterp->setvoutcb( staticCallback, this );
00261   theInterp->setverrcb( staticCallback, this );
00262 
00263   // san - This is necessary for troubleless initialization
00264   onPyInterpChanged( theInterp );
00265 }
00266 
00272 PyConsole_Editor::~PyConsole_Editor()
00273 {
00274 }
00275 
00282 bool PyConsole_Editor::isSync() const
00283 {
00284   return myIsSync;
00285 }
00286 
00297 void PyConsole_Editor::setIsSync( const bool on )
00298 {
00299   myIsSync = on;
00300 }
00301 
00308 bool PyConsole_Editor::isSuppressOutput() const
00309 {
00310   return myIsSuppressOutput;
00311 }
00312 
00321 void PyConsole_Editor::setIsSuppressOutput( const bool on )
00322 {
00323   myIsSuppressOutput = on;
00324 }
00325 
00332 bool PyConsole_Editor::isShowBanner() const
00333 {
00334   return myShowBanner;
00335 }
00336 
00345 void PyConsole_Editor::setIsShowBanner( const bool on )
00346 {
00347   if ( myShowBanner != on ) {
00348     myShowBanner = on;
00349     clear();
00350   }
00351 }
00352 
00357 QSize PyConsole_Editor::sizeHint() const
00358 {
00359   QFontMetrics fm( font() );
00360   int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
00361   QSize s(100, fm.lineSpacing()*nbLines);
00362   return s;
00363 }
00364 
00370 void PyConsole_Editor::addText( const QString& str, 
00371                                 const bool     newBlock )
00372 {
00373   moveCursor( QTextCursor::End );
00374   if ( newBlock )
00375     textCursor().insertBlock();
00376   textCursor().insertText( str );
00377   moveCursor( QTextCursor::End );
00378   ensureCursorVisible();
00379 }
00380 
00386 void PyConsole_Editor::exec( const QString& command )
00387 {
00388   if ( isReadOnly() ) {
00389     // some interactive command is being executed in this editor...
00390     // shedule the command to the queue
00391     myQueue.push_back( command );
00392     return;
00393   }
00394   // remove last line
00395   moveCursor( QTextCursor::End );
00396   moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
00397   textCursor().removeSelectedText();
00398   // set "ready" prompt
00399   myPrompt = READY_PROMPT;
00400   // clear command buffer
00401   myCommandBuffer.truncate( 0 );
00402   // unset history browsing mode
00403   myCmdInHistory = -1;
00404   // print command line by line
00405   QString cmd = command;
00406   if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
00407   QStringList lines = command.split( "\n" );
00408   for ( int i = 0; i < lines.size(); i++ ) {
00409     if ( !lines[i].trimmed().isEmpty() )
00410       myHistory.push_back( lines[i] );
00411     addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
00412   }
00413   // IPAL20182
00414   addText( "", true );
00415   // set read-only mode
00416   setReadOnly( true );
00417   // set busy cursor
00418   setCursor( Qt::BusyCursor );
00419   
00420   // post a request to execute Python command;
00421   // editor will be informed via a custom event that execution has been completed
00422   PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
00423 }
00424 
00430 PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
00431 {
00432   return new ExecCommand( myInterp, command, this, isSync() );
00433 }
00434 
00441 void PyConsole_Editor::execAndWait( const QString& command )
00442 {
00443   // already running ?
00444   if( myEventLoop )
00445     return;
00446 
00447   // create new event loop
00448   myEventLoop = new QEventLoop( this );
00449   // execute command
00450   exec( command );
00451   // run event loop
00452   myEventLoop->exec();
00453   // delete event loop after command is processed
00454   delete myEventLoop;
00455   myEventLoop = 0;
00456 }
00457 
00463 void PyConsole_Editor::handleReturn()
00464 {
00465   // get last line
00466   QTextBlock par = document()->end().previous();
00467   if ( !par.isValid() ) return;
00468 
00469   // get command
00470   QString cmd = par.text().remove( 0, PROMPT_SIZE );
00471   // extend the command buffer with the current command 
00472   myCommandBuffer.append( cmd );
00473   // add command to the history
00474   if ( !cmd.trimmed().isEmpty() )
00475     myHistory.push_back( cmd );
00476 
00477   // IPAL19397
00478   addText( "", true ); 
00479   
00480   // set read-only mode
00481   setReadOnly( true );
00482   // set busy cursor
00483   setCursor( Qt::BusyCursor );
00484   
00485   // post a request to execute Python command;
00486   // editor will be informed via a custom event that execution has been completed
00487   PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
00488 }
00489 
00496 void PyConsole_Editor::dropEvent( QDropEvent* event )
00497 {
00498   // get the initial drop position
00499   QPoint pos = event->pos();
00500   QTextCursor cur = cursorForPosition( event->pos() );
00501   // if the position is not in the last line move it to the end of the command line
00502   if ( cur.position() < document()->end().previous().position() + PROMPT_SIZE ) {
00503     moveCursor( QTextCursor::End );
00504     pos = cursorRect().center();
00505   }
00506   // create new drop event and use it instead of the original
00507   QDropEvent de( pos,
00508                  event->possibleActions(),
00509                  event->mimeData(),
00510                  event->mouseButtons(),
00511                  event->keyboardModifiers(),
00512                  event->type() );
00513   QTextEdit::dropEvent( &de );
00514   // accept the original event
00515   event->acceptProposedAction();
00516 }
00517 
00525 void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
00526 {
00527   if ( event->button() == Qt::LeftButton ) {
00528     QTextEdit::mouseReleaseEvent( event );
00529     //copy();
00530   }
00531   else if ( event->button() == Qt::MidButton ) {
00532     QString text;
00533     if ( QApplication::clipboard()->supportsSelection() )
00534       text = QApplication::clipboard()->text( QClipboard::Selection );
00535     if ( text.isEmpty() )
00536       text = QApplication::clipboard()->text( QClipboard::Clipboard );
00537     QTextCursor cur = cursorForPosition( event->pos() );
00538     // if the position is not in the last line move it to the end of the command line
00539     if ( cur.position() < document()->end().previous().position() + PROMPT_SIZE ) {
00540       moveCursor( QTextCursor::End );
00541     }
00542     else {
00543       setTextCursor( cur );
00544     }
00545     textCursor().clearSelection();
00546     textCursor().insertText( text );
00547   }
00548   else {
00549     QTextEdit::mouseReleaseEvent( event );
00550   }
00551 }
00552 
00560 bool PyConsole_Editor::isCommand( const QString& str ) const
00561 {
00562   return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
00563 }
00564 
00572 void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
00573 {
00574   // get cursor position
00575   QTextCursor cur = textCursor();
00576   int curLine = cur.blockNumber();
00577   int curCol  = cur.columnNumber();
00578 
00579   // get last edited line
00580   int endLine = document()->blockCount()-1;
00581 
00582   // get pressed key code
00583   int aKey = event->key();
00584 
00585   // check if <Ctrl> is pressed
00586   bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
00587   // check if <Shift> is pressed
00588   bool shftPressed = event->modifiers() & Qt::ShiftModifier;
00589 
00590   if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
00591     // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
00592     myCommandBuffer.truncate( 0 );
00593     myPrompt = READY_PROMPT;
00594     addText( myPrompt, true );
00595     horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
00596     return;
00597   }
00598   else if ( ctrlPressed && aKey == Qt::Key_C ) {
00599     // process <Ctrl>+<C> key-binding : copy
00600     copy();
00601     return;
00602   }
00603   else if ( ctrlPressed && aKey == Qt::Key_X ) {
00604     // process <Ctrl>+<X> key-binding : cut
00605     cut();
00606     return;
00607   }
00608   else if ( ctrlPressed && aKey == Qt::Key_V ) {
00609     // process <Ctrl>+<V> key-binding : paste
00610     paste();
00611     return;
00612   }
00613 
00614   // check for printed key
00615   aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
00616 
00617   switch ( aKey ) {
00618   case 0 :
00619     // any printed key: just print it
00620     {
00621       if ( curLine < endLine || curCol < PROMPT_SIZE ) {
00622         moveCursor( QTextCursor::End );
00623       }
00624       QTextEdit::keyPressEvent( event );
00625       break;
00626     }
00627   case Qt::Key_Return:
00628   case Qt::Key_Enter:
00629     // <Enter> key: process the current command
00630     {
00631       handleReturn();
00632       break;
00633     }
00634   case Qt::Key_Up:
00635     // <Up> arrow key: process as follows:
00636     // - without <Ctrl>, <Shift> modifiers: previous command in history
00637     // - with <Ctrl> modifier key pressed:  move cursor one row up without selection
00638     // - with <Shift> modifier key pressed: move cursor one row up with selection
00639     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
00640     {
00641       if ( ctrlPressed && shftPressed ) {
00642         int value   = verticalScrollBar()->value();
00643         int spacing = fontMetrics().lineSpacing();
00644         verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
00645       }
00646       else if ( shftPressed || ctrlPressed ) {
00647         if ( curLine > 0 )
00648           moveCursor( QTextCursor::Up, 
00649                       shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
00650       }
00651       else { 
00652         if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
00653           // set history browsing mode
00654           myCmdInHistory = myHistory.count();
00655           // remember current command
00656           QTextBlock par = document()->end().previous();
00657           myCurrentCommand = par.text().remove( 0, PROMPT_SIZE );
00658         }
00659         if ( myCmdInHistory > 0 ) {
00660           myCmdInHistory--;
00661           // get previous command in the history
00662           QString previousCommand = myHistory.at( myCmdInHistory );
00663           // print previous command
00664           moveCursor( QTextCursor::End );
00665           moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
00666           textCursor().removeSelectedText();
00667           addText( myPrompt + previousCommand ); 
00668           // move cursor to the end
00669           moveCursor( QTextCursor::End );
00670         }
00671       }
00672       break;
00673     }
00674   case Qt::Key_Down:
00675     // <Down> arrow key: process as follows:
00676     // - without <Ctrl>, <Shift> modifiers: next command in history
00677     // - with <Ctrl> modifier key pressed:  move cursor one row down without selection
00678     // - with <Shift> modifier key pressed: move cursor one row down with selection
00679     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
00680     {
00681       if ( ctrlPressed && shftPressed ) {
00682         int value   = verticalScrollBar()->value();
00683         int maxval  = verticalScrollBar()->maximum();
00684         int spacing = fontMetrics().lineSpacing();
00685         verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
00686       }
00687       else if ( shftPressed || ctrlPressed) {
00688         if ( curLine < endLine )
00689           moveCursor( QTextCursor::Down, 
00690                       shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
00691       }
00692       else { 
00693         if ( myCmdInHistory >= 0 ) {
00694           // get next command in the history
00695           myCmdInHistory++;
00696           QString nextCommand;
00697           if ( myCmdInHistory < myHistory.count() ) {
00698             // next command in history
00699             nextCommand = myHistory.at( myCmdInHistory );
00700           }
00701           else {
00702             // end of history is reached
00703             // last printed command
00704             nextCommand = myCurrentCommand;
00705             // unset history browsing mode
00706             myCmdInHistory = -1;
00707           }
00708           // print next or current command
00709           moveCursor( QTextCursor::End );
00710           moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
00711           textCursor().removeSelectedText();
00712           addText( myPrompt + nextCommand );
00713           // move cursor to the end
00714           moveCursor( QTextCursor::End );
00715         }
00716       }
00717       break;
00718     }
00719   case Qt::Key_Left:
00720     // <Left> arrow key: process as follows:
00721     // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
00722     // - with <Ctrl> modifier key pressed:  move one word left (taking into account prompt)
00723     // - with <Shift> modifier key pressed: move one symbol left with selection
00724     // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
00725     {
00726       QString txt = textCursor().block().text();
00727       if ( !shftPressed && isCommand( txt ) && curCol <= PROMPT_SIZE ) {
00728         moveCursor( QTextCursor::Up );
00729         moveCursor( QTextCursor::EndOfBlock );
00730       }
00731       else {
00732         QTextEdit::keyPressEvent( event );
00733       }
00734       break;
00735     }
00736   case Qt::Key_Right:
00737     // <Right> arrow key: process as follows:
00738     // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
00739     // - with <Ctrl> modifier key pressed:  move one word right (taking into account prompt)
00740     // - with <Shift> modifier key pressed: move one symbol right with selection
00741     // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
00742     {
00743       QString txt = textCursor().block().text();
00744       if ( !shftPressed ) {
00745         if ( curCol < txt.length() ) {
00746           if ( isCommand( txt ) && curCol < PROMPT_SIZE ) {
00747             cur.setPosition( cur.block().position() + PROMPT_SIZE );
00748             setTextCursor( cur );
00749             break;
00750           }
00751         }
00752         else {
00753           if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
00754             cur.setPosition( cur.position() + PROMPT_SIZE+1 );
00755             setTextCursor( cur );
00756             horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
00757             break;
00758           }
00759         }
00760       }
00761       QTextEdit::keyPressEvent( event );
00762       break;
00763     }
00764   case Qt::Key_PageUp:
00765     // <PageUp> key: process as follows:
00766     // - without <Ctrl>, <Shift> modifiers: first command in history
00767     // - with <Ctrl> modifier key pressed:  move cursor one page up without selection
00768     // - with <Shift> modifier key pressed: move cursor one page up with selection
00769     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
00770     {
00771       if ( ctrlPressed && shftPressed ) {
00772         verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
00773       }
00774       else if ( shftPressed || ctrlPressed ) {
00775         bool moved = false;
00776         qreal lastY = cursorRect( cur ).top();
00777         qreal distance = 0;
00778         // move using movePosition to keep the cursor's x
00779         do {
00780           qreal y = cursorRect( cur ).top();
00781           distance += qAbs( y - lastY );
00782           lastY = y;
00783           moved = cur.movePosition( QTextCursor::Up, 
00784                                     shftPressed ? QTextCursor::KeepAnchor : 
00785                                                   QTextCursor::MoveAnchor );
00786         } while ( moved && distance < viewport()->height() );
00787         if ( moved ) {
00788           cur.movePosition( QTextCursor::Down, 
00789                             shftPressed ? QTextCursor::KeepAnchor : 
00790                                           QTextCursor::MoveAnchor );
00791           verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
00792         }
00793         setTextCursor( cur );
00794       }
00795       else { 
00796         if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
00797           // set history browsing mode
00798           myCmdInHistory = myHistory.count();
00799           // remember current command
00800           QTextBlock par = document()->end().previous();
00801           myCurrentCommand = par.text().remove( 0, PROMPT_SIZE );
00802         }
00803         if ( myCmdInHistory > 0 ) {
00804           myCmdInHistory = 0;
00805           // get very first command in the history
00806           QString firstCommand = myHistory.at( myCmdInHistory );
00807           // print first command
00808           moveCursor( QTextCursor::End );
00809           moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
00810           textCursor().removeSelectedText();
00811           addText( myPrompt + firstCommand ); 
00812           // move cursor to the end
00813           moveCursor( QTextCursor::End );
00814         }
00815       }
00816       break;
00817     }
00818   case Qt::Key_PageDown:
00819     // <PageDown> key: process as follows:
00820     // - without <Ctrl>, <Shift> modifiers: last command in history
00821     // - with <Ctrl> modifier key pressed:  move cursor one page down without selection
00822     // - with <Shift> modifier key pressed: move cursor one page down with selection
00823     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
00824     {
00825       if ( ctrlPressed && shftPressed ) {
00826         verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
00827       }
00828       else if ( shftPressed || ctrlPressed ) {
00829         bool moved = false;
00830         qreal lastY = cursorRect( cur ).top();
00831         qreal distance = 0;
00832         // move using movePosition to keep the cursor's x
00833         do {
00834           qreal y = cursorRect( cur ).top();
00835           distance += qAbs( y - lastY );
00836           lastY = y;
00837           moved = cur.movePosition( QTextCursor::Down, 
00838                                     shftPressed ? QTextCursor::KeepAnchor : 
00839                                                   QTextCursor::MoveAnchor );
00840         } while ( moved && distance < viewport()->height() );
00841         if ( moved ) {
00842           cur.movePosition( QTextCursor::Up, 
00843                             shftPressed ? QTextCursor::KeepAnchor : 
00844                                           QTextCursor::MoveAnchor );
00845           verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
00846         }
00847         setTextCursor( cur );
00848       }
00849       else { 
00850         if ( myCmdInHistory >= 0 ) {
00851           // unset history browsing mode
00852           myCmdInHistory = -1;
00853           // print current command
00854           moveCursor( QTextCursor::End );
00855           moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
00856           textCursor().removeSelectedText();
00857           addText( myPrompt + myCurrentCommand ); 
00858           // move cursor to the end
00859           moveCursor( QTextCursor::End );
00860         }
00861       }
00862       break;
00863     }
00864   case Qt::Key_Home: 
00865     // <Home> key: process as follows:
00866     // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
00867     // - with <Ctrl> modifier key pressed:  move cursor to the very first symbol without selection
00868     // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
00869     // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
00870     {
00871       if ( ctrlPressed ) { 
00872         moveCursor( QTextCursor::Start, 
00873                     shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
00874       }
00875       else {
00876         QString txt = textCursor().block().text();
00877         if ( isCommand( txt ) ) {
00878           if ( shftPressed ) {
00879             if ( curCol > PROMPT_SIZE ) {
00880               cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
00881               cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, PROMPT_SIZE );
00882             }
00883           }
00884           else {
00885             cur.movePosition( QTextCursor::StartOfLine );
00886             cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PROMPT_SIZE );
00887           }
00888           setTextCursor( cur );
00889         }
00890         else {
00891           moveCursor( QTextCursor::StartOfBlock, 
00892                       shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
00893         }
00894         horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
00895       }
00896       break;
00897     }
00898   case Qt::Key_End:
00899     // <End> key: process as follows:
00900     // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
00901     // - with <Ctrl> modifier key pressed:  move cursor to the very last symbol without selection
00902     // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
00903     // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
00904     {
00905       moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock, 
00906                   shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
00907       break;
00908     }  
00909   case Qt::Key_Backspace :
00910     // <Backspace> key: process as follows
00911     // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
00912     // - with <Shift> modifier key pressed: delete previous word
00913     // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
00914     // works only for last (command) line
00915     {
00916       if ( cur.hasSelection() ) {
00917         cut();
00918       }
00919       else if ( cur.position() > document()->end().previous().position() + PROMPT_SIZE ) {
00920         if ( shftPressed ) {
00921           moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
00922           textCursor().removeSelectedText();
00923         }
00924         else if ( ctrlPressed ) {
00925           cur.setPosition( document()->end().previous().position() + PROMPT_SIZE, 
00926                            QTextCursor::KeepAnchor );
00927           setTextCursor( cur );
00928           textCursor().removeSelectedText();
00929         }
00930         else {
00931           QTextEdit::keyPressEvent( event );
00932         }
00933       }
00934       else {
00935         cur.setPosition( document()->end().previous().position() + PROMPT_SIZE );
00936         setTextCursor( cur );
00937         horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
00938       }
00939       break;
00940     }
00941   case Qt::Key_Delete :
00942     // <Delete> key: process as follows
00943     // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
00944     // - with <Shift> modifier key pressed: delete next word
00945     // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
00946     // works only for last (command) line
00947     {
00948       if ( cur.hasSelection() ) {
00949         cut();
00950       }
00951       else if ( cur.position() > document()->end().previous().position() + PROMPT_SIZE-1 ) {
00952         if ( shftPressed ) {
00953           moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
00954           textCursor().removeSelectedText();
00955         }
00956         else if ( ctrlPressed ) {
00957           moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
00958           textCursor().removeSelectedText();
00959         }
00960         else {
00961           QTextEdit::keyPressEvent( event );
00962         }
00963       }
00964       else {
00965         cur.setPosition( document()->end().previous().position() + PROMPT_SIZE );
00966         setTextCursor( cur );
00967         horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
00968       }
00969       break;
00970     }
00971   case Qt::Key_Insert :
00972     // <Insert> key: process as follows
00973     // - with <Ctrl> modifier key pressed:  copy()
00974     // - with <Shift> modifier key pressed: paste() to the command line
00975     {
00976       if ( ctrlPressed ) {
00977         copy();
00978       }
00979       else if ( shftPressed ) {
00980         paste();
00981       }
00982       else
00983         QTextEdit::keyPressEvent( event );
00984       break;
00985     }
00986   }
00987 }
00988 
00993 void PyConsole_Editor::customEvent( QEvent* event )
00994 {
00995   switch( event->type() )
00996   {
00997   case PRINT_EVENT:
00998     {
00999       PrintEvent* pe=(PrintEvent*)event;
01000       addText( pe->text() );
01001       return;
01002     }
01003   case PyInterp_Event::ES_OK:
01004   case PyInterp_Event::ES_ERROR:
01005   {
01006     // clear command buffer
01007     myCommandBuffer.truncate( 0 );
01008     // add caret return line if necessary
01009     QTextBlock par = document()->end().previous();
01010     QString txt = par.text();
01011     txt.truncate( txt.length() - 1 );
01012     // IPAL19397 : addText moved to handleReturn() method
01013     //if ( !txt.isEmpty() )
01014     //  addText( "", true );
01015     // set "ready" prompt
01016     myPrompt = READY_PROMPT;
01017     addText( myPrompt );
01018     // unset busy cursor
01019     unsetCursor();
01020     // stop event loop (if running)
01021     if ( myEventLoop )
01022       myEventLoop->exit();
01023     break;
01024   }
01025   case PyInterp_Event::ES_INCOMPLETE:
01026   {
01027     // extend command buffer (multi-line command)
01028     myCommandBuffer.append( "\n" );
01029     // add caret return line if necessary
01030     QTextBlock par = document()->end().previous();
01031     QString txt = par.text();
01032     txt.truncate( txt.length() - 1 );
01033     // IPAL19397 : addText moved to handleReturn() method
01034     //if ( !txt.isEmpty() )
01035     //  addText( "", true );
01036     // set "dot" prompt
01037     myPrompt = DOTS_PROMPT;
01038     addText( myPrompt/*, true*/ ); // IPAL19397
01039     // unset busy cursor
01040     unsetCursor();
01041     // stop event loop (if running)
01042     if ( myEventLoop )
01043       myEventLoop->exit();
01044     break;
01045   }
01046   default:
01047     QTextEdit::customEvent( event );
01048   }
01049   
01050   // unset read-only state
01051   setReadOnly( false );
01052   // unset history browsing mode
01053   myCmdInHistory = -1;
01054 
01055   if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
01056   {
01057     // process the next sheduled command from the queue (if there is any)
01058     QString nextcmd = myQueue[0];
01059     myQueue.pop_front();
01060     exec( nextcmd );
01061   }
01062 }
01063 
01070 void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
01071 {
01072   if ( myInterp != interp 
01073        // Force read-only state and wait cursor when myInterp is NULL
01074       || !myInterp ) {
01075     myInterp = interp;
01076     if ( myInterp ) {
01077       // print banner
01078       myBanner = myInterp->getbanner().c_str();
01079       if ( isShowBanner() )
01080        addText( myBanner );
01081       // clear command buffer
01082       myCommandBuffer.truncate(0);
01083       // unset read-only state
01084       setReadOnly( false );
01085       // unset history browsing mode
01086       myCmdInHistory = -1;
01087       // add prompt
01088       addText( myPrompt );
01089       // unset busy cursor
01090       viewport()->unsetCursor();
01091       // stop event loop (if running)
01092       if( myEventLoop)
01093         myEventLoop->exit();
01094     }
01095     else {
01096       // clear contents
01097       clear();
01098       // set read-only state
01099       setReadOnly( true );
01100       // set busy cursor
01101       setCursor( Qt::WaitCursor );
01102     }
01103   }
01104 }
01105 
01112 void PyConsole_Editor::cut()
01113 {
01114   QTextCursor cur = textCursor();
01115   if ( cur.hasSelection() ) {
01116     QApplication::clipboard()->setText( cur.selectedText() );
01117     int startSelection = cur.selectionStart();
01118     if ( startSelection < document()->end().previous().position() + PROMPT_SIZE )
01119       startSelection = document()->end().previous().position() + PROMPT_SIZE;
01120     int endSelection = cur.selectionEnd();
01121     if ( endSelection < document()->end().previous().position() + PROMPT_SIZE )
01122       endSelection = document()->end().previous().position() + PROMPT_SIZE;
01123     cur.setPosition( startSelection );
01124     cur.setPosition( endSelection, QTextCursor::KeepAnchor );
01125     horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
01126     setTextCursor( cur );
01127     textCursor().removeSelectedText();
01128   }
01129 }
01130 
01137 void PyConsole_Editor::paste()
01138 {
01139   QTextCursor cur = textCursor();
01140   if ( cur.hasSelection() ) {
01141     int startSelection = cur.selectionStart();
01142     if ( startSelection < document()->end().previous().position() + PROMPT_SIZE )
01143       startSelection = document()->end().previous().position() + PROMPT_SIZE;
01144     int endSelection = cur.selectionEnd();
01145     if ( endSelection < document()->end().previous().position() + PROMPT_SIZE )
01146       endSelection = document()->end().previous().position() + PROMPT_SIZE;
01147     cur.setPosition( startSelection );
01148     cur.setPosition( endSelection, QTextCursor::KeepAnchor );
01149     horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
01150     setTextCursor( cur );
01151     textCursor().removeSelectedText();
01152   }
01153   if ( textCursor().position() < document()->end().previous().position() + PROMPT_SIZE )
01154     moveCursor( QTextCursor::End );
01155   QTextEdit::paste();
01156 }
01157 
01164 void PyConsole_Editor::clear()
01165 {
01166   QTextEdit::clear();
01167   if ( isShowBanner() )
01168     addText( myBanner );
01169   myPrompt = READY_PROMPT;
01170   addText( myPrompt );
01171 }
01172 
01176 void PyConsole_Editor::dump()
01177 {
01178   QStringList aFilters;
01179   aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
01180   
01181   QString fileName = SUIT_FileDlg::getFileName( this, QString(),
01182                                  aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
01183                                  false, true, new DumpCommandsFileValidator( this ) );
01184   if ( fileName != "" ) {
01185     QFile file( fileName ); 
01186     if ( !file.open( QFile::WriteOnly ) )
01187       return;
01188 
01189     QTextStream out (&file);
01190   
01191     for( int i=0; i<myHistory.count(); i++ ) {
01192          out<<myHistory[i]<<endl;
01193     }
01194     file.close();
01195   }
01196 }