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 }