1 /*****************************************************************************
2 * messages.cpp : Information about an item
3 ****************************************************************************
4 * Copyright (C) 2006-2011 the VideoLAN team
7 * Authors: Jean-Baptiste Kempf <jb (at) videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
27 #include "dialogs/messages.hpp"
29 #include <QPlainTextEdit>
30 #include <QTextCursor>
32 #include <QFileDialog>
33 #include <QTextStream>
34 #include <QMessageBox>
36 #include <QTreeWidget>
37 #include <QTreeWidgetItem>
42 #include <QMutexLocker>
47 MsgEvent_Type = QEvent::User + MsgEventTypeOffset + 1,
50 class MsgEvent : public QEvent
53 MsgEvent( int, const msg_item_t *, const char * );
63 MsgEvent::MsgEvent( int type, const msg_item_t *msg, const char *text )
64 : QEvent( (QEvent::Type)MsgEvent_Type ),
66 object_id( msg->i_object_id ),
67 object_type( qfu(msg->psz_object_type) ),
68 header( qfu(msg->psz_header) ),
69 module( qfu(msg->psz_module) ),
74 MessagesDialog::MessagesDialog( intf_thread_t *_p_intf)
75 : QVLCFrame( _p_intf )
77 setWindowTitle( qtr( "Messages" ) );
78 setWindowRole( "vlc-messages" );
81 ui.bottomButtonsBox->addButton( new QPushButton( qtr("&Close"), this ),
82 QDialogButtonBox::RejectRole );
85 ui.modulesTree->setHeaderHidden( true );
87 /* Buttons and general layout */
88 ui.saveLogButton->setToolTip( qtr( "Saves all the displayed logs to a file" ) );
90 int i_verbosity = var_InheritInteger( p_intf, "verbose" );
91 changeVerbosity( i_verbosity );
92 ui.verbosityBox->setValue( qMin( i_verbosity, 2 ) );
94 getSettings()->beginGroup( "Messages" );
95 ui.filterEdit->setText( getSettings()->value( "messages-filter" ).toString() );
96 getSettings()->endGroup();
98 updateButton = new QPushButton( QIcon(":/update"), "" );
99 updateButton->setFlat( true );
100 ui.mainTab->setCornerWidget( updateButton );
103 QWidget *pldebugTab = new QWidget();
104 QVBoxLayout *pldebugTabLayout = new QVBoxLayout();
105 pldebugTab->setLayout( pldebugTabLayout );
106 ui.mainTab->addTab( pldebugTab, "Playlist Tree" );
107 pldebugTree = new QTreeWidget();
108 pldebugTree->headerItem()->setText( 0, "Name" );
109 pldebugTree->headerItem()->setText( 1, "PL id" );
110 pldebugTree->headerItem()->setText( 2, "Item id" );
111 pldebugTree->headerItem()->setText( 3, "PL flags" );
112 pldebugTree->headerItem()->setText( 4, "Item flags" );
113 pldebugTree->setColumnCount( 5 );
114 pldebugTabLayout->addWidget( pldebugTree );
119 BUTTONACT( updateButton, updateOrClear() );
120 BUTTONACT( ui.saveLogButton, save() );
121 CONNECT( ui.filterEdit, editingFinished(), this, updateConfig() );
122 CONNECT( ui.filterEdit, textChanged(QString), this, filterMessages() );
123 CONNECT( ui.bottomButtonsBox, rejected(), this, hide() );
124 CONNECT( ui.verbosityBox, valueChanged( int ),
125 this, changeVerbosity( int ) );
127 CONNECT( ui.mainTab, currentChanged( int ), this, tabChanged( int ) );
130 restoreWidgetPosition( "Messages", QSize( 600, 450 ) );
132 /* Hook up to LibVLC messaging */
133 vlc_Subscribe( &sub, MsgCallback, this );
135 buildTree( NULL, VLC_OBJECT( p_intf->p_libvlc ) );
138 MessagesDialog::~MessagesDialog()
140 saveWidgetPosition( "Messages" );
141 vlc_Unsubscribe( &sub );
144 void MessagesDialog::changeVerbosity( int i_verbosity )
146 vlc_atomic_set( &this->verbosity, i_verbosity );
149 void MessagesDialog::updateConfig()
151 getSettings()->beginGroup( "Messages" );
152 getSettings()->setValue( "messages-filter", ui.filterEdit->text() );
153 getSettings()->endGroup();
156 void MessagesDialog::filterMessages()
158 QMutexLocker locker( &messageLocker );
159 QPlainTextEdit *messages = ui.messages;
160 QTextBlock block = messages->document()->firstBlock();
162 while( block.isValid() )
164 block.setVisible( matchFilter( block.text().toLower() ) );
165 block = block.next();
168 /* Consider the whole QTextDocument as dirty now */
169 messages->document()->markContentsDirty( 0, messages->document()->characterCount() );
171 /* FIXME This solves a bug (Qt?) with the viewport not resizing the
172 vertical scroll bar when one or more QTextBlock are hidden */
173 QSize vsize = messages->viewport()->size();
174 messages->viewport()->resize( vsize + QSize( 1, 1 ) );
175 messages->viewport()->resize( vsize );
178 bool MessagesDialog::matchFilter( const QString& text )
180 const QString& filter = ui.filterEdit->text();
182 if( filter.isEmpty() || text.contains( filter.toLower() ) )
187 void MessagesDialog::sinkMessage( const MsgEvent *msg )
189 QMutexLocker locker( &messageLocker );
191 QPlainTextEdit *messages = ui.messages;
192 /* Only scroll if the viewport is at the end.
193 Don't bug user by auto-changing/losing viewport on insert(). */
194 bool b_autoscroll = ( messages->verticalScrollBar()->value()
195 + messages->verticalScrollBar()->pageStep()
196 >= messages->verticalScrollBar()->maximum() );
198 /* Copy selected text to the clipboard */
199 if( messages->textCursor().hasSelection() )
202 /* Fix selected text bug */
203 if( !messages->textCursor().atEnd() ||
204 messages->textCursor().anchor() != messages->textCursor().position() )
205 messages->moveCursor( QTextCursor::End );
207 /* Start a new logic block so we can hide it on-demand */
208 messages->textCursor().insertBlock();
210 QString buf = QString( "<i><font color='darkblue'>%1</font>" ).arg( msg->module );
212 switch ( msg->priority )
215 buf += "<font color='blue'> info: </font>";
218 buf += "<font color='red'> error: </font>";
221 buf += "<font color='green'> warning: </font>";
225 buf += "<font color='grey'> debug: </font>";
229 /* Insert the prefix */
230 messages->textCursor().insertHtml( buf /* + "</i>" */ );
232 /* Insert the message */
233 messages->textCursor().insertHtml( msg->text );
235 /* Pass the new message thru the filter */
236 QTextBlock b = messages->document()->lastBlock();
237 b.setVisible( matchFilter( b.text() ) );
239 /* Tell the QTextDocument to recompute the size of the given area */
240 messages->document()->markContentsDirty( b.position(), b.length() );
242 if ( b_autoscroll ) messages->ensureCursorVisible();
245 void MessagesDialog::customEvent( QEvent *event )
247 MsgEvent *msge = static_cast<MsgEvent *>(event);
253 bool MessagesDialog::save()
255 QString saveLogFileName = QFileDialog::getSaveFileName(
256 this, qtr( "Save log file as..." ),
257 QVLCUserDir( VLC_DOCUMENTS_DIR ),
258 qtr( "Texts / Logs (*.log *.txt);; All (*.*) ") );
260 if( !saveLogFileName.isNull() )
262 QFile file( saveLogFileName );
263 if ( !file.open( QFile::WriteOnly | QFile::Text ) ) {
264 QMessageBox::warning( this, qtr( "Application" ),
265 qtr( "Cannot write to file %1:\n%2." )
266 .arg( saveLogFileName )
267 .arg( file.errorString() ) );
271 QTextStream out( &file );
273 QTextBlock block = ui.messages->document()->firstBlock();
274 while( block.isValid() )
276 if( block.isVisible() )
277 out << block.text() << "\n";
279 block = block.next();
286 void MessagesDialog::buildTree( QTreeWidgetItem *parentItem,
287 vlc_object_t *p_obj )
289 QTreeWidgetItem *item;
292 item = new QTreeWidgetItem( parentItem );
294 item = new QTreeWidgetItem( ui.modulesTree );
296 char *name = vlc_object_get_name( p_obj );
297 item->setText( 0, QString("%1%2 (0x%3)")
298 .arg( qfu( p_obj->psz_object_type ) )
299 .arg( ( name != NULL )
300 ? QString( " \"%1\"" ).arg( qfu( name ) )
302 .arg( (uintptr_t)p_obj, 0, 16 )
305 item->setExpanded( true );
307 vlc_list_t *l = vlc_list_children( p_obj );
308 for( int i=0; i < l->i_count; i++ )
309 buildTree( item, l->p_values[i].p_object );
310 vlc_list_release( l );
313 void MessagesDialog::updateOrClear()
315 if( ui.mainTab->currentIndex() == 1)
317 ui.modulesTree->clear();
318 buildTree( NULL, VLC_OBJECT( p_intf->p_libvlc ) );
320 else if( ui.mainTab->currentIndex() == 0 )
321 ui.messages->clear();
328 void MessagesDialog::tabChanged( int i )
330 updateButton->setIcon( i != 0 ? QIcon(":/update") : QIcon(":/toolbar/clear") );
331 updateButton->setToolTip( i != 0 ? qtr("Update the tree")
332 : qtr("Clear the messages") );
335 void MessagesDialog::MsgCallback( void *self, int type, const msg_item_t *item,
336 const char *format, va_list ap )
338 MessagesDialog *dialog = (MessagesDialog *)self;
340 int verbosity = vlc_atomic_get( &dialog->verbosity );
342 if( verbosity < 0 || verbosity < (type - VLC_MSG_ERR)
343 || unlikely(vasprintf( &str, format, ap ) == -1) )
346 int canc = vlc_savecancel();
347 QApplication::postEvent( dialog, new MsgEvent( type, item, str ) );
348 vlc_restorecancel( canc );
353 static QTreeWidgetItem * PLWalk( playlist_item_t *p_node )
355 QTreeWidgetItem *current = new QTreeWidgetItem();
356 current->setText( 0, qfu( p_node->p_input->psz_name ) );
357 current->setToolTip( 0, qfu( p_node->p_input->psz_uri ) );
358 current->setText( 1, QString("%1").arg( p_node->i_id ) );
359 current->setText( 2, QString("%1").arg( p_node->p_input->i_id ) );
360 current->setText( 3, QString("0x%1").arg( p_node->i_flags, 0, 16 ) );
361 current->setText( 4, QString("0x%1").arg( p_node->p_input->i_type, 0, 16 ) );
362 for ( int i = 0; p_node->i_children > 0 && i < p_node->i_children; i++ )
363 current->addChild( PLWalk( p_node->pp_children[ i ] ) );
367 void MessagesDialog::updatePLTree()
369 playlist_t *p_playlist = THEPL;
370 pldebugTree->clear();
372 pldebugTree->addTopLevelItem( PLWalk( p_playlist->p_root_category ) );
374 pldebugTree->expandAll();
375 for ( int i=0; i< 5; i++ )
376 pldebugTree->resizeColumnToContents( i );