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