]> git.sesse.net Git - vlc/blobdiff - modules/gui/qt4/dialogs/messages.cpp
Qt4: grammar
[vlc] / modules / gui / qt4 / dialogs / messages.cpp
index ca3f1cbe2c2d08038e3ec52df24c74b83902dff7..223c4798f2534ceda56741a5ec62e6a8f05228dd 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
- * Messages.cpp : Information about an item
+ * messages.cpp : Information about an item
  ****************************************************************************
- * Copyright (C) 2006-2007 the VideoLAN team
+ * Copyright (C) 2006-2011 the VideoLAN team
  * $Id$
  *
  * Authors: Jean-Baptiste Kempf <jb (at) videolan.org>
 #endif
 
 #include "dialogs/messages.hpp"
-#include "dialogs_provider.hpp"
 
-#include <QSpacerItem>
-#include <QSpinBox>
-#include <QLabel>
-#include <QTextEdit>
+#include <QPlainTextEdit>
 #include <QTextCursor>
+#include <QTextBlock>
 #include <QFileDialog>
 #include <QTextStream>
 #include <QMessageBox>
 #include <QTabWidget>
 #include <QTreeWidget>
 #include <QTreeWidgetItem>
-#include <QHeaderView>
+#include <QMutex>
+#include <QLineEdit>
+#include <QScrollBar>
+#include <QMutex>
+#include <QMutexLocker>
 
-MessagesDialog *MessagesDialog::instance = NULL;
+#include <assert.h>
+
+enum {
+    MsgEvent_Type = QEvent::User + MsgEventTypeOffset + 1,
+};
+
+class MsgEvent : public QEvent
+{
+public:
+    MsgEvent( int, const vlc_log_t *, const char * );
+
+    int priority;
+    uintptr_t object_id;
+    QString object_type;
+    QString header;
+    QString module;
+    QString text;
+};
+
+MsgEvent::MsgEvent( int type, const vlc_log_t *msg, const char *text )
+    : QEvent( (QEvent::Type)MsgEvent_Type ),
+      priority( type ),
+      object_id( msg->i_object_id ),
+      object_type( qfu(msg->psz_object_type) ),
+      header( qfu(msg->psz_header) ),
+      module( qfu(msg->psz_module) ),
+      text( qfu(text) )
+{
+}
 
 MessagesDialog::MessagesDialog( intf_thread_t *_p_intf)
                : QVLCFrame( _p_intf )
 {
     setWindowTitle( qtr( "Messages" ) );
+    setWindowRole( "vlc-messages" );
+    /* Build Ui */
+    ui.setupUi( this );
+    ui.bottomButtonsBox->addButton( new QPushButton( qtr("&Close"), this ),
+                                         QDialogButtonBox::RejectRole );
 
-    /* General widgets */
-    QGridLayout *mainLayout = new QGridLayout( this );
-    mainTab = new QTabWidget( this );
-    mainTab->setTabPosition( QTabWidget::North );
-
-
-    /* Messages */
-    QWidget     *msgWidget = new QWidget;
-    QGridLayout *msgLayout = new QGridLayout( msgWidget );
+    /* Modules tree */
+    ui.modulesTree->setHeaderHidden( true );
 
-    messages = new QTextEdit();
-    messages->setReadOnly( true );
-    messages->setGeometry( 0, 0, 440, 600 );
-    messages->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
+    /* Buttons and general layout */
+    ui.saveLogButton->setToolTip( qtr( "Saves all the displayed logs to a file" ) );
+
+    int i_verbosity = var_InheritInteger( p_intf, "verbose" );
+    changeVerbosity( i_verbosity );
+    ui.verbosityBox->setValue( qMin( i_verbosity, 2 ) );
+
+    getSettings()->beginGroup( "Messages" );
+    ui.filterEdit->setText( getSettings()->value( "messages-filter" ).toString() );
+    getSettings()->endGroup();
+
+    updateButton = new QPushButton( QIcon(":/update"), "" );
+    updateButton->setFlat( true );
+    ui.mainTab->setCornerWidget( updateButton );
+
+#ifndef NDEBUG
+    QWidget *pldebugTab = new QWidget();
+    QVBoxLayout *pldebugTabLayout = new QVBoxLayout();
+    pldebugTab->setLayout( pldebugTabLayout );
+    ui.mainTab->addTab( pldebugTab, "Playlist Tree" );
+    pldebugTree = new QTreeWidget();
+    pldebugTree->headerItem()->setText( 0, "Name" );
+    pldebugTree->headerItem()->setText( 1, "PL id" );
+    pldebugTree->headerItem()->setText( 2, "Item id" );
+    pldebugTree->headerItem()->setText( 3, "PL flags" );
+    pldebugTree->headerItem()->setText( 4, "Item flags" );
+    pldebugTree->setColumnCount( 5 );
+    pldebugTabLayout->addWidget( pldebugTree );
+#endif
 
-    msgLayout->addWidget( messages, 0, 0, 1, 0 );
-    mainTab->addTab( msgWidget, qtr( "Messages" ) );
-    ON_TIMEOUT( updateLog() );
+    tabChanged(0);
 
+    BUTTONACT( updateButton, updateOrClear() );
+    BUTTONACT( ui.saveLogButton, save() );
+    CONNECT( ui.filterEdit, editingFinished(), this, updateConfig() );
+    CONNECT( ui.filterEdit, textChanged(QString), this, filterMessages() );
+    CONNECT( ui.bottomButtonsBox, rejected(), this, hide() );
+    CONNECT( ui.verbosityBox, valueChanged( int ),
+             this, changeVerbosity( int ) );
 
-    /* Modules tree */
-    QWidget     *treeWidget = new QWidget;
-    QGridLayout *treeLayout = new QGridLayout( treeWidget );
+    CONNECT( ui.mainTab, currentChanged( int ), this, tabChanged( int ) );
 
-    modulesTree = new QTreeWidget();
-    modulesTree->header()->hide();
+    /* General action */
+    restoreWidgetPosition( "Messages", QSize( 600, 450 ) );
 
-    treeLayout->addWidget( modulesTree, 0, 0, 1, 0 );
-    mainTab->addTab( treeWidget, qtr( "Modules tree" ) );
+    /* Hook up to LibVLC messaging */
+    vlc_LogSet( p_intf->p_libvlc, MsgCallback, this );
 
+    buildTree( NULL, VLC_OBJECT( p_intf->p_libvlc ) );
+}
 
