view libgui/src/documentation.cc @ 25460:627d6bde9b8d

solve installation info initialization problem differently Back out changesets 893344cee100 and 69fc8935020b. * defaults.h, defaults.cc: Instead of storing installation info like include_dir, data_dir, info_dir, etc., in file-scope static variables or a class object owned by the interpreter, store the initialized values as constants inside the functions that return them. This way they are initialized on demand and we don't have to worry about initialization order of static data. It's OK for these to be static because even though they may require some computation to intialize (looking at environment variables or substituting the value of OCTAVE_HOME), they are constants for any given installation of Octave and will be the same for any instantiation of the interpreter. Only include defaults.h in files that actually need it.
author John W. Eaton <jwe@octave.org>
date Tue, 12 Jun 2018 13:03:04 -0400
parents 893344cee100
children 517c73173011
line wrap: on
line source

/*

Copyright (C) 2018 Torsten <mttl@maibox.org>

This file is part of Octave.

Octave is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Octave is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING.  If not, see
<https://www.gnu.org/licenses/>.

*/

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include "defaults.h"
#include "file-ops.h"
#include "oct-env.h"

#include <QApplication>
#include <QCompleter>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QHelpContentWidget>
#include <QHelpIndexWidget>
#include <QHelpSearchEngine>
#include <QHelpSearchQueryWidget>
#include <QHelpSearchResultWidget>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QTabWidget>
#include <QToolButton>
#include <QVBoxLayout>

#include "documentation.h"
#include "resource-manager.h"
#include "shortcut-manager.h"

