diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-07-10 15:24:15 -0500 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-07-10 15:24:15 -0500 |
commit | bd0f3345a938b35ce6a12f6150373b0955b8dd12 (patch) | |
tree | 7a520322212d48ebcb9fbe1087e7fca28b76185c /examples/network | |
download | qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.tar.gz qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.zip |
Add Qt3 development HEAD version
Diffstat (limited to 'examples/network')
69 files changed, 4553 insertions, 0 deletions
diff --git a/examples/network/archivesearch/archivedialog.ui b/examples/network/archivesearch/archivedialog.ui new file mode 100644 index 0000000..0aafb7b --- /dev/null +++ b/examples/network/archivesearch/archivedialog.ui @@ -0,0 +1,137 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>ArchiveDialog</class> +<widget class="QDialog"> + <property name="name"> + <cstring>ArchiveDialog</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>673</width> + <height>561</height> + </rect> + </property> + <property name="caption"> + <string>Qt-interest Archive Search Tool</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>myTextLabel</cstring> + </property> + <property name="text"> + <string>Search phrase</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>myLineEdit</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QSplitter"> + <property name="name"> + <cstring>splitter1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Matching documents</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>myListView</cstring> + </property> + </widget> + <widget class="QTextBrowser"> + <property name="name"> + <cstring>myTextBrowser</cstring> + </property> + </widget> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>mySpacer</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>281</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>myPushButton</cstring> + </property> + <property name="text"> + <string>&Quit</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<includes> + <include location="global" impldecl="in declaration">qhttp.h</include> + <include location="global" impldecl="in declaration">qurl.h</include> + <include location="global" impldecl="in declaration">qmessagebox.h</include> + <include location="global" impldecl="in declaration">qapplication.h</include> + <include location="global" impldecl="in declaration">qcursor.h</include> + <include location="global" impldecl="in declaration">qregexp.h</include> + <include location="local" impldecl="in implementation">archivedialog.ui.h</include> +</includes> +<variables> + <variable access="private">QHttp articleSearcher;</variable> + <variable access="private">QHttp articleFetcher;</variable> +</variables> +<slots> + <slot specifier="non virtual">fetch( QListViewItem * it )</slot> + <slot specifier="non virtual">fetchDone( bool error )</slot> + <slot specifier="non virtual">search()</slot> + <slot specifier="non virtual">searchDone( bool error )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> +</functions> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/examples/network/archivesearch/archivedialog.ui.h b/examples/network/archivesearch/archivedialog.ui.h new file mode 100644 index 0000000..6608cd6 --- /dev/null +++ b/examples/network/archivesearch/archivedialog.ui.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + +void ArchiveDialog::init() +{ + connect(&articleSearcher, SIGNAL(done(bool)), this, SLOT(searchDone(bool))); + connect(&articleFetcher, SIGNAL(done(bool)), this, SLOT(fetchDone(bool))); + connect(myListView, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(fetch(QListViewItem*))); + connect(myLineEdit, SIGNAL(returnPressed()), this, SLOT(search())); + connect(myListView, SIGNAL(returnPressed(QListViewItem*)), this, SLOT(fetch(QListViewItem*))); + connect(myPushButton, SIGNAL(clicked()), this, SLOT(close())); +} + +void ArchiveDialog::fetch( QListViewItem *it ) +{ + QUrl u(it->text(1)); + articleFetcher.setHost(u.host()); + articleFetcher.get(it->text(1)); +} + +void ArchiveDialog::fetchDone( bool error ) +{ + if (error) { + QMessageBox::critical(this, "Error fetching", + "An error occurred when fetching this document: " + + articleFetcher.errorString(), + QMessageBox::Ok, QMessageBox::NoButton); + } else { + myTextBrowser->setText(articleFetcher.readAll()); + } +} + +void ArchiveDialog::search() +{ + if (articleSearcher.state() == QHttp::HostLookup + || articleSearcher.state() == QHttp::Connecting + || articleSearcher.state() == QHttp::Sending + || articleSearcher.state() == QHttp::Reading) { + articleSearcher.abort(); + } + + if (myLineEdit->text() == "") { + QMessageBox::critical(this, "Empty query", + "Please type a search string.", + QMessageBox::Ok, QMessageBox::NoButton); + } else { + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + articleSearcher.setHost("lists.trolltech.com"); + + QHttpRequestHeader header("POST", "/qt-interest/search.php"); + header.setValue("Host", "lists.trolltech.com"); + header.setContentType("application/x-www-form-urlencoded"); + + QString encodedTopic = myLineEdit->text(); + QUrl::encode(encodedTopic); + QString searchString = "qt-interest=on&search=" + encodedTopic; + + articleSearcher.request(header, searchString.utf8()); + } + +} + +void ArchiveDialog::searchDone( bool error ) +{ + if (error) { + QMessageBox::critical(this, "Error searching", + "An error occurred when searching: " + + articleSearcher.errorString(), + QMessageBox::Ok, QMessageBox::NoButton); + } else { + QString result(articleSearcher.readAll()); + + QRegExp rx("<a href=\"(http://lists\\.trolltech\\.com/qt-interest/.*)\">(.*)</a>"); + rx.setMinimal(TRUE); + int pos = 0; + while (pos >= 0) { + pos = rx.search(result, pos); + if (pos > -1) { + pos += rx.matchedLength(); + new QListViewItem(myListView, rx.cap(2), rx.cap(1)); + } + } + } + + QApplication::restoreOverrideCursor(); +} diff --git a/examples/network/archivesearch/archivesearch.doc b/examples/network/archivesearch/archivesearch.doc new file mode 100644 index 0000000..e4589cc --- /dev/null +++ b/examples/network/archivesearch/archivesearch.doc @@ -0,0 +1,25 @@ +/* +*/ + +/*! \page archivesearch-example.html + + \ingroup network-examples + + \title A qt-interest mail archive search + + This example does a search on the qt-interest mailinglist archives. It uses + QHttp to issue the search command and to fetch the results. The GUI parts + were done using \link designer-manual.book Qt Designer\endlink. + + <hr> + + The implementation of the HTTP requests (archivedialog.ui.h): + + \include network/archivesearch/archivedialog.ui.h + + <hr> + + Main (main.cpp): + + \include network/archivesearch/main.cpp +*/ diff --git a/examples/network/archivesearch/archivesearch.pro b/examples/network/archivesearch/archivesearch.pro new file mode 100644 index 0000000..1423814 --- /dev/null +++ b/examples/network/archivesearch/archivesearch.pro @@ -0,0 +1,9 @@ +TEMPLATE = app + +CONFIG += qt warn_on release + +REQUIRES = network full-config + +HEADERS += archivedialog.ui.h +INTERFACES += archivedialog.ui +SOURCES += main.cpp diff --git a/examples/network/archivesearch/main.cpp b/examples/network/archivesearch/main.cpp new file mode 100644 index 0000000..1766470 --- /dev/null +++ b/examples/network/archivesearch/main.cpp @@ -0,0 +1,23 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include "archivedialog.h" +#include <qapplication.h> + +int main(int argc, char **argv) +{ + QApplication a( argc, argv ); + ArchiveDialog ad; + ad.show(); + + QObject::connect( &a, SIGNAL(lastWindowClosed()), + &a, SLOT(quit()) ); + + return a.exec(); +} diff --git a/examples/network/clientserver/client/client.cpp b/examples/network/clientserver/client/client.cpp new file mode 100644 index 0000000..35cfb98 --- /dev/null +++ b/examples/network/clientserver/client/client.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qsocket.h> +#include <qapplication.h> +#include <qvbox.h> +#include <qhbox.h> +#include <qtextview.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qtextstream.h> + + +class Client : public QVBox +{ + Q_OBJECT +public: + Client( const QString &host, Q_UINT16 port ) + { + // GUI layout + infoText = new QTextView( this ); + QHBox *hb = new QHBox( this ); + inputText = new QLineEdit( hb ); + QPushButton *send = new QPushButton( tr("Send") , hb ); + QPushButton *close = new QPushButton( tr("Close connection") , this ); + QPushButton *quit = new QPushButton( tr("Quit") , this ); + + connect( send, SIGNAL(clicked()), SLOT(sendToServer()) ); + connect( close, SIGNAL(clicked()), SLOT(closeConnection()) ); + connect( quit, SIGNAL(clicked()), qApp, SLOT(quit()) ); + + // create the socket and connect various of its signals + socket = new QSocket( this ); + connect( socket, SIGNAL(connected()), + SLOT(socketConnected()) ); + connect( socket, SIGNAL(connectionClosed()), + SLOT(socketConnectionClosed()) ); + connect( socket, SIGNAL(readyRead()), + SLOT(socketReadyRead()) ); + connect( socket, SIGNAL(error(int)), + SLOT(socketError(int)) ); + + // connect to the server + infoText->append( tr("Trying to connect to the server\n") ); + socket->connectToHost( host, port ); + } + + ~Client() + { + } + +private slots: + void closeConnection() + { + socket->close(); + if ( socket->state() == QSocket::Closing ) { + // We have a delayed close. + connect( socket, SIGNAL(delayedCloseFinished()), + SLOT(socketClosed()) ); + } else { + // The socket is closed. + socketClosed(); + } + } + + void sendToServer() + { + // write to the server + QTextStream os(socket); + os << inputText->text() << "\n"; + inputText->setText( "" ); + } + + void socketReadyRead() + { + // read from the server + while ( socket->canReadLine() ) { + infoText->append( socket->readLine() ); + } + } + + void socketConnected() + { + infoText->append( tr("Connected to server\n") ); + } + + void socketConnectionClosed() + { + infoText->append( tr("Connection closed by the server\n") ); + } + + void socketClosed() + { + infoText->append( tr("Connection closed\n") ); + } + + void socketError( int e ) + { + infoText->append( tr("Error number %1 occurred\n").arg(e) ); + } + +private: + QSocket *socket; + QTextView *infoText; + QLineEdit *inputText; +}; + + +int main( int argc, char** argv ) +{ + QApplication app( argc, argv ); + Client client( argc<2 ? "localhost" : argv[1], 4242 ); + app.setMainWidget( &client ); + client.show(); + return app.exec(); +} + +#include "client.moc" diff --git a/examples/network/clientserver/client/client.pro b/examples/network/clientserver/client/client.pro new file mode 100644 index 0000000..bc3b9e9 --- /dev/null +++ b/examples/network/clientserver/client/client.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +TARGET = client + +CONFIG += qt warn_on release + +REQUIRES = network full-config + +HEADERS = +SOURCES = client.cpp diff --git a/examples/network/clientserver/clientserver.doc b/examples/network/clientserver/clientserver.doc new file mode 100644 index 0000000..67b72a5 --- /dev/null +++ b/examples/network/clientserver/clientserver.doc @@ -0,0 +1,35 @@ +/* +*/ + +/*! \page clientserver-example.html + + \ingroup network-examples + + \title A small client-server example + + This example shows how two programs can communicate using sockets. + + Two simple example programs are provided, a client program and a + server program. Both use the QSocket class, and the server also uses + QServerSocket class. + + The server listens on port number 4242 and accepts incoming connections. + It sends back every line it receives from the client, prepended with + the line number. + + The client tries to connect to the server on the host specified on the + command line or to localhost if no command line arguments are + specified. You can send single lines to the server. + + <hr> + + Implementation server (server.cpp): + + \include network/clientserver/server/server.cpp + + <hr> + + Implementation client (client.cpp): + + \include network/clientserver/client/client.cpp +*/ diff --git a/examples/network/clientserver/server/server.cpp b/examples/network/clientserver/server/server.cpp new file mode 100644 index 0000000..440647c --- /dev/null +++ b/examples/network/clientserver/server/server.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qsocket.h> +#include <qserversocket.h> +#include <qapplication.h> +#include <qvbox.h> +#include <qtextview.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qtextstream.h> + +#include <stdlib.h> + + +/* + The ClientSocket class provides a socket that is connected with a client. + For every client that connects to the server, the server creates a new + instance of this class. +*/ +class ClientSocket : public QSocket +{ + Q_OBJECT +public: + ClientSocket( int sock, QObject *parent=0, const char *name=0 ) : + QSocket( parent, name ) + { + line = 0; + connect( this, SIGNAL(readyRead()), + SLOT(readClient()) ); + connect( this, SIGNAL(connectionClosed()), + SLOT(deleteLater()) ); + setSocket( sock ); + } + + ~ClientSocket() + { + } + +signals: + void logText( const QString& ); + +private slots: + void readClient() + { + QTextStream ts( this ); + while ( canReadLine() ) { + QString str = ts.readLine(); + emit logText( tr("Read: '%1'\n").arg(str) ); + + ts << line << ": " << str << endl; + emit logText( tr("Wrote: '%1: %2'\n").arg(line).arg(str) ); + + line++; + } + } + +private: + int line; +}; + + +/* + The SimpleServer class handles new connections to the server. For every + client that connects, it creates a new ClientSocket -- that instance is now + responsible for the communication with that client. +*/ +class SimpleServer : public QServerSocket +{ + Q_OBJECT +public: + SimpleServer( QObject* parent=0 ) : + QServerSocket( 4242, 1, parent ) + { + if ( !ok() ) { + qWarning("Failed to bind to port 4242"); + exit(1); + } + } + + ~SimpleServer() + { + } + + void newConnection( int socket ) + { + ClientSocket *s = new ClientSocket( socket, this ); + emit newConnect( s ); + } + +signals: + void newConnect( ClientSocket* ); +}; + + +/* + The ServerInfo class provides a small GUI for the server. It also creates the + SimpleServer and as a result the server. +*/ +class ServerInfo : public QVBox +{ + Q_OBJECT +public: + ServerInfo() + { + SimpleServer *server = new SimpleServer( this ); + + QString itext = tr( + "This is a small server example.\n" + "Connect with the client now." + ); + QLabel *lb = new QLabel( itext, this ); + lb->setAlignment( AlignHCenter ); + infoText = new QTextView( this ); + QPushButton *quit = new QPushButton( tr("Quit") , this ); + + connect( server, SIGNAL(newConnect(ClientSocket*)), + SLOT(newConnect(ClientSocket*)) ); + connect( quit, SIGNAL(clicked()), qApp, + SLOT(quit()) ); + } + + ~ServerInfo() + { + } + +private slots: + void newConnect( ClientSocket *s ) + { + infoText->append( tr("New connection\n") ); + connect( s, SIGNAL(logText(const QString&)), + infoText, SLOT(append(const QString&)) ); + connect( s, SIGNAL(connectionClosed()), + SLOT(connectionClosed()) ); + } + + void connectionClosed() + { + infoText->append( tr("Client closed connection\n") ); + } + +private: + QTextView *infoText; +}; + + +int main( int argc, char** argv ) +{ + QApplication app( argc, argv ); + ServerInfo info; + app.setMainWidget( &info ); + info.show(); + return app.exec(); +} + +#include "server.moc" diff --git a/examples/network/clientserver/server/server.pro b/examples/network/clientserver/server/server.pro new file mode 100644 index 0000000..0a845b5 --- /dev/null +++ b/examples/network/clientserver/server/server.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +TARGET = server + +CONFIG += qt warn_on release + +REQUIRES = network full-config + +HEADERS = +SOURCES = server.cpp diff --git a/examples/network/ftpclient/README b/examples/network/ftpclient/README new file mode 100644 index 0000000..8b1bd7e --- /dev/null +++ b/examples/network/ftpclient/README @@ -0,0 +1,3 @@ +This example is a small ftp client. It shows how to use the QFtp class. + +This example needs the network extension. diff --git a/examples/network/ftpclient/connectdialog.ui b/examples/network/ftpclient/connectdialog.ui new file mode 100644 index 0000000..1531054 --- /dev/null +++ b/examples/network/ftpclient/connectdialog.ui @@ -0,0 +1,244 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>ConnectDialog</class> +<widget class="QDialog"> + <property name="name"> + <cstring>ConnectDialog</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>384</width> + <height>165</height> + </rect> + </property> + <property name="caption"> + <string>Choose host to connect to</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>Frame3</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>&Remote Host:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>host</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel5</cstring> + </property> + <property name="text"> + <string>&Username:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>username</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel6</cstring> + </property> + <property name="text"> + <string>&Password:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>password</cstring> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>password</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>username</cstring> + </property> + <property name="text"> + <string>anonymous</string> + </property> + </widget> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>Layout21</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>host</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel3</cstring> + </property> + <property name="text"> + <string>P&ort:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>port</cstring> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>port</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="value"> + <number>21</number> + </property> + </widget> + </hbox> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>0</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <spacer> + <property name="name"> + <cstring>Horizontal Spacing2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>OK</string> + </property> + <property name="accel"> + <number>0</number> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + <property name="accel"> + <number>0</number> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>ConnectDialog</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>ConnectDialog</receiver> + <slot>reject()</slot> + </connection> +</connections> +<tabstops> + <tabstop>host</tabstop> + <tabstop>port</tabstop> + <tabstop>username</tabstop> + <tabstop>password</tabstop> + <tabstop>buttonOk</tabstop> + <tabstop>buttonCancel</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/examples/network/ftpclient/ftpclient.doc b/examples/network/ftpclient/ftpclient.doc new file mode 100644 index 0000000..13be191 --- /dev/null +++ b/examples/network/ftpclient/ftpclient.doc @@ -0,0 +1,24 @@ +/* +*/ + +/*! \page ftpclient-example.html + + \ingroup network-examples + + \title An FTP client + + This example implements a FTP client. It uses QFtp to perform its FTP + commands. The GUI parts are done in the Designer. + + <hr> + + The implementation of the FTP commands (ftpmainwindow.ui.h): + + \include network/ftpclient/ftpmainwindow.ui.h + + <hr> + + Main (main.cpp): + + \include network/ftpclient/main.cpp +*/ diff --git a/examples/network/ftpclient/ftpclient.pro b/examples/network/ftpclient/ftpclient.pro new file mode 100644 index 0000000..eb71f13 --- /dev/null +++ b/examples/network/ftpclient/ftpclient.pro @@ -0,0 +1,14 @@ +TEMPLATE = app +TARGET = ftpclient + +CONFIG += qt warn_on release + +REQUIRES = network full-config nocrosscompiler + +HEADERS = ftpviewitem.h +SOURCES = main.cpp \ + ftpviewitem.cpp +FORMS = ftpmainwindow.ui \ + connectdialog.ui +IMAGES = images/file.png \ + images/folder.png diff --git a/examples/network/ftpclient/ftpmainwindow.ui b/examples/network/ftpclient/ftpmainwindow.ui new file mode 100644 index 0000000..6c3ab30 --- /dev/null +++ b/examples/network/ftpclient/ftpmainwindow.ui @@ -0,0 +1,280 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>FtpMainWindow</class> +<widget class="QMainWindow"> + <property name="name"> + <cstring>FtpMainWindow</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>480</height> + </rect> + </property> + <property name="caption"> + <string>Ftp client example</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView" row="1" column="0" rowspan="1" colspan="2"> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Size</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Last Modified</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>remoteView</cstring> + </property> + <property name="showSortIndicator"> + <bool>true</bool> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <property name="name"> + <cstring>remotePath</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel4</cstring> + </property> + <property name="text"> + <string>&Remote Path:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>remotePath</cstring> + </property> + </widget> + </grid> +</widget> +<menubar> + <property name="name"> + <cstring>menubar</cstring> + </property> + <item text="&File" name="fileMenu"> + <action name="fileConnectAction"/> + <action name="fileDownloadAction"/> + <action name="fileUploadAction"/> + <action name="fileRemoveAction"/> + <separator/> + <action name="fileExitAction"/> + </item> +</menubar> +<toolbars> + <toolbar dock="2"> + <property name="name"> + <cstring>toolBar</cstring> + </property> + <property name="label"> + <string>Tools</string> + </property> + <action name="fileConnectAction"/> + <action name="fileDownloadAction"/> + <action name="fileUploadAction"/> + <action name="fileRemoveAction"/> + </toolbar> +</toolbars> +<actions> + <action> + <property name="name"> + <cstring>fileDownloadAction</cstring> + </property> + <property name="iconSet"> + <iconset>image0</iconset> + </property> + <property name="text"> + <string>Download...</string> + </property> + <property name="accel"> + <number>0</number> + </property> + </action> + <action> + <property name="name"> + <cstring>fileExitAction</cstring> + </property> + <property name="text"> + <string>Exit</string> + </property> + <property name="menuText"> + <string>E&xit</string> + </property> + <property name="accel"> + <number>0</number> + </property> + </action> + <action> + <property name="name"> + <cstring>fileRemoveAction</cstring> + </property> + <property name="iconSet"> + <iconset>image1</iconset> + </property> + <property name="text"> + <string>Remove</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileUploadAction</cstring> + </property> + <property name="iconSet"> + <iconset>image2</iconset> + </property> + <property name="text"> + <string>Upload...</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileConnectAction</cstring> + </property> + <property name="iconSet"> + <iconset>image3</iconset> + </property> + <property name="text"> + <string>Connect...</string> + </property> + </action> +</actions> +<images> + <image name="image0"> + <data format="XPM.GZ" length="2477">789c95944953e3301046eff9152e74a3a61ac79bec9a9a03fbbeef4ccdc1926d92400224619d9aff3eeaafc719b640f02387477f96a5b6e49969ef6467d39b9e690c86f9b06d3ddbcafbde7471dbed3efefcf5e377632a083cf797265e30f5ad31b53bf4acb775d52b594839513e2ef86ced710a9faf3d95fa8e7814c621fc066e7c2379056ffa0cea47f066e06b83fa101e30a8afc1331d86325e0c370c7c573cd4b1c5fd863df019d41378c4c0f7e186819f8f1cf7530eaf18789f3df4433f8d313eea61938177e1b8e0182fd40ceedf80e74191c8789138833c8d5ce6b3557b22be5d7b26ebd770cbc0176a97feab01bc60e0d7f08a417ee491f4678e9ddb13a33fcac271a17e0ad78e0ceec35306f9367b1c30a81f8c5ce6b72e9e04f27cd582270cea4db8d6718c7e29ecb73865e05770c3209fc12d030fd993c821e3219f6807fa4b67f08c81a33f3ad56926ebc77c74c9c05378c5c003f634303a95f59dc04387f4a782733be47d3f8d5ccec761edb9e457e0b943d6730caff2dcc8fc57d9315d99ff235c3b24ff00b70e995f092fb332c77ea465f6dc77c8f37af0cc21fd5a849bdc5899ef12dc3a64bc7b78e1102fe0a5431cfbc3440cbc3b729c0fba835b07f61fe13c9a8a81ef8997612a7ec96e7d067e010f18bc7fa9470cead86f78fd721ed17f5b31a8dfb2171103c7f7a4c81c72de3af08a411d5e860c7c13aead95f5a99e380347bf4b6e38f693c27cf1b990f9e27b5165bef6258ffe55b8e4e33a39e3f3a46a26c9534e862c15545245e713e45bd4a60e5dd02575a94757742dcf199bbfa13e0d6848b77447f7f4408ff4c4778ccdcfd21ccdd3022dd2122dd38afbad7e925fa375daa0cd09f35bb44d3bb44b7b13e6f7e9800ee9888e913f71e9d30fd7abe88c7c6a52e03a1a5244f1cb7ebe7d3b9490a69432f72fa572655ee6955585a354953a7ffe14d552ede76ff855bea32ed4a5ea7ebe7ffee57bea4a5dab9bd729d51f937733a28e1a7c21cfd8d7a949f2aa5fa7fea7c7e7395df3c17adf1dffdd3cdf51f366d66ff393f2e5fc9fef8dbfa8437f7d</data> + </image> + <image name="image1"> + <data format="XPM.GZ" length="721">789ca590c10ac2300c86ef7b8ab2de866c3a0f1ec447503c0ae2214d237a5041e741c477b749376d3bf5e29f16fae7234d4855a8d572ae8a2abb34d0ec51e10eceaab0d7c3e1b6deccee595ed7ca9d891ae5832c2f15aac5e948fcd6eead8722b6c4d68c39d85ab196832db2b5c4c116d81275b586ed56245d3eea3fa25bc504004a6d44a8e31a87003d318c20f8ad43961140d4c733eb5a31080909b28802428224cc5d9d10d79d1100f5894101a64f240fd023922757863169f3c6503cc12befc748f6e6b72363f467f3a274076f2524d47752fe268f69f604fb43a540</data> + </image> + <image name="image2"> + <data format="XPM.GZ" length="2461">789cd594496fdb301046effe1542780b8a89ac8d128a1eb22fcdbe27450f1425c58e633b5eb216fdefe57c1319718bd6ceb17af1e1851fa5e190d2d2a27779b8e72d2e354663336e5bcfb6ccd05b2c1ebadd976fdfbffc682c0481e7fed2d80b163e35168ec69ef5f6fbbd92859413e5e3822fd71ea7f0d5da53193f148fc238645703f6a6cf60fc1cde0c7c9d637c040f188cefc0331d86984f313c67e047e2a18e2de6e7ec81cf603c81470cfc049e33f0d6c4319f647ec5c087eca11ffa698cfb1b78938177e1b8e03770cd60fe2edc044522f78bc419e469e252cf7eed89f841ed99ac5fc32d035fab5dfaada4de8281dfc32b06f941ed91f467859ddb13a33fcac27161fc0aae1d19dc87a70cf26df63860307e3a71a9efab7812c8f315fa1d270cc69b70ade318fd52385f71cac0fbf09c413e835b061eb2279143ee87f526da81fed2353c63e0385f3ad56926eb47fdba64e029bc62e0017b1ae43a95f55dc24387f407fb8d76c87ebf4e5cde87b3da8de4b7e0c621ebb98057c6e452ff363bca95fa5fe0da21f967b875487d15bccc4a83f3489becc677c8f3d03f9339a45febf0dce456eadd805b87dcef095e38c44b78e9102fd8f38881f7268ef7831ee1d681f347384f79c5c08fc5cb3015c7fb637d067e070f18ecbf78c460fc168e038071acdf560cc61fd88b88818fe19943de37cc2f2a06e31df63264e07b706dadac4ff5c41938fa5172c3719e14e6e37321e703df872af3b52f79f4abc2251fd3f9f97b9e54cdac3c19f7cbc9524125557443ad5979479b6ea94377d4a51ef5e99e06f29ce93c9235431ad1981ee8919ee8995ee89567bccf731d9359cbb442abb446ebb4419bb4e57edbd3f93afd56ff32edd057daa5bdd979d83e1dd0211dd1f19cf9133aa5333aa70be42f5dfaea5ffd777dbf269f9a14b88e8614513cddcf3f778712d29452e6fea59451f9745e5955384a55a99bf74f512dd57ebfc3bfe56f5547dda9eeecf3f396efa9beba5783b9f3ae22eaa8e107f28cfd0ff32337a366343b3f2f1fcefffcdcf80560e77be9</data> + </image> + <image name="image3"> + <data format="XPM.GZ" length="2878">789c8d95476fe4381085effe150dd7cd58d44854c6600fce394ff0cc620f2425b5533bb4dba1bdd8ff3eac57526381757bac671bf8c0c7122b88feb434383bda1f2c7d5a7898d8c9851ff8733b1e2cd58fa3d1f4afbffffc6761d19841f889e3786016ff58583c9e0cfce0e0f6a611600a40111e611af56c4aacaff45ceafab372e2335ddf574e932c01df835de4749d74bf8b6d5181c7c2e12c69a1fe536513150eeb4fe032c9330f46bcb88aab22833f063b11f84039293aff2ab83565a9f14b611389b07e39630b7e04db2c4d359f23b013818733d6f8ebe0dad4899ec7825b11d65f84932828c6fab8e752fd053816c18ffc1323c27a35639c9f182c4f0a46be499a651def823311f8109c8b106f076c4d9de37c1c298bc0d98c35dfbd9e2b7dff96729176f537602f02aff6acf3c177e0366953ad37e6233522b00327411aef1b38844f311ffc1d5c8ae05f06573649753ed09fd48ac027c2592c02a31f991121def18cf5bcdbcab949b45f0f1d575a0f42fe28a7e6bf0196a700e37bc97211e29d818ba25be71c5c8ac029d88bb01ff5c99acc74f5d5f3b4595beafe9fc2791aa4f16fc145909e0ffdccad088cfcf25604f63d179a3ff22bcaa2acb41f9be02a48eb710e6e44e004dc8ac03f844be38a52e37d052741983f6ec1d22e3dffeb8cb57f6b3d57fabe939eadeec7794a1ba4f3f705dc5aeb903f217e150569bd6ec02648f7633eab3c48e7790a9604755eaec06590f274c67a1e7caf950fd2fa0d7bb63acf0db8a91aabdf33e6c14641fa7e9cc75641dadf1a2c4f8d75e46f9d755eeb83fbc3fa20ad2fee535b0729ebfe264819fd74a9083c9ab17eef389fcb83743ed14f272fd47ae23e753e48cf83efcfb522f0a1729394cad7c23e1281513f6f44e00b30061aef9b8043f619e687743f0a08567f2d02633e7cebdb5aef03cc771d05697cdcc7752a02237e5d05697f717f3789088cfbaf29bcefea83fba20d4f55ea3fbb8f6bbe9fa9d747fc6cd9b1e79a1b6e79f801ff395ff0255ff1358ff8866ff94edf33d77fcf637ee0093ff2133ff30b4ff95576ccf52f07adf02aaff13a6ff066f8dd9aef879b789b7778f7f7fecebdc7fb7cc087bff377ee233ee6133ee52ff07f0dee6f6fe70bbfea3b9ff18f50d19f1c713caf9e5d97c46f38e19433ceb9e0f22d3f57c4c165f44cb2373cf6bf1deefd61c591a79a9ad0a1ce3d7f7e78482d0de99c1d5da89b2ee7fbb90a1578a12b5ea76b44275ea1d1fb7ebaa29bf0f796eee89ec6f4f061ff841ee9899edff3d30b4d3bff2b2dd30aadd21aadbf976fa8cd066d22fe166dd30eedbeed9ed587688ff6c9f1331d840e257d77e6f9b1e3908ed8d0319dccabfdfffb4ba7f39d9dffdfcf0bbf00b514c1b3</data> + </image> +</images> +<connections> + <connection> + <sender>fileExitAction</sender> + <signal>activated()</signal> + <receiver>FtpMainWindow</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>fileDownloadAction</sender> + <signal>activated()</signal> + <receiver>FtpMainWindow</receiver> + <slot>downloadFile()</slot> + </connection> + <connection> + <sender>fileRemoveAction</sender> + <signal>activated()</signal> + <receiver>FtpMainWindow</receiver> + <slot>removeFile()</slot> + </connection> + <connection> + <sender>fileUploadAction</sender> + <signal>activated()</signal> + <receiver>FtpMainWindow</receiver> + <slot>uploadFile()</slot> + </connection> + <connection> + <sender>fileConnectAction</sender> + <signal>activated()</signal> + <receiver>FtpMainWindow</receiver> + <slot>connectToHost()</slot> + </connection> + <connection> + <sender>remotePath</sender> + <signal>activated(const QString&)</signal> + <receiver>FtpMainWindow</receiver> + <slot>changePath(const QString&)</slot> + </connection> + <connection> + <sender>remoteView</sender> + <signal>doubleClicked(QListViewItem*)</signal> + <receiver>FtpMainWindow</receiver> + <slot>changePathOrDownload(QListViewItem*)</slot> + </connection> + <connection> + <sender>remoteView</sender> + <signal>returnPressed(QListViewItem*)</signal> + <receiver>FtpMainWindow</receiver> + <slot>changePathOrDownload(QListViewItem*)</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">ftpmainwindow.ui.h</include> +</includes> +<forwards> + <forward>class QFtp;</forward> + <forward>class QUrlInfo;</forward> +</forwards> +<variables> + <variable access="private">QFtp *ftp</variable> + <variable access="private">QLabel *stateFtp</variable> + <variable access="private">QString currentFtpDir</variable> +</variables> +<slots> + <slot access="public">connectToHost()</slot> + <slot access="private">uploadFile()</slot> + <slot access="private">downloadFile()</slot> + <slot access="private">removeFile()</slot> + <slot access="private">changePath( const QString & newPath )</slot> + <slot access="private">ftp_commandStarted()</slot> + <slot access="private">ftp_commandFinished()</slot> + <slot access="private">ftp_done( bool error )</slot> + <slot access="private">ftp_stateChanged( int state )</slot> + <slot access="private">ftp_listInfo( const QUrlInfo & i )</slot> + <slot access="private">ftp_rawCommandReply( int code, const QString & text )</slot> + <slot access="private">changePathOrDownload( QListViewItem * )</slot> +</slots> +<functions> + <function access="private">init()</function> + <function access="private">destroy()</function> +</functions> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/examples/network/ftpclient/ftpmainwindow.ui.h b/examples/network/ftpclient/ftpmainwindow.ui.h new file mode 100644 index 0000000..2feeb5a --- /dev/null +++ b/examples/network/ftpclient/ftpmainwindow.ui.h @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +/**************************************************************************** +** +** ui.h extension file, included from the uic-generated form implementation. +** +** The init() function is used in place of a constructor. +** The destroy() function is used in place of a destructor. +** The slots uploadFile(), downloadFile(), removeFile() and connectToHost() are +** connected with the resp. actions of the GUI. +** +*****************************************************************************/ + +#include <qftp.h> +#include <qlineedit.h> +#include <qspinbox.h> +#include <qstatusbar.h> +#include <qmessagebox.h> +#include <qfiledialog.h> +#include <qprogressdialog.h> +#include <qapplication.h> + +#include "connectdialog.h" +#include "ftpviewitem.h" + +void FtpMainWindow::init() +{ + stateFtp = new QLabel( tr("Unconnected"), statusBar() ); + statusBar()->addWidget( stateFtp, 0, TRUE ); + + ftp = new QFtp( this ); + connect( ftp, SIGNAL(commandStarted(int)), + SLOT(ftp_commandStarted()) ); + connect( ftp, SIGNAL(commandFinished(int,bool)), + SLOT(ftp_commandFinished()) ); + connect( ftp, SIGNAL(done(bool)), + SLOT(ftp_done(bool)) ); + connect( ftp, SIGNAL(stateChanged(int)), + SLOT(ftp_stateChanged(int)) ); + connect( ftp, SIGNAL(listInfo(const QUrlInfo &)), + SLOT(ftp_listInfo(const QUrlInfo &)) ); + connect( ftp, SIGNAL(rawCommandReply(int, const QString &)), + SLOT(ftp_rawCommandReply(int, const QString &)) ); +} + +void FtpMainWindow::destroy() +{ + if ( ftp->state() != QFtp::Unconnected ) + ftp->close(); +} + +void FtpMainWindow::uploadFile() +{ + QString fileName = QFileDialog::getOpenFileName( + QString::null, + QString::null, + this, + "upload file dialog", + tr("Choose a file to upload") ); + if ( fileName.isNull() ) + return; + + QFile *file = new QFile( fileName ); + if ( !file->open( IO_ReadOnly ) ) { + QMessageBox::critical( this, tr("Upload error"), + tr("Can't open file '%1' for reading.").arg(fileName) ); + delete file; + return; + } + + QProgressDialog progress( + tr("Uploading file..."), + tr("Cancel"), + 0, + this, + "upload progress dialog", + TRUE ); + connect( ftp, SIGNAL(dataTransferProgress(int,int)), + &progress, SLOT(setProgress(int,int)) ); + connect( ftp, SIGNAL(commandFinished(int,bool)), + &progress, SLOT(reset()) ); + connect( &progress, SIGNAL(cancelled()), + ftp, SLOT(abort()) ); + + QFileInfo fi( fileName ); + ftp->put( file, fi.fileName() ); + progress.exec(); // ### takes a lot of time!!! + + ftp->list(); +} + +void FtpMainWindow::downloadFile() +{ + FtpViewItem *item = (FtpViewItem*)remoteView->selectedItem(); + if ( !item || item->isDir() ) + return; + + QString fileName = QFileDialog::getSaveFileName( + item->text(0), + QString::null, + this, + "download file dialog", + tr("Save downloaded file as") ); + if ( fileName.isNull() ) + return; + + // create file on the heap because it has to be valid throughout the whole + // asynchronous download operation + QFile *file = new QFile( fileName ); + if ( !file->open( IO_WriteOnly ) ) { + QMessageBox::critical( this, tr("Download error"), + tr("Can't open file '%1' for writing.").arg(fileName) ); + delete file; + return; + } + + QProgressDialog progress( + tr("Downloading file..."), + tr("Cancel"), + 0, + this, + "download progress dialog", + TRUE ); + connect( ftp, SIGNAL(dataTransferProgress(int,int)), + &progress, SLOT(setProgress(int,int)) ); + connect( ftp, SIGNAL(commandFinished(int,bool)), + &progress, SLOT(reset()) ); + connect( &progress, SIGNAL(cancelled()), + ftp, SLOT(abort()) ); + + ftp->get( item->text(0), file ); + progress.exec(); // ### takes a lot of time!!! +} + +void FtpMainWindow::removeFile() +{ + FtpViewItem *item = (FtpViewItem*)remoteView->selectedItem(); + if ( !item || item->isDir() ) + return; + + ftp->remove( item->text(0) ); + ftp->list(); +} + +void FtpMainWindow::connectToHost() +{ + ConnectDialog connectDialog; + if ( connectDialog.exec() == QDialog::Rejected ) + return; + + remotePath->clear(); + remoteView->clear(); + + if ( ftp->state() != QFtp::Unconnected ) + ftp->close(); + + ftp->connectToHost( connectDialog.host->text(), connectDialog.port->value() ); + ftp->login( connectDialog.username->text(), connectDialog.password->text() ); + ftp->rawCommand( "PWD" ); + ftp->list(); +} + +// This slot is connected to the QComboBox::activated() signal of the +// remotePath. +void FtpMainWindow::changePath( const QString &newPath ) +{ + ftp->cd( newPath ); + ftp->rawCommand( "PWD" ); + ftp->list(); +} + +// This slot is connected to the QListView::doubleClicked() and +// QListView::returnPressed() signals of the remoteView. +void FtpMainWindow::changePathOrDownload( QListViewItem *item ) +{ + if ( ((FtpViewItem*)item)->isDir() ) + changePath( item->text(0) ); + else + downloadFile(); +} + +/**************************************************************************** +** +** Slots connected to signals of the QFtp class +** +*****************************************************************************/ + +void FtpMainWindow::ftp_commandStarted() +{ + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); + if ( ftp->currentCommand() == QFtp::List ) { + remoteView->clear(); + if ( currentFtpDir != "/" ) + new FtpViewItem( remoteView, FtpViewItem::Directory, "..", "", "" ); + } +} + +void FtpMainWindow::ftp_commandFinished() +{ + QApplication::restoreOverrideCursor(); + delete ftp->currentDevice(); +} + +void FtpMainWindow::ftp_done( bool error ) +{ + if ( error ) { + QMessageBox::critical( this, tr("FTP Error"), ftp->errorString() ); + + // If we are connected, but not logged in, it is not meaningful to stay + // connected to the server since the error is a really fatal one (login + // failed). + if ( ftp->state() == QFtp::Connected ) + ftp->close(); + } +} + +void FtpMainWindow::ftp_stateChanged( int state ) +{ + switch ( (QFtp::State)state ) { + case QFtp::Unconnected: + stateFtp->setText( tr("Unconnected") ); + break; + case QFtp::HostLookup: + stateFtp->setText( tr("Host lookup") ); + break; + case QFtp::Connecting: + stateFtp->setText( tr("Connecting") ); + break; + case QFtp::Connected: + stateFtp->setText( tr("Connected") ); + break; + case QFtp::LoggedIn: + stateFtp->setText( tr("Logged in") ); + break; + case QFtp::Closing: + stateFtp->setText( tr("Closing") ); + break; + } +} + +void FtpMainWindow::ftp_listInfo( const QUrlInfo &i ) +{ + FtpViewItem::Type type; + if ( i.isDir() ) + type = FtpViewItem::Directory; + else + type = FtpViewItem::File; + + new FtpViewItem( remoteView, type, + i.name(), QString::number(i.size()), i.lastModified().toString() ); +} + +void FtpMainWindow::ftp_rawCommandReply( int code, const QString &text ) +{ + if ( code == 257 ) { + currentFtpDir = text.section( '"', 1, 1 ); + + for ( int i = 0; i<remotePath->count(); i++ ) { + // make sure that we don't insert duplicates + if ( remotePath->text( i ) == currentFtpDir ) + remotePath->removeItem( i ); + } + remotePath->insertItem( currentFtpDir, 0 ); + remotePath->setCurrentItem( 0 ); + } +} diff --git a/examples/network/ftpclient/ftpviewitem.cpp b/examples/network/ftpclient/ftpviewitem.cpp new file mode 100644 index 0000000..bb5d689 --- /dev/null +++ b/examples/network/ftpclient/ftpviewitem.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qpixmap.h> + +#include "ftpviewitem.h" + + +FtpViewItem::FtpViewItem( QListView *parent, Type t, const QString &name, const QString &size, const QString &lastModified ) + : QListViewItem(parent,name,size,lastModified), type(t) +{ + // the pixmaps for folders and files are in an image collection + if ( type == Directory ) + setPixmap( 0, QPixmap::fromMimeSource( "folder.png" ) ); + else + setPixmap( 0, QPixmap::fromMimeSource( "file.png" ) ); +} + +int FtpViewItem::compare( QListViewItem * i, int col, bool ascending ) const +{ + // The entry ".." is always the first one. + if ( text(0) == ".." ) { + if ( ascending ) + return -1; + else + return 1; + } + if ( i->text(0) == ".." ) { + if ( ascending ) + return 1; + else + return -1; + } + + // Directories are before files. + if ( type != ((FtpViewItem*)i)->type ) { + if ( type == Directory ) { + if ( ascending ) + return -1; + else + return 1; + } else { + if ( ascending ) + return 1; + else + return -1; + } + } + + // Use default sorting otherwise. + return QListViewItem::compare( i, col, ascending ); +} diff --git a/examples/network/ftpclient/ftpviewitem.h b/examples/network/ftpclient/ftpviewitem.h new file mode 100644 index 0000000..4ea931d --- /dev/null +++ b/examples/network/ftpclient/ftpviewitem.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef FTPVIEWITEM_H +#define FTPVIEWITEM_H + +#include <qlistview.h> +#include <qdatetime.h> + +class FtpViewItem : public QListViewItem +{ +public: + enum Type { + Directory, + File + }; + + FtpViewItem( QListView *parent, Type t, const QString &name, const QString &size, const QString &lastModified ); + + int compare( QListViewItem * i, int col, bool ascending ) const; + + bool isDir() + { return type==Directory; } + +private: + Type type; +}; + +#endif diff --git a/examples/network/ftpclient/images/file.png b/examples/network/ftpclient/images/file.png Binary files differnew file mode 100644 index 0000000..f042c91 --- /dev/null +++ b/examples/network/ftpclient/images/file.png diff --git a/examples/network/ftpclient/images/folder.png b/examples/network/ftpclient/images/folder.png Binary files differnew file mode 100644 index 0000000..5fd0a43 --- /dev/null +++ b/examples/network/ftpclient/images/folder.png diff --git a/examples/network/ftpclient/main.cpp b/examples/network/ftpclient/main.cpp new file mode 100644 index 0000000..abd6e89 --- /dev/null +++ b/examples/network/ftpclient/main.cpp @@ -0,0 +1,23 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qapplication.h> +#include "ftpmainwindow.h" + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + FtpMainWindow m; + a.setMainWidget( &m ); + m.show(); + a.processEvents(); + m.connectToHost(); + return a.exec(); +} diff --git a/examples/network/httpd/README b/examples/network/httpd/README new file mode 100644 index 0000000..dc7229d --- /dev/null +++ b/examples/network/httpd/README @@ -0,0 +1,7 @@ +This is an example for the QServerSocket class which is included in the +network module. + +It is a simple http daemon. After starting it you can connect to port +8080 with a web browser and an html page is shown. The program does +not really parse the http request; so it is not of much use except for +being a small example for QServerSocket. diff --git a/examples/network/httpd/httpd.cpp b/examples/network/httpd/httpd.cpp new file mode 100644 index 0000000..4c40961 --- /dev/null +++ b/examples/network/httpd/httpd.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ +#include <stdlib.h> +#include <qsocket.h> +#include <qregexp.h> +#include <qserversocket.h> +#include <qapplication.h> +#include <qmainwindow.h> +#include <qtextstream.h> +#include <qvbox.h> +#include <qlabel.h> +#include <qtextview.h> +#include <qpushbutton.h> + +// HttpDaemon is the the class that implements the simple HTTP server. +class HttpDaemon : public QServerSocket +{ + Q_OBJECT +public: + HttpDaemon( QObject* parent=0 ) : + QServerSocket(8080,1,parent) + { + if ( !ok() ) { + qWarning("Failed to bind to port 8080"); + exit( 1 ); + } + } + + void newConnection( int socket ) + { + // When a new client connects, the server constructs a QSocket and all + // communication with the client is done over this QSocket. QSocket + // works asynchronouslyl, this means that all the communication is done + // in the two slots readClient() and discardClient(). + QSocket* s = new QSocket( this ); + connect( s, SIGNAL(readyRead()), this, SLOT(readClient()) ); + connect( s, SIGNAL(delayedCloseFinished()), this, SLOT(discardClient()) ); + s->setSocket( socket ); + emit newConnect(); + } + +signals: + void newConnect(); + void endConnect(); + void wroteToClient(); + +private slots: + void readClient() + { + // This slot is called when the client sent data to the server. The + // server looks if it was a get request and sends a very simple HTML + // document back. + QSocket* socket = (QSocket*)sender(); + if ( socket->canReadLine() ) { + QStringList tokens = QStringList::split( QRegExp("[ \r\n][ \r\n]*"), socket->readLine() ); + if ( tokens[0] == "GET" ) { + QTextStream os( socket ); + os.setEncoding( QTextStream::UnicodeUTF8 ); + os << "HTTP/1.0 200 Ok\r\n" + "Content-Type: text/html; charset=\"utf-8\"\r\n" + "\r\n" + "<h1>Nothing to see here</h1>\n"; + socket->close(); + emit wroteToClient(); + } + } + } + void discardClient() + { + QSocket* socket = (QSocket*)sender(); + delete socket; + emit endConnect(); + } +}; + + +// HttpInfo provides a simple graphical user interface to the server and shows +// the actions of the server. +class HttpInfo : public QVBox +{ + Q_OBJECT +public: + HttpInfo() + { + HttpDaemon *httpd = new HttpDaemon( this ); + + QString itext = QString( + "This is a small httpd example.\n" + "You can connect with your\n" + "web browser to port %1" + ).arg( httpd->port() ); + QLabel *lb = new QLabel( itext, this ); + lb->setAlignment( AlignHCenter ); + infoText = new QTextView( this ); + QPushButton *quit = new QPushButton( "quit" , this ); + + connect( httpd, SIGNAL(newConnect()), SLOT(newConnect()) ); + connect( httpd, SIGNAL(endConnect()), SLOT(endConnect()) ); + connect( httpd, SIGNAL(wroteToClient()), SLOT(wroteToClient()) ); + connect( quit, SIGNAL(pressed()), qApp, SLOT(quit()) ); + } + + ~HttpInfo() + { + } + +private slots: + void newConnect() + { + infoText->append( "New connection" ); + } + void endConnect() + { + infoText->append( "Connection closed\n\n" ); + } + void wroteToClient() + { + infoText->append( "Wrote to client" ); + } + +private: + QTextView *infoText; +}; + + +int main( int argc, char** argv ) +{ + QApplication app( argc, argv ); + HttpInfo info; + app.setMainWidget( &info ); + info.show(); + return app.exec(); +} + +#include "httpd.moc" diff --git a/examples/network/httpd/httpd.doc b/examples/network/httpd/httpd.doc new file mode 100644 index 0000000..de1e0be --- /dev/null +++ b/examples/network/httpd/httpd.doc @@ -0,0 +1,20 @@ +/* +*/ + +/*! \page httpd-example.html + + \ingroup network-examples + + \title A simple HTTP daemon + + This example shows how to use the QServerSocket class. It is a very + simple implementation of a HTTP daemon that listens on port 8080 and + sends back a simple HTML page back for every GET request it gets. After + sending the page, it closes the connection. + + <hr> + + Implementation (httpd.cpp): + + \include network/httpd/httpd.cpp +*/ diff --git a/examples/network/httpd/httpd.pro b/examples/network/httpd/httpd.pro new file mode 100644 index 0000000..a364499 --- /dev/null +++ b/examples/network/httpd/httpd.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +TARGET = httpd + +CONFIG += qt warn_on release + +REQUIRES = network large-config + +HEADERS = +SOURCES = httpd.cpp diff --git a/examples/network/infoprotocol/infoclient/client.cpp b/examples/network/infoprotocol/infoclient/client.cpp new file mode 100644 index 0000000..89bcfd2 --- /dev/null +++ b/examples/network/infoprotocol/infoclient/client.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qsocket.h> +#include <qapplication.h> +#include <qtextedit.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qtextstream.h> +#include <qlistbox.h> + +#include "client.h" + + +ClientInfo::ClientInfo( QWidget *parent, const char *name ) : + ClientInfoBase( parent, name ), socket( 0 ) +{ + edHost->setText( "localhost" ); + edPort->setText( QString::number( (uint)infoPort ) ); + + connect( infoList, SIGNAL(selected(const QString&)), SLOT(selectItem(const QString&)) ); + connect( btnConnect, SIGNAL(clicked()), SLOT(connectToServer()) ); + connect( btnBack, SIGNAL(clicked()), SLOT(stepBack()) ); + connect( btnQuit, SIGNAL(clicked()), qApp, SLOT(quit()) ); +} + + +void ClientInfo::connectToServer() +{ + delete socket; + socket = new QSocket( this ); + connect( socket, SIGNAL(connected()), SLOT(socketConnected()) ); + connect( socket, SIGNAL(connectionClosed()), SLOT(socketConnectionClosed()) ); + connect( socket, SIGNAL(readyRead()), SLOT(socketReadyRead()) ); + connect( socket, SIGNAL(error(int)), SLOT(socketError(int)) ); + + socket->connectToHost( edHost->text(), edPort->text().toInt() ); +} + +void ClientInfo::selectItem( const QString& item ) +{ + // item in listBox selected, use LIST or GET depending of the node type. + if ( item.endsWith( "/" ) ) { + sendToServer( List, infoPath->text() + item ); + infoPath->setText( infoPath->text() + item ); + } else + sendToServer( Get, infoPath->text() + item ); +} + +void ClientInfo::stepBack() +{ + // go back (up) in path hierarchy + int i = infoPath->text().findRev( '/', -2 ); + if ( i > 0 ) + infoPath->setText( infoPath->text().left( i + 1 ) ); + else + infoPath->setText( "/" ); + infoList->clear(); + sendToServer( List, infoPath->text() ); +} + + +void ClientInfo::socketConnected() +{ + sendToServer( List, "/" ); +} + +void ClientInfo::sendToServer( Operation op, const QString& location ) +{ + QString line; + switch (op) { + case List: + infoList->clear(); + line = "LIST " + location; + break; + case Get: + line = "GET " + location; + break; + } + infoText->clear(); + QTextStream os(socket); + os << line << "\r\n"; +} + +void ClientInfo::socketReadyRead() +{ + QTextStream stream( socket ); + QString line; + while ( socket->canReadLine() ) { + line = stream.readLine(); + if ( line.startsWith( "500" ) || line.startsWith( "550" ) ) { + infoText->append( tr( "error: " ) + line.mid( 4 ) ); + } else if ( line.startsWith( "212+" ) ) { + infoList->insertItem( line.mid( 6 ) + QString( ( line[ 4 ] == 'D' ) ? "/" : "" ) ); + } else if ( line.startsWith( "213+" ) ) { + infoText->append( line.mid( 4 ) ); + } + } +} + + +void ClientInfo::socketConnectionClosed() +{ + infoText->clear(); + infoText->append( tr( "error: Connection closed by the server\n" ) ); +} + +void ClientInfo::socketError( int code ) +{ + infoText->clear(); + infoText->append( tr( "error: Error number %1 occurred\n" ).arg( code ) ); +} + diff --git a/examples/network/infoprotocol/infoclient/client.h b/examples/network/infoprotocol/infoclient/client.h new file mode 100644 index 0000000..a3e09af --- /dev/null +++ b/examples/network/infoprotocol/infoclient/client.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef CLIENT_H +#define CLIENT_H + +#include "clientbase.h" + +class QSocket; +class QTextEdit; +class QLineEdit; +class QListBox; +class QLabel; + +static const Q_UINT16 infoPort = 42417; + +class ClientInfo : public ClientInfoBase +{ + Q_OBJECT +public: + ClientInfo( QWidget *parent = 0, const char *name = 0 ); + +private: + enum Operation { List, Get }; + +private slots: + void connectToServer(); + void selectItem( const QString& item ); + void stepBack(); + void sendToServer( Operation op, const QString& location ); + void socketConnected(); + void socketReadyRead(); + void socketConnectionClosed(); + void socketError( int code ); + +private: + QSocket *socket; +}; + +#endif // CLIENT_H + diff --git a/examples/network/infoprotocol/infoclient/clientbase.ui b/examples/network/infoprotocol/infoclient/clientbase.ui new file mode 100644 index 0000000..3f73c9a --- /dev/null +++ b/examples/network/infoprotocol/infoclient/clientbase.ui @@ -0,0 +1,276 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>ClientInfoBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ClientInfoBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>384</width> + <height>488</height> + </rect> + </property> + <property name="caption"> + <string>Info Client</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout15</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnConnect</cstring> + </property> + <property name="text"> + <string>&Connect</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="text"> + <string>Host:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>edHost</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2_2</cstring> + </property> + <property name="text"> + <string>Port:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>edPort</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QSplitter"> + <property name="name"> + <cstring>Splitter4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout16</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout14</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnBack</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Back</string> + </property> + <property name="toolTip" stdset="0"> + <string>go one step back</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>infoPath</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>/</string> + </property> + <property name="toolTip" stdset="0"> + <string>current path</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QListBox"> + <property name="name"> + <cstring>infoList</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>double click to open</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout3</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>Data:</string> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>infoText</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </vbox> + </widget> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout12</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer10</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnQuit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Quit</string> + </property> + </widget> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer9</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/examples/network/infoprotocol/infoclient/infoclient.pro b/examples/network/infoprotocol/infoclient/infoclient.pro new file mode 100644 index 0000000..46759dd --- /dev/null +++ b/examples/network/infoprotocol/infoclient/infoclient.pro @@ -0,0 +1,11 @@ +TEMPLATE = app +TARGET = infoclient + +CONFIG += qt warn_on release + +REQUIRES = network full-config nocrosscompiler + +HEADERS = client.h +SOURCES = main.cpp \ + client.cpp +INTERFACES = clientbase.ui diff --git a/examples/network/infoprotocol/infoclient/main.cpp b/examples/network/infoprotocol/infoclient/main.cpp new file mode 100644 index 0000000..cc4950a --- /dev/null +++ b/examples/network/infoprotocol/infoclient/main.cpp @@ -0,0 +1,21 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qapplication.h> + +#include "client.h" + +int main( int argc, char** argv ) +{ + QApplication app( argc, argv ); + ClientInfo info; + app.setMainWidget( &info ); + info.show(); + return app.exec(); +} diff --git a/examples/network/infoprotocol/infoserver/infodata.cpp b/examples/network/infoprotocol/infoserver/infodata.cpp new file mode 100644 index 0000000..597f8a3 --- /dev/null +++ b/examples/network/infoprotocol/infoserver/infodata.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include "infodata.h" + + +// we hard code all nodes and data in InfoData class +InfoData::InfoData() : + nodes( 17, TRUE ), data( 17, TRUE ) +{ + nodes.setAutoDelete(TRUE); + data.setAutoDelete(TRUE); + QStringList *item; + + nodes.insert( "/", item = new QStringList( ) ); + (*item) << "D network"; + nodes.insert( "/network/", item = new QStringList() ); + (*item) << "D workstations" << "D printers" << "D fax"; + nodes.insert( "/network/workstations/", item = new QStringList() ); + (*item) << "D nibble" << "D douglas"; + nodes.insert( "/network/workstations/nibble/", item = new QStringList() ); + (*item) << "F os" << "F cpu" << "F memory"; + nodes.insert( "/network/workstations/douglas/", item = new QStringList() ); + (*item) << "F os" << "F cpu" << "F memory"; + nodes.insert( "/network/printers/", item = new QStringList() ); + (*item) << "D overbitt" << "D kroksleiven"; + nodes.insert( "/network/printers/overbitt/", item = new QStringList() ); + (*item) << "D jobs" << "F type"; + nodes.insert( "/network/printers/overbitt/jobs/", item = new QStringList() ); + (*item) << "F job1" << "F job2"; + nodes.insert( "/network/printers/kroksleiven/", item = new QStringList() ); + (*item) << "D jobs" << "F type"; + nodes.insert( "/network/printers/kroksleiven/jobs/", item = new QStringList() ); + nodes.insert( "/network/fax/", item = new QStringList() ); + (*item) << "F last_number"; + + data.insert( "/network/workstations/nibble/os", new QString( "Linux" ) ); + data.insert( "/network/workstations/nibble/cpu", new QString( "AMD Athlon 1000" ) ); + data.insert( "/network/workstations/nibble/memory", new QString( "256 MB" ) ); + data.insert( "/network/workstations/douglas/os", new QString( "Windows 2000" ) ); + data.insert( "/network/workstations/douglas/cpu", new QString( "2 x Intel Pentium III 800" ) ); + data.insert( "/network/workstations/douglas/memory", new QString( "256 MB" ) ); + data.insert( "/network/printers/overbitt/type", new QString( "Lexmark Optra S 1255 PS" ) ); + data.insert( "/network/printers/overbitt/jobs/job1", + new QString( "Qt manual\n" "A4 size\n" "3000 pages" ) ); + data.insert( "/network/printers/overbitt/jobs/job2", + new QString( "Monthly report\n" "Letter size\n" "24 pages\n" "8 copies" ) ); + data.insert( "/network/printers/kroksleiven/type", new QString( "HP C LaserJet 4500-PS" ) ); + data.insert( "/network/fax/last_number", new QString( "22 22 22 22" ) ); +} + +QStringList InfoData::list( QString path, bool *found ) const +{ + if ( !path.endsWith( "/" ) ) + path += "/"; + if ( !path.startsWith( "/" ) ) + path = "/" + path; + QStringList *list = nodes[ path ]; + if ( list ) { + *found = TRUE; + return *list; + } else { + *found = FALSE; + QStringList empty; + return empty; + } +} + +QString InfoData::get( QString path, bool *found ) const +{ + if ( !path.startsWith( "/" ) ) + path = "/" + path; + QString *file = data[ path ]; + if ( file ) { + *found = TRUE; + return *file; + } else { + *found = FALSE; + QString empty; + return empty; + } +} diff --git a/examples/network/infoprotocol/infoserver/infodata.h b/examples/network/infoprotocol/infoserver/infodata.h new file mode 100644 index 0000000..0189332 --- /dev/null +++ b/examples/network/infoprotocol/infoserver/infodata.h @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef INFODATA_H +#define INFODATA_H + +#include <qdict.h> +#include <qstringlist.h> + + +// The InfoData class manages data, organized in tree structure. +class InfoData +{ +public: + InfoData(); + QStringList list( QString path, bool *found ) const; + QString get( QString path, bool *found ) const; + +private: + QDict< QStringList > nodes; + QDict< QString > data; +}; + +#endif // INFODATA_H + diff --git a/examples/network/infoprotocol/infoserver/infoserver.pro b/examples/network/infoprotocol/infoserver/infoserver.pro new file mode 100644 index 0000000..3d79aef --- /dev/null +++ b/examples/network/infoprotocol/infoserver/infoserver.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +TARGET = infoserver + +CONFIG += qt warn_on release + +REQUIRES = network full-config nocrosscompiler + +HEADERS = server.h \ + infodata.h +SOURCES = main.cpp \ + server.cpp \ + infodata.cpp +INTERFACES = serverbase.ui diff --git a/examples/network/infoprotocol/infoserver/main.cpp b/examples/network/infoprotocol/infoserver/main.cpp new file mode 100644 index 0000000..fd0e8e3 --- /dev/null +++ b/examples/network/infoprotocol/infoserver/main.cpp @@ -0,0 +1,22 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qapplication.h> + +#include "server.h" + +int main( int argc, char** argv ) +{ + QApplication app( argc, argv ); + Q_UINT16 port = ( argc > 1 ) ? QString( argv[ 1 ] ).toInt() : infoPort; + ServerInfo info( port, 0, "server info" ); + app.setMainWidget( &info ); + info.show(); + return app.exec(); +} diff --git a/examples/network/infoprotocol/infoserver/server.cpp b/examples/network/infoprotocol/infoserver/server.cpp new file mode 100644 index 0000000..f3815ae --- /dev/null +++ b/examples/network/infoprotocol/infoserver/server.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qtextview.h> +#include <qpushbutton.h> +#include <qtextstream.h> +#include <qapplication.h> +#include <qmessagebox.h> +#include <stdlib.h> + +#include "server.h" + + + +ServerInfo::ServerInfo( Q_UINT16 port, QWidget *parent, const char *name ) : + ServerInfoBase( parent, name ) +{ + SimpleServer *server = new SimpleServer( port, this, "simple server" ); + connect( server, SIGNAL(newConnect()), SLOT(newConnect()) ); + connect( btnQuit, SIGNAL(clicked()), qApp, SLOT(quit()) ); +} + +void ServerInfo::newConnect() +{ + infoText->append( tr( "New connection\n" ) ); +} + + +SimpleServer::SimpleServer( Q_UINT16 port, QObject* parent, const char *name ) : + QServerSocket( port, 1, parent, name ) +{ + if ( !ok() ) { + QMessageBox::critical( 0, tr( "Error" ), tr( "Failed to bind to port %1" ).arg( port ) ); + exit(1); + } +} + +void SimpleServer::newConnection( int socket ) +{ + (void)new ClientSocket( socket, &info, this, "client socket" ); + emit newConnect(); +} + + +ClientSocket::ClientSocket( int sock, InfoData *i, QObject *parent, const char *name ) : + QSocket( parent, name ), info( i ) +{ + connect( this, SIGNAL(readyRead()), SLOT(readClient()) ); + connect( this, SIGNAL(connectionClosed()), SLOT(connectionClosed()) ); + setSocket( sock ); +} + +void ClientSocket::readClient() +{ + QTextStream stream( this ); + QStringList answer; + while ( canReadLine() ) { + stream << processCommand( stream.readLine() ); + } +} + +QString ClientSocket::processCommand( const QString& command ) +{ + QString answer; + QString com = command.simplifyWhiteSpace (); + if ( com.startsWith( "LIST" ) ) { + bool ok; + QStringList nodes = info->list( com.mid( 5 ), &ok ); + if ( ok ) { + for ( QStringList::Iterator it = nodes.begin(); it != nodes.end(); ++it ) + answer += "212+" + *it + "\r\n"; + answer += "212 \r\n"; + } else + answer += "550 Invalid path\r\n"; + } else if ( com.startsWith( "GET " ) ) { + bool ok; + QStringList data = QStringList::split( '\n', info->get( com.mid( 4 ), &ok ), TRUE ); + if ( ok ) { + for ( QStringList::Iterator it = data.begin(); it != data.end(); ++it ) + answer += "213+" + *it + "\r\n"; + answer += "213 \r\n"; + } else + answer += "550 Info not found\r\n"; + } else + answer += "500 Syntax error\r\n"; + + return answer; +} + +void ClientSocket::connectionClosed() +{ + delete this; +} diff --git a/examples/network/infoprotocol/infoserver/server.h b/examples/network/infoprotocol/infoserver/server.h new file mode 100644 index 0000000..fa07b93 --- /dev/null +++ b/examples/network/infoprotocol/infoserver/server.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef SERVER_H +#define SERVER_H + +#include <qsocket.h> +#include <qserversocket.h> + +#include "infodata.h" +#include "serverbase.h" + +static const Q_UINT16 infoPort = 42417; + + +/* + The ServerInfo class provides a small GUI for the server. It also creates the + SimpleServer and as a result the server. +*/ +class ServerInfo : public ServerInfoBase +{ + Q_OBJECT +public: + ServerInfo( Q_UINT16 port = infoPort, QWidget *parent = 0, const char *name = 0 ); + +private slots: + void newConnect(); +}; + + +class SimpleServer : public QServerSocket +{ + Q_OBJECT +public: + SimpleServer( Q_UINT16 port = infoPort, QObject* parent = 0, const char *name = 0 ); + void newConnection( int socket ); + +signals: + void newConnect(); + +private: + InfoData info; +}; + + +class ClientSocket : public QSocket +{ + Q_OBJECT +public: + ClientSocket( int sock, InfoData *i, QObject *parent = 0, const char *name = 0 ); + +private slots: + void readClient(); + void connectionClosed(); + +private: + QString processCommand( const QString& command ); + InfoData *info; +}; + + +#endif //SERVER_H + diff --git a/examples/network/infoprotocol/infoserver/serverbase.ui b/examples/network/infoprotocol/infoserver/serverbase.ui new file mode 100644 index 0000000..12998da --- /dev/null +++ b/examples/network/infoprotocol/infoserver/serverbase.ui @@ -0,0 +1,117 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>ServerInfoBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ServerInfoBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>272</width> + <height>282</height> + </rect> + </property> + <property name="caption"> + <string>Info Server</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="text"> + <string>This is a small Information Server. +Accepting client requests...</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>infoText</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnQuit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Quit</string> + </property> + </widget> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/examples/network/infoprotocol/infourlclient/client.cpp b/examples/network/infoprotocol/infourlclient/client.cpp new file mode 100644 index 0000000..712090f --- /dev/null +++ b/examples/network/infoprotocol/infourlclient/client.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qapplication.h> +#include <qtextedit.h> +#include <qpushbutton.h> +#include <qfiledialog.h> + +#include "qip.h" +#include "client.h" + + + + +ClientInfo::ClientInfo( QWidget *parent, const char *name ) : + ClientInfoBase( parent, name ) +{ + connect( btnOpen, SIGNAL(clicked()), SLOT(downloadFile()) ); + connect( btnQuit, SIGNAL(clicked()), qApp, SLOT(quit()) ); + connect( &op, SIGNAL( data( const QByteArray &, QNetworkOperation * ) ), + this, SLOT( newData( const QByteArray & ) ) ); +} + +void ClientInfo::downloadFile() +{ + // under Windows you must not use the native file dialog + QString file = getOpenFileName(); + if ( !file.isEmpty() ) { + infoText->clear(); + // download the data + op = file; + op.get(); + } +} + +QString ClientInfo::getOpenFileName() +{ + static QString workingDirectory( "qip://localhost/" ); + + QFileDialog dlg( workingDirectory, QString::null, 0, 0, TRUE ); + dlg.setCaption( QFileDialog::tr( "Open" ) ); + dlg.setMode( QFileDialog::ExistingFile ); + QString result; + if ( dlg.exec() == QDialog::Accepted ) { + result = dlg.selectedFile(); + workingDirectory = dlg.url(); + } + return result; +} + +void ClientInfo::newData( const QByteArray &ba ) +{ + infoText->append( QString::fromUtf8( ba ) ); +} diff --git a/examples/network/infoprotocol/infourlclient/client.h b/examples/network/infoprotocol/infourlclient/client.h new file mode 100644 index 0000000..ca8340b --- /dev/null +++ b/examples/network/infoprotocol/infourlclient/client.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef CLIENT_H +#define CLIENT_H + +#include <qurloperator.h> + +#include "clientbase.h" + + +class ClientInfo : public ClientInfoBase +{ + Q_OBJECT + +public: + ClientInfo( QWidget *parent = 0, const char *name = 0 ); + +private slots: + void downloadFile(); + void newData( const QByteArray &ba ); + +private: + QUrlOperator op; + QString getOpenFileName(); +}; + +#endif // CLIENT_H + diff --git a/examples/network/infoprotocol/infourlclient/clientbase.ui b/examples/network/infoprotocol/infourlclient/clientbase.ui new file mode 100644 index 0000000..e18b9a3 --- /dev/null +++ b/examples/network/infoprotocol/infourlclient/clientbase.ui @@ -0,0 +1,118 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>ClientInfoBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ClientInfoBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>325</width> + <height>279</height> + </rect> + </property> + <property name="caption"> + <string>Info Client Protocol</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnOpen</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Open</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>open data</string> + </property> + </widget> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnQuit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Quit</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>Data:</string> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>infoText</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/examples/network/infoprotocol/infourlclient/infourlclient.pro b/examples/network/infoprotocol/infourlclient/infourlclient.pro new file mode 100644 index 0000000..219083d --- /dev/null +++ b/examples/network/infoprotocol/infourlclient/infourlclient.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +TARGET = infourlclient + +CONFIG += qt warn_on release + +REQUIRES = network full-config nocrosscompiler + +HEADERS = client.h \ + qip.h +SOURCES = main.cpp \ + client.cpp \ + qip.cpp +INTERFACES = clientbase.ui diff --git a/examples/network/infoprotocol/infourlclient/main.cpp b/examples/network/infoprotocol/infourlclient/main.cpp new file mode 100644 index 0000000..677f8a1 --- /dev/null +++ b/examples/network/infoprotocol/infourlclient/main.cpp @@ -0,0 +1,23 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qapplication.h> + +#include "qip.h" +#include "client.h" + +int main( int argc, char** argv ) +{ + QApplication app( argc, argv ); + QNetworkProtocol::registerNetworkProtocol( "qip", new QNetworkProtocolFactory< Qip > ); + ClientInfo info; + app.setMainWidget( &info ); + info.show(); + return app.exec(); +} diff --git a/examples/network/infoprotocol/infourlclient/qip.cpp b/examples/network/infoprotocol/infourlclient/qip.cpp new file mode 100644 index 0000000..ef71ea3 --- /dev/null +++ b/examples/network/infoprotocol/infourlclient/qip.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qsocket.h> +#include <qurlinfo.h> +#include <qurloperator.h> +#include <qtextstream.h> + +#include "qip.h" + + +Qip::Qip() +{ + state = Start; + socket = new QSocket( this ); + connect( socket, SIGNAL(connected()), SLOT(socketConnected()) ); + connect( socket, SIGNAL(connectionClosed()), SLOT(socketConnectionClosed()) ); + connect( socket, SIGNAL(readyRead()), SLOT(socketReadyRead()) ); + connect( socket, SIGNAL(error(int)), SLOT(socketError(int)) ); +} + +int Qip::supportedOperations() const +{ + return OpListChildren | OpGet; +} + +bool Qip::checkConnection( QNetworkOperation * ) +{ + if ( socket->isOpen() ) + return TRUE; + + // don't call connectToHost() if we are already trying to connect + if ( socket->state() == QSocket::Connecting ) + return FALSE; + + socket->connectToHost( url()->host(), url()->port() != -1 ? url()->port() : infoPort ); + return FALSE; +} + +void Qip::operationListChildren( QNetworkOperation * ) +{ + QTextStream os(socket); + os << "LIST " + url()->path() + "\r\n"; + operationInProgress()->setState( StInProgress ); +} + +void Qip::operationGet( QNetworkOperation * ) +{ + QTextStream os(socket); + os << "GET " + url()->path() + "\r\n"; + operationInProgress()->setState( StInProgress ); +} + +void Qip::socketConnected() +{ + if ( url() ) + emit connectionStateChanged( ConConnected, tr( "Connected to host %1" ).arg( url()->host() ) ); + else + emit connectionStateChanged( ConConnected, tr ("Connected to host" ) ); +} + +void Qip::socketConnectionClosed() +{ + if ( url() ) + emit connectionStateChanged( ConClosed, tr( "Connection to %1 closed" ).arg( url()->host() ) ); + else + emit connectionStateChanged( ConClosed, tr ("Connection closed" ) ); +} + +void Qip::socketError( int code ) +{ + if ( code == QSocket::ErrHostNotFound || + code == QSocket::ErrConnectionRefused ) { + error( ErrHostNotFound, tr( "Host not found or couldn't connect to: %1\n" ).arg( url()->host() ) ); + } else + error( ErrUnsupported, tr( "Error" ) ); +} + +void Qip::socketReadyRead() +{ + // read from the server + QTextStream stream( socket ); + QString line; + while ( socket->canReadLine() ) { + line = stream.readLine(); + if ( line.startsWith( "500" ) ) { + error( ErrValid, line.mid( 4 ) ); + } else if ( line.startsWith( "550" ) ) { + error( ErrFileNotExisting, line.mid( 4 ) ); + } else if ( line.startsWith( "212+" ) ) { + if ( state != List ) { + state = List; + emit start( operationInProgress() ); + } + QUrlInfo inf; + inf.setName( line.mid( 6 ) + QString( ( line[ 4 ] == 'D' ) ? "/" : "" ) ); + inf.setDir( line[ 4 ] == 'D' ); + inf.setSymLink( FALSE ); + inf.setFile( line[ 4 ] == 'F' ); + inf.setWritable( FALSE ); + inf.setReadable( TRUE ); + emit newChild( inf, operationInProgress() ); + } else if ( line.startsWith( "213+" ) ) { + state = Data; + emit data( line.mid( 4 ).utf8(), operationInProgress() ); + } + if( line[3] == ' ' && state != Start) { + state = Start; + operationInProgress()->setState( StDone ); + emit finished( operationInProgress() ); + } + } +} + +void Qip::error( int code, const QString& msg ) +{ + if ( operationInProgress() ) { + operationInProgress()->setState( StFailed ); + operationInProgress()->setErrorCode( code ); + operationInProgress()->setProtocolDetail( msg ); + clearOperationQueue(); + emit finished( operationInProgress() ); + } + state = Start; +} + diff --git a/examples/network/infoprotocol/infourlclient/qip.h b/examples/network/infoprotocol/infourlclient/qip.h new file mode 100644 index 0000000..339e46b --- /dev/null +++ b/examples/network/infoprotocol/infourlclient/qip.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef QIP_H +#define QIP_H + +#include <qnetworkprotocol.h> + +class QSocket; + +static const Q_UINT16 infoPort = 42417; + + +class Qip : public QNetworkProtocol +{ + Q_OBJECT + +public: + Qip(); + virtual int supportedOperations() const; + +protected: + virtual void operationListChildren( QNetworkOperation *op ); + virtual void operationGet( QNetworkOperation *op ); + virtual bool checkConnection( QNetworkOperation *op ); + +private slots: + void socketConnected(); + void socketReadyRead(); + void socketConnectionClosed(); + void socketError( int code ); + +private: + QSocket *socket; + enum State { Start, List, Data } state; + void error( int code, const QString& msg ); +}; + + + +#endif // QIP_H + diff --git a/examples/network/mail/composer.cpp b/examples/network/mail/composer.cpp new file mode 100644 index 0000000..4c2bb36 --- /dev/null +++ b/examples/network/mail/composer.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include "composer.h" +#include "smtp.h" + +#include <qlineedit.h> +#include <qmultilineedit.h> +#include <qpushbutton.h> +#include <qlabel.h> +#include <qlayout.h> + +Composer::Composer( QWidget *parent ) + : QWidget( parent ) +{ + QGridLayout * layout = new QGridLayout( this, 1, 1, 6 ); + + layout->addWidget( new QLabel( tr( "From:" ), this ), 0, 0 ); + from = new QLineEdit( this ); + layout->addWidget( from, 0, 1 ); + + layout->addWidget( new QLabel( tr( "To:" ), this ), 1, 0 ); + to = new QLineEdit( this ); + layout->addWidget( to, 1, 1 ); + + layout->addWidget( new QLabel( tr( "Subject:" ), this ), 2, 0 ); + subject = new QLineEdit( this ); + layout->addWidget( subject, 2, 1 ); + + message = new QMultiLineEdit( this ); + layout->addMultiCellWidget( message, 3, 3, 0, 1 ); + + send = new QPushButton( tr( "&Send" ), this ); + layout->addWidget( send, 4, 0 ); + connect( send, SIGNAL( clicked() ), this, SLOT( sendMessage() ) ); + + sendStatus = new QLabel( this ); + layout->addWidget( sendStatus, 4, 1 ); +} + + +void Composer::sendMessage() +{ + send->setEnabled( FALSE ); + sendStatus->setText( tr( "Looking up mail servers" ) ); + Smtp *smtp = new Smtp( from->text(), to->text(), + subject->text(), + message->text() ); + connect( smtp, SIGNAL(destroyed()), + this, SLOT(enableSend()) ); + connect( smtp, SIGNAL(status(const QString &)), + sendStatus, SLOT(setText(const QString &)) ); +} + + +void Composer::enableSend() +{ + send->setEnabled( TRUE ); +} diff --git a/examples/network/mail/composer.h b/examples/network/mail/composer.h new file mode 100644 index 0000000..fa457ce --- /dev/null +++ b/examples/network/mail/composer.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef COMPOSER_H +#define COMPOSER_H + +#include <qwidget.h> + + +class QLineEdit; +class QMultiLineEdit; +class QLabel; +class QPushButton; + + +class Composer : public QWidget +{ + Q_OBJECT + +public: + Composer( QWidget *parent = 0 ); + +private slots: + void sendMessage(); + void enableSend(); + +private: + QLineEdit *from, *to, *subject; + QMultiLineEdit *message; + QLabel * sendStatus; + QPushButton * send; +}; + + +#endif diff --git a/examples/network/mail/mail.doc b/examples/network/mail/mail.doc new file mode 100644 index 0000000..009f155 --- /dev/null +++ b/examples/network/mail/mail.doc @@ -0,0 +1,25 @@ +/* +*/ + +/*! \page mail-example.html + + \ingroup network-examples + + \title A simple mail client + + This example shows how to use the QSocket class. The client can only be + used to send mails. The interesting part is the implementation of the + SMTP protocol. + + <hr> + + Header file (smtp.h): + + \include network/mail/smtp.h + + <hr> + + Implementation (smtp.cpp): + + \include network/mail/smtp.cpp +*/ diff --git a/examples/network/mail/mail.pro b/examples/network/mail/mail.pro new file mode 100644 index 0000000..39c9de5 --- /dev/null +++ b/examples/network/mail/mail.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +TARGET = mail + +CONFIG += qt warn_on release + +REQUIRES = network full-config + +HEADERS = composer.h \ + smtp.h +SOURCES = composer.cpp \ + main.cpp \ + smtp.cpp diff --git a/examples/network/mail/main.cpp b/examples/network/mail/main.cpp new file mode 100644 index 0000000..ac60756 --- /dev/null +++ b/examples/network/mail/main.cpp @@ -0,0 +1,23 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qapplication.h> + +#include "composer.h" + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + Composer c; + a.setMainWidget( &c ); + c.resize( 400, 500 ); + c.show(); + return a.exec(); +} diff --git a/examples/network/mail/smtp.cpp b/examples/network/mail/smtp.cpp new file mode 100644 index 0000000..f801070 --- /dev/null +++ b/examples/network/mail/smtp.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include "smtp.h" + +#include <qtextstream.h> +#include <qsocket.h> +#include <qdns.h> +#include <qtimer.h> +#include <qapplication.h> +#include <qmessagebox.h> +#include <qregexp.h> + + +Smtp::Smtp( const QString &from, const QString &to, + const QString &subject, + const QString &body ) +{ + socket = new QSocket( this ); + connect ( socket, SIGNAL( readyRead() ), + this, SLOT( readyRead() ) ); + connect ( socket, SIGNAL( connected() ), + this, SLOT( connected() ) ); + + mxLookup = new QDns( to.mid( to.find( '@' )+1 ), QDns::Mx ); + connect( mxLookup, SIGNAL(resultsReady()), + this, SLOT(dnsLookupHelper()) ); + + message = QString::fromLatin1( "From: " ) + from + + QString::fromLatin1( "\nTo: " ) + to + + QString::fromLatin1( "\nSubject: " ) + subject + + QString::fromLatin1( "\n\n" ) + body + "\n"; + message.replace( QString::fromLatin1( "\n" ), + QString::fromLatin1( "\r\n" ) ); + message.replace( QString::fromLatin1( "\r\n.\r\n" ), + QString::fromLatin1( "\r\n..\r\n" ) ); + + this->from = from; + rcpt = to; + + state = Init; +} + + +Smtp::~Smtp() +{ + delete t; + delete socket; +} + + +void Smtp::dnsLookupHelper() +{ + QValueList<QDns::MailServer> s = mxLookup->mailServers(); + if ( s.isEmpty() ) { + if ( !mxLookup->isWorking() ) + emit status( tr( "Error in MX record lookup" ) ); + return; + } + + emit status( tr( "Connecting to %1" ).arg( s.first().name ) ); + + socket->connectToHost( s.first().name, 25 ); + t = new QTextStream( socket ); +} + + +void Smtp::connected() +{ + emit status( tr( "Connected to %1" ).arg( socket->peerName() ) ); +} + +void Smtp::readyRead() +{ + // SMTP is line-oriented + if ( !socket->canReadLine() ) + return; + + QString responseLine; + do { + responseLine = socket->readLine(); + response += responseLine; + } while( socket->canReadLine() && responseLine[3] != ' ' ); + responseLine.truncate( 3 ); + + if ( state == Init && responseLine[0] == '2' ) { + // banner was okay, let's go on + *t << "HELO there\r\n"; + state = Mail; + } else if ( state == Mail && responseLine[0] == '2' ) { + // HELO response was okay (well, it has to be) + *t << "MAIL FROM: <" << from << ">\r\n"; + state = Rcpt; + } else if ( state == Rcpt && responseLine[0] == '2' ) { + *t << "RCPT TO: <" << rcpt << ">\r\n"; + state = Data; + } else if ( state == Data && responseLine[0] == '2' ) { + *t << "DATA\r\n"; + state = Body; + } else if ( state == Body && responseLine[0] == '3' ) { + *t << message << ".\r\n"; + state = Quit; + } else if ( state == Quit && responseLine[0] == '2' ) { + *t << "QUIT\r\n"; + // here, we just close. + state = Close; + emit status( tr( "Message sent" ) ); + } else if ( state == Close ) { + deleteLater(); + return; + } else { + // something broke. + QMessageBox::warning( qApp->activeWindow(), + tr( "Qt Mail Example" ), + tr( "Unexpected reply from SMTP server:\n\n" ) + + response ); + state = Close; + } + + response = ""; +} diff --git a/examples/network/mail/smtp.h b/examples/network/mail/smtp.h new file mode 100644 index 0000000..0a469a5 --- /dev/null +++ b/examples/network/mail/smtp.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef SMTP_H +#define SMTP_H + +#include <qobject.h> +#include <qstring.h> + +class QSocket; +class QTextStream; +class QDns; + +class Smtp : public QObject +{ + Q_OBJECT + +public: + Smtp( const QString &from, const QString &to, + const QString &subject, const QString &body ); + ~Smtp(); + +signals: + void status( const QString & ); + +private slots: + void dnsLookupHelper(); + void readyRead(); + void connected(); + +private: + enum State { + Init, + Mail, + Rcpt, + Data, + Body, + Quit, + Close + }; + + QString message; + QString from; + QString rcpt; + QSocket *socket; + QTextStream * t; + int state; + QString response; + QDns * mxLookup; +}; + +#endif diff --git a/examples/network/networkprotocol/README b/examples/network/networkprotocol/README new file mode 100644 index 0000000..7c1e69d --- /dev/null +++ b/examples/network/networkprotocol/README @@ -0,0 +1,5 @@ +This class shows you how to implement your own network protocols and how to +use them in a program. This example is part of the network module. + +The class Nntp implements the nntp protocol. The implementation of the +protocol is not complete and should not be used in real programs. diff --git a/examples/network/networkprotocol/main.cpp b/examples/network/networkprotocol/main.cpp new file mode 100644 index 0000000..8d636e4 --- /dev/null +++ b/examples/network/networkprotocol/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include <qapplication.h> +#include <qnetwork.h> + +#include "nntp.h" +#include "view.h" + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + qInitNetworkProtocols(); + QNetworkProtocol::registerNetworkProtocol( "nntp", new QNetworkProtocolFactory<Nntp> ); + + View v; + v.resize( 600, 600 ); + v.show(); + a.setMainWidget( &v ); + + return a.exec(); +} diff --git a/examples/network/networkprotocol/networkprotocol.doc b/examples/network/networkprotocol/networkprotocol.doc new file mode 100644 index 0000000..ea00797 --- /dev/null +++ b/examples/network/networkprotocol/networkprotocol.doc @@ -0,0 +1,26 @@ +/* +*/ + +/*! \page networkprotocol-example.html + + \ingroup network-examples + + \title A simple NNTP implementation + + This example shows how to implement your own QNetworkProtocol. The + protocol that was chosen for this example is NTTP. Please note that this + implementation is very simple since it is designed to be an example. It + should not be used as a real NNTP implemention. + + <hr> + + Header file (nntp.h): + + \include network/networkprotocol/nntp.h + + <hr> + + Implementation (nntp.cpp): + + \include network/networkprotocol/nntp.cpp +*/ diff --git a/examples/network/networkprotocol/networkprotocol.pro b/examples/network/networkprotocol/networkprotocol.pro new file mode 100644 index 0000000..1c6ea07 --- /dev/null +++ b/examples/network/networkprotocol/networkprotocol.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +TARGET = networkprotocol + +CONFIG += qt warn_on release + +REQUIRES = network full-config + +HEADERS = nntp.h view.h +SOURCES = main.cpp \ + nntp.cpp view.cpp diff --git a/examples/network/networkprotocol/nntp.cpp b/examples/network/networkprotocol/nntp.cpp new file mode 100644 index 0000000..1307b9b --- /dev/null +++ b/examples/network/networkprotocol/nntp.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include "nntp.h" +#include <qurlinfo.h> +#include <stdlib.h> +#include <qurloperator.h> +#include <qstringlist.h> +#include <qregexp.h> + +Nntp::Nntp() + : QNetworkProtocol(), connectionReady( FALSE ), + readGroups( FALSE ), readArticle( FALSE ) +{ + // create the command socket and connect to its signals + commandSocket = new QSocket( this ); + connect( commandSocket, SIGNAL( hostFound() ), + this, SLOT( hostFound() ) ); + connect( commandSocket, SIGNAL( connected() ), + this, SLOT( connected() ) ); + connect( commandSocket, SIGNAL( connectionClosed() ), + this, SLOT( closed() ) ); + connect( commandSocket, SIGNAL( readyRead() ), + this, SLOT( readyRead() ) ); + connect( commandSocket, SIGNAL( error( int ) ), + this, SLOT( error( int ) ) ); +} + +Nntp::~Nntp() +{ + close(); + delete commandSocket; +} + +void Nntp::operationListChildren( QNetworkOperation * ) +{ + // create a command + QString path = url()->path(), cmd; + if ( path.isEmpty() || path == "/" ) { + // if the path is empty or we are in the root dir, + // we want to read the list of available newsgroups + cmd = "list newsgroups\r\n"; + } else if ( url()->isDir() ) { + // if the path is a directory (in our case a news group) + // we want to list the articles of this group + path = path.replace( "/", "" ); + cmd = "listgroup " + path + "\r\n"; + } else + return; + + // write the command to the socket + commandSocket->writeBlock( cmd.latin1(), cmd.length() ); + readGroups = TRUE; +} + +void Nntp::operationGet( QNetworkOperation *op ) +{ + // get the dirPath of the URL (this is our news group) + // and the filename (which is the article we want to read) + QUrl u( op->arg( 0 ) ); + QString dirPath = u.dirPath(), file = u.fileName(); + dirPath = dirPath.replace( "/", "" ); + + // go to the group in which the article is + QString cmd; + cmd = "group " + dirPath + "\r\n"; + commandSocket->writeBlock( cmd.latin1(), cmd.length() ); + + // read the head of the article + cmd = "article " + file + "\r\n"; + commandSocket->writeBlock( cmd.latin1(), cmd.length() ); + readArticle = TRUE; +} + +bool Nntp::checkConnection( QNetworkOperation * ) +{ + // we are connected, return TRUE + if ( commandSocket->isOpen() && connectionReady ) + return TRUE; + + // seems that there is no chance to connect + if ( commandSocket->isOpen() ) + return FALSE; + + // don't call connectToHost() if we are already trying to connect + if ( commandSocket->state() == QSocket::Connecting ) + return FALSE; + + // start connecting + connectionReady = FALSE; + commandSocket->connectToHost( url()->host(), + url()->port() != -1 ? url()->port() : 119 ); + return FALSE; +} + +void Nntp::close() +{ + // close the command socket + if ( commandSocket->isOpen() ) { + commandSocket->writeBlock( "quit\r\n", strlen( "quit\r\n" ) ); + commandSocket->close(); + } +} + +int Nntp::supportedOperations() const +{ + // we only support listing children and getting data + return OpListChildren | OpGet; +} + +void Nntp::hostFound() +{ + if ( url() ) + emit connectionStateChanged( ConHostFound, tr( "Host %1 found" ).arg( url()->host() ) ); + else + emit connectionStateChanged( ConHostFound, tr( "Host found" ) ); +} + +void Nntp::connected() +{ + if ( url() ) + emit connectionStateChanged( ConConnected, tr( "Connected to host %1" ).arg( url()->host() ) ); + else + emit connectionStateChanged( ConConnected, tr( "Connected to host" ) ); +} + +void Nntp::closed() +{ + if ( url() ) + emit connectionStateChanged( ConClosed, tr( "Connection to %1 closed" ).arg( url()->host() ) ); + else + emit connectionStateChanged( ConClosed, tr( "Connection closed" ) ); +} + +void Nntp::readyRead() +{ + // new data arrived on the command socket + + // of we should read the list of available groups, let's do so + if ( readGroups ) { + parseGroups(); + return; + } + + // of we should read an article, let's do so + if ( readArticle ) { + parseArticle(); + return; + } + + // read the new data from the socket + QCString s; + s.resize( commandSocket->bytesAvailable() + 1 ); + commandSocket->readBlock( s.data(), commandSocket->bytesAvailable() ); + + if ( !url() ) + return; + + // of the code of the server response was 200, we know that the + // server is ready to get commands from us now + if ( s.left( 3 ) == "200" ) + connectionReady = TRUE; +} + +void Nntp::parseGroups() +{ + if ( !commandSocket->canReadLine() ) + return; + + // read one line after the other + while ( commandSocket->canReadLine() ) { + QString s = commandSocket->readLine(); + + // if the line starts with a dot, all groups or articles have been listed, + // so we finished processing the listChildren() command + if ( s[ 0 ] == '.' ) { + readGroups = FALSE; + operationInProgress()->setState( StDone ); + emit finished( operationInProgress() ); + return; + } + + // if the code of the server response is 215 or 211 + // the next line will be the first group or article (depending on what we read). + // So let others know that we start reading now... + if ( s.left( 3 ) == "215" || s.left( 3 ) == "211" ) { + operationInProgress()->setState( StInProgress ); + emit start( operationInProgress() ); + continue; + } + + // parse the line and create a QUrlInfo object + // which describes the child (group or article) + bool tab = s.find( '\t' ) != -1; + QString group = s.mid( 0, s.find( tab ? '\t' : ' ' ) ); + QUrlInfo inf; + inf.setName( group ); + QString path = url()->path(); + inf.setDir( path.isEmpty() || path == "/" ); + inf.setSymLink( FALSE ); + inf.setFile( !inf.isDir() ); + inf.setWritable( FALSE ); + inf.setReadable( TRUE ); + + // let others know about our new child + emit newChild( inf, operationInProgress() ); + } + +} + +void Nntp::parseArticle() +{ + if ( !commandSocket->canReadLine() ) + return; + + // read an article one line after the other + while ( commandSocket->canReadLine() ) { + QString s = commandSocket->readLine(); + + // if the line starts with a dot, we finished reading something + if ( s[ 0 ] == '.' ) { + readArticle = FALSE; + operationInProgress()->setState( StDone ); + emit finished( operationInProgress() ); + return; + } + + if ( s.right( 1 ) == "\n" ) + s.remove( s.length() - 1, 1 ); + + // emit the new data of the article which we read + emit data( QCString( s.ascii() ), operationInProgress() ); + } +} + +void Nntp::error( int code ) +{ + if ( code == QSocket::ErrHostNotFound || + code == QSocket::ErrConnectionRefused ) { + // this signal is called if connecting to the server failed + if ( operationInProgress() ) { + QString msg = tr( "Host not found or couldn't connect to: \n" + url()->host() ); + operationInProgress()->setState( StFailed ); + operationInProgress()->setProtocolDetail( msg ); + operationInProgress()->setErrorCode( (int)ErrHostNotFound ); + clearOperationQueue(); + emit finished( operationInProgress() ); + } + } +} diff --git a/examples/network/networkprotocol/nntp.h b/examples/network/networkprotocol/nntp.h new file mode 100644 index 0000000..82a8142 --- /dev/null +++ b/examples/network/networkprotocol/nntp.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef NNTP_H +#define NNTP_H + +#include <qsocket.h> +#include <qnetworkprotocol.h> + +class Nntp : public QNetworkProtocol +{ + Q_OBJECT + +public: + Nntp(); + virtual ~Nntp(); + virtual int supportedOperations() const; + +protected: + virtual void operationListChildren( QNetworkOperation *op ); + virtual void operationGet( QNetworkOperation *op ); + + QSocket *commandSocket; + bool connectionReady; + bool readGroups; + bool readArticle; + +private: + bool checkConnection( QNetworkOperation *op ); + void close(); + void parseGroups(); + void parseArticle(); + +protected slots: + void hostFound(); + void connected(); + void closed(); + void readyRead(); + void error( int ); + +}; + +#endif diff --git a/examples/network/networkprotocol/view.cpp b/examples/network/networkprotocol/view.cpp new file mode 100644 index 0000000..fed5bdf --- /dev/null +++ b/examples/network/networkprotocol/view.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#include "view.h" + +#include <qlabel.h> +#include <qpushbutton.h> +#include <qmultilineedit.h> +#include <qfiledialog.h> + +View::View() + : QVBox() +{ + // setup the GUI + setSpacing( 5 ); + setMargin( 5 ); + + QLabel *l = new QLabel( this ); + l->setAlignment( Qt::WordBreak ), + l->setText( tr( "The button below opens the QFileDialog and you " + "can choose a file then which is downloaded and " + "opened below then. You can use for that the <b>local " + "filesystem</b> using the file protocol, you can download " + "files from an <b>FTP</b> server using the ftp protocol and " + "you can download and open <b>USENET</b> articles using the " + "demo implementation of the nntp protocol of this " + "example (<i>This implementation of the nntp protocol is a very " + "basic and incomplete one, so you need to connect to a news server " + "which allows reading without authentification</i>)\n" + "To open a file from the local filesystem, enter in the " + "path combobox of the file dialog a url starting with file " + "(like <u>file:/usr/bin</u>), to download something from an FTP " + "server, use something like <u>ftp://ftp.trolltech.com</u> as url, and " + "for downloading a news article start with an url like " + "<u>nntp://news.tu-graz.ac.at</u> " ) ); + QPushButton *b = new QPushButton( tr( "Open a file..." ), this ); + connect( b, SIGNAL( clicked() ), + this, SLOT( downloadFile() ) ); + + fileView = new QMultiLineEdit( this ); + fileView->setReadOnly( TRUE ); + + // if new data comes in, display it + connect( &op, SIGNAL( data( const QByteArray &, QNetworkOperation * ) ), + this, SLOT( newData( const QByteArray & ) ) ); +} + +void View::downloadFile() +{ + // QString file = QFileDialog::getOpenFileName(); + // under Windows you must not use the native file dialog + QString file = getOpenFileName(); + if ( !file.isEmpty() ) { + // clear the view + fileView->clear(); + + // download the data + op = file; + op.get(); + } +} + +QString View::getOpenFileName() +{ + static QString workingDirectory = QDir::currentDirPath(); + + QFileDialog *dlg = new QFileDialog( workingDirectory, + QString::null, 0, 0, TRUE ); + dlg->setCaption( QFileDialog::tr( "Open" ) ); + dlg->setMode( QFileDialog::ExistingFile ); + QString result; + if ( dlg->exec() == QDialog::Accepted ) { + result = dlg->selectedFile(); + workingDirectory = dlg->url(); + } + delete dlg; + return result; +} + +void View::newData( const QByteArray &ba ) +{ + // append new data + fileView->append( ba ); +} diff --git a/examples/network/networkprotocol/view.h b/examples/network/networkprotocol/view.h new file mode 100644 index 0000000..9baba75 --- /dev/null +++ b/examples/network/networkprotocol/view.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef VIEW_H +#define VIEW_H + +#include <qvbox.h> +#include <qcstring.h> +#include <qurloperator.h> + +class QMultiLineEdit; + +class View : public QVBox +{ + Q_OBJECT + +public: + View(); + +private slots: + void downloadFile(); + void newData( const QByteArray &ba ); + +private: + QMultiLineEdit *fileView; + QUrlOperator op; + + QString getOpenFileName(); +}; + +#endif diff --git a/examples/network/remotecontrol/ipcserver.cpp b/examples/network/remotecontrol/ipcserver.cpp new file mode 100644 index 0000000..f83f8a2 --- /dev/null +++ b/examples/network/remotecontrol/ipcserver.cpp @@ -0,0 +1,84 @@ +#include "ipcserver.h" + +#include <qsocket.h> +#include <qvariant.h> +#include <qimage.h> +#include <qpalette.h> +#include <qapplication.h> + +class IpcSocket : public QSocket +{ + Q_OBJECT + +public: + IpcSocket( QObject *parent) : QSocket( parent ) + { + packetSize = 0; + connect( this, SIGNAL(readyRead()), SLOT(read()) ); + } + +signals: + void receivedText( const QString& ); + void receivedPixmap( const QPixmap& ); + +private slots: + void read() + { + Q_ULONG bytesAvail = bytesAvailable(); + for ( ;; ) { + if ( packetSize == 0 ) { + QDataStream ds( this ); + if ( bytesAvail < 4 ) + return; + ds >> packetSize; + bytesAvail -= 4; + } else { + if ( bytesAvail < packetSize ) + return; + // read the packet in a byte array to be sure that you don't + // read too much or too less + QByteArray ba( packetSize ); + readBlock( ba.data(), packetSize ); + bytesAvail -= packetSize; + packetSize = 0; + + QVariant variant; + QDataStream ds( ba, IO_ReadOnly ); + ds >> variant; + switch ( variant.type() ) { + case QVariant::String: + emit receivedText( variant.toString() ); + break; + case QVariant::Image: + emit receivedPixmap( QPixmap(variant.toImage()) ); + break; + case QVariant::Palette: + QApplication::setPalette( variant.toPalette(), TRUE ); + break; + default: + break; + } + } + } + } + +private: + Q_UINT32 packetSize; +}; + +IpcServer::IpcServer( Q_UINT16 port, QObject *parent ) : + QServerSocket( 0x7f000001, port, 1, parent ) +{ +} + +void IpcServer::newConnection( int socket ) +{ + IpcSocket *s = new IpcSocket( this ); + s->setSocket( socket ); + connect( s, SIGNAL(receivedText(const QString&)), + SIGNAL(receivedText(const QString&)) ); + connect( s, SIGNAL(receivedPixmap(const QPixmap&)), + SIGNAL(receivedPixmap(const QPixmap&)) ); +} + +#include "ipcserver.moc" diff --git a/examples/network/remotecontrol/ipcserver.h b/examples/network/remotecontrol/ipcserver.h new file mode 100644 index 0000000..2dc2515 --- /dev/null +++ b/examples/network/remotecontrol/ipcserver.h @@ -0,0 +1,20 @@ +#ifndef IPCSERVER_H +#define IPCSERVER_H + +#include <qserversocket.h> + +class IpcServer : public QServerSocket +{ + Q_OBJECT + +public: + IpcServer( Q_UINT16 port, QObject *parent ); + + void newConnection( int socket ); + +signals: + void receivedText( const QString& ); + void receivedPixmap( const QPixmap& ); +}; + +#endif // IPCSERVER_H diff --git a/examples/network/remotecontrol/main.cpp b/examples/network/remotecontrol/main.cpp new file mode 100644 index 0000000..563fe8c --- /dev/null +++ b/examples/network/remotecontrol/main.cpp @@ -0,0 +1,11 @@ +#include <qapplication.h> + +#include "startup.h" + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + StartUp s; + QObject::connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) ); + return a.exec(); +} diff --git a/examples/network/remotecontrol/maindialog.ui b/examples/network/remotecontrol/maindialog.ui new file mode 100644 index 0000000..bd1c619 --- /dev/null +++ b/examples/network/remotecontrol/maindialog.ui @@ -0,0 +1,165 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>MainDialog</class> +<widget class="QDialog"> + <property name="name"> + <cstring>MainDialog</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>273</width> + <height>263</height> + </rect> + </property> + <property name="caption"> + <string>Image viewer</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>MShape</enum> + </property> + <property name="text"> + <string>If you start another instance of this application, you can use that instance to control this one.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignCenter</set> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>image</cstring> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>description</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>26</pointsize> + </font> + </property> + <property name="text"> + <string>The Qt logo</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>PushButton1</cstring> + </property> + <property name="text"> + <string>Quit</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<images> + <image name="image0"> + <data format="XPM.GZ" length="55092">789cccbd47b7eb38b2ef397f9fa2d6e5ecae5e7cf266d0035266cb6da7ed77af1ed07b2f1aa9bf7c83883f704c56d5cd7c75bb5f95d6c9ca5f8a22114044202200f0fccffffcdbe7d3fddffef37ffe8fea625c02eb6f966f947ffb4fbb4e92ebfff57fff9fffcffff88fe160f0b7fecf68389bfe6df41fffc7fff80febf96fd6df9401ff5fcff64232ffde36244f7a762ac953fefd56f29cf35cf2a267f3072ff9f733c906e7836493f35eb2c59fd74ab6797b1f243bfcfb46b2cb7fef0a1e72799c5af2907f7f94cce5732cc963fefd483297cf3a8387d43f992999ffde0c25f3dfb79a64de5f662099dfcfe924cf386f24f3fe4a44ff0fd17e5b327f9e772799dfcfb8078fa87d662499cbebbb9249de6fc9bcbd9d2f99dab795ccc7b31a48e6e3b71a4ae6e315ed25f3f1b04f82a93f0dc1636a9ff32999daf32599f757a54ae6fde17e48e6fae23892b97e78a2bd63ea1f438cef849e97e892f9f7652b99b76f3d91cce53763f0947e6f08fd9c1137b664de5e43c83fc7f53bf0022cf475499c8be719c4f113d824de5cc01671fd0eb6895dbaff50d8ab174ae6f2ad3ac9a47f63c924df4832d73f732a99f777f324998fb73993ccfb5f7f96ccc7df3a49e6e3514592b9bd3aa664ae1fd6bd646eaf9a2b98f4ddf52573fdb58e60d89f6d49e6df9b13c97c3c1d45326fbf634826ffb2934cfee40086fdf88164de7ff65232d9eb9b646e1fa607867e9bbe641a8f816092cf14f2423f4d070c7d8b9f25f3e7bbaf60e89fb791ccfb3b15df43ffe22f30f42f13f240ffea1b18fab716bf877ee967b043dc92bf1f0ea16fdd9af3a8f740bc3dbe64f2f70bc9bcbfcd2fc9bcbfbd4432f90b4730f5cff55e3297bfc9c0187f732899f4772099c6e35b321f7f6b0fc6f8daa6649aff7692f9f3724d32d7f7cb4932ef6ffb4e32e9b302c6f86723c9dcdefc50326f9ff32e99f7975382a10fd75c32d7e795906742fdd3bd49e6df67a2bdd09ff20886beb89164fefcd54932ef9ffa2c98fca72bda0b7d6a9f25f3feb0c4f842bf9a07b0d02fd13fd0afe2090cffd6c660e85b24ae87be05a40f63e1df8277c9bc7debab643e5e575732efef6e2799cb1bfcf89ecbdbed2573fd5cfd781e1fcf952d98fa3b91cf277f74cd24f3fe0f3ec08807ba8d64ae9fc54232e98f27993faff9f13df787ae680ff4d57995cce57103c95c7fac3bc97c7c7c5f308d67b8028bf9f72299cbd3dc49e6f2981f92797fd69664de5f7e2499dbb33b92ccdb6f2dc1d067772099fca72e99b7ff6a49a6f946f437f4b9bd48267934b0d0ef77c9bc7dad68df9cee9fee25f3fe69447fcc31df88f1843eaf6e92f9f3da89649a0fee25f3f1cb85be41ff0331bed07f5f8ca7f0a7d43f13a1df9d2a99f777f32899dfffea80111faeae92b93cee41f090f7effa0c463ce77f48a6f9ee51321fafeb4930f547bb01433fecb164fe7cff5b321f2f2f92ccfb3f93d793bd745b30f421ae2593bfd94be6fd1b2692b93cde4e32b79fda164ced8ddec1d017e74332f9dbb564de5e772899e299140c7dcaa692b93db8df92797fe61d18fe3117e303fd59b792e979b164de3f8918ff05f5cf46f40ff4c715e301fdb9bd80e13f57f792797fad4a30fce906bf17f3f51ae331c4fc19a13d43f89780e693a9f0a7ed028cebfd4e321fafe6198c7cc00b2473f9bc77c95cdf4a4ff0908fc75a5c8f78bfb52553fcf02999ebbfdf08a6f10e5ec1c2bfdc49a6f97c2b99e6ff27c9642f3fbee7fab416f2407fc299643efe912f99ec692099cbd3b492b9bc9ab8ff94c63711fd0bfdb2a792c93e2ac9140f689229df5125f3feed5660e85ffd2a99fce34432efdfee00867e5e14c9941f8ec0d0bfcc904cf9572a99b7a76e25f3fe73c760a1afaa646ebf99e85fe86f2ec61bfaea0b79e11f0ba12f2eda8bfb4b7d267b98897c64dd81a1cfeb503297afbd934cf1d45e30fc93f81ef363b896ccefbf3a4a26793dc1149fae7d30f247ff2a99ea1d3fbee7fed68b05a31eb1924cf9c2083ca5f6076bc9542fd948e6fa900e2473fdbbbc09463d630246be1a6692f9f84481643edef95532b73f5df01cfd7993ccdb7be92453beb5924cf3c14432efbf5cfc7e01fffa2999e6bfb1646e5f9b022ce2cba364de3e37114cfec132c0d047f74d32f9e37b30f26b2f05431f5763c9bc7f42c1d04797e6cf99d0c71be41fc21fa52a18fe24207d9f0bfd5a1d04d3f8570fe009d573dca3642e7fbb054f697ca237c1246f3a944cf53a5b32c5a72f60f81bd796ccaf4f2bc994af2d2593be6f2453bea682e7345eed4e32d5ff14c9bcbf2f57c95cff7dd15ef8a35097ccdb93cd25537e2bfa6b3124fffd055ec2bfcc24537c548045fe518331decda7647ebd2eda6ba37fc478c01f6d44ffb8d43e1ded17e3af3960cc97f5022cf481e257a6dd149fd96bf08cc6ffe64aa6f92604c35eec4032b7efab2799e2375b30e94b2aae5f50ff58aa642e6fe148a67ce1065e927fb80d2553bd2494cc9fdf3e49a6fc45c863c07e1dc97cfcd3a3606a5f7d05633e887682497faf4bb08df8e2030cfb2bc9de17a2ff4bf49f886762f4cf10f666bd80618f3e8dff92f5177f5e914be6ed2f36e005c91b5f24533cf22598ecafeec04be467ae64f28757b041bfaf5f040f29fe68c0268d9726da63917e14a2bdd0c7f424998f5f44fdb314fd91bf83a18f8105467fdc4660e86783e789fee94a30e215e3118c78a3a0f9dc60fd45fa34072f491fc329d8a0f685a964aae7dfc0265d5f2f05537f568f60d86bb091ccfbe36248e6fad988dfbba85f8e24533e48f66f88fe892b30fa67634ba6f97508467f6d1cb0a827435ed11f35fa6388f9c9a7f8c814f157fd065e92fe5f6e6083e42f5cb0897ac417d8227dca3fc036f55fb305431fba77c1747f3b02bba4df3ad9ab29e42f669229df46fb647fe460d8d3aa0243fecd2758d4bbefc0a21eb403a33f2a8a772c263f6fff35055ba827d1f85aa2bf2f1a58f4f70a8cfb252a18febdf1c0f02701f58f2dda7f5d48a6fe9b8071fff21d2cfc83603caf51c1b87fb106433f4b1a1f87fd17eebf1b17ec903efbd43e46b0f71a0c79cb2d58c4e72d18cf2f1bb078be0dc6f3339abf5cd11f8906c6f5de1a8cf846e3f6c1cbe724df068cf6548964ae3f1b130c7f9096608c7f7603a3bde90e8ce7bb2f60b4d77e00c39f075c9f98f639e47fa34832d7e7f84c2c9eb7ba80216ffb08c6f3cc042cd64f1a30e42f9760d88fe382d19e1ced19c11e42aebf0cf17cd306435ecb04a33d610e16f20b16f1470a16ed3981457cf9443c82bdae2660e19f79bc341c8be7d70e18cfd71ec022df8ac178feaa05e379d72bf108e3df70ff369cc8fe1d8345bc9c81d1fe60452cfaeb7a03c3be74ba7e2adbd382f1fbcb3b18e3910dc1689f4df24e657fd4603cafb2c168bffb0cc6f32b1ebf0c67e2fe01e9d34cdcef1283e12faa6f30ee67f860e1ffe660a1efdc7f0ee7e2fed58c58b6e70cc6ef8d002cd6173db0a83f2dc0d0b78edbf77021e5bd078bf6aec1f87d41fabd14f2155bb0f8fd108cdfeb2330da93de83f1fc8ac6d310f2ac1a309ed772ff3734c5fdb48d64ee0fb72e58d4e75ec0f87d7202a33f0d713df4a5237bb7e4782a60b11ee681d15eed112cee1782a1cf158f4f86b6b8bf6581c5fa2af5a733823f762d30eed7d2efdd11f2efcd1d18d76bdc5e468311f2a3c007e379f5082cea75f760e8fbfa8d782cf49dfba3d150fcbebc030b7bdc83a17f099faf472371ffe64c2ceed73460e883cefb6b3416f7ab4fc463e10f2c30e46b8927e279b94b2cee6f6cc1d087ec092cec4930c6f342f24f85fc46442caef75660e8cf85fb93d18cfd9ec783f1162ceaa37cfe19cdc5fd3ceabfb990d79d80d13eeb152cd64fe9fa85f8de7c05a33df9188cf66cbec0d0c7f50d0c7df3b8ff1f2d85bcce002ce227078ceb1d0f2cfca54e3c41fb6f74bd21ef5780f17c5d058bf53beeaf46e618ebd77a2299c77f5bba9f25c6b7f4c1c2bfbd80d19eeb07f104fda1933cb678de2603e37eba01c6ef6f57b0a8ff71ff3d72c6a8b7e8d47faee88fb507c6effd188cdf3bdc1ec67d38c739e2e3c5a63393e21723269e60fde4f22c99d677b93e307577285eb7b8fd8f27ecfe9cf50e0c7d8a79ff8ca7ec7bfefb6b412cc627db81c57e823730ead9eb1158d43fb8bce399b8fe7a0716eb5d7330c663c3fb733c67eda1fa484d2cc6c39a83a11fed193ca57a651282b13e16f3f96ebc9860bdc8d880c5fe810c2cf21bae8fe3e544d4cb23c93c5ed30a30ea071b170cff92d0f81813d443d71730eaed9a07c6f53eb7b7b1c9bee7fa117c49a6f5024b30c5b3760cb6b19ec9e3adb13331e8face93ccfbaf3883f13c23013ba8a72e05d3fd0d1b8c7c734df23b53d8df86facf9da03e7e1b80f1fc82f7e76430813e6d63e229fa3be5e333194e4cd40b52b04ded31b8fe4e4653110ff2fa0a73a736f28d9a788afd67fa180cfd6a1db0588fe3fe7f32610accfb77c3f59721f2c77c493cc5fe0aaf00e37eb731788afa4e059ed1f8361f60d48f6dde1f93d914ebc7460a46bd2fe4fd37994fc7940f6cbec1e89f98cf5f93a578def5013c433d86da6f4c51bf09b9bd4dcce982f2b39cebcbc49a22fff6b83e4dec29ea15d95932adb704c433acffebf43c776ad1fd039d7886f5d315bf3f5356f09acf77d3d1d4a5fa65734f3cc3fe035d134cf7372c30ec355b824dc413dc3fb0e9906574dcdf79c4e2f74501c6fa5b74104cdfb73c3e9d4ea6d6887f7fdb10b3e791bef1f9940ddf1ceb078fe005f5b7c5e7e3e96c86f58f8d0246fd3a76c1f311f9b377b043e31bf07c693a9fa1de19df816dec07f804bb584fa3fe5accc47a86069e517b82002cd6f775b043f39bc6e3dde9923d8eeb7332035bd47f3705ec60ff824d3cc7fecf84ebefd498c19e2faf609bfa2be6f9f0d49c2d51cff5c016e5fb2b176c8f78fb8b15d8a5fa99c9e7d7a93543fdab1b831dfafde6287844f5d10e8cf1af49bfac39c63bff024f499e98ee6fb3fb937f7d023bd4fed407bb435aafb124d37e2d5d30b53f03cf87d43eeb0d8cf5dd7803c67cb7e1f63d75587f707df1e9fece7c30a2fd5d7b30f6b3469f9269fdc792cce5594dc1587fba52ffb84c1edebecd5e326f7f41faebceb19f2cda8271bfa6133ca2e727e0118d4fb2164cfa9c2892f978af4c30eca1f1c19321d7bf2bff7e3660e34ff5e53bb043cfaf5dc174bf6c0076a97fdb929805acfcfb6d0446bdbee4ed9f0d857eddd660f89b6e2298da5f05c4ac3f687e6bc0c311ad87e07a263f9f0fda77f094da537f8067245fc5f569369a1963d29f2b31eb5fdabfe20ba6e7a777e0d188ff3eb9815906c29fa78227d04f053ca3f6b65c3f6663260fd553a93d2cfca4fec91bc95cfeea041e517f5fb8fece2662fcdd023c86ff33c1d31167cd06cf21cf5c308d679d8017d41f0df72fb3e9cc1ef3ebab14ec8c69bfc05432ed47d38885fe5d4cc9b4fe37028fc8fecb37c9649f25784cf6ebbf82e7f47dbc042fe87e05f5cf4ce8936b82e7f4fb36113ca6f17a002fe8f986b8de80fe8bef4dd28fc292cce52fa68269fc4d1effb0db2f49decd0c0c7fdc92fe2ce6131a3f6d2d98fcb7bf02cf49bf370330d68fea5a30fdde38831dfabea3fe59b2fea178b0009bd41e4f03db23aeff37713decb5f00593fc2df5a7c1ee47eb6f2330eadd17156c8ea87ff7609bfc45590b267f78d5c02e714bfa6ab2f1207ff80136c87ef22918f34dfe06c67cd186c48b018dafcee3cd99c5ee47faa48217647f99609bf4c7a0f1b2160392afa3f6da6cfc68bcd69279ff949f6093daef4ec1f067f5817831a4e76de8f70ebb9ece177c806d6abfb3235ec0dfba2bc9b4df9bc6d7ed1376ae5f5c3fe703263f6f4f520a1e53fc99102f66d4be8cf7cf7cc8e4e3d7673e784c7cdd82a7d4de5a03c3fe751e9f3265c2fca09be009d94fc2e397394b87c99f186bf08cfa4fe3fd3b9fb0e7717dacc792e97c0b8f5f58f8bb20d6793d613e5f2cc7b49fc5005bd49e35b56fb1c4fcecf1fe9e2fd9f7743dfdde582ca83f8267b049ed59f3f9686eb2eb69fd782699d62fa83de67244f67de1fa3eb79643b237ef2698f4ab9e8347d4feb0008fc99e1c6a8fbd70d05f4762261e6f9f46e3e730a6f3160618fdbbe6f9c6dc5d0ea8bd0df5bf6b88f89aebdfa25f00a5f8ff4e32edd749c0636aff66019ee17939780e7de2f92a73f643e8bb031e537b7c133c237bf5dec073ba7f44f71f1a225ea3e78f447f79dfe011ddcfe3f22dc68c29feff06e379ba0e9ed2ef0b1effb1747d44f3b1fd0d5ed2f78d0236c65cdf367c7e5fb0009858bf07cfd15f7bf082f4a3492473f96f19d8257b6df8f82e66cb05f55fbe223630ff6f797cbc98b3f67079527afe82319d17e1f6b7581a03fafdc693ccc7d7a0fb194b97da7bbb111b63f2af1bae9f0bd31810eb7cbd61612d1dd2bf94da671943c8cfe7af856d8c69be6e0563be594f2573fdd7b8bd2c1c763ded0f53c01392c7bb4ae6fdd3717d5db8c684fa4bbb134cdfa767f094e28180fba3e5c080fff15fc0b309f5ef9764d24ff13dfc43cae7a3e5d0807d6cf8782d47acbdfcfeed0b783ea1fad50ebca0ef6f3ed82079a32f30f2c5644f6c627f5acce7df655fd0e1e3930460a662dc3f1cc14beaaf40133ca178a8069bf47cdf90ccc74ff3c10e8de78dfa676c8afd09b564daff5982b1ff36a6f64c58ffd2fed40b7836a1fac504bc9c50fd6101c6fc97abc426e2b3700fc67911f3018cf33ac99d643e1edd198cf3176e07463da4fa0063bf8826be47bd53e7fe773965e345fded806db217eb1d8c78f29612b3f6923ea9603cbffb0663bf4949d7b38083c663b3022f68fceb7bb043f6118682a9ff8c02ec92bfcbe9fe7dba49f5c30d18fb636c9e2f2ce7ecf7144f2a6097e4bbe5c4ecf7140fcdc0b0b734018bfde03a784af3c53504cfb15f47dc0ffb0bbb8160ecbffc002fa95e58edc1d8bf1a4dc1163d2f36c1a8575495609c5f1a4aa6f3a134febd83e2ed2f376093faafa3e72fcc01f1f50b3c217ddd5660ac37ad63f00cfe5407cf69beef5230ea35c6026c60bfe55532e55b39d8a4eb6f73b0d8df20ee6f637fe04a32cdff03b043fd757b904cf9d9168c7ae7ed2299ce7b3692697fc898d8c2f99ddb128cf3b317f49f85fa6348f75f1a06d9afb604c37ff817b04dfa1c0ec10ee9bb4ef22ecd015d7fdd8047345eb5079e907e598e60d2d7b40363ffe3762598e2d5750e16e703d692297ffc128cf35768af897afb36108cfaee5c32d56b0f9269fe2e24f3f61731d880ff5125d37ed80558ac974c25d37ec09d64de7fe64132f73fea27d8a2f15732c9b4bea008a6f655a23fb1dfb28c24d3fe470d8c7ae1ed5132e95b2099cf8fa6e82fd4e3c32fc9b4bee3125b38ef7723ff641816d99b7d03dbe48fc22bd8a1f9f95a825d9adf4af28706eb1ede5eaf060fe97aaf133ca1fc5605237e6d8ee009e5bbd5198cf58b4efc1efb6fb71730f67bae66e025c5df8a09c6fecae020998fafd248a6f3ba909fd93b6f4f71025bb0af52329ddf9849e6cf533fc036e993924ba6f96726993f2fcec00ef57ff12e99e2c34a32ed7f0cc1f017d5bb649aef7cc9bc3f22f13c97fa4fc3f859388f5a6d25f3f69443c1d86f3b06e37c4be54ae6edd354c9bc7dd791643aefdc4aa67acf2b18e7f134f13dced325a4ef26d3379affc95f9826e2cb952a99c6f7153ca57835fd0463ff691108267fb579028b78c2001b34dee14532bf7f62814df88b4832d52f2ab085f59d0118eba3752899ecff2699eae307c9b43fb405431f9454329d2fbb97ccdb938cc02ee95b2adaebd2f8b590dfc279e448974ce3f12998e67b4d30ce074713c934ff403e0be72d2dba9fc5e607defeca014fc85f5413f094e2313b954cfea0114cfe467901233fd41792a99e6a49a6f527c198afd71bc9549fb9822dea8fba944cf9502799dad782b17e6d7592c9be6782495fea42321fdff8c7f77cbcb477b043fa1deb9229ff4904e37cc697641a7f0decd27828aa647abfc45032ade7b982495f13b25fcbc2fb0c944032d9cb93606abf5949a6f314cf60bc4fc00a24f3f6b79960d29f5a914cf11dfa53d87bf82999d6df303e16ceb755a4cf369b3fc8ff7660ec3f57de04933ff0f760ac5f665bc114df2b67c1a44f57156c507f5f1cc9b49ff3198cfdc69bab60aac7ac4f601be7fd22c1781fc95132ddaf95ccdbabd492b93e794f60e883229e87fd9ef959326fbf3a964cf9e00318fe5d9d4be6fda192fed916ce83a92f92a93dbe64ca8fd11f229e4c6f92f9f3938164ca1f4f82b1fe994ae6df871f60acf79a6bc9bc7f62f49785f3b1b75230d95775914cf30bfacfc279b22df90f47c413a621995f9f7be011f99b6d2b98e2136d021e4f683d63059e4cb83e7ae2fa19dd4f3b83e734df943118f581f22099cba33c8397549f592dc026d69b8f9279fb6f9f60e453d6156c537f5a63c9644f8160aa47695330fc8dee0aa6e7c5b964f28791648a6fe692297e580aa6fa6628da8bf8c3bc974cf9642118fa38954cf58f89649acf1692e93c27e9936361ffbdd149e6df2b9e609c1f7d96ccf549ad24d37974e887d0676721998f8fba924cf1fa9364b247713df687754f92e9fc672399ea45a23d581f35af92f9f7b9f81eef13885ac1181f4b32d5dfa10f16f6a3044bc974dee9158cfd3f892399fcbb2d993fff26fa6786fd3ba23f711ef146d7bbcc7e283f7b944ce7995ec0639aafab25784af671db0a9e52feb200cfc9fe320f2cec650f5e52beae7d49a6f319e2fe4bd457766083f453bf80b11fa878154cf6b2adc1585f37c4fde0af235530f251717fc45b9e90d7c17ca848a67add5530ad1f74a160aa8f76e279c8e7c34630f97f3b174cf2379a645abf247fe35a781f90ff2818f3c1443297571d49a67cfd4930cd87da1b58e8ff5630ece74b32cd1fa564aaef7982697dc87900633f73694ae6faa0be4aa6fd789a648aa724637fd25a32ed3f79924ceb3d3330f6f325ad64de1fd98364927f2598facbd42553fd3594ccafcf311e16cecbdf22c95c5e652a99de7f267e8ff3c9e1bb641a6f4732bf3efb008bfd812f92297eb32473794b31bec87fa30a2ccea3ee0493ff30f79269bfe23718f53e6b2099ea0377609c7f34be2453fc7b914cf91c1f2f63c0e6478a0fb6e019d58be207c1538aff8fe039e503712b99e6eb5c3297a750c04b1afff22618fb1922c1347fc73bc9b47f4630f2f9eda360b2efab2e98fcd1ea158cfd83452598f47f6b48e6cf8feec1a8b7a40bc1f05f3bc9bcffb61918f596f85d32d5e3447fbaa48ff94030f6ffdc24737bf267c416f6a7ac3f2453fe580bc67e99122cceeb9c24533d6b2299f4ad108cfce30773fd500cc9341f7792a9fe6b0ba6f5da6b2399ce372ec1d85f1d9d25f3efd59b60f89f3bc9bc3fd4a364f22f6f60ec97543792797fa8a960d8c74d32f9932918f61ed49269ff832d99e44d24f3f61a23c1d43f96fc1ef1fe1c8cf763448e645a3f16cfc3fe3d632899e2955832cdafd01f0be7d5ad4a32ad9f3792b9fcc64030ced3d792b93e9723c9b41feb02c6fe67732b99f68b7c4aa6faa3180f9c17f6499ffa032dc46bf098ec5d8f24d3fe08133ca1f530ed049e527eb7f50593ffb83c48a6fd570a7846f7d36b30e6fff25b32d5ab73c1145f28ef82299ed1e660d473b64f92a9def92698fcdd5a3cdf46fd24914cfbcd74c9b43e21e445fda611f7437cb1fe124cf65edcc02edd3f7225d3fb001e05d3fc5ebd108b7879bb93ccfbc72f05937e66a9605abf538e92493f56e021d99f6249a6fa5b2b98fc9f72914cedc3785a789f52f12698e42943c964ef17c9e40fe792697e1849e6f2648e60ac470660bc9f243b4aa6fd3fa1647ebda20826f9d24c32ef0ff55e32d5d7447f60fe2f9f25933f5a4aa6f8e62c18f9502299f40ffa61e1fd4b4a2899f6279492495ed19e29e297028cf795048d64dedef05530e29137c9e47fe4efb19f740cc6fed152134ced0f55c9fcfb488c17dedf74b9934cf64dfa3c326754ffab0cc1643f510ac67a78ba04633e77466083ecb309c026cdffa921998f476582b19fa7ba0aa6f93e5e0ba6fca6188051bf4b447bb1bf4aff00237ff60f92e9fd41f792e9bc7d2599e6f717c1d8cf247e8f7a5d160ba6fecf23c1b4bf21a0f86264e13c72f82499f2fd57c1f06727c954bfcec098df755b30d9ef7a2e98ecdf78974cf54cb447d47b3d5330e2cb0fc9b4df47dc0ffbd12ea9647a9fec1e2cde07f92e99f60b1c05239e3e4ba6f9df974cf9d74932ed9ffff17b2e5f70014f90ef288261df8664b25f5332bd6f0ae36be1fc4ef92499f22d4b3297ff593c0fefb7525dc9e4df3c30de5fa53a9229feb12553bc2ec66f46f2565f92c97f37609ca7b4df25d37e825a32adff89fba39eaafb92293fea24f3ebb7627cb13ebbfd944cfbadbf2453fe37154cf287a664d257d15e715e80fa6fcce673f2af117846f94076075e503d2188c1d88f9a9b82c91f2457c954cf38832df2279ba9647adfe70a8cfaecf62098e2ef6e2699ce27ed04533cb2d5c0d8efaa3c09a6fa46f12199ec5d3c0feba51dcd9f63517f5dbd0886bd3f4be6d76f7f30d59b62c164afde5432d5271f2453bdf0118c7abd77124ced4f1ac914afae2553bd5edc0ff3b9f32498ec6d1308a6f66c55c9341fdd83313f9ba23da8ef46a564dabf664aa6fd21f27b8a37e291643abfe249a6f9ce174cfb13a2028cf78b25b9647a1fd4a364f20fdf92c97f0c25d37a512599d607a03f22bf375ac994ffa49229ff78944cf94a2618fe48fe1ef59e4632bd3f270163becf2ac994bf8beb71fe4b0d2453bd27964cf552d13fa807da2f9269ff612499e627d13ed40f424330b55f13d763bfc6ed4332c50f37c1f0f78a64daff3d964cfb43c578637daf7a138cfd39429f719e6745fa30314df21f590ec6fedd32134cfbb10c5d30f98bcb8b64da2f3c01dbd87fbc174cf5c92001e37c4d25eee760fff1b7605a0fc8b792e9fc81b8bf43f149740263ff68fe2a989eb722fb9c8878201849a6f75f9e25d3f37cc974fee15532bdaf752898ecdd0904d37a475080512faf7e30e9432e18e7f56e82914f5482c91e0bf13ce4ff7e2698c627fb924cebcfe2f7d8ff1b3e80719eaa934cfaee3992491e717ff80f6f2699f22ff13dcecfa753c9e4bf0e92293f394aa6fd139660b2cfc8904cf9df4230e68b4a32add78bf1c0feda740ec6fbebcab3648a8f2792c95fd492299e1848a67ccd164cf25b2d58f887ab64f2379d64de3fea4132bd6f4efc7e8e7827944cfb7f1e2553bd4f134cfa6c799269ffb6781eea8df14432b57f2718ebe591645a2f8b05231f15bfc77e0f652699e229f13cbc7faa9a4ba6f361427fb03f5013fa8cf5e2c495ccbfd73dc924af180fec0fd33e24737922f17c9c6f6cc91ea626f69b5e13f082fccfe64d32d547bfc0385f565ec0886fba1cecd07a663394ccfb237b05633d627b144cf6779949a6f75fd682c93f06349f4dc5fa433d154cfaace982697d721b4aa6f3c11918f585742299fce5bd64aa67958229be5ac560ac27ac12c178bfd79b64da0ff1e37b7adf95fc3dcef70592a95ebb174cfe3a10cf47fda01c08c67e1b5f32bdcfb0134cfde12a92493ed17f585fc81f25d378dc49a6f8e253308d57249e3f417c7a904cfbcd7e30c5d382b11e982592797f24df82b1de954ba67aa6907f4af69baec138af6c8d24533d692f99ea733bc1e4ff3c0b2cde37be944cfe712499f2b1b964b2675732c52bd0670bfb45d54832e58fba64da0fb301e3fd8d4a2499d67f857ee0fc72f82d99be5f81713e3e194aa6fd49e27e588fb89ec1884fae13c9d4ffa27fb0bff0229e67537fae481f66e612fb2d776083ec3debc026eaa53618f18a3b0323fe580f04d3f5c98b64da7f63825df217852e99ce77907c33113f6c4dc134df6f1f24d37c9982b17ee04f05537b9a77c9148f88dfa3deb07a92ccfb7f73168cf365778229be48bf25537d19f28bf501e55e309d4fc94f92c97fcec158cf2b12c1e41fbb4fc9b43e3d104cf3f7f6158cf87ebb170cffe448267942c1e47fba05187f1f455049a6f5614330f4ff0d8cf3b88ab83fde3770d94ba6fafc12bca0f6a557c1c80f6dc9644fb964aa7f1492a99eb1934cf3f718bc44fd632199f2ab67c914bfd782518f16e389f78346a23dd8bfa58c25d379ba4030c95b5f2453fc5c824deacfec2299f6eb6c25d37ae74630e2b94632ad170bf9b13fb87b964cf514713df61bd4aa643a9f0f7bb31cecbf10fa28f6e3f89269bd83e2adb9399f52bd33032fa6fcfef9086cd07e8d95f81eeb1b97a964b2bf0dd8a2f958ff02dbb41fbbbb825d9a3f2eb5608a3fe28560caafb6141fcd45fdb11a83b11e180f05d37e5c5f114cfe669d8051ef6f3dc1a40fab108cf3d9e55a30ce3b1a60d4d78bab649a5fe5f7645f978364dededbb360f247452899ea616730d6e7d6779269fdfb4330f9bbd4148cf8e7058cf5b8e44932bdffe25e30f6835c24d3f9d1b964b27f713decdd16ed9b937fd2f792a99ed2082679a247b0380f33964cf3ab18af05c52f57d1df381f5baf2453bcff0046fcacfe60daaf6a4aa6f9ed4b30ce7fce25d3fedd1918f65f8af1c37e6d4f954cf9cf5a32c927fa17fbede254326faf22aec77c1bd582a9fd971d58ecef3524d3fe5ea1bfd84f97d1f82ec47a83be124cf5c8ca07dba4efbab8de9df2dffba44f0b51afeb5cc1747d32154cf61eecc043ccf74f9269ffd3098cf7495c1e25d3f9c14a30ad9f281f92693de2001ee3bcf34430e94f71114cfe259883f17ef6b52599de2f36144cfaa23c4aa6fafd188cfa79d60a267de832c1345f1786649a3f7e30cd97a2bdc86ff55232c54fb9648a6fc4f367e44fbaa1649a5f669269ffb7f87e4ef6915f2453beaf4ba6f9443c1feb655a2c99e47b944cfb532493ff6bef25d3f86ec1e2bc532318ef1712fa82f3cdc55e32b5ff1b0c7b5acd25d3df9fa04ba67ac25e32bd0f44b4cfc0f95ad13f787f759c08c6fb8f34c9141f6e2453fee80b46fe22fac7c2fbafef24933f17fa8efdf9590dc6fb8d4a8a079626ceb3d513b04bfa5a527cb664f645eb65067848f359fd8369bfec063cc27ae45532d5e31f25f3f15c3d83315f5d12c9544f740553fe1e6592a93f3dc158bfb88127145ffb9560b2873a96cce5d75f25537ce90ba6fa62f283a95e2ae49bd2f82b0f82c93ef385645a6f17ed453d39984aa6fac04c32fdfd5607c1a42ff14a30cdb7b74232e5a76f60e483fe5132cdafb960c4fb5f60c4aba52218ebaf7b30d69fb6e27b9c1f0ceec1d82f962b92c95f1d24537da7148cf93d05239f534f92e9bc8bb81fcefb1547c9544fabc1383fe68af1c67e92f807d3fe43213ff6af7662fcb19fdaa37cc410f5ebfc5e30cd1f6b07ecd2f85a347e06332faa8f1cc0d0efdb17784cf6e91a82499ff44ff004eb318f92693fdebd649adf5f05937efa67c1347fe91618e7bb6e7782112faa60ac8736a1601a8fac00a31ee98c05e37d155bc1347ff8aa647a1fc009bca0f1f21682c99f9ae2fe0b6acf360763fd62bb168cfcb0148cf7790879f1be9ad4954cf53cd17fd8bfe49f24533db1164cfdbd7d07e33cd346fc1efb17b789643a4f300423bf303760bcdfec42f669b27885ee47f38169e1bc977e124cf18d3706e3fd11fa163c22fdda9482a7b43e7805c3ff058660ba9f2b9e87f364d14632d5fb4792e9ef37fe02e33cd926028bf364efe039c9b712f747fdd9ad24d3f58231fee58b601affdb9b648a6f7230dedfe12d25d3fa760a467d3817f218145fd99f9269bda1924cefdf93df23fe7e009b349f98623c2c1a5ffb2298ee97d8609c3f32e6609c3f8b2cc9646f0bc914cf3f4aa6786607c6fe958ee20f8bcdafb43f81e20b165e93fe5c3e25d37e30f13dea3ddd093cc27ede2d18f5983804e3fd1d9a25989ed7168269ff61fa089e223f9e8167d47f862699d6f39e25d3fe68152ce2b70318e7c9ec67c9f4fe12138cf70f252918f1932b9e67507caf08f9f1be9938069b648fc15e308dbf32108cf7df88e7637f627c150cff7a0163ff42f000c6f99c88ec9b4d37a867d1fc62b3f1a3fd3c743f5b8c5ff102463cb2da82117fb4df9269bde12498ec510f25d3fef01c8cfcb8b425d37eaa5232bd5f2506235ed85a9269bda4154ce31d89f6cef17ea8037841f76b447b17d84fb6012fa9fd6b211fea49978b608a4f0c156c423f3bc9b49f3602c31e6f7bc1d0bf4a32c50fe44f6c1bfb3be22918e7e93a6287d907adb70ec063eacfd51b784afdb3d1c0c85f8a5232cd97afe0398ddf652d99fcf75130e5879d09c67c961482c97eae7bb049f74b3ac9349e86649a4f2692b93cb978be8df749b88229be509660eca7f39f24d3f922713dd6afaedf8269bcd690c7c6f9d755001e52bc5b533ceb5a63d29fab2199ce63edc1139a9f82083c25f9f5068cf745dd2cf082c6238d25d3fb1d378229bf503ec178dfce6a0946fce9bd0aa6fe5ccf04e37d0af27aca77ac1bd8a2e747df9229fe2dc136f98ffc4530e9f34a934cf9f10fa6e78be739387f48fde9daa8f7273c3e310716f67b260f9269ffde1318efefb91e24537dcd06e3fd79f6156ca0deb1904cf6a080d1fe6a2199e67b5f303d4fbb801dd8fb0a8cf8f9a611db78ff5dac8211df745f60d413133e7e2c389be17ccc183c27ff97459279ffac4b30de27947e0826f94c4532bd7f5d032fa9fd6d2a784af58e21d8a0f9e1aa48a6f53f5b30d58ffd6730de5f94ad89ed01d9ef86eb2b0b2eb0feac6fc04b92af7525d3fbcd8f6093fccde64532d9cb4d30c57fe1128cfd33eb77c1a41feb028cf7355a1f82e9f9ee1cec62ffea9b60f227892918e785f0bd8dfdafe9b3601a4f270463fda81980f17ea59adbbf3966fd41ef472dc14bf2075b1b6c60fdec4132d50f0bc9944f1cc126c693dad7bf5e9ff2075c6f63bd6b45fd33b1e6549fb8adc0580fdc7682c97fdd5ab035a5f74309c67961e50bec523c1de2f736eaf7f11c3ca4feaba8bfa6d664467fffdc080c7f53b492a97e6f08a6f686776013fab4964cf69982717ed224ff30b5b15fe81608c6fbd3ce60c48b2d8dd78cdd8ff2eb5430c5839b5730f2dff60086be74a4af33d6ff148fdd8391ff6a74bf390b30693de808b6917f64c436ce8fdf1230f6bfb7345e0b6b3a27fbe9c0f319bdaf8bfcc782a5f7e43f0f60cc4705f99f256b3fedf71c826d92ef3625b627349f68dcdf9b2c9f9b927f237d3458ffd1fc5b8327f007241f530f1aaf6b00467cad51ffb2f8674efd41fed5b6711ed0e4fac192cf39b57733022fe97ecf977feda32abf7d8c9f3e26ffa7827fa74fcf0afb9da5daaac3fe58ff6a0be8f3df21cb2f72b9ecf3bb5c0afe9b90afff77f7df5116d62247f5d81ffad87ddba87590cbfd87bfb3f1f9779605f20899fa6be8cfffb79fff167b71e9f393743ffadc1663f4e33aa96fbdedf8eccf3f18b9ffff65e16dfbc90e6009240d1fa9bf2b3bc9439ee0df4d1661eb3f64f9313a3f24f3e4e717cbfa7791e5379fecfefa91d758f05a8e94457a8a7f2b590235542335561335553335570b364ea55aa917b5669f86e951ab76ec7365b2de544dd5d99f15ff9dc67eb1fe379365a3aed827652391a85bf6b963d2ecd43dfbec982c07464726dd91fdfb4e3da9f74cce7bf5817d0c76e53ff4daff5b6479e43dfdc446e699fddb597d515fb94ebda9efea87faa906ec9a9449f8c524f85607ea501da96375a24ed5993a67d2fc1bc9c25a7a5637ec9f91ba607f02d8fd92c9f2cafe6929aafaa4284cc7be15431d2ba662b18fad380af31a8aa7f8ff3d92fcf7cc954aa0844aa4c44aa2a44aa6e44ccf5ca63d2dfbf4b6622a059b45064aa954ca45a9954669954eb92a3745537465a5acffe8b5ff37cab251b6ca9db253cf8aa2ec95036bf1513929f7ca83f2a83c29cfca5979515e9537e55df9503e952ff6dfbf9581325446ca589928d37f23596c65a6cc9505d3b393b26413cd83f261288661b044c0b00dc7700dcff08dc0088dc8888dc4488dccc88dc2288dcab8a87746fddf23c99f9345b58ca6ff23a390df6677a3353ae36adc0ccdd08d95b13636c6d6b83376c69efd391847e364dcb3cf897d8e8c1fd8e7d178329e8db3f162bc1a6fc6bbf1c1b4b0cf02685e757f9b797f9aadfe999ff8af6561527c1a5feccfb7313086c6c818315fd4186363624c8d194b9916c6b2370ad3304dd3326de3683aa68b8fc73ebe19b04f684666c465eaa53a9a31936a6726666a66666e16666956e6c5accdc66ccdcebc9a375333757365aecdcd2f33b1f2eb1cfcd764311a736bde3169becd9db9e7b21ccc8379344fe6bdf9603e9a4fe6b379365fcc57f3cd7c373fcc4ff38b7dbecd8139c467648ed967c23e63736aceccb9b960992a3334e3de322cd3b22cdb722cd7f22cdf0aacd08aacd84aacd4caacdc2aac12b9dc2f91c5ffaa2c56655dac4b3f2e566d0cad5a3d5b8dd55a9d75b56e9666e9d6ca5a5b1bd6d3f7ccd96ead3bd6522689b5b3f61677bfd6019f2393666a9dac7beb817d1ead27ebd93ab351da592fd6abf566bd5b1fd6274b98bf5884b0601e3d61b3e9c0faeea3cf1fd1d1bfa2634cb31a8bfe82f001fff74a89aca135b2c6d6c49a5a336b6e2d7acdb196cc74fa4a9ed18f85b5b34d48f2439643bf4c614e6ddb766cd77ab03ddbb7033bb42326cfc178b4633bb1533bb37336abda766197ea935dd997de92fecce74fc932307756cdc6646055aa67d77663b776675fed9badd9ba71303fec95bdb6376c249ef9676fed9924625414662bcc5e602bf766681bf6d6beb377f6de3eb0cfde3eda27fb9e8debde7eb01fed27fbd93eb319d7b35fec57656fbfb1d1b07efffcafc9f27cb16a66274cb78cc6f8b4df992c1ff6a7fd657fdb037bc8faf3c47a7864bbe6dc1edb137b6a2db924bb7e64fab161ff9cb1f6ce85fd9b91bdb0972c485698ddcc1dc3311d662c8eed384cd2bde33a9ee33b8113b218f5dd89545df17fcdecfe155954d7183ab113333bb1edda499cd4c9ec2f27778a5e0ea7640ff0ed3d6b55653d3b17361acf4cc706cc6216dc6ed8ff3b356b6be3b44ca6b9d3998173756e8e66f7938feeac9cb5b3b1b7ecf78eb365d21c8d9d73e7ec9cbd73708ecec9b9771e7ec9107ecb7a7eb6a03f234b2f09f3c683fe7ecea3f3e43c3b67e7c58cadb33db1ced6b33d6656cc3ece2b6b5dc5dab5c0a7f765ec63cfe9e374f8bc39efbd24f6d20e9d0f66fd47e7d3f9ea7fe954ce379366dffff5f6cec8e9ff5acca933b3eb9fe4f8576599ab9db3b05f9c25fba1e21a6ce2b08c23f34accd3bab6ebb8aebd702a73e17ab66b1f5d9ff9a8931b981fccdacfaca5f76ec86c2ae673e6d18ddcd84d98bde0e3746e6a7acc73b02b594fbcd93336be67377373b7704bb7722f6e6d176e63356e6b358ae67656f3afe8987a70afaaeddeecd6d55cdd5db96b3626cfeec6dd9a43f7ceddb97b3622dbde86dd837bb4576e6031ed331df7e4debb0feea3fbe43e1b817b765fdc472195fbeabeb9efec5707f7c3f9703fdd2ff7db1db84377c474f1c0fe7dec4edca93b73e7eec25daabd2f6091b8a7aa81f70fbdda9f91c533d8f46d79b6e778ae937b9e71b2179eef055ec8e4601fdbf0222ff612e6030eb6d1eb8c977a99977b85577a9577f16aaff15aaff3aedecdd33cdd5b796b36e7c7c6c9db785befce7a70471ebb0b1b5d9dfd3ff36fde8e59cec1db7b07efe89dbc7bb5f01e58467af31ebcc71f5af5bb1ff853b23c79cfded97bf15ebd37a730637362dbde3bd32f2e099368cbe6f229b39ae7de76bd0fefd3fbf2bebd8137f446ded89b78536fe6cdbd85b76441d7a3aff8866ffa966ffb8e71f45ddfb31e7c9ff9e8ad6df8811ff663e547ee8069e4831ffb899ffa198bd6be51bffd523c9189fe75599e2f7eee177ee957fec5af0d6603e6d46f7a49fc967d3aff6acead27ffd6cf77bee6ebfeca5ffb1b7febdff93b7fef1ffca37f62f966a2bef8f78aea3ff88ffe93ffec9ffd17ffd57ff3df8da3bdf03ffc4fff8bf9f485bd30c74ee57ffb037fe88f8cbd3ff627fed49ff9737fa1382c27fd6679f70f597ea97dfc99d8d25f066aa004060b112da65f6aff97c0f7fae5b7811d38fe3570cd0fe31478811f04411844411c24411a64411e14411954c14509839a7d1afb3d68838ee59af3e06a3f07b7400bf460c5fc48ef0d9560ddeb99ad30d906f6c27a0e36ecaedbe02ed8057ba50e0e4a191c8353708f0af54f35ab3f2d8b1d3c048fc153f01c9ccdd829ad73f012bcf6da15bc05ef81137c049fccf38c83afe03b1804c360148c8309fb330d66c19c49b47016c1325443253442d3188416fbc39c71e8865ee88741188651c86cc73a8709f3cb4698f29881c50f61d6fbfc300f8bb00cabf012d661e32fd421d3b41fb2387f94e5e77af6cf63d25f1db661175ec39b19875aa8b3bbafac636f27e13a5c33795e8c7b2f0d37e136bc0b77e13e3c84c7f014de870fe163f8143e8767f52d7c095fc337ab08dfc38ff033fc62d2982c22f6d97ff90e07e1301c8563e3184ed82cb40dd6e1943d63e6ee99fd7c9b53f6dfe7e1225c466aa428456444e68fcacdaf5a46b288ff862c28f1d4c8525fd5d7c88e1c7f19b9e673e41987c8376751c02375a61351c8e2f7cf88e524511c25511a65c62dca8347ab65f369cafe3c4645f01895aa1255d125aaa3266a5910d345d7e8166966678ccc75d4467ab48ad6d126da4677c629da452c82f0039ef778d68ecdb40bf6bc7d74888ed129ba77d2e8217aecefcdabed866af4d1eeaff6f2932c068fb043b77516ca2e7a8a9ea373f4629ea357f6a437e6750d16a79ca37773c2e6e8aad7eae823fa8cbea2ef68a0ce153f1a2aea8f2a2c7f9e1b8da27134096d2b8ca6d1cc8aa379b48896b11a2b56111b319bac629b85166eccbc3d9f73962c7f93b29843e33ef6e3200ea3fb388ae3f081f554cadbcafc5a1f25fe2c0b7ff68f7131d4f8f9e22cd4f73889d3388bf3b888cbb86277bcb0f87111d7d6dedd5b4f7c2639b959dcc46ddcc557ff515dfdb4f662cb3bbaca5875e25bacc5ba3f8b57f13adec4dbf82edec57ba5890fea323ec6a7f83e7e881fe327e3143fb3d866c9bc7380b86e689de373fc120ce2d76011bfc5effd88736b3055a38fdeff992c0ad34726cb6b9cb8adba8a3f4c23fe649db7641ec6b09ee3da665ed3fc0c33f6dcaff83b1ec4c378a456eaca7e51ee94713c862c3c1eecff9dfdb7493c55f57816cfe345bc4c54e3c17e4894c448ccc44aecc449dcc44bfc24484263efde9b3e8bd8d887e7a42c3b65f3d6d17c4da2244e92244db2a84872b1baf07765b1c886f8155b36bbb2fe65914fa2b8499194e69b71f26fd69e8dc9d9b998dff6c13ad953e39054c925a99326d6d5f7beea9db4ccca52f40d55c1d97d95bb385332bb4abae49adc12cd38252ceb4956c93ad924dbe42eba26bb641f1c9243724cd82c657a8ec272014364d94c9b4b2f4dee9387e4d158264f09b34ab146d2e722ff4c96e499f5e198e50e2bf6efe7e4257935dfad3e3f3f276f56ff8afa7df26ece8dfb709c7c249fc957f29d0c143d192a8b64c4abe3bdc7b4fbac8dc9c264b2df93713231de9269324be6c63159d8c76469c6a99a2aa9919aa995daa9e3dfa76eeaa5bef1603a2cbfe1b2f45910cb54b769601cd3308dd2384dd294cbc2d76dfa7cc4180aaff6932c96b0fe3453466e6b97cc8e9d344f8bb4343fd22a4c98f75a5afdfe8687f4621becee75daa46ddaa5d7d451f4a04b6fa996ea91c9fd3f978549c3644957e93adda4dbf42eddb15fedd3837d4c8fe9c9d8a7f7e943fa18fae953642b7a344c9fd373fa92bed2c8902c2ca7db4681719fbea5efe947fa997ef5feb197a56f7f9f5bfd2acb8f91e17a78607dbb5412ff2139a7dfce9069eb673ae86da5f7c54ee5fa696f8f553a4ac7e9249da6b3a4b50b65c3f42c5177fe23ef31361a7d0edad724d379ba4897999a29c63163f971b4efb3e3ccecb3e2649559999d39ea57ea0475e6665ee66781e9fa81a3a06ab0eb675096e1c5599845599c25ce23b3172e8b6af5b9d52fb2889a9add3f99d14efd4a064ccf952c553eb2cc74cc2fe61b077dcecbf477613d982316e37efaeb2ccf8aaccc2a61e7e45b98b34db24b56678d5dc763f53b6bb32ebb662c62f343d6df6ff6c2fd66de9c652c99661ce3a74ccf56d93adb306bd3b36d76e74efa5999c53363a603cf76bfcd8445369691edb23d93e5901db313d31d85db7491ddff268bf28b2ca67a973d3032b24743c99eb82ccc37f67d642ebcc4661146f69c9db397ec357bcbde852c1855577d4bdaec23fb8c9e93419a655fd97736c8d8e866a36cec8eb24936355da7cc66d9bcf7ebacbf17d93257734509552b377233b7723b77d8377d3d6dc29edad74398b7cedddccbfdec900779a828ff4496be024a7dabf8bd36f63edacc23c330ee982cbd97efed70c024619219f7799c27fe364ff32c3c30597eac4172bf95e779919779a556f925aff3266f8d63dee5d75eb7dc613f1ef9cdfdeeb3e25cb316f153aee7ab7cad9eed32dfe4dbfcced67396c1b09879c8729a4bafd9feb7719f29f93e3fe4c73cc88e4c9b1392c52afe812cbd0f559209d332db2ed5263f1926f32baed05ca6678eed5877ecbfbce6f7fe5dfe104cf347a3fd690d92d97afe943f671725cecff94bfe9abfe5ef7dd4937ff49909af719cddaf6cca73628dcd8871fe997fe5dff9404d9cc833f3613ecac7f1579ff1997dbc4c51b4cfee30c9a7f92cc9988e1d59a699f47e377c0bdffe200bb75792259fab7b854d65ea77be302c63cfd2fb1dd5bc481673943bf9b2500b8545f5b3e8814547b6dcabc0575793b1fa919f0ba33059b7d9ac557d2cbfe8f312ebaeb7e4c2e96b1785cb62ad53e1157e111461112923d52ee22229d22233f67d5ec365611ac6aecdd98cb92f8aa22caa3c2459faf6b398f5f597f9e5375958c6b34f6fcc62be8b0b7be4ce74a97227646177ae8ba6688b2e288a6b714bf25f7684303d53fc645268855eac8ab5710835e7b597a5d8b06cabaf6a7ef4d5be60cd243916dbe2aed815fbe2a0d8c5317b284ec57df1503c16cc3efa1a279764614e0b268f7128cec54bf15abcfd248b1aaa7f57164fcab22bde59fe742a3ed894b5efa33c5420875c9645f1597c15dfc5a01806398ba2c90bffd8555114a3625c4c8a692f4931ebd72c989ea8c5bc8f467b5b6677e03163b12cd5ecbb544287c540d7d250c2d22cadd22e9dd2354ebc22c2ae36c7f6a1f498cdc6a55f06e1ae64290dd731f7efc822d654c40cf3adb665c472b053191b2ecbef852cfb5e16f3cb3a9749999659996749745f163f564a7824669465599597b22e9b5e92b2cd34661b565f3b6371dc985bb4e10ffaaca7ecca6b79b3be4acd0a4a9d8d67c4a45995eb72536e992cbabdeda326368e5bebbecf64cabb7257ee7b591425cd7a6d28b552fb45961fd13ef773ea8dcd103b75c56439183e1bebc81c5b7d3d2cb0674e63cfcb63792aefcb87f2b17c6271ce86e527a398c594ca9d5d97cfe5b97c295fcbb7f29d79d12eef32adafee9bb3bebec13f337b5f7eb079bc2c3fcbaff2bb1c985d392c47e5b89c94d3c42d67e5bc5c944be3c45c7d156a7c4c0f665f1939564a655466d06746a7e0d4ef2ab2aadf62fedefb3059d8b7663f36c8d834bbaa2c26cbbd19b2deec97483a264b6b06c6a1b22bc79bc5e3a00996e5c4dc562e538ba86236cfa29545e5574115329fca66bb3ea736471513d73a313946fd9a4515dbdb2aa9d22aabf2aaa84a2bacaaea52d55553b5556745c1b1ba062b362e5ba7a355b57ebda3aff254b74aabf42467b21c83a36ad9f51f641151a5cf6571d488e9e246bd315559556b264bc4662c268b193269f64c96a37bf65a6fae2e2387e9eb5bb5a9b6d55db5abf6cab53a54c7ea54f575e17be6c717d681c5d5233e26a3fe2ebd3d570f4c96c7eaa97af646d5b97ab1aaead5dc556fd57bf5517d565ff128afabef5e966ac07ac2e5f5cf678bddb11a56a36a9ce4ce23c6c5339adf627e4bca6270efba60d1ee539ac5a36a524db98e912c51af654c965335f33a8f656bca3c34ccae9a87af9eaac6ac3f17d5f2a26637f7f5a25cb81fb615f38349d17f26b406c3ee7570377d7e75312fd6c5be38e1cbc5bd78179f8d897f092e6134b8449798c9d2b91fccbadefb159cde673059946a7449823e47f6d9c7fea32ca859283c96225912f549bdbfa4ec9e593fc6fdfa9c6df523d4af3d18a774e75d2fb9925e8ad0c8eefbbcfd525eaacbe5525f9a4b6b9c2edde56a6fcdd1e5165f98bd93244764f10773e445d68e5da55df4cbeab2b6c2cbe6b2bddc5d7655a7b697fde57039ba2fbd2c173633b1d8cde27fd5d684fde2fef27079e4b2a0adfd5add3f95256071f24d29c2faf27479eeb585d61bfb95a07ecd81e9d8a377f35545b3df7975c88ca6feec72bebc5c5ecdc4383afae58daf136d8235f75dbd7e8db92c7c3d96f5f196c5dd5333bebc5f3e2e9f97afcb777cbcf06a13d388e16578195dc66c869ddb6c66cd466c967db5ee7adbbf4c2ed3cb8ccba2f0bcc2fe5d16392f4096545373b5cdcf97f9657159f6eb90bdcdb2d8b85fd50accc08d8c83a7298f8a1fb4c620fca8d55a49b6b5519bc98ac9f9565bb56def1cc38bfaf99ecd8d336ef74cc7b8341e9b3146e6dcdef82cdeac9ddaadbddaaf83484b46e9ad0eeba88eeb84e5ca9fcce3796c6e9db008f4c9dd32590e755a67756e325990875bfd0aeaeff9cbcfb2287eaa2b6e5dd4a5f25557c68149c0bc0fb3dfa8978569716cc69eee2b6aa9cc422bfc8caa9a4590ec7f0d6b5beb7eb84347cdaf2c63af9cd73063f67faa22f6ebdeaff7d27875c746f9cbfc626d5cd6d7fa566bb55eafea35d36ca77c8af27a536fcdd8b9b23cdceb6318d6239bdec2eabb7a57efcb3d932515759e3fe695d8112074cc55efd44370523ae5b13e184cb3597e7f6fdbbc12cfda581f599b4ff57dfde0b6a1556d13b77eac9fea67e3549feb17a65b8bfab59f159cd728b02d1691ceeb37bfb1b7fe376fd994b220ca89acbdfb5abfd71ff56738a8bfeaef7a500fcba01e992ed3c27db17077aec3f29881cb324b63578feb493de5b2507ef20f7264f6a797a59ffb13f6ff0d9bfda7f5ac9ed70b9657b2fc8bc52e5b7b592f9d779bcdc98dda288de13f5a35f3ca1f8dd958c64363db47f6d9374e3f438693be6ed3fbb2c665ff34a2d0def09a9af1cb1ae0d074eb5be3357e1334a1f3d4444ddc244dcafae291e54977eeaec9ac2dfb158b429bbc299ab2a9b8bdd09ca8f42b763fcb42732397c5e4b364aa8cd2cc99c77a7369eaa6316377c3eefb7039352d0b8359d6e186c6aee99a6b738badd0b48a466b74fbc18c9b557aecc7c57c6fd64cdfc76c2c3efa5cbdd9345bebdcdcb9dbbe8283d53fac9c9b5ebfb3a4d935fbe6604d9a63736aee9b0726c363f3d43c3799bbeb6be64c2fe3e6dcbc34afcd9b948559c51f65a1d95ecaa2f1959bb5faa138cd7bf3c1a22ac7ff609e89d973bfa6686f9b4fe3e0dbcd57f31dbae167ad3483669828d60b7bde283359ae356bc66c961b46611f41369366dacc9ab971b296eeb6e1e3815566167d9b9ed3354b63efad5bb5555aa3355bcbe8ebd581396efb15c45d9f2b87ba71689dd66dbdd6ef63181e7719aadfaf09ff365752a5cfa4bdc2fdfa937a8bc76de03dc65d1b1afd4afdb658703fbbed6dba8d8c531bb7499b865ef5de666d9e9fdaa22ddbcad8370fed85595585fce4beaddba66ddbaebd5e5876dfded8bcf3ddd70ee4eabfefed5aaddf1de36baddeaeda755bb35f6dda6df0dede056f5ec89eb7734a63d7eedb437b6c4f52163f38fd41161bd97abff39945bc2c951e313ddbe5b9a2b7f7ed43fbc8b4f7686ffa9d05b5d53ef55adf326fdf9edb97f6d518861f4e5c44ed5bb269df59ccb3cf3b7bc9645ef5f949fbd17eb65fed773b6887eda865b3068b697aed123b19f67e906bf6b69db0acf8d44edb593b6751cb397875ed76d12e3bb59fdbeace38754a67746667c571922779af472c8e39fe4116e75759d84c99a94967770e93e6dab99d67192cd770d89cb063be56ef7c7b5b9f7d3753baa00bbba8da767197746997b1b0b8e8ca7e6dc8db75157bfea5abbbc6df756dd775d7eed6699ddeadfaac8e723b5ec95b326b687b8fe2fb4e65dce75db7ee36eeaedb76776c02d875fbe6397aef0eddb13b75f7dd43f7f8ab2cbfc563b40fdae2fbb87b59fabdb76ef7d43de7cfddb97be95e6bc738b9ae7de8ff1a4166c38eb3eddeba77964f9cba8feeb3fbeabe9d85fad80dba6137eac6d19d195b0b33f6b56ed24dbb5937ef16ddf2aa5e95ab71358d9dfbca7763f9bd1ce6d00ffa99fd6ad97bc7bada4cbe73b7bb322bb18ee1cadd5d5deffdea3109dfaffe35b886d7280fa302b66ffe1d1d13eb0b72bf196a7fdcef5de36b52b2d65d53ebe19ab119ef6c9dddcf7eb5f79ab3ff5a5ccb6bc5e29897ebe55a5bfeb5b9b6d7ee7acd27d7db55bb321d5533b30bdf8da69c5c57d7f57573dde6937efe655aa8f4750ce623a2be06c6f7638dd87c74ecfd38d515ade7de5aaa8171bcde5d77d7bdda3847b5edab2c5993353c07367f9c07f8afd7f89ca7ebe17a8cd953afa7eb3d3dddbf998b6641d1eef5e1fa787dba3e5fcfd797eb6bad3069deaeefd78febe7f5ebfa5d75d74139bc0eafa3eb987d26d7e975769d7b6bd60b8bcccccceb92dba2f5f327f2dd7d73c7fc1c27ebcc52c3a79b7263f955348c4c36f3b9aa6dbf33591cbebef31764b99937eb66df1cd6379ecb66877edf8bad140b2fb9b139d0b8f7b51b0b346f8152ddc25b748bd5db2db9a5b7ec96df8a68d025e15755df4a7377ab6e97f0e3c626c65b5bac6e9db1ef772df0fd16d6af1f5eafb0d86cc4246aee7236b3dcaeb7db4d4bc6d133cb2d44fefb87ddf3ffb52ce6e34dbfad6eeb86c5860d1b0d663515f3f8ecff6d8fc5eecc9fe593dbe6b6bdddd5a5fa62d7cae2b65386b7fdeda0d4ea58dd869f55773bde4eb7fbcbddede1f6787bba3ddfce51623f180fb71733643e7af24759983454afa4fbbfdede54f7f67efb48068a226ac97f7d7d3f2a6e9fb7afdbf76dc02b565f2cb35c58776c86d89a73162f6d6e43e3741bddc6b7c96d9abab759eadce6f1241ef77bf56f8bc80a5fa2eab6d4d4a8d394a85202f5ac199aa9599aadb1b16e5acdedade4e78fbbef3396c82759cc58f3345f0bb4508bf242d1d20ce3f287d3597fc25e1eb5584bb454cbfad981dd7b617e7a119385f624cdedad96f733a75668a5566917ad564325615ed0b65fe289b25322add15aadd3aedacdf84c1dd573979aa6e9da4a5b6b1b7ba16da99af0ebc8b03f2a8bb1ad7667dada9db653bba0b9bddbef2c7f37642dcfa1f8f22fc8926a7beda01db59376af3db008efce9cfa1f2c3bda71eb6451633568f92e43ed517bd29eb5b3f6a2bd46b935d2de8c85f61e4fb40f36269f2c216e94246ccb2767e999da97f6ad0d3436a6edeeefd98bf5dcee2c35f2b59136d6260a8bd955d72eed525db37111f562ef2fcbf2a84db59936d716da52578d7da8318f79673d9993b86e6fecb363518056b6bac26c79a71bbad92a9dab5bc64db77527327457c97596ee844e7fe622ce597e32d4033dd4233dd6133dedc7e0d74f98f5b2447e2f95bbd7333dcfebb2d20bf6fbfe0c84266b797c86ff2b3a16157aa957fa45aff5466ff5aedf11c3f2de39af8f322fa45ffb798faffbfaa6ebbe66817ed3355dd757fa5adfe85bfdcef850bff59dbe77c6d5413fe847fda4dfeb0ffaa3fe14bd07aece722db28dde4ef86e0bf3e78fe9e8e764adbfe8af4aa36ead86cdca34abf4f6eff075c43fbd8724c9f537fdbdb8ea1ffaa7fea57febcc07588affed6ed3ca56a3f7beea637afd3a561f4733895c33d687fa888dd0589fe8537da6cff585be5ca92b6565accc95e5eddd3c7ded778d38d5ca5e39b6e1eec90f0b0ff68b2cae7170dc95bbf2567e3c535f56415f310a4e2cdeea57df6a5a7bfb73b2048fab701505b355bc4a56e92a5be5ed78555807166b6efa5ccb3afe248bc9b28265dee5ceed8565b4b755b9aa5697555dcd568d3e5ab5b1c7f7c23129dcddaaeb76abab395e752c5bbbe3f66e626ef955166f7533e3b261f9d270a5b1f178e335567efeafaf2afd9571311f57fa6ab55a6bb362b8daacb6abbbcbc76ad75767d2a05f79b0b67d0ecf64796372ec983c0adfc570cff7819ef85efea3d8cf6bdcbbdf76e87ff8cd6abf3aac8ead1d7cb84edffa7e9e679aa6b63b6627bfcae2af4eab7b9643ad570fcdfbea919fce727f3a052c4f69fca971795a3dafceab176db67ad5ebd5dbea7df5b1fa8cbff26ef595f615a31fbbddf98749f7c177bc0f9d57f6795b7daf06d17e35147b2cfbbdbccd9339ee7739b58bd5a8d72e173b9358e3acdf65b1ceab717d67ec5693d574355bcd51c3b7708ec1fdf3314c92af16abe55a5d2b6b439badcdb5b5b6d7ceda5d7bc69e45cffb1f7bdefb489eaffb32097a7b62b3105fc7e3fba9f9beea7e36627e6318266b7f1dacc3c0091cbecb91452c6431bd17f85d967e7473dbd8d7efeb681dab77e20c2d9745f9b16be9cff8b1f03198af933c5d5bc1789daeb375be2ed6e5ba5a5fd6b5b5b437741601fbf7fbd5b30fbe57a25f6bb15a16fc34ceba757416dfb3e83e9ce9d77ecd807df6ebceefd6cc62bc50c8d2eeb411f366bfd90bd7d26374b7beadb53573e4b2fee5fed5793f628aba5eaf37ebedfa6ebd5befd787f5717d5adfaf1fd68feba790c5fdd69eef5ac1b8b04fbf9ef76a7e704b3e584bbedb9f5950bffba8f7e2eb7e97fcb1df83b63eaf5f02d736cc28effa73246c8e1feb193f73d2e7373edfafe4b23c69bb7e5dbf35cafa7dfdc1e617c8f2f7f78ffdf38ff3283f69566595d196e7f5e7fa6bfdbd1eac87eb11d335bea38c568b980e4db0afbf8fa6c6acc7cffc04c6b2dfadd7cbe90f7a7bea77569a43967505d5e0a69a713e6171d79d36d1f3f5b8f776eb49bfa3b61f93cb389f38de7aba9eade7eabdd8d32b674be7afc8f2d3d9621ecf798f6a11dcaf179774bdcc5f36ea46d9181bb37faead164ebf368979c230a77db5439cb4602336661ab91436d3dc39174ba1fd0a2caa7f6511e4ae0f0b36569fdfacc6cce59f8ce3c6c9271b77e36dfc4d607da94f56f35b9bfed2b8fc7eb6381a2a9ee2da55972acd26dc449b78936cd24db6c9f9aa0bcb6eac27ffdbf68bbc782effdf76ae6549552488eee733a6f615fdba7dbb6356a899203e5a6db51f3b4444f0c14b1489987f9faa8202a4bd37b457331113b9d010943a661645669d93b6d9e37b5a6207f5ce54d90a7b630ee6244ed8f3c2d4532060d9afca32ad349d280e4d481246103617866ae8b10a11c3b2b36c86248e1fe983506f36cb7bd7f59cde1344592e9dd23decc91c0e90c0115250a0014d680104cf80a691efb672cf7c667bc1191af13a6a6c8d2ed758790ea8a041fba8a546ca6681e23a2c67047dffd9b4a1130fb9e20cbad0833e2ce34747a706bc2c0e85fef98baee722bfd4f4404187dc51957668dcd0c2d0b660004318c12b8c61025378f3347fb13bb0acbe5df0f8ab3a31cdd0e01d3ee0136ee036e9c2ddb1ed7437f7e95459c13d7169c0bcfd003fe0117eb2bb700a4ff08c94def1b9eef5e9337dca1557dfc052a8ea8aefae6ed98c216b9d45ef4ffa485524aebeeaa181333471eeaa5edbb7f71bff47100423b4421517519a70a1686ff7106f632dbe471b97ebe0a027b3a4874ec8a276354cdffc603e255ca59dee228325a4b8c2354b719e8d29bbca113774bcb961e319c8f15c1f6327bd3884873ca043ee1bdcda7dce35430f7d0c30740718e10e636fed2f7ddd7f0cc2861b3aa1161991827b3cecfa8d66ec6112bce2115354f693204dfadb96d559cd6ccab25f967cd21655d61b3627a2d61336b0892d72a03dc20e53978ef868d8fa229f5faee75a7fe935c07eb745b764495b08ec7d0311d5f514356ca3bffd70224ff774bfb3f1822818bb5ad8461d3bd8c5de6e837df6fe0507fb088787258ef015c7d68bd0fa1a7cc79ef3f0a8623d21573237714a0f6bb636061dba66de58d0211dac1b76bfe293ef6039b59ad281736c393795d713791dceebf80ee712f96e3009ddd07544a5d179e13613e6e4967d435ca3ec245389a0cad56bfd58aad7bf7aee9fc122ff178e852391e3f3ba9c7d9399eff23a234758aeb7d96b869def6fe7fb0f468e60216ac18678a2378b9890bd31a42ee18c42f1d27b720dcf09969c1fb0909d7904e76ec356026184f0baafc89d66956e3da5fecbf6f6f369d67545d6b5c5d924df135a7ec542cb5e26e49a7cbfc201afa191da8093df3604f79408ae9ab0ad576031ca9ca3caca1635c83935653d22cfb448ce3bcf6adc752c32e62a9eb9443352aa19aaaae7e288acea1ab4729f2cb50e052bf5d4871cf952b02732ae47d9db635ea9689b453c57bc2062244752ad5f5e84c592fcc3e1ee4c949995795bc152fdbea830d827ffc84c20e9088f91d2fb395e4b665b520152c3422492ebf27dce3a1163b10a2cc60916c11710bad85b6ed69a7320cdc09db863ceee0a1533e20c1ccef6c814cd995eb334f9fc9ecf4b7ebda988b5c23b67b1108e04dff03d63da7fc32f8b9a5f4caeec2fc7c9c7e6f5bc5e154b795ce2e596e3679fca7dd222b624e3bc58134fe3a1e0bbcd199677fcb81c4b3d6a7ea7d6aeb0fb7e71ceb9a3f5738b7cbeda2ba7de3ba79caf57e52fff15fb1fcbbfd3fefcfbaf3ffe0193cc32fd</data> + </image> +</images> +<connections> + <connection> + <sender>PushButton1</sender> + <signal>clicked()</signal> + <receiver>MainDialog</receiver> + <slot>close()</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/examples/network/remotecontrol/remotecontrol.pro b/examples/network/remotecontrol/remotecontrol.pro new file mode 100644 index 0000000..0570253 --- /dev/null +++ b/examples/network/remotecontrol/remotecontrol.pro @@ -0,0 +1,16 @@ +TEMPLATE = app +TARGET = remotecontrol + +CONFIG += qt warn_on release + +REQUIRES = network full-config nocrosscompiler + +HEADERS = startup.h \ + remotectrlimpl.h \ + ipcserver.h +SOURCES = main.cpp \ + startup.cpp \ + remotectrlimpl.cpp \ + ipcserver.cpp +INTERFACES = remotectrl.ui \ + maindialog.ui diff --git a/examples/network/remotecontrol/remotectrl.ui b/examples/network/remotecontrol/remotectrl.ui new file mode 100644 index 0000000..a1cecef --- /dev/null +++ b/examples/network/remotecontrol/remotectrl.ui @@ -0,0 +1,145 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>RemoteCtrl</class> +<widget class="QDialog"> + <property name="name"> + <cstring>RemoteCtrl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>279</width> + <height>185</height> + </rect> + </property> + <property name="caption"> + <string>Remote control</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>textToSend</cstring> + </property> + </widget> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer5_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout6</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>sText</cstring> + </property> + <property name="text"> + <string>Send &Text</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>sImage</cstring> + </property> + <property name="text"> + <string>Send &Image...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>sPalette</cstring> + </property> + <property name="text"> + <string>Send &Palette...</string> + </property> + </widget> + <spacer> + <property name="name" stdset="0"> + <cstring>Spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>quitButton</cstring> + </property> + <property name="text"> + <string>&Quit</string> + </property> + </widget> + </vbox> + </widget> + </hbox> +</widget> +<connections> + <connection> + <sender>quitButton</sender> + <signal>clicked()</signal> + <receiver>RemoteCtrl</receiver> + <slot>close()</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/examples/network/remotecontrol/remotectrlimpl.cpp b/examples/network/remotecontrol/remotectrlimpl.cpp new file mode 100644 index 0000000..bac7806 --- /dev/null +++ b/examples/network/remotecontrol/remotectrlimpl.cpp @@ -0,0 +1,50 @@ +#include "remotectrlimpl.h" + +#include <qpushbutton.h> +#include <qlineedit.h> +#include <qsocket.h> +#include <qfiledialog.h> +#include <qcolordialog.h> +#include <qimage.h> + +RemoteCtrlImpl::RemoteCtrlImpl( QSocket *s ) +{ + socket = s; + connect( sImage, SIGNAL(clicked()), SLOT(sendImage()) ); + connect( sText, SIGNAL(clicked()), SLOT(sendText()) ); + connect( sPalette, SIGNAL(clicked()), SLOT(sendPalette()) ); +} + +void RemoteCtrlImpl::sendPacket( const QVariant &v ) +{ + QByteArray ba; + QDataStream varDs( ba, IO_WriteOnly ); + varDs << v; + + QDataStream ds( socket ); + ds << (Q_UINT32) ba.size(); + socket->writeBlock( ba.data(), ba.size() ); +} + +void RemoteCtrlImpl::sendImage() +{ + QString imageName = QFileDialog::getOpenFileName( QString::null, + "Images (*.png *.xpm *.jpg)", this ); + QImage image( imageName ); + if ( !image.isNull() ) { + sendPacket( image ); + } +} + +void RemoteCtrlImpl::sendText() +{ + sendPacket( textToSend->text() ); +} + +void RemoteCtrlImpl::sendPalette() +{ + QColor col = QColorDialog::getColor( white, this ); + if ( col.isValid() ) { + sendPacket( QPalette(col,col) ); + } +} diff --git a/examples/network/remotecontrol/remotectrlimpl.h b/examples/network/remotecontrol/remotectrlimpl.h new file mode 100644 index 0000000..382c9df --- /dev/null +++ b/examples/network/remotecontrol/remotectrlimpl.h @@ -0,0 +1,26 @@ +#ifndef REMOTECTRLIMPL_H +#define REMOTECTRLIMPL_H + +#include "remotectrl.h" + +class QSocket; + +class RemoteCtrlImpl : public RemoteCtrl +{ + Q_OBJECT + +public: + RemoteCtrlImpl( QSocket * ); + +private slots: + void sendImage(); + void sendText(); + void sendPalette(); + +private: + void sendPacket( const QVariant & ); + + QSocket *socket; +}; + +#endif // REMOTECTRLIMPL_H diff --git a/examples/network/remotecontrol/startup.cpp b/examples/network/remotecontrol/startup.cpp new file mode 100644 index 0000000..03fa87f --- /dev/null +++ b/examples/network/remotecontrol/startup.cpp @@ -0,0 +1,45 @@ +#include "startup.h" +#include "remotectrlimpl.h" +#include "maindialog.h" +#include "ipcserver.h" + +#include <qsocket.h> +#include <qlabel.h> + +static const Q_UINT16 ipcPort = 54923; + +StartUp::StartUp() +{ + remoteCtrl = 0; + mainDialog = 0; + + socket = new QSocket( this ); + connect( socket, SIGNAL(connected()), SLOT(startRemoteCtrl()) ); + connect( socket, SIGNAL(error(int)), SLOT(startMainDialog()) ); + socket->connectToHost( "localhost", ipcPort ); +} + +StartUp::~StartUp() +{ + delete remoteCtrl; + delete mainDialog; +} + +void StartUp::startRemoteCtrl() +{ + remoteCtrl = new RemoteCtrlImpl( socket ); + remoteCtrl->show(); +} + +void StartUp::startMainDialog() +{ + mainDialog = new MainDialog(); + mainDialog->show(); + + IpcServer *server = new IpcServer( ipcPort, this ); + + connect( server, SIGNAL(receivedText(const QString&)), + mainDialog->description, SLOT(setText(const QString&)) ); + connect( server, SIGNAL(receivedPixmap(const QPixmap&)), + mainDialog->image, SLOT(setPixmap(const QPixmap&)) ); +} diff --git a/examples/network/remotecontrol/startup.h b/examples/network/remotecontrol/startup.h new file mode 100644 index 0000000..900d841 --- /dev/null +++ b/examples/network/remotecontrol/startup.h @@ -0,0 +1,28 @@ +#ifndef STARTUP_H +#define STARTUP_H + +#include <qobject.h> + +class QSocket; +class RemoteCtrlImpl; +class MainDialog; + +class StartUp : public QObject +{ + Q_OBJECT + +public: + StartUp(); + ~StartUp(); + +private slots: + void startRemoteCtrl(); + void startMainDialog(); + +private: + QSocket *socket; + RemoteCtrlImpl *remoteCtrl; + MainDialog *mainDialog; +}; + +#endif // STARTUP_H |