/ src / mod-mgr.c
mod-mgr.c
  1  /*
  2    Gpredict: Real-time satellite tracking and orbit prediction program
  3  
  4    Copyright (C)  2001-2013  Alexandru Csete, OZ9AEC.
  5  
  6    Authors: Alexandru Csete
  7             Charles Suprin
  8  
  9    Comments, questions and bugreports should be submitted via
 10    http://sourceforge.net/projects/gpredict/
 11    More details can be found at the project home page:
 12  
 13    http://gpredict.oz9aec.net/
 14   
 15    This program is free software; you can redistribute it and/or modify
 16    it under the terms of the GNU General Public License as published by
 17    the Free Software Foundation; either version 2 of the License, or
 18    (at your option) any later version.
 19    
 20    This program is distributed in the hope that it will be useful,
 21    but WITHOUT ANY WARRANTY; without even the implied warranty of
 22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 23    GNU General Public License for more details.
 24    
 25    You should have received a copy of the GNU General Public License
 26    along with this program; if not, visit http://www.fsf.org/
 27  */
 28  /*
 29   * Module manager.
 30   *
 31   * The module manager is responsible for the management of opened modules.
 32   * It consist of a GtkNoteBook container where the modules are placed initially.
 33   *
 34   * The module manager is initialised with the mod_mgr_create function, which will
 35   * create the notebook widget and re-open the modules that have been open when
 36   * gpredict has been quit last time.
 37   *
 38   * To add additional modules the mod_mgr_add_module function should be used. This
 39   * function takes a fully initialised GtkSatModule (FIXME: cast to GtkWidget) and
 40   * a boolean flag indicating whether the module should be docked into the notebook
 41   * or not. Please note, that if a module is added with dock=FALSE, the caller will
 42   * have the responsibility of creating a proper container window for the module.
 43   *
 44   * Finally, when gpredict is about to exit, the state of the module manager can be
 45   * saved by calling the mod_mgr_save_state. This will save a list of open modules
 46   * so that they can be restored next time gpredict is re-opened.
 47   *
 48   * The mod-mgr maintains an internal GSList with references to the opened modules.
 49   * This allows the mod-mgr to know about both docked and undocked modules.
 50   *
 51   */
 52  #ifdef HAVE_CONFIG_H
 53  #include <build-config.h>
 54  #endif
 55  #include <glib/gi18n.h>
 56  #include <gtk/gtk.h>
 57  
 58  #include "config-keys.h"
 59  #include "compat.h"
 60  #include "gtk-sat-module.h"
 61  #include "gtk-sat-module-popup.h"
 62  #include "mod-cfg.h"
 63  #include "mod-mgr.h"
 64  #include "sat-cfg.h"
 65  #include "sat-log.h"
 66  
 67  extern GtkWidget *app;
 68  
 69  /* List of modules, docked and undocked */
 70  static GSList  *modules = NULL;
 71  
 72  
 73  /* The notebook widget for docked modules */
 74  static GtkWidget *nbook = NULL;
 75  
 76  
 77  static void     update_window_title(void);
 78  static void     switch_page_cb(GtkNotebook * notebook,
 79                                 gpointer * page,
 80                                 guint page_num, gpointer user_data);
 81  
 82  static void     create_module_window(GtkWidget * module);
 83  
 84  
 85  GtkWidget      *mod_mgr_create(void)
 86  {
 87      gchar          *openmods = NULL;
 88      gchar         **mods;
 89      gint            count, i;
 90      GtkWidget      *module;
 91      gchar          *modfile;
 92      gchar          *confdir;
 93      gint            page;
 94  
 95      nbook = gtk_notebook_new();
 96      gtk_notebook_set_scrollable(GTK_NOTEBOOK(nbook), TRUE);
 97      gtk_notebook_popup_enable(GTK_NOTEBOOK(nbook));
 98      g_signal_connect(G_OBJECT(nbook), "switch-page",
 99                       G_CALLBACK(switch_page_cb), NULL);
100  
101      openmods = sat_cfg_get_str(SAT_CFG_STR_OPEN_MODULES);
102      page = sat_cfg_get_int(SAT_CFG_INT_MODULE_CURRENT_PAGE);
103  
104      if (openmods)
105      {
106          mods = g_strsplit(openmods, ";", 0);
107          count = g_strv_length(mods);
108  
109          for (i = 0; i < count; i++)
110          {
111  
112              confdir = get_modules_dir();
113              modfile = g_strconcat(confdir, G_DIR_SEPARATOR_S,
114                                    mods[i], ".mod", NULL);
115              g_free(confdir);
116              module = gtk_sat_module_new(modfile);
117  
118              if (IS_GTK_SAT_MODULE(module))
119              {
120  
121                  /* if module state was window or user does not want to restore the
122                     state of the modules, pack the module into the notebook */
123                  if ((GTK_SAT_MODULE(module)->state == GTK_SAT_MOD_STATE_DOCKED)
124                      || !sat_cfg_get_bool(SAT_CFG_BOOL_MOD_STATE))
125                  {
126                      mod_mgr_add_module(module, TRUE);
127                  }
128                  else
129                  {
130                      mod_mgr_add_module(module, FALSE);
131                      create_module_window(module);
132                  }
133              }
134              else
135              {
136                  sat_log_log(SAT_LOG_LEVEL_ERROR,
137                              _("%s: Failed to restore %s"), __func__, mods[i]);
138  
139                  /* try to smartly handle disappearing modules */
140                  page--;
141              }
142  
143              g_free(modfile);
144  
145          }
146  
147          /* set to the page open when gpredict was closed */
148          if (page >= 0)
149              gtk_notebook_set_current_page(GTK_NOTEBOOK(nbook), page);
150  
151          g_strfreev(mods);
152          g_free(openmods);
153  
154          /* disable tabs if only one page in notebook */
155          if ((gtk_notebook_get_n_pages(GTK_NOTEBOOK(nbook))) == 1)
156          {
157              gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), FALSE);
158          }
159          else
160          {
161              gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), TRUE);
162          }
163      }
164      else
165      {
166          sat_log_log(SAT_LOG_LEVEL_INFO,
167                      _("%s: No modules have to be restored."), __func__);
168      }
169  
170      return nbook;
171  }
172  
173  /* Register a new module in the mod-mgr. If the dock flag is true the module is
174   * added to the mod-mgr notebook, otherwise it will be up to the caller to
175   * create a proper container.
176   */
177  gint mod_mgr_add_module(GtkWidget * module, gboolean dock)
178  {
179      gint            retcode = 0;
180      gint            page;
181  
182  
183      if (module)
184      {
185  
186          /* add module to internal list */
187          modules = g_slist_append(modules, module);
188  
189          if (dock)
190          {
191              /* add module to notebook if state = DOCKED */
192              page = gtk_notebook_append_page(GTK_NOTEBOOK(nbook),
193                                              module,
194                                              gtk_label_new(GTK_SAT_MODULE
195                                                            (module)->name));
196  
197              /* allow nmodule to be dragged to different position */
198              gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(nbook), module,
199                                               TRUE);
200  
201              gtk_notebook_set_current_page(GTK_NOTEBOOK(nbook), page);
202  
203              /* send message to logger */
204              sat_log_log(SAT_LOG_LEVEL_INFO,
205                          _("%s: Added %s to module manager (page %d)."),
206                          __func__, GTK_SAT_MODULE(module)->name, page);
207          }
208          else
209          {
210              /* send message to logger */
211              sat_log_log(SAT_LOG_LEVEL_INFO,
212                          _("%s: Added %s to module manager (NOT DOCKED)."),
213                          __func__, GTK_SAT_MODULE(module)->name);
214          }
215          retcode = 0;
216      }
217      else
218      {
219          sat_log_log(SAT_LOG_LEVEL_ERROR,
220                      _("%s: Module %s seems to be NULL"),
221                      __func__, GTK_SAT_MODULE(module)->name);
222          retcode = 1;
223      }
224  
225      /* disable tabs if only one page in notebook */
226      if ((gtk_notebook_get_n_pages(GTK_NOTEBOOK(nbook))) == 1)
227      {
228          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), FALSE);
229      }
230      else
231      {
232          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), TRUE);
233      }
234  
235      update_window_title();
236  
237      return retcode;
238  }
239  
240  gint mod_mgr_remove_module(GtkWidget * module)
241  {
242      gint            page;
243      gint            retcode = 0;
244  
245      /* remove from notebook */
246      if (GTK_SAT_MODULE(module)->state == GTK_SAT_MOD_STATE_DOCKED)
247      {
248          /* get page number for this module */
249          page = gtk_notebook_page_num(GTK_NOTEBOOK(nbook), module);
250  
251          if (page == -1)
252          {
253              /* this is some kind of bug (inconsistency between internal states) */
254              sat_log_log(SAT_LOG_LEVEL_ERROR,
255                          _
256                          ("%s: Could not find child in notebook. This may hurt..."),
257                          __func__);
258  
259              retcode = 1;
260          }
261          else
262          {
263              gtk_notebook_remove_page(GTK_NOTEBOOK(nbook), page);
264  
265              sat_log_log(SAT_LOG_LEVEL_INFO,
266                          _("%s: Removed child from notebook page %d."),
267                          __func__, page);
268  
269              retcode = 0;
270          }
271      }
272  
273      modules = g_slist_remove(modules, module);
274  
275      /* undocked modules will have to destroy themselves
276         because of their parent window
277       */
278  
279      /* disable tabs if only one page in notebook */
280      if ((gtk_notebook_get_n_pages(GTK_NOTEBOOK(nbook))) == 1)
281      {
282          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), FALSE);
283      }
284      else
285      {
286          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), TRUE);
287      }
288  
289      /* update window title */
290      update_window_title();
291  
292      return retcode;
293  }
294  
295  /*
296   * Save state of module manager.
297   *
298   * This function saves the state of the module manager. Currently, this consists
299   * of saving the list of open modules. If no modules are open, the function saves
300   * a NULL-list, indication that the corresponding configuration key should be
301   * removed.
302   */
303  void mod_mgr_save_state()
304  {
305      guint           num;
306      guint           i;
307      GtkWidget      *module;
308      gchar          *mods = NULL;
309      gchar          *buff;
310      gint            page;
311  
312  
313      if (!nbook)
314      {
315          sat_log_log(SAT_LOG_LEVEL_ERROR,
316                      _("%s: Attempt to save state but mod-mgr is NULL?"),
317                      __func__);
318          return;
319      }
320  
321      num = g_slist_length(modules);
322      if (num == 0)
323      {
324          sat_log_log(SAT_LOG_LEVEL_INFO,
325                      _("%s: No modules need to save state."), __func__);
326  
327          sat_cfg_set_str(SAT_CFG_STR_OPEN_MODULES, NULL);
328  
329          return;
330      }
331  
332      for (i = 0; i < num; i++)
333      {
334          module = GTK_WIDGET(g_slist_nth_data(modules, i));
335  
336          /* save state of the module */
337          mod_cfg_save(GTK_SAT_MODULE(module)->name,
338                       GTK_SAT_MODULE(module)->cfgdata);
339  
340          if (i == 0)
341          {
342              buff = g_strdup(GTK_SAT_MODULE(module)->name);
343          }
344          else
345          {
346              buff = g_strconcat(mods, ";", GTK_SAT_MODULE(module)->name, NULL);
347              g_free(mods);
348          }
349  
350          mods = g_strdup(buff);
351          g_free(buff);
352          sat_log_log(SAT_LOG_LEVEL_DEBUG, _("%s: Stored %s"),
353                      __func__, GTK_SAT_MODULE(module)->name);
354      }
355  
356      /* store the currently open page number */
357      page = gtk_notebook_get_current_page(GTK_NOTEBOOK(nbook));
358  
359      sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Saved states for %d modules."),
360                  __func__, num);
361  
362      sat_cfg_set_str(SAT_CFG_STR_OPEN_MODULES, mods);
363      sat_cfg_set_int(SAT_CFG_INT_MODULE_CURRENT_PAGE, page);
364  
365      g_free(mods);
366  }
367  
368  gboolean mod_mgr_mod_is_visible(GtkWidget * module)
369  {
370      gint            page;
371      gboolean        retcode = TRUE;
372  
373      /* get page number for this module */
374      page = gtk_notebook_page_num(GTK_NOTEBOOK(nbook), module);
375  
376      if (page != -1)
377      {
378          if (gtk_notebook_get_current_page(GTK_NOTEBOOK(nbook)) == page)
379          {
380              retcode = TRUE;
381          }
382          else
383          {
384              retcode = FALSE;
385          }
386      }
387      else
388      {
389          retcode = FALSE;
390      }
391  
392      return retcode;
393  }
394  
395  /*
396   * Dock a module into the notebook.
397   *
398   * This function inserts the module into the notebook but does not add it
399   * to the list of modules, since it should already be there.
400   *
401   * The function does some sanity checks to ensure the the module actually
402   * is in the internal list of modules and also that the module is not
403   * already present in the notebook. If any of these checks fail, the function
404   * will send an error message and try to recover.
405   *
406   * The function does not modify the internal state of the module, module->state,
407   * that is up to the module itself.
408   */
409  gint mod_mgr_dock_module(GtkWidget * module)
410  {
411      gint            retcode = 0;
412      gint            page;
413  
414      if (!g_slist_find(modules, module))
415      {
416          sat_log_log(SAT_LOG_LEVEL_ERROR,
417                      _("%s: Module %s not found in list. Trying to recover."),
418                      __func__, GTK_SAT_MODULE(module)->name);
419          modules = g_slist_append(modules, module);
420      }
421  
422      page = gtk_notebook_page_num(GTK_NOTEBOOK(nbook), module);
423      if (page != -1)
424      {
425          sat_log_log(SAT_LOG_LEVEL_ERROR,
426                      _("%s: Module %s already in notebook!"),
427                      __func__, GTK_SAT_MODULE(module)->name);
428          retcode = 1;
429      }
430      else
431      {
432          /* add module to notebook */
433          page = gtk_notebook_append_page(GTK_NOTEBOOK(nbook),
434                                          module,
435                                          gtk_label_new(GTK_SAT_MODULE(module)->
436                                                        name));
437  
438          sat_log_log(SAT_LOG_LEVEL_INFO,
439                      _("%s: Docked %s into notebook (page %d)"),
440                      __func__, GTK_SAT_MODULE(module)->name, page);
441  
442          retcode = 0;
443      }
444  
445      /* disable tabs if only one page in notebook */
446      if ((gtk_notebook_get_n_pages(GTK_NOTEBOOK(nbook))) == 1)
447      {
448          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), FALSE);
449      }
450      else
451      {
452          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), TRUE);
453      }
454  
455      /* update window title */
456      update_window_title();
457  
458      return retcode;
459  }
460  
461  /*
462   * Undock module from notebook
463   *
464   * This function removes module from the notebook without removing it from
465   * the internal list of modules.
466   *
467   * The function does some sanity checks to ensure that the module actually
468   * exists in the mod-mgr, if not it will add module to the internal list
469   * and raise a warning.
470   *
471   * The function does not modify the internal state of the module, module->state,
472   * that is up to the module itself.
473   *
474   * \note The module itself is responsible for temporarily incrementing the
475   *       reference count of the widget in order to avoid destruction when
476   *       removing from the notebook.
477   */
478  gint mod_mgr_undock_module(GtkWidget * module)
479  {
480      gint            retcode = 0;
481      gint            page;
482  
483      if (!g_slist_find(modules, module))
484      {
485          sat_log_log(SAT_LOG_LEVEL_ERROR,
486                      _("%s: Module %s not found in list. Trying to recover."),
487                      __func__, GTK_SAT_MODULE(module)->name);
488          modules = g_slist_append(modules, module);
489      }
490  
491      page = gtk_notebook_page_num(GTK_NOTEBOOK(nbook), module);
492      if (page == -1)
493      {
494          sat_log_log(SAT_LOG_LEVEL_ERROR,
495                      _("%s: Module %s does not seem to be docked!"),
496                      __func__, GTK_SAT_MODULE(module)->name);
497          retcode = 1;
498      }
499      else
500      {
501  
502          gtk_notebook_remove_page(GTK_NOTEBOOK(nbook), page);
503  
504          sat_log_log(SAT_LOG_LEVEL_INFO,
505                      _("%s: Removed %s from notebook page %d."),
506                      __func__, GTK_SAT_MODULE(module)->name, page);
507  
508          retcode = 0;
509      }
510  
511      /* disable tabs if only one page in notebook */
512      if ((gtk_notebook_get_n_pages(GTK_NOTEBOOK(nbook))) == 1)
513      {
514          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), FALSE);
515      }
516      else
517      {
518          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(nbook), TRUE);
519      }
520  
521      /* update window title */
522      update_window_title();
523  
524      return retcode;
525  }
526  
527  static void update_window_title()
528  {
529      gint            pgn, num;
530      GtkWidget      *pg;
531      gchar          *title;
532  
533      /* get number of pages */
534      num = gtk_notebook_get_n_pages(GTK_NOTEBOOK(nbook));
535  
536      if (num == 0)
537      {
538          gtk_window_set_title(GTK_WINDOW(app), _("Gpredict: (none)"));
539      }
540      else
541      {
542          pgn = gtk_notebook_get_current_page(GTK_NOTEBOOK(nbook));
543          pg = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nbook), pgn);
544          title = g_strdup_printf(_("Gpredict: %s"),
545                                  gtk_notebook_get_tab_label_text(GTK_NOTEBOOK
546                                                                  (nbook), pg));
547          gtk_window_set_title(GTK_WINDOW(app), title);
548          g_free(title);
549      }
550  }
551  
552  static void switch_page_cb(GtkNotebook * notebook,
553                             gpointer * page, guint page_num, gpointer user_data)
554  {
555      GtkWidget      *pg;
556      gchar          *title;
557  
558      (void)notebook;
559      (void)page;
560      (void)user_data;
561  
562      pg = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nbook), page_num);
563      title = g_strdup_printf(_("Gpredict: %s"),
564                              gtk_notebook_get_tab_label_text(GTK_NOTEBOOK
565                                                              (nbook), pg));
566      gtk_window_set_title(GTK_WINDOW(app), title);
567      g_free(title);
568  }
569  
570  void mod_mgr_reload_sats()
571  {
572      guint           num;
573      guint           i;
574      GtkSatModule   *mod;
575  
576      if (!nbook)
577      {
578          sat_log_log(SAT_LOG_LEVEL_ERROR,
579                      _("%s: Attempt to reload sats but mod-mgr is NULL?"),
580                      __func__);
581          return;
582      }
583  
584      num = g_slist_length(modules);
585      if (num == 0)
586      {
587          sat_log_log(SAT_LOG_LEVEL_INFO,
588                      _("%s: No modules need to reload sats."), __func__);
589          return;
590      }
591  
592      /* for each module in the GSList execute sat_module_reload_sats() */
593      for (i = 0; i < num; i++)
594      {
595          mod = GTK_SAT_MODULE(g_slist_nth_data(modules, i));
596          gtk_sat_module_reload_sats(mod);
597      }
598  }
599  
600  static void create_module_window(GtkWidget * module)
601  {
602      gint            w, h;
603      gchar          *icon;       /* icon file name */
604      gchar          *title;      /* window title */
605      GtkAllocation   aloc;
606  
607      gtk_widget_get_allocation(module, &aloc);
608      /* get stored size; use size from main window if size not explicitly stoed */
609      if (g_key_file_has_key(GTK_SAT_MODULE(module)->cfgdata,
610                             MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_WIDTH, NULL))
611      {
612          w = g_key_file_get_integer(GTK_SAT_MODULE(module)->cfgdata,
613                                     MOD_CFG_GLOBAL_SECTION,
614                                     MOD_CFG_WIN_WIDTH, NULL);
615      }
616      else
617      {
618          w = aloc.width;
619      }
620      if (g_key_file_has_key(GTK_SAT_MODULE(module)->cfgdata,
621                             MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_HEIGHT, NULL))
622      {
623          h = g_key_file_get_integer(GTK_SAT_MODULE(module)->cfgdata,
624                                     MOD_CFG_GLOBAL_SECTION,
625                                     MOD_CFG_WIN_HEIGHT, NULL);
626      }
627      else
628      {
629          h = aloc.height;
630      }
631  
632      /* increase reference count of module */
633      //g_object_ref (module);
634  
635      /* we don't need the positions */
636      //GTK_SAT_MODULE (module)->vpanedpos = -1;
637      //GTK_SAT_MODULE (module)->hpanedpos = -1;
638  
639      /* undock from mod-mgr */
640      //mod_mgr_undock_module (module);
641  
642      /* create window */
643      GTK_SAT_MODULE(module)->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
644      title = g_strconcat(_("Gpredict: "),
645                          GTK_SAT_MODULE(module)->name,
646                          " (", GTK_SAT_MODULE(module)->qth->name, ")", NULL);
647      gtk_window_set_title(GTK_WINDOW(GTK_SAT_MODULE(module)->win), title);
648      g_free(title);
649      gtk_window_set_default_size(GTK_WINDOW(GTK_SAT_MODULE(module)->win), w, h);
650      g_signal_connect(G_OBJECT(GTK_SAT_MODULE(module)->win), "configure_event",
651                       G_CALLBACK(module_window_config_cb), module);
652  
653      icon = logo_file_name("gpredict_icon_color.svg");
654      if (g_file_test(icon, G_FILE_TEST_EXISTS))
655      {
656          gtk_window_set_icon_from_file(GTK_WINDOW(GTK_SAT_MODULE(module)->win),
657                                        icon, NULL);
658      }
659      g_free(icon);
660  
661      /* move window to stored position if requested by configuration */
662      if (sat_cfg_get_bool(SAT_CFG_BOOL_MOD_WIN_POS) &&
663          g_key_file_has_key(GTK_SAT_MODULE(module)->cfgdata,
664                             MOD_CFG_GLOBAL_SECTION,
665                             MOD_CFG_WIN_POS_X,
666                             NULL) &&
667          g_key_file_has_key(GTK_SAT_MODULE(module)->cfgdata,
668                             MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_POS_Y, NULL))
669      {
670  
671          gtk_window_move(GTK_WINDOW(GTK_SAT_MODULE(module)->win),
672                          g_key_file_get_integer(GTK_SAT_MODULE(module)->cfgdata,
673                                                 MOD_CFG_GLOBAL_SECTION,
674                                                 MOD_CFG_WIN_POS_X, NULL),
675                          g_key_file_get_integer(GTK_SAT_MODULE(module)->cfgdata,
676                                                 MOD_CFG_GLOBAL_SECTION,
677                                                 MOD_CFG_WIN_POS_Y, NULL));
678      }
679  
680      gtk_container_add(GTK_CONTAINER(GTK_SAT_MODULE(module)->win), module);
681      gtk_widget_show_all(GTK_SAT_MODULE(module)->win);
682  
683      /* reparent time manager window if visible */
684      if (GTK_SAT_MODULE(module)->tmgActive)
685      {
686          gtk_window_set_transient_for(GTK_WINDOW
687                                       (GTK_SAT_MODULE(module)->tmgWin),
688                                       GTK_WINDOW(GTK_SAT_MODULE(module)->win));
689      }
690  }