fb5c55b8bb2c5241e034aeeaa137e4dd6d78a0d6
[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     while (nCount > 0)
63     {
64       menuText = pPlugIn->getCommandTitle(--nCount);
65       menuCommand = pPlugIn->getCommand(nCount);
66       
67       if (menuText != 0 && strlen(menuText) > 0)
68       {
69         if (!strcmp(menuText, "-"))
70         {
71           item = gtk_menu_item_new();
72           gtk_widget_set_sensitive (item, FALSE);
73         }
74         else if (!strcmp(menuText, ">"))
75         {
76           menuText = pPlugIn->getCommandTitle(--nCount);
77           menuCommand = pPlugIn->getCommand(nCount);
78           if (!strcmp(menuText, "-") || !strcmp(menuText, ">") || !strcmp(menuText, "<"))
79           {
80             globalErrorStream() << pPlugIn->getMenuName() << " Invalid title (" << menuText << ") for submenu.\n";
81             continue;
82           }
83           
84           item = gtk_menu_item_new_with_label(menuText);
85           gtk_widget_show (item);
86           gtk_container_add (GTK_CONTAINER (menu), item);
87           
88           subMenu = gtk_menu_new();
89           gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), subMenu);
90           menuStack.push(menu);
91           menu = subMenu;
92           continue;
93         }
94         else if (!strcmp(menuText, "<"))
95         {
96           if (!menuStack.empty())
97           {
98               menu = menuStack.top();
99               menuStack.pop();
100           }
101           else
102           {
103             globalErrorStream() << pPlugIn->getMenuName() << ": Attempt to end non-existent submenu ignored.\n";
104           }
105           continue;
106         }
107         else
108         {
109           item = gtk_menu_item_new_with_label (menuText);
110           g_object_set_data(G_OBJECT(item),"command", const_cast<gpointer>(static_cast<const void*>(menuCommand)));
111           g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(plugin_activated), gint_to_pointer(m_nNextPlugInID));
112         }
113         gtk_widget_show (item);
114         gtk_container_add (GTK_CONTAINER (menu), item);
115         pPlugIn->addMenuID(m_nNextPlugInID++);
116       }
117     }
118     if (!menuStack.empty())
119     {
120       std::size_t size = menuStack.size();
121       if (size != 0)
122       {
123         globalErrorStream() << pPlugIn->getMenuName() << " mismatched > <. " << Unsigned(size) << " submenu(s) not closed.\n";
124       }
125       for (std::size_t i = 0; i < (size -1); i++)
126       {
127         menuStack.pop();
128       }
129       menu = menuStack.top();
130       menuStack.pop();
131     }
132     gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
133   }
134 }
135
136 GtkMenu* g_plugins_menu = 0;
137 GtkMenuItem* g_plugins_menu_separator = 0;
138
139 void PluginsMenu_populate()
140 {
141   class PluginsMenuConstructor : public PluginsVisitor
142   {
143     GtkMenu* m_menu;
144   public:
145     PluginsMenuConstructor(GtkMenu* menu) : m_menu(menu)
146     {
147     }
148     void visit(IPlugIn& plugin)
149     {
150       PlugInMenu_Add(m_menu, &plugin);
151     }
152   };
153   
154   PluginsMenuConstructor constructor(g_plugins_menu);
155   GetPlugInMgr().constructMenu(constructor);
156 }
157
158 void PluginsMenu_clear()
159 {
160   m_nNextPlugInID = 0;
161
162   GList* lst = g_list_find (gtk_container_children(GTK_CONTAINER(g_plugins_menu)), GTK_WIDGET(g_plugins_menu_separator));
163   while (lst->next)
164   {
165     gtk_container_remove (GTK_CONTAINER(g_plugins_menu), GTK_WIDGET (lst->next->data));
166     lst = g_list_find (gtk_container_children(GTK_CONTAINER(g_plugins_menu)),  GTK_WIDGET(g_plugins_menu_separator));
167   }
168 }
169
170 GtkMenuItem* create_plugins_menu()
171 {
172   // Plugins menu
173   GtkMenuItem* plugins_menu_item = new_sub_menu_item_with_mnemonic("_Plugins");
174   GtkMenu* menu = GTK_MENU(gtk_menu_item_get_submenu(plugins_menu_item));
175   if (g_Layout_enableDetachableMenus.m_value)
176   {
177     menu_tearoff(menu);
178   }
179
180   g_plugins_menu = menu;
181
182   //TODO: some modules/plugins do not yet support refresh
183 #if 0
184   create_menu_item_with_mnemonic(menu, "Refresh", FreeCaller<Restart>());
185
186   // NOTE: the seperator is used when doing a refresh of the list, everything past the seperator is removed
187   g_plugins_menu_separator = menu_separator(menu);
188 #endif
189
190   PluginsMenu_populate();
191
192   return plugins_menu_item;
193 }
194