namespace octave
{
  // The documentation splitter, which is the main widget
  // of the doc dock widget
  documentation::documentation (QWidget *p)
    : QSplitter (Qt::Horizontal, p),
      m_show_shortcut (new QShortcut (p)),
      m_findnext_shortcut (new QShortcut (p)),
      m_findprev_shortcut (new QShortcut (p))
  {
    // Get original collection
    QString collection = getenv ("OCTAVE_QTHELP_COLLECTION");
    if (collection.isEmpty ())
      collection = QString::fromStdString (config::oct_doc_dir ()
                                           + sys::file_ops::dir_sep_str ()
                                           + "octave_interpreter.qhc");

    // Setup the help engine with the original collection, use a writable copy
    // of the original collection and load the help data
    m_help_engine = new QHelpEngine (collection, this);

    QString tmpdir = QDir::tempPath();
    m_collection
      = QString::fromStdString (sys::tempnam (tmpdir.toStdString (),
                                              "oct-qhelp-"));

    if (m_help_engine->copyCollectionFile (m_collection))
      m_help_engine->setCollectionFile (m_collection);
    else
      QMessageBox::warning (this, tr ("Octave Documentation"),
                            tr ("Could not copy help collection to temporary\n"
                                "file. Search capabilities may be affected.\n"
                                "%1").arg (m_help_engine->error ()));

    connect(m_help_engine, SIGNAL(setupFinished()),
            m_help_engine->searchEngine(), SLOT(indexDocumentation()));

    if (! m_help_engine->setupData())
      {
        QMessageBox::warning (this, tr ("Octave Documentation"),
                              tr ("Could not setup the data required for the\n"
                                  "documentation viewer. Only help texts in\n"
                                  "the Command Window will be available."));
        if (m_help_engine)
          delete m_help_engine;
        m_help_engine = 0;
        return;
      }

    // The browser
    QWidget *browser_find = new QWidget (this);
    m_doc_browser = new documentation_browser (m_help_engine, browser_find);
    connect (m_doc_browser, SIGNAL (cursorPositionChanged (void)),
             this, SLOT(handle_cursor_position_change (void)));

    QWidget *find_footer = new QWidget (browser_find);
    QLabel *find_label = new QLabel (tr ("Find:"), find_footer);
    m_find_line_edit = new QLineEdit (find_footer);
    connect (m_find_line_edit, SIGNAL (returnPressed (void)),
             this, SLOT(find_forward (void)));
    connect (m_find_line_edit, SIGNAL (textEdited (const QString&)),
             this, SLOT(find_forward_from_anchor (const QString&)));
    QToolButton *forward_button = new QToolButton (find_footer);
    forward_button->setText (tr ("Search forward"));
    forward_button->setToolTip (tr ("Search forward"));
    forward_button->setIcon (resource_manager::icon ("go-down"));
    connect (forward_button, SIGNAL (pressed (void)),
             this, SLOT(find_forward (void)));
    QToolButton *backward_button = new QToolButton (find_footer);
    backward_button->setText (tr ("Search backward"));
    backward_button->setToolTip (tr ("Search backward"));
    backward_button->setIcon (resource_manager::icon ("go-up"));
    connect (backward_button, SIGNAL (pressed (void)),
             this, SLOT(find_backward (void)));
    QHBoxLayout *h_box_find_footer = new QHBoxLayout (find_footer);
    h_box_find_footer->addWidget (find_label);
    h_box_find_footer->addWidget (m_find_line_edit);
    h_box_find_footer->addWidget (forward_button);
    h_box_find_footer->addWidget (backward_button);
    h_box_find_footer->setMargin (2);
    find_footer->setLayout (h_box_find_footer);

    QVBoxLayout *v_box_browser_find = new QVBoxLayout (browser_find);
    v_box_browser_find->addWidget (m_doc_browser);
    v_box_browser_find->addWidget (find_footer);
    browser_find->setLayout (v_box_browser_find);

    notice_settings (resource_manager::get_settings ());

    m_show_shortcut->setContext (Qt::WidgetWithChildrenShortcut);
    connect (m_show_shortcut, SIGNAL (activated (void)),
             m_find_line_edit->parentWidget (), SLOT (show (void)));
    connect (m_show_shortcut, SIGNAL (activated (void)),
             m_find_line_edit, SLOT (selectAll (void)));
    connect (m_show_shortcut, SIGNAL (activated (void)),
             m_find_line_edit, SLOT (setFocus (void)));
    QShortcut *hide_shortcut = new QShortcut (Qt::Key_Escape, p);

    hide_shortcut->setContext (Qt::WidgetWithChildrenShortcut);
    connect (hide_shortcut, SIGNAL (activated (void)),
             m_find_line_edit->parentWidget (), SLOT(hide (void)));
    connect (hide_shortcut, SIGNAL (activated (void)),
             m_doc_browser, SLOT (setFocus (void)));

    m_findnext_shortcut->setContext (Qt::WidgetWithChildrenShortcut);
    connect (m_findnext_shortcut, SIGNAL (activated (void)),
             this, SLOT(find_forward (void)));
    m_findprev_shortcut->setContext (Qt::WidgetWithChildrenShortcut);
    connect (m_findprev_shortcut, SIGNAL (activated (void)),
             this, SLOT(find_backward (void)));

    find_footer->hide ();
    m_search_anchor_position = 0;

    // Layout contents, index and search
    QTabWidget *navi = new QTabWidget (this);
    navi->setTabsClosable (false);
    navi->setMovable (true);

    // Contents
    QHelpContentWidget *content = m_help_engine->contentWidget ();
    navi->addTab (content, tr ("Contents"));

    connect(m_help_engine->contentWidget (),
            SIGNAL (linkActivated (const QUrl&)),
            m_doc_browser, SLOT(handle_index_clicked (const QUrl&)));

    // Index
    QHelpIndexWidget *index = m_help_engine->indexWidget ();

    m_filter = new QComboBox (this);
    m_filter->setToolTip (tr ("Enter text to search the indices"));
    m_filter->setEditable (true);
    m_filter->setInsertPolicy (QComboBox::NoInsert);
    m_filter->setMaxCount (10);
    m_filter->setMaxVisibleItems (10);
    m_filter->setSizeAdjustPolicy (
      QComboBox::AdjustToMinimumContentsLengthWithIcon);
    QSizePolicy sizePol (QSizePolicy::Expanding, QSizePolicy::Preferred);
    m_filter->setSizePolicy (sizePol);
    m_filter->completer ()->setCaseSensitivity (Qt::CaseSensitive);
    QLabel *filter_label = new QLabel (tr ("Search"));

    QWidget *filter_all = new QWidget (navi);
    QHBoxLayout *h_box_index = new QHBoxLayout (filter_all);
    h_box_index->addWidget (filter_label);
    h_box_index->addWidget (m_filter);
    h_box_index->setMargin (2);
    filter_all->setLayout (h_box_index);

    QWidget *index_all = new QWidget (navi);
    QVBoxLayout *v_box_index = new QVBoxLayout (index_all);
    v_box_index->addWidget (filter_all);
    v_box_index->addWidget (index);
    index_all->setLayout (v_box_index);

    navi->addTab (index_all, tr ("Index"));

    connect(m_help_engine->indexWidget (),
            SIGNAL (linkActivated (const QUrl&, const QString&)),
            m_doc_browser, SLOT(handle_index_clicked (const QUrl&,
                                                      const QString&)));

    connect (m_filter, SIGNAL (editTextChanged (const QString&)),
             this, SLOT(filter_update (const QString&)));

    connect (m_filter->lineEdit (), SIGNAL (editingFinished (void)),
             this, SLOT(filter_update_history (void)));

    // Search
    QHelpSearchEngine *search_engine = m_help_engine->searchEngine ();
    QHelpSearchQueryWidget *search = search_engine->queryWidget ();
    QHelpSearchResultWidget *result = search_engine->resultWidget ();
    QWidget *search_all = new QWidget (navi);
    QVBoxLayout *v_box_search = new QVBoxLayout (search_all);
    v_box_search->addWidget (search);
    v_box_search->addWidget (result);
    search_all->setLayout (v_box_search);
    navi->addTab (search_all, tr ("Search"));

    connect (search, SIGNAL (search (void)),
             this, SLOT(global_search (void)));

    connect (search_engine, SIGNAL (searchingStarted (void)),
             this, SLOT(global_search_started (void)));
    connect (search_engine, SIGNAL (searchingFinished (int)),
             this, SLOT(global_search_finished (int)));

    connect (search_engine->resultWidget (),
             SIGNAL (requestShowLink (const QUrl&)),
             m_doc_browser,
             SLOT(handle_index_clicked (const QUrl&)));

    // Fill the splitter
    insertWidget (0, navi);
    insertWidget (1, browser_find);
    setStretchFactor (1, 1);

    // Initial view: Contents
    m_doc_browser->setSource (QUrl (
        "qthelp://org.octave.interpreter-1.0/doc/octave.html/index.html"));
  }

