menu-context.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:12k
源码类别:

OpenGL

开发平台:

Visual C++

  1. /*
  2.  *  Celestia GTK+ Front-End
  3.  *  Copyright (C) 2005 Pat Suwalski <pat@suwalski.net>
  4.  *
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  $Id: menu-context.cpp,v 1.4 2008-01-03 00:20:33 vincent_gian Exp $
  11.  */
  12. #include <algorithm>
  13. #include <gtk/gtk.h>
  14. #include <celengine/simulation.h>
  15. #include <celestia/celestiacore.h>
  16. #include <celutil/utf8.h>
  17. #include "menu-context.h"
  18. #include "actions.h"
  19. #include "common.h"
  20. /* Definitions: Callbacks */
  21. static void wrapAction(GtkAction* action);
  22. static void menuMark();
  23. static void menuUnMark();
  24. static void handleContextPlanet(gpointer data);
  25. static void handleContextSurface(gpointer data);
  26. /* Definitions: Helpers */
  27. static GtkMenuItem* AppendMenu(GtkWidget* parent, GtkSignalFunc callback, const gchar* name, gpointer extra);
  28. static GtkMenu* CreatePlanetarySystemMenu(string parentName, const PlanetarySystem* psys);
  29. static GtkMenu* CreateAlternateSurfaceMenu(const vector<string>& surfaces);
  30. /* There is no way to pass the AppData struct to the menu at this time. This
  31.  * keeps the global variable local to this file, at least conceptually. */
  32. static AppData* app;
  33. /* Initializer. Sets the appData, since there is no way to pass it. */
  34. void initContext(AppData* a)
  35. {
  36. app = a;
  37. }
  38. /* ENTRY: Context menu (event handled by appCore)
  39.  *        Normally, float x and y, but unused, so removed. */
  40. void menuContext(float, float, Selection sel)
  41. {
  42. GtkWidget* popup = gtk_menu_new();
  43. string name;
  44. switch (sel.getType())
  45. {
  46. case Selection::Type_Body:
  47. {
  48. name = sel.body()->getName();
  49. AppendMenu(popup, NULL, name.c_str(), gtk_action_group_get_action(app->agMain, "CenterSelection"));
  50. AppendMenu(popup, NULL, NULL, 0);
  51. AppendMenu(popup, NULL, "_Goto", gtk_action_group_get_action(app->agMain, "GotoSelection"));
  52. AppendMenu(popup, NULL, "_Follow", gtk_action_group_get_action(app->agMain, "FollowSelection"));
  53. AppendMenu(popup, NULL, "S_ync Orbit", gtk_action_group_get_action(app->agMain, "SyncSelection"));
  54. /* Add info eventually:
  55.  * AppendMenu(popup, NULL, "_Info", 0); */
  56. const PlanetarySystem* satellites = sel.body()->getSatellites();
  57. if (satellites != NULL && satellites->getSystemSize() != 0)
  58. {
  59. GtkMenu* satMenu = CreatePlanetarySystemMenu(name, satellites);
  60. gtk_menu_item_set_submenu(AppendMenu(popup, NULL, "_Satellites", 0), GTK_WIDGET(satMenu));
  61. }
  62. vector<string>* altSurfaces = sel.body()->getAlternateSurfaceNames();
  63. if (altSurfaces != NULL)
  64. {
  65. if (altSurfaces->size() > 0)
  66. {
  67. GtkMenu* surfMenu = CreateAlternateSurfaceMenu(*altSurfaces);
  68. gtk_menu_item_set_submenu(AppendMenu(popup, NULL, "_Alternate Surfaces", 0), GTK_WIDGET(surfMenu));
  69. delete altSurfaces;
  70. }
  71. }
  72. }
  73. break;
  74. case Selection::Type_Star:
  75. {
  76. Simulation* sim = app->simulation;
  77. name = ReplaceGreekLetterAbbr(sim->getUniverse()->getStarCatalog()->getStarName(*(sel.star())));
  78. AppendMenu(popup, NULL, name.c_str(), gtk_action_group_get_action(app->agMain, "CenterSelection"));
  79. AppendMenu(popup, NULL, NULL, 0);
  80. AppendMenu(popup, NULL, "_Goto", gtk_action_group_get_action(app->agMain, "GotoSelection"));
  81. /* Add info eventually:
  82.  * AppendMenu(popup, NULL, "_Info", 0); */
  83. SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog();
  84. SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel.star()->getCatalogNumber());
  85. if (iter != solarSystemCatalog->end())
  86. {
  87. SolarSystem* solarSys = iter->second;
  88. GtkMenu* planetsMenu = CreatePlanetarySystemMenu(name, solarSys->getPlanets());
  89. if (name == "Sol")
  90. gtk_menu_item_set_submenu(AppendMenu(popup, NULL, "Orbiting Bodies", 0), GTK_WIDGET(planetsMenu));
  91. else
  92. gtk_menu_item_set_submenu(AppendMenu(popup, NULL, "Planets", 0), GTK_WIDGET(planetsMenu));
  93. }
  94. }
  95. break;
  96. case Selection::Type_DeepSky:
  97. {
  98. AppendMenu(popup, NULL, app->simulation->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky()).c_str(), gtk_action_group_get_action(app->agMain, "CenterSelection"));
  99. AppendMenu(popup, NULL, NULL, 0);
  100. AppendMenu(popup, NULL, "_Goto", gtk_action_group_get_action(app->agMain, "GotoSelection"));
  101. AppendMenu(popup, NULL, "_Follow", gtk_action_group_get_action(app->agMain, "FollowSelection"));
  102. /* Add info eventually:
  103.  * AppendMenu(popup, NULL, "_Info", 0); */
  104. }
  105. break;
  106. case Selection::Type_Location:
  107. break;
  108. default:
  109. break;
  110. }
  111. if (app->simulation->getUniverse()->isMarked(sel, 1))
  112. AppendMenu(popup, menuUnMark, "_Unmark", 0);
  113. else
  114. AppendMenu(popup, menuMark, "_Mark", 0);
  115. app->simulation->setSelection(sel);
  116. gtk_widget_show_all(popup);
  117. gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
  118. }
  119. /* CALLBACK: Wrap a GtkAction. The whole context menu should be replaced at
  120.  *           some point */
  121. static void wrapAction(GtkAction* action)
  122. {
  123. gtk_action_activate(action);
  124. }
  125. /* CALLBACK: Mark the selected object. Might some day be a GtkAction. */
  126. static void menuMark()
  127. {
  128. Simulation* sim = app->simulation;
  129. if (sim->getUniverse() != NULL)
  130. {
  131. MarkerRepresentation markerRep(MarkerRepresentation::Diamond, 10.0f, Color(0.0f, 1.0f, 0.0f, 0.9f));
  132. sim->getUniverse()->markObject(sim->getSelection(), markerRep, 1);
  133. }
  134. }
  135. /* CALLBACK: Unmark the selected object. Might some day be a GtkAction. */
  136. static void menuUnMark()
  137. {
  138. Simulation* sim = app->simulation;
  139. if (sim->getUniverse() != NULL)
  140. sim->getUniverse()->unmarkObject(sim->getSelection(), 1);
  141. }
  142. /* CALLBACK: Handle a planetary selection from the context menu. */
  143. static void handleContextPlanet(gpointer data)
  144. {
  145. int value = GPOINTER_TO_INT(data);
  146. /* Handle the satellite/child object submenu */
  147. Selection sel = app->simulation->getSelection();
  148. switch (sel.getType())
  149. {
  150. case Selection::Type_Star:
  151. app->simulation->selectPlanet(value);
  152. break;
  153. case Selection::Type_Body:
  154. {
  155. PlanetarySystem* satellites = (PlanetarySystem*) sel.body()->getSatellites();
  156. app->simulation->setSelection(Selection(satellites->getBody(value)));
  157. break;
  158. }
  159. case Selection::Type_DeepSky:
  160. /* Current deep sky object/galaxy implementation does
  161.  * not have children to select. */
  162. break;
  163. case Selection::Type_Location:
  164. break;
  165. default:
  166. break;
  167. }
  168. }
  169. /* CALLBACK: Handle an alternate surface from the context menu. */
  170. static void handleContextSurface(gpointer data)
  171. {
  172. int value = GPOINTER_TO_INT(data);
  173. /* Handle the alternate surface submenu */
  174. Selection sel = app->simulation->getSelection();
  175. if (sel.body() != NULL)
  176. {
  177. guint index = value - 1;
  178. vector<string>* surfNames = sel.body()->getAlternateSurfaceNames();
  179. if (surfNames != NULL)
  180. {
  181. string surfName;
  182. if (index < surfNames->size())
  183. surfName = surfNames->at(index);
  184. app->simulation->getActiveObserver()->setDisplayedSurface(surfName);
  185. delete surfNames;
  186. }
  187. }
  188. }
  189. /* HELPER: Append a menu item and return pointer. Used for context menu. */
  190. static GtkMenuItem* AppendMenu(GtkWidget* parent, GtkSignalFunc callback, const gchar* name, gpointer extra)
  191. {
  192. GtkWidget* menuitem;
  193. gpointer data;
  194. /* Check for separator */
  195. if (name == NULL)
  196. menuitem = gtk_separator_menu_item_new();
  197. else
  198. menuitem = gtk_menu_item_new_with_mnemonic(name);
  199. /* If no callback was provided, pass GtkAction, else convert to pointer */
  200. if (callback == NULL && extra != 0)
  201. {
  202. callback = G_CALLBACK(wrapAction);
  203. data = extra;
  204. }
  205. else
  206. data = GINT_TO_POINTER(extra);
  207. /* Add handler */
  208. if (callback != NULL)
  209. g_signal_connect_swapped (G_OBJECT(menuitem), "activate",
  210.                           G_CALLBACK(callback),
  211.                           data);
  212. gtk_menu_shell_append(GTK_MENU_SHELL(parent), menuitem);
  213. return GTK_MENU_ITEM(menuitem);
  214. }
  215. /* Typedefs and structs for sorting objects by name in context menu. */
  216. typedef pair<int,string> IntStrPair;
  217. typedef vector<IntStrPair> IntStrPairVec;
  218. struct IntStrPairComparePredicate
  219. {
  220.     IntStrPairComparePredicate() : dummy(0) {}
  221. bool operator()(const IntStrPair pair1, const IntStrPair pair2) const
  222. {
  223. return (pair1.second.compare(pair2.second) < 0);
  224. }
  225. int dummy;
  226. };
  227. /* HELPER: Create planetary submenu for context menu, return menu pointer. */
  228. static GtkMenu* CreatePlanetarySystemMenu(string parentName, const PlanetarySystem* psys)
  229. {
  230. /*
  231.  * Modified from winmain.cpp
  232.  *
  233.  * Use some vectors to categorize and sort the bodies within this
  234.  * PlanetarySystem. In order to generate sorted menus, we must carry the
  235.  * name and menu index as a single unit in the sort. One obvious way is to
  236.  * create a vector<pair<int,string>> and then use a comparison predicate
  237.  * to sort the vector based on the string in each pair.
  238.  */
  239. /* Declare vector<pair<int,string>> objects for each classification of body */
  240. vector<IntStrPair> asteroids;
  241. vector<IntStrPair> comets;
  242. vector<IntStrPair> invisibles;
  243. vector<IntStrPair> moons;
  244. vector<IntStrPair> planets;
  245. vector<IntStrPair> spacecraft;
  246. /* We will use these objects to iterate over all the above vectors */
  247. vector<IntStrPairVec> objects;
  248. vector<string> menuNames;
  249. /* Place each body in the correct vector based on classification */
  250. GtkWidget* menu = gtk_menu_new();
  251. for (int i = 0; i < psys->getSystemSize(); i++)
  252. {
  253. Body* body = psys->getBody(i);
  254. switch(body->getClassification())
  255. {
  256. case Body::Asteroid:
  257. asteroids.push_back(make_pair(i, body->getName()));
  258. break;
  259. case Body::Comet:
  260. comets.push_back(make_pair(i, body->getName()));
  261. break;
  262. case Body::Invisible:
  263. invisibles.push_back(make_pair(i, body->getName()));
  264. break;
  265. case Body::Moon:
  266. moons.push_back(make_pair(i, body->getName()));
  267. break;
  268. case Body::Planet:
  269. planets.push_back(make_pair(i, body->getName()));
  270. break;
  271. case Body::Spacecraft:
  272. spacecraft.push_back(make_pair(i, body->getName()));
  273. break;
  274. }
  275. }
  276. /* Add each vector of PlanetarySystem bodies to a vector to iterate over */
  277. objects.push_back(asteroids);
  278. menuNames.push_back("Asteroids");
  279. objects.push_back(comets);
  280. menuNames.push_back("Comets");
  281. objects.push_back(invisibles);
  282. menuNames.push_back("Invisibles");
  283. objects.push_back(moons);
  284. menuNames.push_back("Moons");
  285. objects.push_back(planets);
  286. menuNames.push_back("Planets");
  287. objects.push_back(spacecraft);
  288. menuNames.push_back("Spacecraft");
  289. /* Now sort each vector and generate submenus */
  290. IntStrPairComparePredicate pred;
  291. vector<IntStrPairVec>::iterator obj;
  292. vector<IntStrPair>::iterator it;
  293. vector<string>::iterator menuName;
  294. GtkWidget* subMenu;
  295. int numSubMenus;
  296. /* Count how many submenus we need to create */
  297. numSubMenus = 0;
  298. for (obj=objects.begin(); obj != objects.end(); obj++)
  299. {
  300. if (obj->size() > 0)
  301. numSubMenus++;
  302. }
  303. menuName = menuNames.begin();
  304. for (obj=objects.begin(); obj != objects.end(); obj++)
  305. {
  306. /* Only generate a submenu if the vector is not empty */
  307. if (obj->size() > 0)
  308. {
  309. /* Don't create a submenu for a single item */
  310. if (obj->size() == 1)
  311. {
  312. it=obj->begin();
  313. AppendMenu(menu, GTK_SIGNAL_FUNC(handleContextPlanet), it->second.c_str(), GINT_TO_POINTER(it->first));
  314. }
  315. else
  316. {
  317. /* Skip sorting if we are dealing with the planets in our own
  318.  * Solar System. */
  319. if (parentName != "Sol" || *menuName != "Planets")
  320. sort(obj->begin(), obj->end(), pred);
  321. if (numSubMenus > 1)
  322. {
  323. /* Add items to submenu */
  324. subMenu = gtk_menu_new();
  325. for(it=obj->begin(); it != obj->end(); it++)
  326. AppendMenu(subMenu, GTK_SIGNAL_FUNC(handleContextPlanet), it->second.c_str(), GINT_TO_POINTER(it->first));
  327. gtk_menu_item_set_submenu(AppendMenu(menu, NULL, menuName->c_str(), 0), GTK_WIDGET(subMenu));
  328. }
  329. else
  330. {
  331. /* Just add items to the popup */
  332. for(it=obj->begin(); it != obj->end(); it++)
  333. AppendMenu(menu, GTK_SIGNAL_FUNC(handleContextPlanet), it->second.c_str(), GINT_TO_POINTER(it->first));
  334. }
  335. }
  336. }
  337. menuName++;
  338. }
  339. return GTK_MENU(menu);
  340. }
  341. /* HELPER: Create surface submenu for context menu, return menu pointer. */
  342. static GtkMenu* CreateAlternateSurfaceMenu(const vector<string>& surfaces)
  343. {
  344. GtkWidget* menu = gtk_menu_new();
  345. AppendMenu(menu, GTK_SIGNAL_FUNC(handleContextSurface), "Normal", 0);
  346. for (guint i = 0; i < surfaces.size(); i++)
  347. {
  348. AppendMenu(menu, GTK_SIGNAL_FUNC(handleContextSurface), surfaces[i].c_str(), GINT_TO_POINTER(i+1));
  349. }
  350. return GTK_MENU(menu);
  351. }