OnlineDocumentation.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net>     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <QBuffer>
00027 # include <QHttpResponseHeader>
00028 # include <QMessageBox>
00029 # include <QTcpSocket>
00030 #endif
00031 
00032 #include <sstream>
00033 #include <CXX/Objects.hxx>
00034 #include <zipios++/zipfile.h>
00035 #include <Base/Interpreter.h>
00036 #include <Base/Stream.h>
00037 #include <App/Application.h>
00038 
00039 #include "MainWindow.h"
00040 #include "BitmapFactory.h"
00041 #include "OnlineDocumentation.h"
00042 
00043 using namespace Gui;
00044 
00045 // the favicon
00046 static const unsigned int navicon_data_len = 318;
00047 static const unsigned char navicon_data[] = {
00048     0x00,0x00,0x01,0x00,0x01,0x00,0x10,0x10,0x10,0x00,0x01,0x00,0x04,0x00,
00049     0x28,0x01,0x00,0x00,0x16,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x10,0x00,
00050     0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x04,0x00,0x00,0x00,0x00,0x00,
00051     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
00052     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,
00053     0x84,0x82,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
00054     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
00055     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
00056     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
00057     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x11,0x00,
00058     0x00,0x00,0x00,0x00,0x01,0x10,0x01,0x10,0x00,0x00,0x00,0x00,0x11,0x00,
00059     0x00,0x10,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
00060     0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,
00061     0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x10,
00062     0x00,0x00,0x00,0x00,0x01,0x10,0x01,0x10,0x00,0x20,0x00,0x00,0x00,0x11,
00063     0x11,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,
00064     0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
00065     0x00,0x00,0x00,0x00,0x02,0x22,0x22,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
00066     0x00,0x00,0xff,0xff,0x00,0x00,0xfc,0x3f,0x00,0x00,0xf9,0x9f,0x00,0x00,
00067     0x93,0xdf,0x00,0x00,0x93,0xff,0x00,0x00,0x93,0xff,0x00,0x00,0x93,0xff,
00068     0x00,0x00,0x93,0xfd,0x00,0x00,0x81,0xd8,0x00,0x00,0x99,0x9d,0x00,0x00,
00069     0x9c,0x3d,0x00,0x00,0x9f,0xfd,0x00,0x00,0x80,0xfd,0x00,0x00,0xff,0x7d,
00070     0x00,0x00,0xfe,0x01,0x00,0x00,0xff,0x7f,0x00,0x00};
00071 
00072 OnlineDocumentation::OnlineDocumentation()
00073 {
00074     // store the listed files in a stringlist
00075     std::string path = App::GetApplication().GetHomePath();
00076     path += "/doc/docs.zip";
00077     zipios::ZipFile zip(path);
00078     if (zip.isValid()) {
00079         zipios::ConstEntries entries = zip.entries();
00080         for (zipios::ConstEntries::iterator it = entries.begin(); it != entries.end(); ++it) {
00081             this->files.push_back(QString::fromAscii((*it)->getFileName().c_str()));
00082         }
00083     }
00084 }
00085 
00086 OnlineDocumentation::~OnlineDocumentation()
00087 {
00088 }
00089 
00090 QByteArray OnlineDocumentation::loadResource(const QString& filename) const
00091 {
00092     QString fn = filename;
00093     fn = filename.mid(1);
00094     QByteArray res;
00095 
00096     if (fn == QLatin1String("favicon.ico")) {
00097         // Return an resource icon in ico format
00098         res.reserve(navicon_data_len);
00099         for (int i=0; i<(int)navicon_data_len;i++) {
00100             res[i] = navicon_data[i];
00101         }
00102     }
00103     else if (filename == QLatin1String("/")) {
00104         // load the startpage
00105         QString header = QString::fromAscii(
00106             "<!doctype html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"
00107             "<link rel=\"shortcut icon\" href=\"favicon.ico\" type=\"image/x-icon\">"
00108             "<html><head><title>Python: Index of Modules</title>"
00109             "</head><body bgcolor=\"#f0f0f8\">"
00110             ""
00111             "<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"heading\">"
00112             "<tr bgcolor=\"#7799ee\">"
00113             "<td valign=bottom>&nbsp;<br>"
00114             "<font color=\"#ffffff\" face=\"helvetica, arial\">&nbsp;<br><big><big><strong>Python: Index of Modules</strong></big></big></font></td>"
00115             "<td align=right valign=bottom>"
00116             "<font color=\"#ffffff\" face=\"helvetica, arial\">&nbsp;</font></td></tr></table>"
00117             "<p><p>"
00118             "<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"section\">"
00119             "<tr bgcolor=\"#ee77aa\">"
00120             "<td colspan=3 valign=bottom>&nbsp;<br>"
00121             "<font color=\"#ffffff\" face=\"helvetica, arial\"><big><strong>FreeCAD Modules</strong></big></font></td></tr>"
00122             ""
00123             "<tr><td bgcolor=\"#ee77aa\"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>"
00124             "<td width=\"100%\"><table width=\"100%\" summary=\"list\"><tr><td width=\"25%\" valign=top>");
00125         int ct=0;
00126         for (QStringList::ConstIterator it = this->files.begin(); it != this->files.end(); ++it) {
00127             QString file = *it;
00128             if (file.endsWith(QLatin1String(".html"))) {
00129                 file.chop(5);
00130                 if ((++ct)%15 == 0)
00131                     header += QString::fromAscii("</td><td width=\"25%\" valign=top>");
00132                 header += QString::fromAscii("<a href=\"%1.html\">%2</a><br>").arg(file).arg(file);
00133             }
00134         }
00135 
00136         header += QString::fromAscii(
00137         "</td></tr></table></td></tr></table> <p>"
00138         //"<p align=right>"
00139         //"<font color=\"#909090\" face=\"helvetica, arial\"><strong>"
00140         //"pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>"
00141         "</body></html>");
00142         res.append(header);
00143     }
00144     else if (this->files.contains(fn)) {
00145         // load the requested page from zip 
00146         std::string path = App::GetApplication().GetHomePath();
00147         path += "/doc/docs.zip";
00148         zipios::ZipFile zip(path);
00149         zipios::ConstEntryPointer entry = zip.getEntry((const char*)fn.toAscii());
00150         std::istream* str = zip.getInputStream(entry);
00151 
00152         // set size of the array so that no re-allocation is needed when reading from the stream
00153         res.reserve(entry->getSize());
00154         QBuffer buffer(&res);
00155         buffer.open(QIODevice::WriteOnly);
00156         Base::IODeviceOStreambuf buf(&buffer);
00157         (*str) >> &buf;
00158     }
00159     else {
00160         // load the error page
00161         QHttpResponseHeader header(404, QString::fromAscii("File not found"));
00162         header.setContentType(QString::fromAscii("text/html\r\n"
00163             "\r\n"
00164             "<html><head><title>Error</title></head>"
00165             "<body bgcolor=\"#f0f0f8\">"
00166             "<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"heading\">"
00167             "<tr bgcolor=\"#7799ee\">"
00168             "<td valign=bottom>&nbsp;<br>"
00169             "<font color=\"#ffffff\" face=\"helvetica, arial\">&nbsp;<br><big><big><strong>FreeCAD Documentation</strong></big></big></font></td>"
00170             "<td align=right valign=bottom>"
00171             "<font color=\"#ffffff\" face=\"helvetica, arial\">&nbsp;</font></td></tr></table>"
00172             "<p><p>"
00173             "<h1>404 - File not found</h1>"
00174             "<div><p><strong>The requested URL was not found on this server."
00175             "</strong></p>"
00176             "</div></body>"
00177             "</html>"
00178             "\r\n"));
00179         res.append(header.toString());
00180     }
00181 
00182     return res;
00183 }
00184 
00185 PythonOnlineHelp::PythonOnlineHelp()
00186 {
00187 }
00188 
00189 PythonOnlineHelp::~PythonOnlineHelp()
00190 {
00191 }
00192 
00193 QByteArray PythonOnlineHelp::loadResource(const QString& filename) const
00194 {
00195     QString fn = filename;
00196     fn = filename.mid(1);
00197     QByteArray res;
00198 
00199     if (fn == QLatin1String("favicon.ico")) {
00200         // Return an resource icon in ico format
00201         res.reserve(navicon_data_len);
00202         for (int i=0; i<(int)navicon_data_len;i++) {
00203             res[i] = navicon_data[i];
00204         }
00205     }
00206     else if (filename == QLatin1String("/")) {
00207         // get the global interpreter lock otherwise the app may crash with the error
00208         // 'PyThreadState_Get: no current thread' (see pystate.c)
00209         Base::PyGILStateLocker lock;
00210         PyObject* main = PyImport_AddModule("__main__");
00211         PyObject* dict = PyModule_GetDict(main);
00212         dict = PyDict_Copy(dict);
00213 
00214         QByteArray cmd =
00215             "import string, os, sys, pydoc, pkgutil\n"
00216             "\n"
00217             "class FreeCADDoc(pydoc.HTMLDoc):\n"
00218             "    def index(self, dir, shadowed=None):\n"
00219             "        \"\"\"Generate an HTML index for a directory of modules.\"\"\"\n"
00220             "        modpkgs = []\n"
00221             "        if shadowed is None: shadowed = {}\n"
00222             "        for importer, name, ispkg in pkgutil.iter_modules([dir]):\n"
00223             "            if name == 'Init': continue\n"
00224             "            if name == 'InitGui': continue\n"
00225             "            if name[-2:] == '_d': continue\n"
00226             "            modpkgs.append((name, '', ispkg, name in shadowed))\n"
00227             "            shadowed[name] = 1\n"
00228             "\n"
00229             "        if len(modpkgs) == 0: return None\n"
00230             "        modpkgs.sort()\n"
00231             "        contents = self.multicolumn(modpkgs, self.modpkglink)\n"
00232             "        return self.bigsection(dir, '#ffffff', '#ee77aa', contents)\n"
00233             "\n"
00234             "pydoc.html=FreeCADDoc()\n"
00235             "\n"
00236             "heading = pydoc.html.heading(\n"
00237             "'<big><big><strong>Python: Index of Modules</strong></big></big>',\n"
00238             "'#ffffff', '#7799ee')\n"
00239             "def bltinlink(name):\n"
00240             "    return '<a href=\"%s.html\">%s</a>' % (name, name)\n"
00241             "names = filter(lambda x: x != '__main__',\n"
00242             "               sys.builtin_module_names)\n"
00243             "contents = pydoc.html.multicolumn(names, bltinlink)\n"
00244             "indices = ['<p>' + pydoc.html.bigsection(\n"
00245             "    'Built-in Modules', '#ffffff', '#ee77aa', contents)]\n"
00246             "\n"
00247             "names = ['FreeCAD', 'FreeCADGui']\n"
00248             "contents = pydoc.html.multicolumn(names, bltinlink)\n"
00249             "indices.append('<p>' + pydoc.html.bigsection(\n"
00250             "    'Built-in FreeCAD Modules', '#ffffff', '#ee77aa', contents))\n"
00251             "\n"
00252             "seen = {}\n"
00253             "for dir in sys.path:\n"
00254             "    dir = os.path.realpath(dir)\n"
00255             "    ret = pydoc.html.index(dir, seen)\n"
00256             "    if ret != None:\n"
00257             "        indices.append(ret)\n"
00258             "contents = heading + string.join(indices) + '''<p align=right>\n"
00259             "<font color=\"#909090\" face=\"helvetica, arial\"><strong>\n"
00260             "pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''\n";
00261 
00262         PyObject* result = PyRun_String(cmd.constData(), Py_file_input, dict, dict);
00263         if (result) {
00264             Py_DECREF(result);
00265             result = PyDict_GetItemString(dict, "contents");
00266             const char* contents = PyString_AsString(result);
00267             res.append(contents);
00268             return res;
00269         }
00270         else {
00271             // load the error page
00272             PyErr_Clear();
00273             res = fileNotFound();
00274         }
00275 
00276         Py_DECREF(dict);
00277     }
00278     else {
00279         // get the global interpreter lock otherwise the app may crash with the error
00280         // 'PyThreadState_Get: no current thread' (see pystate.c)
00281         Base::PyGILStateLocker lock;
00282         QString name = fn.left(fn.length()-5);
00283         PyObject* main = PyImport_AddModule("__main__");
00284         PyObject* dict = PyModule_GetDict(main);
00285         dict = PyDict_Copy(dict);
00286         QByteArray cmd = 
00287             "import pydoc\n"
00288             "object, name = pydoc.resolve(\"";
00289         cmd += name.toUtf8();
00290         cmd += "\")\npage = pydoc.html.page(pydoc.describe(object), pydoc.html.document(object, name))\n";
00291         PyObject* result = PyRun_String(cmd.constData(), Py_file_input, dict, dict);
00292         if (result) {
00293             Py_DECREF(result);
00294             result = PyDict_GetItemString(dict, "page");
00295             const char* page = PyString_AsString(result);
00296             res.append(page);
00297         }
00298         else {
00299             // get information about the error
00300             Base::PyException e;
00301             Base::Console().Warning("PythonOnlineHelp::loadResource: %s\n", e.what());
00302             // load the error page
00303             res = fileNotFound();
00304         }
00305 
00306         Py_DECREF(dict);
00307     }
00308 
00309     return res;
00310 }
00311 
00312 QByteArray PythonOnlineHelp::fileNotFound() const
00313 {
00314     QByteArray res;
00315     QHttpResponseHeader header(404, QString::fromAscii("File not found"));
00316     header.setContentType(QString::fromAscii("text/html\r\n"
00317         "\r\n"
00318         "<html><head><title>Error</title></head>"
00319         "<body bgcolor=\"#f0f0f8\">"
00320         "<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"heading\">"
00321         "<tr bgcolor=\"#7799ee\">"
00322         "<td valign=bottom>&nbsp;<br>"
00323         "<font color=\"#ffffff\" face=\"helvetica, arial\">&nbsp;<br><big><big><strong>FreeCAD Documentation</strong></big></big></font></td>"
00324         "<td align=right valign=bottom>"
00325         "<font color=\"#ffffff\" face=\"helvetica, arial\">&nbsp;</font></td></tr></table>"
00326         "<p><p>"
00327         "<h1>404 - File not found</h1>"
00328         "<div><p><strong>The requested URL was not found on this server."
00329         "</strong></p>"
00330         "</div></body>"
00331         "</html>"
00332         "\r\n"));
00333     res.append(header.toString());
00334     return res;
00335 }
00336 
00337 HttpServer::HttpServer(QObject* parent)
00338   : QTcpServer(parent), disabled(false)
00339 {
00340 }
00341 
00342 void HttpServer::incomingConnection(int socket)
00343 {
00344     if (disabled)
00345         return;
00346 
00347     // When a new client connects the server constructs a QTcpSocket and all
00348     // communication with the client is done over this QTcpSocket. QTcpSocket
00349     // works asynchronously, this means that all the communication is done
00350     // in the two slots readClient() and discardClient().
00351     QTcpSocket* s = new QTcpSocket(this);
00352     connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
00353     connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
00354     s->setSocketDescriptor(socket);
00355 }
00356 
00357 void HttpServer::pause()
00358 {
00359     disabled = true;
00360 }
00361 
00362 void HttpServer::resume()
00363 {
00364     disabled = false;
00365 }
00366 
00367 void HttpServer::readClient()
00368 {
00369     if (disabled)
00370         return;
00371 
00372     // This slot is called when the client sent data to the server. The
00373     // server looks if it was a GET request and  sends back the 
00374     // corresponding HTML document from the ZIP file.
00375     QTcpSocket* socket = (QTcpSocket*)sender();
00376     if (socket->canReadLine()) {
00377         QString request = QString::fromAscii(socket->readLine());
00378         QHttpRequestHeader header(request);
00379         if (header.method() == QLatin1String("GET")) {
00380             socket->write(help.loadResource(header.path()));
00381             socket->close();
00382             if (socket->state() == QTcpSocket::UnconnectedState) {
00383                 //mark the socket for deletion but do not destroy immediately
00384                 socket->deleteLater();
00385             }
00386         }
00387     }
00388 }
00389 
00390 void HttpServer::discardClient()
00391 {
00392     QTcpSocket* socket = (QTcpSocket*)sender();
00393     socket->deleteLater();
00394 }
00395 
00396 // --------------------------------------------------------------------
00397 
00398 /* TRANSLATOR Gui::StdCmdPythonHelp */
00399 
00400 StdCmdPythonHelp::StdCmdPythonHelp()
00401   : Command("Std_PythonHelp"), server(0)
00402 {
00403     sGroup        = QT_TR_NOOP("Tools");
00404     sMenuText     = QT_TR_NOOP("Python Modules");
00405     sToolTipText  = QT_TR_NOOP("Opens a browser to show the Python modules");
00406     sWhatsThis    = QT_TR_NOOP("Opens a browser to show the Python modules");
00407     sStatusTip    = QT_TR_NOOP("Opens a browser to show the Python modules");
00408 }
00409 
00410 StdCmdPythonHelp::~StdCmdPythonHelp()
00411 {
00412     if (server) {
00413         server->close();
00414         delete server;
00415     }
00416 }
00417 
00418 void StdCmdPythonHelp::activated(int iMsg)
00419 {
00420     // try to open a connection over this port
00421     qint16 port = 7465;
00422     if (!this->server)
00423         this->server = new HttpServer();
00424 
00425     // if server is not yet running try to open one
00426     if (this->server->isListening() || 
00427         this->server->listen(QHostAddress(QHostAddress::LocalHost), port)) {
00428         // okay the server is running, now we try to open the system internet browser
00429         bool failed = true;
00430 
00431         // The webbrowser Python module allows to start the system browser in an 
00432         // OS-independent way
00433         Base::PyGILStateLocker lock;
00434         PyObject* module = PyImport_ImportModule("webbrowser");
00435         if (module) {
00436             // get the methods dictionary and search for the 'open' method
00437             PyObject* dict = PyModule_GetDict(module);
00438             PyObject* func = PyDict_GetItemString(dict, "open");
00439             if (func) {
00440                 char szBuf[201];
00441                 snprintf(szBuf, 200, "http://localhost:%d", port);
00442                 PyObject* args = Py_BuildValue("(s)", szBuf);
00443                 PyObject* result = PyEval_CallObject(func,args);
00444                 if (result)
00445                     failed = false;
00446         
00447                 // decrement the args and module reference
00448                 Py_XDECREF(result);
00449                 Py_DECREF(args);
00450                 Py_DECREF(module);
00451             }
00452         }
00453 
00454         // print error message on failure
00455         if (failed) {
00456             QMessageBox::critical(Gui::getMainWindow(), QObject::tr("No Browser"), 
00457                 QObject::tr("Unable to open your browser.\n\n"
00458                 "Please open a browser window and type in: http://localhost:%1.").arg(port));
00459         }
00460     }
00461     else {
00462         QMessageBox::critical(Gui::getMainWindow(), QObject::tr("No Server"), 
00463             QObject::tr("Unable to start the server to port %1: %2.").arg(port).arg(server->errorString()));
00464     }
00465 }
00466 
00467 bool Gui::OpenURLInBrowser(const char * URL)
00468 {
00469     // The webbrowser Python module allows to start the system browser in an OS-independent way
00470     bool failed = true;
00471     Base::PyGILStateLocker lock;
00472     PyObject* module = PyImport_ImportModule("webbrowser");
00473     if (module) {
00474         // get the methods dictionary and search for the 'open' method
00475         PyObject* dict = PyModule_GetDict(module);
00476         PyObject* func = PyDict_GetItemString(dict, "open");
00477         if (func) {
00478             PyObject* args = Py_BuildValue("(s)", URL);
00479             PyObject* result = PyEval_CallObject(func,args);
00480             if (result)
00481                 failed = false;
00482         
00483             // decrement the args and module reference
00484             Py_XDECREF(result);
00485             Py_DECREF(args);
00486             Py_DECREF(module);
00487         }
00488     } 
00489 
00490     // print error message on failure
00491     if (failed) {
00492         QMessageBox::critical(Gui::getMainWindow(), QObject::tr("No Browser"), 
00493             QObject::tr("Unable to open your system browser."));
00494         return false;
00495     }
00496   
00497     return true;
00498 }
00499 
00500 
00501 #include "moc_OnlineDocumentation.cpp"
00502 

Generated on Wed Nov 23 19:00:24 2011 for FreeCAD by  doxygen 1.6.1