diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1,462 +1,472 @@
 #include "mainwindow.h"
 #include "updater.h"
 #include "psettings.h"
 #include "sqlerrordlg.h"
 #include <sqlproc.h>
 #include <kactivityindicator.h>
 #include <ksettings.h>
 #include <ksds.h>
 #ifdef Q_OS_WIN32
 #include <klogon_win.h>
 #endif
 #include <QApplication>
 #include <QCloseEvent>
 #include <QEvent>
 #include <QLabel>
 #include <QFileDialog>
 #include <QHBoxLayout>
 #include <QVBoxLayout>
 #include <QPushButton>
 #include <QProgressBar>
 #include <QtConcurrentRun>
 #include <QShortcut>
 #include <QStyle>
 #include <QTextEdit>
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 MainWindow::MainWindow( QWidget* parent, Qt::WindowFlags flags )
     : QDialog( parent, flags )
     , _exitCode( 0 )
     , _actionEventType( QEvent::User )
     , _errorCount( 0 )
     , _updater( new DatabaseUpdater )
 {
     setWindowTitle( QString::fromUtf8( "Создание/обновление базы данных" ) );
     setWindowFlags( windowFlags() & ~Qt::WindowContextHelpButtonHint );
     setSizeGripEnabled( true );
 
     QHBoxLayout* mainLayout = new QHBoxLayout;
     QVBoxLayout* leftLayout = new QVBoxLayout;
     QVBoxLayout* rightLayout = new QVBoxLayout;
     QHBoxLayout* bottomRightLayout = new QHBoxLayout;
 
     leftLayout->setContentsMargins( 0, 
         2* style()->pixelMetric( QStyle::PM_LayoutTopMargin ), 
         style()->pixelMetric( QStyle::PM_LayoutRightMargin ), 0 );
 
     QLabel* picture = new QLabel;
     picture->setPixmap( QPixmap( ":/picture.png" ) );
 
     //кнопки, скрытые до завершения процесса
     _btnSave = new QPushButton( QString::fromUtf8( "Сохранить" ) );
     _btnClose = new QPushButton( QString::fromUtf8( "Закрыть" ) );
     _btnSave->setVisible( false );
     _btnClose->setVisible( false );
     _btnClose->setDefault( true );
     
     leftLayout->addWidget( picture );
     leftLayout->addStretch();
     leftLayout->addWidget( _btnSave );
     leftLayout->addWidget( _btnClose );
 
     QLabel* label = new QLabel( QString::fromUtf8( "Протокол работы" ) );
     rightLayout->addWidget( label );
 
     _protocol = new QTextEdit();
     _protocol->setReadOnly( true );
     label->setBuddy( _protocol );
     rightLayout->addWidget( _protocol );
 
     //индикаторы выполнения
     _progress = new QProgressBar;
     _progress->setTextVisible( false );
     _ai = new KActivityIndicator( KActivityIndicator::Gray );
     _ai->setVisible( false );
 
     bottomRightLayout->addWidget( _progress );
     bottomRightLayout->addWidget( _ai );
     rightLayout->addLayout( bottomRightLayout );
 
     mainLayout->addLayout( leftLayout );
     mainLayout->addLayout( rightLayout );
     setLayout( mainLayout );
 
     //нажатие Esc не соединяется со слотом, чтобы окно не закрывалось
     new QShortcut( QKeySequence(Qt::Key_Escape), this );
 
     //соединить сигналы со слотами
     connect( _btnSave, SIGNAL(clicked()), this, SLOT(saveProtocol()) );
     connect( _btnClose, SIGNAL(clicked()), this, SLOT(closeDialog()) );
 
     //получить незадействованный тип события
     _actionEventType = QEvent::registerEventType();
 }
 
 MainWindow::~MainWindow()
 {
     delete _updater;
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::showEvent( QShowEvent* a_event )
 {
 	QDialog::showEvent( a_event );
     qApp->postEvent( this, new QEvent( QEvent::Type(_actionEventType) ) );
 }
 
 //---------------------------------------------------------------------------
 bool MainWindow::event( QEvent* a_event )
 {
     if ( a_event->type() == _actionEventType )
     {
         startExecution();
         a_event->accept();
         return true;
     }
 
     return QWidget::event( a_event );
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::closeEvent( QCloseEvent* a_event )
 {
     if ( _futureWatcher.isRunning() )
         a_event->ignore();
     else
         a_event->accept();
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::startExecution()
 {
     if ( _futureWatcher.isRunning() )
         return;
 
     ProgramSettings pset;
 
     processArguments( pset );
     logon( pset );
 
     connect( _updater, SIGNAL(progress(int)), _progress, SLOT(setValue(int)) );
     connect( _updater, SIGNAL(error(const QString&)), this, SLOT(error(const QString&)) );
     connect( _updater, SIGNAL(message(const QString&)), this, SLOT(message(const QString&)) );
     connect( _updater, SIGNAL(sqlError(const QString&,const QString&,const QString&)),
              this, SLOT(sqlError(const QString&,const QString&,const QString&)) );
     connect( _updater, SIGNAL(logConnectionParameters(const QString&,const QString&,const QString&)), 
              this, SLOT(logConnectionParameters(const QString&,const QString&,const QString&)) );
     connect( &_futureWatcher, SIGNAL(finished()), this, SLOT(finishExecution()) );
     
     _ai->setVisible( true );
     _ai->start();
 
     //int rc = _updater.run( pset );
     QFuture<int> rc = QtConcurrent::run( _updater, &DatabaseUpdater::run, pset );
     _futureWatcher.setFuture( rc );
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::finishExecution()
 {
     _ai->stop();
     _ai->setVisible( false );
 
     //проверяем число ошибок, т.к. при выполнении без транзакции run() всегда возвращает 0
     if ( _errorCount == 0 ) 
     {
         int before = _updater->revisionBefore();
         int after = _updater->revisionAfter();
 
         if ( before == after )
             message( QString::fromUtf8( "База данных в актуальном состоянии." ) );
         else if ( before == 0 )
             message( QString::fromUtf8( "База данных создана и обновлена до версии %1." ).arg( after ) );
         else
             message( QString::fromUtf8( "База данных версии %1 обновлена до версии %2." ).
                      arg( before ).arg( after ) );
         _exitCode = (before & 0xFFFF) | ((after & 0xFFFF) << 16);
 
         closeDialog(); //при успешном завершении, то окно закрывается автоматически
     }
     else
     {
         _btnClose->show();
         _btnSave->show();
         _btnClose->setFocus();
          message( QString::fromUtf8( "Выполнение прервано в результате ошибки."
             "<br>База данных не изменилась.") );
         _exitCode = -1;
     }
 }
 
 //---------------------------------------------------------------------------
 int MainWindow::executeAction()
 {
     ProgramSettings pset;
 
     processArguments( pset );
     logon( pset );
 
     _ai->start();
     DatabaseUpdater updater;
 
     connect( &updater, SIGNAL(progress(int)), _progress, SLOT(setValue(int)) );
     connect( &updater, SIGNAL(error(const QString&)), this, SLOT(error(const QString&)) );
     connect( &updater, SIGNAL(message(const QString&)), this, SLOT(message(const QString&)) );
     connect( &updater, SIGNAL(sqlError(const QString&,const QString&,const QString&)),
              this, SLOT(sqlError(const QString&,const QString&,const QString&)) );
     connect( &updater, SIGNAL(logConnectionParameters(const QString&,const QString&,const QString&)), 
              this, SLOT(logConnectionParameters(const QString&,const QString&,const QString&)) );
     
     int rc = updater.run( pset );
 
     //проверяем число ошибок, т.к. при выполнении без транзакции run() всегда возвращает 0 
     if ( _errorCount == 0 ) 
     {
         int before = updater.revisionBefore();
         int after = updater.revisionAfter();
 
         if ( before == after )
             message( QString::fromUtf8( "База данных в актуальном состоянии." ) );
         else if ( before == 0 )
             message( QString::fromUtf8( "База данных создана и обновлена до версии %1." ).arg( after ) );
         else
             message( QString::fromUtf8( "База данных версии %1 обновлена до версии %2." ).
                      arg( before ).arg( after ) );
         rc = (before & 0xFFFF) | ((after & 0xFFFF) << 16);
     }
     else
     {
         _btnClose->show();
         _btnSave->show();
         _btnClose->setFocus();
          message( QString::fromUtf8( "Выполнение прервано в результате ошибки."
             "<br>База данных не изменилась.") );
         rc = -1;
     }
     return rc;
 }
 
 void MainWindow::processArguments( ProgramSettings& a_pset )
 {
     QString USERNAME_PARAM( "username" );
     QString PASSWORD_PARAM( "password" );
     QString DATABASE_PARAM( "database" );
     QString DROPDB_PARAM( "dropdb" );
     QString HOST_PARAM( "host" );
     QString PORT_PARAM( "port" );
     QString FILE_PARAM( "file" );
 
     //заполнить параметры default значениями
     QHash<QString, QString> params;
     params.insert( USERNAME_PARAM, QString() );
     params.insert( PASSWORD_PARAM, QString() );
     params.insert( DATABASE_PARAM, QString() );
     params.insert( DROPDB_PARAM, QString() );
     params.insert( HOST_PARAM, QString() );
     params.insert( PORT_PARAM, QString() );
     params.insert( FILE_PARAM, QString() );
 
     //чтение аргументов
     QString key;
     QString curArg;
     QStringListIterator args( QApplication::arguments() );
     args.next();    //пропускаем первый параметр - имя программы
     while ( args.hasNext() )
     {
         curArg = args.next();
         
         if ( curArg.startsWith( "--" ) )
         {//обработка длинного параметра
             curArg.remove( 0, 2 );
             QMutableHashIterator<QString, QString> option( params );
             while ( option.hasNext() )
             {
                 option.next();
                 key = option.key();
                 if ( curArg.startsWith( key ) && curArg.at( key.size() ) == '=' )
                     option.setValue( curArg.right( curArg.size() - key.size() - 1 ) );
             }
             key.clear();
         }
         else if ( curArg.at( 0 ) == QChar('-') )
         {//обработка короткого параметра
             key.clear();
             switch ( curArg.at( 1 ).toLatin1() )
             {
                 case 'U':
                     key = USERNAME_PARAM;
                     break;
                 case 'd':
                     key = DATABASE_PARAM;
                     break;
                 case 'f':
                     key = FILE_PARAM;
                     break;
                 case 'h':
                     key = HOST_PARAM;
                     break;
                 case 'p':
                     key = PORT_PARAM;
                     break;
                 case 'W':
                     key = PASSWORD_PARAM;
                     break;
                 case '0':
                     params[ DROPDB_PARAM ] = '1';
                     break;
             }
         }
         else
         {//обработка одиночного параметра
             if ( key.isEmpty() )
                 key = FILE_PARAM;
             params[ key ] = curArg;
             key.clear();
         }
     }
 
     //записать прочитанные значения в структуру ProgramSettings
     a_pset.username = params.value( USERNAME_PARAM );
     a_pset.password = params.value( PASSWORD_PARAM );
     a_pset.database = params.value( DATABASE_PARAM );
     a_pset.host = params.value( HOST_PARAM );
     a_pset.port = params.value( PORT_PARAM );
     a_pset.controlFile = params.value( FILE_PARAM );
     a_pset.dropdb = !params.value( DROPDB_PARAM ).isEmpty();
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::logon( ProgramSettings& a_pset )
 {
     if ( !a_pset.host.isEmpty() || !a_pset.username.isEmpty() )
         return;
 
 #if defined(Q_OS_WIN)
     KLogon* logon = KLogon::create();
     if ( !logon )
         return;
 
     QString defaultDSN;
 
     HKEY k;
     DWORD cb = 0;
     QString valuename( "System Directory" );
     if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\irs\\b03"), 0, KEY_QUERY_VALUE, &k ) == ERROR_SUCCESS
       && RegQueryValueEx( k, LPCTSTR(valuename.constData()), 0, 0, 0, &cb ) == ERROR_SUCCESS
       && cb > 0 )
     {
         QString systemdir;
         systemdir.resize( (cb / sizeof(QChar)) - 1 );
         RegQueryValueEx( k, LPCTSTR(valuename.constData()), 0, 0, LPBYTE(systemdir.data()), &cb );
         RegCloseKey( k );
 
         KSettings systemini( systemdir + "/system.ini" );
         systemini.beginGroup( "Database" );
         defaultDSN = systemini.value( "DefaultDSN" ).toString();
     }
     QString username = QString::fromLocal8Bit( qgetenv( "USERNAME" ) );
     logon->setParent( winId() );
     logon->setUsername( username );
     if ( !defaultDSN.isNull() )
         logon->setDSN( defaultDSN );
 
     if ( !logon->execute() )
         emit message( QString::fromUtf8( "Отказ от ввода имени и пароля пользователя" ) );
     else
     {
         a_pset.host = logon->host();
         a_pset.username = logon->username();
         a_pset.password = logon->password();
     }
 
     delete logon;
 #else
     QString username = QString::fromLocal8Bit( getenv( "USER" ) );
 #endif
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::logConnectionParameters( const QString& a_host, 
                                           const QString& a_database,
                                           const QString& a_username )
 {
     message( QString::fromUtf8( "Подключение к базе данных<br>"
-        "&nbsp;&nbsp;Сервер: <b>%1</b><br>&nbsp;&nbsp;База данных: <b>%2</b><br>"
-        "&nbsp;&nbsp;Пользователь: <b>%3</b>").arg( a_host, a_database, a_username ) );
+        "<ul><li>Сервер: <b>%1</b></li><li>База данных: <b>%2</b></li>"
+        "<li>Пользователь: <b>%3</b></li></ul>").arg( a_host, a_database, a_username ) );
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::sqlError( const QString& a_dbError,
                            const QString& a_commandDescription,
                            const QString& a_command )
 {
     QString errorHtml;
 
     if ( !a_commandDescription.isEmpty() )
         errorHtml.append( QString::fromUtf8( "<font color=\"#0000FF\">Операция:</font><br>" ) ).
                 append( a_commandDescription ).append( "<br>" );
 
     QString dbError = a_dbError;
     errorHtml.append( QString::fromUtf8( "<font color=\"#0000FF\">Сообщение об ошибке:</font><pre>" ) ).
             append( dbError.trimmed().replace( '\n', "<br>" ) ).append( "</pre>");
 
     if ( !a_command.isEmpty() )
         errorHtml.append( QString::fromUtf8( "<font color=\"#0000FF\">Текст команды:</font><pre>" ) ).
                 append( a_command ).append( "</pre>" );
 
     errorHtmlToLog( errorHtml );
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::error( const QString& a_error )
 {
     QString errorHtml = a_error;
     errorHtmlToLog( errorHtml.replace( '<', "&lt;" ).replace( '>', "&gt;" ).replace( '\n', "<br>" ) );
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::errorHtmlToLog( const QString& a_errorHtml )
 {
     QString errorHtml = a_errorHtml;
     QTextCursor cursor = _protocol->textCursor();
     cursor.insertHtml( QString::fromUtf8( "<font color=\"#0000FF\">Ошибка</font><br>" ) );
     cursor.insertHtml( errorHtml );
     cursor.insertHtml( QString::fromUtf8( "<hr>" ) );
     _protocol->moveCursor( QTextCursor::End );
     ++_errorCount;
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::message( const QString& a_message )
 {
+    static int position = -1;
     QString message = a_message;
-    _protocol->textCursor().insertHtml( message.replace( QChar('\n'), "<br>" ).append( "<br>" ) );
+    QTextCursor cursor = _protocol->textCursor();
+    if ( position >= 0 )
+    {
+        cursor.setPosition( position );
+        cursor.deleteChar();
+        cursor.insertText( QChar(0x2611) );
+        cursor.movePosition( QTextCursor::End );
+    }
+    position = cursor.position();
+    cursor.insertHtml( message.replace( QChar('\n'), "<br>" ).append( "<br>" ).prepend( '\t' ).prepend( QChar(0x2610) ) );
     _protocol->moveCursor( QTextCursor::End );
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::closeDialog()
 {
     QDialog::done( _exitCode );
 }
 
 //---------------------------------------------------------------------------
 void MainWindow::saveProtocol()
 {
     QString filename = QFileDialog::getSaveFileName( this, 
         QString::fromUtf8( "Сохранить протокол в файл…" ), QString(), 
         QString::fromUtf8( "Текстовые файлы (*.txt);;Все файлы (*.*)" ) );
     if ( !filename.isEmpty() )
     {
         QFile outf( filename );
         if ( outf.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
         {
             QTextStream os( &outf );
             os.setGenerateByteOrderMark( true );
             os.setCodec( "UTF-8" );
             os << _protocol->toPlainText();
         }
     }
 }
 
 //!главное окно приложения