p7

Next Previous Table of Contents

In this step, we are prepared to increase the usability of p5, in order to learn how to use a few other standard KDE classes that should be used in near every application.


#include <kapp.h>
#include "p7.h"
 
int main( int argc, char **argv )
{
    KApplication a( argc, argv, "p7" );
 
    MainWindow *mywindow=new MainWindow( "Tutorial - p7" );
    mywindow->resize( 300, 200 );
 
    a.setMainWidget( mywindow );
    mywindow->show();
 
    return a.exec();
}  

main.cpp


#include <dcopobject.h>
 
class p7Iface : virtual public DCOPObject
{
   K_DCOP
 
k_dcop:
   virtual void setURL( QString s )=0;
 
}; 

p7Iface.h


#include "p7Iface.h"
#include <kmainwindow.h>
#include <kurl.h>
#include <kparts/browserextension.h>
#include <qvaluestack.h>
 
class QLineEdit;
class KHTMLPart;
 
class MainWindow : public KMainWindow, virtual public p7Iface
{
  Q_OBJECT
  public:
    MainWindow ( const char * titulo );
 
    virtual void setURL ( const QString url );
 
  public slots:
    void fileSetDefaultPage();
    void changeLocation();
    void bookLocation();
    void gotoPreviousPage();
    void openURLRequest(const KURL &url, const KParts::URLArgs & );
 
  private:
    QLineEdit *location;
    KHTMLPart *browser;
    QValueStack <QString> history;
}; 

p7.h


#include "p7.h"
#include <qvbox.h>
#include <qlineedit.h>
#include <dcopclient.h>
#include <kfiledialog.h>
#include <kapp.h>
#include <kmenubar.h>
#include <ktoolbar.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <khtml_part.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kiconloader.h>
 
 
#define TOOLBAR_ID_ADDBOOKMARK 1
#define TOOLBAR_ID_BACK 2
#define TOOLBAR_ID_QUIT 3
 
MainWindow::MainWindow ( const char * name ) : KMainWindow ( 0L, name ), 
					  DCOPObject ( "browser" )
{
    setCaption("KDE Tutorial - p7");

    QPopupMenu * filemenu = new QPopupMenu;
    filemenu->insertItem( i18n( "&Set default page" ), 
				this, SLOT( fileSetDefaultPage() ) );
    filemenu->insertItem( i18n( "&Quit" ), kapp, SLOT( quit() ) );
    QString about =
            i18n("p7 1.0\n\n"
                 "(C) 1999-2002 Antonio Larrosa Jimenez\n"
                 "larrosa@kde.org\t\tantlarr@supercable.es\n"
                 "Malaga (Spain)\n\n"
                 "Simple KDE Tutorial\n"
                 "This tutorial comes with ABSOLUTELY NO WARRANTY \n"
                 "This is free software, and you are welcome to redistribute it\n"
                 "under certain conditions\n");
 
    QPopupMenu *helpmenu = helpMenu(about);
    KMenuBar * menu = menuBar();
    menu->insertItem( i18n( "&File" ), filemenu);
    menu->insertSeparator();
    menu->insertItem(i18n( "&Help" ), helpmenu);
 
    KToolBar *toolbar=new KToolBar(this);

    toolbar->insertButton(BarIcon("reload"), TOOLBAR_ID_ADDBOOKMARK,
                          SIGNAL(clicked(int)),this,SLOT(bookLocation()),TRUE,
                          i18n("Add to Bookmarks"));
    toolbar->insertButton(BarIcon("back"), TOOLBAR_ID_BACK,
                          SIGNAL(clicked(int)),this,SLOT(gotoPreviousPage()),
                          FALSE, i18n("Back to previous page"));
    toolbar->insertButton(BarIcon("exit"), TOOLBAR_ID_QUIT,
                          SIGNAL(clicked(int)),kapp,SLOT(quit()),TRUE,
                          i18n("Quit the application")); 
    addToolBar(toolbar);
 
    QVBox * vbox = new QVBox ( this );
 
    location = new QLineEdit ( vbox );
 
    KConfig *config=kapp->config();
    config->setGroup("Settings");
    location->setText( config->readEntry( "defaultPage", 
				"http://localhost") );
 
    connect( location , SIGNAL( returnPressed() ),
                this, SLOT( changeLocation() ) );
 
    browser=new KHTMLPart( vbox );
    browser->openURL( location->text() );
 
    connect( browser->browserExtension(),
	SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ),
	this, SLOT( openURLRequest(const KURL &, const KParts::URLArgs & ) ) );           

    setCentralWidget( vbox );
 
    DCOPClient *client = kapp->dcopClient();
    client->attach();
    client->registerAs("p7");
}
 
 
void MainWindow::changeLocation()
{
    history.push( browser->url().url() );
    toolBar()->setItemEnabled( TOOLBAR_ID_BACK, TRUE);
    browser->openURL( location->text() );
}
 
