1d08fd43ef329a59891a216981a39b262b6e0d67
[xonotic/netradiant.git] / radiant / patchmanip.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 "patchmanip.h"
23
24 #include "debugging/debugging.h"
25
26
27 #include "iselection.h"
28 #include "ipatch.h"
29
30 #include "math/vector.h"
31 #include "math/aabb.h"
32 #include "generic/callback.h"
33
34 #include "gtkutil/menu.h"
35 #include "gtkutil/image.h"
36 #include "map.h"
37 #include "mainframe.h"
38 #include "commands.h"
39 #include "gtkmisc.h"
40 #include "gtkdlgs.h"
41 #include "texwindow.h"
42 #include "xywindow.h"
43 #include "select.h"
44 #include "patch.h"
45 #include "grid.h"
46
47 PatchCreator* g_patchCreator = 0;
48
49 void Scene_PatchConstructPrefab(scene::Graph& graph, const AABB& aabb, const char* shader, EPatchPrefab eType, int axis, std::size_t width = 3, std::size_t height = 3)
50 {
51   GlobalSelectionSystem().setSelectedAll(false);
52
53   NodeSmartReference node(g_patchCreator->createPatch());
54   Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(node);
55
56   Patch* patch = Node_getPatch(node);
57   patch->SetShader(shader);
58
59   patch->ConstructPrefab(aabb, eType, axis, width, height);
60   patch->controlPointsChanged();
61
62   {
63     scene::Path patchpath(makeReference(GlobalSceneGraph().root()));
64     patchpath.push(makeReference(*Map_GetWorldspawn(g_map)));
65     patchpath.push(makeReference(node.get()));
66     Instance_getSelectable(*graph.find(patchpath))->setSelected(true);
67   }
68 }
69
70
71 void Patch_makeCaps(Patch& patch, scene::Instance& instance, EPatchCap type, const char* shader)
72 {
73   if((type == eCapEndCap || type == eCapIEndCap)
74     && patch.getWidth() != 5)
75   {
76     globalErrorStream() << "cannot create end-cap - patch width != 5\n";
77     return;
78   }
79   if((type == eCapBevel || type == eCapIBevel)
80     && patch.getWidth() != 3)
81   {
82     globalErrorStream() << "cannot create bevel-cap - patch width != 3\n";
83     return;
84   }
85   if(type == eCapCylinder
86     && patch.getWidth() != 9)
87   {
88     globalErrorStream() << "cannot create cylinder-cap - patch width != 9\n";
89     return;
90   }
91
92   {
93     NodeSmartReference cap(g_patchCreator->createPatch());
94     Node_getTraversable(instance.path().parent())->insert(cap);
95
96     patch.MakeCap(Node_getPatch(cap), type, ROW, true);
97     Node_getPatch(cap)->SetShader(shader);
98
99     scene::Path path(instance.path());
100     path.pop();
101     path.push(makeReference(cap.get()));
102     selectPath(path, true);
103   }
104
105   {
106     NodeSmartReference cap(g_patchCreator->createPatch());
107     Node_getTraversable(instance.path().parent())->insert(cap);
108
109     patch.MakeCap(Node_getPatch(cap), type, ROW, false);
110     Node_getPatch(cap)->SetShader(shader);
111
112     scene::Path path(instance.path());
113     path.pop();
114     path.push(makeReference(cap.get()));
115     selectPath(path, true);
116   }
117 }
118
119 typedef std::vector<scene::Instance*> InstanceVector;
120
121 class PatchStoreInstance
122 {
123   InstanceVector& m_instances;
124 public:
125   PatchStoreInstance(InstanceVector& instances) : m_instances(instances)
126   {
127   }
128   void operator()(PatchInstance& patch) const
129   {
130     m_instances.push_back(&patch);
131   }
132 };
133
134 enum ECapDialog {
135   PATCHCAP_BEVEL = 0,
136   PATCHCAP_ENDCAP,
137   PATCHCAP_INVERTED_BEVEL,
138   PATCHCAP_INVERTED_ENDCAP,
139   PATCHCAP_CYLINDER
140 };
141
142 EMessageBoxReturn DoCapDlg(ECapDialog *type);
143
144 void Scene_PatchDoCap_Selected(scene::Graph& graph, const char* shader)
145 {
146   ECapDialog nType;
147
148   if(DoCapDlg(&nType) == eIDOK)
149   {
150     EPatchCap eType;
151     switch(nType)
152     {
153     case PATCHCAP_INVERTED_BEVEL:
154       eType = eCapIBevel;
155       break;
156     case PATCHCAP_BEVEL:
157       eType = eCapBevel;
158       break;
159     case PATCHCAP_INVERTED_ENDCAP:
160       eType = eCapIEndCap;
161       break;
162     case PATCHCAP_ENDCAP:
163       eType = eCapEndCap;
164       break;
165     case PATCHCAP_CYLINDER:
166       eType = eCapCylinder;
167       break;
168     default:
169       ERROR_MESSAGE("invalid patch cap type");
170       return;
171     }
172   
173     InstanceVector instances;
174     Scene_forEachVisibleSelectedPatchInstance(PatchStoreInstance(instances));
175     for(InstanceVector::const_iterator i = instances.begin(); i != instances.end(); ++i)
176     {
177       Patch_makeCaps(* Node_getPatch((*i)->path().top()), *(*i), eType, shader);
178     }
179   }
180 }
181
182 Patch* Scene_GetUltimateSelectedVisiblePatch()
183 {
184   if(GlobalSelectionSystem().countSelected() != 0)
185   {
186     scene::Node& node = GlobalSelectionSystem().ultimateSelected().path().top();
187     if(node.visible())
188     {
189       return Node_getPatch(node);
190     }
191   }
192   return 0;
193 }
194
195
196 class PatchCapTexture
197 {
198 public:
199   void operator()(Patch& patch) const
200   {
201     patch.ProjectTexture(Patch::m_CycleCapIndex);
202   }
203 };
204
205 void Scene_PatchCapTexture_Selected(scene::Graph& graph)
206 {
207   Scene_forEachVisibleSelectedPatch(PatchCapTexture());
208   Patch::m_CycleCapIndex = (Patch::m_CycleCapIndex == 0) ? 1 : (Patch::m_CycleCapIndex == 1) ? 2 : 0;
209   SceneChangeNotify();
210 }
211
212 class PatchFlipTexture
213 {
214   int m_axis;
215 public:
216   PatchFlipTexture(int axis) : m_axis(axis)
217   {
218   }
219   void operator()(Patch& patch) const
220   {
221     patch.FlipTexture(m_axis);
222   }
223 };
224
225 void Scene_PatchFlipTexture_Selected(scene::Graph& graph, int axis)
226 {
227   Scene_forEachVisibleSelectedPatch(PatchFlipTexture(axis));
228 }
229
230 class PatchNaturalTexture
231 {
232 public:
233   void operator()(Patch& patch) const
234   {
235     patch.NaturalTexture();
236   }
237 };
238
239 void Scene_PatchNaturalTexture_Selected(scene::Graph& graph)
240 {
241   Scene_forEachVisibleSelectedPatch(PatchNaturalTexture());
242   SceneChangeNotify();
243 }
244
245
246 class PatchInsertRemove
247 {
248   bool m_insert, m_column, m_first;
249 public:
250   PatchInsertRemove(bool insert, bool column, bool first) : m_insert(insert), m_column(column), m_first(first)
251   {
252   }
253   void operator()(Patch& patch) const
254   {
255     patch.InsertRemove(m_insert, m_column, m_first);
256   }
257 };
258
259 void Scene_PatchInsertRemove_Selected(scene::Graph& graph, bool bInsert, bool bColumn, bool bFirst)
260 {
261   Scene_forEachVisibleSelectedPatch(PatchInsertRemove(bInsert, bColumn, bFirst));
262 }
263
264 class PatchInvertMatrix
265 {
266 public:
267   void operator()(Patch& patch) const
268   {
269     patch.InvertMatrix();
270   }
271 };
272
273 void Scene_PatchInvert_Selected(scene::Graph& graph)
274 {
275   Scene_forEachVisibleSelectedPatch(PatchInvertMatrix());
276 }
277
278 class PatchRedisperse
279 {
280   EMatrixMajor m_major;
281 public:
282   PatchRedisperse(EMatrixMajor major) : m_major(major)
283   {
284   }
285   void operator()(Patch& patch) const
286   {
287     patch.Redisperse(m_major);
288   }
289 };
290
291 void Scene_PatchRedisperse_Selected(scene::Graph& graph, EMatrixMajor major)
292 {
293   Scene_forEachVisibleSelectedPatch(PatchRedisperse(major));
294 }
295
296 class PatchTransposeMatrix
297 {
298 public:
299   void operator()(Patch& patch) const
300   {
301     patch.TransposeMatrix();
302   }
303 };
304
305 void Scene_PatchTranspose_Selected(scene::Graph& graph)
306 {
307   Scene_forEachVisibleSelectedPatch(PatchTransposeMatrix());
308 }
309
310 class PatchSetShader
311 {
312   const char* m_name;
313 public:
314   PatchSetShader(const char* name)
315     : m_name(name)
316   {
317   }
318   void operator()(Patch& patch) const
319   {
320     patch.SetShader(m_name);
321   }
322 };
323
324 void Scene_PatchSetShader_Selected(scene::Graph& graph, const char* name)
325 {
326   Scene_forEachVisibleSelectedPatch(PatchSetShader(name));
327   SceneChangeNotify();
328 }
329
330 void Scene_PatchGetShader_Selected(scene::Graph& graph, CopiedString& name)
331 {
332   Patch* patch = Scene_GetUltimateSelectedVisiblePatch();
333   if(patch != 0)
334   {
335     name = patch->GetShader();
336   }
337 }
338
339 class PatchSelectByShader
340 {
341   const char* m_name;
342 public:
343   inline PatchSelectByShader(const char* name)
344     : m_name(name)
345   {
346   }
347   void operator()(PatchInstance& patch) const
348   {
349     if(shader_equal(patch.getPatch().GetShader(), m_name))
350     {
351       patch.setSelected(true);
352     }
353   }
354 };
355
356 void Scene_PatchSelectByShader(scene::Graph& graph, const char* name)
357 {
358   Scene_forEachVisiblePatchInstance(PatchSelectByShader(name));
359 }
360
361
362 class PatchFindReplaceShader
363 {
364   const char* m_find;
365   const char* m_replace;
366 public:
367   PatchFindReplaceShader(const char* find, const char* replace) : m_find(find), m_replace(replace)
368   {
369   }
370   void operator()(Patch& patch) const
371   {
372     if(shader_equal(patch.GetShader(), m_find))
373     {
374       patch.SetShader(m_replace);
375     }
376   }
377 };
378
379 void Scene_PatchFindReplaceShader(scene::Graph& graph, const char* find, const char* replace)
380 {
381   Scene_forEachVisiblePatch(PatchFindReplaceShader(find, replace));
382 }
383
384 void Scene_PatchFindReplaceShader_Selected(scene::Graph& graph, const char* find, const char* replace)
385 {
386   Scene_forEachVisibleSelectedPatch(PatchFindReplaceShader(find, replace));
387 }
388
389
390 AABB PatchCreator_getBounds()
391 {
392   AABB aabb(aabb_for_minmax(Select_getWorkZone().d_work_min, Select_getWorkZone().d_work_max));
393
394   float gridSize = GetGridSize();
395
396   if(aabb.extents[0] == 0)
397   {
398     aabb.extents[0] = gridSize;
399   }
400   if(aabb.extents[1] == 0)
401   {
402     aabb.extents[1] = gridSize;
403   }
404   if(aabb.extents[2] == 0)
405   {
406     aabb.extents[2] = gridSize;
407   }
408
409   if(aabb_valid(aabb))
410   {
411     return aabb;
412   }
413   return AABB(Vector3(0, 0, 0), Vector3(64, 64, 64));
414 }
415
416 void Patch_Cylinder()
417 {
418   UndoableCommand undo("patchCreateCylinder");
419
420   Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), eCylinder, GlobalXYWnd_getCurrentViewType());
421 }
422
423 void Patch_DenseCylinder()
424 {
425   UndoableCommand undo("patchCreateDenseCylinder");
426
427   Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), eDenseCylinder, GlobalXYWnd_getCurrentViewType());
428 }
429
430 void Patch_VeryDenseCylinder()
431 {
432   UndoableCommand undo("patchCreateVeryDenseCylinder");
433
434   Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), eVeryDenseCylinder, GlobalXYWnd_getCurrentViewType());
435 }
436
437 void Patch_SquareCylinder()
438 {
439   UndoableCommand undo("patchCreateSquareCylinder");
440
441   Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), eSqCylinder, GlobalXYWnd_getCurrentViewType());
442 }
443
444 void Patch_Endcap()
445 {
446   UndoableCommand undo("patchCreateCaps");
447
448   Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), eEndCap, GlobalXYWnd_getCurrentViewType());
449 }
450
451 void Patch_Bevel()
452 {
453   UndoableCommand undo("patchCreateBevel");
454
455   Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), eBevel, GlobalXYWnd_getCurrentViewType());
456 }
457
458 void Patch_SquareBevel()
459 {
460 }
461
462 void Patch_SquareEndcap()
463 {
464 }
465
466 void Patch_Cone()
467 {
468   UndoableCommand undo("patchCreateCone");
469
470   Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), eCone, GlobalXYWnd_getCurrentViewType());
471 }
472
473 void DoNewPatchDlg();
474
475 void Patch_Plane()
476 {
477   UndoableCommand undo("patchCreatePlane");
478
479   DoNewPatchDlg();
480 }
481
482 void Patch_InsertInsertColumn()
483 {
484   UndoableCommand undo("patchInsertColumns");
485
486   Scene_PatchInsertRemove_Selected(GlobalSceneGraph(), true, true, false);
487 }
488
489 void Patch_InsertAddColumn()
490 {
491   UndoableCommand undo("patchAddColumns");
492
493   Scene_PatchInsertRemove_Selected(GlobalSceneGraph(), true, true, true);
494 }
495
496 void Patch_InsertInsertRow()
497 {
498   UndoableCommand undo("patchInsertRows");
499
500   Scene_PatchInsertRemove_Selected(GlobalSceneGraph(), true, false, false);
501 }
502
503 void Patch_InsertAddRow()
504 {
505   UndoableCommand undo("patchAddRows");
506
507   Scene_PatchInsertRemove_Selected(GlobalSceneGraph(), true, false, true);
508 }
509
510 void Patch_DeleteFirstColumn()
511 {
512   UndoableCommand undo("patchDeleteFirstColumns");
513
514   Scene_PatchInsertRemove_Selected(GlobalSceneGraph(), false, true, true);
515 }
516
517 void Patch_DeleteLastColumn()
518 {
519   UndoableCommand undo("patchDeleteLastColumns");
520
521   Scene_PatchInsertRemove_Selected(GlobalSceneGraph(), false, true, false);
522 }
523
524 void Patch_DeleteFirstRow()
525 {
526   UndoableCommand undo("patchDeleteFirstRows");
527
528   Scene_PatchInsertRemove_Selected(GlobalSceneGraph(), false, false, true);
529 }
530
531 void Patch_DeleteLastRow()
532 {
533   UndoableCommand undo("patchDeleteLastRows");
534
535   Scene_PatchInsertRemove_Selected(GlobalSceneGraph(), false, false, false);
536 }
537
538 void Patch_Invert()
539 {
540   UndoableCommand undo("patchInvert");
541
542   Scene_PatchInvert_Selected(GlobalSceneGraph());
543 }
544
545 void Patch_RedisperseRows()
546 {
547   UndoableCommand undo("patchRedisperseRows");
548
549   Scene_PatchRedisperse_Selected(GlobalSceneGraph(), COL);
550 }
551
552 void Patch_RedisperseCols()
553 {
554   UndoableCommand undo("patchRedisperseColumns");
555
556   Scene_PatchRedisperse_Selected(GlobalSceneGraph(), COL);
557 }
558
559 void Patch_Transpose()
560 {
561   UndoableCommand undo("patchTranspose");
562
563   Scene_PatchTranspose_Selected(GlobalSceneGraph());
564 }
565
566 void Patch_Cap()
567 {
568   // FIXME: add support for patch cap creation
569   // Patch_CapCurrent();
570   UndoableCommand undo("patchCreateCaps");
571
572   Scene_PatchDoCap_Selected(GlobalSceneGraph(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
573 }
574
575 void Patch_CycleProjection()
576 {
577   UndoableCommand undo("patchCycleUVProjectionAxis");
578
579   Scene_PatchCapTexture_Selected(GlobalSceneGraph());
580 }
581
582 ///\todo Unfinished.
583 void Patch_OverlayOn()
584 {
585 }
586
587 ///\todo Unfinished.
588 void Patch_OverlayOff()
589 {
590 }
591
592 void Patch_FlipTextureX()
593 {
594   UndoableCommand undo("patchFlipTextureU");
595
596   Scene_PatchFlipTexture_Selected(GlobalSceneGraph(), 0);
597 }
598
599 void Patch_FlipTextureY()
600 {
601   UndoableCommand undo("patchFlipTextureV");
602
603   Scene_PatchFlipTexture_Selected(GlobalSceneGraph(), 1);
604 }
605
606 void Patch_NaturalTexture()
607 {
608   UndoableCommand undo("patchNaturalTexture");
609
610   Scene_PatchNaturalTexture_Selected(GlobalSceneGraph());
611 }
612
613
614
615
616 #include "ifilter.h"
617
618
619 class filter_patch_all : public PatchFilter
620 {
621 public:
622   bool filter(const Patch& patch) const
623   {
624     return true;
625   }
626 };
627
628 class filter_patch_shader : public PatchFilter
629 {
630   const char* m_shader;
631 public:
632   filter_patch_shader(const char* shader) : m_shader(shader)
633   {
634   }
635   bool filter(const Patch& patch) const
636   {
637     return shader_equal(patch.GetShader(), m_shader);
638   }
639 };
640
641 class filter_patch_flags : public PatchFilter
642 {
643   int m_flags;
644 public:
645   filter_patch_flags(int flags) : m_flags(flags)
646   {
647   }
648   bool filter(const Patch& patch) const
649   {
650     return (patch.getShaderFlags() & m_flags) != 0;
651   }
652 };
653
654
655 filter_patch_all g_filter_patch_all;
656 filter_patch_shader g_filter_patch_clip("textures/common/clip");
657 filter_patch_shader g_filter_patch_weapclip("textures/common/weapclip");
658 filter_patch_flags g_filter_patch_translucent(QER_TRANS);
659
660 void PatchFilters_construct()
661 {
662   add_patch_filter(g_filter_patch_all, EXCLUDE_CURVES);
663   add_patch_filter(g_filter_patch_clip, EXCLUDE_CLIP);
664   add_patch_filter(g_filter_patch_weapclip, EXCLUDE_CLIP);
665   add_patch_filter(g_filter_patch_translucent, EXCLUDE_TRANSLUCENT);
666 }
667
668
669 #include "preferences.h"
670
671 void Patch_constructPreferences(PreferencesPage& page)
672 {
673   page.appendEntry("Patch Subdivide Threshold", g_PatchSubdivideThreshold);
674 }
675 void Patch_constructPage(PreferenceGroup& group)
676 {
677   PreferencesPage page(group.createPage("Patches", "Patch Display Preferences"));
678   Patch_constructPreferences(page);
679 }
680 void Patch_registerPreferencesPage()
681 {
682   PreferencesDialog_addDisplayPage(FreeCaller1<PreferenceGroup&, Patch_constructPage>());
683 }
684
685
686 #include "preferencesystem.h"
687
688 void PatchPreferences_construct()
689 {
690   GlobalPreferenceSystem().registerPreference("Subdivisions", IntImportStringCaller(g_PatchSubdivideThreshold), IntExportStringCaller(g_PatchSubdivideThreshold));
691 }
692
693
694 #include "generic/callback.h"
695
696 void Patch_registerCommands()
697 {
698   GlobalCommands_insert("InvertCurveTextureX", FreeCaller<Patch_FlipTextureX>(), Accelerator('I', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
699   GlobalCommands_insert("InvertCurveTextureY", FreeCaller<Patch_FlipTextureY>(), Accelerator('I', (GdkModifierType)GDK_SHIFT_MASK));
700   GlobalCommands_insert("IncPatchColumn", FreeCaller<Patch_InsertInsertColumn>(), Accelerator(GDK_KP_Add, (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
701   GlobalCommands_insert("IncPatchRow", FreeCaller<Patch_InsertInsertRow>(), Accelerator(GDK_KP_Add, (GdkModifierType)GDK_CONTROL_MASK));
702   GlobalCommands_insert("DecPatchColumn", FreeCaller<Patch_DeleteLastColumn>(), Accelerator(GDK_KP_Subtract, (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
703   GlobalCommands_insert("DecPatchRow", FreeCaller<Patch_DeleteLastRow>(), Accelerator(GDK_KP_Subtract, (GdkModifierType)GDK_CONTROL_MASK));
704   GlobalCommands_insert("NaturalizePatch", FreeCaller<Patch_NaturalTexture>(), Accelerator('N', (GdkModifierType)GDK_CONTROL_MASK));
705   GlobalCommands_insert("PatchCylinder", FreeCaller<Patch_Cylinder>());
706   GlobalCommands_insert("PatchDenseCylinder", FreeCaller<Patch_DenseCylinder>());
707   GlobalCommands_insert("PatchVeryDenseCylinder", FreeCaller<Patch_VeryDenseCylinder>());
708   GlobalCommands_insert("PatchSquareCylinder", FreeCaller<Patch_SquareCylinder>());
709   GlobalCommands_insert("PatchEndCap", FreeCaller<Patch_Endcap>());
710   GlobalCommands_insert("PatchBevel", FreeCaller<Patch_Bevel>());
711   GlobalCommands_insert("PatchSquareBevel", FreeCaller<Patch_SquareBevel>());
712   GlobalCommands_insert("PatchSquareEndcap", FreeCaller<Patch_SquareEndcap>());
713   GlobalCommands_insert("PatchCone", FreeCaller<Patch_Cone>());
714   GlobalCommands_insert("SimplePatchMesh", FreeCaller<Patch_Plane>(), Accelerator('P', (GdkModifierType)GDK_SHIFT_MASK));
715   GlobalCommands_insert("PatchInsertInsertColumn", FreeCaller<Patch_InsertInsertColumn>());
716   GlobalCommands_insert("PatchInsertAddColumn", FreeCaller<Patch_InsertAddColumn>());
717   GlobalCommands_insert("PatchInsertInsertRow", FreeCaller<Patch_InsertInsertRow>());
718   GlobalCommands_insert("PatchInsertAddRow", FreeCaller<Patch_InsertAddRow>());
719   GlobalCommands_insert("PatchDeleteFirstColumn", FreeCaller<Patch_DeleteFirstColumn>());
720   GlobalCommands_insert("PatchDeleteLastColumn", FreeCaller<Patch_DeleteLastColumn>());
721   GlobalCommands_insert("PatchDeleteFirstRow", FreeCaller<Patch_DeleteFirstRow>());
722   GlobalCommands_insert("PatchDeleteLastRow", FreeCaller<Patch_DeleteLastRow>());
723   GlobalCommands_insert("InvertCurve", FreeCaller<Patch_Invert>(), Accelerator('I', (GdkModifierType)GDK_CONTROL_MASK));
724   GlobalCommands_insert("RedisperseRows", FreeCaller<Patch_RedisperseRows>(), Accelerator('E', (GdkModifierType)GDK_CONTROL_MASK));
725   GlobalCommands_insert("RedisperseCols", FreeCaller<Patch_RedisperseCols>(), Accelerator('E', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
726   GlobalCommands_insert("MatrixTranspose", FreeCaller<Patch_Transpose>(), Accelerator('M', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
727   GlobalCommands_insert("CapCurrentCurve", FreeCaller<Patch_Cap>(), Accelerator('C', (GdkModifierType)GDK_SHIFT_MASK));
728   GlobalCommands_insert("CycleCapTexturePatch", FreeCaller<Patch_CycleProjection>(), Accelerator('N', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
729   GlobalCommands_insert("MakeOverlayPatch", FreeCaller<Patch_OverlayOn>(), Accelerator('Y'));
730   GlobalCommands_insert("ClearPatchOverlays", FreeCaller<Patch_OverlayOff>(), Accelerator('L', (GdkModifierType)GDK_CONTROL_MASK));
731 }
732
733 void Patch_constructToolbar(GtkToolbar* toolbar)
734 {
735   toolbar_append_button(toolbar, "Put caps on the current patch", "curve_cap.bmp", "CapCurrentCurve");
736 }
737
738 void Patch_constructMenu(GtkMenu* menu)
739 {
740   create_menu_item_with_mnemonic(menu, "Cylinder", "PatchCylinder");
741   {
742     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "More Cylinders");
743     create_menu_item_with_mnemonic(menu_in_menu, "Dense Cylinder", "PatchDenseCylinder");
744     create_menu_item_with_mnemonic(menu_in_menu, "Very Dense Cylinder", "PatchVeryDenseCylinder");
745     create_menu_item_with_mnemonic(menu_in_menu, "Square Cylinder", "PatchSquareCylinder");
746   }
747   menu_separator (menu);
748   create_menu_item_with_mnemonic(menu, "End cap", "PatchEndCap");
749   create_menu_item_with_mnemonic(menu, "Bevel", "PatchBevel");
750   {
751     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "More End caps, Bevels");
752     create_menu_item_with_mnemonic(menu_in_menu, "Square Endcap", "PatchSquareBevel");
753     create_menu_item_with_mnemonic(menu_in_menu, "Square Bevel", "PatchSquareEndcap");
754   }
755   menu_separator (menu);
756   create_menu_item_with_mnemonic(menu, "Cone", "PatchCone");
757   menu_separator (menu);
758   create_menu_item_with_mnemonic(menu, "Simple Patch Mesh...", "SimplePatchMesh");
759   menu_separator (menu);
760   {
761     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Insert");
762     create_menu_item_with_mnemonic(menu_in_menu, "Insert (2) Columns", "PatchInsertInsertColumn");
763     create_menu_item_with_mnemonic(menu_in_menu, "Add (2) Columns", "PatchInsertAddColumn");
764     menu_separator (menu_in_menu);
765     create_menu_item_with_mnemonic(menu_in_menu, "Insert (2) Rows", "PatchInsertInsertRow");
766     create_menu_item_with_mnemonic(menu_in_menu, "Add (2) Rows", "PatchInsertAddRow");
767   }
768   {
769     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Delete");
770     create_menu_item_with_mnemonic(menu_in_menu, "First (2) Columns", "PatchDeleteFirstColumn");
771     create_menu_item_with_mnemonic(menu_in_menu, "Last (2) Columns", "PatchDeleteLastColumn");
772     menu_separator (menu_in_menu);
773     create_menu_item_with_mnemonic(menu_in_menu, "First (2) Rows", "PatchDeleteFirstRow");
774     create_menu_item_with_mnemonic(menu_in_menu, "Last (2) Rows", "PatchDeleteLastRow");
775   }
776   menu_separator (menu);
777   {
778     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Matrix");
779     create_menu_item_with_mnemonic(menu_in_menu, "Invert", "InvertCurve");
780     GtkMenu* menu_3 = create_sub_menu_with_mnemonic (menu_in_menu, "Re-disperse");
781     create_menu_item_with_mnemonic(menu_3, "Rows", "RedisperseRows");
782     create_menu_item_with_mnemonic(menu_3, "Columns", "RedisperseCols");
783     create_menu_item_with_mnemonic(menu_in_menu, "Transpose", "MatrixTranspose");
784   }
785   menu_separator (menu);
786   create_menu_item_with_mnemonic(menu, "Cap Selection", "CapCurrentCurve");
787   create_menu_item_with_mnemonic(menu, "Cycle Cap Texture", "CycleCapTexturePatch");
788   menu_separator (menu);
789   {
790     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Overlay");
791     create_menu_item_with_mnemonic(menu_in_menu, "Set", "MakeOverlayPatch");
792     create_menu_item_with_mnemonic(menu_in_menu, "Clear", "ClearPatchOverlays");
793   }
794 }
795
796
797 #include <gtk/gtkbox.h>
798 #include <gtk/gtktable.h>
799 #include <gtk/gtktogglebutton.h>
800 #include <gtk/gtkradiobutton.h>
801 #include <gtk/gtkcombobox.h>
802 #include <gtk/gtklabel.h>
803 #include "gtkutil/dialog.h"
804 #include "gtkutil/widget.h"
805
806 void DoNewPatchDlg()
807 {
808   ModalDialog dialog;
809   GtkComboBox* width;
810   GtkComboBox* height;
811
812   GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Patch density", G_CALLBACK(dialog_delete_callback), &dialog);
813
814   GtkAccelGroup* accel = gtk_accel_group_new();
815   gtk_window_add_accel_group(window, accel);
816
817   {
818     GtkHBox* hbox = create_dialog_hbox(4, 4);
819     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox));
820     {
821       GtkTable* table = create_dialog_table(2, 2, 4, 4);
822       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0);
823       {
824         GtkLabel* label = GTK_LABEL(gtk_label_new("Width:"));
825         gtk_widget_show(GTK_WIDGET(label));
826         gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1,
827                           (GtkAttachOptions) (GTK_FILL),
828                           (GtkAttachOptions) (0), 0, 0);
829         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
830       }
831       {
832         GtkLabel* label = GTK_LABEL(gtk_label_new("Height:"));
833         gtk_widget_show(GTK_WIDGET(label));
834         gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 1, 2,
835                           (GtkAttachOptions) (GTK_FILL),
836                           (GtkAttachOptions) (0), 0, 0);
837         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
838       }
839
840       {
841         GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
842         gtk_combo_box_append_text(combo, "3");
843         gtk_combo_box_append_text(combo, "5");
844         gtk_combo_box_append_text(combo, "7");
845         gtk_combo_box_append_text(combo, "9");
846         gtk_combo_box_append_text(combo, "11");
847         gtk_combo_box_append_text(combo, "13");
848         gtk_combo_box_append_text(combo, "15");
849         gtk_widget_show(GTK_WIDGET(combo));
850         gtk_table_attach(table, GTK_WIDGET(combo), 1, 2, 0, 1,
851                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
852                           (GtkAttachOptions) (0), 0, 0);
853
854         width = combo;
855       }
856       {
857         GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
858         gtk_combo_box_append_text(combo, "3");
859         gtk_combo_box_append_text(combo, "5");
860         gtk_combo_box_append_text(combo, "7");
861         gtk_combo_box_append_text(combo, "9");
862         gtk_combo_box_append_text(combo, "11");
863         gtk_combo_box_append_text(combo, "13");
864         gtk_combo_box_append_text(combo, "15");
865         gtk_widget_show(GTK_WIDGET(combo));
866         gtk_table_attach(table, GTK_WIDGET(combo), 1, 2, 1, 2,
867                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
868                           (GtkAttachOptions) (0), 0, 0);
869
870         height = combo;
871       }
872     }
873
874     {
875       GtkVBox* vbox = create_dialog_vbox(4);
876       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0);
877       {
878         GtkButton* button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &dialog);
879         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
880         widget_make_default(GTK_WIDGET(button));
881         gtk_widget_grab_focus(GTK_WIDGET(button));
882         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
883       }
884       {
885         GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &dialog);
886         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
887         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
888       }
889     }
890   }
891
892   // Initialize dialog
893   gtk_combo_box_set_active(width, 0);
894   gtk_combo_box_set_active(height, 0);
895
896   if(modal_dialog_show(window, dialog) == eIDOK)
897   {
898     int w = gtk_combo_box_get_active(width) * 2 + 3;
899     int h = gtk_combo_box_get_active(height) * 2 + 3;
900
901     Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), ePlane, GlobalXYWnd_getCurrentViewType(), w, h);
902   }
903
904   gtk_widget_destroy(GTK_WIDGET(window));
905 }
906
907
908
909
910 EMessageBoxReturn DoCapDlg(ECapDialog* type)
911 {
912   ModalDialog dialog;
913   ModalDialogButton ok_button(dialog, eIDOK);
914   ModalDialogButton cancel_button(dialog, eIDCANCEL);
915   GtkWidget* bevel;
916   GtkWidget* ibevel;
917   GtkWidget* endcap;
918   GtkWidget* iendcap;
919   GtkWidget* cylinder;
920  
921   GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), "Cap", dialog);
922
923   GtkAccelGroup *accel_group = gtk_accel_group_new();
924   gtk_window_add_accel_group(window, accel_group);
925
926   {
927     GtkHBox* hbox = create_dialog_hbox(4, 4);
928     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox));
929
930     {
931       // Gef: Added a vbox to contain the toggle buttons
932       GtkVBox* radio_vbox = create_dialog_vbox(4);
933       gtk_container_add(GTK_CONTAINER(hbox), GTK_WIDGET(radio_vbox));
934       
935       {
936         GtkTable* table = GTK_TABLE(gtk_table_new(5, 2, FALSE));
937         gtk_widget_show(GTK_WIDGET(table));
938         gtk_box_pack_start(GTK_BOX(radio_vbox), GTK_WIDGET(table), TRUE, TRUE, 0);
939         gtk_table_set_row_spacings(table, 5);
940         gtk_table_set_col_spacings(table, 5);
941  
942         {
943           GtkImage* image = new_local_image("cap_bevel.bmp");
944           gtk_widget_show(GTK_WIDGET(image));
945           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 0, 1,
946                             (GtkAttachOptions) (GTK_FILL),
947                             (GtkAttachOptions) (0), 0, 0);
948         }
949         {
950           GtkImage* image = new_local_image("cap_endcap.bmp");
951           gtk_widget_show(GTK_WIDGET(image));
952           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 1, 2,
953                             (GtkAttachOptions) (GTK_FILL),
954                             (GtkAttachOptions) (0), 0, 0);
955         }
956         {
957           GtkImage* image = new_local_image("cap_ibevel.bmp");
958           gtk_widget_show(GTK_WIDGET(image));
959           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 2, 3,
960                             (GtkAttachOptions) (GTK_FILL),
961                             (GtkAttachOptions) (0), 0, 0);
962         }
963         {
964           GtkImage* image = new_local_image("cap_iendcap.bmp");
965           gtk_widget_show(GTK_WIDGET(image));
966           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 3, 4,
967                             (GtkAttachOptions) (GTK_FILL),
968                             (GtkAttachOptions) (0), 0, 0);
969         }
970         {
971           GtkImage* image = new_local_image("cap_cylinder.bmp");
972           gtk_widget_show(GTK_WIDGET(image));
973           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 4, 5,
974                             (GtkAttachOptions) (GTK_FILL),
975                             (GtkAttachOptions) (0), 0, 0);
976         }
977
978         GSList* group = 0;
979         {
980           GtkWidget* button = gtk_radio_button_new_with_label (group, "Bevel");
981           gtk_widget_show (button);
982           gtk_table_attach(table, button, 1, 2, 0, 1,
983                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
984                             (GtkAttachOptions) (0), 0, 0);
985           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
986
987           bevel = button;
988         }
989         {
990           GtkWidget* button = gtk_radio_button_new_with_label (group, "Endcap");
991           gtk_widget_show (button);
992           gtk_table_attach(table, button, 1, 2, 1, 2,
993                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
994                             (GtkAttachOptions) (0), 0, 0);
995           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
996
997           endcap = button;
998         }
999         {
1000           GtkWidget* button = gtk_radio_button_new_with_label (group, "Inverted Bevel");
1001           gtk_widget_show (button);
1002           gtk_table_attach(table, button, 1, 2, 2, 3,
1003                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
1004                             (GtkAttachOptions) (0), 0, 0);
1005           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
1006
1007           ibevel = button;
1008         }
1009         {
1010           GtkWidget* button = gtk_radio_button_new_with_label (group, "Inverted Endcap");
1011           gtk_widget_show (button);
1012           gtk_table_attach(table, button, 1, 2, 3, 4,
1013                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
1014                             (GtkAttachOptions) (0), 0, 0);
1015           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
1016
1017           iendcap = button;
1018         }
1019         {
1020           GtkWidget* button = gtk_radio_button_new_with_label (group, "Cylinder");
1021           gtk_widget_show (button);
1022           gtk_table_attach(table, button, 1, 2, 4, 5,
1023                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
1024                             (GtkAttachOptions) (0), 0, 0);
1025           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
1026
1027           cylinder = button;
1028         }
1029       }
1030     }
1031     
1032     {
1033       GtkVBox* vbox = create_dialog_vbox(4);
1034       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 0);
1035       {
1036         GtkButton* button = create_modal_dialog_button("OK", ok_button);
1037         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1038         widget_make_default(GTK_WIDGET(button));
1039         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
1040       }
1041       {
1042         GtkButton* button = create_modal_dialog_button("Cancel", cancel_button);
1043         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1044         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
1045       }
1046     }
1047   }
1048
1049   // Initialize dialog
1050   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bevel), TRUE);
1051   
1052   EMessageBoxReturn ret = modal_dialog_show(window, dialog);
1053   if (ret == eIDOK)
1054   {
1055     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bevel)))
1056       *type = PATCHCAP_BEVEL;
1057     else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(endcap)))
1058       *type = PATCHCAP_ENDCAP;
1059     else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ibevel)))
1060       *type = PATCHCAP_INVERTED_BEVEL;
1061     else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iendcap)))
1062       *type = PATCHCAP_INVERTED_ENDCAP;
1063     else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cylinder)))
1064       *type = PATCHCAP_CYLINDER;
1065   }
1066
1067   gtk_widget_destroy(GTK_WIDGET(window));
1068
1069   return ret;
1070 }