-    /* Buttons and general layout */
-    QPushButton *closeButton = new QPushButton( qtr( "&Close" ) );
-    closeButton->setDefault( true );
-    clearUpdateButton = new QPushButton( qtr( "&Clear" ) );
-    saveLogButton = new QPushButton( qtr( "&Save as..." ) );
-
-    verbosityBox = new QSpinBox();
-    verbosityBox->setRange( 0, 2 );
-    verbosityBox->setValue( config_GetInt( p_intf, "verbose" ) );
-    verbosityBox->setWrapping( true );
-    verbosityBox->setMaximumWidth( 50 );
-
-    verbosityLabel = new QLabel( qtr( "Verbosity Level" ) );
-
-    mainLayout->addWidget( mainTab, 0, 0, 1, 0 );
-    mainLayout->addWidget( verbosityLabel, 1, 0, 1, 1 );
-    mainLayout->addWidget( verbosityBox, 1, 1 );
-    mainLayout->addWidget( saveLogButton, 1, 3 );
-    mainLayout->addWidget( clearUpdateButton, 1, 4 );
-    mainLayout->addWidget( closeButton, 1, 5 );
-
-    BUTTONACT( closeButton, hide() );
-    BUTTONACT( clearUpdateButton, clearOrUpdate() );
-    BUTTONACT( saveLogButton, save() );
-    CONNECT( mainTab, currentChanged( int ),
-             this, updateTab( int ) );
+MessagesDialog::~MessagesDialog()
+{
+    saveWidgetPosition( "Messages" );
+    vlc_LogSet( p_intf->p_libvlc, NULL, NULL );
+};
 
-    /* General action */
-    readSettings( "Messages", QSize( 600, 450 ) );
+void MessagesDialog::changeVerbosity( int i_verbosity )
+{
+    vlc_atomic_set( &this->verbosity, i_verbosity );
 }
 