  documentation::~documentation (void)
  {
    if (m_help_engine)
      delete m_help_engine;

    // Cleanup temporary file and directory
    QFile file (m_collection);
    if (file.exists ())
      {
        QFileInfo finfo (file);
        QString bname = finfo.fileName ();
        QDir dir = finfo.absoluteDir ();
        dir.setFilter (QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden);
        QStringList namefilter;
        namefilter.append ("*" + bname + "*");
        foreach (QFileInfo fi, dir.entryInfoList (namefilter))
          {
            std::string file_name = fi.absoluteFilePath ().toStdString ();
            sys::recursive_rmdir (file_name);
          }

        file.remove();
      }
  }

  void documentation::global_search (void)
  {
#if defined (HAVE_QHELPSEARCHQUERYWIDGET_SEARCHINPUT)
    QString queries
      = m_help_engine->searchEngine ()->queryWidget ()->searchInput ();
#else
    QList<QHelpSearchQuery> queries
      = m_help_engine->searchEngine ()->queryWidget ()->query ();
#endif

    m_help_engine->searchEngine ()->search (queries);
  }

  void documentation::global_search_started (void)
  {
    qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
  }

  void documentation::global_search_finished (int)
  {
    qApp->restoreOverrideCursor();
  }

  void documentation::notice_settings (const QSettings *)
  {
    shortcut_manager::shortcut (m_show_shortcut, "editor_edit:find_replace");
    shortcut_manager::shortcut (m_findnext_shortcut, "editor_edit:find_next");
    shortcut_manager::shortcut (m_findprev_shortcut, "editor_edit:find_previous");
  }

