Logo Search packages:      
Sourcecode: kdbus version File versions  Download package

dbustreewidget.cpp

/***************************************************************************
 *   Copyright (C) 2006 by Rohan McGovern                                  *
 *   rohan.pm@gmail.com                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <kstatusbar.h>
#include <klistview.h>
#include <klocale.h>
#include <kdebug.h>
#include <qtimer.h>
#include <qvaluelist.h>
#include <qvbox.h>
#include <qsplitter.h>

#include "dbusitem.h"
#include "dbusdispatcher.h"
#include "dbustreewidget.h"
#include "dbus/qdbuserror.h"
#include "dbus/qdbusconnection.h"
#include "dbus/qdbusmessage.h"
#include "kdbus.h"

class DBusTreeWidget::Private {
public:
    /**
     * Initialise GUI components.
     */
    void initGUI();
    
    /**
     * Populate tree with every introspectable D-BUS service.
     */
    void populateDBusTree()
    throw( QDBusSendError, QDBusConnectError );


    // See constructor documentation
    kdbus * app;
    std::auto_ptr< DBusDispatcher > dispatcher;
    QDBusConnection::BusType type;

    /// Status bar
    KStatusBar * statusBar;

    /// List view
    KListView * dbusTree;

    /// Splitter
    QSplitter * splitter;

    /// DBus connection used for communication
    std::auto_ptr< QDBusProxy > proxy;

    /// DBus enumerator
    std::auto_ptr< DBusServiceEnumerator > enumerator;

    /// Bottom frame
    QWidgetStack * bottomFrame;
    
    /// Pointer to widget currently in bottom frame
    std::auto_ptr< QWidget > bottomWidget;

    DBusTreeWidget * p;
};


00081 DBusTreeWidget::DBusTreeWidget(
  kdbus * parent,
  QDBusConnection::BusType const & type
)
 : d( new DBusTreeWidget::Private() )
{
    d->app = parent;
    d->type = type;
    d->p = this;
    d->initGUI();
    QTimer::singleShot( 0, this, SLOT(initObjects()) );
}

void DBusTreeWidget::Private::initGUI() {
    splitter = new QSplitter( p );
    splitter->setOrientation(QSplitter::Vertical);

    dbusTree = new KListView( splitter );
    dbusTree->addColumn("Item");
    dbusTree->addColumn("Type");
    dbusTree->setItemMargin( 2 );
    dbusTree->setRootIsDecorated( true );
    dbusTree->setEnabled( true );
    
    bottomFrame = new QWidgetStack( splitter );
    bottomFrame->setMargin( 3 );

    QScrollView * scrollview = new QScrollView( bottomFrame );
    QVBox * vbox = new QVBox( scrollview->viewport() );
    scrollview->addChild( vbox );
    scrollview->setMidLineWidth( 0 );
    scrollview->setLineWidth( 0 );
    scrollview->setResizePolicy( QScrollView::AutoOneFit );
    new QLabel( i18n("Please select an item above."), vbox );
    bottomFrame->addWidget( scrollview, 0 );
    bottomFrame->raiseWidget( 0 );

    statusBar = new KStatusBar( p );
    statusBar->insertItem( i18n("Please wait, connecting to D-BUS..."), 0 );
    statusBar->setSizePolicy( QSizePolicy(
      QSizePolicy::Maximum,
      QSizePolicy::Preferred
    ) );

    if ( !connect(
      dbusTree,
      SIGNAL( clicked( QListViewItem * ) ),
      p,
      SLOT( selectionChanged() )
      )
    )
        throw std::runtime_error(
          "Couldn't connect clicked to selectionChanged!"
        );
    connect(
      dbusTree,
      SIGNAL( returnPressed( QListViewItem * ) ),
      p,
      SLOT( selectionChanged() )
    );
    connect(
      dbusTree,
      SIGNAL( spacePressed( QListViewItem * ) ),
      p,
      SLOT( selectionChanged() )
    );

    QTimer::singleShot( 0, p, SLOT(resizeSplitter()) );
}

void DBusTreeWidget::Private::populateDBusTree()
throw( QDBusSendError, QDBusConnectError ) {

    dbusTree->clear();
    p->selectionChanged();

    if ( !proxy->connection().isConnected() )
        throw QDBusConnectError(
          QString( "%1: %2" )
          .arg( proxy->connection().lastError().name() )
          .arg( proxy->connection().lastError().message() )
        );
    
    enumerator.reset( new DBusServiceEnumerator( p, proxy.get() ) );
    enumerator->start();
    // Weird crashes have appeared which go away thanks to the below line
    // (i.e., crashes due to something not being thread-safe).
    // Don't comment it out without extensive testing.
    enumerator->wait();

    // For purposes of listening for signals, we should always return to the
    // org.freedesktop.DBus object.
    proxy->setService( "org.freedesktop.DBus" );
    proxy->setPath( "/org/freedesktop/DBus" );
    proxy->setInterface( "org.freedesktop.DBus" );
}

