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