void MainWindow::setURL( QString url )
{
    location->setText( url );
    changeLocation();
}
 
void MainWindow::openURLRequest( const KURL &url, const KParts::URLArgs & )
{
    setURL( url.url() );                                                                  
}

void MainWindow::gotoPreviousPage()
{
    location->setText( history.pop() );
    if (history.isEmpty()) toolBar()->setItemEnabled( TOOLBAR_ID_BACK, FALSE);
    browser->openURL( location->text() );
}
 
void MainWindow::bookLocation()
{
    DCOPClient *client=kapp->dcopClient();
    QByteArray params;
    QDataStream stream(params, IO_WriteOnly);
    stream << location->text();
    if (!client->send("p8-*", "bookmarkList", "add(QString)", params))
       kdDebug << "Error with DCOP\n";
}
 
void MainWindow::fileSetDefaultPage()
{
    KConfig *config=kapp->config();
 
    config->setGroup("Settings");
    config->writeEntry( "defaultPage", browser->url().url() );
}


p7.cpp

The first thing we do is to add a DCOP interface to p7 for p8 (the next generation on bookmark lists :-) ) to use. It's very similar to the one in p6, so I won't comment it.

#define TOOLBAR_ID_ADDBOOKMARK 1
#define TOOLBAR_ID_BACK 2
#define TOOLBAR_ID_QUIT 3

Ok, we're now looking at the beginning of p7.cpp . Here we define some identifiers to identify the toolbar buttons. This isn't always needed and you may create toolbars letting the toolbar class assign default identifiers. But as we're going to enable/disable toolbar buttons, it's more clear to do it this way.

