Index: src/optionsDialog.h =================================================================== --- src/optionsDialog.h (revision 2705) +++ src/optionsDialog.h (working copy) @@ -170,7 +170,13 @@ void editColorFore(); void editColorBack(); + //--------------------------------------------------------------------------- + // User commands + void userCommandSelected( int cmd ); + void userCommandTextChanged(const QString &newStr); + void userCommandTitleTextChanged(const QString &newStr); + private: /*----- member functions -----*/ Index: src/app.h =================================================================== --- src/app.h (revision 2705) +++ src/app.h (working copy) @@ -285,6 +285,7 @@ void ignoreFileRight(); void helpManPage(); void helpAbout(); + void fillUserMenu(); // // Keyboard accelerators cursor motion callbacks. @@ -309,7 +310,11 @@ void fontSizeIncrease(); void fontSizeDecrease(); // - + + // User-specified + // + void userInvoke(int id); + // signals: // Signal emitted when the cursor changes line. @@ -404,6 +409,9 @@ // Get the merged filename. QString getMergedFilename() const; + // Get a user command + QString getUserCommand(unsigned int id) const; + // Compute if there is even a single byte different between the files. bool computeAbsoluteDifference() const; @@ -453,6 +461,7 @@ QkPopupMenu* _displayMenu; QkPopupMenu* _hordiffMenu; QkPopupMenu* _windowsMenu; + QkPopupMenu* _userMenu; int _menuids[ MAX_MENUIDS ]; QWidget* _overviewArea; QLabel* _remUnselView; Index: src/optionsDialog.cpp =================================================================== --- src/optionsDialog.cpp (revision 2705) +++ src/optionsDialog.cpp (working copy) @@ -289,6 +289,16 @@ // Make this dialog an observer of the resources. connect( resourcesPtr, SIGNAL( changed() ), this, SLOT( synchronize() ) ); + + //--------------------------------------------------------------------------- + // User menu + + connect( _comboUserCommand, SIGNAL( activated(int) ), + this, SLOT( userCommandSelected(int) ) ); + connect( _editUserCommand, SIGNAL( textChanged(const QString &) ), + this, SLOT( userCommandTextChanged(const QString &) ) ); + connect( _editUserCommandTitle, SIGNAL( textChanged(const QString &) ), + this, SLOT( userCommandTitleTextChanged(const QString &) ) ); } //------------------------------------------------------------------------------ @@ -466,6 +476,13 @@ _labelEditBack->update(); } } + + //--------------------------------------------------------------------------- + // User menu + + int n = _comboUserCommand->currentItem( ); + _editUserCommandTitle->setText( resources.getUserCommandTitle( n ) ); + _editUserCommand->setText( resources.getUserCommand( n ) ); } //------------------------------------------------------------------------------ @@ -887,6 +904,37 @@ //------------------------------------------------------------------------------ // +void XxOptionsDialog::userCommandSelected( int cmd ) +{ + _comboUserCommand->setCurrentItem( cmd ); + // Redraw the widgets + synchronize(); +} + +void XxOptionsDialog::userCommandTextChanged(const QString &newStr) +{ + XxResources& resources = _app->getResourcesNC(); + + resources.setUserCommand( (unsigned int) _comboUserCommand->currentItem(), newStr ); + // Redraw the widgets + synchronize(); + // Recreate the menu + _app->fillUserMenu(); +} + +void XxOptionsDialog::userCommandTitleTextChanged(const QString &newStr) +{ + XxResources& resources = _app->getResourcesNC(); + + resources.setUserCommandTitle( (unsigned int) _comboUserCommand->currentItem(), newStr ); + // Redraw the widgets + synchronize(); + // Recreate the menu + _app->fillUserMenu(); +} + +//------------------------------------------------------------------------------ +// bool XxOptionsDialog::isInCommand( const QString& command, const QString& option Index: src/optionsDialogBase.ui =================================================================== --- src/optionsDialogBase.ui (revision 2705) +++ src/optionsDialogBase.ui (working copy) @@ -1283,6 +1283,146 @@ + + + UserMenu + + + User menu + + + + layout90 + + + + 20 + 10 + 210 + 50 + + + + + unnamed + + + + textLabel3 + + + Menu item + + + + + + Item 1 + + + + + Item 2 + + + + + Item 3 + + + + _comboUserCommand + + + + + + + buttonGroup4_4_3 + + + + 10 + 60 + 640 + 120 + + + + User Command + + + + TestLabel1_2_4 + + + + 10 + 70 + 90 + 29 + + + + Command: + + + + + TestLabel1_2_4_2 + + + + 10 + 30 + 90 + 29 + + + + Title: + + + + + _editUserCommandTitle + + + + 100 + 30 + 530 + 29 + + + + + + + The title that appears for the User menu item + + + + + _editUserCommand + + + + 100 + 70 + 530 + 29 + + + + + + + The command to execute when the menu item is selected + + + + Index: src/app.cpp =================================================================== --- src/app.cpp (revision 2705) +++ src/app.cpp (working copy) @@ -1716,6 +1716,12 @@ //--------------------------------------------------------------------------- + // User menu + _userMenu = new QkPopupMenu; + fillUserMenu(); + + //--------------------------------------------------------------------------- + // Help menu QkPopupMenu* helpMenu = new QkPopupMenu; helpMenu->insertItem( @@ -1746,6 +1752,7 @@ m->insertItem( "O&ptions", _optionsMenu ); m->insertItem( "&Display", _displayMenu ); m->insertItem( "W&indows", _windowsMenu ); + m->insertItem( "&User", _userMenu ); m->insertSeparator(); m->insertItem( "&Help", helpMenu ); } @@ -2931,6 +2938,44 @@ //------------------------------------------------------------------------------ // +QString XxApp::getUserCommand(unsigned int id) const +{ + QString name; + + // FIXME: Use _cmdline? + name = _resources->getUserCommand(id); + + QString left, middle, right; + XxBuffer* leftbuf = getBuffer( 0 ); + if ( leftbuf ) { + left = leftbuf->getName(); + } + if ( _nbFiles == 2 ) { + XxBuffer* rightbuf = getBuffer( 1 ); + if ( rightbuf ) { + right = rightbuf->getName(); + } + } + else { + XxBuffer* middlebuf = getBuffer( 1 ); + if ( middlebuf ) { + middle = middlebuf->getName(); + } + XxBuffer* rightbuf = getBuffer( 2 ); + if ( rightbuf ) { + right = rightbuf->getName(); + } + } + + name.replace( QRegExp("%L"), left ); + name.replace( QRegExp("%M"), middle ); + name.replace( QRegExp("%R"), right ); + + return name; +} + +//------------------------------------------------------------------------------ +// void XxApp::saveSelectedOnly() { if ( _diffs.get() == 0 ) { @@ -4138,6 +4183,23 @@ //------------------------------------------------------------------------------ // +void XxApp::fillUserMenu() +{ + // Recreate the user menu + _userMenu->clear(); + for ( int ii = 0; ii < USER_COMMAND_LAST; ii++ ) { + if ( !_resources->getUserCommand(ii).isEmpty() ) { + int id = _userMenu->insertItem( + _resources->getUserCommandTitle(ii), this, SLOT(userInvoke(int)), + _resources->getAccelerator( ACCEL_USER_ADD_NEW ) + ); + _userMenu->setItemParameter(id, ii); + } + } +} + +//------------------------------------------------------------------------------ +// void XxApp::synchronizeUI() { if ( _filesAreDirectories == false ) { @@ -4528,4 +4590,32 @@ } +//------------------------------------------------------------------------------ +// +void XxApp::userInvoke(int id) +{ + const char **args; + QStringList filenames; + QString command = getUserCommand(id); + + XxUtil::splitArgs( command, filenames, args ); + try { + XxUtil::spawnCommand( args ); + } + catch ( const XxIoError& ioerr) { + QString text; + { + QTextOStream oss( &text ); + oss << "There has been an error spawning user program:" + << ioerr.getMsg() << endl; + } + QMessageBox* box = new XxSuicideMessageBox( + _mainWindow, "Error.", text, QMessageBox::Warning + ); + box->show(); + + } + free (args); +} + XX_NAMESPACE_END Index: src/resParser.l =================================================================== --- src/resParser.l (revision 2705) +++ src/resParser.l (working copy) @@ -45,6 +45,7 @@ %s GEOM_SC %s ACCEL_SC +%s USER_SC USERCT_SC %s COLOR_SC COLORBF_SC %s COMMAND_SC %s COMMANDSW_SC @@ -92,6 +93,24 @@ ); } +{id} { + int token = parseFromKeywordList( + userList, sizeof(userList)/sizeof(StringToken), + USERNAME, "user", + yytext, yylval->num + ); + if ( token != ERROR_TOKEN ) { + BEGIN(USERCT_SC); + } + return token; +} + +([cC]ommand|[tT]itle) { + BEGIN(INITIAL); + yylval->num = ( (yytext[0] == 'c') || (yytext[0] == 'C') ) ? USERCOMMAND : USERTITLE; + return yylval->num; +} + {id} { int token = parseFromKeywordList( colorList, sizeof(colorList)/sizeof(StringToken), @@ -192,6 +211,7 @@ switch ( yylval->num ) { case PREFGEOMETRY: BEGIN(GEOM_SC); break; case ACCEL: BEGIN(ACCEL_SC); break; + case USER: BEGIN(USER_SC); break; case COLOR: BEGIN(COLOR_SC); break; case COMMAND: BEGIN(COMMAND_SC); break; case COMMANDSW: BEGIN(COMMANDSW_SC); break; Index: src/resources.h =================================================================== --- src/resources.h (revision 2705) +++ src/resources.h (working copy) @@ -202,6 +202,7 @@ ACCEL_IGNORE_FILE_LEFT, ACCEL_IGNORE_FILE_MIDDLE, ACCEL_IGNORE_FILE_RIGHT, + ACCEL_USER_ADD_NEW, ACCEL_HELP_MAN_PAGE, ACCEL_HELP_ON_CONTEXT, ACCEL_HELP_ABOUT, @@ -336,6 +337,13 @@ HD_MULTIPLE }; +enum XxUserCommand { + USER_COMMAND1, + USER_COMMAND2, + USER_COMMAND3, + USER_COMMAND_LAST, // Not a real resource +}; + /*============================================================================== * CLASS XxResources *============================================================================*/ @@ -549,7 +557,15 @@ void setMergedFilename( const QString& fn ); // + // Get/set user command. + // + const QString& getUserCommand( unsigned int n) const; + void setUserCommand( unsigned n, const QString& fn ); + const QString& getUserCommandTitle( unsigned int n) const; + void setUserCommandTitle( unsigned int n, const QString& fn ); + // + // Return a table for the dynamic programming algorithm, if the maximum size // of the table allows it. If not, then return 0. int* getDynProgTable( const uint htx, const uint hty ) const; @@ -626,6 +642,8 @@ uint _hordiffContext; uint _showPaneMergedViewPercent; QString _mergedFilename; + QString _userCommand[ USER_COMMAND_LAST ]; + QString _userCommandTitle[ USER_COMMAND_LAST ]; // Dynamic programming table used for horizontal diffs computation. // Index: src/resParser.y =================================================================== --- src/resParser.y (revision 2705) +++ src/resParser.y (working copy) @@ -54,6 +54,9 @@ %token ACCEL %token ACCELNAME +%token USER +%token USERNAME USERCOMMAND USERTITLE + %token COLOR %token COLORNAME BACK FORE @@ -117,6 +120,7 @@ /* typed rules */ %type colorbf +%type userct %type boolkwd %start xxdiffrc @@ -134,6 +138,7 @@ | prefgeometry | style | accel + | user | color | boolopt | command @@ -211,6 +216,28 @@ } ; +user : USER DOT USERNAME DOT userct COLON STRING + { + /*printf( "==> User %d, %d: %s\n", $3, $5, $7 );*/ + if ($5 == USERCOMMAND) + RESOURCES->setUserCommand( $3, $7 ); + else if ($5 == USERTITLE) + RESOURCES->setUserCommandTitle( $3, $7 ); + else { + char buf[2048]; + ::snprintf( buf, 2048, + "Unrecognized user command: %s\n", $7 ); + yyerror( buf ); + } + + } + ; + +userct : USERCOMMAND + | USERTITLE + ; + + color : COLOR DOT COLORNAME COLON STRING { /*printf( "==> color %d back: %s\n", $3, $5 );*/ Index: src/resources.inline.h =================================================================== --- src/resources.inline.h (revision 2705) +++ src/resources.inline.h (working copy) @@ -240,6 +240,24 @@ //------------------------------------------------------------------------------ // +inline const QString& XxResources::getUserCommand(unsigned int n) const +{ + if (n < USER_COMMAND_LAST) + return _userCommand[n]; + return _userCommand[USER_COMMAND_LAST-1]; +} + +//------------------------------------------------------------------------------ +// +inline const QString& XxResources::getUserCommandTitle(unsigned int n) const +{ + if (n < USER_COMMAND_LAST) + return _userCommandTitle[n]; + return _userCommandTitle[USER_COMMAND_LAST-1]; +} + +//------------------------------------------------------------------------------ +// inline void XxResources::setFbColors( XxColor color, const char* backstr, Index: src/resParser.cpp =================================================================== --- src/resParser.cpp (revision 2705) +++ src/resParser.cpp (working copy) @@ -91,6 +91,13 @@ "Color choice for diff hunks, and for certain other items in the text \ view." }, + { "User", USER, + "User-specified commands in the User menu. The command will be executed \ +when the entry is selected in the menu. %L, %M, %R can be used as placeholders \ +for left, middle and right filenames repectively. Note that ClearCase suffixes \ +are stripped automatically. Title specifies the title in the menu for the \ +command." }, + { "FontApp", FONT_APP, "General application font, used for widgets and menus." }, @@ -475,6 +482,15 @@ " Color of text region selection " } }; +StringToken userList[] = { + { "UserCommand1", USER_COMMAND1, + "User command 1." }, + { "UserCommand2", USER_COMMAND2, + "User command 2." }, + { "UserCommand3", USER_COMMAND3, + "User command 3." }, +}; + StringToken commandList[] = { { "DiffFiles2", CMD_DIFF_FILES_2, "Command to use for comparing two files." }, @@ -909,6 +925,7 @@ sortTokens( STPARAM(commandSwitchList) ); sortTokens( STPARAM(showList) ); sortTokens( STPARAM(tagList) ); + sortTokens( STPARAM(userList) ); } //------------------------------------------------------------------------------ @@ -1177,6 +1194,20 @@ } } + int nbuser = sizeof(userList)/sizeof(StringToken); + const char* userStr = searchTokenName( STPARAM(kwdList), USER ); + for ( ii = 0; ii < nbuser; ++ii ) { + XxUserCommand bo = XxUserCommand(userList[ii]._token); + const QString& bc1 = res1.getUserCommand( bo ); + const QString& bt1 = res1.getUserCommandTitle( bo ); + if ( bc1 != res2.getUserCommand( bo ) || bt1 != res2.getUserCommandTitle( bo )) { + os << userStr << "." << userList[ii]._name << ".Command" << ": \"" + << bc1 << "\"" << endl; + os << userStr << "." << userList[ii]._name << ".Title" << ": \"" + << bt1 << "\"" << endl; + } + } + const char* initSwitchStr = searchTokenName( STPARAM(kwdList), INITSW ); for ( ii = 0; ii < nbcommandSwitch; ++ii ) { XxCommandSwitch bo = XxCommandSwitch(commandSwitchList[ii]._token); @@ -1353,6 +1384,19 @@ << b1.latin1() << "\"" << endl; } + int nbusercommand = sizeof(userList)/sizeof(StringToken); + const char* userCommandStr = + searchTokenName( STPARAM(kwdList), USER ); + for ( ii = 0; ii < nbusercommand; ++ii ) { + XxUserCommand bo = XxUserCommand(userList[ii]._token); + const QString& bc1 = res.getUserCommand( bo ); + const QString& bt1 = res.getUserCommandTitle( bo ); + os << userCommandStr << "." << userList[ii]._name << ".Command" << ": \"" + << bc1.latin1() << "\"" << endl; + os << userCommandStr << "." << userList[ii]._name << ".Title" << ": \"" + << bt1.latin1() << "\"" << endl; + } + const char* initSwitchStr = searchTokenName( STPARAM(kwdList), INITSW ); for ( ii = 0; ii < nbcommandSwitch; ++ii ) { XxCommandSwitch bo = XxCommandSwitch(commandSwitchList[ii]._token); @@ -1449,7 +1493,7 @@ drbegin( os ); int nbaccel = sizeof(accelList)/sizeof(StringToken); const StringToken* tok = searchToken( STPARAM(kwdList), ACCEL ); - os << tok->_name << "." << "[NAME]." << ": \"[ACCELERATOR]\"" << endl; + os << tok->_name << "." << "[NAME]" << ": \"[ACCELERATOR]\"" << endl; drend( os ); ddbegin( os ); os << tok->_desc << endl; @@ -1566,6 +1610,49 @@ } { + drbegin( os ); + int nbusercommands = sizeof(userList)/sizeof(StringToken); + const StringToken* tok = searchToken( STPARAM(kwdList), USER ); + os << tok->_name << "." << "[NAME].[Command]" + << ": \"[COMMAND]\"" << endl; + os << tok->_name << "." << "[NAME].[Title]" + << ": \"[MENU TITLE]\"" << endl; + drend( os ); + ddbegin( os ); + os << tok->_desc << endl; + + for ( ii = 0; ii < nbusercommands; ++ii ) { + const StringToken* tokc = &( userList[ii] ); + + drbegin( os ); + + os << tok->_name << "." << tokc->_name << ".Title" << ": \""; + if ( qApp != 0 ) { + os << res.getUserCommandTitle( ii ); + } + else { + os << "<menu title>"; + } + os << "\"" << endl; + + os << tok->_name << "." << tokc->_name << ".Command" << ": \""; + if ( qApp != 0 ) { + os << res.getUserCommand( ii ); + } + else { + os << "<command>"; + } + os << "\"" << endl; + + drend( os ); + ddbegin( os ); + os << tokc->_desc << endl; + ddend( os ); + } + ddend( os ); + } + + { int nbbool = sizeof(boolkwdList)/sizeof(StringToken); for ( ii = 0; ii < nbbool; ++ii ) { XxBoolOpt bo = boolMap[ boolkwdList[ii]._token - BOOLKWD_BASE ]; Index: src/resources.cpp =================================================================== --- src/resources.cpp (revision 2705) +++ src/resources.cpp (working copy) @@ -800,6 +800,28 @@ //------------------------------------------------------------------------------ // +void XxResources::setUserCommand( unsigned int n, const QString& fn ) +{ + if (n < USER_COMMAND_LAST) { + _userCommand[n] = fn; + emit changed(); + } + /* Fail silently */ +} + +//------------------------------------------------------------------------------ +// +void XxResources::setUserCommandTitle( unsigned int n, const QString& fn ) +{ + if (n < USER_COMMAND_LAST) { + _userCommandTitle[n] = fn; + emit changed(); + } + /* Fail silently */ +} + +//------------------------------------------------------------------------------ +// bool XxResources::compareFonts( const QFont& f1, const QFont& f2 ) { if ( f1.rawMode() || f2.rawMode() ) {