Page 1 of 1

Modifying GUI to use kchmviewer if available

PostPosted: 14. Oct 2015, 11:17
by mYse|f
When accessing the documentation, the OSE version opens UserManual.pdf in whatever is the default PDF viewer in the system (e.g. Evince) while the official version opens VirtualBox.chm in an older (GPLv2) version of kchmviewer, that comes bundled in the package. Since my build contains documentation in both PDF and CHM formats (but no kchmviewer), I would like to adapt VirtualBox's code to check whether kchmviewer is installed (e.g. using "which kchmviewer"), and if not, fall back to UserManual.pdf. Currently I have no C++ skills and would be grateful if the developers would show me how to properly do this.

Here's the relevant code snippet from VirtualBox-5.0.6/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.cpp:
Code: Select all   Expand viewCollapse view
void UIMessageCenter::sltShowHelpHelpDialog()
{
#ifndef VBOX_OSE
    /* For non-OSE version we just open it: */
    sltShowUserManual(vboxGlobal().helpFile());
#else /* #ifndef VBOX_OSE */
    /* For OSE version we have to check if it present first: */
    QString strUserManualFileName1 = vboxGlobal().helpFile();
    QString strShortFileName = QFileInfo(strUserManualFileName1).fileName();
    QString strUserManualFileName2 = QDir(vboxGlobal().homeFolder()).absoluteFilePath(strShortFileName);
    /* Show if user manual already present: */
    if (QFile::exists(strUserManualFileName1))
        sltShowUserManual(strUserManualFileName1);
    else if (QFile::exists(strUserManualFileName2))
        sltShowUserManual(strUserManualFileName2);
    /* If downloader is running already: */
    else if (UIDownloaderUserManual::current())
    {
        /* Just show network access manager: */
        gNetworkManager->show();
    }
    /* Else propose to download user manual: */
    else if (cannotFindUserManual(strUserManualFileName1))
    {
        /* Create User Manual downloader: */
        UIDownloaderUserManual *pDl = UIDownloaderUserManual::create();
        /* After downloading finished => show User Manual: */
        connect(pDl, SIGNAL(sigDownloadFinished(const QString&)), this, SLOT(sltShowUserManual(const QString&)));
        /* Start downloading: */
        pDl->start();
    }
#endif /* #ifdef VBOX_OSE */
}

void UIMessageCenter::sltShowUserManual(const QString &strLocation)
{
#if defined (Q_WS_WIN32)
    HtmlHelp(GetDesktopWindow(), strLocation.utf16(), HH_DISPLAY_TOPIC, NULL);
#elif defined (Q_WS_X11)
# ifndef VBOX_OSE
    char szViewerPath[RTPATH_MAX];
    int rc;
    rc = RTPathAppPrivateArch(szViewerPath, sizeof(szViewerPath));
    AssertRC(rc);
    QProcess::startDetached(QString(szViewerPath) + "/kchmviewer", QStringList(strLocation));
# else /* #ifndef VBOX_OSE */
    vboxGlobal().openURL("file://" + strLocation);
# endif /* #ifdef VBOX_OSE */
#elif defined (Q_WS_MAC)
    vboxGlobal().openURL("file://" + strLocation);
#endif
}

…and also VBoxGlobal.cpp:
Code: Select all   Expand viewCollapse view
QString VBoxGlobal::helpFile() const
{
#if defined (Q_WS_WIN32)
    const QString name = "VirtualBox";
    const QString suffix = "chm";
#elif defined (Q_WS_MAC)
    const QString name = "UserManual";
    const QString suffix = "pdf";
#elif defined (Q_WS_X11)
# if defined VBOX_OSE
    const QString name = "UserManual";
    const QString suffix = "pdf";
# else
    const QString name = "VirtualBox";
    const QString suffix = "chm";
# endif
#endif
    /* Where are the docs located? */
    char szDocsPath[RTPATH_MAX];
    int rc = RTPathAppDocs (szDocsPath, sizeof (szDocsPath));
    AssertRC (rc);
    /* Make sure that the language is in two letter code.
     * Note: if languageId() returns an empty string lang.name() will
     * return "C" which is an valid language code. */
    QLocale lang (VBoxGlobal::languageId());

    /* Construct the path and the filename */
    QString manual = QString ("%1/%2_%3.%4").arg (szDocsPath)
                                            .arg (name)
                                            .arg (lang.name())
                                            .arg (suffix);
    /* Check if a help file with that name exists */
    QFileInfo fi (manual);
    if (fi.exists())
        return manual;

    /* Fall back to the standard */
    manual = QString ("%1/%2.%4").arg (szDocsPath)
                                 .arg (name)
                                 .arg (suffix);
    return manual;
}