-void MessagesDialog::updateTab( int index )
+void MessagesDialog::updateConfig()
 {
-    /* Second tab : modules tree */
-    if( index == 1 )
-    {
-        verbosityLabel->hide();
-        verbosityBox->hide();
-        clearUpdateButton->setText( qtr( "&Update" ) );
-        saveLogButton->hide();
-        updateTree();
-    }
-    /* First tab : messages */
-    else
-    {
-        verbosityLabel->show();
-        verbosityBox->show();
-        clearUpdateButton->setText( qtr( "&Clear" ) );
-        saveLogButton->show();
-    }
+    getSettings()->beginGroup( "Messages" );
+    getSettings()->setValue( "messages-filter", ui.filterEdit->text() );
+    getSettings()->endGroup();
 }
 
-void MessagesDialog::updateLog()
+void MessagesDialog::filterMessages()
 {
-    msg_subscription_t *p_sub = p_intf->p_sys->p_sub;
-    int i_start;
-
-    vlc_mutex_lock( p_sub->p_lock );
-    int i_stop = *p_sub->pi_stop;
-    vlc_mutex_unlock( p_sub->p_lock );
+    QMutexLocker locker( &messageLocker );
+    QPlainTextEdit *messages = ui.messages;
+    QTextBlock block = messages->document()->firstBlock();
 
-    if( p_sub->i_start != i_stop )
+    while( block.isValid() )
     {
-        messages->textCursor().movePosition( QTextCursor::End );
+        block.setVisible( matchFilter( block.text().toLower() ) );
+        block = block.next();
+    }
 
-        for( i_start = p_sub->i_start;
-                i_start != i_stop;
-                i_start = (i_start+1) % VLC_MSG_QSIZE )
-        {
-            if( p_sub->p_msg[i_start].i_type == VLC_MSG_INFO ||
-                p_sub->p_msg[i_start].i_type == VLC_MSG_ERR ||
-                p_sub->p_msg[i_start].i_type == VLC_MSG_WARN &&
-                    verbosityBox->value() >= 1 ||
-                p_sub->p_msg[i_start].i_type == VLC_MSG_DBG &&
-                    verbosityBox->value() >= 2 )
-            {
-                messages->setFontItalic( true );
-                messages->setTextColor( "darkBlue" );
-                messages->insertPlainText( qfu( p_sub->p_msg[i_start].psz_module ) );
-            }
-            else
-                continue;
-
-            switch( p_sub->p_msg[i_start].i_type )
-            {
-                case VLC_MSG_INFO:
-                    messages->setTextColor( "blue" );
-                    messages->insertPlainText( " info: " );
-                    break;
-                case VLC_MSG_ERR:
-                    messages->setTextColor( "red" );
-                    messages->insertPlainText( " error: " );
-                    break;
-                case VLC_MSG_WARN:
-                    messages->setTextColor( "green" );
-                    messages->insertPlainText( " warning: " );
-                    break;
-                case VLC_MSG_DBG:
-                default:
-                    messages->setTextColor( "grey" );
-                    messages->insertPlainText( " debug: " );
-                    break;
-            }
-
-            /* Add message Regular black Font */
-            messages->setFontItalic( false );
-            messages->setTextColor( "black" );
-            messages->insertPlainText( qfu(p_sub->p_msg[i_start].psz_msg) );
-            messages->insertPlainText( "\n" );
-        }
-        messages->ensureCursorVisible();
+    /* Consider the whole QTextDocument as dirty now */
+    messages->document()->markContentsDirty( 0, messages->document()->characterCount() );
 
-        vlc_mutex_lock( p_sub->p_lock );
-        p_sub->i_start = i_start;
-        vlc_mutex_unlock( p_sub->p_lock );
-    }
+    /* FIXME This solves a bug (Qt?) with the viewport not resizing the
+       vertical scroll bar when one or more QTextBlock are hidden */
+    QSize vsize = messages->viewport()->size();
+    messages->viewport()->resize( vsize + QSize( 1, 1 ) );
+    messages->viewport()->resize( vsize );
 }
 
-void MessagesDialog::buildTree( QTreeWidgetItem *parentItem,
-                                vlc_object_t *p_obj )
+bool MessagesDialog::matchFilter( const QString& text )
 {
-    vlc_object_yield( p_obj );
-    QTreeWidgetItem *item;
+    const QString& filter = ui.filterEdit->text();
 
-    if( parentItem )
-        item = new QTreeWidgetItem( parentItem );
-    else
-        item = new QTreeWidgetItem( modulesTree );
+    if( filter.isEmpty() || text.contains( filter.toLower() ) )
+        return true;
+    return false;
+}
 
