Wrap more GTK
[xonotic/netradiant.git] / contrib / hydratoolz / plugin.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 "plugin.h"
23 #include "version.h"
24
25 /*! \file plugin.cpp
26     \brief HydraToolz!
27
28     HydraToolz by Dominic Clifton - Hydra (Hydra@Hydras-World.com)
29
30     Overview
31     ========
32
33
34     Version History
35     ===============
36
37     v0.1 - 28/May/2002
38       - Initial version.
39
40
41     ToDo
42     ====
43
44  * Code it ? :)
45
46  */
47
48 // =============================================================================
49 // Globals
50
51 _QERFuncTable_1 g_FuncTable;
52 _QERFileSystemTable g_FileSystemTable;
53 _QEREntityTable g_EntityTable;
54
55 // cast to GtkWidget*
56 void *g_pMainWnd;
57
58 // =============================================================================
59 // Ripped from TexTool.cpp
60
61 static void dialog_button_callback( GtkWidget *widget, gpointer data ){
62         GtkWidget *parent;
63         int *loop, *ret;
64
65         parent = gtk_widget_get_toplevel( widget );
66         loop = (int*)gtk_object_get_data( GTK_OBJECT( parent ), "loop" );
67         ret = (int*)gtk_object_get_data( GTK_OBJECT( parent ), "ret" );
68
69         *loop = 0;
70         *ret = gpointer_to_int( data );
71 }
72
73 static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
74         int *loop;
75
76         gtk_widget_hide( widget );
77         loop = (int*)gtk_object_get_data( GTK_OBJECT( widget ), "loop" );
78         *loop = 0;
79
80         return TRUE;
81 }
82
83 int DoMessageBox( const char* lpText, const char* lpCaption, guint32 uType ){
84         GtkWidget *w, *hbox;
85         int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;
86
87         auto window = ui::Window( ui::window_type::TOP );
88         window.connect( "delete_event",
89                                                 G_CALLBACK( dialog_delete_callback ), NULL );
90         window.connect( "destroy",
91                                                 G_CALLBACK( gtk_widget_destroy ), NULL );
92         gtk_window_set_title( GTK_WINDOW( window ), lpCaption );
93         gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );
94         gtk_object_set_data( GTK_OBJECT( window ), "loop", &loop );
95         gtk_object_set_data( GTK_OBJECT( window ), "ret", &ret );
96         gtk_widget_realize( window );
97
98         auto vbox = ui::VBox( FALSE, 10 );
99         window.add(vbox);
100         vbox.show();
101
102         w = ui::Label( lpText );
103         gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
104         gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
105         gtk_widget_show( w );
106
107         w = gtk_hseparator_new();
108         gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
109         gtk_widget_show( w );
110
111         hbox = ui::HBox( FALSE, 10 );
112         gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 2 );
113         gtk_widget_show( hbox );
114
115         if ( mode == MB_OK ) {
116                 w = ui::Button( "Ok" );
117                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
118                 w.connect( "clicked",
119                                                         G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
120                 gtk_widget_set_can_default( w, true );
121                 gtk_widget_grab_default( w );
122                 gtk_widget_show( w );
123                 ret = IDOK;
124         }
125         else if ( mode ==  MB_OKCANCEL ) {
126                 w = ui::Button( "Ok" );
127                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
128                 w.connect( "clicked",
129                                                         G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
130                 gtk_widget_set_can_default( w, true );
131                 gtk_widget_grab_default( w );
132                 gtk_widget_show( w );
133
134                 w = ui::Button( "Cancel" );
135                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
136                 w.connect( "clicked",
137                                                         G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
138                 gtk_widget_show( w );
139                 ret = IDCANCEL;
140         }
141         else if ( mode == MB_YESNOCANCEL ) {
142                 w = ui::Button( "Yes" );
143                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
144                 w.connect( "clicked",
145                                                         G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
146                 gtk_widget_set_can_default( w, true );
147                 gtk_widget_grab_default( w );
148                 gtk_widget_show( w );
149
150                 w = ui::Button( "No" );
151                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
152                 w.connect( "clicked",
153                                                         G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
154                 gtk_widget_show( w );
155
156                 w = ui::Button( "Cancel" );
157                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
158                 w.connect( "clicked",
159                                                         G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
160                 gtk_widget_show( w );
161                 ret = IDCANCEL;
162         }
163         else /* if (mode == MB_YESNO) */
164         {
165                 w = ui::Button( "Yes" );
166                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
167                 w.connect( "clicked",
168                                                         G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
169                 gtk_widget_set_can_default( w, true );
170                 gtk_widget_grab_default( w );
171                 gtk_widget_show( w );
172
173                 w = ui::Button( "No" );
174                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
175                 w.connect( "clicked",
176                                                         G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
177                 gtk_widget_show( w );
178                 ret = IDNO;
179         }
180
181         gtk_widget_show( window );
182         gtk_grab_add( window );
183
184         while ( loop )
185                 gtk_main_iteration();
186
187         gtk_grab_remove( window );
188         window.destroy();
189
190         return ret;
191 }
192
193 // End of rip from TexTool.cpp
194
195 // =============================================================================
196 // Ripped from cmdlib.cpp
197
198 /*
199    ====================
200    Extract file parts
201    ====================
202  */
203 void ExtractFilePath( const char *path, char *dest ){
204         const char *src;
205
206         src = path + strlen( path ) - 1;
207
208 //
209 // back up until a \ or the start
210 //
211         while ( src != path && *( src - 1 ) != '/' && *( src - 1 ) != '\\' )
212                 src--;
213
214         memcpy( dest, path, src - path );
215         dest[src - path] = 0;
216 }
217
218 void ExtractFileName( const char *path, char *dest ){
219         const char *src;
220
221         src = path + strlen( path ) - 1;
222
223 //
224 // back up until a \ or the start
225 //
226         while ( src != path && *( src - 1 ) != '/'
227                         && *( src - 1 ) != '\\' )
228                 src--;
229
230         while ( *src )
231         {
232                 *dest++ = *src++;
233         }
234         *dest = 0;
235 }
236
237 void ConvertDOSToUnixName( char *dst, const char *src ){
238         while ( *src )
239         {
240                 if ( *src == '\\' ) {
241                         *dst = '/';
242                 }
243                 else{
244                         *dst = *src;
245                 }
246                 dst++; src++;
247         }
248         *dst = 0;
249 }
250
251 // End of rip from cmdlib.cpp
252
253 // =============================================================================
254 // Actual Plugin Code
255
256 // get the wad name from the shader name (or an actual wadname) and add to a list of wad names making
257 // sure we don't add duplicates.
258
259 GSList *AddToWadList( GSList *wadlist, const char *shadername, const char *wad ){
260         char tmpstr[QER_MAX_NAMELEN];
261         char *wadname;
262         if ( !shadername && !wad ) {
263                 return wadlist;
264         }
265
266         if ( shadername ) {
267                 if ( strcmp( shadername,"color" ) == 0 ) {
268                         return wadlist;
269                 }
270                 ExtractFilePath( shadername,tmpstr );
271                 // Sys_Printf("checking: %s\n",shadername);
272
273                 int l = strlen( tmpstr ) - 1;
274
275                 if ( tmpstr[l] == '/' || tmpstr[l] == '\\' ) {
276                         tmpstr[l] = 0;
277                 }
278                 else
279                 {
280                         Sys_Printf( "WARNING: Unknown wad file for shader %s\n",shadername );
281                         return wadlist;
282                 }
283
284                 ExtractFileName( tmpstr,tmpstr );
285
286                 wadname = (char *)malloc( strlen( tmpstr ) + 5 );
287                 sprintf( wadname,"%s.wad",tmpstr );
288         }
289         else
290         {
291                 wadname = strdup( wad );
292         }
293
294         for ( GSList *l = wadlist; l != NULL ; l = l->next )
295         {
296                 if ( string_equal_nocase( (char *)l->data,wadname ) ) {
297                         free( wadname );
298                         return wadlist;
299                 }
300         }
301
302         Sys_Printf( "Adding Wad File to WAD list: %s (reason: ",wadname );
303         if ( shadername ) {
304                 Sys_Printf( "see shader \"%s\")\n", shadername );
305         }
306         else{
307                 Sys_Printf( "already in WAD key. )\n" );
308         }
309         return ( g_slist_append( wadlist, wadname ) );
310 }
311
312 void UpdateWadKeyPair( void ){
313         int i,nb;
314
315         char wads[2048]; // change to CString usage ?
316         wads[0] = 0;
317         char *p1,*p2;
318         entity_t *pEntity;
319         epair_t *pEpair;
320         GSList *wadlist = NULL;
321         face_t  *f;
322         brush_t *b;
323         char cleanwadname[QER_MAX_NAMELEN];
324         const char *actualwad;
325
326
327         pEntity = (entity_t *)g_FuncTable.m_pfnGetEntityHandle( 0 ); // get the worldspawn ent
328
329         Sys_Printf( "Searching for in-use wad files...\n" );
330         for ( pEpair = pEntity->epairs; pEpair != NULL; pEpair = pEpair->next )
331         {
332                 if ( string_equal_nocase( pEpair->key,"wad" ) ) {
333                         strcpy( wads,pEpair->value );
334                         ConvertDOSToUnixName( wads,wads );
335
336                         // ok, we got the list of ; delimited wads, now split it into a GSList that contains
337                         // just the wad names themselves.
338
339                         p1 = wads;
340
341                         do
342                         {
343                                 p2 = strchr( p1,';' );
344                                 if ( p2 ) {
345                                         *p2 = 0; // swap the ; with a null terminator
346
347                                 }
348                                 if ( strchr( p1,'/' ) || strchr( p1,'\\' ) ) {
349                                         ExtractFileName( p1,cleanwadname );
350                                         wadlist = AddToWadList( wadlist, NULL, cleanwadname );
351                                 }
352                                 else
353                                 {
354                                         wadlist = AddToWadList( wadlist, NULL, p1 );
355                                 }
356                                 if ( p2 ) {
357                                         p1 = p2 + 1; // point back to the remainder of the string
358                                 }
359                                 else{
360                                         p1 = NULL; // make it so we exit the loop.
361
362                                 }
363                         } while ( p1 );
364
365                         // ok, now we have a list of wads in GSList.
366                         // now we need to add any new wadfiles (with their paths) to this list
367                         // so scan all brushes and see what wads are in use
368                         // FIXME: scan brushes only in the region ?
369
370                         break; // we don't need to process any more key/pairs.
371                 }
372         }
373
374         nb = g_FuncTable.m_pfnAllocateActiveBrushHandles();
375         for ( i = 0; i < nb; i++ )
376         {
377                 b = (brush_t *)g_FuncTable.m_pfnGetActiveBrushHandle( i );
378                 if ( b->patchBrush ) { // patches in halflife ?
379                         wadlist = AddToWadList( wadlist, b->pPatch->pShader->getName(),NULL );
380                 }
381                 else
382                 {
383                         for ( f = b->brush_faces ; f ; f = f->next )
384                         {
385                                 wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
386                         }
387                 }
388         }
389         g_FuncTable.m_pfnReleaseActiveBrushHandles();
390
391         nb = g_FuncTable.m_pfnAllocateSelectedBrushHandles();
392         for ( i = 0; i < nb; i++ )
393         {
394                 b = (brush_t *)g_FuncTable.m_pfnGetSelectedBrushHandle( i );
395                 if ( b->patchBrush ) { // patches in halflife ?
396                         wadlist = AddToWadList( wadlist, b->pPatch->pShader->getName(),NULL );
397                 }
398                 else
399                 {
400                         for ( f = b->brush_faces ; f ; f = f->next )
401                         {
402                                 wadlist = AddToWadList( wadlist, f->pShader->getName(),NULL );
403                         }
404                 }
405         }
406         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
407
408         // Now we have a complete list of wadnames (without paths) so we just have to turn this
409         // back to a ; delimited list.
410
411         wads[0] = 0;
412         while ( wadlist )
413         {
414                 if ( string_equal_nocase( (char *)wadlist->data,"common-hydra.wad" ) ) {
415                         Sys_Printf( "Skipping radiant-supplied wad file %s\n",(char *)wadlist->data );
416                 }
417                 else
418                 {
419                         if ( wads[0] ) {
420                                 strcat( wads,";" );
421                         }
422
423                         actualwad = vfsGetFullPath( (char *)wadlist->data );
424
425                         if ( actualwad ) {
426                                 strcat( wads, actualwad );
427                         }
428                         else
429                         {
430                                 Sys_Printf( "WARNING: could not locate wad file %s\n",(char *)wadlist->data );
431                                 strcat( wads, (char *)wadlist->data );
432                         }
433                 }
434
435                 free( wadlist->data );
436                 wadlist = g_slist_remove( wadlist, wadlist->data );
437         }
438
439         // store the wad list back in the worldspawn.
440         if ( wads[0] ) {
441                 //free(pEpair->value);
442                 //pEpair->value = strdup(wads);
443                 SetKeyValue( pEntity, "wad", wads );
444         }
445
446 }
447
448 // =============================================================================
449 // PLUGIN INTERFACE STUFF
450
451 // plugin name
452 const char *PLUGIN_NAME = "Q3 Texture Tools";
453
454 // commands in the menu
455 const char *PLUGIN_COMMANDS = "About...;Create/Update WAD keypair";
456
457 const char *PLUGIN_ABOUT = "HydraToolz for GTKRadiant\n\n"
458                                                    "By Hydra!";
459
460 extern "C" void* WINAPI QERPlug_GetFuncTable(){
461         return &g_FuncTable;
462 }
463
464 const char* QERPlug_Init( void* hApp, void *pWidget ){
465         GtkWidget* pMainWidget = static_cast<GtkWidget*>( pWidget );
466
467         g_pMainWnd = pMainWidget;
468         memset( &g_FuncTable, 0, sizeof( _QERFuncTable_1 ) );
469         g_FuncTable.m_nSize = sizeof( _QERFuncTable_1 );
470         return "HydraToolz for GTKRadiant"; // do we need this ? hmmm
471 }
472
473 const char* QERPlug_GetName(){
474         return (char*)PLUGIN_NAME;
475 }
476
477 const char* QERPlug_GetCommandList(){
478         return PLUGIN_COMMANDS;
479 }
480
481 extern "C" void QERPlug_Dispatch( const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush ){
482         if ( !strcmp( p, "Create/Update WAD keypair" ) ) {
483                 UpdateWadKeyPair();
484         }
485         else if ( !strcmp( p, "About..." ) ) {
486                 g_FuncTable.m_pfnMessageBox( (GtkWidget*)NULL, PLUGIN_ABOUT, "About", eMB_OK );
487         }
488 }
489
490 // =============================================================================
491 // SYNAPSE
492
493 CSynapseServer* g_pSynapseServer = NULL;
494 CSynapseClientHydraToolz g_SynapseClient;
495
496 extern "C" CSynapseClient * SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ){
497         if ( strcmp( version, SYNAPSE_VERSION ) ) {
498                 Syn_Printf( "ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version );
499                 return NULL;
500         }
501         g_pSynapseServer = pServer;
502         g_pSynapseServer->IncRef();
503         Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );
504
505         g_SynapseClient.AddAPI( PLUGIN_MAJOR, "HydraToolz", sizeof( _QERPluginTable ) );
506         g_SynapseClient.AddAPI( RADIANT_MAJOR, NULL, sizeof( g_FuncTable ), SYN_REQUIRE, &g_FuncTable );
507         g_SynapseClient.AddAPI( VFS_MAJOR, "wad", sizeof( g_FileSystemTable ), SYN_REQUIRE, &g_FileSystemTable );
508         g_SynapseClient.AddAPI( ENTITY_MAJOR, NULL, sizeof( g_EntityTable ), SYN_REQUIRE, &g_EntityTable );
509         return &g_SynapseClient;
510 }
511
512 bool CSynapseClientHydraToolz::RequestAPI( APIDescriptor_t *pAPI ){
513         if ( !strcmp( pAPI->major_name, PLUGIN_MAJOR ) ) {
514                 _QERPluginTable *pTable = static_cast<_QERPluginTable*>( pAPI->mpTable );
515                 pTable->m_pfnQERPlug_Init = QERPlug_Init;
516                 pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
517                 pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
518                 pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
519                 return true;
520         }
521
522         Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
523         return false;
524 }
525
526 const char* CSynapseClientHydraToolz::GetInfo(){
527         return "HydraToolz plugin built " __DATE__ " " RADIANT_VERSION;
528 }