Thank you in advance.

EDIT: I added the missing piece of code that I previously overlooked.
EDIT 2: Also added code from VBoxGlobal.cpp that defines the filename. I guess this is the part that I have to change.

Re: Modifying GUI to use kchmviewer if available

PostPosted: 16. Oct 2015, 04:43
by mYse|f
I've tried to replace the line
# ifndef VBOX_OSE
with
# if (system("which kchmviewer") != 0)
as pointed out here, but I'm getting the following error message (screenshot):
Code: Select all   Expand viewCollapse view
/media/trisquel/NewVirtualDisk1/VirtualBox-5.0.6/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.cpp:2678:13: error: missing binary operator before token "("
 # if (system("which kchmviewer") != 0)
             ^
kmk: *** [/media/trisquel/NewVirtualDisk1/VirtualBox-5.0.6/out/debian/builddir/obj/VirtualBox/src/globals/UIMessageCenter.o] Error 1

Can anyone help me out?

Re: Modifying GUI to use kchmviewer if available

PostPosted: 16. Oct 2015, 13:53
by Perryg
OSE builds typically disable the build of the kchmviewer. Add the following to your LocalConfig.kmk
Code: Select all   Expand viewCollapse view
VBOX_WITH_KCHMVIEWER = 1

Also when I built with the viewer enabled I needed to have wine installed to do so. There is the real problem. Newer versions of wine don't work. It had to be a really old one that you just can not find any more.

Re: Modifying GUI to use kchmviewer if available

PostPosted: 16. Oct 2015, 16:26
by mYse|f
Thanks, but this isn't what I'm trying to do. I'm not trying to include kchmviewer in the package, instead, I want VirtualBox to make use of it if it's available in the system. Also, I'm building the documentation separately before VirtualBox using a different CHM compiler so the Wine issue doesn't affect me.

Re: Modifying GUI to use kchmviewer if available

PostPosted: 16. Oct 2015, 16:30
by Perryg
Very well. Do let us know how it turns out.

Re: Modifying GUI to use kchmviewer if available

PostPosted: 16. Oct 2015, 16:50
by mYse|f
I will since I'm asking here :). Just out of curiosity, how did you managed to build kchmviewer along with VirtualBox when the sources are not included in the tarball?

Re: Modifying GUI to use kchmviewer if available

PostPosted: 17. Oct 2015, 20:49
by mYse|f
I updated my initial post to reflect my findings. I also tried to ask for help on the #vbox and #vbox-dev IRC channels respectively, but haven't received any.

According to the explanation on Stack Overflow, the problem I'm encountering is a preprocessor error which "occur when the preprocessor encounters invalid syntax while trying to evaluate an expression in a #if or #endif directive." I guess this means that I cannot place system calls after #if. But then how am I supposed to do this? :?

Re: Modifying GUI to use kchmviewer if available

PostPosted: 18. Oct 2015, 00:53
by mYse|f
I have solved the preprocessor problem by removing the hashtags from the directives and adding brackets:
Code: Select all   Expand viewCollapse view
if (system("which kchmviewer") != 0)
{
    const QString name = "UserManual";
    const QString suffix = "pdf";
}
else
{
    const QString name = "VirtualBox";
    const QString suffix = "chm";
}

However, the compiler now complains that the specific constants haven't been declared (screenshot):
Code: Select all   Expand viewCollapse view
/media/trisquel/NewVirtualDisk1/VirtualBox-5.0.6/src/VBox/Frontends/VirtualBox/src/globals/VBoxGlobal.cpp: In member function ‘QString VBoxGlobal::helpFile() const’:
/media/trisquel/NewVirtualDisk1/VirtualBox-5.0.6/src/VBox/Frontends/VirtualBox/src/globals/VBoxGlobal.cpp:2491:51: error: ‘name’ was not declared in this scope
                                             .arg (name)
                                                   ^
/media/trisquel/NewVirtualDisk1/VirtualBox-5.0.6/src/VBox/Frontends/VirtualBox/src/globals/VBoxGlobal.cpp:2493:51: error: ‘suffix’ was not declared in this scope
                                             .arg (suffix);
                                                   ^
kmk: *** [/media/trisquel/NewVirtualDisk1/VirtualBox-5.0.6/out/debian/builddir/obj/VirtualBox/src/globals/VBoxGlobal.o] Error 1

Guess I'm giving up.

Re: Modifying GUI to use kchmviewer if available

PostPosted: 22. Oct 2015, 22:14
by mYse|f
UPDATE: http://stackoverflow.com/questions/3322 ... -available.

Any help on solving this problem is welcome.