-    if( p_obj->psz_object_name )
-        item->setText( 0, qfu( p_obj->psz_object_type ) + " \"" +
-                       qfu( p_obj->psz_object_name ) + "\" (" +
-                       QString::number(p_obj->i_object_id) + ")" );
-    else
-        item->setText( 0, qfu( p_obj->psz_object_type ) + " (" +
-                       QString::number(p_obj->i_object_id) + ")" );
+void MessagesDialog::sinkMessage( const MsgEvent *msg )
+{
+    QMutexLocker locker( &messageLocker );
 
-    item->setExpanded( true );
+    QPlainTextEdit *messages = ui.messages;
+    /* Only scroll if the viewport is at the end.
+       Don't bug user by auto-changing/losing viewport on insert(). */
+    bool b_autoscroll = ( messages->verticalScrollBar()->value()
+                          + messages->verticalScrollBar()->pageStep()
+                          >= messages->verticalScrollBar()->maximum() );
+
+    /* Copy selected text to the clipboard */
+    if( messages->textCursor().hasSelection() )
+        messages->copy();
+
+    /* Fix selected text bug */
+    if( !messages->textCursor().atEnd() ||
+         messages->textCursor().anchor() != messages->textCursor().position() )
+         messages->moveCursor( QTextCursor::End );
+
+    /* Start a new logic block so we can hide it on-demand */
+    messages->textCursor().insertBlock();
+
+    QString buf = QString( "<i><font color='darkblue'>%1</font>" ).arg( msg->module );
 
-    for( int i=0; i < p_obj->i_children; i++ )
+    switch ( msg->priority )
     {
-        buildTree( item, p_obj->pp_children[i]);
+        case VLC_MSG_INFO:
+            buf += "<font color='blue'> info: </font>";
+            break;
+        case VLC_MSG_ERR:
+            buf += "<font color='red'> error: </font>";
+            break;
+        case VLC_MSG_WARN:
+            buf += "<font color='green'> warning: </font>";
+            break;
+        case VLC_MSG_DBG:
+        default:
+            buf += "<font color='grey'> debug: </font>";
+            break;
     }
 
-    vlc_object_release( p_obj );
-}
+    /* Insert the prefix */
+    messages->textCursor().insertHtml( buf /* + "</i>" */ );
 
-void MessagesDialog::clearOrUpdate()
-{
-    if( mainTab->currentIndex() )
-        updateTree();
-    else
-        clear();
-}
+    /* Insert the message */
+    messages->textCursor().insertHtml( msg->text );
 
-void MessagesDialog::updateTree()
-{
-    modulesTree->clear();
+    /* Pass the new message thru the filter */
+    QTextBlock b = messages->document()->lastBlock();
+    b.setVisible( matchFilter( b.text() ) );
 
-    buildTree( NULL, VLC_OBJECT( p_intf->p_libvlc ) );
+    /* Tell the QTextDocument to recompute the size of the given area */
+    messages->document()->markContentsDirty( b.position(), b.length() );
+
+    if ( b_autoscroll ) messages->ensureCursorVisible();
 }
 
-void MessagesDialog::clear()
+void MessagesDialog::customEvent( QEvent *event )
 {
-    messages->clear();
+    MsgEvent *msge = static_cast<MsgEvent *>(event);
+
+    assert( msge );
+    sinkMessage( msge );
 }
 
 bool MessagesDialog::save()
 {
     QString saveLogFileName = QFileDialog::getSaveFileName(
-            this, qtr( "Choose a filename to save the logs under..." ),
-            qfu( p_intf->p_libvlc->psz_homedir ),
+            this, qtr( "Save log file as..." ),
+            QVLCUserDir( VLC_DOCUMENTS_DIR ),
             qtr( "Texts / Logs (*.log *.txt);; All (*.*) ") );
 
     if( !saveLogFileName.isNull() )
@@ -257,16 +262,117 @@ bool MessagesDialog::save()
         QFile file( saveLogFileName );
         if ( !file.open( QFile::WriteOnly | QFile::Text ) ) {
             QMessageBox::warning( this, qtr( "Application" ),
-                    qtr( "Cannot write file %1:\n%2." )
+                    qtr( "Cannot write to file %1:\n%2." )
                     .arg( saveLogFileName )
                     .arg( file.errorString() ) );
             return false;
         }
 
         QTextStream out( &file );
-        out << messages->toPlainText() << "\n";
 
+        QTextBlock block = ui.messages->document()->firstBlock();
+        while( block.isValid() )
+        {
+            if( block.isVisible() )
+                out << block.text() << "\n";
+
+            block = block.next();
+        }
         return true;
     }
     return false;
 }
