/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* Gpredict: Real-time satellite tracking and orbit prediction program Copyright (C) 2001-2007 Alexandru Csete, OZ9AEC. Authors: Alexandru Csete Comments, questions and bugreports should be submitted via http://sourceforge.net/projects/groundstation/ More details can be found at the project home page: http://groundstation.sourceforge.net/ This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, visit http://www.fsf.org/ */ /** \brief Module manager. * * The module manager is responsible for the management of opened modules. * It consist of a GtkNoteBook container where the modules are placed initially. * * The module manager is initialised with the mod_mgr_create function, which will * create the notebook widget and re-open the modules that have been open when * gpredict has been quit last time. * * To add additional modules the mod_mgr_add_module function should be used. This * function takes a fully initialised GtkSatModule (FIXME: cast to GtkWidget) and * a boolean flag indicating whether the module should be docked into the notebook * or not. Please note, that if a module is added with dock=FALSE, the caller will * have the responsibility of creating a proper container window for the module. * * Finally, when gpredict is about to exit, the state of the module manager can be * saved by calling the mod_mgr_save_state. This will save a list of open modules * so that they can be restored next time gpredict is re-opened. * * The mod-mgr maintains an internal GSList with references to the opened modules. * This allows the mod-mgr to know about both docked and undocked modules. * */ #include #include #include "gtk-sat-module.h" #include "gtk-sat-module-popup.h" #include "sat-cfg.h" #include "sat-log.h" #include "config-keys.h" #include "mod-mgr.h" #include "mod-cfg.h" #include "compat.h" #ifdef HAVE_CONFIG_H # include #endif /** \brief Main application widget. */ extern GtkWidget *app; /** \brief List of modules, docked and undocked */ static GSList *modules = NULL; /** \brief The notebook widget for docked modules */ static GtkWidget *nbook = NULL; static void update_window_title (void); static void switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data); static void create_module_window (GtkWidget *module); /** \brief Create and initialise module manger. * \return The main module container widget (GtkNotebook). * * This function creates and initialises the module manager widget, which * consist of a GtkNotebook container. Before returning the container to the * caller, the function checks whether any modules should be restored (ie. * openend), if yes, it creates them and adds them to the notebook. * */ GtkWidget * mod_mgr_create (void) { gchar *openmods = NULL; gchar **mods; gint count,i; GtkWidget *module; gchar *modfile; /* create notebook */ nbook = gtk_notebook_new (); gtk_notebook_set_scrollable (GTK_NOTEBOOK (nbook), TRUE); gtk_notebook_popup_enable (GTK_NOTEBOOK (nbook)); g_object_set (G_OBJECT (nbook), "homogeneous", TRUE, NULL); g_signal_connect (G_OBJECT (nbook), "switch-page", G_CALLBACK (switch_page_cb), NULL); /* get list of modules which should be open */ openmods = sat_cfg_get_str (SAT_CFG_STR_OPEN_MODULES); if (openmods) { mods = g_strsplit (openmods, ";", 0); count = g_strv_length (mods); for (i = 0; i < count; i++) { /* get data file name */ modfile = g_strconcat (g_get_home_dir (), G_DIR_SEPARATOR_S, ".gpredict2", G_DIR_SEPARATOR_S, "modules", G_DIR_SEPARATOR_S, mods[i], ".mod", NULL); /* create module */ module = gtk_sat_module_new (modfile); if (IS_GTK_SAT_MODULE (module)) { /* if module state was window or user does not want to restore the state of the modules, pack the module into the notebook */ if ((GTK_SAT_MODULE (module)->state == GTK_SAT_MOD_STATE_DOCKED) || !sat_cfg_get_bool (SAT_CFG_BOOL_MOD_STATE)) { mod_mgr_add_module (module, TRUE); } else { mod_mgr_add_module (module, FALSE); create_module_window (module); } } else { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Failed to restore %s"), __FUNCTION__, mods[i]); } g_free (modfile); } g_strfreev (mods); g_free (openmods); /* disable tabs if only one page in notebook */ if ((gtk_notebook_get_n_pages (GTK_NOTEBOOK(nbook))) == 1) { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), FALSE); } else { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), TRUE); } } else { sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: No modules have to be restored."), __FUNCTION__); } return nbook; } /** \brief Add a new module to mod-mgr. * \param module The GtkSatModule widget to add * \param dock Flag indicating whether module should be docked or not. * * This function registers a new module in the mod-mgr. If the dock flag is true * the module is added to the mod-mgr notebook, otherwise it will be up to the * caller to create a proper container. * */ gint mod_mgr_add_module (GtkWidget *module, gboolean dock) { gint retcode = 0; gint page; if (module) { /* add module to internal list */ modules = g_slist_append (modules, module); if (dock) { /* add module to notebook if state = DOCKED */ page = gtk_notebook_append_page (GTK_NOTEBOOK (nbook), module, gtk_label_new (GTK_SAT_MODULE (module)->name)); gtk_notebook_set_current_page (GTK_NOTEBOOK (nbook), page); /* send message to logger */ sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: Added %s to module manger (page %d)."), __FUNCTION__, GTK_SAT_MODULE (module)->name, page); } else { /* send message to logger */ sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: Added %s to module manger (NOT DOCKED)."), __FUNCTION__, GTK_SAT_MODULE (module)->name); } retcode = 0; } else { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Module %s seems to be NULL"), __FUNCTION__, GTK_SAT_MODULE (module)->name); retcode = 1; } /* disable tabs if only one page in notebook */ if ((gtk_notebook_get_n_pages (GTK_NOTEBOOK(nbook))) == 1) { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), FALSE); } else { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), TRUE); } update_window_title (); return retcode; } /** \brief Remove a module from the notebook. * \param module The module that should be removed. * \return 0 if the module has been removed or 1 if the requested module * could not be found in the notebook. */ gint mod_mgr_remove_module (GtkWidget *module) { gint page; gint retcode = 0; /* remove from notebook */ if (GTK_SAT_MODULE (module)->state == GTK_SAT_MOD_STATE_DOCKED) { /* get page number for this module */ page = gtk_notebook_page_num (GTK_NOTEBOOK (nbook), module); if (page == -1) { /* this is some kind of bug (inconsistency between internal states) */ sat_log_log (SAT_LOG_LEVEL_BUG, _("%s: Could not find child in notebook. This may hurt..."), __FUNCTION__); retcode = 1; } else { gtk_notebook_remove_page (GTK_NOTEBOOK (nbook), page); sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: Removed child from notebook page %d."), __FUNCTION__, page); retcode = 0; } } /* remove from list */ modules = g_slist_remove (modules, module); /* undocked modules will have to destroy themselves because of their parent window */ /* disable tabs if only one page in notebook */ if ((gtk_notebook_get_n_pages (GTK_NOTEBOOK(nbook))) == 1) { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), FALSE); } else { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), TRUE); } /* update window title */ update_window_title (); return retcode; } /** \brief Save state of module manager. * * This function saves the state of the module manager. Currently, this consists * of saving the list of open modules. If no modules are open, the function saves * a NULL-list, indication that the corresponding configuration key should be * removed. */ void mod_mgr_save_state () { guint num; guint i; GtkWidget *module; gchar *mods = NULL; gchar *buff; if (!nbook) { sat_log_log (SAT_LOG_LEVEL_BUG, _("%s: Attempt to save state but mod-mgr is NULL?"), __FUNCTION__); return; } num = g_slist_length (modules); if (num == 0) { sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: No modules need to save state."), __FUNCTION__); sat_cfg_set_str (SAT_CFG_STR_OPEN_MODULES, NULL); return; } for (i = 0; i < num; i++) { module = GTK_WIDGET (g_slist_nth_data (modules, i)); /* save state of the module */ mod_cfg_save (GTK_SAT_MODULE (module)->name, GTK_SAT_MODULE (module)->cfgdata); if (i == 0) { buff = g_strdup (GTK_SAT_MODULE (module)->name); } else { buff = g_strconcat (mods, ";", GTK_SAT_MODULE (module)->name, NULL); g_free (mods); } mods = g_strdup (buff); g_free (buff); sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: Stored %s"), __FUNCTION__, GTK_SAT_MODULE (module)->name); } sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: Saved states for %d modules."), __FUNCTION__, num); sat_cfg_set_str (SAT_CFG_STR_OPEN_MODULES, mods); g_free (mods); } /** \brief Check whether module is visible or not. * \param module The module to check. * \return TRUE if the module appears to be visible, FALSE otherwise. */ gboolean mod_mgr_mod_is_visible (GtkWidget *module) { gint page; gboolean retcode = TRUE; /* get page number for this module */ page = gtk_notebook_page_num (GTK_NOTEBOOK (nbook), module); if (page != -1) { if (gtk_notebook_get_current_page (GTK_NOTEBOOK (nbook)) == page) { retcode = TRUE; } else { retcode = FALSE; } } else { retcode = FALSE; } return retcode; } /** \brief Dock a module into the notebook. * \param module The module to insert into the notebook. * \return 0 if the operation was successful, 1 otherwise. * * This function inserts the module into the notebook but does not add it * to the list of modules, since it should already be there. * * The function does some sanity checks to ensure the the module actually * is in the internal list of modules and also that the module is not * already present in the notebook. If any of these checks fail, the function * will send an error message and try to recover. * * The function does not modify the internal state of the module, module->state, * that is up to the module itself. */ gint mod_mgr_dock_module (GtkWidget *module) { gint retcode = 0; gint page; if (!g_slist_find (modules, module)) { sat_log_log (SAT_LOG_LEVEL_BUG, _("%s: Module %s not found in list. Trying to recover."), __FUNCTION__, GTK_SAT_MODULE (module)->name); modules = g_slist_append (modules, module); } page = gtk_notebook_page_num (GTK_NOTEBOOK (nbook), module); if (page != -1) { sat_log_log (SAT_LOG_LEVEL_BUG, _("%s: Module %s already in notebook!"), __FUNCTION__, GTK_SAT_MODULE (module)->name); retcode = 1; } else { /* add module to notebook */ page = gtk_notebook_append_page (GTK_NOTEBOOK (nbook), module, gtk_label_new (GTK_SAT_MODULE (module)->name)); /* fix size allocation, i.e. paned position */ gtk_sat_module_fix_size (module); sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: Docked %s into notebook (page %d)"), __FUNCTION__, GTK_SAT_MODULE (module)->name, page); retcode = 0; } /* disable tabs if only one page in notebook */ if ((gtk_notebook_get_n_pages (GTK_NOTEBOOK(nbook))) == 1) { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), FALSE); } else { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), TRUE); } /* update window title */ update_window_title (); return retcode; } /** \brief Undock module from notebook * \param module The module that should be undocked. * * This function removes module from the notebook without removing it from * the internal list of modules. * * The function does some sanity checks to ensure that the module actually * exists in the mod-mgr, if not it will add module to the internal list * and raise a warning. * * The function does not modify the internal state of the module, module->state, * that is up to the module itself. * * \note The module itself is responsible for temporarily incrementing the * reference count of the widget in order to avoid destruction when * removing from the notebook. */ gint mod_mgr_undock_module (GtkWidget *module) { gint retcode = 0; gint page; if (!g_slist_find (modules, module)) { sat_log_log (SAT_LOG_LEVEL_BUG, _("%s: Module %s not found in list. Trying to recover."), __FUNCTION__, GTK_SAT_MODULE (module)->name); modules = g_slist_append (modules, module); } page = gtk_notebook_page_num (GTK_NOTEBOOK (nbook), module); if (page == -1) { sat_log_log (SAT_LOG_LEVEL_BUG, _("%s: Module %s does not seem to be docked!"), __FUNCTION__, GTK_SAT_MODULE (module)->name); retcode = 1; } else { gtk_notebook_remove_page (GTK_NOTEBOOK (nbook), page); sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: Removed %s from notebook page %d."), __FUNCTION__, GTK_SAT_MODULE (module)->name, page); retcode = 0; } /* disable tabs if only one page in notebook */ if ((gtk_notebook_get_n_pages (GTK_NOTEBOOK(nbook))) == 1) { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), FALSE); } else { gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nbook), TRUE); } /* update window title */ update_window_title (); return retcode; } static void update_window_title () { gint pgn,num; GtkWidget *pg; gchar *title; /* get number of pages */ num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nbook)); if (num == 0) { gtk_window_set_title (GTK_WINDOW (app), _("GPREDICT: (none)")); } else { pgn = gtk_notebook_get_current_page (GTK_NOTEBOOK (nbook)); pg = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nbook), pgn); title = g_strdup_printf (_("GPREDICT: %s"), gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (nbook), pg)); gtk_window_set_title (GTK_WINDOW (app), title); g_free (title); } } static void switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data) { GtkWidget *pg; gchar *title; pg = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nbook), page_num); title = g_strdup_printf (_("GPREDICT: %s"), gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (nbook), pg)); gtk_window_set_title (GTK_WINDOW (app), title); g_free (title); } /** brief Reload satellites in all modules. */ void mod_mgr_reload_sats () { guint num; guint i; GtkSatModule *mod; if (!nbook) { sat_log_log (SAT_LOG_LEVEL_BUG, _("%s: Attempt to reload sats but mod-mgr is NULL?"), __FUNCTION__); return; } num = g_slist_length (modules); if (num == 0) { sat_log_log (SAT_LOG_LEVEL_MSG, _("%s: No modules need to reload sats."), __FUNCTION__); return; } /* for each module in the GSList execute sat_module_reload_sats() */ for (i = 0; i < num; i++) { mod = GTK_SAT_MODULE (g_slist_nth_data (modules, i)); gtk_sat_module_reload_sats (mod); } } /** \brief Create a module window. * * This function is used to create a module window when opening modules * that should not be packed into the notebook. */ static void create_module_window (GtkWidget *module) { gint w,h; gchar *icon; /* icon file name */ gchar *title; /* window title */ /* get stored size; use size from main window if size not explicitly stoed */ if (g_key_file_has_key (GTK_SAT_MODULE (module)->cfgdata, MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_WIDTH, NULL)) { w = g_key_file_get_integer (GTK_SAT_MODULE (module)->cfgdata, MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_WIDTH, NULL); } else { w = module->allocation.width; } if (g_key_file_has_key (GTK_SAT_MODULE (module)->cfgdata, MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_HEIGHT, NULL)) { h = g_key_file_get_integer (GTK_SAT_MODULE (module)->cfgdata, MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_HEIGHT, NULL); } else { h = module->allocation.height; } /* increase reference count of module */ //g_object_ref (module); /* we don't need the positions */ //GTK_SAT_MODULE (module)->vpanedpos = -1; //GTK_SAT_MODULE (module)->hpanedpos = -1; /* undock from mod-mgr */ //mod_mgr_undock_module (module); /* create window */ GTK_SAT_MODULE (module)->win = gtk_window_new (GTK_WINDOW_TOPLEVEL); title = g_strconcat ("GPREDICT: ", GTK_SAT_MODULE (module)->name, " (", GTK_SAT_MODULE (module)->qth->name, ")", NULL); gtk_window_set_title (GTK_WINDOW (GTK_SAT_MODULE (module)->win), title); g_free (title); gtk_window_set_default_size (GTK_WINDOW (GTK_SAT_MODULE (module)->win), w, h); g_signal_connect (G_OBJECT (GTK_SAT_MODULE (module)->win), "configure_event", G_CALLBACK (module_window_config_cb), module); /* window icon */ icon = icon_file_name ("gpredict-icon.png"); if (g_file_test (icon, G_FILE_TEST_EXISTS)) { gtk_window_set_icon_from_file (GTK_WINDOW (GTK_SAT_MODULE (module)->win), icon, NULL); } g_free (icon); /* move window to stored position if requested by configuration */ if (sat_cfg_get_bool (SAT_CFG_BOOL_MOD_WIN_POS) && g_key_file_has_key (GTK_SAT_MODULE (module)->cfgdata, MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_POS_X, NULL) && g_key_file_has_key (GTK_SAT_MODULE (module)->cfgdata, MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_POS_Y, NULL)) { gtk_window_move (GTK_WINDOW (GTK_SAT_MODULE (module)->win), g_key_file_get_integer (GTK_SAT_MODULE (module)->cfgdata, MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_POS_X, NULL), g_key_file_get_integer (GTK_SAT_MODULE (module)->cfgdata, MOD_CFG_GLOBAL_SECTION, MOD_CFG_WIN_POS_Y, NULL)); } /* add module to window */ gtk_container_add (GTK_CONTAINER (GTK_SAT_MODULE (module)->win), module); /* show window */ gtk_widget_show_all (GTK_SAT_MODULE (module)->win); /* reparent time manager window if visible */ if (GTK_SAT_MODULE (module)->tmgActive) { gtk_window_set_transient_for (GTK_WINDOW (GTK_SAT_MODULE (module)->tmgWin), GTK_WINDOW (GTK_SAT_MODULE (module)->win)); } }