menu-context.cpp

来自「celestia源代码」· C++ 代码 · 共 421 行

CPP
421
字号
/* *  Celestia GTK+ Front-End *  Copyright (C) 2005 Pat Suwalski <pat@suwalski.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. * *  $Id: menu-context.cpp,v 1.3 2005/12/13 06:19:57 suwalski Exp $ */#include <algorithm>#include <gtk/gtk.h>#include <celengine/simulation.h>#include <celestia/celestiacore.h>#include <celutil/utf8.h>#include "menu-context.h"#include "actions.h"#include "common.h"/* Definitions: Callbacks */static void wrapAction(GtkAction* action);static void menuMark();static void menuUnMark();static void handleContextPlanet(gpointer data);static void handleContextSurface(gpointer data);/* Definitions: Helpers */static GtkMenuItem* AppendMenu(GtkWidget* parent, GtkSignalFunc callback, const gchar* name, gpointer extra);static GtkMenu* CreatePlanetarySystemMenu(string parentName, const PlanetarySystem* psys);static GtkMenu* CreateAlternateSurfaceMenu(const vector<string>& surfaces);/* There is no way to pass the AppData struct to the menu at this time. This * keeps the global variable local to this file, at least conceptually. */static AppData* app;/* Initializer. Sets the appData, since there is no way to pass it. */void initContext(AppData* a){	app = a;}/* ENTRY: Context menu (event handled by appCore) *        Normally, float x and y, but unused, so removed. */void menuContext(float, float, Selection sel){	GtkWidget* popup = gtk_menu_new();	string name;	switch (sel.getType())	{		case Selection::Type_Body:		{			name = sel.body()->getName();			AppendMenu(popup, NULL, name.c_str(), gtk_action_group_get_action(app->agMain, "CenterSelection"));			AppendMenu(popup, NULL, NULL, 0);			AppendMenu(popup, NULL, "_Goto", gtk_action_group_get_action(app->agMain, "GotoSelection"));			AppendMenu(popup, NULL, "_Follow", gtk_action_group_get_action(app->agMain, "FollowSelection"));			AppendMenu(popup, NULL, "S_ync Orbit", gtk_action_group_get_action(app->agMain, "SyncSelection"));			/* Add info eventually:			 * AppendMenu(popup, NULL, "_Info", 0); */			const PlanetarySystem* satellites = sel.body()->getSatellites();			if (satellites != NULL && satellites->getSystemSize() != 0)			{				GtkMenu* satMenu = CreatePlanetarySystemMenu(name, satellites);				gtk_menu_item_set_submenu(AppendMenu(popup, NULL, "_Satellites", 0), GTK_WIDGET(satMenu));			}			vector<string>* altSurfaces = sel.body()->getAlternateSurfaceNames();			if (altSurfaces != NULL)			{				if (altSurfaces->size() > 0)				{					GtkMenu* surfMenu = CreateAlternateSurfaceMenu(*altSurfaces);					gtk_menu_item_set_submenu(AppendMenu(popup, NULL, "_Alternate Surfaces", 0), GTK_WIDGET(surfMenu));					delete altSurfaces;				}			}		}		break;		case Selection::Type_Star:		{			Simulation* sim = app->simulation;			name = ReplaceGreekLetterAbbr(sim->getUniverse()->getStarCatalog()->getStarName(*(sel.star())));			AppendMenu(popup, NULL, name.c_str(), gtk_action_group_get_action(app->agMain, "CenterSelection"));			AppendMenu(popup, NULL, NULL, 0);			AppendMenu(popup, NULL, "_Goto", gtk_action_group_get_action(app->agMain, "GotoSelection"));			/* Add info eventually:			 * AppendMenu(popup, NULL, "_Info", 0); */			SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog();			SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel.star()->getCatalogNumber());			if (iter != solarSystemCatalog->end())			{				SolarSystem* solarSys = iter->second;				GtkMenu* planetsMenu = CreatePlanetarySystemMenu(name, solarSys->getPlanets());				if (name == "Sol")					gtk_menu_item_set_submenu(AppendMenu(popup, NULL, "Orbiting Bodies", 0), GTK_WIDGET(planetsMenu));				else					gtk_menu_item_set_submenu(AppendMenu(popup, NULL, "Planets", 0), GTK_WIDGET(planetsMenu));			}		}		break;		case Selection::Type_DeepSky:		{			AppendMenu(popup, NULL, app->simulation->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky()).c_str(), gtk_action_group_get_action(app->agMain, "CenterSelection"));			AppendMenu(popup, NULL, NULL, 0);			AppendMenu(popup, NULL, "_Goto", gtk_action_group_get_action(app->agMain, "GotoSelection"));			AppendMenu(popup, NULL, "_Follow", gtk_action_group_get_action(app->agMain, "FollowSelection"));			/* Add info eventually:			 * AppendMenu(popup, NULL, "_Info", 0); */		}		break;		case Selection::Type_Location:			break;		default:			break;	}	if (app->simulation->getUniverse()->isMarked(sel, 1))		AppendMenu(popup, menuUnMark, "_Unmark", 0);	else		AppendMenu(popup, menuMark, "_Mark", 0);	app->simulation->setSelection(sel);	gtk_widget_show_all(popup);		gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());}/* CALLBACK: Wrap a GtkAction. The whole context menu should be replaced at *           some point */static void wrapAction(GtkAction* action){	gtk_action_activate(action);}/* CALLBACK: Mark the selected object. Might some day be a GtkAction. */static void menuMark(){	Simulation* sim = app->simulation;	if (sim->getUniverse() != NULL)	{		sim->getUniverse()->markObject(sim->getSelection(),						10.0f,						Color(0.0f, 1.0f, 0.0f, 0.9f),						Marker::Diamond,						1);	}}/* CALLBACK: Unmark the selected object. Might some day be a GtkAction. */static void menuUnMark(){	Simulation* sim = app->simulation;	if (sim->getUniverse() != NULL)		sim->getUniverse()->unmarkObject(sim->getSelection(), 1);}/* CALLBACK: Handle a planetary selection from the context menu. */static void handleContextPlanet(gpointer data){	int value = GPOINTER_TO_INT(data);		/* Handle the satellite/child object submenu */	Selection sel = app->simulation->getSelection();	switch (sel.getType())	{		case Selection::Type_Star:			app->simulation->selectPlanet(value);			break;		case Selection::Type_Body:			{				PlanetarySystem* satellites = (PlanetarySystem*) sel.body()->getSatellites();				app->simulation->setSelection(Selection(satellites->getBody(value)));				break;			}		case Selection::Type_DeepSky:			/* Current deep sky object/galaxy implementation does			 * not have children to select. */			break;		case Selection::Type_Location:			break;		default:			break;	}}/* CALLBACK: Handle an alternate surface from the context menu. */static void handleContextSurface(gpointer data){	int value = GPOINTER_TO_INT(data);		/* Handle the alternate surface submenu */	Selection sel = app->simulation->getSelection();	if (sel.body() != NULL)	{		guint index = value - 1;		vector<string>* surfNames = sel.body()->getAlternateSurfaceNames();		if (surfNames != NULL)		{			string surfName;			if (index < surfNames->size())				surfName = surfNames->at(index);			app->simulation->getActiveObserver()->setDisplayedSurface(surfName);			delete surfNames;		}	}}/* HELPER: Append a menu item and return pointer. Used for context menu. */static GtkMenuItem* AppendMenu(GtkWidget* parent, GtkSignalFunc callback, const gchar* name, gpointer extra){	GtkWidget* menuitem;	gpointer data;	/* Check for separator */	if (name == NULL)		menuitem = gtk_separator_menu_item_new();	else		menuitem = gtk_menu_item_new_with_mnemonic(name);	/* If no callback was provided, pass GtkAction, else convert to pointer */	if (callback == NULL && extra != 0)	{		callback = G_CALLBACK(wrapAction);		data = extra;	}	else		data = GINT_TO_POINTER(extra);		/* Add handler */	if (callback != NULL)		g_signal_connect_swapped (G_OBJECT(menuitem), "activate",		                          G_CALLBACK(callback),		                          data);	gtk_menu_shell_append(GTK_MENU_SHELL(parent), menuitem);	return GTK_MENU_ITEM(menuitem);}/* Typedefs and structs for sorting objects by name in context menu. */typedef pair<int,string> IntStrPair;typedef vector<IntStrPair> IntStrPairVec;struct IntStrPairComparePredicate{    IntStrPairComparePredicate() : dummy(0) {}	bool operator()(const IntStrPair pair1, const IntStrPair pair2) const	{		return (pair1.second.compare(pair2.second) < 0);	}	int dummy;};/* HELPER: Create planetary submenu for context menu, return menu pointer. */static GtkMenu* CreatePlanetarySystemMenu(string parentName, const PlanetarySystem* psys){	/*	 * Modified from winmain.cpp	 *	 * Use some vectors to categorize and sort the bodies within this	 * PlanetarySystem. In order to generate sorted menus, we must carry the	 * name and menu index as a single unit in the sort. One obvious way is to	 * create a vector<pair<int,string>> and then use a comparison predicate	 * to sort the vector based on the string in each pair.	 */	/* Declare vector<pair<int,string>> objects for each classification of body */	vector<IntStrPair> asteroids;	vector<IntStrPair> comets;	vector<IntStrPair> invisibles;	vector<IntStrPair> moons;	vector<IntStrPair> planets;	vector<IntStrPair> spacecraft;	/* We will use these objects to iterate over all the above vectors */	vector<IntStrPairVec> objects;	vector<string> menuNames;	/* Place each body in the correct vector based on classification */	GtkWidget* menu = gtk_menu_new();	for (int i = 0; i < psys->getSystemSize(); i++)	{		Body* body = psys->getBody(i);		switch(body->getClassification())		{			case Body::Asteroid:				asteroids.push_back(make_pair(i, body->getName()));				break;			case Body::Comet:				comets.push_back(make_pair(i, body->getName()));				break;			case Body::Invisible:				invisibles.push_back(make_pair(i, body->getName()));				break;			case Body::Moon:				moons.push_back(make_pair(i, body->getName()));				break;			case Body::Planet:				planets.push_back(make_pair(i, body->getName()));				break;			case Body::Spacecraft:				spacecraft.push_back(make_pair(i, body->getName()));				break;		}	}	/* Add each vector of PlanetarySystem bodies to a vector to iterate over */	objects.push_back(asteroids);	menuNames.push_back("Asteroids");	objects.push_back(comets);	menuNames.push_back("Comets");	objects.push_back(invisibles);	menuNames.push_back("Invisibles");	objects.push_back(moons);	menuNames.push_back("Moons");	objects.push_back(planets);	menuNames.push_back("Planets");	objects.push_back(spacecraft);	menuNames.push_back("Spacecraft");	/* Now sort each vector and generate submenus */	IntStrPairComparePredicate pred;	vector<IntStrPairVec>::iterator obj;	vector<IntStrPair>::iterator it;	vector<string>::iterator menuName;	GtkWidget* subMenu;	int numSubMenus;	/* Count how many submenus we need to create */	numSubMenus = 0;	for (obj=objects.begin(); obj != objects.end(); obj++)	{		if (obj->size() > 0)			numSubMenus++;	}	menuName = menuNames.begin();	for (obj=objects.begin(); obj != objects.end(); obj++)	{		/* Only generate a submenu if the vector is not empty */		if (obj->size() > 0)		{			/* Don't create a submenu for a single item */			if (obj->size() == 1)			{				it=obj->begin();				AppendMenu(menu, GTK_SIGNAL_FUNC(handleContextPlanet), it->second.c_str(), GINT_TO_POINTER(it->first));			}			else			{				/* Skip sorting if we are dealing with the planets in our own				 * Solar System. */				if (parentName != "Sol" || *menuName != "Planets")					sort(obj->begin(), obj->end(), pred);				if (numSubMenus > 1)				{					/* Add items to submenu */					subMenu = gtk_menu_new();					for(it=obj->begin(); it != obj->end(); it++)						AppendMenu(subMenu, GTK_SIGNAL_FUNC(handleContextPlanet), it->second.c_str(), GINT_TO_POINTER(it->first));					gtk_menu_item_set_submenu(AppendMenu(menu, NULL, menuName->c_str(), 0), GTK_WIDGET(subMenu));				}				else				{					/* Just add items to the popup */					for(it=obj->begin(); it != obj->end(); it++)						AppendMenu(menu, GTK_SIGNAL_FUNC(handleContextPlanet), it->second.c_str(), GINT_TO_POINTER(it->first));				}			}		}		menuName++;	}	return GTK_MENU(menu);}/* HELPER: Create surface submenu for context menu, return menu pointer. */static GtkMenu* CreateAlternateSurfaceMenu(const vector<string>& surfaces){	GtkWidget* menu = gtk_menu_new();	AppendMenu(menu, GTK_SIGNAL_FUNC(handleContextSurface), "Normal", 0);	for (guint i = 0; i < surfaces.size(); i++)	{		AppendMenu(menu, GTK_SIGNAL_FUNC(handleContextSurface), surfaces[i].c_str(), GINT_TO_POINTER(i+1));	}	return GTK_MENU(menu);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?