MainWindow::MainWindow ( const char * name ) : KMainWindow ( 0L, name ), 
					  DCOPObject ( "browser" )
{
    QPopupMenu * filemenu = new QPopupMenu;
    filemenu->insertItem( i18n( "&Set default page" ), 
				this, SLOT( fileSetDefaultPage() ) );

The MainWindow class now also inherits from p7Iface, so we have to call the DCOPObject constructor. We're going to name this object browser. There's now another item on the File menu, it will be used to set the actual page to be the default one that will be downloaded when running the application. This will show us how to store a configuration variable.

    KToolBar *toolbar=new KToolBar(this);

KToolBar is the class that implements a tool bar in KDE. By using it you get automatically the standard look of tool bars, a little handle to move it in and out of the window, etc.

    toolbar->insertButton(BarIcon("reload"), TOOLBAR_ID_ADDBOOKMARK,
                          SIGNAL(clicked(int)),this,SLOT(bookLocation()),TRUE,
                          i18n("Add to Bookmarks"));
    toolbar->insertButton(BarIcon("back"), TOOLBAR_ID_BACK,
                          SIGNAL(clicked(int)),this,SLOT(gotoPreviousPage()),
                          FALSE, i18n("Back to previous page"));
    toolbar->insertButton(BarIcon("exit"), TOOLBAR_ID_QUIT,
                          SIGNAL(clicked(int)),kapp,SLOT(quit()),TRUE,
                          i18n("Quit the application")); 

We are going to add three buttons, the first one will be used instead of the big (and if you allow me, ugly) button that we used on p5. The second button will be used to go back to the previous visited page, so we are going to need a way to handle the history of visited pages. Finally, the third button will be used to quit the application (note: The user interface authorities recommend not to put a button on the toolbar to quit the application, anyway, as this is not a real application, we are going to omit this serious advice)

BarIcon("reload") is a function that automatically searches the reload icon (wherever it may be, given that it's on a standard place for toolbar icons, like $KDEDIR/share/toolbar or $KDEDIR/share/apps/<appname>/toolbar ) and returns a valid QPixmap object that stores it.

The next parameter is the identifier we are going to use to later refer to this button, then comes the signal we are going to connect and the object that will receive it, followed by the slot that will be called. Note that most times, you won't need to worry about toolbar buttons, so you can just use 0 as the identifier for every button as you don't need to assign an unique indentifier to each one. An exception to this is when you want to enable/disable a button, as we want to do with the back button.

    addToolBar(toolbar);

We have finished to construct the toolbar, so we add it to the main widget with addToolBar. addToolBar doesn't follow the same nomenclature that setMenu because you're not restricted to use just a toolbar (as you are with menus), so you're not setting it, but adding it.

    KConfig *config=kapp->config();
    config->setGroup("Settings");
    location->setText( config->readEntry( "defaultPage", 
				"http://localhost") );

We already know what kapp is, but we haven't used the config() method before. This method returns the active KConfig object of this application. KConfig is used to store the configuration variables of an application. It automatically stores the keys on the application configuration file and restores them when running the application again.

Once we have the config object, we set the active group to Settings to work on it. Groups are used to better structure the configuration file.

Finally, instead of using the "http://localhost" string as the default location, we read the application configuration. defaultPage is the name of the key we want to read, and "http://localhost" is the default value that is returned in case the config file doesn't store any value for defaultPage

You can also read integer values with KConfig::readNumEntry, boolean values with KConfig::readBoolEntry, etc.

    DCOPClient *client = kapp->dcopClient();
    client->attach();
    client->registerAs("p7");

Finally, we attach this application to the dcop server and register it as p7, as we want p8 to be able to call it to set a page.

void MainWindow::setURL( QString url )
{
    location->setText( url );
    changeLocation();
}

This member is now used to genericly set a given url. Currently this function can be called by the DCOP interface or when the user clicks on a link. We first set the location text to display the url, and then call the changeLocation function.

void MainWindow::changeLocation()
{
    history.push( browser->url() );
    toolBar()->setItemEnabled( TOOLBAR_ID_BACK, TRUE);
    browser->openURL( location->text() );
}

changeLocation (which is also connected to the enterPressed() signal in the location object) is used as the central place to load a new page once the url is already on the location bar. This is done this way to implement history capabilities into p7.

We note now that we defined a history object in p7.h of the type QValueStack <QString> . QValueStack is a Qt class that implements a stack using a template as data type for its elements. The difference between QValueStack and QStack is that the first stores the value of the elements you add to it creating a copy of each element (there should be a copy-constructor for that), while the second just stores pointers to the added elements, so you should take care of not deleting them while they're still on the stack.

As the QString class shares the data among all the copies of the string until a change is made to one of them, there's not as much penalty in making a copy of the urls.

Now we get the default toolbar (if there were more, we could get the other ones by using an identifier as a parameter to toolBar), and enable the back button to let the user click on it.

Finally we open the new url in the browser widget by getting the url from the location bar.

void MainWindow::gotoPreviousPage()
{
    location->setText( history.pop() );

When the user wants to go back to the previous page we just take out the last inserted element from the history stack (with pop as it's a stack after all :-) ) and put it on the location bar. The string is removed from the stack, so the next time we call pop, we take the previous url, and so on until the stack is empty.

    if (history.isEmpty()) toolBar()->setItemEnabled( TOOLBAR_ID_BACK, FALSE);
    browser->openURL( location->text() );
}

If the stack is empty, we better disable the back button, once we add another element, the button is enabled again in changeLocation() . Then, we open the popped url in the browser.

void MainWindow::fileSetDefaultPage()
{
    KConfig *config=kapp->config();
 
    config->setGroup("Settings");
    config->writeEntry( "defaultPage", browser->url() );
}

Finally, we implement the method that is connected to the Set default page menu item. This method stores the current url in the configuration file of this application.

First we get the KConfig object and set the Settings group as we did in the constructor. Then, we write a new entry with writeEntry.

The first parameter is the name of the key and the second one is the value we set it to. Note that thanks to overloaded member functions, we can use writeEntry with different data types variables in the second parameter ( int, bool, float, QString, QStringList, QFont , etc. ).

We are now reaching the end of the tutorial, but first, we have to make a little change to p8 so that it communicates with p7 in order to set the current page when you click on a url in the bookmark list (in other case, there's not much point in having a bookmark list :-) )

Next Previous Table of Contents


© 1999-2002 Antonio Larrosa