From: namespace Date: Sat, 30 Sep 2006 15:56:36 +0000 (+0000) Subject: - Added LocalLcPath to plugin interface X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=bae89ec8cba114979ecd643e8b2afd78db982b9f;p=xonotic%2Fnetradiant.git - Added LocalLcPath to plugin interface - Added enhanced Texturebrowser (ETB) by Shaderman - Added shaderplug by Shaderman, accompanies the ETB - Added xmltagging lib by Shaderman, accompanies the ETB - Added new icons for ETB, Console, Entityinspector and Lighteditor - Fixed minor warning in Sunplug Project - Fixed PATH_MAX namecollision in ptrview plugin on Linux - Final fix for 64 bit patch.h issue git-svn-id: https://zerowing.idsoftware.com/svn/radiant/GtkRadiant/trunk@107 8a3a26a2-13c4-0310-b231-cf6edde360e5 --- diff --git a/CHANGES b/CHANGES index 461b47d7..de2083a5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,19 @@ This is the changelog for developers, != changelog for the end user that we distribute with the binaries. (see changelog) +30/09/2006 +namespace +- Added LocalLcPath to plugin interface +- Added enhanced Texturebrowser (ETB) by Shaderman +- Added shaderplug by Shaderman, accompanies the ETB +- Added xmltagging lib by Shaderman, accompanies the ETB +- Added new icons for ETB, Console, Entityinspector and Lighteditor +- Fixed minor warning in Sunplug Project +- Fixed PATH_MAX namecollision in ptrview plugin on Linux +- Final fix for 64 bit patch.h issue + + + 12/09/2006 namespace - Fixed 64 Bit issue in patch.h, see http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=1109 diff --git a/GtkRadiant.sln b/GtkRadiant.sln index 544534f3..c9235331 100644 --- a/GtkRadiant.sln +++ b/GtkRadiant.sln @@ -5,6 +5,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "entityq3", "plugins\entity\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GtkRadiant", "radiant\GtkRadiant.vcproj", "{8E70385C-223A-4DD1-9B99-28FF2331A2B5}" ProjectSection(ProjectDependencies) = postProject + {1C785349-866D-447D-8C55-8A51E5CA0E87} = {1C785349-866D-447D-8C55-8A51E5CA0E87} {68E2C6B6-96CA-4BBD-A485-FEE6F2E65407} = {68E2C6B6-96CA-4BBD-A485-FEE6F2E65407} {8845C5C1-4154-425F-8643-447FADC03449} = {8845C5C1-4154-425F-8643-447FADC03449} {8845C5C1-4154-425F-8643-447FADC03449} = {8845C5C1-4154-425F-8643-447FADC03449} @@ -158,6 +159,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "brushexport", "contrib\brus ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaderplug", "contrib\shaderplug\shaderplug.vcproj", "{46B36F0C-5E17-458E-AE6F-AECE52F66EDE}" + ProjectSection(ProjectDependencies) = postProject + {1C785349-866D-447D-8C55-8A51E5CA0E87} = {1C785349-866D-447D-8C55-8A51E5CA0E87} + EndProjectSection +EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug @@ -296,6 +302,10 @@ Global {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Debug.Build.0 = Debug|Win32 {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.ActiveCfg = Release|Win32 {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.Build.0 = Release|Win32 + {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Debug.ActiveCfg = Debug|Win32 + {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Debug.Build.0 = Debug|Win32 + {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.ActiveCfg = Release|Win32 + {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection diff --git a/SConscript b/SConscript index f33c75fe..68a875e9 100644 --- a/SConscript +++ b/SConscript @@ -16,6 +16,12 @@ def build_list(s_prefix, s_string): cmdlib_lib = g_env.StaticLibrary(target='libs/cmdlib', source='libs/cmdlib/cmdlib.cpp') +xml_env = g_env.Copy() +xml_env.Prepend(CPPPATH = 'include') +xml_env.Append(CXXFLAGS='`pkg-config glib-2.0 --cflags` `xml2-config --cflags`') +xml_src = 'ixml.cpp xmlparser.cpp xmlwriter.cpp xmlelement.cpp xmltextags.cpp' +xml_lib = xml_env.StaticLibrary(target='libs/xmllib', source=build_list('libs/xml', xml_src)) + mathlib_src = 'mathlib.c bbox.c line.c m4x4.c ray.c' mathlib_lib = g_env.StaticLibrary(target='libs/mathlib', source=build_list('libs/mathlib', mathlib_src)) @@ -488,6 +494,14 @@ sunplug_env.useGtk2() sunplug_lib = sunplug_env.SharedLibrarySafe(target='sunplug', source=sunplug_lst, LIBPATH='libs') sunplug_env.Install(INSTALL + '/plugins', sunplug_lib) +shaderplug_env = module_env.Copy() +shaderplug_lst = build_list('contrib/shaderplug', 'shaderplug.cpp') +shaderplug_env.useGlib2() +shaderplug_env.useGtk2() +shaderplug_env.useXML2() +shaderplug_lib = shaderplug_env.SharedLibrarySafe(target='shaderplug', source=shaderplug_lst, LIBS='xmllib', LIBPATH='libs') +shaderplug_env.Install(INSTALL + '/plugins', shaderplug_lib) + #gensurf_lst = build_list('contrib/gtkgensurf', #'bitmap.cpp dec.cpp face.cpp font.cpp gendlgs.cpp genmap.cpp gensurf.cpp heretic.cpp plugin.cpp view.cpp triangle.c') #bob_env.SharedLibrarySafe(target='gensurf', source=gensurf_lst) @@ -599,13 +613,14 @@ radiant_src = [ for i in range(len(radiant_src)): radiant_src[i] = 'radiant/' + radiant_src[i] -radiant_libs = ['mathlib', 'cmdlib', 'l_net', 'profile', 'gtkutil'] +radiant_libs = ['mathlib', 'cmdlib', 'l_net', 'profile', 'gtkutil', 'xmllib'] radiant_prog = radiant_env.Program(target='radiant.' + g_cpu, source=radiant_src, LIBS=radiant_libs, LIBPATH='libs') radiant_env.Depends(radiant_prog, mathlib_lib) radiant_env.Depends(radiant_prog, cmdlib_lib) radiant_env.Depends(radiant_prog, l_net_lib) radiant_env.Depends(radiant_prog, profile_lib) radiant_env.Depends(radiant_prog, gtkutil_lib) +radiant_env.Depends(radiant_prog, xml_lib) radiant_env.Install(INSTALL, radiant_prog) diff --git a/TODO b/TODO index c32101b6..4ebb4f43 100644 --- a/TODO +++ b/TODO @@ -42,7 +42,6 @@ This variable could then be used in a command like this: Entity: option to filter non-world entities (e.g. not func_group or func_static) Rotate Tool: if more than one object is selected, with different local orientations, use parent-space rotation pivot instead of local-space -Texture Browser: add a way to make large texture sets more manageable - shaderlist.txt was previously used this way Brush: MMB+ctrl to paint texture on whole brush/patch. Camera: add alternative highlighting styles (used to be J). Doom3: filter func_splinemovers diff --git a/contrib/prtview/portals.h b/contrib/prtview/portals.h index 5e61fe99..06f4af4b 100644 --- a/contrib/prtview/portals.h +++ b/contrib/prtview/portals.h @@ -52,7 +52,7 @@ public: bool Build(char *def); }; -#define PATH_MAX 260 +#define PRTVIEW_PATH_MAX 260 typedef guint32 PackedColour; #define RGB(r, g, b) ((guint32)(((guint8) (r) | ((guint16) (g) << 8))|(((guint32) (guint8) (b)) << 16))) #define GetRValue(rgb) ((guint8)(rgb)) @@ -76,7 +76,7 @@ public: void FixColors(); - char fn[PATH_MAX]; + char fn[PRTVIEW_PATH_MAX]; int zbuffer; int polygons; diff --git a/contrib/shaderplug/shaderplug.cpp b/contrib/shaderplug/shaderplug.cpp new file mode 100644 index 00000000..facb3822 --- /dev/null +++ b/contrib/shaderplug/shaderplug.cpp @@ -0,0 +1,263 @@ +/* +Copyright (C) 2006, Stefan Greven. +All Rights Reserved. + +This file is part of GtkRadiant. + +GtkRadiant is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +GtkRadiant is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GtkRadiant; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "shaderplug.h" + +#include "debugging/debugging.h" + +#include +#include +#include "string/string.h" +#include "modulesystem/singletonmodule.h" +#include "stream/stringstream.h" +#include "os/file.h" + +#include + +#include "iplugin.h" +#include "qerplugin.h" +#include "ifilesystem.h" +#include "iarchive.h" +#include "ishaders.h" +#include "iscriplib.h" + +#include "generic/callback.h" + +namespace { +const char SHADERTAG_FILE[] = "shadertags.xml"; +} + +class ShaderPlugPluginDependencies : public GlobalRadiantModuleRef, + public GlobalFileSystemModuleRef, + public GlobalShadersModuleRef +{ +public: + ShaderPlugPluginDependencies() : + GlobalShadersModuleRef(GlobalRadiant().getRequiredGameDescriptionKeyValue("shaders")) + { + } +}; + +namespace Shaderplug +{ + GtkWindow* g_window; + + std::vector archives; + std::set shaders; + std::set textures; + + XmlTagBuilder TagBuilder; + void CreateTagFile(); + + const char* init(void* hApp, void* pMainWidget) + { + g_window = GTK_WINDOW(pMainWidget); + return ""; + } + const char* getName() + { + return "ShaderPlug"; + } + const char* getCommandList() + { + return "About;Create tag file"; + } + const char* getCommandTitleList() + { + return ""; + } + void dispatch(const char* command, float* vMin, float* vMax, bool bSingleBrush) + { + if(string_equal(command, "About")) + { + GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window), "Shaderplug (1.0)\n\n" + "by Shaderman (shaderman@gmx.net)", + "About", + eMB_OK, + eMB_ICONDEFAULT); + } + if(string_equal(command, "Create tag file")) + { + CreateTagFile(); + } + } + + void loadArchiveFile(const char* filename) + { + archives.push_back(filename); + } + + typedef FreeCaller1 LoadArchiveFileCaller; + + void LoadTextureFile(const char* filename) + { + std::string s_filename = filename; + + char buffer[256]; + strcpy(buffer, "textures/"); + + // append filename without trailing file extension (.tga or .jpg for example) + strncat(buffer, filename, s_filename.length() - 4); + + std::set::iterator iter; + iter = shaders.find(buffer); + + // a shader with this name already exists + if(iter == shaders.end()) + { + textures.insert(buffer); + } + } + + typedef FreeCaller1 LoadTextureFileCaller; + + void GetTextures(char* extension) + { + GlobalFileSystem().forEachFile("textures/", extension, LoadTextureFileCaller(), 0); + } + + void LoadShaderList(const char* filename) + { + if(string_equal_prefix(filename, "textures/")) + { + shaders.insert(filename); + } + } + + typedef FreeCaller1 LoadShaderListCaller; + + void GetAllShaders() + { + GlobalShaderSystem().foreachShaderName(LoadShaderListCaller()); + } + + void GetArchiveList() + { + GlobalFileSystem().forEachArchive(LoadArchiveFileCaller()); + globalOutputStream() << "Shaderplug: " << (const Unsigned)Shaderplug::archives.size() << " archives found.\n"; + } + + void CreateTagFile() + { + const char* shader_type = GlobalRadiant().getGameDescriptionKeyValue("shaders"); + + GetAllShaders(); + globalOutputStream() << "Shaderplug: " << (const Unsigned)shaders.size() << " shaders found.\n"; + + if(string_equal(shader_type, "quake3")) + { + GetTextures("jpg"); + GetTextures("tga"); + GetTextures("png"); + + globalOutputStream() << "Shaderplug: " << (const Unsigned)textures.size() << " textures found.\n"; + } + + if(shaders.size() || textures.size() != 0) + { + globalOutputStream() << "Shaderplug: Creating XML tag file.\n"; + + TagBuilder.CreateXmlDocument(); + + std::set::reverse_iterator r_iter; + + for(r_iter = textures.rbegin(); r_iter != textures.rend(); ++r_iter) + { + TagBuilder.AddShaderNode(const_cast((*r_iter).c_str()), STOCK, TEXTURE); + } + + for(r_iter = shaders.rbegin(); r_iter != shaders.rend(); ++r_iter) + { + TagBuilder.AddShaderNode(const_cast((*r_iter).c_str()), STOCK, SHADER); + } + + // Get the tag file + StringOutputStream tagFileStream(256); + tagFileStream << GlobalRadiant().getLocalRcPath() << SHADERTAG_FILE; + char* tagFile = tagFileStream.c_str(); + + char message[256]; + strcpy(message, "Tag file saved to\n"); + strcat(message, tagFile); + strcat(message, "\nPlease restart Radiant now.\n"); + + if(file_exists(tagFile)) + { + EMessageBoxReturn result = GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window), + "WARNING! A tag file already exists! Overwrite it?", "Overwrite tag file?", + eMB_NOYES, + eMB_ICONWARNING); + + if(result == eIDYES) + { + TagBuilder.SaveXmlDoc(tagFile); + GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window), message, "INFO", eMB_OK, eMB_ICONASTERISK); + } + } else { + TagBuilder.SaveXmlDoc(tagFile); + GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window), message, "INFO", eMB_OK, eMB_ICONASTERISK); + } + } else { + GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window), + "No shaders or textures found. No XML tag file created!\n" + "", + "ERROR", + eMB_OK, + eMB_ICONERROR); + } + } +} // namespace + +class ShaderPluginModule +{ + _QERPluginTable m_plugin; +public: + typedef _QERPluginTable Type; + STRING_CONSTANT(Name, "ShaderPlug"); + + ShaderPluginModule() + { + m_plugin.m_pfnQERPlug_Init = &Shaderplug::init; + m_plugin.m_pfnQERPlug_GetName = &Shaderplug::getName; + m_plugin.m_pfnQERPlug_GetCommandList = &Shaderplug::getCommandList; + m_plugin.m_pfnQERPlug_GetCommandTitleList = &Shaderplug::getCommandTitleList; + m_plugin.m_pfnQERPlug_Dispatch = &Shaderplug::dispatch; + } + _QERPluginTable* getTable() + { + return &m_plugin; + } +}; + +typedef SingletonModule SingletonShaderPluginModule; + +SingletonShaderPluginModule g_ShaderPluginModule; + +extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer& server) +{ + initialiseModule(server); + + g_ShaderPluginModule.selfRegister(); +} + + + + diff --git a/contrib/shaderplug/shaderplug.def b/contrib/shaderplug/shaderplug.def new file mode 100644 index 00000000..be71ca58 --- /dev/null +++ b/contrib/shaderplug/shaderplug.def @@ -0,0 +1,7 @@ +; shaderplug.def : Declares the module parameters for the DLL. + +LIBRARY "SHADERPLUG" + +EXPORTS + ; Explicit exports can go here + Radiant_RegisterModules @1 diff --git a/contrib/shaderplug/shaderplug.h b/contrib/shaderplug/shaderplug.h new file mode 100644 index 00000000..4842cc22 --- /dev/null +++ b/contrib/shaderplug/shaderplug.h @@ -0,0 +1,27 @@ +/* +Copyright (C) 2006, Stefan Greven. +All Rights Reserved. + +This file is part of GtkRadiant. + +GtkRadiant is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +GtkRadiant is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GtkRadiant; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if !defined(INCLUDED_SHADERPLUG_H) +#define INCLUDED_SHADERPLUG_H + +#include "xml/xmltextags.h" + +#endif diff --git a/contrib/shaderplug/shaderplug.vcproj b/contrib/shaderplug/shaderplug.vcproj new file mode 100644 index 00000000..1041b73b --- /dev/null +++ b/contrib/shaderplug/shaderplug.vcproj @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/sunplug/sunplug.vcproj b/contrib/sunplug/sunplug.vcproj index 2d6fce90..8cf2773b 100644 --- a/contrib/sunplug/sunplug.vcproj +++ b/contrib/sunplug/sunplug.vcproj @@ -31,7 +31,7 @@ BrowseInformation="0" WarningLevel="3" Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="4"/> + DebugInformationFormat="3"/> @@ -233,6 +233,12 @@ + + + + diff --git a/libs/xml/xmltextags.cpp b/libs/xml/xmltextags.cpp new file mode 100644 index 00000000..34b05d54 --- /dev/null +++ b/libs/xml/xmltextags.cpp @@ -0,0 +1,593 @@ +/* +Copyright (C) 2006, Stefan Greven. +All Rights Reserved. + +This file is part of GtkRadiant. + +GtkRadiant is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +GtkRadiant is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GtkRadiant; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "xmltextags.h" + +#include + +#include "qerplugin.h" +#include "stream/stringstream.h" + +XmlTagBuilder::XmlTagBuilder() +{ +} + +XmlTagBuilder::~XmlTagBuilder() +{ + // clean up + xmlFreeDoc(doc); + xmlXPathFreeContext(context); +} + +bool XmlTagBuilder::CreateXmlDocument() +{ + /* Creates an XML file + + returns TRUE if the file was created successfully or FALSE when failed + */ + + xmlTextWriterPtr writer; + + writer = xmlNewTextWriterDoc(&doc, 0); + + // begin a new UTF-8 formatted xml document + xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL); + + // create the root node with stock and custom elements + xmlTextWriterStartElement(writer, (xmlChar*)"root"); + xmlTextWriterWriteString(writer, (xmlChar*)"\n "); + xmlTextWriterStartElement(writer, (xmlChar*)"stock"); + xmlTextWriterWriteString(writer, (xmlChar*)"\n "); + xmlTextWriterEndElement(writer); + xmlTextWriterWriteString(writer, (xmlChar*)"\n "); + xmlTextWriterStartElement(writer, (xmlChar*)"custom"); + xmlTextWriterWriteString(writer, (xmlChar*)"\n "); + xmlTextWriterEndElement(writer); + xmlTextWriterWriteString(writer, (xmlChar*)"\n"); + xmlTextWriterEndElement(writer); + + // end of the xml document + xmlTextWriterEndDocument(writer); + xmlFreeTextWriter(writer); + + if(!doc) + { + return false; + } else { + context = xmlXPathNewContext(doc); + return true; + } +} + +bool XmlTagBuilder::OpenXmlDoc(const char* file) +{ + /* Reads a XML document from a file + + returns TRUE if the document was read successfully or FALSE when failed + */ + + filename = file; + doc = xmlParseFile(file); // TODO error checking! + + if(!doc) + { + return false; + } else { + context = xmlXPathNewContext(doc); + return true; + } +} + +bool XmlTagBuilder::SaveXmlDoc(void) +{ + return SaveXmlDoc(filename.c_str()); +} + +bool XmlTagBuilder::SaveXmlDoc(const char* file) +{ + /* Writes the XML document + + returns TRUE if the document was saved successfully or FALSE when saving failed + */ + + xmlSaveNoEmptyTags = 1; + + if(xmlSaveFile(file, doc) != -1) + { + return true; + } + return false; +} + +bool XmlTagBuilder::AddShaderNode(const char* shader, TextureType textureType, NodeShaderType nodeShaderType) +{ + /* Adds a shader node + + char* shader - the name of the shader or texture (without trailing .tga or something) + + returns TRUE if the node was added successfully or FALSE when failed + */ + + xmlNodeSetPtr nodePtr = NULL; + xmlXPathObjectPtr xpathPtr = NULL; + + switch(textureType) + { + case STOCK: + xpathPtr = XpathEval("/root/stock"); + break; + case CUSTOM: + xpathPtr = XpathEval("/root/custom"); + }; + + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return false; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + xmlNodePtr newnode, newtext; + xmlNodePtr nodeParent = nodePtr->nodeTab[0]; + + // create a new node and set the node attribute (shader path) + switch(nodeShaderType) + { + case SHADER: + newnode = xmlNewNode(NULL, (xmlChar*)"shader"); + break; + case TEXTURE: + newnode = xmlNewNode(NULL, (xmlChar*)"texture"); + }; + + newnode = xmlDocCopyNode(newnode, doc, 1); + xmlSetProp(newnode, (xmlChar*)"path", (xmlChar*)shader); + xmlNodeSetContent(newnode, (xmlChar*)"\n "); + + if(nodePtr->nodeTab[0]->children->next == NULL) // there are no shaders yet + { + // add spaces + newtext = xmlNewText((xmlChar*)" "); + xmlAddChild(nodeParent->children, newtext); + + // add the new node + xmlAddNextSibling(nodeParent->children, newnode); + + // append a new line + newtext = xmlNewText((xmlChar*)"\n "); + xmlAddNextSibling(nodeParent->children->next, newtext); + } else { + // add the node + xmlAddNextSibling(nodeParent->children, newnode); + + // append a new line and spaces + newtext = xmlNewText((xmlChar*)"\n "); + xmlAddNextSibling(nodeParent->children->next, newtext); + } + + xmlXPathFreeObject(xpathPtr); + return true; + } else { + xmlXPathFreeObject(xpathPtr); + return false; + } +} + +bool XmlTagBuilder::DeleteShaderNode(const char* shader) +{ + /* Deletes a shader node + + char* shader - the name of the shader or texture (without trailing .tga or something) + + returns TRUE if the node was deleted successfully or FALSE when failed + */ + + char buffer[256]; + char* expression = GetTagsXpathExpression(buffer, shader, EMPTY); + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return false; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + xmlNodePtr ptrContent = nodePtr->nodeTab[0]; + xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev; + + // delete the node + xmlUnlinkNode(ptrContent); + xmlFreeNode(ptrContent); + + // delete leading whitespace node + xmlUnlinkNode(ptrWhitespace); + xmlFreeNode(ptrWhitespace); + xmlXPathFreeObject(xpathPtr); + return true; + } + xmlXPathFreeObject(xpathPtr); + return false; +} + +bool XmlTagBuilder::CheckShaderTag(const char* shader) +{ + /* Checks whether there exists an entry for a shader/texture with at least one tag + + char* shader - the name of the shader or texture (without trailing .tga or something) + + returns TRUE if the shader is already stored in the XML tag file and has at least one tag + */ + + // build the XPath expression to search for + char buffer[256]; + strcpy(buffer, "/root/*/*[@path='"); + strcat(buffer, shader); + strcat(buffer, "']"); + + char* expression = buffer; + + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return false; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + xmlXPathFreeObject(xpathPtr); + return true; + } else { + xmlXPathFreeObject(xpathPtr); + return false; + } +} + +bool XmlTagBuilder::CheckShaderTag(const char* shader, const char* content) +{ + /* Checks whether a tag with content already exists + + char* shader - the name of the shader or texture (without trailing .tga or something) + char* content - the node content (a tag name) + + returns TRUE if the tag with content already exists or FALSE if not + */ + + // build the XPath expression to search for + // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']"; + + char buffer[256]; + strcpy(buffer, "/root/*/*[@path='"); + strcat(buffer, shader); + strcat(buffer, "'][child::tag='"); + strcat(buffer, content); + strcat(buffer, "']"); + + char* expression = buffer; + + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return false; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + xmlXPathFreeObject(xpathPtr); + return true; + } else { + xmlXPathFreeObject(xpathPtr); + return false; + } +} + +bool XmlTagBuilder::AddShaderTag(const char* shader, const char* content, NodeTagType nodeTagType) +{ + /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet + + char* shader - the name of the shader or texture (without trailing .tga or something) + char* content - the node content (a tag name) + + returns TRUE if the node was added successfully or FALSE when failed + */ + + // build the XPath expression + char buffer[256]; + char* expression = GetTagsXpathExpression(buffer, shader, EMPTY); + + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return false; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) // node was found + { + xmlNodePtr newnode = xmlNewNode(NULL, (xmlChar*)"tag"); + xmlNodePtr nodeParent = nodePtr->nodeTab[0]; + newnode = xmlDocCopyNode(newnode, doc, 1); + xmlNodeSetContent(newnode, (xmlChar*)content); + + if(nodePtr->nodeTab[0]->children->next == NULL) // shader node has NO children + { + // add spaces + xmlNodePtr newtext = xmlNewText((xmlChar*)" "); + xmlAddChild(nodeParent->children, newtext); + + // add new node + xmlAddNextSibling(nodeParent->children, newnode); + + // append a new line + spaces + newtext = xmlNewText((xmlChar*)"\n "); + xmlAddNextSibling(nodeParent->children->next, newtext); + } else { // shader node has children already - the new node will be the first sibling + xmlAddNextSibling(nodeParent->children, newnode); + xmlNodePtr newtext = xmlNewText((xmlChar*)"\n "); + xmlAddNextSibling(nodeParent->children->next, newtext); + } + xmlXPathFreeObject(xpathPtr); + return true; + } else { + xmlXPathFreeObject(xpathPtr); + return false; + } +} + +//int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag) +int XmlTagBuilder::RenameShaderTag(const char* oldtag, CopiedString newtag) +{ + /* Replaces tag node contents + + char* oldtag - the node content that sould be changed + char* newtag - the new node content + + returns the number of renamed shaders + */ + + int num = 0; + + // build the XPath expression + char expression[256]; + strcpy(expression, "/root/*/*[child::tag='"); + strcat(expression, oldtag); + strcat(expression, "']/*"); + + xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*)expression, context); + if(!result) + return 0; + xmlNodeSetPtr nodePtr = result->nodesetval; + + for(int i = 0; i < nodePtr->nodeNr; i++) + { + xmlNodePtr ptrContent = nodePtr->nodeTab[i]; + char* content = (char*)xmlNodeGetContent(ptrContent); + + if(strcmp(content, oldtag) == 0) // found a node with old content? + { + xmlNodeSetContent(ptrContent, (xmlChar*)newtag.c_str()); + num++; + } + } + + SaveXmlDoc(); + xmlXPathFreeObject(result);// CHANGED + return num; +} + +bool XmlTagBuilder::DeleteShaderTag(const char* shader, const char* tag) +{ + /* Deletes a child node of a shader + + char* shader - the name of the shader or texture (without trailing .tga or something) + char* tag - the tag being deleted + + returns TRUE if the node was deleted successfully or FALSE when failed + */ + + char buffer[256]; + char* expression = GetTagsXpathExpression(buffer, shader, TAG); + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return false; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + for(int i = 0; i < nodePtr->nodeNr; i++) + { + xmlNodePtr ptrContent = nodePtr->nodeTab[i]; + char* content = (char*)(xmlChar*)xmlNodeGetContent(ptrContent); + + if(strcmp(content, tag) == 0) // find the node + { + xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev; + // delete the node + xmlUnlinkNode(ptrContent); + xmlFreeNode(ptrContent); + + // delete leading whitespace node + xmlUnlinkNode(ptrWhitespace); + xmlFreeNode(ptrWhitespace); + xmlXPathFreeObject(xpathPtr); + return true; + } + } + } + xmlXPathFreeObject(xpathPtr); + return false; +} + +bool XmlTagBuilder::DeleteTag(const char* tag) +{ + /* Deletes a tag from all shaders + + char* tag - the tag being deleted from all shaders + + returns TRUE if the tag was deleted successfully or FALSE when failed + */ + + char expression[256]; + strcpy(expression, "/root/*/*[child::tag='"); + strcat(expression, tag); + strcat(expression, "']"); + + std::set dellist; + TagSearch(expression, dellist); + std::set::iterator iter; + + for(iter = dellist.begin(); iter != dellist.end(); iter++) + { + DeleteShaderTag(iter->c_str(), tag); + } + SaveXmlDoc(); + + return true; +} + +void XmlTagBuilder::GetShaderTags(const char* shader, std::vector& tags) +{ + /* Gets the tags from a shader + + char* shader - the name of the shader + + returns a vector containing the tags + */ + + char* expression; + + if(shader == NULL) // get all tags from all shaders + { + expression = "/root/*/*/tag"; + } else { + char buffer[256]; + expression = GetTagsXpathExpression(buffer, shader, TAG); + } + + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + for(int i = 0; i < nodePtr->nodeNr; i++) + { + tags.push_back((CopiedString)(char*)xmlNodeGetContent(nodePtr->nodeTab[i])); + } + } + xmlXPathFreeObject(xpathPtr); +} + +void XmlTagBuilder::GetUntagged(std::set& shaders) +{ + /* Gets all textures and shaders listed in the xml file that don't have any tag + + returns a set containing the shaders (with path) + */ + + char* expression = "/root/*/*[not(child::tag)]"; + + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + xmlNodePtr ptr; + + for(int i = 0; i < nodePtr->nodeNr; i++) + { + ptr = nodePtr->nodeTab[i]; + shaders.insert((char*)xmlGetProp(ptr, (xmlChar*)"path")); + } + } + + xmlXPathFreeObject(xpathPtr); +} + +void XmlTagBuilder::GetAllTags(std::set& tags) +{ + /* Gets a list of all tags that are used (assigned to any shader) + + returns a set containing all used tags + */ + + char* expression = "/root/*/*/tag"; + + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + for(int i = 0; i < nodePtr->nodeNr; i++) + { + tags.insert((CopiedString)(char*)xmlNodeGetContent(nodePtr->nodeTab[i])); + } + } + + xmlXPathFreeObject(xpathPtr); +} + +void XmlTagBuilder::TagSearch(const char* expression, std::set& paths) +{ + /* Searches shaders by tags + + char* expression - the XPath expression to search + + returns a set containing the found shaders + */ + + xmlXPathObjectPtr xpathPtr = XpathEval(expression); + xmlNodeSetPtr nodePtr; + if(xpathPtr) + nodePtr = xpathPtr->nodesetval; + else + return; + + if(!xmlXPathNodeSetIsEmpty(nodePtr)) + { + xmlNodePtr ptr; + xmlChar* xmlattrib; + for(int i = 0; i < nodePtr->nodeNr; i++) + { + ptr = nodePtr->nodeTab[i]; + xmlattrib = xmlGetProp(ptr, (xmlChar*)"path"); + paths.insert((CopiedString)(char*)xmlattrib); + } + } + xmlXPathFreeObject(xpathPtr); +} + diff --git a/libs/xml/xmltextags.h b/libs/xml/xmltextags.h new file mode 100644 index 00000000..e58c1c14 --- /dev/null +++ b/libs/xml/xmltextags.h @@ -0,0 +1,106 @@ +/* +Copyright (C) 2006, Stefan Greven. +All Rights Reserved. + +This file is part of GtkRadiant. + +GtkRadiant is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +GtkRadiant is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GtkRadiant; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if !defined(INCLUDED_XMLTEXTAGS_H) +#define INCLUDED_XMLTEXTAGS_H + +#include +#include +#include + +#include "iscriplib.h" + +#include +#include + +enum NodeTagType +{ + TAG, + EMPTY +}; + +enum NodeShaderType +{ + SHADER, + TEXTURE +}; + +enum TextureType +{ + STOCK, + CUSTOM +}; + +class XmlTagBuilder +{ +private: + CopiedString filename; + xmlDocPtr doc; + xmlXPathContextPtr context; + xmlNodeSetPtr nodePtr; + + xmlXPathObjectPtr XpathEval(const char* queryString) + { + xmlChar* expression = (xmlChar*)queryString; + xmlXPathObjectPtr result = xmlXPathEvalExpression(expression, context); + return result; + }; + + char* GetTagsXpathExpression(char* buffer, const char* shader, NodeTagType nodeTagType) + { + strcpy(buffer, "/root/*/*[@path='"); + strcat(buffer, shader); + + switch(nodeTagType) + { + case TAG: + strcat(buffer, "']/tag"); + break; + case EMPTY: + strcat(buffer, "']"); + }; + + return buffer; + } + +public: + XmlTagBuilder(); + ~XmlTagBuilder(); + + bool CreateXmlDocument(); + bool OpenXmlDoc(const char* file); + bool SaveXmlDoc(const char* file); + bool SaveXmlDoc(void); + bool AddShaderNode(const char* shader, TextureType textureType, NodeShaderType nodeShaderType); + bool DeleteShaderNode(const char* shader); + bool CheckShaderTag(const char* shader); + bool CheckShaderTag(const char* shader, const char* content); + bool AddShaderTag(const char* shader, const char* content, NodeTagType nodeTagType); + bool DeleteTag(const char* tag); + int RenameShaderTag(const char* oldtag, CopiedString newtag); + bool DeleteShaderTag(const char* shader, const char* tag); + void GetShaderTags(const char* shader, std::vector& tags); + void GetUntagged(std::set& shaders); + void GetAllTags(std::set& tags); + void TagSearch(const char* expression, std::set& paths); +}; + +#endif diff --git a/radiant/gtkdlgs.cpp b/radiant/gtkdlgs.cpp index 71dac527..1b1e5cc4 100644 --- a/radiant/gtkdlgs.cpp +++ b/radiant/gtkdlgs.cpp @@ -613,93 +613,6 @@ void DoAbout() gtk_widget_destroy(GTK_WIDGET(window)); } -// ============================================================================= -// Texture List dialog - -void DoTextureListDlg() -{ - ModalDialog dialog; - ModalDialogButton ok_button(dialog, eIDOK); - ModalDialogButton cancel_button(dialog, eIDCANCEL); - GtkWidget* texture_list; - - GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), "Textures", dialog, 400, 400); - - GtkHBox* hbox = create_dialog_hbox(4, 4); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox)); - - { - GtkScrolledWindow* scr = create_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_box_pack_start(GTK_BOX (hbox), GTK_WIDGET(scr), TRUE, TRUE, 0); - - - { - GtkListStore* store = gtk_list_store_new(1, G_TYPE_STRING); - - GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - - { - GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); - GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 0, 0); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - } - - gtk_widget_show(view); - gtk_container_add(GTK_CONTAINER (scr), view); - - { - // Initialize dialog - GSList *textures = 0; - TextureGroupsMenu_ListItems(textures); - while (textures != 0) - { - { - GtkTreeIter iter; - gtk_list_store_append(store, &iter); - StringOutputStream name(64); - name << ConvertLocaleToUTF8(reinterpret_cast(textures->data)); - gtk_list_store_set(store, &iter, 0, name.c_str(), -1); - } - textures = g_slist_remove (textures, textures->data); - } - } - - g_object_unref(G_OBJECT(store)); - - texture_list = view; - } - } - - GtkVBox* vbox = create_dialog_vbox(4); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, TRUE, 0); - { - GtkButton* button = create_modal_dialog_button("Load", ok_button); - gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); - } - { - GtkButton* button = create_modal_dialog_button("Close", cancel_button); - gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); - } - - if(modal_dialog_show(window, dialog) == eIDOK) - { - GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(texture_list)); - - GtkTreeModel* model; - GtkTreeIter iter; - if(gtk_tree_selection_get_selected(selection, &model, &iter)) - { - GtkTreePath* path = gtk_tree_model_get_path(model, &iter); - if(gtk_tree_path_get_depth(path) == 1) - TextureBrowser_ShowDirectory(GlobalTextureBrowser(), TextureGroupsMenu_GetName(gtk_tree_path_get_indices(path)[0])); - gtk_tree_path_free(path); - } - } - - gtk_widget_destroy(GTK_WIDGET(window)); -} - // ============================================================================= // TextureLayout dialog @@ -1025,6 +938,125 @@ EMessageBoxReturn DoLightIntensityDlg (int *intensity) return ret; } +// ============================================================================= +// Add new shader tag dialog + +EMessageBoxReturn DoShaderTagDlg (CopiedString* tag, char* title) +{ + ModalDialog dialog; + GtkEntry* textentry; + ModalDialogButton ok_button(dialog, eIDOK); + ModalDialogButton cancel_button(dialog, eIDCANCEL); + + GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), title, dialog, -1, -1); + + GtkAccelGroup *accel_group = gtk_accel_group_new(); + gtk_window_add_accel_group(window, accel_group); + + { + GtkHBox* hbox = create_dialog_hbox(4, 4); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox)); + { + GtkVBox* vbox = create_dialog_vbox(4); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 0); + { + //GtkLabel* label = GTK_LABEL(gtk_label_new("Enter one ore more tags separated by spaces")); + GtkLabel* label = GTK_LABEL(gtk_label_new("ESC to cancel, ENTER to validate")); + gtk_widget_show(GTK_WIDGET(label)); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 0); + } + { + GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); + gtk_widget_show(GTK_WIDGET(entry)); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(entry), TRUE, TRUE, 0); + + gtk_widget_grab_focus(GTK_WIDGET(entry)); + + textentry = entry; + } + } + { + GtkVBox* vbox = create_dialog_vbox(4); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 0); + + { + GtkButton* button = create_modal_dialog_button("OK", ok_button); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); + widget_make_default(GTK_WIDGET(button)); + gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE); + } + { + GtkButton* button = create_modal_dialog_button("Cancel", cancel_button); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); + gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE); + } + } + } + + EMessageBoxReturn ret = modal_dialog_show(window, dialog); + if(ret == eIDOK) + { + *tag = gtk_entry_get_text(textentry); + } + + gtk_widget_destroy(GTK_WIDGET(window)); + + return ret; +} + +EMessageBoxReturn DoShaderInfoDlg (const char* name, const char* filename, char* title) +{ + ModalDialog dialog; + ModalDialogButton ok_button(dialog, eIDOK); + + GtkWindow* window = create_modal_dialog_window(MainFrame_getWindow(), title, dialog, -1, -1); + + GtkAccelGroup *accel_group = gtk_accel_group_new(); + gtk_window_add_accel_group(window, accel_group); + + { + GtkHBox* hbox = create_dialog_hbox(4, 4); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox)); + { + GtkVBox* vbox = create_dialog_vbox(4); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 0); + { + GtkLabel* label = GTK_LABEL(gtk_label_new("The selected shader")); + gtk_widget_show(GTK_WIDGET(label)); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 0); + } + { + GtkLabel* label = GTK_LABEL(gtk_label_new(name)); + gtk_widget_show(GTK_WIDGET(label)); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 0); + } + { + GtkLabel* label = GTK_LABEL(gtk_label_new("is located in file")); + gtk_widget_show(GTK_WIDGET(label)); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 0); + } + { + GtkLabel* label = GTK_LABEL(gtk_label_new(filename)); + gtk_widget_show(GTK_WIDGET(label)); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 0); + } + { + GtkButton* button = create_modal_dialog_button("OK", ok_button); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(button), FALSE, FALSE, 0); + widget_make_default(GTK_WIDGET(button)); + gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group, GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE); + } + } + } + + EMessageBoxReturn ret = modal_dialog_show(window, dialog); + + gtk_widget_destroy(GTK_WIDGET(window)); + + return ret; +} + + #ifdef WIN32 #include diff --git a/radiant/gtkdlgs.h b/radiant/gtkdlgs.h index d14be79c..36989916 100644 --- a/radiant/gtkdlgs.h +++ b/radiant/gtkdlgs.h @@ -32,14 +32,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INCLUDED_GTKDLGS_H #include "qerplugin.h" +#include "string/string.h" EMessageBoxReturn DoLightIntensityDlg (int *intensity); +EMessageBoxReturn DoShaderTagDlg (CopiedString *tag, char* title); +EMessageBoxReturn DoShaderInfoDlg (const char* name, const char* filename, char* title); EMessageBoxReturn DoTextureLayout (float *fx, float *fy); void DoTextEditor (const char* filename, int cursorpos); void DoProjectSettings(); -void DoTextureListDlg(); void DoFind(); void DoSides(int type, int axis); void DoAbout(); diff --git a/radiant/mainframe.cpp b/radiant/mainframe.cpp index c7444c49..7c8a1181 100644 --- a/radiant/mainframe.cpp +++ b/radiant/mainframe.cpp @@ -339,6 +339,19 @@ const char* AppPath_get() return g_strAppPath.c_str(); } +/// the path to the local rc-dir +const char* LocalRcPath_get(void) +{ + static CopiedString rc_path; + if(rc_path.empty()) + { + StringOutputStream stream(256); + stream << GlobalRadiant().getSettingsPath() << g_pGameDescription->mGameFile.c_str() << "/"; + rc_path = stream.c_str(); + } + return rc_path.c_str(); +} + /// directory for temp files /// NOTE: on *nix this is were we check for .pid CopiedString g_strSettingsPath; @@ -2018,7 +2031,7 @@ GtkMenuItem* create_view_menu(MainFrame::EViewStyle style) if(style == MainFrame::eFloating || style == MainFrame::eSplit) { create_menu_item_with_mnemonic(menu, "Console View", "ToggleConsole"); - create_menu_item_with_mnemonic(menu, "Texture Browser", "ViewTextures"); + create_menu_item_with_mnemonic(menu, "Texture Browser", "ToggleTextures"); create_menu_item_with_mnemonic(menu, "Entity Inspector", "ToggleEntityInspector"); } else @@ -2183,44 +2196,6 @@ GtkMenuItem* create_grid_menu() return grid_menu_item; } -void RefreshShaders() -{ - ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Shaders"); - GlobalShaderSystem().refresh(); - UpdateAllWindows(); -} - - -GtkMenuItem* create_textures_menu() -{ - // Textures menu - GtkMenuItem* textures_menu_item = new_sub_menu_item_with_mnemonic("_Textures"); - GtkMenu* menu = GTK_MENU(gtk_menu_item_get_submenu(textures_menu_item)); - g_textures_menu = menu; - if (g_Layout_enableDetachableMenus.m_value) - menu_tearoff (menu); - - create_check_menu_item_with_mnemonic(menu, "Hide _Unused", "ShowInUse"); - create_menu_item_with_mnemonic(menu, "Show All", "ShowAllTextures"); - - menu_separator(menu); - create_check_menu_item_with_mnemonic(menu, "Show shaders", "ToggleShowShaders"); - create_menu_item_with_mnemonic(menu, "Flush & Reload Shaders", "RefreshShaders"); - create_menu_item_with_mnemonic(menu, "Directory list...", "TextureDirectoryList"); - menu_separator(menu); - - create_menu_item_with_mnemonic(menu, "Find / Replace...", "FindReplaceTextures"); - - - menu_separator(menu); - create_check_menu_item_with_mnemonic (menu, "Shaders Only", "ToggleShowShaderlistOnly"); - g_textures_menu_separator = menu_separator(menu); - - TextureGroupsMenu_Construct(); - - return textures_menu_item; -} - GtkMenuItem* create_misc_menu() { // Misc menu @@ -2315,7 +2290,6 @@ GtkMenuBar* create_main_menu(MainFrame::EViewStyle style) gtk_container_add(GTK_CONTAINER(menu_bar), GTK_WIDGET(create_selection_menu())); gtk_container_add(GTK_CONTAINER(menu_bar), GTK_WIDGET(create_bsp_menu())); gtk_container_add(GTK_CONTAINER(menu_bar), GTK_WIDGET(create_grid_menu())); - gtk_container_add(GTK_CONTAINER(menu_bar), GTK_WIDGET(create_textures_menu())); gtk_container_add(GTK_CONTAINER(menu_bar), GTK_WIDGET(create_misc_menu())); gtk_container_add(GTK_CONTAINER(menu_bar), GTK_WIDGET(create_entity_menu())); gtk_container_add(GTK_CONTAINER(menu_bar), GTK_WIDGET(create_brush_menu())); @@ -2507,6 +2481,21 @@ GtkToolbar* create_main_toolbar(MainFrame::EViewStyle style) toolbar_append_toggle_button(toolbar, "Texture Lock", "texture_lock.bmp", "TogTexLock"); + gtk_toolbar_append_space (GTK_TOOLBAR (toolbar)); + + GtkButton* g_view_entities_button = toolbar_append_button(toolbar, "Entities", "entities.bmp", "ToggleEntityInspector"); + GtkButton* g_view_console_button = toolbar_append_button(toolbar, "Console", "console.bmp", "ToggleConsole"); + GtkButton* g_view_textures_button = toolbar_append_button(toolbar, "Texture Browser", "texture_browser.bmp", "ToggleTextures"); + // TODO: call light inspector + //GtkButton* g_view_lightinspector_button = toolbar_append_button(toolbar, "Light Inspector", "lightinspector.bmp", "ToggleLightInspector"); + + // disable the console and texture button in the regular layouts + if(style == MainFrame::eRegular || style == MainFrame::eRegularLeft) + { + gtk_widget_set_sensitive(GTK_WIDGET(g_view_console_button), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(g_view_textures_button), FALSE); + } + return toolbar; } @@ -2980,7 +2969,6 @@ void MainFrame::Create() GtkFrame* texture_window = create_framed_widget(TextureBrowser_constructWindow(window)); gtk_paned_add2(GTK_PANED(vsplit2), GTK_WIDGET(texture_window)); - } } } @@ -3164,8 +3152,6 @@ void MainFrame::Shutdown() EntityList_destroyWindow(); - g_textures_menu = 0; - delete m_pXYWnd; m_pXYWnd = 0; delete m_pYZWnd; @@ -3420,10 +3406,6 @@ void MainFrame_Construct() GlobalCommands_insert("CSGMerge", FreeCaller(), Accelerator('U', (GdkModifierType)GDK_CONTROL_MASK)); GlobalCommands_insert("CSGHollow", FreeCaller()); - GlobalCommands_insert("TextureDirectoryList", FreeCaller()); - - GlobalCommands_insert("RefreshShaders", FreeCaller()); - Grid_registerCommands(); GlobalCommands_insert("SnapToGrid", FreeCaller(), Accelerator('G', (GdkModifierType)GDK_CONTROL_MASK)); diff --git a/radiant/mainframe.h b/radiant/mainframe.h index 53f3844b..5997abd3 100644 --- a/radiant/mainframe.h +++ b/radiant/mainframe.h @@ -225,6 +225,8 @@ const char* AppPath_get(); extern CopiedString g_strSettingsPath; const char* SettingsPath_get(); +const char* LocalRcPath_get(void); + const char* const g_pluginsDir = "plugins/"; ///< name of plugins directory, always sub-directory of toolspath const char* const g_modulesDir = "modules/"; ///< name of modules directory, always sub-directory of toolspath diff --git a/radiant/patch.h b/radiant/patch.h index d1763873..ddbbffbd 100644 --- a/radiant/patch.h +++ b/radiant/patch.h @@ -116,7 +116,7 @@ struct BezierCurve Vector3 right; }; -const std::size_t BEZIERCURVETREE_MAX_INDEX = std::numeric_limits::max() / 2 + 1; +const std::size_t BEZIERCURVETREE_MAX_INDEX = std::size_t(1) << (std::numeric_limits::digits - 1); struct BezierCurveTree { diff --git a/radiant/plugin.cpp b/radiant/plugin.cpp index b4816b35..fad47ea2 100644 --- a/radiant/plugin.cpp +++ b/radiant/plugin.cpp @@ -130,6 +130,7 @@ public: RadiantCoreAPI() { m_radiantcore.getEnginePath = &EnginePath_get; + m_radiantcore.getLocalRcPath = &LocalRcPath_get; m_radiantcore.getAppPath = &AppPath_get; m_radiantcore.getGameToolsPath = &GameToolsPath_get; m_radiantcore.getSettingsPath = &SettingsPath_get; diff --git a/radiant/texwindow.cpp b/radiant/texwindow.cpp index 04476866..a036dbdd 100644 --- a/radiant/texwindow.cpp +++ b/radiant/texwindow.cpp @@ -30,34 +30,29 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "debugging/debugging.h" #include "warnings.h" -#include "iimage.h" #include "ifilesystem.h" -#include "ishaders.h" -#include "iscriplib.h" -#include "iselection.h" -#include "iscenegraph.h" -#include "itextures.h" -#include "irender.h" #include "iundo.h" #include "igl.h" #include "iarchive.h" #include "moduleobserver.h" #include +#include +#include -#include +#include #include #include #include #include #include -#include #include "signal/signal.h" #include "math/vector.h" #include "texturelib.h" #include "string/string.h" #include "shaderlib.h" +#include "os/file.h" #include "os/path.h" #include "stream/memstream.h" #include "stream/textfilestream.h" @@ -72,6 +67,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "gtkutil/cursor.h" #include "gtkutil/widget.h" #include "gtkutil/glwidget.h" +#include "gtkutil/messagebox.h" #include "error.h" #include "map.h" @@ -93,156 +89,20 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "shaders.h" #include "commands.h" - - -bool TextureGroupsMenu_showWads() +bool TextureBrowser_showWads() { return !string_empty(g_pGameDescription->getKeyValue("show_wads")); } -// globals for textures -class TextureMenuName -{ - enum { c_menuNameLength = 64 }; - char m_name[c_menuNameLength]; -public: - TextureMenuName(const char* name) - { - strncpy(m_name, name, c_menuNameLength - 1); - m_name[c_menuNameLength - 1] = '\0'; - } - const char* c_str() const - { - return m_name; - } -}; - -typedef std::vector TextureMenuNames; -TextureMenuNames texture_menunames; - -const char* TextureGroupsMenu_GetName(std::size_t menunum) -{ - return texture_menunames[menunum].c_str(); -} - -void TextureGroupsMenu_ListItems(GSList*& items) -{ - for(TextureMenuNames::const_iterator i = texture_menunames.begin(); i != texture_menunames.end(); ++i) - { - items = g_slist_append(items, const_cast((*i).c_str())); - } -} - -void TextureBrowser_queueDraw(TextureBrowser& textureBrower); - -class TextureGroupLoader -{ - std::size_t m_id; -public: - TextureGroupLoader(std::size_t id) - : m_id(id) - { - } - void loadGroup() - { - ScopeDisableScreenUpdates disableScreenUpdates(TextureGroupsMenu_GetName(m_id), "Loading Textures"); - - TextureBrowser_ShowDirectory(GlobalTextureBrowser(), TextureGroupsMenu_GetName(m_id)); - TextureBrowser_queueDraw(GlobalTextureBrowser()); - } -}; - -std::list g_texture_group_loaders; - -void texturegroup_activated(GtkWidget* widget, gpointer data) -{ - reinterpret_cast(data)->loadGroup(); -} +void TextureBrowser_queueDraw(TextureBrowser& textureBrowser); bool string_equal_start(const char* string, StringRange start) { return string_equal_n(string, start.first, start.last - start.first); } -GtkMenuItem* MenuItem_create(const char* name) -{ - StringOutputStream buffer(64); - buffer << ConvertLocaleToUTF8(name); - return GTK_MENU_ITEM(gtk_menu_item_new_with_label(buffer.c_str())); -} - -GtkMenuItem* Menu_addItem(GtkMenu* menu, const char* name) -{ - GtkMenuItem* item = MenuItem_create(name); - gtk_widget_show(GTK_WIDGET(item)); - menu_add_item(menu, item); - return item; -} - -void TextureGroupsMenu_addItem(GtkMenu* menu, const char* dirName) -{ - GtkMenuItem* item = Menu_addItem(menu, dirName); - - g_texture_group_loaders.push_back(TextureGroupLoader(texture_menunames.size())); - g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(texturegroup_activated), &g_texture_group_loaders.back()); - - if(TextureGroupsMenu_showWads()) - { - texture_menunames.push_back(dirName); - } - else - { - char buffer[1024]; - strcpy(buffer, dirName); - strcat(buffer, "/"); - texture_menunames.push_back(buffer); - } -} - typedef std::set TextureGroups; -void TextureGroupsMenu_Construct(GtkMenu* menu, const TextureGroups& groups) -{ - texture_menunames.clear(); - - TextureGroups::const_iterator i = groups.begin(); - while(i != groups.end()) - { - const char* dirName = (*i).c_str(); - const char* firstUnderscore = strchr(dirName, '_'); - StringRange dirRoot(dirName, (firstUnderscore == 0) ? dirName : firstUnderscore + 1); - - // do we shrink the menus? - // we shrink only if we have at least two things to shrink :-) - TextureGroups::const_iterator next = i; - ++next; - if(firstUnderscore != 0 - && next != groups.end() - && string_equal_start((*next).c_str(), dirRoot)) - { - GtkMenuItem* item = Menu_addItem(menu, CopiedString(StringRange(dirName, firstUnderscore)).c_str()); - - GtkMenu *pSubMenu = GTK_MENU(gtk_menu_new()); - gtk_menu_item_set_submenu(item, GTK_WIDGET(pSubMenu)); - - // keep going... - while(i != groups.end() && string_equal_start((*i).c_str(), dirRoot)) - { - TextureGroupsMenu_addItem(pSubMenu, (*i).c_str()); - - ++i; - } - } - else - { - TextureGroupsMenu_addItem(menu, dirName); - - ++i; - } - } -} - - void TextureGroups_addWad(TextureGroups& groups, const char* archive) { if(extension_equal(path_get_extension(archive), "wad")) @@ -277,81 +137,11 @@ void TextureGroups_addDirectory(TextureGroups& groups, const char* directory) } typedef ReferenceCaller1 TextureGroupsAddDirectoryCaller; -GtkMenu* g_textures_menu = 0; -GtkMenuItem* g_textures_menu_separator = 0; namespace { - bool g_TexturesMenu_shaderlistOnly = false; -} -void TextureGroupsMenu_Construct() -{ - TextureGroups groups; - - if(TextureGroupsMenu_showWads()) - { - GlobalFileSystem().forEachArchive(TextureGroupsAddWadCaller(groups)); - } - else - { - // scan texture dirs and pak files only if not restricting to shaderlist - if(g_pGameDescription->mGameType != "doom3" && !g_TexturesMenu_shaderlistOnly) - { - GlobalFileSystem().forEachDirectory("textures/", TextureGroupsAddDirectoryCaller(groups)); - } - - GlobalShaderSystem().foreachShaderName(TextureGroupsAddShaderCaller(groups)); - } - - TextureGroupsMenu_Construct(g_textures_menu, groups); -} - -void TextureGroupsMenu_Destroy() -{ - // delete everything - GtkMenu* menu = g_textures_menu; - GtkMenuItem* sep = g_textures_menu_separator; - GList* lst = g_list_find(gtk_container_children(GTK_CONTAINER(menu)), GTK_WIDGET(sep)); - while(lst->next) - { - // these delete functions are recursive, it's gonna free all submenus - gtk_widget_destroy(GTK_WIDGET (lst->next->data)); - // lst is no longer relevant, need to get it again - lst = g_list_find(gtk_container_children(GTK_CONTAINER(menu)), GTK_WIDGET(sep)); - } + bool g_TextureBrowser_shaderlistOnly = false; } - -class TextureGroupsMenu : public ModuleObserver -{ - std::size_t m_unrealised; -public: - TextureGroupsMenu() : m_unrealised(2) - { - } - void realise() - { - if(--m_unrealised == 0) - { - if(g_textures_menu != 0) - { - TextureGroupsMenu_Construct(); - } - } - } - void unrealise() - { - if(++m_unrealised == 1) - { - if(g_textures_menu != 0) - { - TextureGroupsMenu_Destroy(); - } - } - } -}; - -TextureGroupsMenu g_TextureGroupsMenu; - class DeferredAdjustment { gdouble m_value; @@ -409,9 +199,17 @@ enum StartupShaders { STARTUPSHADERS_NONE = 0, STARTUPSHADERS_COMMON, - STARTUPSHADERS_ALL, }; +void TextureBrowser_hideUnusedExport(const BoolImportCallback& importer); +typedef FreeCaller1 TextureBrowserHideUnusedExport; + +void TextureBrowser_showShadersExport(const BoolImportCallback& importer); +typedef FreeCaller1 TextureBrowserShowShadersExport; + +void TextureBrowser_showShaderlistOnly(const BoolImportCallback& importer); +typedef FreeCaller1 TextureBrowserShowShaderlistOnlyExport; + class TextureBrowser { public: @@ -421,17 +219,32 @@ public: CopiedString shader; - GtkEntry* m_filter; - NonModalEntry m_filterEntry; - GtkWindow* m_parent; GtkWidget* m_gl_widget; + GtkWidget* m_texture_scroll; + GtkWidget* m_treeViewTree; + GtkWidget* m_treeViewTags; + GtkWidget* m_tag_frame; + GtkListStore* m_assigned_store; + GtkListStore* m_available_store; + GtkWidget* m_assigned_tree; + GtkWidget* m_available_tree; + GtkWidget* m_scr_win_tree; + GtkWidget* m_scr_win_tags; + GtkWidget* m_shader_info_item; + + std::set m_all_tags; + GtkListStore* m_all_tags_list; + std::vector m_copied_tags; + std::set m_found_shaders; + + ToggleItem m_hideunused_item; + ToggleItem m_showshaders_item; + ToggleItem m_showshaderlistonly_item; guint m_sizeHandler; guint m_exposeHandler; - GtkWidget* m_texture_scroll; - bool m_heightChanged; bool m_originInvalid; @@ -442,7 +255,6 @@ public: // the increment step we use against the wheel mouse std::size_t m_mouseWheelScrollIncrement; std::size_t m_textureScale; - bool m_showTextureFilter; // make the texture increments match the grid changes bool m_showShaders; bool m_showTextureScrollbar; @@ -450,30 +262,30 @@ public: // if true, the texture window will only display in-use shaders // if false, all the shaders in memory are displayed bool m_hideUnused; - - - void clearFilter() - { - gtk_entry_set_text(m_filter, ""); - TextureBrowser_queueDraw(*this); - } - typedef MemberCaller ClearFilterCaller; + bool m_rmbSelected; + bool m_searchedTags; + bool m_tags; + bool m_showTags; TextureBrowser() : - m_filter(0), - m_filterEntry(TextureBrowserQueueDrawCaller(*this), ClearFilterCaller(*this)), m_texture_scroll(0), + m_hideunused_item(TextureBrowserHideUnusedExport()), + m_showshaders_item(TextureBrowserShowShadersExport()), + m_showshaderlistonly_item(TextureBrowserShowShaderlistOnlyExport()), m_heightChanged(true), m_originInvalid(true), m_scrollAdjustment(TextureBrowser_scrollChanged, this), color_textureback(0.25f, 0.25f, 0.25f), m_mouseWheelScrollIncrement(64), m_textureScale(50), - m_showTextureFilter(false), - m_showShaders(true), + m_showShaders(true), m_showTextureScrollbar(true), m_startupShaders(STARTUPSHADERS_NONE), - m_hideUnused(false) + m_hideUnused(false), + m_rmbSelected(false), + m_searchedTags(false), + m_tags(false), + m_showTags(false) { } }; @@ -504,21 +316,6 @@ const char* TextureBrowser_getComonShadersDir() return "common/"; } - -void TextureBrowser_setShowFilter(TextureBrowser& textureBrowser, bool show) -{ - widget_set_visible(GTK_WIDGET(textureBrowser.m_filter), show); -} - -const char* TextureBrowser_getFilter(TextureBrowser& textureBrowser) -{ - if(textureBrowser.m_showTextureFilter) - { - return gtk_entry_get_text(textureBrowser.m_filter); - } - return 0; -} - inline int TextureBrowser_fontHeight(TextureBrowser& textureBrowser) { return GlobalOpenGL().m_fontHeight; @@ -551,6 +348,19 @@ void TextureBrowser_SetSelectedShader(TextureBrowser& textureBrowser, const char { FindTextureDialog_selectTexture(shader); } + + // disable the menu item "shader info" if no shader was selected + IShader* ishader = QERApp_Shader_ForName(shader); + CopiedString filename = ishader->getShaderFileName(); + + if(filename.empty()) + { + gtk_widget_set_sensitive(textureBrowser.m_shader_info_item, FALSE); + } else { + gtk_widget_set_sensitive(textureBrowser.m_shader_info_item, TRUE); + } + + ishader->DecRef(); } @@ -609,9 +419,37 @@ void Texture_NextPos(TextureBrowser& textureBrowser, TextureLayout& layout, qtex layout.current_x += 8; } +bool TextureSearch_IsShown(const char* name) +{ + std::set::iterator iter; + + iter = GlobalTextureBrowser().m_found_shaders.find(name); + + if(iter == GlobalTextureBrowser().m_found_shaders.end()) + { + return false; + } else { + return true; + } +} + // if texture_showinuse jump over non in-use textures -bool Texture_IsShown(IShader* shader, bool show_shaders, bool hideUnused, const char* filter) +bool Texture_IsShown(IShader* shader, bool show_shaders, bool hideUnused) { + if(g_TextureBrowser_currentDirectory == "Untagged") + { + std::set::iterator iter; + + iter = GlobalTextureBrowser().m_found_shaders.find(shader->getName()); + + if(iter == GlobalTextureBrowser().m_found_shaders.end()) + { + return false; + } else { + return true; + } + } + if(!shader_equal_prefix(shader->getName(), "textures/")) return false; @@ -621,19 +459,19 @@ bool Texture_IsShown(IShader* shader, bool show_shaders, bool hideUnused, const if(hideUnused && !shader->IsInUse()) return false; - if(!string_empty(g_TextureBrowser_currentDirectory.c_str())) + if(GlobalTextureBrowser().m_searchedTags) { + if(!TextureSearch_IsShown(shader->getName())) + { + return false; + } else { + return true; + } + } else { if(!shader_equal_prefix(shader_get_textureName(shader->getName()), g_TextureBrowser_currentDirectory.c_str())) { - return false; - } - } - - if (filter != 0) - { - // some basic filtering - if (strstr( shader_get_textureName(shader->getName()), filter ) == 0) - return false; + return false; + } } return true; @@ -661,7 +499,7 @@ void TextureBrowser_evaluateHeight(TextureBrowser& textureBrowser) { IShader* shader = QERApp_ActiveShaders_IteratorCurrent(); - if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused, TextureBrowser_getFilter(textureBrowser))) + if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused)) continue; int x, y; @@ -768,15 +606,6 @@ void TextureBrowser_importShowScrollbar(TextureBrowser& textureBrowser, bool val } typedef ReferenceCaller1 TextureBrowserImportShowScrollbarCaller; -void TextureBrowser_importShowFilter(TextureBrowser& textureBrowser, bool value) -{ - textureBrowser.m_showTextureFilter = value; - if(textureBrowser.m_filter != 0) - { - TextureBrowser_setShowFilter(textureBrowser, textureBrowser.m_showTextureFilter); - } -} -typedef ReferenceCaller1 TextureBrowserImportShowFilterCaller; /* ============== @@ -819,7 +648,7 @@ void TextureBrowser_SetHideUnused(TextureBrowser& textureBrowser, bool hideUnuse GtkWidget* g_page_textures; -void TextureBrowser_toggleShown() +void TextureBrowser_toggleShow() { GroupDialog_showPage(g_page_textures); } @@ -896,7 +725,7 @@ public: void TextureBrowser_ShowDirectory(TextureBrowser& textureBrowser, const char* directory) { - if(TextureGroupsMenu_showWads()) + if(TextureBrowser_showWads()) { Archive* archive = GlobalFileSystem().getArchive(directory); ASSERT_NOTNULL(archive); @@ -929,6 +758,31 @@ void TextureBrowser_ShowDirectory(TextureBrowser& textureBrowser, const char* di TextureBrowser_updateTitle(); } +void TextureBrowser_ShowTagSearchResult(TextureBrowser& textureBrowser, const char* directory) +{ + g_TextureBrowser_currentDirectory = directory; + TextureBrowser_heightChanged(textureBrowser); + + std::size_t shaders_count; + GlobalShaderSystem().foreachShaderName(makeCallback1(TextureCategoryLoadShader(directory, shaders_count))); + globalOutputStream() << "Showing " << Unsigned(shaders_count) << " shaders.\n"; + + if(g_pGameDescription->mGameType != "doom3") + { + // load remaining texture files + StringOutputStream dirstring(64); + dirstring << "textures/" << directory; + + { + LoadTexturesByTypeVisitor visitor(dirstring.c_str()); + Radiant_getImageModules().foreachModule(visitor); + } + } + + // we'll display the newly loaded textures + all the ones already in use + TextureBrowser_SetHideUnused(textureBrowser, false); +} + bool TextureBrowser_hideUnused(); @@ -946,27 +800,10 @@ typedef FreeCaller1 void TextureBrowser_showShaderlistOnly(const BoolImportCallback& importer) { - importer(g_TexturesMenu_shaderlistOnly); + importer(g_TextureBrowser_shaderlistOnly); } typedef FreeCaller1 TextureBrowserShowShaderlistOnlyExport; -class TexturesMenu -{ -public: - ToggleItem m_hideunused_item; - ToggleItem m_showshaders_item; - ToggleItem m_showshaderlistonly_item; - - TexturesMenu() : - m_hideunused_item(TextureBrowserHideUnusedExport()), - m_showshaders_item(TextureBrowserShowShadersExport()), - m_showshaderlistonly_item(TextureBrowserShowShaderlistOnlyExport()) - { - } -}; - -TexturesMenu g_TexturesMenu; - void TextureBrowser_SetHideUnused(TextureBrowser& textureBrowser, bool hideUnused) { if(hideUnused) @@ -978,7 +815,7 @@ void TextureBrowser_SetHideUnused(TextureBrowser& textureBrowser, bool hideUnuse textureBrowser.m_hideUnused = false; } - g_TexturesMenu.m_hideunused_item.update(); + textureBrowser.m_hideunused_item.update(); TextureBrowser_heightChanged(textureBrowser); textureBrowser.m_originInvalid = true; @@ -990,13 +827,6 @@ void TextureBrowser_ShowStartupShaders(TextureBrowser& textureBrowser) { TextureBrowser_ShowDirectory(textureBrowser, TextureBrowser_getComonShadersDir()); } - else if(textureBrowser.m_startupShaders == STARTUPSHADERS_ALL) - { - for(TextureMenuNames::const_iterator i = texture_menunames.begin(); i != texture_menunames.end(); ++i) - { - TextureBrowser_ShowDirectory(textureBrowser, (*i).c_str()); - } - } } @@ -1014,7 +844,7 @@ void TextureBrowser_Focus(TextureBrowser& textureBrowser, const char* name) { IShader* shader = QERApp_ActiveShaders_IteratorCurrent(); - if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused, TextureBrowser_getFilter(textureBrowser))) + if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused)) continue; int x, y; @@ -1057,7 +887,7 @@ IShader* Texture_At(TextureBrowser& textureBrowser, int mx, int my) { IShader* shader = QERApp_ActiveShaders_IteratorCurrent(); - if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused, TextureBrowser_getFilter(textureBrowser))) + if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused)) continue; int x, y; @@ -1102,7 +932,7 @@ void SelectTexture(TextureBrowser& textureBrowser, int mx, int my, bool bShift) TextureBrowser_SetSelectedShader(textureBrowser, shader->getName()); TextureBrowser_textureSelected(shader->getName()); - if (!FindTextureDialog_isOpen()) + if (!FindTextureDialog_isOpen() && !textureBrowser.m_rmbSelected) { UndoableCommand undo("textureNameSetSelected"); Select_SetShader(shader->getName()); @@ -1194,7 +1024,7 @@ void Texture_Draw(TextureBrowser& textureBrowser) { IShader* shader = QERApp_ActiveShaders_IteratorCurrent(); - if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused, TextureBrowser_getFilter(textureBrowser))) + if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused)) continue; int x, y; @@ -1225,7 +1055,12 @@ void Texture_Draw(TextureBrowser& textureBrowser) if (shader_equal(TextureBrowser_GetSelectedShader(textureBrowser), shader->getName())) { glLineWidth (3); - glColor3f (1,0,0); + if(textureBrowser.m_rmbSelected) + { + glColor3f (0,0,1); + } else { + glColor3f (1,0,0); + } glDisable (GL_TEXTURE_2D); glBegin (GL_LINE_LOOP); @@ -1344,7 +1179,78 @@ void TextureBrowser_MouseWheel(TextureBrowser& textureBrowser, bool bUp) TextureBrowser_setOriginY(textureBrowser, originy); } +XmlTagBuilder TagBuilder; + +enum +{ + TAG_COLUMN, + N_COLUMNS +}; + +void BuildStoreAssignedTags(GtkListStore* store, const char* shader, TextureBrowser* textureBrowser) +{ + GtkTreeIter iter; + + gtk_list_store_clear(store); + + std::vector assigned_tags; + TagBuilder.GetShaderTags(shader, assigned_tags); + + for (size_t i = 0; i < assigned_tags.size(); i++) + { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, TAG_COLUMN, assigned_tags[i].c_str(), -1); + } +} + +void BuildStoreAvailableTags( GtkListStore* storeAvailable, + GtkListStore* storeAssigned, + const std::set& allTags, + TextureBrowser* textureBrowser) +{ + GtkTreeIter iterAssigned; + GtkTreeIter iterAvailable; + std::set::const_iterator iterAll; + gchar* tag_assigned; + + gtk_list_store_clear(storeAvailable); + + bool row = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(storeAssigned), &iterAssigned) != 0; + + if(!row) // does the shader have tags assigned? + { + for (iterAll = allTags.begin(); iterAll != allTags.end(); ++iterAll) + { + gtk_list_store_append (storeAvailable, &iterAvailable); + gtk_list_store_set (storeAvailable, &iterAvailable, TAG_COLUMN, (*iterAll).c_str(), -1); + } + } + else + { + while(row) // available tags = all tags - assigned tags + { + gtk_tree_model_get(GTK_TREE_MODEL(storeAssigned), &iterAssigned, TAG_COLUMN, &tag_assigned, -1); + for (iterAll = allTags.begin(); iterAll != allTags.end(); ++iterAll) + { + if(strcmp((char*)tag_assigned, (*iterAll).c_str()) != 0) + { + gtk_list_store_append (storeAvailable, &iterAvailable); + gtk_list_store_set (storeAvailable, &iterAvailable, TAG_COLUMN, (*iterAll).c_str(), -1); + } + else + { + row = gtk_tree_model_iter_next(GTK_TREE_MODEL(storeAssigned), &iterAssigned) != 0; + + if(row) + { + gtk_tree_model_get(GTK_TREE_MODEL(storeAssigned), &iterAssigned, TAG_COLUMN, &tag_assigned, -1); + } + } + } + } + } +} gboolean TextureBrowser_button_press(GtkWidget* widget, GdkEventButton* event, TextureBrowser* textureBrowser) { @@ -1352,11 +1258,34 @@ gboolean TextureBrowser_button_press(GtkWidget* widget, GdkEventButton* event, T { if(event->button == 3) { - TextureBrowser_Tracking_MouseDown(*textureBrowser); + if(GlobalTextureBrowser().m_tags) + { + textureBrowser->m_rmbSelected = true; + TextureBrowser_Selection_MouseDown (*textureBrowser, event->state, static_cast(event->x), static_cast(event->y)); + + BuildStoreAssignedTags(textureBrowser->m_assigned_store, textureBrowser->shader.c_str(), textureBrowser); + BuildStoreAvailableTags(textureBrowser->m_available_store, textureBrowser->m_assigned_store, textureBrowser->m_all_tags, textureBrowser); + textureBrowser->m_heightChanged = true; + gtk_widget_show(textureBrowser->m_tag_frame); + + process_gui(); + + TextureBrowser_Focus(*textureBrowser, textureBrowser->shader.c_str()); + } + else + { + TextureBrowser_Tracking_MouseDown(*textureBrowser); + } } else if(event->button == 1) { TextureBrowser_Selection_MouseDown(*textureBrowser, event->state, static_cast(event->x), static_cast(event->y)); + + if(GlobalTextureBrowser().m_tags) + { + textureBrowser->m_rmbSelected = false; + gtk_widget_hide(textureBrowser->m_tag_frame); + } } } return FALSE; @@ -1368,7 +1297,10 @@ gboolean TextureBrowser_button_release(GtkWidget* widget, GdkEventButton* event, { if(event->button == 3) { - TextureBrowser_Tracking_MouseUp(*textureBrowser); + if(!GlobalTextureBrowser().m_tags) + { + TextureBrowser_Tracking_MouseUp(*textureBrowser); + } } } return FALSE; @@ -1472,67 +1404,740 @@ void TextureBrowser_ToggleHideUnused() } } -GtkWidget* TextureBrowser_constructWindow(GtkWindow* toplevel) +void TextureGroups_constructTreeModel(TextureGroups groups, GtkTreeStore* store) { - GlobalShaderSystem().setActiveShadersChangedNotify(ReferenceCaller(g_TextureBrowser)); - - GtkWidget* hbox = gtk_hbox_new (FALSE, 0); - - g_TextureBrowser.m_parent = toplevel; + // put the information from the old textures menu into a treeview + GtkTreeIter iter, child; + TextureGroups::const_iterator i = groups.begin(); + while (i != groups.end()) { - GtkWidget* w = gtk_vscrollbar_new (GTK_ADJUSTMENT (gtk_adjustment_new (0,0,0,1,1,1))); - gtk_widget_show (w); - gtk_box_pack_end (GTK_BOX (hbox), w, FALSE, TRUE, 0); - g_TextureBrowser.m_texture_scroll = w; + const char* dirName = (*i).c_str(); + const char* firstUnderscore = strchr(dirName, '_'); + StringRange dirRoot (dirName, (firstUnderscore == 0) ? dirName : firstUnderscore + 1); - GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_TextureBrowser.m_texture_scroll)); - g_signal_connect(G_OBJECT(vadjustment), "value_changed", G_CALLBACK(TextureBrowser_verticalScroll), &g_TextureBrowser); + TextureGroups::const_iterator next = i; + ++next; + if(firstUnderscore != 0 + && next != groups.end() + && string_equal_start((*next).c_str(), dirRoot)) + { + gtk_tree_store_append(store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, CopiedString(StringRange(dirName, firstUnderscore)).c_str(), -1); - widget_set_visible(g_TextureBrowser.m_texture_scroll, g_TextureBrowser.m_showTextureScrollbar); + // keep going... + while (i != groups.end() && string_equal_start((*i).c_str(), dirRoot)) + { + gtk_tree_store_append(store, &child, &iter); + gtk_tree_store_set (store, &child, 0, (*i).c_str(), -1); + ++i; + } + } + else + { + gtk_tree_store_append(store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, dirName, -1); + ++i; + } + } +} + +TextureGroups TextureGroups_constructTreeView() +{ + TextureGroups groups; + + if (TextureBrowser_showWads()) + { + GlobalFileSystem().forEachArchive (TextureGroupsAddWadCaller (groups)); } + else { - GtkWidget* texbox = gtk_vbox_new (FALSE, 0); - gtk_widget_show(texbox); - gtk_box_pack_start(GTK_BOX(hbox), texbox, TRUE, TRUE, 0); + // scan texture dirs and pak files only if not restricting to shaderlist + if (g_pGameDescription->mGameType != "doom3" && !g_TextureBrowser_shaderlistOnly) + { + GlobalFileSystem().forEachDirectory ("textures/", TextureGroupsAddDirectoryCaller(groups)); + } - { - GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); - gtk_box_pack_start(GTK_BOX(texbox), GTK_WIDGET(entry), FALSE, FALSE, 0); + GlobalShaderSystem().foreachShaderName(TextureGroupsAddShaderCaller(groups)); + } + + return groups; +} + +void TextureBrowser_constructTreeStore() +{ + TextureGroups groups = TextureGroups_constructTreeView(); + GtkTreeStore* store = gtk_tree_store_new(1, G_TYPE_STRING); + TextureGroups_constructTreeModel(groups, store); + std::set::iterator iter; + + GtkTreeModel* model = GTK_TREE_MODEL(store); + + gtk_tree_view_set_model(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTree), model); + + g_object_unref(G_OBJECT(store)); +} + +void TextureBrowser_constructTreeStoreTags() +{ + TextureGroups groups; + GtkTreeStore* store = gtk_tree_store_new(1, G_TYPE_STRING); + GtkTreeModel* model = GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list); + + gtk_tree_view_set_model(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTags), model); + + g_object_unref(G_OBJECT(store)); +} + +void TreeView_onRowActivated(GtkTreeView* treeview, GtkTreePath* path, GtkTreeViewColumn* col, gpointer userdata) +{ + GtkTreeIter iter; + + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); + + if (gtk_tree_model_get_iter (model, &iter, path)) + { + gchar* dirName; + gtk_tree_model_get(model, &iter, 0, &dirName, -1); + + g_TextureBrowser.m_searchedTags = false; + + if(!TextureBrowser_showWads()) + { + char buffer[1024]; + strcpy(buffer, dirName); + strcat(buffer, "/"); + dirName = buffer; + } + + ScopeDisableScreenUpdates disableScreenUpdates(dirName, "Loading Textures"); + TextureBrowser_ShowDirectory(GlobalTextureBrowser (), dirName); + TextureBrowser_queueDraw(GlobalTextureBrowser ()); + } +} + +void TextureBrowser_createTreeViewTree() +{ + GtkCellRenderer* renderer; + g_TextureBrowser.m_treeViewTree = GTK_WIDGET(gtk_tree_view_new()); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTree), FALSE); + g_signal_connect(g_TextureBrowser.m_treeViewTree, "row-activated", (GCallback) TreeView_onRowActivated, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTree), -1, "", renderer, "text", 0, NULL); + + TextureBrowser_constructTreeStore(); +} + +void TextureBrowser_createTreeViewTags() +{ + GtkCellRenderer* renderer; + g_TextureBrowser.m_treeViewTags = GTK_WIDGET(gtk_tree_view_new()); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTags), FALSE); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTags), -1, "", renderer, "text", 0, NULL); + + TextureBrowser_constructTreeStoreTags(); +} + +GtkMenuItem* TextureBrowser_constructViewMenu(GtkMenu* menu) +{ + GtkMenuItem* textures_menu_item = new_sub_menu_item_with_mnemonic("_View"); + GtkWidget* separator = gtk_separator_menu_item_new(); + + if(g_Layout_enableDetachableMenus.m_value) + menu_tearoff (menu); + + create_check_menu_item_with_mnemonic(menu, "Hide _Unused", "ShowInUse"); + create_menu_item_with_mnemonic(menu, "Show All", "ShowAllTextures"); + + // we always want to show shaders but don't want a "Show Shaders" menu for doom3 games + if(string_equal(g_pGameDescription->getRequiredKeyValue("shaders"), "doom3")) + { + g_TextureBrowser.m_showShaders = true; + } + else + { + create_check_menu_item_with_mnemonic(menu, "Show shaders", "ToggleShowShaders"); + } + + create_check_menu_item_with_mnemonic (menu, "Shaders Only", "ToggleShowShaderlistOnly"); + if(g_TextureBrowser.m_tags) + { + create_menu_item_with_mnemonic(menu, "Show Untagged", "ShowUntagged"); + } + gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator); + gtk_widget_show(separator); + + g_TextureBrowser.m_shader_info_item = GTK_WIDGET(create_menu_item_with_mnemonic(menu, "Shader Info", "ShaderInfo")); + gtk_widget_set_sensitive(g_TextureBrowser.m_shader_info_item, FALSE); + + return textures_menu_item; +} + +GtkMenuItem* TextureBrowser_constructToolsMenu(GtkMenu* menu) +{ + GtkMenuItem* textures_menu_item = new_sub_menu_item_with_mnemonic("_Tools"); + + if (g_Layout_enableDetachableMenus.m_value) + menu_tearoff (menu); - g_TextureBrowser.m_filter = entry; - if(g_TextureBrowser.m_showTextureFilter) + create_menu_item_with_mnemonic(menu, "Flush & Reload Shaders", "RefreshShaders"); + create_menu_item_with_mnemonic(menu, "Find / Replace...", "FindReplaceTextures"); + + return textures_menu_item; +} + +GtkMenuItem* TextureBrowser_constructTagsMenu(GtkMenu* menu) +{ + GtkMenuItem* textures_menu_item = new_sub_menu_item_with_mnemonic("T_ags"); + GtkWidget* separator = gtk_separator_menu_item_new(); + + if (g_Layout_enableDetachableMenus.m_value) + menu_tearoff (menu); + + create_menu_item_with_mnemonic(menu, "Add tag", "AddTag"); + create_menu_item_with_mnemonic(menu, "Rename tag", "RenameTag"); + create_menu_item_with_mnemonic(menu, "Delete tag", "DeleteTag"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator); + gtk_widget_show(separator); + create_menu_item_with_mnemonic(menu, "Copy tags from selected", "CopyTag"); + create_menu_item_with_mnemonic(menu, "Paste tags to selected", "PasteTag"); + + return textures_menu_item; +} + +gboolean TextureBrowser_tagMoveHelper(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, GSList** selected) +{ + g_assert(selected != NULL); + + GtkTreeRowReference* rowref = gtk_tree_row_reference_new (model, path); + *selected = g_slist_append(*selected, rowref); + + return FALSE; +} + +void TextureBrowser_assignTags() +{ + GSList* selected = NULL; + GSList* node; + gchar* tag_assigned; + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_available_tree)); + + gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)TextureBrowser_tagMoveHelper, &selected); + + if(selected != NULL) + { + for (node = selected; node != NULL; node = node->next) + { + GtkTreePath* path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node->data); + + if(path) { - gtk_widget_show(GTK_WIDGET(g_TextureBrowser.m_filter)); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(g_TextureBrowser.m_available_store), &iter, path)) + { + gtk_tree_model_get(GTK_TREE_MODEL(g_TextureBrowser.m_available_store), &iter, TAG_COLUMN, &tag_assigned, -1); + if(!TagBuilder.CheckShaderTag(g_TextureBrowser.shader.c_str())) + { + // create a custom shader/texture entry + IShader* ishader = QERApp_Shader_ForName(g_TextureBrowser.shader.c_str()); + CopiedString filename = ishader->getShaderFileName(); + + if(filename.empty()) + { + // it's a texture + TagBuilder.AddShaderNode(g_TextureBrowser.shader.c_str(), CUSTOM, TEXTURE); + } else { + // it's a shader + TagBuilder.AddShaderNode(g_TextureBrowser.shader.c_str(), CUSTOM, SHADER); + } + ishader->DecRef(); + } + TagBuilder.AddShaderTag(g_TextureBrowser.shader.c_str(), (char*)tag_assigned, TAG); + + gtk_list_store_remove(g_TextureBrowser.m_available_store, &iter); + gtk_list_store_append (g_TextureBrowser.m_assigned_store, &iter); + gtk_list_store_set (g_TextureBrowser.m_assigned_store, &iter, TAG_COLUMN, (char*)tag_assigned, -1); + } } + } + + g_slist_foreach(selected, (GFunc)gtk_tree_row_reference_free, NULL); - g_TextureBrowser.m_filterEntry.connect(entry); + // Save changes + TagBuilder.SaveXmlDoc(); + } + g_slist_free(selected); +} + +void TextureBrowser_removeTags() +{ + GSList* selected = NULL; + GSList* node; + gchar* tag; + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_assigned_tree)); + + gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)TextureBrowser_tagMoveHelper, &selected); + + if(selected != NULL) + { + for (node = selected; node != NULL; node = node->next) + { + GtkTreePath* path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node->data); + + if (path) + { + GtkTreeIter iter; + + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(g_TextureBrowser.m_assigned_store), &iter, path)) + { + gtk_tree_model_get(GTK_TREE_MODEL(g_TextureBrowser.m_assigned_store), &iter, TAG_COLUMN, &tag, -1); + TagBuilder.DeleteShaderTag(g_TextureBrowser.shader.c_str(), tag); + gtk_list_store_remove(g_TextureBrowser.m_assigned_store, &iter); + } } + } - { - g_TextureBrowser.m_gl_widget = glwidget_new(FALSE); - gtk_widget_ref(g_TextureBrowser.m_gl_widget); + g_slist_foreach(selected, (GFunc)gtk_tree_row_reference_free, NULL); + + // Update the "available tags list" + BuildStoreAvailableTags(g_TextureBrowser.m_available_store, g_TextureBrowser.m_assigned_store, g_TextureBrowser.m_all_tags, &g_TextureBrowser); - gtk_widget_set_events(g_TextureBrowser.m_gl_widget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK); - GTK_WIDGET_SET_FLAGS(g_TextureBrowser.m_gl_widget, GTK_CAN_FOCUS); + // Save changes + TagBuilder.SaveXmlDoc(); + } + g_slist_free(selected); +} - gtk_box_pack_start(GTK_BOX(texbox), g_TextureBrowser.m_gl_widget, TRUE, TRUE, 0); - gtk_widget_show(g_TextureBrowser.m_gl_widget); +void TextureBrowser_buildTagList() +{ + GtkTreeIter treeIter; + gtk_list_store_clear(g_TextureBrowser.m_all_tags_list); - g_TextureBrowser.m_sizeHandler = g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "size_allocate", G_CALLBACK(TextureBrowser_size_allocate), &g_TextureBrowser); - g_TextureBrowser.m_exposeHandler = g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "expose_event", G_CALLBACK(TextureBrowser_expose), &g_TextureBrowser); + std::set::iterator iter; - g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "button_press_event", G_CALLBACK(TextureBrowser_button_press), &g_TextureBrowser); - g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "button_release_event", G_CALLBACK(TextureBrowser_button_release), &g_TextureBrowser); - g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "motion_notify_event", G_CALLBACK(TextureBrowser_motion), &g_TextureBrowser); - g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "scroll_event", G_CALLBACK(TextureBrowser_scroll), &g_TextureBrowser); + for (iter = g_TextureBrowser.m_all_tags.begin(); iter != g_TextureBrowser.m_all_tags.end(); ++iter) + { + gtk_list_store_append(g_TextureBrowser.m_all_tags_list, &treeIter); + gtk_list_store_set(g_TextureBrowser.m_all_tags_list, &treeIter, TAG_COLUMN, (*iter).c_str(), -1); + } +} + +void toggle_tags_textures() +{ + if(g_TextureBrowser.m_showTags) + { + gtk_widget_hide(GTK_WIDGET(g_TextureBrowser.m_scr_win_tags)); + gtk_widget_show(GTK_WIDGET(g_TextureBrowser.m_scr_win_tree)); + } else { + gtk_widget_hide(GTK_WIDGET(g_TextureBrowser.m_scr_win_tree)); + gtk_widget_show(GTK_WIDGET(g_TextureBrowser.m_scr_win_tags)); + } + g_TextureBrowser.m_showTags ^= 1; +} + +void TextureBrowser_searchTags() +{ + GSList* selected = NULL; + GSList* node; + gchar* tag; + char buffer[256]; + char tags_searched[256]; + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTags)); + + gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)TextureBrowser_tagMoveHelper, &selected); + + if(selected != NULL) + { + strcpy(buffer, "/root/*/*[tag='"); + strcpy(tags_searched, "[TAGS] "); + + for (node = selected; node != NULL; node = node->next) + { + GtkTreePath* path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node->data); + + if (path) + { + GtkTreeIter iter; + + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list), &iter, path)) + { + gtk_tree_model_get(GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list), &iter, TAG_COLUMN, &tag, -1); + + strcat(buffer, tag); + strcat(tags_searched, tag); + if(node != g_slist_last(node)) + { + strcat(buffer, "' and tag='"); + strcat(tags_searched, ", "); + } + } } } - TextureBrowser_updateScroll(g_TextureBrowser); - gtk_container_set_focus_chain(GTK_CONTAINER(hbox), NULL); + strcat(buffer, "']"); - return hbox; + g_slist_foreach(selected, (GFunc)gtk_tree_row_reference_free, NULL); + + g_TextureBrowser.m_found_shaders.clear(); // delete old list + TagBuilder.TagSearch(buffer, g_TextureBrowser.m_found_shaders); + + if(!g_TextureBrowser.m_found_shaders.empty()) // found something + { + size_t shaders_found = g_TextureBrowser.m_found_shaders.size(); + + globalOutputStream() << "Found " << shaders_found << " textures and shaders with " << tags_searched << "\n"; + ScopeDisableScreenUpdates disableScreenUpdates("Searching...", "Loading Textures"); + + std::set::iterator iter; + + for(iter = g_TextureBrowser.m_found_shaders.begin(); iter != g_TextureBrowser.m_found_shaders.end(); iter++) + { + std::string path = (*iter).c_str(); + size_t pos = path.find_last_of("/", path.size()); + std::string name = path.substr(pos + 1, path.size()); + path = path.substr(0, pos + 1); + TextureDirectory_loadTexture(path.c_str(), name.c_str()); + } + + g_TextureBrowser.m_searchedTags = true; + g_TextureBrowser_currentDirectory = tags_searched; + + g_TextureBrowser.m_nTotalHeight = 0; + TextureBrowser_setOriginY(g_TextureBrowser, 0); + TextureBrowser_heightChanged(g_TextureBrowser); + TextureBrowser_updateTitle(); + } + } + g_slist_free(selected); +} + +GtkWidget* TextureBrowser_constructTagToolbar() +{ + GtkWidget* toolbar = gtk_toolbar_new(); + GtkTooltips* toolbar_tips = gtk_tooltips_new(); + + GtkWidget* image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_SMALL_TOOLBAR); + GtkWidget* button = gtk_button_new(); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(TextureBrowser_searchTags), NULL); + gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), button, "Search with selected tags", "Search with selected tags"); + gtk_container_add(GTK_CONTAINER(button), image); + gtk_container_add(GTK_CONTAINER(toolbar), button); + gtk_widget_show_all(button); + + image = gtk_image_new_from_stock(GTK_STOCK_INDEX, GTK_ICON_SIZE_SMALL_TOOLBAR); + button = gtk_toggle_button_new(); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_tags_textures), NULL); + gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), button, "Toggle tag/texture view", "Toggle tag/texture view"); + gtk_container_add(GTK_CONTAINER(button), image); + gtk_container_add(GTK_CONTAINER(toolbar), button); + gtk_widget_show_all(button); + return toolbar; +} + + +void TextureBrowser_checkTagFile() +{ + const char SHADERTAG_FILE[] = "shadertags.xml"; + CopiedString filename; + StringOutputStream stream(256); + + stream << LocalRcPath_get(); + stream << SHADERTAG_FILE; + filename = stream.c_str(); + + if(file_exists(filename.c_str())) + { + g_TextureBrowser.m_tags = TagBuilder.OpenXmlDoc(filename.c_str()); + + if(g_TextureBrowser.m_tags) + { + globalOutputStream() << "Loading tag file " << filename.c_str() << ".\n"; + } + } + else + { + // default tagfile laden + stream.clear(); + stream << g_pGameDescription->mGameToolsPath.c_str(); + stream << SHADERTAG_FILE; + filename = stream.c_str(); + + globalErrorStream() << filename.c_str() << "\n"; + + if(file_exists(filename.c_str())) + { + g_TextureBrowser.m_tags = TagBuilder.OpenXmlDoc(filename.c_str()); + + if(g_TextureBrowser.m_tags) + { + globalOutputStream() << "Loading default tag file " << filename.c_str() << ".\n"; + } + } + else + { + globalErrorStream() << "Unable to find default tag file " << filename.c_str() << ". No tag support.\n"; + } + } +} + +GtkWidget* TextureBrowser_constructWindow(GtkWindow* toplevel) +{ + // The gl_widget and the tag assignment frame should be packed into a GtkVPaned with the slider + // position stored in local.pref. gtk_paned_get_position() and gtk_paned_set_position() don't + // seem to work in gtk 2.4 and the arrow buttons don't handle GTK_FILL, so here's another thing + // for the "once-the-gtk-libs-are-updated-TODO-list" :x + + TextureBrowser_checkTagFile(); + + if(g_TextureBrowser.m_tags) + { + g_TextureBrowser.m_all_tags_list = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING); + GtkTreeSortable* sortable = GTK_TREE_SORTABLE(g_TextureBrowser.m_all_tags_list); + gtk_tree_sortable_set_sort_column_id(sortable, TAG_COLUMN, GTK_SORT_ASCENDING); + + TagBuilder.GetAllTags(g_TextureBrowser.m_all_tags); + TextureBrowser_buildTagList(); + } + + GlobalShaderSystem().setActiveShadersChangedNotify(ReferenceCaller(g_TextureBrowser)); + + g_TextureBrowser.m_parent = toplevel; + + GtkWidget* table = gtk_table_new(3, 3, FALSE); + GtkWidget* frame_table = NULL; + GtkWidget* vbox = gtk_vbox_new(FALSE, 0); + gtk_table_attach(GTK_TABLE(table), vbox, 0, 1, 1, 3, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(vbox); + + { // menu bar + GtkWidget* menu_bar = gtk_menu_bar_new(); + GtkWidget* menu_view = gtk_menu_new(); + GtkWidget* view_item = (GtkWidget*)TextureBrowser_constructViewMenu(GTK_MENU(menu_view)); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(view_item), menu_view); + gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), view_item); + + GtkWidget* menu_tools = gtk_menu_new(); + GtkWidget* tools_item = (GtkWidget*)TextureBrowser_constructToolsMenu(GTK_MENU(menu_tools)); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(tools_item), menu_tools); + gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), tools_item); + + if(g_TextureBrowser.m_tags) + { + GtkWidget* menu_tags = gtk_menu_new(); + GtkWidget* tags_item = (GtkWidget*)TextureBrowser_constructTagsMenu(GTK_MENU(menu_tags)); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(tags_item), menu_tags); + gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), tags_item); + } + + gtk_table_attach(GTK_TABLE (table), menu_bar, 0, 3, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show(menu_bar); + } + { // tag tool bar + if(g_TextureBrowser.m_tags) + { + GtkWidget* toolbar = TextureBrowser_constructTagToolbar(); + + gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); + gtk_widget_show(toolbar); + } + } + { // gl_widget scrollbar + GtkWidget* w = gtk_vscrollbar_new(GTK_ADJUSTMENT(gtk_adjustment_new (0,0,0,1,1,1))); + gtk_table_attach(GTK_TABLE (table), w, 2, 3, 1, 2, GTK_SHRINK, GTK_FILL, 0, 0); + gtk_widget_show(w); + g_TextureBrowser.m_texture_scroll = w; + + GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_TextureBrowser.m_texture_scroll)); + g_signal_connect(G_OBJECT(vadjustment), "value_changed", G_CALLBACK(TextureBrowser_verticalScroll), &g_TextureBrowser); + + widget_set_visible(g_TextureBrowser.m_texture_scroll, g_TextureBrowser.m_showTextureScrollbar); + } + { // TreeView + g_TextureBrowser.m_scr_win_tree = gtk_scrolled_window_new(NULL, NULL); + gtk_container_set_border_width(GTK_CONTAINER(g_TextureBrowser.m_scr_win_tree), 0); + + // vertical only scrolling for treeview + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(g_TextureBrowser.m_scr_win_tree), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + + gtk_box_pack_end(GTK_BOX(vbox), g_TextureBrowser.m_scr_win_tree, TRUE, TRUE, 0); + gtk_widget_show(g_TextureBrowser.m_scr_win_tree); + + TextureBrowser_createTreeViewTree(); + + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(g_TextureBrowser.m_scr_win_tree), GTK_WIDGET(g_TextureBrowser.m_treeViewTree)); + gtk_widget_show(GTK_WIDGET(g_TextureBrowser.m_treeViewTree)); + } + { // TreeView for tags + if(g_TextureBrowser.m_tags) + { + g_TextureBrowser.m_scr_win_tags = gtk_scrolled_window_new(NULL, NULL); + gtk_container_set_border_width(GTK_CONTAINER(g_TextureBrowser.m_scr_win_tags), 0); + + // vertical only scrolling for treeview + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(g_TextureBrowser.m_scr_win_tags), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + + gtk_box_pack_end(GTK_BOX(vbox), g_TextureBrowser.m_scr_win_tags, TRUE, TRUE, 0); + + TextureBrowser_createTreeViewTags(); + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTags)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW (g_TextureBrowser.m_scr_win_tags), GTK_WIDGET (g_TextureBrowser.m_treeViewTags)); + gtk_widget_show(GTK_WIDGET(g_TextureBrowser.m_treeViewTags)); + } + } + { // gl_widget + g_TextureBrowser.m_gl_widget = glwidget_new(FALSE); + gtk_widget_ref(g_TextureBrowser.m_gl_widget); + + gtk_widget_set_events(g_TextureBrowser.m_gl_widget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK); + GTK_WIDGET_SET_FLAGS(g_TextureBrowser.m_gl_widget, GTK_CAN_FOCUS); + + gtk_table_attach_defaults(GTK_TABLE(table), g_TextureBrowser.m_gl_widget, 1, 2, 1, 2); + gtk_widget_show(g_TextureBrowser.m_gl_widget); + + g_TextureBrowser.m_sizeHandler = g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "size_allocate", G_CALLBACK(TextureBrowser_size_allocate), &g_TextureBrowser); + g_TextureBrowser.m_exposeHandler = g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "expose_event", G_CALLBACK(TextureBrowser_expose), &g_TextureBrowser); + + g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "button_press_event", G_CALLBACK(TextureBrowser_button_press), &g_TextureBrowser); + g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "button_release_event", G_CALLBACK(TextureBrowser_button_release), &g_TextureBrowser); + g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "motion_notify_event", G_CALLBACK(TextureBrowser_motion), &g_TextureBrowser); + g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "scroll_event", G_CALLBACK(TextureBrowser_scroll), &g_TextureBrowser); + } + { // tag frame + if(g_TextureBrowser.m_tags) + { + frame_table = gtk_table_new(3, 3, FALSE); + + g_TextureBrowser.m_tag_frame = gtk_frame_new("Tag assignment"); + gtk_frame_set_label_align(GTK_FRAME(g_TextureBrowser.m_tag_frame), 0.5, 0.5); + gtk_frame_set_shadow_type(GTK_FRAME(g_TextureBrowser.m_tag_frame), GTK_SHADOW_NONE); + + gtk_table_attach(GTK_TABLE(table), g_TextureBrowser.m_tag_frame, 1, 3, 2, 3, GTK_FILL, GTK_SHRINK, 0, 0); + + // set the size of the tag frame + gtk_widget_show(frame_table); + + gtk_container_add (GTK_CONTAINER(g_TextureBrowser.m_tag_frame), frame_table); + } + } + { // assigned tag list + if(g_TextureBrowser.m_tags) + { + GtkWidget* scrolled_win = gtk_scrolled_window_new(NULL, NULL); + gtk_container_set_border_width(GTK_CONTAINER (scrolled_win), 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + + g_TextureBrowser.m_assigned_store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING); + + GtkTreeSortable* sortable = GTK_TREE_SORTABLE(g_TextureBrowser.m_assigned_store); + gtk_tree_sortable_set_sort_column_id(sortable, TAG_COLUMN, GTK_SORT_ASCENDING); + + GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); + + g_TextureBrowser.m_assigned_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL (g_TextureBrowser.m_assigned_store)); + g_object_unref(G_OBJECT (g_TextureBrowser.m_assigned_store)); + g_signal_connect(g_TextureBrowser.m_assigned_tree, "row-activated", (GCallback) TextureBrowser_removeTags, NULL); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (g_TextureBrowser.m_assigned_tree), FALSE); + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_assigned_tree)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + + GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("", renderer, "text", TAG_COLUMN, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW (g_TextureBrowser.m_assigned_tree), column); + gtk_widget_show(g_TextureBrowser.m_assigned_tree); + + gtk_widget_show(scrolled_win); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW (scrolled_win), GTK_WIDGET (g_TextureBrowser.m_assigned_tree)); + + gtk_table_attach(GTK_TABLE(frame_table), scrolled_win, 0, 1, 1, 3, GTK_FILL, GTK_FILL, 0, 0); + } + } + { // available tag list + if(g_TextureBrowser.m_tags) + { + GtkWidget* scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + + g_TextureBrowser.m_available_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING); + GtkTreeSortable* sortable = GTK_TREE_SORTABLE(g_TextureBrowser.m_available_store); + gtk_tree_sortable_set_sort_column_id(sortable, TAG_COLUMN, GTK_SORT_ASCENDING); + + GtkCellRenderer* renderer = gtk_cell_renderer_text_new (); + + g_TextureBrowser.m_available_tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (g_TextureBrowser.m_available_store)); + g_object_unref (G_OBJECT (g_TextureBrowser.m_available_store)); + g_signal_connect(g_TextureBrowser.m_available_tree, "row-activated", (GCallback) TextureBrowser_assignTags, NULL); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (g_TextureBrowser.m_available_tree), FALSE); + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_available_tree)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); + + GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes ("", renderer, "text", TAG_COLUMN, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (g_TextureBrowser.m_available_tree), column); + gtk_widget_show (g_TextureBrowser.m_available_tree); + + gtk_widget_show (scrolled_win); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_win), GTK_WIDGET (g_TextureBrowser.m_available_tree)); + + gtk_table_attach (GTK_TABLE (frame_table), scrolled_win, 2, 3, 1, 3, GTK_FILL, GTK_FILL, 0, 0); + } + } + { // arrow buttons + if(g_TextureBrowser.m_tags) + { + GtkWidget* m_btn_left = gtk_button_new(); + GtkWidget* m_btn_right = gtk_button_new(); + GtkWidget* m_arrow_left = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_OUT); + GtkWidget* m_arrow_right = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_OUT); + gtk_container_add(GTK_CONTAINER(m_btn_left), m_arrow_left); + gtk_container_add(GTK_CONTAINER(m_btn_right), m_arrow_right); + + // workaround. the size of the tag frame depends of the requested size of the arrow buttons. + gtk_widget_set_size_request(m_arrow_left, -1, 68); + gtk_widget_set_size_request(m_arrow_right, -1, 68); + + gtk_table_attach(GTK_TABLE(frame_table), m_btn_left, 1, 2, 1, 2, GTK_SHRINK, GTK_EXPAND, 0, 0); + gtk_table_attach(GTK_TABLE(frame_table), m_btn_right, 1, 2, 2, 3, GTK_SHRINK, GTK_EXPAND, 0, 0); + + g_signal_connect(G_OBJECT (m_btn_left), "clicked", G_CALLBACK(TextureBrowser_assignTags), NULL); + g_signal_connect(G_OBJECT (m_btn_right), "clicked", G_CALLBACK(TextureBrowser_removeTags), NULL); + + gtk_widget_show(m_btn_left); + gtk_widget_show(m_btn_right); + gtk_widget_show(m_arrow_left); + gtk_widget_show(m_arrow_right); + } + } + { // tag frame labels + if(g_TextureBrowser.m_tags) + { + GtkWidget* m_lbl_assigned = gtk_label_new ("Assigned"); + GtkWidget* m_lbl_unassigned = gtk_label_new ("Available"); + + gtk_table_attach (GTK_TABLE (frame_table), m_lbl_assigned, 0, 1, 0, 1, GTK_EXPAND, GTK_SHRINK, 0, 0); + gtk_table_attach (GTK_TABLE (frame_table), m_lbl_unassigned, 2, 3, 0, 1, GTK_EXPAND, GTK_SHRINK, 0, 0); + + gtk_widget_show (m_lbl_assigned); + gtk_widget_show (m_lbl_unassigned); + } + } + + // TODO do we need this? + //gtk_container_set_focus_chain(GTK_CONTAINER(hbox_table), NULL); + + return table; } void TextureBrowser_destroyWindow() @@ -1556,29 +2161,251 @@ void TextureBrowser_setBackgroundColour(TextureBrowser& textureBrowser, const Ve TextureBrowser_queueDraw(textureBrowser); } +void TextureBrowser_selectionHelper(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, GSList** selected) +{ + g_assert(selected != NULL); + + gchar* name; + gtk_tree_model_get(model, iter, TAG_COLUMN, &name, -1); + *selected = g_slist_append(*selected, name); +} + +void TextureBrowser_shaderInfo() +{ + const char* name = TextureBrowser_GetSelectedShader(g_TextureBrowser); + IShader* shader = QERApp_Shader_ForName(name); + + DoShaderInfoDlg(name, shader->getShaderFileName(), "Shader Info"); + + shader->DecRef(); +} + +void TextureBrowser_addTag() +{ + CopiedString tag; + + EMessageBoxReturn result = DoShaderTagDlg(&tag, "Add shader tag"); + + if (result == eIDOK && !tag.empty()) + { + GtkTreeIter iter, iter2; + g_TextureBrowser.m_all_tags.insert(tag.c_str()); + gtk_list_store_append(g_TextureBrowser.m_available_store, &iter); + gtk_list_store_set(g_TextureBrowser.m_available_store, &iter, TAG_COLUMN, tag.c_str(), -1); + + // Select the currently added tag in the available list + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_available_tree)); + gtk_tree_selection_select_iter(selection, &iter); + + gtk_list_store_append(g_TextureBrowser.m_all_tags_list, &iter2); + gtk_list_store_set(g_TextureBrowser.m_all_tags_list, &iter2, TAG_COLUMN, tag.c_str(), -1); + } +} + +void TextureBrowser_renameTag() +{ + /* WORKAROUND: The tag treeview is set to GTK_SELECTION_MULTIPLE. Because + gtk_tree_selection_get_selected() doesn't work with GTK_SELECTION_MULTIPLE, + we need to count the number of selected rows first and use + gtk_tree_selection_selected_foreach() then to go through the list of selected + rows (which always containins a single row). + */ + + GSList* selected = NULL; + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTags)); + gtk_tree_selection_selected_foreach(selection, GtkTreeSelectionForeachFunc(TextureBrowser_selectionHelper), &selected); + + if(g_slist_length(selected) == 1) // we only rename a single tag + { + CopiedString newTag; + EMessageBoxReturn result = DoShaderTagDlg(&newTag, "Rename shader tag"); + + if (result == eIDOK && !newTag.empty()) + { + GtkTreeIter iterList; + gchar* rowTag; + gchar* oldTag = (char*)selected->data; + + bool row = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list), &iterList) != 0; + + while(row) + { + gtk_tree_model_get(GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list), &iterList, TAG_COLUMN, &rowTag, -1); + + if(strcmp(rowTag, oldTag) == 0) + { + gtk_list_store_set(g_TextureBrowser.m_all_tags_list, &iterList, TAG_COLUMN, newTag.c_str(), -1); + } + row = gtk_tree_model_iter_next(GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list), &iterList) != 0; + } + + TagBuilder.RenameShaderTag(oldTag, newTag.c_str()); + + g_TextureBrowser.m_all_tags.erase((CopiedString)oldTag); + g_TextureBrowser.m_all_tags.insert(newTag); + + BuildStoreAssignedTags(g_TextureBrowser.m_assigned_store, g_TextureBrowser.shader.c_str(), &g_TextureBrowser); + BuildStoreAvailableTags(g_TextureBrowser.m_available_store, g_TextureBrowser.m_assigned_store, g_TextureBrowser.m_all_tags, &g_TextureBrowser); + } + } + else + { + gtk_MessageBox(GTK_WIDGET(g_TextureBrowser.m_parent), "Select a single tag for renaming."); + } +} + +void TextureBrowser_deleteTag() +{ + GSList* selected = NULL; + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_TextureBrowser.m_treeViewTags)); + gtk_tree_selection_selected_foreach(selection, GtkTreeSelectionForeachFunc(TextureBrowser_selectionHelper), &selected); + + if(g_slist_length(selected) == 1) // we only delete a single tag + { + EMessageBoxReturn result = gtk_MessageBox(GTK_WIDGET(g_TextureBrowser.m_parent), "Are you sure you want to delete the selected tag?", "Delete Tag", eMB_YESNO, eMB_ICONQUESTION); + + if(result == eIDYES) + { + GtkTreeIter iterSelected; + gchar *rowTag; + + gchar* tagSelected = (char*)selected->data; + + bool row = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list), &iterSelected) != 0; + + while(row) + { + gtk_tree_model_get(GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list), &iterSelected, TAG_COLUMN, &rowTag, -1); + + if(strcmp(rowTag, tagSelected) == 0) + { + gtk_list_store_remove(g_TextureBrowser.m_all_tags_list, &iterSelected); + break; + } + row = gtk_tree_model_iter_next(GTK_TREE_MODEL(g_TextureBrowser.m_all_tags_list), &iterSelected) != 0; + } + + TagBuilder.DeleteTag(tagSelected); + g_TextureBrowser.m_all_tags.erase((CopiedString)tagSelected); + + BuildStoreAssignedTags(g_TextureBrowser.m_assigned_store, g_TextureBrowser.shader.c_str(), &g_TextureBrowser); + BuildStoreAvailableTags(g_TextureBrowser.m_available_store, g_TextureBrowser.m_assigned_store, g_TextureBrowser.m_all_tags, &g_TextureBrowser); + } + } else { + gtk_MessageBox(GTK_WIDGET(g_TextureBrowser.m_parent), "Select a single tag for deletion."); + } +} + +void TextureBrowser_copyTag() +{ + g_TextureBrowser.m_copied_tags.clear(); + TagBuilder.GetShaderTags(g_TextureBrowser.shader.c_str(), g_TextureBrowser.m_copied_tags); +} + +void TextureBrowser_pasteTag() +{ + IShader* ishader = QERApp_Shader_ForName(g_TextureBrowser.shader.c_str()); + CopiedString shader = g_TextureBrowser.shader.c_str(); + + if(!TagBuilder.CheckShaderTag(shader.c_str())) + { + CopiedString shaderFile = ishader->getShaderFileName(); + if(shaderFile.empty()) + { + // it's a texture + TagBuilder.AddShaderNode(shader.c_str(), CUSTOM, TEXTURE); + } + else + { + // it's a shader + TagBuilder.AddShaderNode(shader.c_str(), CUSTOM, SHADER); + } + + for(size_t i = 0; i < g_TextureBrowser.m_copied_tags.size(); ++i) + { + TagBuilder.AddShaderTag(shader.c_str(), g_TextureBrowser.m_copied_tags[i].c_str(), TAG); + } + } + else + { + for(size_t i = 0; i < g_TextureBrowser.m_copied_tags.size(); ++i) + { + if(!TagBuilder.CheckShaderTag(shader.c_str(), g_TextureBrowser.m_copied_tags[i].c_str())) + { + // the tag doesn't exist - let's add it + TagBuilder.AddShaderTag(shader.c_str(), g_TextureBrowser.m_copied_tags[i].c_str(), TAG); + } + } + } + + ishader->DecRef(); + + TagBuilder.SaveXmlDoc(); + BuildStoreAssignedTags(g_TextureBrowser.m_assigned_store, shader.c_str(), &g_TextureBrowser); + BuildStoreAvailableTags (g_TextureBrowser.m_available_store, g_TextureBrowser.m_assigned_store, g_TextureBrowser.m_all_tags, &g_TextureBrowser); +} + +void RefreshShaders() +{ + ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Shaders"); + GlobalShaderSystem().refresh(); + UpdateAllWindows(); +} void TextureBrowser_ToggleShowShaders() { g_TextureBrowser.m_showShaders ^= 1; - g_TexturesMenu.m_showshaders_item.update(); + g_TextureBrowser.m_showshaders_item.update(); TextureBrowser_queueDraw(g_TextureBrowser); } void TextureBrowser_ToggleShowShaderListOnly() { - g_TexturesMenu_shaderlistOnly ^= 1; - g_TexturesMenu.m_showshaderlistonly_item.update(); - TextureGroupsMenu_Destroy(); - TextureGroupsMenu_Construct(); + g_TextureBrowser_shaderlistOnly ^= 1; + g_TextureBrowser.m_showshaderlistonly_item.update(); + + TextureBrowser_constructTreeStore(); } void TextureBrowser_showAll() { g_TextureBrowser_currentDirectory = ""; + g_TextureBrowser.m_searchedTags = false; TextureBrowser_heightChanged(g_TextureBrowser); TextureBrowser_updateTitle(); } +void TextureBrowser_showUntagged() +{ + EMessageBoxReturn result = gtk_MessageBox(GTK_WIDGET(g_TextureBrowser.m_parent), "WARNING! This function might need a lot of memory and time. Are you sure you want to use it?", "Show Untagged", eMB_YESNO, eMB_ICONWARNING); + + if(result == eIDYES) + { + g_TextureBrowser.m_found_shaders.clear(); + TagBuilder.GetUntagged(g_TextureBrowser.m_found_shaders); + std::set::iterator iter; + + ScopeDisableScreenUpdates disableScreenUpdates("Searching untagged textures...", "Loading Textures"); + + for(iter = g_TextureBrowser.m_found_shaders.begin(); iter != g_TextureBrowser.m_found_shaders.end(); iter++) + { + std::string path = (*iter).c_str(); + size_t pos = path.find_last_of("/", path.size()); + std::string name = path.substr(pos + 1, path.size()); + path = path.substr(0, pos + 1); + TextureDirectory_loadTexture(path.c_str(), name.c_str()); + globalErrorStream() << path.c_str() << name.c_str() << "\n"; + } + + g_TextureBrowser_currentDirectory = "Untagged"; + TextureBrowser_queueDraw(GlobalTextureBrowser()); + TextureBrowser_heightChanged(g_TextureBrowser); + TextureBrowser_updateTitle(); + } +} + void TextureBrowser_exportTitle(const StringImportCallback& importer) { StringOutputStream buffer(64); @@ -1643,11 +2470,6 @@ typedef ReferenceCaller1(GlobalTextureBrowser().m_startupShaders), STRING_ARRAY_RANGE(startup_shaders)); } } @@ -1690,26 +2512,30 @@ void TextureClipboard_textureSelected(const char* shader); void TextureBrowser_Construct() { - GlobalToggles_insert("ShowInUse", FreeCaller(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_hideunused_item), Accelerator('U')); + GlobalCommands_insert("ShaderInfo", FreeCaller()); + GlobalCommands_insert("ShowUntagged", FreeCaller()); + GlobalCommands_insert("AddTag", FreeCaller()); + GlobalCommands_insert("RenameTag", FreeCaller()); + GlobalCommands_insert("DeleteTag", FreeCaller()); + GlobalCommands_insert("CopyTag", FreeCaller()); + GlobalCommands_insert("PasteTag", FreeCaller()); + GlobalCommands_insert("RefreshShaders", FreeCaller()); + GlobalToggles_insert("ShowInUse", FreeCaller(), ToggleItem::AddCallbackCaller(g_TextureBrowser.m_hideunused_item), Accelerator('U')); GlobalCommands_insert("ShowAllTextures", FreeCaller(), Accelerator('A', (GdkModifierType)GDK_CONTROL_MASK)); - GlobalCommands_insert("ViewTextures", FreeCaller(), Accelerator('T')); - GlobalToggles_insert("ToggleShowShaders", FreeCaller(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_showshaders_item)); - GlobalToggles_insert("ToggleShowShaderlistOnly", FreeCaller(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_showshaderlistonly_item)); + GlobalCommands_insert("ToggleTextures", FreeCaller(), Accelerator('T')); + GlobalToggles_insert("ToggleShowShaders", FreeCaller(), ToggleItem::AddCallbackCaller(g_TextureBrowser.m_showshaders_item)); + GlobalToggles_insert("ToggleShowShaderlistOnly", FreeCaller(), ToggleItem::AddCallbackCaller(g_TextureBrowser.m_showshaderlistonly_item)); GlobalPreferenceSystem().registerPreference("TextureScale", makeSizeStringImportCallback(TextureBrowserSetScaleCaller(g_TextureBrowser)), SizeExportStringCaller(g_TextureBrowser.m_textureScale) ); - GlobalPreferenceSystem().registerPreference("NewTextureWindowStuff", - makeBoolStringImportCallback(TextureBrowserImportShowFilterCaller(g_TextureBrowser)), - BoolExportStringCaller(GlobalTextureBrowser().m_showTextureFilter) - ); GlobalPreferenceSystem().registerPreference("TextureScrollbar", makeBoolStringImportCallback(TextureBrowserImportShowScrollbarCaller(g_TextureBrowser)), BoolExportStringCaller(GlobalTextureBrowser().m_showTextureScrollbar) ); GlobalPreferenceSystem().registerPreference("ShowShaders", BoolImportStringCaller(GlobalTextureBrowser().m_showShaders), BoolExportStringCaller(GlobalTextureBrowser().m_showShaders)); - GlobalPreferenceSystem().registerPreference("ShowShaderlistOnly", BoolImportStringCaller(g_TexturesMenu_shaderlistOnly), BoolExportStringCaller(g_TexturesMenu_shaderlistOnly)); + GlobalPreferenceSystem().registerPreference("ShowShaderlistOnly", BoolImportStringCaller(g_TextureBrowser_shaderlistOnly), BoolExportStringCaller(g_TextureBrowser_shaderlistOnly)); GlobalPreferenceSystem().registerPreference("LoadShaders", IntImportStringCaller(reinterpret_cast(GlobalTextureBrowser().m_startupShaders)), IntExportStringCaller(reinterpret_cast(GlobalTextureBrowser().m_startupShaders))); GlobalPreferenceSystem().registerPreference("WheelMouseInc", SizeImportStringCaller(GlobalTextureBrowser().m_mouseWheelScrollIncrement), SizeExportStringCaller(GlobalTextureBrowser().m_mouseWheelScrollIncrement)); GlobalPreferenceSystem().registerPreference("SI_Colors0", Vector3ImportStringCaller(GlobalTextureBrowser().color_textureback), Vector3ExportStringCaller(GlobalTextureBrowser().color_textureback)); @@ -1721,15 +2547,11 @@ void TextureBrowser_Construct() TextureBrowser_registerPreferencesPage(); GlobalShaderSystem().attach(g_ShadersObserver); - GlobalShaderSystem().attach(g_TextureGroupsMenu); - GlobalFileSystem().attach(g_TextureGroupsMenu); TextureBrowser_textureSelected = TextureClipboard_textureSelected; } void TextureBrowser_Destroy() { - GlobalFileSystem().detach(g_TextureGroupsMenu); - GlobalShaderSystem().detach(g_TextureGroupsMenu); GlobalShaderSystem().detach(g_ShadersObserver); Textures_setModeChangedNotify(Callback()); diff --git a/radiant/texwindow.h b/radiant/texwindow.h index dd871b37..c5dbf33c 100644 --- a/radiant/texwindow.h +++ b/radiant/texwindow.h @@ -25,23 +25,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "math/vector.h" #include "generic/callbackfwd.h" #include "signal/signalfwd.h" +#include "xml/xmltextags.h" -// textures menu - -typedef struct _GSList GSList; typedef struct _GtkWidget GtkWidget; -typedef struct _GtkMenu GtkMenu; -typedef struct _GtkMenuItem GtkMenuItem; - -extern GtkMenu* g_textures_menu; -extern GtkMenuItem* g_textures_menu_separator; -void TextureGroupsMenu_Construct(); -void TextureGroupsMenu_Destroy(); -void TextureGroupsMenu_ListItems(GSList*& items); -const char* TextureGroupsMenu_GetName(std::size_t menunum); - - -// texture browser class TextureBrowser; TextureBrowser& GlobalTextureBrowser(); diff --git a/setup/data/tools/bitmaps/console.bmp b/setup/data/tools/bitmaps/console.bmp new file mode 100644 index 00000000..c45c6866 Binary files /dev/null and b/setup/data/tools/bitmaps/console.bmp differ diff --git a/setup/data/tools/bitmaps/entities.bmp b/setup/data/tools/bitmaps/entities.bmp new file mode 100644 index 00000000..47e9c249 Binary files /dev/null and b/setup/data/tools/bitmaps/entities.bmp differ diff --git a/setup/data/tools/bitmaps/lightinspector.bmp b/setup/data/tools/bitmaps/lightinspector.bmp new file mode 100644 index 00000000..00b00a90 Binary files /dev/null and b/setup/data/tools/bitmaps/lightinspector.bmp differ diff --git a/setup/data/tools/bitmaps/texture_browser.bmp b/setup/data/tools/bitmaps/texture_browser.bmp new file mode 100644 index 00000000..40c0c2c5 Binary files /dev/null and b/setup/data/tools/bitmaps/texture_browser.bmp differ