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