allow changing the "Alternate texture projection" mode at runtime (requires Map_New...
[divverent/netradiant.git] / radiant / feedback.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 //-----------------------------------------------------------------------------
23 //
24 // DESCRIPTION:
25 // classes used for describing geometry information from q3map feedback
26 //
27
28 #include "feedback.h"
29
30 #include "debugging/debugging.h"
31
32 #include "igl.h"
33 #include "iselection.h"
34
35 #include <gtk/gtktreeview.h>
36 #include <gtk/gtktreeselection.h>
37 #include <gtk/gtkliststore.h>
38 #include <gtk/gtkcellrenderertext.h>
39 #include <gtk/gtkwindow.h>
40 #include <gtk/gtkscrolledwindow.h>
41
42 #include "map.h"
43 #include "dialog.h"
44 #include "mainframe.h"
45
46
47 CDbgDlg g_DbgDlg;
48
49 void Feedback_draw2D(VIEWTYPE viewType)
50 {
51   g_DbgDlg.draw2D(viewType);
52 }
53
54 void CSelectMsg::saxStartElement(message_info_t *ctx, const xmlChar *name, const xmlChar **attrs)
55 {
56   if(string_equal(reinterpret_cast<const char*>(name), "select"))
57   {
58     // read the message
59     ESelectState = SELECT_MESSAGE;
60   }
61   else
62   {
63     // read the brush
64     ASSERT_MESSAGE(string_equal(reinterpret_cast<const char*>(name), "brush"), "FEEDBACK PARSE ERROR");
65     ASSERT_MESSAGE(ESelectState == SELECT_MESSAGE, "FEEDBACK PARSE ERROR");
66     ESelectState = SELECT_BRUSH;
67     globalOutputStream() << message.c_str() << '\n';
68   }
69 }
70
71 void CSelectMsg::saxEndElement(message_info_t *ctx, const xmlChar *name)
72 {
73   if(string_equal(reinterpret_cast<const char*>(name), "select"))
74   {
75   }
76 }
77
78 void CSelectMsg::saxCharacters(message_info_t *ctx, const xmlChar *ch, int len)
79 {
80   if(ESelectState == SELECT_MESSAGE)
81   {
82     message.write(reinterpret_cast<const char*>(ch), len);
83   }
84   else
85   {
86     brush.write(reinterpret_cast<const char*>(ch), len);
87   }
88 }
89
90 IGL2DWindow* CSelectMsg::Highlight()
91 {
92   GlobalSelectionSystem().setSelectedAll(false);
93   int entitynum, brushnum;
94   if(sscanf(reinterpret_cast<const char*>(brush.c_str()), "%i %i", &entitynum, &brushnum) == 2)
95   {
96     SelectBrush (entitynum, brushnum);
97   }
98   return 0;
99 }
100
101 void CPointMsg::saxStartElement(message_info_t *ctx, const xmlChar *name, const xmlChar **attrs)
102 {
103   if(string_equal(reinterpret_cast<const char*>(name), "pointmsg"))
104   {
105     // read the message
106     EPointState = POINT_MESSAGE;
107   }
108   else
109   {
110     // read the brush
111     ASSERT_MESSAGE(string_equal(reinterpret_cast<const char*>(name), "point"), "FEEDBACK PARSE ERROR");
112     ASSERT_MESSAGE(EPointState == POINT_MESSAGE, "FEEDBACK PARSE ERROR");
113     EPointState = POINT_POINT;
114     globalOutputStream() << message.c_str() << '\n';
115   }
116 }
117
118 void CPointMsg::saxEndElement (message_info_t *ctx, const xmlChar *name)
119 {
120   if(string_equal(reinterpret_cast<const char*>(name), "pointmsg"))
121   {
122   }
123   else if(string_equal(reinterpret_cast<const char*>(name), "point"))
124   {
125     sscanf(point.c_str(), "%g %g %g", &(pt[0]), &(pt[1]), &(pt[2]));
126     point.clear();
127   }
128 }
129
130 void CPointMsg::saxCharacters (message_info_t *ctx, const xmlChar *ch, int len)
131 {
132   if(EPointState == POINT_MESSAGE)
133   {
134     message.write(reinterpret_cast<const char*>(ch), len);
135   }
136   else
137   {
138     ASSERT_MESSAGE(EPointState == POINT_POINT, "FEEDBACK PARSE ERROR");
139     point.write(reinterpret_cast<const char*>(ch), len);
140   }
141 }
142
143 IGL2DWindow* CPointMsg::Highlight()
144 {
145   return this;
146 }
147
148 void CPointMsg::DropHighlight()
149 {
150 }
151
152 void CPointMsg::Draw2D( VIEWTYPE vt )
153 {
154   int nDim1 = (vt == YZ) ? 1 : 0;
155   int nDim2 = (vt == XY) ? 1 : 2;
156   glPointSize(4);
157   glColor3f(1.0f,0.0f,0.0f);
158   glBegin (GL_POINTS);
159   glVertex2f (pt[nDim1], pt[nDim2]);
160   glEnd();
161   glBegin (GL_LINE_LOOP);
162   glVertex2f (pt[nDim1]-8, pt[nDim2]-8);
163   glVertex2f (pt[nDim1]+8, pt[nDim2]-8);
164   glVertex2f (pt[nDim1]+8, pt[nDim2]+8);
165   glVertex2f (pt[nDim1]-8, pt[nDim2]+8);
166   glEnd();
167 }
168
169 void CWindingMsg::saxStartElement(message_info_t *ctx, const xmlChar *name, const xmlChar **attrs)
170 {
171   if(string_equal(reinterpret_cast<const char*>(name), "windingmsg"))
172   {
173     // read the message
174     EPointState = WINDING_MESSAGE;
175   }
176   else
177   {
178     // read the brush
179     ASSERT_MESSAGE(string_equal(reinterpret_cast<const char*>(name), "winding"), "FEEDBACK PARSE ERROR");
180     ASSERT_MESSAGE(EPointState == WINDING_MESSAGE, "FEEDBACK PARSE ERROR");
181     EPointState = WINDING_WINDING;
182     globalOutputStream() << message.c_str() << '\n';
183   }
184 }
185
186 void CWindingMsg::saxEndElement(message_info_t *ctx, const xmlChar *name)
187 {
188   if(string_equal(reinterpret_cast<const char*>(name), "windingmsg"))
189   {
190   }
191   else if(string_equal(reinterpret_cast<const char*>(name), "winding"))
192   {
193     const char* c = winding.c_str();
194     sscanf(c, "%i ", &numpoints);
195
196     int i = 0;
197     for(; i < numpoints; i++)
198     {
199       c = strchr(c + 1, '(');
200       if(c) // even if we are given the number of points when the cycle begins .. don't trust it too much
201         sscanf(c, "(%g %g %g)", &wt[i][0], &wt[i][1], &wt[i][2]);
202       else
203         break;
204     }
205     numpoints = i;
206   }
207 }
208
209 void CWindingMsg::saxCharacters(message_info_t *ctx, const xmlChar *ch, int len)
210 {
211   if(EPointState == WINDING_MESSAGE)
212   {
213     message.write(reinterpret_cast<const char*>(ch), len);
214   }
215   else
216   {
217     ASSERT_MESSAGE(EPointState == WINDING_WINDING, "FEEDBACK PARSE ERROR");
218     winding.write(reinterpret_cast<const char*>(ch), len);
219   }
220 }
221
222 IGL2DWindow* CWindingMsg::Highlight()
223 {
224   return this;
225 }
226
227 void CWindingMsg::DropHighlight()
228 {
229 }
230
231 void CWindingMsg::Draw2D( VIEWTYPE vt )
232 {
233   int i;
234
235   int nDim1 = (vt == YZ) ? 1 : 0;
236   int nDim2 = (vt == XY) ? 1 : 2;
237   glColor3f(1.0f,0.f,0.0f);
238
239   glPointSize(4);
240   glBegin (GL_POINTS);
241   for(i = 0; i < numpoints; i++)
242     glVertex2f (wt[i][nDim1], wt[i][nDim2]);
243   glEnd();
244   glPointSize(1);
245
246   glEnable (GL_BLEND);
247   glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
248   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
249   glColor4f(0.133f,0.4f,1.0f,0.5f);
250   glBegin (GL_POLYGON);
251         for(i = 0; i < numpoints; i++)
252           glVertex2f (wt[i][nDim1], wt[i][nDim2]);
253   glEnd();
254   glDisable (GL_BLEND);
255 }
256
257 // triggered when the user selects an entry in the feedback box
258 static void feedback_selection_changed(GtkTreeSelection* selection, gpointer data)
259 {
260   g_DbgDlg.DropHighlight();
261
262   GtkTreeModel* model;
263   GtkTreeIter selected;
264   if(gtk_tree_selection_get_selected(selection, &model, &selected))
265   {
266     GtkTreePath* path = gtk_tree_model_get_path(model, &selected);
267       g_DbgDlg.SetHighlight(gtk_tree_path_get_indices(path)[0]);
268     gtk_tree_path_free(path);
269   }
270 }
271
272 void CDbgDlg::DropHighlight()
273 {
274   if(m_pHighlight != 0)
275   {
276     m_pHighlight->DropHighlight();
277     m_pHighlight = 0;
278     m_pDraw2D = 0;
279   }
280 }
281
282 void CDbgDlg::SetHighlight(gint row)
283 {
284   ISAXHandler *h = GetElement(row);
285   if(h != NULL)
286   {
287     m_pDraw2D = h->Highlight();
288     m_pHighlight = h;
289   }
290 }
291
292 ISAXHandler *CDbgDlg::GetElement (std::size_t row)
293 {
294   return static_cast<ISAXHandler *>(g_ptr_array_index(m_pFeedbackElements, gint(row)));
295 }
296
297 void CDbgDlg::Init()
298 {
299   DropHighlight();
300
301   // free all the ISAXHandler*, clean it
302   while (m_pFeedbackElements->len)
303   {
304     static_cast<ISAXHandler *>(g_ptr_array_index (m_pFeedbackElements, 0))->Release();
305     g_ptr_array_remove_index (m_pFeedbackElements, 0);
306   }
307
308   if(m_clist != NULL)
309     gtk_list_store_clear (m_clist);
310 }
311
312 void CDbgDlg::Push (ISAXHandler *pHandler)
313 {
314   // push in the list
315   g_ptr_array_add (m_pFeedbackElements, (void *)pHandler);
316
317   if(GetWidget() == 0)
318   {
319     Create();
320   }
321
322   // put stuff in the list
323   gtk_list_store_clear (m_clist);
324   for(std::size_t i = 0; i < static_cast<std::size_t>(m_pFeedbackElements->len); ++i)
325   {
326     GtkTreeIter iter;
327     gtk_list_store_append(m_clist, &iter);
328     gtk_list_store_set(m_clist, &iter, 0, GetElement(i)->getName(), -1);
329   }
330
331   ShowDlg();
332 }
333
334 GtkWindow* CDbgDlg::BuildDialog()
335 {
336   GtkWindow* window = create_floating_window("Q3Map debug window", MainFrame_getWindow());
337
338   GtkWidget* scr = gtk_scrolled_window_new (NULL, NULL);
339   gtk_widget_show (scr);
340   gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (scr));
341   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
342   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scr), GTK_SHADOW_IN);
343
344   {
345     GtkListStore* store = gtk_list_store_new(1, G_TYPE_STRING);
346
347     GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
348     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
349
350     {
351       GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
352       GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 0, NULL);
353       gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
354     }
355
356     {
357       GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
358       gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
359       g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(feedback_selection_changed), NULL);
360     }
361
362     gtk_widget_show(view);
363
364     gtk_container_add(GTK_CONTAINER (scr), view);
365
366     g_object_unref(G_OBJECT(store));
367
368     m_clist = store;
369   }
370
371   return window;
372 }