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
mod_mgr_create(void)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 */
mod_mgr_add_module(GtkWidget * module,gboolean dock)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
mod_mgr_remove_module(GtkWidget * module)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 */
mod_mgr_save_state()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
mod_mgr_mod_is_visible(GtkWidget * module)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 */
mod_mgr_dock_module(GtkWidget * module)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 */
mod_mgr_undock_module(GtkWidget * module)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
update_window_title()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
switch_page_cb(GtkNotebook * notebook,gpointer * page,guint page_num,gpointer user_data)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
mod_mgr_reload_sats()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
create_module_window(GtkWidget * module)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 }
691