GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
resource-manager.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2011-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <algorithm>
31 #include <string>
32 
33 #include <QDir>
34 #include <QFile>
35 #include <QFontComboBox>
36 #include <QFontDatabase>
37 #include <QLibraryInfo>
38 #include <QMessageBox>
39 #include <QNetworkProxy>
40 #if defined (HAVE_QSTANDARDPATHS)
41 # include <QStandardPaths>
42 #else
43 # include <QDesktopServices>
44 #endif
45 
46 #include <QTextCodec>
47 
48 #include "QTerminal.h"
49 #include "gui-preferences-ed.h"
50 #include "gui-preferences-global.h"
51 #include "octave-qobject.h"
52 #include "resource-manager.h"
53 #include "variable-editor.h"
54 #include "workspace-model.h"
55 
56 #include "file-ops.h"
57 #include "localcharset-wrapper.h"
58 #include "oct-env.h"
59 
60 #include "defaults.h"
61 #include "error.h"
62 #include "help.h"
63 
64 namespace octave
65 {
66  static QString
68  {
69  std::string dsf = sys::env::getenv ("OCTAVE_DEFAULT_QT_SETTINGS");
70 
71  if (dsf.empty ())
72  dsf = (config::oct_etc_dir ()
74  + "default-qt-settings");
75 
76  return QString::fromStdString (dsf);
77  }
78 
80  : m_settings_directory (), m_settings_file (), m_settings (nullptr),
81  m_default_settings (nullptr), m_temporary_files ()
82  {
83  // Let gui_settings decide where to put the ini file with gui preferences
85  = new gui_settings (QSettings::IniFormat, QSettings::UserScope,
86  "octave", "octave-gui");
87 
88  m_settings_file = m_default_settings->fileName ();
89 
90  QFileInfo sfile (m_settings_file);
91  m_settings_directory = sfile.absolutePath ();
92 
93  QString xdg_config_home
94  = QString::fromLocal8Bit (qgetenv ("XDG_CONFIG_HOME"));
95 
96  if ((! sfile.exists ()) && xdg_config_home.isEmpty ())
97  {
98  // File does not exist yet: Look for a settings file at the old
99  // location ($HOME/.config/octave/qt-settings) for impoting all
100  // available keys into the new settings file.
101  // Do not look for an old settings file if XDG_CONFIG_HOME is set,
102  // since then a nonexistent new settings file does not necessarily
103  // indicate a first run of octave with new config file locations.
104 #if defined (HAVE_QSTANDARDPATHS)
105  QString home_path
106  = QStandardPaths::writableLocation (QStandardPaths::HomeLocation);
107 #else
108  QString home_path
109  = QDesktopServices::storageLocation (QDesktopServices::HomeLocation);
110 #endif
111 
112  QString old_settings_directory = home_path + "/.config/octave";
113  QString old_settings_file = old_settings_directory + "/qt-settings";
114 
115  QFile ofile (old_settings_file);
116 
117  if (ofile.exists ())
118  {
119  // Old settings file exists; create a gui_settings object related
120  // to it and copy all available keys to the new settings
121  gui_settings old_settings (old_settings_file, QSettings::IniFormat);
122 
123  QStringList keys = old_settings.allKeys ();
124  for (int i = 0; i < keys.count(); i++)
125  m_default_settings->setValue (keys.at(i),
126  old_settings.value(keys.at(i)));
127 
128  m_default_settings->sync (); // Done, make sure keys are written
129  }
130  }
131  }
132 
134  {
135  delete m_settings;
136  delete m_default_settings;
137 
138  for (int i = m_temporary_files.count () - 1; i >=0; i--)
140  }
141 
143  {
144  // get environment variable for the locale dir (e.g. from run-octave)
145  std::string dldir = sys::env::getenv ("OCTAVE_LOCALE_DIR");
146  if (dldir.empty ())
147  dldir = config::oct_locale_dir (); // env-var empty, load the default location
148  return QString::fromStdString (dldir);
149  }
150 
151  void resource_manager::config_translators (QTranslator *qt_tr,
152  QTranslator *qsci_tr,
153  QTranslator *gui_tr)
154  {
155  bool loaded;
156 
157  QString qt_trans_dir
158  = QLibraryInfo::location (QLibraryInfo::TranslationsPath);
159 
160  QString language = "SYSTEM"; // take system language per default
161 
162  // FIXME: can we somehow ensure that the settings object will always
163  // be initialize and valid?
164 
165  if (m_settings)
166  {
167  // get the locale from the settings if already available
168  language = m_settings->value (global_language.key,
169  global_language.def).toString ();
170  }
171 
172  // load the translations depending on the settings
173  if (language == "SYSTEM")
174  {
175  // get the system locale and pass it to the translators for loading
176  // the suitable translation files
177  QLocale sys_locale = QLocale::system ();
178 
179  qt_tr->load (sys_locale, "qt", "_", qt_trans_dir);
180  qsci_tr->load (sys_locale, "qscintilla", "_", qt_trans_dir);
181  gui_tr->load (sys_locale, "", "", get_gui_translation_dir ());
182  }
183  else
184  {
185  // load the translation files depending on the given locale name
186  loaded = qt_tr->load ("qt_" + language, qt_trans_dir);
187  if (! loaded) // try lower case
188  qt_tr->load ("qt_" + language.toLower (), qt_trans_dir);
189 
190  loaded = qsci_tr->load ("qscintilla_" + language, qt_trans_dir);
191  if (! loaded) // try lower case
192  qsci_tr->load ("qscintilla_" + language.toLower (), qt_trans_dir);
193 
194  gui_tr->load (language, get_gui_translation_dir ());
195  }
196 
197  }
198 
200  {
201  return m_settings;
202  }
203 
205  {
206  return m_default_settings;
207  }
208 
210  {
211  return m_settings_directory;
212  }
213 
215  {
216  return m_settings_file;
217  }
218 
220  {
221  QString default_family;
222 
223 #if defined (Q_OS_MAC)
224  // Use hard coded default on macOS, since selection of fixed width
225  // default font is unreliable (see bug #59128).
226 
227  // Get all available fixed width fonts via a font combobox
228  QFontComboBox font_combo_box;
229  font_combo_box.setFontFilters (QFontComboBox::MonospacedFonts);
230  QStringList fonts;
231 
232  for (int index = 0; index < font_combo_box.count(); index++)
233  fonts << font_combo_box.itemText(index);
234 
235  // Test for macOS default fixed width font
236  if (fonts.contains (global_mono_font.def.toString ()))
237  default_family = global_mono_font.def.toString ();
238 #endif
239 
240  // If default font is still empty (on all other platforms or
241  // if macOS default font is not available): use QFontDatabase
242  if (default_family.isEmpty ())
243  {
244 #if defined (HAVE_QFONTDATABASE_SYSTEMFONT)
245  // Get the system's default monospaced font
246  QFont fixed_font = QFontDatabase::systemFont (QFontDatabase::FixedFont);
247  default_family = fixed_font.defaultFamily ();
248 #elif defined (HAVE_QFONT_MONOSPACE)
249  QFont fixed_font;
250  fixed_font.setStyleHint (QFont::Monospace);
251  default_family = fixed_font.defaultFamily ();
252 #else
253  default_family = global_font_family;
254 #endif
255  }
256 
257  // Test env variable which has preference
258  std::string env_default_family = sys::env::getenv ("OCTAVE_DEFAULT_FONT");
259  if (! env_default_family.empty ())
260  default_family = QString::fromStdString (env_default_family);
261 
262  return default_family;
263  }
264 
266  {
267  QString default_family = get_default_font_family ();
268 
269  if (! QFile::exists (m_settings_file))
270  {
271  QDir ("/").mkpath (m_settings_directory);
272  QFile qt_settings (default_qt_settings_file ());
273 
274  if (! qt_settings.open (QFile::ReadOnly))
275  return;
276 
277  QTextStream in (&qt_settings);
278  QString settings_text = in.readAll ();
279  qt_settings.close ();
280 
281  default_family = get_default_font_family ();
282 
283  QString default_font_size = "10";
284 
285  std::string env_default_font_size
286  = sys::env::getenv ("OCTAVE_DEFAULT_FONT_SIZE");
287 
288  if (! env_default_font_size.empty ())
289  default_font_size = QString::fromStdString (env_default_font_size);
290 
291  // Get the default custom editor
292 #if defined (Q_OS_WIN32)
293  QString custom_editor = "notepad++ -n%l %f";
294 #else
295  QString custom_editor = "emacs +%l %f";
296 #endif
297 
298  std::string env_default_editor
299  = sys::env::getenv ("OCTAVE_DEFAULT_EDITOR");
300 
301  if (! env_default_editor.empty ())
302  custom_editor = QString::fromStdString (env_default_editor);
303 
304  // Replace placeholders
305  settings_text.replace ("__default_custom_editor__", custom_editor);
306  settings_text.replace ("__default_font__", default_family);
307  settings_text.replace ("__default_font_size__", default_font_size);
308 
309  QFile user_settings (m_settings_file);
310 
311  if (! user_settings.open (QIODevice::WriteOnly))
312  return;
313 
314  QTextStream out (&user_settings);
315 
316  out << settings_text;
317 
318  user_settings.close ();
319  }
320 
322 
323  // Write the default monospace font into the settings for later use by
324  // console and editor as fallbacks of their font preferences.
325  if (m_settings)
326  m_settings->setValue (global_mono_font.key, default_family);
327 
328  }
329 
330  void resource_manager::set_settings (const QString& file)
331  {
332  delete m_settings;
333  m_settings = new gui_settings (file, QSettings::IniFormat);
334 
335  if (! (QFile::exists (m_settings->fileName ())
336  && m_settings->isWritable ()
337  && m_settings->status () == QSettings::NoError))
338  {
339  QString msg
340  = QString (QT_TR_NOOP ("The settings file\n%1\n"
341  "does not exist and can not be created.\n"
342  "Make sure you have read and write permissions to\n%2\n\n"
343  "Octave GUI must be closed now."));
344 
345  QMessageBox::critical (nullptr,
346  QString (QT_TR_NOOP ("Octave Critical Error")),
347  msg.arg (get_settings_file ()).arg (get_settings_directory ()));
348 
349  exit (1);
350  }
351  }
352 
353  bool resource_manager::update_settings_key (const QString& old_key,
354  const QString& new_key)
355  {
356  if (m_settings->contains (old_key))
357  {
358  QVariant preference = m_settings->value (old_key);
359  m_settings->setValue (new_key, preference);
360  m_settings->remove (old_key);
361  return true;
362  }
363 
364  return false;
365  }
366 
368  {
369  return ! QFile::exists (m_settings_file);
370  }
371 
373  {
374  if (m_settings)
375  {
376  QNetworkProxy::ProxyType proxyType = QNetworkProxy::NoProxy;
377 
379  {
380  QString proxyTypeString
382 
383  if (proxyTypeString == "Socks5Proxy")
384  proxyType = QNetworkProxy::Socks5Proxy;
385  else if (proxyTypeString == "HttpProxy")
386  proxyType = QNetworkProxy::HttpProxy;
387  }
388 
389  QNetworkProxy proxy;
390 
391  proxy.setType (proxyType);
392  proxy.setHostName (m_settings->value (global_proxy_host.key,
393  global_proxy_host.def).toString ());
394  proxy.setPort (m_settings->value (global_proxy_port.key,
395  global_proxy_port.def).toInt ());
396  proxy.setUser (m_settings->value (global_proxy_user.key,
397  global_proxy_user.def).toString ());
398  proxy.setPassword (m_settings->value (global_proxy_pass.key,
399  global_proxy_pass.def).toString ());
400 
401  QNetworkProxy::setApplicationProxy (proxy);
402  }
403  else
404  {
405  // FIXME: Is this an error? If so, what should we do?
406  }
407  }
408 
409  QIcon resource_manager::icon (const QString& icon_name, bool fallback)
410  {
411  // If system icon theme is not desired, take own icon files
412  if (! m_settings->value (global_icon_theme).toBool ())
413  return QIcon (":/actions/icons/" + icon_name + ".png");
414 
415  // Use system icon theme with own files as fallback except when the
416  // fallback is explicitly disabled (fallback=false)
417  if (fallback)
418  return QIcon::fromTheme (icon_name,
419  QIcon (":/actions/icons/" + icon_name + ".png"));
420  else
421  return QIcon::fromTheme (icon_name);
422  }
423 
424  // get a list of all available encodings
425  void resource_manager::get_codecs (QStringList *codecs)
426  {
427  // get the codec name for each mib
428  QList<int> all_mibs = QTextCodec::availableMibs ();
429  for (auto mib : all_mibs)
430  {
431  QTextCodec *c = QTextCodec::codecForMib (mib);
432  codecs->append (c->name ().toUpper ());
433  }
434 
435  // Append SYSTEM
436  codecs->append (QString ("SYSTEM (") +
437  QString (octave_locale_charset_wrapper ()).toUpper () +
438  QString (")"));
439 
440  // Clean up and sort list of codecs
441  codecs->removeDuplicates ();
442  std::sort (codecs->begin (), codecs->end ());
443  }
444 
445  // initialize a given combo box with available text encodings
446  void resource_manager::combo_encoding (QComboBox *combo,
447  const QString& current)
448  {
449  QStringList all_codecs;
450  get_codecs (&all_codecs);
451 
452  // get the value from the settings file if no current encoding is given
453  QString enc = current;
454 
455  // Check for valid codec for the default. If this fails, "SYSTEM" (i.e.
456  // locale_charset) will be chosen.
457  // FIXME: The default is "SYSTEM" on all platforms. So can this fallback
458  // logic be removed completely?
459  bool default_exists = false;
460  bool show_system = false;
461  if (ed_default_enc.def.toString ().startsWith ("SYSTEM"))
462  show_system = true;
463  else if (QTextCodec::codecForName (ed_default_enc.def.toString ().toLatin1 ()))
464  default_exists = true;
465 
466  QString default_enc =
467  QString ("SYSTEM (") +
468  QString (octave_locale_charset_wrapper ()).toUpper () + QString (")");
469 
470  if (enc.isEmpty ())
471  {
472  enc = m_settings->value (ed_default_enc).toString ();
473 
474  if (enc.isEmpty ()) // still empty?
475  {
476  if (default_exists)
477  enc = ed_default_enc.def.toString ();
478  else
479  enc = default_enc;
480  }
481  }
482 
483  // fill the combo box
484  for (const auto& c : all_codecs)
485  combo->addItem (c);
486 
487  // prepend the default item
488  combo->insertSeparator (0);
489  if (show_system || ! default_exists)
490  combo->insertItem (0, default_enc);
491  else
492  combo->insertItem (0, ed_default_enc.def.toString ());
493 
494  // select the default or the current one
495  int idx = combo->findText (enc, Qt::MatchExactly);
496  if (idx >= 0)
497  combo->setCurrentIndex (idx);
498  else
499  combo->setCurrentIndex (0);
500 
501  combo->setMaxVisibleItems (12);
502  }
503 
504  QPointer<QTemporaryFile>
505  resource_manager::create_tmp_file (const QString& extension,
506  const QString& contents)
507  {
508  QString ext = extension;
509  if ((! ext.isEmpty ()) && (! ext.startsWith ('.')))
510  ext = QString (".") + ext;
511 
512  // Create octave dir within temp. dir
513  QString tmp_dir = QDir::tempPath () + QDir::separator() + "octave";
514  QDir::temp ().mkdir ("octave");
515 
516  // Create temp. file
517  QPointer<QTemporaryFile> tmp_file
518  = new QTemporaryFile (tmp_dir + QDir::separator() +
519  "octave_XXXXXX" + ext, this);
520 
521  if (tmp_file->open ())
522  {
523  tmp_file->write (contents.toUtf8 ());
524  tmp_file->close ();
525 
526  m_temporary_files << tmp_file;
527  }
528 
529  return tmp_file;
530  }
531 
532  void resource_manager::remove_tmp_file (QPointer<QTemporaryFile> tmp_file)
533  {
534  if (tmp_file)
535  {
536  if (tmp_file->exists ())
537  tmp_file->remove ();
538 
539  m_temporary_files.removeAll (tmp_file);
540  }
541  }
542 }
QVariant value(const gui_pref &pref) const
Definition: gui-settings.h:63
QString get_default_font_family(void)
void combo_encoding(QComboBox *combo, const QString &current=QString())
gui_settings * get_settings(void) const
gui_settings * get_default_settings(void) const
void get_codecs(QStringList *codecs)
gui_settings * m_default_settings
QString get_gui_translation_dir(void)
bool is_first_run(void) const
QList< QTemporaryFile * > m_temporary_files
QPointer< QTemporaryFile > create_tmp_file(const QString &extension=QString(), const QString &contents=QString())
QIcon icon(const QString &icon_name, bool fallback=true)
void remove_tmp_file(QPointer< QTemporaryFile > tmp_file)
bool update_settings_key(const QString &new_key, const QString &old_key)
QString get_settings_directory(void)
void set_settings(const QString &file)
void config_translators(QTranslator *qt_tr, QTranslator *qsci_tr, QTranslator *gui_tr)
static std::string getenv(const std::string &name)
Definition: oct-env.cc:271
const gui_pref ed_default_enc("editor/default_encoding", QVariant(QString("SYSTEM (")+QString(octave_locale_charset_wrapper()).toUpper()+QString(")")))
const gui_pref global_use_proxy("useProxyServer", QVariant(false))
const gui_pref global_mono_font("monospace_font", global_font_family)
const gui_pref global_proxy_pass("proxyPassword", QVariant(QString()))
const gui_pref global_proxy_host("proxyHostName", QVariant(QString()))
const gui_pref global_proxy_type("proxyType", QVariant(QString()))
const gui_pref global_proxy_user("proxyUserName", QVariant(QString()))
const gui_pref global_icon_theme("use_system_icon_theme", QVariant(true))
const QString global_font_family
const gui_pref global_proxy_port("proxyPort", QVariant(80))
const gui_pref global_language("language", QVariant("SYSTEM"))
const char * octave_locale_charset_wrapper(void)
QString fromStdString(const std::string &s)
std::string oct_locale_dir(void)
Definition: defaults.cc:371
std::string oct_etc_dir(void)
Definition: defaults.cc:339
std::string dir_sep_str(void)
Definition: file-ops.cc:243
static QString default_qt_settings_file(void)
const QString key
const QVariant def