  void documentation::copyClipboard (void) { }

  void documentation::pasteClipboard (void) { }

  void documentation::selectAll (void) { }

  void documentation::load_ref (const QString& ref_name)
  {
    if (m_help_engine)
      {
        QMap<QString, QUrl> found_links
          = m_help_engine->linksForIdentifier (ref_name);
        if (found_links.count() > 0)
          m_doc_browser->setSource (found_links.constBegin().value());
      }
  }

  void documentation::filter_update (const QString& expression)
  {
    if (! m_help_engine)
      return;

    QString wildcard;
    if (expression.contains (QLatin1Char('*')))
      wildcard = expression;

    m_help_engine->indexWidget ()->filterIndices(expression, wildcard);
  }

  void documentation::filter_update_history (void)
  {
    QString text = m_filter->currentText ();   // get current text
    int index = m_filter->findText (text);     // and its actual index

    if (index > -1)
      m_filter->removeItem (index);            // remove if already existing

    m_filter->insertItem (0, text);            // (re)insert at beginning
    m_filter->setCurrentIndex (0);
  }

  void documentation::find_forward (void)
  {
    if (! m_help_engine)
      return;

    m_doc_browser->find (m_find_line_edit->text ());
    record_anchor_position ();
  }

  void documentation::find_backward (void)
  {
    if (! m_help_engine)
      return;

    m_doc_browser->find (m_find_line_edit->text (), QTextDocument::FindBackward);
    record_anchor_position ();
  }

  void documentation::find_forward_from_anchor (const QString& text)
  {
    if (! m_help_engine)
      return;

    QTextCursor textcur = m_doc_browser->textCursor ();
    textcur.setPosition (m_search_anchor_position);
    m_doc_browser->setTextCursor (textcur);
    m_doc_browser->find (text);
  }

  void documentation::record_anchor_position (void)
  {
    if (! m_help_engine)
      return;

    m_search_anchor_position = m_doc_browser->textCursor ().position ();
  }

  void documentation::handle_cursor_position_change (void)
  {
    if (! m_help_engine)
      return;

    if (m_doc_browser->hasFocus ())
      record_anchor_position ();
  }

  void documentation::registerDoc (const QString& qch)
  {
    if (m_help_engine)
      {
        QString ns = m_help_engine->namespaceName (qch);
        bool do_setup = true;
        if (m_help_engine->registeredDocumentations ().contains (ns))
          {
            if (m_help_engine->documentationFileName (ns) == qch)
              do_setup = false;
            else
              {
                m_help_engine->unregisterDocumentation (ns);
                m_help_engine->registerDocumentation (qch);
              }
          }
        else if (! m_help_engine->registerDocumentation (qch))
          {
            QMessageBox::warning (this, tr ("Octave Documentation"),
                                  tr ("Unable to register help file %1.").
                                  arg (qch));
            do_setup = false;
            return;
          }

        if (do_setup)
          m_help_engine->setupData();
      }
  }

  void documentation::unregisterDoc (const QString& qch)
  {
    QString ns = m_help_engine->namespaceName (qch);
    if (m_help_engine
        && m_help_engine->registeredDocumentations ().contains (ns)
        && m_help_engine->documentationFileName (ns) == qch)
      {
        m_help_engine->unregisterDocumentation (ns);
        m_help_engine->setupData ();
      }
  }


  // The documentation browser
  documentation_browser::documentation_browser (QHelpEngine *he, QWidget *p)
    : QTextBrowser (p), m_help_engine (he)
  { }

  documentation_browser::~documentation_browser (void)
  { }

  void documentation_browser::handle_index_clicked (const QUrl& url,
                                                    const QString&)
  {
    setSource (url);
  }

  void documentation_browser::notice_settings (const QSettings *)
  { }

  QVariant documentation_browser::loadResource (int type, const QUrl &url)
  {
    if (url.scheme () == "qthelp")
      return QVariant (m_help_engine->fileData(url));
    else
      return QTextBrowser::loadResource(type, url);
  }

}