- Updated help menu web links (removed map-center.com, added ETB documentation) ...
[xonotic/netradiant.git] / radiant / pluginmenu.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "pluginmenu.h"
23
24 #include "stream/textstream.h"
25
26 #include <gtk/gtkmenu.h>
27 #include <gtk/gtkmenuitem.h>
28
29 #include "gtkutil/pointer.h"
30 #include "gtkutil/menu.h"
31
32 #include "pluginmanager.h"
33 #include "mainframe.h"
34 #include "preferences.h"
35
36
37 int m_nNextPlugInID = 0;
38
39 void plugin_activated(GtkWidget* widget, gpointer data)
40 {
41   const char* str = (const char*)g_object_get_data(G_OBJECT(widget),"command");
42   GetPlugInMgr().Dispatch(gpointer_to_int(data), str);
43 }
44
45 #include <stack>
46 typedef std::stack<GtkWidget*> WidgetStack;
47
48 void PlugInMenu_Add(GtkMenu* plugin_menu, IPlugIn* pPlugIn)
49 {
50   GtkWidget *menu, *item, *parent, *subMenu;
51   const char *menuText, *menuCommand;
52   WidgetStack menuStack;
53   
54   parent = gtk_menu_item_new_with_label (pPlugIn->getMenuName());
55   gtk_widget_show (parent);
56   gtk_container_add (GTK_CONTAINER (plugin_menu), parent);
57
58   std::size_t nCount = pPlugIn->getCommandCount();
59   if (nCount > 0)
60   {
61     menu = gtk_menu_new();
62     if (g_Layout_enableDetachableMenus.m_value)
63       menu_tearoff (GTK_MENU(menu));
64     while (nCount > 0)
65     {
66       menuText = pPlugIn->getCommandTitle(--nCount);
67       menuCommand = pPlugIn->getCommand(nCount);
68       
69       if (menuText != 0 && strlen(menuText) > 0)
70       {
71         if (!strcmp(menuText, "-"))
72         {
73           item = gtk_menu_item_new();
74           gtk_widget_set_sensitive (item, FALSE);
75         }
76         else if (!strcmp(menuText, ">"))
77         {
78           menuText = pPlugIn->getCommandTitle(--nCount);
79           menuCommand = pPlugIn->getCommand(nCount);
80           if (!strcmp(menuText, "-") || !strcmp(menuText, ">") || !strcmp(menuText, "<"))
81           {
82             globalErrorStream() << pPlugIn->getMenuName() << " Invalid title (" << menuText << ") for submenu.\n";
83             continue;
84           }
85           
86           item = gtk_menu_item_new_with_label(menuText);
87           gtk_widget_show (item);
88           gtk_container_add (GTK_CONTAINER (menu), item);
89           
90           subMenu = gtk_menu_new();
91           gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), subMenu);
92           menuStack.push(menu);
93           menu = subMenu;
94           continue;
95         }
96         else if (!strcmp(menuText, "<"))
97         {
98           if (!menuStack.empty())
99           {
100               menu = menuStack.top();
101               menuStack.pop();
102           }
103           else
104           {
105             globalErrorStream() << pPlugIn->getMenuName() << ": Attempt to end non-existent submenu ignored.\n";
106           }
107           continue;
108         }
109         else
110         {
111           item = gtk_menu_item_new_with_label (menuText);
112           g_object_set_data(G_OBJECT(item),"command", const_cast<gpointer>(static_cast<const void*>(menuCommand)));
113           g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(plugin_activated), gint_to_pointer(m_nNextPlugInID));
114         }
115         gtk_widget_show (item);
116         gtk_container_add (GTK_CONTAINER (menu), item);
117         pPlugIn->addMenuID(m_nNextPlugInID++);
118       }
119     }
120     if (!menuStack.empty())
121     {
122       std::size_t size = menuStack.size();
123       if (size != 0)
124       {
125         globalErrorStream() << pPlugIn->getMenuName() << " mismatched > <. " << Unsigned(size) << " submenu(s) not closed.\n";
126       }
127       for (std::size_t i = 0; i < (size -1); i++)
128       {
129         menuStack.pop();
130       }
131       menu = menuStack.top();
132       menuStack.pop();
133     }
134     gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
135   }
136 }
137
138 GtkMenu* g_plugins_menu = 0;
139 GtkMenuItem* g_plugins_menu_separator = 0;
140
141 void PluginsMenu_populate()
142 {
143   class PluginsMenuConstructor : public PluginsVisitor
144   {
145     GtkMenu* m_menu;
146   public:
147     PluginsMenuConstructor(GtkMenu* menu) : m_menu(menu)
148     {
149     }
150     void visit(IPlugIn& plugin)
151     {
152       PlugInMenu_Add(m_menu, &plugin);
153     }
154   };
155   
156   PluginsMenuConstructor constructor(g_plugins_menu);
157   GetPlugInMgr().constructMenu(constructor);
158 }
159
160 void PluginsMenu_clear()
161 {
162   m_nNextPlugInID = 0;
163
164   GList* lst = g_list_find (gtk_container_children(GTK_CONTAINER(g_plugins_menu)), GTK_WIDGET(g_plugins_menu_separator));
165   while (lst->next)
166   {
167     gtk_container_remove (GTK_CONTAINER(g_plugins_menu), GTK_WIDGET (lst->next->data));
168     lst = g_list_find (gtk_container_children(GTK_CONTAINER(g_plugins_menu)),  GTK_WIDGET(g_plugins_menu_separator));
169   }
170 }
171
172 GtkMenuItem* create_plugins_menu()
173 {
174   // Plugins menu
175   GtkMenuItem* plugins_menu_item = new_sub_menu_item_with_mnemonic("_Plugins");
176   GtkMenu* menu = GTK_MENU(gtk_menu_item_get_submenu(plugins_menu_item));
177   if (g_Layout_enableDetachableMenus.m_value)
178   {
179     menu_tearoff(menu);
180   }
181
182   g_plugins_menu = menu;
183
184   //TODO: some modules/plugins do not yet support refresh
185 #if 0
186   create_menu_item_with_mnemonic(menu, "Refresh", FreeCaller<Restart>());
187
188   // NOTE: the seperator is used when doing a refresh of the list, everything past the seperator is removed
189   g_plugins_menu_separator = menu_separator(menu);
190 #endif
191
192   PluginsMenu_populate();
193
194   return plugins_menu_item;
195 }
196