7.5. Network Transparency

The term "network transparent" refers to an interface that allows a user to access remote files using the same methods as accessing local files. KDE applications should be network transparent to be KDE compliant. Luckily for you, the KDE application developers, all of the hard work of accessing remote files has been done and made available (in simplest form) through static convenience functions.

7.5.1. Programming Example

The KDE library libkio contains the classes that implement network transparency (among other things). The class of particular interest is KIO::NetAccess. It contains the methods download() and upload(), which transfer files to and from remote sources. These methods work synchronously but keep your GUI alive, so they are easy to work with. The download() and upload() methods can use FTP and HTTP (or any protocol for which a handler has been written) for accessing remote files. (The protocol is identified by the protocol identifier in the URL. For example, http://www.kde.org uses HTTP.)

The following program, KRemoteDemo, shows how the KIO::NetAccess methods download() and upload() can be used to implement basic network transparency in a KDE application.


Example 7.18. kremotedemo.h: Class declaration for KRemoteDemo

   1 
   2  1: #ifndef __KREMOTEDEMO_H__
   3  2: #define __KREMOTEDEMO_H__
   4  3:
   5  4: #include <qstring.h>
   6  5:
   7  6: #include <ktmainwindow.h>
   8  7: #include <kurl.h>
   9  8:
  10  9: class QMultiLineEdit;
  11 10: class KAction;
  12 11:
  13 12: /**
  14 13:  * KRemoteDemo
  15 14:  *
  16 15:  * Load and save remote and local files.
  17 16:  **/
  18 17: class KRemoteDemo : public KTMainWindow
  19 18: {
  20 19:   Q_OBJECT
  21 20:
  22 21:   public:
  23 22:    KRemoteDemo (const char *name=0);
  24 23:
  25 24:   protected slots:
  26 25:    void slotOpen();
  27 26:    void slotSave();
  28 27:
  29 28:   protected:
  30 29:    KAction *save;
  31 30:    KURL kurl;
  32 31:    QString localfilename;
  33 32:    QMultiLineEdit *editor;
  34 33: };
  35 34:
  36 35: #endif

This is a typical class declaration. KRemoteDemo uses a QMultiLineEdit object as its content area and offers remote or local file opening and saving (see Listing 7.19).


Example 7.19. kremotedemo.cpp: Class definition for KRemoteDemo

   1 
   2  1: #include <errno.h>
   3  2: #include <string.h>
   4  3:
   5  4: #include <qmultilineedit.h>
   6  5:
   7  6: #include <kapp.h>
   8  7: #include <kstdaction.h>
   9  8: #include <kaction.h>
  10  9: #include <kfiledialog.h>
  11 10: #include <kio/netaccess.h>
  12 11: #include <knotifyclient.h>
  13 12:
  14 13: #include "kremotedemo.moc"
  15 14:
  16 15: KRemoteDemo::KRemoteDemo (const char *name) :
  17 16:   KTMainWindow (name)
  18 17: {
  19 18:   KStdAction::open ( this, SLOT (slotOpen()),
  20 19:                 actionCollection() );
  21 20:   save = KStdAction::save ( this, SLOT (slotSave()),
  22 21:                        actionCollection() );
  23 22:   KStdAction::quit ( kapp, SLOT (closeAllWindows()),
  24 23:                 actionCollection() );
  25 24:   createGUI();
  26 25:   save->setEnabled (false);
  27 26:
  28 27:   editor = new QMultiLineEdit (this);
  29 28:   setView (editor);
  30 29: }
  31 30:
  32 31: void
  33 32: KRemoteDemo::slotOpen()
  34 33: {
  35 34:   kurl = KFileDialog::getOpenURL ();
  36 35:
  37 36:   if (kurl.isLocalFile())
  38 37:     localfilename = kurl.path();
  39 38:   else if (!KIO::NetAccess::download (kurl, localfilename))
  40 39:     {
  41 40:       KNotifyClient::event ("Could not download file.");
  42 41:       return;
  43 42:     }
  44 43:
  45 44:
  46 45:   QFile qfile (localfilename);
  47 46:    if (qfile.open (IO_ReadOnly))
  48 47:     {
  49 48:       char *buffer = new char [qfile.size()+1];
  50 49:
  51 50:       qfile.readBlock (buffer, qfile.size());
  52 51:       buffer [qfile.size()]='\0';
  53 52:       editor->setText (buffer);
  54 53:
  55 54:       delete buffer;
  56 55:     }
  57 56:   else
  58 57:     {
  59 58:       QString qerr;
  60 59:       qerr.sprintf ("Could not open file: %s", strerror (errno));
  61 60:       KNotifyClient::event (qerr);
  62 61:       return;
  63 62:     }
  64 63:   save->setEnabled (true);
  65 64: }
  66 65: 
  67 66: void
  68 67: KRemoteDemo::slotSave()
  69 68: {
  70 69:   QFile qfile (localfilename);
  71 70:    if (qfile.open (IO_ReadOnly))
  72 71:     {
  73 72:       qfile.writeBlock (editor->text(),
  74 73:                    editor->text().length() );
  75 74:       qfile.close();
  76 75:     }
  77 76:   else
  78 77:     {
  79 78:       QString qerr;
  80 79:       qerr.sprintf ("Could not write file: %s", strerror (errno));
  81 80:       KNotifyClient::event (qerr);
  82 81:       return;
  83 82:     }
  84 83:
  85 84:   if (!kurl.isLocalFile())
  86 85:     if (!KIO::NetAccess::upload (localfilename, kurl))
  87 86:     {
  88 87:       KNotifyClient::event ("Could not upload file.");
  89 88:       return;
  90 89:     }
  91 90: }

The KRemoteDemo constructor, lines 15–29, uses actions to create the menubar and toolbar and creates an instance of QMultiLineEdit for use as the content area. The action corresponding to File, Save is stored in the variable save so that the action can be disabled and reenabled later after a file has been loaded. (For simplicity, and to avoid straying too far from the topic, a File, Save As function is not included here, so don't be surprised when you cannot save a newly created piece of text. In a full KDE application, you should include a File, Save As function and enable it when the document changes from empty to non-empty.)

On line 36, in slotOpen(), you check to see whether the URL returned by getOpenURL() refers to a local file. If it does, you open the file pointed to by kurl.path(), the full path to the file. If the URL does not refer to a local file, you download the remote file using KIO::NetAccess::download(). The second argument to this method (see line 38) is passed by reference and, if it is an empty string, filled in upon return with the name of a local, temporary file. This local file holds a copy of the downloaded remote file. The method download() returns false if an error occurs during download.

Notice that errors are reported to the user with KNotifyClient. In the event of a standard C library error, include the error string (returned by the standard C library function strerror()) in your report to the user (see lines 60 and 81). (Refer to Chapter 6, "KDE Style Reference" for details on KNotifyClient.)

The slot slotSave() shows how to return an edited file to its proper local or remote place. The file is saved to the local location pointed to by the variable localfilename. This is either the original location of the file or the location of the temporary file created by download(). If it is a temporary file (if the file requested by the user was originally stored remotely), slotSave() uploads the file to it original location. The upload() method (line 88) will delete the temporary file after it has been uploaded.

When you create a full KDE application, you should treat user-requested remote and local URLs in the same fashion. Beside loading and saving from and to the URLs, you should keep both local and remote URLs in the Recent submenu of the File menu.

Listing 7.20 shows the main() function that you can use to create an executable application showing how KRemoteDemo works. You will also need to place the XML GUI file, kremotedemoui.rc (available from the Web site), in directory $KDEDIR/share/kremotedemo.


Example 7.20. main.cpp: A main() function suitable for testing KRemoteDemo

   1 
   2  1: #include <kapp.h>
   3  2:
   4  3: #include "kremotedemo.h"
   5  4:
   6  5: int
   7  6: main (int argc, char *argv[])
   8  7: {
   9  8:   KApplication kapplication (argc, argv, "kremotedemo");
  10  9:
  11 10:   KRemoteDemo *KRemotedemo = new KRemoteDemo (0);
  12 11:
  13 12:   kapplication.setMainWidget (KRemotedemo);
  14 13:
  15 14:   KRemotedemo->show();
  16 15:   return kapplication.exec();
  17 16: }