+
+void MessagesDialog::buildTree( QTreeWidgetItem *parentItem,
+                                vlc_object_t *p_obj )
+{
+    QTreeWidgetItem *item;
+
+    if( parentItem )
+        item = new QTreeWidgetItem( parentItem );
+    else
+        item = new QTreeWidgetItem( ui.modulesTree );
+
+    char *name = vlc_object_get_name( p_obj );
+    item->setText( 0, QString("%1%2 (0x%3)")
+                   .arg( qfu( p_obj->psz_object_type ) )
+                   .arg( ( name != NULL )
+                         ? QString( " \"%1\"" ).arg( qfu( name ) )
+                             : "" )
+                   .arg( (uintptr_t)p_obj, 0, 16 )
+                 );
+    free( name );
+    item->setExpanded( true );
+
+    vlc_list_t *l = vlc_list_children( p_obj );
+    for( int i=0; i < l->i_count; i++ )
+        buildTree( item, l->p_values[i].p_object );
+    vlc_list_release( l );
+}
+
+void MessagesDialog::updateOrClear()
+{
+    if( ui.mainTab->currentIndex() == 1)
+    {
+        ui.modulesTree->clear();
+        buildTree( NULL, VLC_OBJECT( p_intf->p_libvlc ) );
+    }
+    else if( ui.mainTab->currentIndex() == 0 )
+        ui.messages->clear();
+#ifndef NDEBUG
+    else
+        updatePLTree();
+#endif
+}
+
+void MessagesDialog::tabChanged( int i )
+{
+    updateButton->setIcon( i != 0 ? QIcon(":/update") : QIcon(":/toolbar/clear") );
+    updateButton->setToolTip( i != 0 ? qtr("Update the tree")
+                                     : qtr("Clear the messages") );
+}
+
+void MessagesDialog::MsgCallback( void *self, int type, const vlc_log_t *item,
+                                  const char *format, va_list ap )
+{
+    MessagesDialog *dialog = (MessagesDialog *)self;
+    char *str;
+    int verbosity = vlc_atomic_get( &dialog->verbosity );
+
+    if( verbosity < 0 || verbosity < (type - VLC_MSG_ERR)
+     || unlikely(vasprintf( &str, format, ap ) == -1) )
+        return;
+
+    int canc = vlc_savecancel();
+    QApplication::postEvent( dialog, new MsgEvent( type, item, str ) );
+    vlc_restorecancel( canc );
+    free( str );
+}
+
+#ifndef NDEBUG
+static QTreeWidgetItem * PLWalk( playlist_item_t *p_node )
+{
+    QTreeWidgetItem *current = new QTreeWidgetItem();
+    current->setText( 0, qfu( p_node->p_input->psz_name ) );
+    current->setToolTip( 0, qfu( p_node->p_input->psz_uri ) );
+    current->setText( 1, QString("%1").arg( p_node->i_id ) );
+    current->setText( 2, QString("%1").arg( p_node->p_input->i_id ) );
+    current->setText( 3, QString("0x%1").arg( p_node->i_flags, 0, 16 ) );
+    current->setText( 4, QString("0x%1").arg(  p_node->p_input->i_type, 0, 16 ) );
+    for ( int i = 0; p_node->i_children > 0 && i < p_node->i_children; i++ )
+        current->addChild( PLWalk( p_node->pp_children[ i ] ) );
+    return current;
+}
+
+void MessagesDialog::updatePLTree()
+{
+    playlist_t *p_playlist = THEPL;
+    pldebugTree->clear();
+    PL_LOCK;
+    pldebugTree->addTopLevelItem( PLWalk( p_playlist->p_root_category ) );
+    PL_UNLOCK;
+    pldebugTree->expandAll();
+    for ( int i=0; i< 5; i++ )
+        pldebugTree->resizeColumnToContents( i );
+}
+#endif