00178 void DBusTreeWidget::dbusSignal( QDBusMessage const & message ) {
    kdDebug() << "Received D-BUS message." << endl
              << "Service: " << message.sender() << endl
              << "Object: " << message.path() << endl
              << "Interface: " << message.interface() << endl
              << "Member: " << message.member() << endl;

    if ( !d->app->reloadOnServiceChange() )
        return;

    if ( message.member() != "NameOwnerChanged" )
        return;

    if ( message.count() != 3 ) {
        kdWarning() <<
          QString("Got NameOwnerChanged with %1 arguments, should have been 3")
          .arg( message.count() ) << endl;
        return;
    }

    QString name = message[0].toString();
    QString oldOwner = message[1].toString();
    QString newOwner = message[2].toString();

    if ( name.isEmpty() || ( oldOwner.isEmpty() && newOwner.isEmpty() ) ) {
        kdWarning() <<
          QString("Got NameOwnerChanged with too many empty arguments") << endl;
        return;
    }

    if ( oldOwner.isEmpty() )
        kdDebug() << QString("New service: %1").arg( name ) << endl;
    else if ( newOwner.isEmpty() )
        kdDebug() << QString("Lost service: %1").arg( name ) << endl;

    // For now, take the easy way out; just refresh everything.
    reload();

    if ( oldOwner.isEmpty() )
        statusMessage(
          i18n(
            "A new service became available: %1"
          ).arg( name )
        );
    else if ( newOwner.isEmpty() )
        statusMessage(
          i18n(
            "A service became unavailable: %1"
          ).arg( name )
        );
    else
        statusMessage(
          i18n(
            "A service changed owner: %1"
          ).arg( name )
        );
}

00236 void DBusTreeWidget::initObjects()
{
    if ( d->type == QDBusConnection::SystemBus )
        kdDebug() << "On system bus" << endl;
    if ( d->type == QDBusConnection::SessionBus )
        kdDebug() << "On session bus" << endl;
    try {
        d->proxy.reset(
          new QDBusProxy(
            QDBusConnection::addConnection(
              d->type,
              QString( "%1" ).arg( d->type )
            )
          )
        );
        d->dispatcher.reset( new DBusDispatcher( this, d->proxy.get() ) );

        d->populateDBusTree();

        if ( !connect(
            d->proxy.get(),
            SIGNAL(dbusSignal(const QDBusMessage &)),
            this,
            SLOT(dbusSignal(const QDBusMessage &))
        ) )
            kdWarning() <<
              "Error occurred while attempting to listen for signals."
            << endl;
    }
    catch ( std::runtime_error const & e ) {
        statusMessage( e.what() );
    }
}

00270 void DBusTreeWidget::statusMessage( QString const & message ) {
    d->statusBar->changeItem( message, 0 );
    d->statusBar->setItemAlignment( 0, Qt::AlignLeft );
    kdDebug() << message << endl;
}

00276 void DBusTreeWidget::selectionChanged() {
    QListViewItem * item = d->dbusTree->selectedItem();

    if ( !item ) {
        d->bottomFrame->raiseWidget( 0 );
        if ( d->bottomWidget.get() )
            d->bottomFrame->removeWidget( d->bottomWidget.get() );
        d->bottomWidget.reset( 0 );
        QTimer::singleShot( 0, this, SLOT(resizeSplitter()) );
        return;
    }

    if ( !dynamic_cast< DBusItem * >( item ) )
        throw std::logic_error( "Something in list is not a DBusItem!" );

    QWidget * widget = dynamic_cast< DBusItem *>(item)->widget(
      d->bottomFrame
    );

    if ( d->bottomWidget.get() )
        d->bottomFrame->removeWidget( d->bottomWidget.get() );

    d->bottomFrame->addWidget( widget, 1 );
    d->bottomFrame->raiseWidget( 1 );

    d->bottomWidget.reset( widget );

    d->bottomWidget->show();

    QTimer::singleShot( 0, this, SLOT(resizeSplitter()) );

}


void DBusTreeWidget::resizeSplitter() {
    if ( dynamic_cast< QScrollView *>( d->bottomFrame->visibleWidget() ) ) {
        QScrollView * sv = dynamic_cast< QScrollView *>(
          d->bottomFrame->visibleWidget()
        );
        unsigned int totalHeight = 0;
        QValueList< int > sizes = d->splitter->sizes();
    
        for ( unsigned int i = 0; i < sizes.count(); i++ )
            totalHeight += sizes[i];
    
        int svHeight = sv->contentsHeight() + 6;
        if ( svHeight != sizes[1] ) {
            sizes.clear();
            sizes.append( totalHeight - svHeight );
            sizes.append( svHeight );
            d->splitter->setSizes( sizes );
        }
    }
}


00332 DBusDispatcher * DBusTreeWidget::dispatcher() {
    return d->dispatcher.get();
}

00336 KListView * DBusTreeWidget::listView() {
    return d->dbusTree;
}

00340 void DBusTreeWidget::reload() {
    kdDebug() << "Reloading D-BUS services..." << endl;
    try {
        d->populateDBusTree();
    }
    catch ( std::runtime_error const & e ) {
        statusMessage( e.what() );
    }
}

DBusTreeWidget::~DBusTreeWidget() {
    delete d;
}

Generated by  Doxygen 1.6.0   Back to index