2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
25 // Leonardo Zide (leo@lokigames.com)
\r
29 #include <gtk/gtk.h>
\r
30 #include <sys/stat.h>
\r
31 #include "gtkmisc.h"
\r
32 #if defined (__linux__) || defined (__APPLE__)
\r
34 #include <X11/keysym.h>
\r
35 #include <gdk/gdkx.h>
\r
36 #include <gdk/gdkprivate.h>
\r
38 // for the logging part
\r
40 #include <sys/types.h>
\r
42 QEGlobals_t g_qeglobals;
\r
43 QEGlobals_GUI_t g_qeglobals_gui;
\r
45 // leo: Track memory allocations for debugging
\r
46 // NOTE TTimo this was never used and probably not relevant
\r
47 // there are tools to do that
\r
50 static GList *memblocks;
\r
52 void* debug_malloc (size_t size, const char* file, int line)
\r
54 void *buf = g_malloc (size + 8);
\r
56 *((const char**)buf) = file;
\r
57 buf = (char*)buf + 4;
\r
58 *((int*)buf) = line;
\r
59 buf = (char*)buf + 4;
\r
61 memblocks = g_list_append (memblocks, buf);
\r
66 void debug_free (void *buf, const char* file, int line)
\r
71 if (g_list_find (memblocks, buf))
\r
73 memblocks = g_list_remove (memblocks, buf);
\r
75 buf = (char*)buf - 4;
\r
77 buf = (char*)buf - 4;
\r
78 f = *((const char**)buf);
\r
80 Sys_FPrintf (SYS_DBG, "free: %s %d", file, line);
\r
81 Sys_FPrintf (SYS_DBG, " allocated: %s %d\n", f, l);
\r
86 // free (buf); // from qmalloc, will leak unless we add this same hack to cmdlib
\r
91 vec_t Rad_rint (vec_t in)
\r
93 if (g_PrefsDlg.m_bNoClamp)
\r
96 return (float)floor (in + 0.5);
\r
99 void WINAPI QE_CheckOpenGLForErrors(void)
\r
102 int i = qglGetError();
\r
103 if (i != GL_NO_ERROR)
\r
105 if (i == GL_OUT_OF_MEMORY)
\r
107 sprintf(strMsg, "OpenGL out of memory error %s\nDo you wish to save before exiting?", qgluErrorString((GLenum)i));
\r
108 if (gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg, "Radiant Error", MB_YESNO) == IDYES)
\r
110 Map_SaveFile(NULL, false);
\r
116 Sys_Printf ("Warning: OpenGL Error %s\n", qgluErrorString((GLenum)i));
\r
121 // NOTE: don't this function, use VFS instead
\r
122 char *ExpandReletivePath (char *p)
\r
124 static char temp[1024];
\r
129 if (p[0] == '/' || p[0] == '\\')
\r
132 base = ValueForKey(g_qeglobals.d_project_entity, "basepath");
\r
133 sprintf (temp, "%s/%s", base, p);
\r
137 char *copystring (char *s)
\r
140 b = (char*)malloc(strlen(s)+1);
\r
146 bool DoesFileExist(const char* pBuff, long& lSize)
\r
149 if (file.Open(pBuff, "r"))
\r
151 lSize += file.GetLength();
\r
159 void Map_Snapshot()
\r
163 // I hope the modified flag is kept correctly up to date
\r
167 // we need to do the following
\r
168 // 1. make sure the snapshot directory exists (create it if it doesn't)
\r
169 // 2. find out what the lastest save is based on number
\r
170 // 3. inc that and save the map
\r
171 CString strOrgPath, strOrgFile;
\r
172 ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile);
\r
173 AddSlash(strOrgPath);
\r
174 strOrgPath += "snapshots";
\r
177 if (stat(strOrgPath, &Stat) == -1)
\r
180 bGo = (_mkdir(strOrgPath) != -1);
\r
183 #if defined (__linux__) || defined (__APPLE__)
\r
184 bGo = (mkdir(strOrgPath,0755) != -1);
\r
187 AddSlash(strOrgPath);
\r
192 CString strNewPath;
\r
193 strNewPath = strOrgPath;
\r
194 strNewPath += strOrgFile;
\r
198 char buf[PATH_MAX];
\r
199 sprintf( buf, "%s.%i", strNewPath.GetBuffer(), nCount );
\r
201 bGo = DoesFileExist(strFile, lSize);
\r
204 // strFile has the next available slot
\r
205 Map_SaveFile(strFile, false);
\r
206 // it is still a modified map (we enter this only if this is a modified map)
\r
207 Sys_SetTitle (currentmap);
\r
208 Sys_MarkMapModified();
\r
209 if (lSize > 12 * 1024 * 1024) // total size of saves > 4 mb
\r
211 Sys_Printf("The snapshot files in %s total more than 4 megabytes. You might consider cleaning up.", strOrgPath.GetBuffer());
\r
216 strMsg.Format("Snapshot save failed.. unabled to create directory\n%s", strOrgPath.GetBuffer());
\r
217 gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg);
\r
226 If five minutes have passed since making a change
\r
227 and the map hasn't been saved, save it out.
\r
232 void QE_CheckAutoSave( void )
\r
234 static time_t s_start;
\r
238 if (modified != 1 || !s_start)
\r
244 if ((now - s_start) > (60 * g_PrefsDlg.m_nAutoSave))
\r
246 if (g_PrefsDlg.m_bAutoSave)
\r
249 strMsg = g_PrefsDlg.m_bSnapShots ? "Autosaving snapshot..." : "Autosaving...";
\r
250 Sys_Printf(strMsg);
\r
252 Sys_Status (strMsg,0);
\r
254 // only snapshot if not working on a default map
\r
255 if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0)
\r
261 Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false);
\r
264 Sys_Status ("Autosaving...Saved.", 0 );
\r
269 Sys_Printf ("Autosave skipped...\n");
\r
270 Sys_Status ("Autosave skipped...", 0 );
\r
277 // NOTE TTimo we don't like that BuildShortPathName too much
\r
278 // the VFS provides a vfsCleanFileName which should perform the cleanup tasks
\r
279 // in the long run I'd like to completely get rid of this
\r
281 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=144
\r
282 // used to be disabled, but caused problems
\r
284 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=291
\r
285 // can't work with long win32 names until the BSP commands are not working differently
\r
287 int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen)
\r
289 char *pFile = NULL;
\r
290 int nResult = GetFullPathName(pPath, nBufferLen, pBuffer, &pFile);
\r
291 nResult = GetShortPathName(pPath, pBuffer, nBufferLen);
\r
293 strcpy(pBuffer, pPath); // Use long filename
\r
298 #if defined (__linux__) || defined (__APPLE__)
\r
299 int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen)
\r
301 // remove /../ from directories
\r
302 const char *scr = pPath; char *dst = pBuffer;
\r
303 for (int i = 0; (i < nBufferLen) && (*scr != 0); i++)
\r
305 if (*scr == '/' && *(scr+1) == '.' && *(scr+2) == '.')
\r
308 while (dst != pBuffer && *(--dst) != '/')
\r
320 return strlen (pBuffer);
\r
325 const char *g_pPathFixups[]=
\r
331 const int g_nPathFixupCount = sizeof(g_pPathFixups) / sizeof(const char*);
\r
333 void QE_CheckProjectEntity()
\r
336 char pBuff[PATH_MAX];
\r
337 char pNewPath[PATH_MAX];
\r
338 for (int i = 0; i < g_nPathFixupCount; i++)
\r
340 char *pPath = ValueForKey (g_qeglobals.d_project_entity, g_pPathFixups[i]);
\r
342 strcpy (pNewPath, pPath);
\r
343 if (pPath[0] != '\\' && pPath[0] != '/')
\r
344 if (GetFullPathName(pPath, PATH_MAX, pBuff, &pFile))
\r
345 strcpy (pNewPath, pBuff);
\r
347 BuildShortPathName (pNewPath, pBuff, PATH_MAX);
\r
349 // check it's not ending with a filename seperator
\r
350 if (pBuff[strlen(pBuff)-1] == '/' || pBuff[strlen(pBuff)-1] == '\\')
\r
352 Sys_FPrintf(SYS_WRN, "WARNING: \"%s\" path in the project file has an ending file seperator, fixing.\n", g_pPathFixups[i]);
\r
353 pBuff[strlen(pBuff)-1]=0;
\r
356 SetKeyValue(g_qeglobals.d_project_entity, g_pPathFixups[i], pBuff);
\r
361 void HandleXMLError( void* ctxt, const char* text, ... )
\r
364 static char buf[32768];
\r
366 va_start (argptr,text);
\r
367 vsprintf (buf, text, argptr);
\r
368 Sys_FPrintf (SYS_ERR, "XML %s\n", buf);
\r
372 #define DTD_BUFFER_LENGTH 1024
\r
373 xmlDocPtr ParseXMLStream(IDataStream *stream, bool validate = false)
\r
375 xmlDocPtr doc = NULL;
\r
376 bool wellFormed = false, valid = false;
\r
377 int res, size = 1024;
\r
379 xmlParserCtxtPtr ctxt;
\r
381 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=433
\r
383 // xmlDoValidityCheckingDefaultValue = 1;
\r
385 xmlDoValidityCheckingDefaultValue = 0;
\r
386 xmlSetGenericErrorFunc(NULL, HandleXMLError);
\r
389 // HACK: use AppPath to resolve DTD location
\r
390 // do a buffer-safe string copy and concatenate
\r
394 char buf[DTD_BUFFER_LENGTH];
\r
399 //assert(g_strAppPath.GetBuffer() != NULL);
\r
400 for(r = g_strAppPath.GetBuffer(); i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++) w[i] = *r;
\r
402 for(r = "dtds/"; i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++) w[i] = *r;
\r
406 if(i == DTD_BUFFER_LENGTH)
\r
408 HandleXMLError(NULL, "ERROR: buffer overflow: DTD path length too large\n");
\r
412 res = stream->Read(chars, 4);
\r
415 ctxt = xmlCreatePushParserCtxt(NULL, NULL, chars, res, buf);
\r
417 while ((res = stream->Read(chars, size)) > 0)
\r
419 xmlParseChunk(ctxt, chars, res, 0);
\r
421 xmlParseChunk(ctxt, chars, 0, 1);
\r
424 wellFormed = (ctxt->wellFormed == 1);
\r
425 valid = (ctxt->valid == 1);
\r
427 xmlFreeParserCtxt(ctxt);
\r
430 if(wellFormed && (!validate || (validate && valid)))
\r
439 xmlDocPtr ParseXMLFile(const char* filename, bool validate = false)
\r
442 if (stream.Open(filename, "r"))
\r
443 return ParseXMLStream(&stream, validate);
\r
445 Sys_FPrintf(SYS_ERR, "Failed to open file: %s\n",filename);
\r
449 // copy a string r to a buffer w
\r
450 // replace $string as appropriate
\r
451 void ReplaceTemplates(char* w, const char* r)
\r
454 const char *__ENGINEPATH = "TEMPLATEenginepath";
\r
455 const char *__USERHOMEPATH = "TEMPLATEuserhomepath";
\r
456 const char *__TOOLSPATH = "TEMPLATEtoolspath";
\r
457 const char *__BASEDIR = "TEMPLATEbasedir";
\r
458 const char *__APPPATH = "TEMPLATEapppath";
\r
460 // iterate through string r
\r
463 // check for special character
\r
466 if(strncmp(r+1, __ENGINEPATH, strlen(__ENGINEPATH)) == 0)
\r
468 r+=strlen(__ENGINEPATH)+1;
\r
469 p = g_pGameDescription->mEnginePath.GetBuffer();
\r
471 else if(strncmp(r+1, __USERHOMEPATH, strlen(__USERHOMEPATH)) == 0)
\r
473 r+=strlen(__USERHOMEPATH)+1;
\r
474 p = g_qeglobals.m_strHomeGame.GetBuffer();
\r
476 else if(strncmp(r+1, __BASEDIR, strlen(__BASEDIR)) == 0)
\r
478 r+=strlen(__BASEDIR)+1;
\r
479 p = g_pGameDescription->mBaseGame;
\r
481 else if(strncmp(r+1, __TOOLSPATH, strlen(__TOOLSPATH)) == 0)
\r
483 r+=strlen(__TOOLSPATH)+1;
\r
484 p = g_strGameToolsPath.GetBuffer();
\r
486 else if(strncmp(r+1, __APPPATH, strlen(__APPPATH)) == 0)
\r
488 r+=strlen(__APPPATH)+1;
\r
489 p = g_strAppPath.GetBuffer();
\r
497 while(*p!='\0') *w++ = *p++;
\r
507 TODO TODO TODO (don't think this got fully merged in)
\r
508 TTimo: added project file "version", version 2 adds '#' chars to the BSP command strings
\r
509 version 3 was .. I don't remember .. version 4 adds q3map2 commands
\r
510 TTimo: when QE_LoadProject is called, the prefs are updated with path to the latest project and saved on disk
\r
513 /*\todo decide on a sensible location/name for project files.*/
\r
514 bool QE_LoadProject (const char *projectfile)
\r
518 xmlNodePtr node, project;
\r
520 Sys_Printf("Loading project file: \"%s\"\n", projectfile);
\r
521 doc = ParseXMLFile(projectfile, true);
\r
523 if(doc == NULL) return false;
\r
525 node=doc->children;
\r
526 while(node != NULL && node->type != XML_DTD_NODE) node=node->next;
\r
527 if(node == NULL || strcmp((char*)node->name, "project") != 0)
\r
529 Sys_FPrintf(SYS_ERR, "ERROR: invalid file type\n");
\r
533 while(node->type != XML_ELEMENT_NODE) node=node->next;
\r
537 if(g_qeglobals.d_project_entity != NULL) Entity_Free(g_qeglobals.d_project_entity);
\r
538 g_qeglobals.d_project_entity = Entity_Alloc();
\r
540 for(node = project->children; node != NULL; node=node->next)
\r
542 if(node->type != XML_ELEMENT_NODE) continue;
\r
545 ReplaceTemplates(buf, (char*)node->properties->next->children->content);
\r
547 SetKeyValue(g_qeglobals.d_project_entity, (char*)node->properties->children->content, buf);
\r
552 // project file version checking
\r
553 // add a version checking to avoid people loading later versions of the project file and bitching
\r
554 int ver = IntForKey( g_qeglobals.d_project_entity, "version" );
\r
555 if (ver > PROJECT_VERSION)
\r
558 sprintf (strMsg, "This is a version %d project file. This build only supports <=%d project files.\n"
\r
559 "Please choose another project file or upgrade your version of Radiant.", ver, PROJECT_VERSION);
\r
560 gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK);
\r
561 // set the project file to nothing so we are sure we'll ask next time?
\r
562 g_PrefsDlg.m_strLastProject = "";
\r
563 g_PrefsDlg.SavePrefs();
\r
567 // set here some default project settings you need
\r
568 if ( strlen( ValueForKey( g_qeglobals.d_project_entity, "brush_primit" ) ) == 0 )
\r
570 SetKeyValue( g_qeglobals.d_project_entity, "brush_primit", "0" );
\r
573 g_qeglobals.m_bBrushPrimitMode = IntForKey( g_qeglobals.d_project_entity, "brush_primit" );
\r
575 g_qeglobals.m_strHomeMaps = g_qeglobals.m_strHomeGame;
\r
576 const char* str = ValueForKey(g_qeglobals.d_project_entity, "gamename");
\r
577 if(str[0] == '\0') str = g_pGameDescription->mBaseGame.GetBuffer();
\r
578 g_qeglobals.m_strHomeMaps += str;
\r
579 g_qeglobals.m_strHomeMaps += '/';
\r
581 // don't forget to create the dirs
\r
582 Q_mkdir(g_qeglobals.m_strHomeGame.GetBuffer(), 0775);
\r
583 Q_mkdir(g_qeglobals.m_strHomeMaps.GetBuffer(), 0775);
\r
585 // usefull for the log file and debuggin fucked up configurations from users:
\r
586 // output the basic information of the .qe4 project file
\r
588 // all these paths should be unix format, with a trailing slash at the end
\r
589 // if not.. to debug, check that the project file paths are set up correctly
\r
590 Sys_Printf("basepath : %s\n", ValueForKey( g_qeglobals.d_project_entity, "basepath") );
\r
591 Sys_Printf("entitypath : %s\n", ValueForKey( g_qeglobals.d_project_entity, "entitypath" ) );
\r
594 // check whether user_project key exists..
\r
595 // if not, save the current project under a new name
\r
596 if (ValueForKey(g_qeglobals.d_project_entity, "user_project")[0] == '\0')
\r
598 Sys_Printf("Loaded a template project file\n");
\r
600 // create the user_project key
\r
601 SetKeyValue( g_qeglobals.d_project_entity, "user_project", "1" );
\r
603 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672
\r
604 if (IntForKey( g_qeglobals.d_project_entity, "version" ) != PROJECT_VERSION)
\r
608 "The template project '%s' has version %d. The editor binary is configured for version %d.\n"
\r
609 "This indicates a problem in your setup. See http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672\n"
\r
610 "I will keep going with this project till you fix this",
\r
611 projectfile, IntForKey( g_qeglobals.d_project_entity, "version" ), PROJECT_VERSION);
\r
612 gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK);
\r
615 // create the writable project file path
\r
616 strcpy(buf, g_qeglobals.m_strHomeGame.GetBuffer());
\r
617 strcat(buf, g_pGameDescription->mBaseGame.GetBuffer());
\r
618 strcat(buf, "/scripts/");
\r
619 // while the filename is already in use, increment the number we add to the end
\r
621 char pUser[PATH_MAX];
\r
624 sprintf( pUser, "%suser%d." PROJECT_FILETYPE, buf, counter );
\r
626 if (access( pUser, R_OK) != 0)
\r
629 strcpy( buf, pUser );
\r
633 // saving project will cause a save prefs
\r
634 g_PrefsDlg.m_strLastProject = buf;
\r
635 g_PrefsDlg.m_nLastProjectVer = IntForKey( g_qeglobals.d_project_entity, "version" );
\r
636 QE_SaveProject(buf);
\r
640 // update preferences::LastProject with path of this successfully-loaded project
\r
641 // save preferences
\r
642 Sys_Printf("Setting current project in prefs to \"%s\"\n", g_PrefsDlg.m_strLastProject.GetBuffer() );
\r
643 g_PrefsDlg.m_strLastProject = projectfile;
\r
644 g_PrefsDlg.SavePrefs();
\r
653 TTimo: whenever QE_SaveProject is called, prefs are updated and saved with the path to the project
\r
656 qboolean QE_SaveProject (const char* filename)
\r
658 Sys_Printf("Save project file '%s'\n", filename);
\r
661 xmlDocPtr doc = xmlNewDoc((xmlChar *)"1.0");
\r
663 xmlCreateIntSubset(doc, (xmlChar *)"project", NULL, (xmlChar *)"project.dtd");
\r
664 // create project node
\r
665 doc->children->next = xmlNewDocNode(doc, NULL, (xmlChar *)"project", NULL);
\r
667 for(epair_t* epair = g_qeglobals.d_project_entity->epairs; epair != NULL; epair = epair->next)
\r
669 node = xmlNewChild(doc->children->next, NULL, (xmlChar *)"key", NULL);
\r
670 xmlSetProp(node, (xmlChar*)"name", (xmlChar*)epair->key);
\r
671 xmlSetProp(node, (xmlChar*)"value", (xmlChar*)epair->value);
\r
674 CreateDirectoryPath(filename);
\r
675 if (xmlSaveFormatFile(filename, doc, 1) != -1)
\r
678 Sys_Printf("Setting current project in prefs to \"%s\"\n", filename );
\r
679 g_PrefsDlg.m_strLastProject = filename;
\r
680 g_PrefsDlg.SavePrefs();
\r
686 Sys_FPrintf(SYS_ERR, "failed to save project file: \"%s\"\n", filename);
\r
698 #define SPEED_MOVE 32
\r
699 #define SPEED_TURN 22.5
\r
706 Sets target / targetname on the two entities selected
\r
707 from the first selected to the secon
\r
710 void ConnectEntities (void)
\r
713 const char *target;
\r
714 char *newtarg = NULL;
\r
716 if (g_qeglobals.d_select_count != 2)
\r
718 Sys_Status ("Must have two brushes selected", 0);
\r
723 e1 = g_qeglobals.d_select_order[0]->owner;
\r
724 e2 = g_qeglobals.d_select_order[1]->owner;
\r
726 if (e1 == world_entity || e2 == world_entity)
\r
728 Sys_Status ("Can't connect to the world", 0);
\r
735 Sys_Status ("Brushes are from same entity", 0);
\r
740 target = ValueForKey (e1, "target");
\r
741 if (target && target[0])
\r
742 newtarg = g_strdup(target);
\r
745 target = ValueForKey(e2, "targetname");
\r
746 if(target && target[0])
\r
747 newtarg = g_strdup(target);
\r
749 Entity_Connect(e1, e2);
\r
752 if(newtarg != NULL)
\r
754 SetKeyValue(e1, "target", newtarg);
\r
755 SetKeyValue(e2, "targetname", newtarg);
\r
759 Sys_UpdateWindows (W_XY | W_CAMERA);
\r
762 Select_Brush (g_qeglobals.d_select_order[1]);
\r
765 qboolean QE_SingleBrush (bool bQuiet)
\r
767 if ( (selected_brushes.next == &selected_brushes)
\r
768 || (selected_brushes.next->next != &selected_brushes) )
\r
772 Sys_Printf ("Error: you must have a single brush selected\n");
\r
776 if (selected_brushes.next->owner->eclass->fixedsize)
\r
780 Sys_Printf ("Error: you cannot manipulate fixed size entities\n");
\r
788 void QE_InitVFS (void)
\r
790 // VFS initialization -----------------------
\r
791 // we will call vfsInitDirectory, giving the directories to look in (for files in pk3's and for standalone files)
\r
792 // we need to call in order, the mod ones first, then the base ones .. they will be searched in this order
\r
793 // *nix systems have a dual filesystem in ~/.q3a, which is searched first .. so we need to add that too
\r
794 Str directory,prefabs;
\r
796 // TTimo: let's leave this to HL mode for now
\r
797 if (g_pGameDescription->mGameFile == "hl.game")
\r
799 // Hydra: we search the "gametools" path first so that we can provide editor
\r
800 // specific pk3's wads and misc files for use by the editor.
\r
801 // the relevant map compiler tools will NOT use this directory, so this helps
\r
802 // to ensure that editor files are not used/required in release versions of maps
\r
803 // it also helps keep your editor files all in once place, with the editor modules,
\r
804 // plugins, scripts and config files.
\r
805 // it also helps when testing maps, as you'll know your files won't/can't be used
\r
806 // by the game engine itself.
\r
809 directory = g_pGameDescription->mGameToolsPath;
\r
810 vfsInitDirectory(directory.GetBuffer());
\r
813 // NOTE TTimo about the mymkdir calls .. this is a bit dirty, but a safe thing on *nix
\r
815 // if we have a mod dir
\r
816 if (*ValueForKey(g_qeglobals.d_project_entity, "gamename") != '\0')
\r
819 #if defined (__linux__) || defined (__APPLE__)
\r
820 // ~/.<gameprefix>/<fs_game>
\r
821 directory = g_qeglobals.m_strHomeGame.GetBuffer();
\r
822 Q_mkdir (directory.GetBuffer (), 0775);
\r
823 directory += ValueForKey(g_qeglobals.d_project_entity, "gamename");
\r
824 Q_mkdir (directory.GetBuffer (), 0775);
\r
825 vfsInitDirectory(directory.GetBuffer());
\r
826 AddSlash (directory);
\r
827 prefabs = directory;
\r
828 // also create the maps dir, it will be used as prompt for load/save
\r
829 directory += "/maps";
\r
830 Q_mkdir (directory, 0775);
\r
831 // and the prefabs dir
\r
832 prefabs += "/prefabs";
\r
833 Q_mkdir (prefabs, 0775);
\r
837 // <fs_basepath>/<fs_game>
\r
838 directory = g_pGameDescription->mEnginePath;
\r
839 directory += ValueForKey(g_qeglobals.d_project_entity, "gamename");
\r
840 Q_mkdir (directory.GetBuffer (), 0775);
\r
841 vfsInitDirectory(directory.GetBuffer());
\r
842 AddSlash(directory);
\r
843 prefabs = directory;
\r
844 // also create the maps dir, it will be used as prompt for load/save
\r
845 directory += "/maps";
\r
846 Q_mkdir (directory.GetBuffer (), 0775);
\r
847 // and the prefabs dir
\r
848 prefabs += "/prefabs";
\r
849 Q_mkdir (prefabs, 0775);
\r
852 #if defined (__linux__) || defined (__APPLE__)
\r
853 // ~/.<gameprefix>/<fs_main>
\r
854 directory = g_qeglobals.m_strHomeGame.GetBuffer();
\r
855 directory += g_pGameDescription->mBaseGame;
\r
856 vfsInitDirectory (directory.GetBuffer ());
\r
859 // <fs_basepath>/<fs_main>
\r
860 directory = g_pGameDescription->mEnginePath;
\r
861 directory += g_pGameDescription->mBaseGame;
\r
862 vfsInitDirectory(directory.GetBuffer());
\r
865 void QE_Init (void)
\r
868 ** initialize variables
\r
870 g_qeglobals.d_gridsize = 8;
\r
871 g_qeglobals.d_showgrid = true;
\r
876 FillClassList(); // list in entity window
\r
888 void WINAPI QE_ConvertDOSToUnixName( char *dst, const char *src )
\r
892 if ( *src == '\\' )
\r
901 int g_numbrushes, g_numentities;
\r
903 void QE_CountBrushesAndUpdateStatusBar( void )
\r
905 static int s_lastbrushcount, s_lastentitycount;
\r
906 static qboolean s_didonce;
\r
914 if ( active_brushes.next != NULL )
\r
916 for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next)
\r
919 if (b->brush_faces )
\r
921 if ( !b->owner->eclass->fixedsize)
\r
929 if ( entities.next != NULL )
\r
931 for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next)
\r
937 if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) )
\r
939 Sys_UpdateStatusBar();
\r
941 s_lastbrushcount = g_numbrushes;
\r
942 s_lastentitycount = g_numentities;
\r
947 char com_token[1024];
\r
955 double I_FloatTime (void)
\r
963 // more precise, less portable
\r
965 struct timezone tzp;
\r
966 static int secbase;
\r
968 gettimeofday(&tp, &tzp);
\r
972 secbase = tp.tv_sec;
\r
973 return tp.tv_usec/1000000.0;
\r
976 return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
\r
985 Parse a token out of a string
\r
988 char *COM_Parse (char *data)
\r
1001 while ( (c = *data) <= ' ')
\r
1006 return NULL; // end of file;
\r
1011 // skip // comments
\r
1012 if (c=='/' && data[1] == '/')
\r
1014 while (*data && *data != '\n')
\r
1020 // handle quoted strings specially
\r
1029 com_token[len] = 0;
\r
1032 com_token[len] = c;
\r
1037 // parse single characters
\r
1038 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
\r
1040 com_token[len] = c;
\r
1042 com_token[len] = 0;
\r
1046 // parse a regular word
\r
1049 com_token[len] = c;
\r
1053 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
\r
1057 com_token[len] = 0;
\r
1061 char* Get_COM_Token()
\r
1067 =============================================================================
\r
1071 =============================================================================
\r
1076 char *argv[MAX_NUM_ARGVS];
\r
1083 void ParseCommandLine (char *lpCmdLine)
\r
1086 argv[0] = "programname";
\r
1088 while (*lpCmdLine && (argc < MAX_NUM_ARGVS))
\r
1090 while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
\r
1095 argv[argc] = lpCmdLine;
\r
1098 while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
\r
1117 Checks for the given parameter in the program's command line arguments
\r
1118 Returns the argument number (1 to argc-1) or 0 if not present
\r
1121 int CheckParm (char *check)
\r
1125 for (i = 1;i<argc;i++)
\r
1127 if ( stricmp(check, argv[i]) )
\r
1139 ParseNum / ParseHex
\r
1142 int ParseHex (char *hex)
\r
1153 if (*str >= '0' && *str <= '9')
\r
1155 else if (*str >= 'a' && *str <= 'f')
\r
1156 num += 10 + *str-'a';
\r
1157 else if (*str >= 'A' && *str <= 'F')
\r
1158 num += 10 + *str-'A';
\r
1160 Error ("Bad hex number: %s",hex);
\r
1168 int ParseNum (char *str)
\r
1170 if (str[0] == '$')
\r
1171 return ParseHex (str+1);
\r
1172 if (str[0] == '0' && str[1] == 'x')
\r
1173 return ParseHex (str+2);
\r
1174 return atol (str);
\r
1177 // BSP frontend plugin
\r
1178 // global flag for BSP frontend plugin is g_qeglobals.bBSPFrontendPlugin
\r
1179 _QERPlugBSPFrontendTable g_BSPFrontendTable;
\r
1181 // =============================================================================
\r
1184 bool Sys_AltDown ()
\r
1187 return (GetKeyState(VK_MENU) & 0x8000) != 0;
\r
1190 #if defined (__linux__) || defined (__APPLE__)
\r
1194 XQueryKeymap(GDK_DISPLAY(), keys);
\r
1196 x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_L);
\r
1197 if (keys[x/8] & (1 << (x % 8)))
\r
1200 x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_R);
\r
1201 if (keys[x/8] & (1 << (x % 8)))
\r
1208 bool Sys_ShiftDown ()
\r
1211 return (GetKeyState(VK_SHIFT) & 0x8000) != 0;
\r
1214 #if defined (__linux__) || defined (__APPLE__)
\r
1218 XQueryKeymap(GDK_DISPLAY(), keys);
\r
1220 x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_L);
\r
1221 if (keys[x/8] & (1 << (x % 8)))
\r
1224 x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_R);
\r
1225 if (keys[x/8] & (1 << (x % 8)))
\r
1232 void Sys_MarkMapModified (void)
\r
1234 char title[PATH_MAX];
\r
1236 if (modified != 1)
\r
1238 modified = true; // mark the map as changed
\r
1239 sprintf (title, "%s *", currentmap);
\r
1241 QE_ConvertDOSToUnixName( title, title );
\r
1242 Sys_SetTitle (title);
\r
1246 void Sys_SetTitle (const char *text)
\r
1248 gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_main_window), text);
\r
1251 bool g_bWaitCursor = false;
\r
1253 void WINAPI Sys_BeginWait (void)
\r
1255 GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);
\r
1256 gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor);
\r
1257 gdk_cursor_unref (cursor);
\r
1258 g_bWaitCursor = true;
\r
1261 void WINAPI Sys_EndWait (void)
\r
1263 GdkCursor *cursor = gdk_cursor_new (GDK_LEFT_PTR);
\r
1264 gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor);
\r
1265 gdk_cursor_unref (cursor);
\r
1266 g_bWaitCursor = false;
\r
1269 void Sys_GetCursorPos (int *x, int *y)
\r
1271 // FIXME: not multihead safe
\r
1272 gdk_window_get_pointer (NULL, x, y, NULL);
\r
1275 void Sys_SetCursorPos (int x, int y)
\r
1277 // NOTE: coordinates are in GDK space, not OS space
\r
1279 int sys_x = x - g_pParentWnd->GetGDKOffsetX();
\r
1280 int sys_y = y - g_pParentWnd->GetGDKOffsetY();
\r
1282 SetCursorPos (sys_x, sys_y);
\r
1285 #if defined (__linux__) || defined (__APPLE__)
\r
1286 XWarpPointer (GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, x, y);
\r
1290 void Sys_Beep (void)
\r
1292 #if defined (__linux__) || defined (__APPLE__)
\r
1295 MessageBeep (MB_ICONASTERISK);
\r
1299 double Sys_DoubleTime (void)
\r
1301 return clock()/ 1000.0;
\r
1305 ===============================================================
\r
1309 ===============================================================
\r
1312 void Sys_UpdateStatusBar( void )
\r
1314 extern int g_numbrushes, g_numentities;
\r
1316 char numbrushbuffer[100]="";
\r
1318 sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities );
\r
1319 g_pParentWnd->SetStatusText(2, numbrushbuffer);
\r
1320 //Sys_Status( numbrushbuffer, 2 );
\r
1323 void Sys_Status(const char *psz, int part )
\r
1325 g_pParentWnd->SetStatusText (part, psz);
\r
1328 // =============================================================================
\r
1332 static GtkWidget *MRU_items[MRU_MAX];
\r
1333 static int MRU_used;
\r
1334 typedef char MRU_filename_t[PATH_MAX];
\r
1335 MRU_filename_t MRU_filenames[MRU_MAX];
\r
1337 static char* MRU_GetText (int index)
\r
1339 return MRU_filenames[index];
\r
1342 void buffer_write_escaped_mnemonic(char* buffer, const char* string)
\r
1344 while(*string != '\0')
\r
1346 if(*string == '_')
\r
1351 *buffer++ = *string++;
\r
1356 static void MRU_SetText (int index, const char *filename)
\r
1358 strcpy(MRU_filenames[index], filename);
\r
1360 char mnemonic[PATH_MAX * 2 + 4];
\r
1361 mnemonic[0] = '_';
\r
1362 sprintf(mnemonic+1, "%d", index+1);
\r
1363 mnemonic[2] = '-';
\r
1364 mnemonic[3] = ' ';
\r
1365 buffer_write_escaped_mnemonic(mnemonic+4, filename);
\r
1366 gtk_label_set_text_with_mnemonic(GTK_LABEL (GTK_BIN (MRU_items[index])->child), mnemonic);
\r
1371 int i = g_PrefsDlg.m_nMRUCount;
\r
1374 i = 4; //FIXME: make this a define
\r
1376 for (; i > 0; i--)
\r
1377 MRU_AddFile (g_PrefsDlg.m_strMRUFiles[i-1].GetBuffer());
\r
1382 g_PrefsDlg.m_nMRUCount = MRU_used;
\r
1384 for (int i = 0; i < MRU_used; i++)
\r
1385 g_PrefsDlg.m_strMRUFiles[i] = MRU_GetText (i);
\r
1388 void MRU_AddWidget (GtkWidget *widget, int pos)
\r
1390 if (pos < MRU_MAX)
\r
1391 MRU_items[pos] = widget;
\r
1394 void MRU_AddFile (const char *str)
\r
1399 // check if file is already in our list
\r
1400 for (i = 0; i < MRU_used; i++)
\r
1402 text = MRU_GetText (i);
\r
1404 if (strcmp (text, str) == 0)
\r
1407 for (; i > 0; i--)
\r
1408 MRU_SetText (i, MRU_GetText (i-1));
\r
1410 MRU_SetText (0, str);
\r
1416 if (MRU_used < MRU_MAX)
\r
1419 // move items down
\r
1420 for (i = MRU_used-1; i > 0; i--)
\r
1421 MRU_SetText (i, MRU_GetText (i-1));
\r
1423 MRU_SetText (0, str);
\r
1424 gtk_widget_set_sensitive (MRU_items[0], TRUE);
\r
1425 gtk_widget_show (MRU_items[MRU_used-1]);
\r
1428 void MRU_Activate (int index)
\r
1430 char *text = MRU_GetText (index);
\r
1432 if (access (text, R_OK) == 0)
\r
1434 text = strdup (text);
\r
1435 MRU_AddFile (text);
\r
1436 Map_LoadFile (text);
\r
1443 for (int i = index; i < MRU_used; i++)
\r
1444 MRU_SetText (i, MRU_GetText (i+1));
\r
1446 if (MRU_used == 0)
\r
1448 gtk_label_set_text (GTK_LABEL (GTK_BIN (MRU_items[0])->child), "Recent Files");
\r
1449 gtk_widget_set_sensitive (MRU_items[0], FALSE);
\r
1453 gtk_widget_hide (MRU_items[MRU_used]);
\r
1459 ======================================================================
\r
1463 ======================================================================
\r
1466 qboolean ConfirmModified ()
\r
1471 if (gtk_MessageBox (g_pParentWnd->m_pWidget, "This will lose changes to the map", "warning", MB_OKCANCEL) == IDCANCEL)
\r
1476 void ProjectDialog ()
\r
1478 const char *filename;
\r
1479 char buffer[NAME_MAX];
\r
1482 * Obtain the system directory name and
\r
1483 * store it in buffer.
\r
1486 strcpy(buffer, g_qeglobals.m_strHomeGame.GetBuffer());
\r
1487 strcat(buffer, g_pGameDescription->mBaseGame.GetBuffer());
\r
1488 strcat (buffer, "/scripts/");
\r
1490 // Display the Open dialog box
\r
1491 filename = file_dialog (NULL, TRUE, "Open File", buffer, "project");
\r
1493 if (filename == NULL)
\r
1494 return; // canceled
\r
1497 // NOTE: QE_LoadProject takes care of saving prefs with new path to the project file
\r
1498 if (!QE_LoadProject(filename))
\r
1499 Sys_Printf ("Failed to load project from file: %s\n", filename);
\r
1501 // FIXME TTimo QE_Init is probably broken if you don't call it during startup right now ..
\r
1506 =======================================================
\r
1508 Menu modifications
\r
1510 =======================================================
\r
1514 ==================
\r
1517 ==================
\r
1519 char *bsp_commands[256];
\r
1521 void FillBSPMenu ()
\r
1523 GtkWidget *item, *menu; // menu points to a GtkMenu (not an item)
\r
1528 menu = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_bsp"));
\r
1530 while ((lst = gtk_container_children (GTK_CONTAINER (menu))) != NULL)
\r
1531 gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (lst->data));
\r
1533 if (g_PrefsDlg.m_bDetachableMenus) {
\r
1534 item = gtk_tearoff_menu_item_new ();
\r
1535 gtk_menu_append (GTK_MENU (menu), item);
\r
1536 gtk_widget_set_sensitive (item, TRUE);
\r
1537 gtk_widget_show (item);
\r
1540 if (g_qeglobals.bBSPFrontendPlugin)
\r
1542 CString str = g_BSPFrontendTable.m_pfnGetBSPMenu();
\r
1544 strcpy(cTemp, str);
\r
1545 char* token = strtok(cTemp, ",;");
\r
1546 if (token && *token == ' ')
\r
1548 while (*token == ' ')
\r
1553 // first token is menu name
\r
1554 item = gtk_menu_get_attach_widget (GTK_MENU (menu));
\r
1555 gtk_label_set_text (GTK_LABEL (GTK_BIN (item)->child), token);
\r
1557 token = strtok(NULL, ",;");
\r
1558 while (token != NULL)
\r
1560 g_BSPFrontendCommands = g_slist_append (g_BSPFrontendCommands, g_strdup (token));
\r
1561 item = gtk_menu_item_new_with_label (token);
\r
1562 gtk_widget_show (item);
\r
1563 gtk_container_add (GTK_CONTAINER (menu), item);
\r
1564 gtk_signal_connect (GTK_OBJECT (item), "activate",
\r
1565 GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i));
\r
1566 token = strtok(NULL, ",;");
\r
1573 for (ep = g_qeglobals.d_project_entity->epairs; ep; ep = ep->next)
\r
1575 if (strncmp(ep->key, "bsp_", 4)==0)
\r
1577 bsp_commands[i] = ep->key;
\r
1578 item = gtk_menu_item_new_with_label (ep->key+4);
\r
1579 gtk_widget_show (item);
\r
1580 gtk_container_add (GTK_CONTAINER (menu), item);
\r
1581 gtk_signal_connect (GTK_OBJECT (item), "activate",
\r
1582 GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i));
\r
1589 //==============================================
\r
1591 void AddSlash(CString& strPath)
\r
1593 if (strPath.GetLength() > 0)
\r
1595 if ((strPath.GetAt(strPath.GetLength()-1) != '/') &&
\r
1596 (strPath.GetAt(strPath.GetLength()-1) != '\\'))
\r
1601 bool ExtractPath_and_Filename(const char* pPath, CString& strPath, CString& strFilename)
\r
1603 CString strPathName;
\r
1604 strPathName = pPath;
\r
1605 int nSlash = strPathName.ReverseFind('\\');
\r
1607 // TTimo: try forward slash, some are using forward
\r
1608 nSlash = strPathName.ReverseFind('/');
\r
1611 strPath = strPathName.Left(nSlash+1);
\r
1612 strFilename = strPathName.Right(strPathName.GetLength() - nSlash - 1);
\r
1614 // TTimo: try forward slash, some are using forward
\r
1616 strFilename = pPath;
\r
1620 //===========================================
\r
1622 //++timo FIXME: no longer used .. remove!
\r
1623 char *TranslateString (char *buf)
\r
1625 static char buf2[32768];
\r
1631 for (i=0 ; i<l ; i++)
\r
1633 if (buf[i] == '\n')
\r
1646 // called whenever we need to open/close/check the console log file
\r
1647 void Sys_LogFile (void)
\r
1649 if (g_PrefsDlg.mGamesDialog.m_bLogConsole && !g_qeglobals.hLogFile)
\r
1651 // settings say we should be logging and we don't have a log file .. so create it
\r
1652 // open a file to log the console (if user prefs say so)
\r
1653 // the file handle is g_qeglobals.hLogFile
\r
1654 // the log file is erased
\r
1656 name = g_strTempPath;
\r
1657 name += "radiant.log";
\r
1658 #if defined (__linux__) || defined (__APPLE__)
\r
1659 g_qeglobals.hLogFile = open( name.GetBuffer(), O_TRUNC | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE );
\r
1662 g_qeglobals.hLogFile = _open( name.GetBuffer(), _O_TRUNC | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE );
\r
1664 if (g_qeglobals.hLogFile)
\r
1666 Sys_Printf("Started logging to %s\n", name.GetBuffer());
\r
1669 Sys_Printf("Today is: %s", ctime(&localtime));
\r
1670 Sys_Printf("This is GtkRadiant '" RADIANT_VERSION "' compiled " __DATE__ "\n");
\r
1671 Sys_Printf(RADIANT_ABOUTMSG "\n");
\r
1674 gtk_MessageBox (NULL, "Failed to create log file, check write permissions in Radiant directory.\n",
\r
1675 "Console logging", MB_OK );
\r
1677 else if (!g_PrefsDlg.mGamesDialog.m_bLogConsole && g_qeglobals.hLogFile)
\r
1679 // settings say we should not be logging but still we have an active logfile .. close it
\r
1682 Sys_Printf("Closing log file at %s\n", ctime(&localtime));
\r
1684 _close( g_qeglobals.hLogFile );
\r
1686 #if defined (__linux__) || defined (__APPLE__)
\r
1687 close( g_qeglobals.hLogFile );
\r
1689 g_qeglobals.hLogFile = 0;
\r
1693 void Sys_ClearPrintf (void)
\r
1695 GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit));
\r
1696 gtk_text_buffer_set_text(buffer, "", -1);
\r
1699 // used to be around 32000, that should be way enough already
\r
1700 #define BUFFER_SIZE 4096
\r
1702 extern "C" void Sys_FPrintf_VA (int level, const char *text, va_list args)
\r
1704 char buf[BUFFER_SIZE];
\r
1707 vsnprintf(buf, BUFFER_SIZE, text, args);
\r
1708 buf[BUFFER_SIZE-1] = 0;
\r
1709 const unsigned int length = strlen(buf);
\r
1711 if (g_qeglobals.hLogFile)
\r
1714 _write(g_qeglobals.hLogFile, buf,length);
\r
1715 _commit(g_qeglobals.hLogFile);
\r
1717 #if defined (__linux__) || defined (__APPLE__)
\r
1718 write(g_qeglobals.hLogFile, buf, length);
\r
1722 if (level != SYS_NOCON)
\r
1724 // TTimo: FIXME: killed the console to avoid GDI leak fuckup
\r
1725 if (g_qeglobals_gui.d_edit != NULL)
\r
1727 GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit));
\r
1730 gtk_text_buffer_get_end_iter(buffer, &iter);
\r
1732 static GtkTextMark* end = gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
\r
1734 const GdkColor yellow = { 0, 0xb0ff, 0xb0ff, 0x0000 };
\r
1735 const GdkColor red = { 0, 0xffff, 0x0000, 0x0000 };
\r
1736 const GdkColor black = { 0, 0x0000, 0x0000, 0x0000 };
\r
1738 static GtkTextTag* error_tag = gtk_text_buffer_create_tag (buffer, "red_foreground", "foreground-gdk", &red, NULL);
\r
1739 static GtkTextTag* warning_tag = gtk_text_buffer_create_tag (buffer, "yellow_foreground", "foreground-gdk", &yellow, NULL);
\r
1740 static GtkTextTag* standard_tag = gtk_text_buffer_create_tag (buffer, "black_foreground", "foreground-gdk", &black, NULL);
\r
1745 tag = warning_tag;
\r
1753 tag = standard_tag;
\r
1756 gtk_text_buffer_insert_with_tags(buffer, &iter, buf, length, tag, NULL);
\r
1758 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit), end);
\r
1760 // update console widget immediatly if we're doing something time-consuming
\r
1761 if( !g_bScreenUpdates && GTK_WIDGET_REALIZED( g_qeglobals_gui.d_edit ) )
\r
1763 gtk_grab_add(g_qeglobals_gui.d_edit);
\r
1765 while(gtk_events_pending())
\r
1766 gtk_main_iteration();
\r
1768 gtk_grab_remove(g_qeglobals_gui.d_edit);
\r
1774 // NOTE: this is the handler sent to synapse
\r
1775 // must match PFN_SYN_PRINTF_VA
\r
1776 extern "C" void Sys_Printf_VA (const char *text, va_list args)
\r
1778 Sys_FPrintf_VA (SYS_STD, text, args);
\r
1781 extern "C" void Sys_Printf (const char *text, ...)
\r
1785 va_start (args, text);
\r
1786 Sys_FPrintf_VA (SYS_STD, text, args);
\r
1790 extern "C" void Sys_FPrintf (int level, const char *text, ...)
\r
1794 va_start (args, text);
\r
1795 Sys_FPrintf_VA (level, text, args);
\r