- Updated help menu web links (removed map-center.com, added ETB documentation) ...
[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 (SHIFT + C)", "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     if (g_Layout_enableDetachableMenus.m_value)
744       menu_tearoff (menu_in_menu);
745     create_menu_item_with_mnemonic(menu_in_menu, "Dense Cylinder", "PatchDenseCylinder");
746     create_menu_item_with_mnemonic(menu_in_menu, "Very Dense Cylinder", "PatchVeryDenseCylinder");
747     create_menu_item_with_mnemonic(menu_in_menu, "Square Cylinder", "PatchSquareCylinder");
748   }
749   menu_separator (menu);
750   create_menu_item_with_mnemonic(menu, "End cap", "PatchEndCap");
751   create_menu_item_with_mnemonic(menu, "Bevel", "PatchBevel");
752   {
753     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "More End caps, Bevels");
754     if (g_Layout_enableDetachableMenus.m_value)
755       menu_tearoff (menu_in_menu);
756     create_menu_item_with_mnemonic(menu_in_menu, "Square Endcap", "PatchSquareBevel");
757     create_menu_item_with_mnemonic(menu_in_menu, "Square Bevel", "PatchSquareEndcap");
758   }
759   menu_separator (menu);
760   create_menu_item_with_mnemonic(menu, "Cone", "PatchCone");
761   menu_separator (menu);
762   create_menu_item_with_mnemonic(menu, "Simple Patch Mesh...", "SimplePatchMesh");
763   menu_separator (menu);
764   {
765     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Insert");
766     if (g_Layout_enableDetachableMenus.m_value)
767       menu_tearoff (menu_in_menu);
768     create_menu_item_with_mnemonic(menu_in_menu, "Insert (2) Columns", "PatchInsertInsertColumn");
769     create_menu_item_with_mnemonic(menu_in_menu, "Add (2) Columns", "PatchInsertAddColumn");
770     menu_separator (menu_in_menu);
771     create_menu_item_with_mnemonic(menu_in_menu, "Insert (2) Rows", "PatchInsertInsertRow");
772     create_menu_item_with_mnemonic(menu_in_menu, "Add (2) Rows", "PatchInsertAddRow");
773   }
774   {
775     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Delete");
776     if (g_Layout_enableDetachableMenus.m_value)
777       menu_tearoff (menu_in_menu);
778     create_menu_item_with_mnemonic(menu_in_menu, "First (2) Columns", "PatchDeleteFirstColumn");
779     create_menu_item_with_mnemonic(menu_in_menu, "Last (2) Columns", "PatchDeleteLastColumn");
780     menu_separator (menu_in_menu);
781     create_menu_item_with_mnemonic(menu_in_menu, "First (2) Rows", "PatchDeleteFirstRow");
782     create_menu_item_with_mnemonic(menu_in_menu, "Last (2) Rows", "PatchDeleteLastRow");
783   }
784   menu_separator (menu);
785   {
786     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Matrix");
787     if (g_Layout_enableDetachableMenus.m_value)
788       menu_tearoff (menu_in_menu);
789     create_menu_item_with_mnemonic(menu_in_menu, "Invert", "InvertCurve");
790     GtkMenu* menu_3 = create_sub_menu_with_mnemonic (menu_in_menu, "Re-disperse");
791     if (g_Layout_enableDetachableMenus.m_value)
792       menu_tearoff (menu_3);
793     create_menu_item_with_mnemonic(menu_3, "Rows", "RedisperseRows");
794     create_menu_item_with_mnemonic(menu_3, "Columns", "RedisperseCols");
795     create_menu_item_with_mnemonic(menu_in_menu, "Transpose", "MatrixTranspose");
796   }
797   menu_separator (menu);
798   create_menu_item_with_mnemonic(menu, "Cap Selection", "CapCurrentCurve");
799   create_menu_item_with_mnemonic(menu, "Cycle Cap Texture", "CycleCapTexturePatch");
800   menu_separator (menu);
801   {
802     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Overlay");
803     if (g_Layout_enableDetachableMenus.m_value)
804       menu_tearoff (menu_in_menu);
805     create_menu_item_with_mnemonic(menu_in_menu, "Set", "MakeOverlayPatch");
806     create_menu_item_with_mnemonic(menu_in_menu, "Clear", "ClearPatchOverlays");
807   }
808 }
809
810
811 #include <gtk/gtkbox.h>
812 #include <gtk/gtktable.h>
813 #include <gtk/gtktogglebutton.h>
814 #include <gtk/gtkradiobutton.h>
815 #include <gtk/gtkcombobox.h>
816 #include <gtk/gtklabel.h>
817 #include "gtkutil/dialog.h"
818 #include "gtkutil/widget.h"
819
820 void DoNewPatchDlg()
821 {
822   ModalDialog dialog;
823   GtkComboBox* width;
824   GtkComboBox* height;
825
826   GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Patch density", G_CALLBACK(dialog_delete_callback), &dialog);
827
828   GtkAccelGroup* accel = gtk_accel_group_new();
829   gtk_window_add_accel_group(window, accel);
830
831   {
832     GtkHBox* hbox = create_dialog_hbox(4, 4);
833     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox));
834     {
835       GtkTable* table = create_dialog_table(2, 2, 4, 4);
836       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0);
837       {
838         GtkLabel* label = GTK_LABEL(gtk_label_new("Width:"));
839         gtk_widget_show(GTK_WIDGET(label));
840         gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 0, 1,
841                           (GtkAttachOptions) (GTK_FILL),
842                           (GtkAttachOptions) (0), 0, 0);
843         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
844       }
845       {
846         GtkLabel* label = GTK_LABEL(gtk_label_new("Height:"));
847         gtk_widget_show(GTK_WIDGET(label));
848         gtk_table_attach(table, GTK_WIDGET(label), 0, 1, 1, 2,
849                           (GtkAttachOptions) (GTK_FILL),
850                           (GtkAttachOptions) (0), 0, 0);
851         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
852       }
853
854       {
855         GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
856         gtk_combo_box_append_text(combo, "3");
857         gtk_combo_box_append_text(combo, "5");
858         gtk_combo_box_append_text(combo, "7");
859         gtk_combo_box_append_text(combo, "9");
860         gtk_combo_box_append_text(combo, "11");
861         gtk_combo_box_append_text(combo, "13");
862         gtk_combo_box_append_text(combo, "15");
863         gtk_widget_show(GTK_WIDGET(combo));
864         gtk_table_attach(table, GTK_WIDGET(combo), 1, 2, 0, 1,
865                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
866                           (GtkAttachOptions) (0), 0, 0);
867
868         width = combo;
869       }
870       {
871         GtkComboBox* combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
872         gtk_combo_box_append_text(combo, "3");
873         gtk_combo_box_append_text(combo, "5");
874         gtk_combo_box_append_text(combo, "7");
875         gtk_combo_box_append_text(combo, "9");
876         gtk_combo_box_append_text(combo, "11");
877         gtk_combo_box_append_text(combo, "13");
878         gtk_combo_box_append_text(combo, "15");
879         gtk_widget_show(GTK_WIDGET(combo));
880         gtk_table_attach(table, GTK_WIDGET(combo), 1, 2, 1, 2,
881                           (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
882                           (GtkAttachOptions) (0), 0, 0);
883
884         height = combo;
885       }
886     }
887
888     {
889       GtkVBox* vbox = create_dialog_vbox(4);
890       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0);
891       {
892         GtkButton* button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &dialog);
893         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
894         widget_make_default(GTK_WIDGET(button));
895         gtk_widget_grab_focus(GTK_WIDGET(button));
896         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
897       }
898       {
899         GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &dialog);
900         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
901         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
902       }
903     }
904   }
905
906   // Initialize dialog
907   gtk_combo_box_set_active(width, 0);
908   gtk_combo_box_set_active(height, 0);
909
910   if(modal_dialog_show(window, dialog) == eIDOK)
911   {
912     int w = gtk_combo_box_get_active(width) * 2 + 3;
913     int h = gtk_combo_box_get_active(height) * 2 + 3;
914
915     Scene_PatchConstructPrefab(GlobalSceneGraph(), PatchCreator_getBounds(), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()), ePlane, GlobalXYWnd_getCurrentViewType(), w, h);
916   }
917
918   gtk_widget_destroy(GTK_WIDGET(window));
919 }
920
921
922
923
924 EMessageBoxReturn DoCapDlg(ECapDialog* type)
925 {
926   ModalDialog dialog;
927   ModalDialogButton ok_button(dialog, eIDOK);
928   ModalDialogButton cancel_button(dialog, eIDCANCEL);
929   GtkWidget* bevel;
930   GtkWidget* ibevel;
931   GtkWidget* endcap;
932   GtkWidget* iendcap;
933   GtkWidget* cylinder;
934  
935   GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), "Cap", dialog);
936
937   GtkAccelGroup *accel_group = gtk_accel_group_new();
938   gtk_window_add_accel_group(window, accel_group);
939
940   {
941     GtkHBox* hbox = create_dialog_hbox(4, 4);
942     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox));
943
944     {
945       // Gef: Added a vbox to contain the toggle buttons
946       GtkVBox* radio_vbox = create_dialog_vbox(4);
947       gtk_container_add(GTK_CONTAINER(hbox), GTK_WIDGET(radio_vbox));
948       
949       {
950         GtkTable* table = GTK_TABLE(gtk_table_new(5, 2, FALSE));
951         gtk_widget_show(GTK_WIDGET(table));
952         gtk_box_pack_start(GTK_BOX(radio_vbox), GTK_WIDGET(table), TRUE, TRUE, 0);
953         gtk_table_set_row_spacings(table, 5);
954         gtk_table_set_col_spacings(table, 5);
955  
956         {
957           GtkImage* image = new_local_image("cap_bevel.bmp");
958           gtk_widget_show(GTK_WIDGET(image));
959           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 0, 1,
960                             (GtkAttachOptions) (GTK_FILL),
961                             (GtkAttachOptions) (0), 0, 0);
962         }
963         {
964           GtkImage* image = new_local_image("cap_endcap.bmp");
965           gtk_widget_show(GTK_WIDGET(image));
966           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 1, 2,
967                             (GtkAttachOptions) (GTK_FILL),
968                             (GtkAttachOptions) (0), 0, 0);
969         }
970         {
971           GtkImage* image = new_local_image("cap_ibevel.bmp");
972           gtk_widget_show(GTK_WIDGET(image));
973           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 2, 3,
974                             (GtkAttachOptions) (GTK_FILL),
975                             (GtkAttachOptions) (0), 0, 0);
976         }
977         {
978           GtkImage* image = new_local_image("cap_iendcap.bmp");
979           gtk_widget_show(GTK_WIDGET(image));
980           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 3, 4,
981                             (GtkAttachOptions) (GTK_FILL),
982                             (GtkAttachOptions) (0), 0, 0);
983         }
984         {
985           GtkImage* image = new_local_image("cap_cylinder.bmp");
986           gtk_widget_show(GTK_WIDGET(image));
987           gtk_table_attach(table, GTK_WIDGET(image), 0, 1, 4, 5,
988                             (GtkAttachOptions) (GTK_FILL),
989                             (GtkAttachOptions) (0), 0, 0);
990         }
991
992         GSList* group = 0;
993         {
994           GtkWidget* button = gtk_radio_button_new_with_label (group, "Bevel");
995           gtk_widget_show (button);
996           gtk_table_attach(table, button, 1, 2, 0, 1,
997                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
998                             (GtkAttachOptions) (0), 0, 0);
999           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
1000
1001           bevel = button;
1002         }
1003         {
1004           GtkWidget* button = gtk_radio_button_new_with_label (group, "Endcap");
1005           gtk_widget_show (button);
1006           gtk_table_attach(table, button, 1, 2, 1, 2,
1007                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
1008                             (GtkAttachOptions) (0), 0, 0);
1009           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
1010
1011           endcap = button;
1012         }
1013         {
1014           GtkWidget* button = gtk_radio_button_new_with_label (group, "Inverted Bevel");
1015           gtk_widget_show (button);
1016           gtk_table_attach(table, button, 1, 2, 2, 3,
1017                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
1018                             (GtkAttachOptions) (0), 0, 0);
1019           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
1020
1021           ibevel = button;
1022         }
1023         {
1024           GtkWidget* button = gtk_radio_button_new_with_label (group, "Inverted Endcap");
1025           gtk_widget_show (button);
1026           gtk_table_attach(table, button, 1, 2, 3, 4,
1027                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
1028                             (GtkAttachOptions) (0), 0, 0);
1029           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
1030
1031           iendcap = button;
1032         }
1033         {
1034           GtkWidget* button = gtk_radio_button_new_with_label (group, "Cylinder");
1035           gtk_widget_show (button);
1036           gtk_table_attach(table, button, 1, 2, 4, 5,
1037                             (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
1038                             (GtkAttachOptions) (0), 0, 0);
1039           group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
1040
1041           cylinder = button;
1042         }
1043       }
1044     }
1045     
1046     {
1047       GtkVBox* vbox = create_dialog_vbox(4);
1048       gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 0);
1049       {
1050         GtkButton* button = create_modal_dialog_button("OK", ok_button);
1051         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1052         widget_make_default(GTK_WIDGET(button));
1053         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
1054       }
1055       {
1056         GtkButton* button = create_modal_dialog_button("Cancel", cancel_button);
1057         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0);
1058         gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
1059       }
1060     }
1061   }
1062
1063   // Initialize dialog
1064   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bevel), TRUE);
1065   
1066   EMessageBoxReturn ret = modal_dialog_show(window, dialog);
1067   if (ret == eIDOK)
1068   {
1069     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bevel)))
1070       *type = PATCHCAP_BEVEL;
1071     else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(endcap)))
1072       *type = PATCHCAP_ENDCAP;
1073     else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ibevel)))
1074       *type = PATCHCAP_INVERTED_BEVEL;
1075     else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iendcap)))
1076       *type = PATCHCAP_INVERTED_ENDCAP;
1077     else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cylinder)))
1078       *type = PATCHCAP_CYLINDER;
1079   }
1080
1081   gtk_widget_destroy(GTK_WIDGET(window));
1082
1083   return ret;
1084 }