- Added LocalLcPath to plugin interface
authornamespace <namespace>
Sat, 30 Sep 2006 15:56:36 +0000 (15:56 +0000)
committernamespace <namespace>
Sat, 30 Sep 2006 15:56:36 +0000 (15:56 +0000)
- 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: svn://svn.icculus.org/gtkradiant/GtkRadiant/trunk@107 8a3a26a2-13c4-0310-b231-cf6edde360e5

26 files changed:
CHANGES
GtkRadiant.sln
SConscript
TODO
contrib/prtview/portals.h
contrib/shaderplug/shaderplug.cpp [new file with mode: 0644]
contrib/shaderplug/shaderplug.def [new file with mode: 0644]
contrib/shaderplug/shaderplug.h [new file with mode: 0644]
contrib/shaderplug/shaderplug.vcproj [new file with mode: 0644]
contrib/sunplug/sunplug.vcproj
include/qerplugin.h
libs/libs.vcproj
libs/xml/xmltextags.cpp [new file with mode: 0644]
libs/xml/xmltextags.h [new file with mode: 0644]
radiant/gtkdlgs.cpp
radiant/gtkdlgs.h
radiant/mainframe.cpp
radiant/mainframe.h
radiant/patch.h
radiant/plugin.cpp
radiant/texwindow.cpp
radiant/texwindow.h
setup/data/tools/bitmaps/console.bmp [new file with mode: 0644]
setup/data/tools/bitmaps/entities.bmp [new file with mode: 0644]
setup/data/tools/bitmaps/lightinspector.bmp [new file with mode: 0644]
setup/data/tools/bitmaps/texture_browser.bmp [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 461b47d..de2083a 100644 (file)
--- 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
index 544534f..c923533 100644 (file)
@@ -5,6 +5,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "entityq3", "plugins\entity\
 EndProject\r
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GtkRadiant", "radiant\GtkRadiant.vcproj", "{8E70385C-223A-4DD1-9B99-28FF2331A2B5}"\r
        ProjectSection(ProjectDependencies) = postProject\r
+               {1C785349-866D-447D-8C55-8A51E5CA0E87} = {1C785349-866D-447D-8C55-8A51E5CA0E87}\r
                {68E2C6B6-96CA-4BBD-A485-FEE6F2E65407} = {68E2C6B6-96CA-4BBD-A485-FEE6F2E65407}\r
                {8845C5C1-4154-425F-8643-447FADC03449} = {8845C5C1-4154-425F-8643-447FADC03449}\r
                {8845C5C1-4154-425F-8643-447FADC03449} = {8845C5C1-4154-425F-8643-447FADC03449}\r
@@ -158,6 +159,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "brushexport", "contrib\brus
        ProjectSection(ProjectDependencies) = postProject\r
        EndProjectSection\r
 EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaderplug", "contrib\shaderplug\shaderplug.vcproj", "{46B36F0C-5E17-458E-AE6F-AECE52F66EDE}"\r
+       ProjectSection(ProjectDependencies) = postProject\r
+               {1C785349-866D-447D-8C55-8A51E5CA0E87} = {1C785349-866D-447D-8C55-8A51E5CA0E87}\r
+       EndProjectSection\r
+EndProject\r
 Global\r
        GlobalSection(SolutionConfiguration) = preSolution\r
                Debug = Debug\r
@@ -296,6 +302,10 @@ Global
                {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Debug.Build.0 = Debug|Win32\r
                {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.ActiveCfg = Release|Win32\r
                {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.Build.0 = Release|Win32\r
+               {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Debug.ActiveCfg = Debug|Win32\r
+               {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Debug.Build.0 = Debug|Win32\r
+               {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.ActiveCfg = Release|Win32\r
+               {46B36F0C-5E17-458E-AE6F-AECE52F66EDE}.Release.Build.0 = Release|Win32\r
        EndGlobalSection\r
        GlobalSection(ExtensibilityGlobals) = postSolution\r
        EndGlobalSection\r
index f33c75f..68a875e 100644 (file)
@@ -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 c32101b..4ebb4f4 100644 (file)
--- 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
index 5e61fe9..06f4af4 100644 (file)
@@ -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 (file)
index 0000000..facb382
--- /dev/null
@@ -0,0 +1,263 @@
+/*\r
+Copyright (C) 2006, Stefan Greven.\r
+All Rights Reserved.\r
+\r
+This file is part of GtkRadiant.\r
+\r
+GtkRadiant is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+GtkRadiant is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with GtkRadiant; if not, write to the Free Software\r
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
+*/\r
+\r
+#include "shaderplug.h"\r
+\r
+#include "debugging/debugging.h"\r
+\r
+#include <string>\r
+#include <vector>\r
+#include "string/string.h"\r
+#include "modulesystem/singletonmodule.h"\r
+#include "stream/stringstream.h"\r
+#include "os/file.h"\r
+\r
+#include <gtk/gtk.h>\r
+\r
+#include "iplugin.h"\r
+#include "qerplugin.h"\r
+#include "ifilesystem.h"\r
+#include "iarchive.h"\r
+#include "ishaders.h"\r
+#include "iscriplib.h"\r
+\r
+#include "generic/callback.h"\r
+\r
+namespace {\r
+const char SHADERTAG_FILE[] = "shadertags.xml";\r
+}\r
+\r
+class ShaderPlugPluginDependencies : public GlobalRadiantModuleRef,\r
+                                     public GlobalFileSystemModuleRef,\r
+                                     public GlobalShadersModuleRef\r
+{\r
+public:\r
+  ShaderPlugPluginDependencies() :\r
+      GlobalShadersModuleRef(GlobalRadiant().getRequiredGameDescriptionKeyValue("shaders"))\r
+  {\r
+  }\r
+};\r
+\r
+namespace Shaderplug\r
+{\r
+  GtkWindow* g_window;\r
+\r
+  std::vector<const char*> archives;\r
+  std::set<std::string> shaders;\r
+  std::set<std::string> textures;\r
+\r
+  XmlTagBuilder TagBuilder;\r
+  void CreateTagFile();\r
+\r
+  const char* init(void* hApp, void* pMainWidget)\r
+  {\r
+    g_window = GTK_WINDOW(pMainWidget);\r
+    return "";\r
+  }\r
+  const char* getName()\r
+  {\r
+    return "ShaderPlug";\r
+  }\r
+  const char* getCommandList()\r
+  {\r
+    return "About;Create tag file";\r
+  }\r
+  const char* getCommandTitleList()\r
+  {\r
+    return "";\r
+  }\r
+  void dispatch(const char* command, float* vMin, float* vMax, bool bSingleBrush)\r
+  {\r
+    if(string_equal(command, "About"))\r
+    {\r
+      GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window), "Shaderplug (1.0)\n\n"\r
+                                        "by Shaderman (shaderman@gmx.net)",\r
+                                        "About",\r
+                                        eMB_OK,\r
+                                        eMB_ICONDEFAULT);\r
+    }\r
+    if(string_equal(command, "Create tag file"))\r
+    {\r
+      CreateTagFile();\r
+    }\r
+  }\r
+\r
+  void loadArchiveFile(const char* filename)\r
+  {\r
+    archives.push_back(filename);\r
+  }\r
+\r
+  typedef FreeCaller1<const char*, loadArchiveFile> LoadArchiveFileCaller;\r
+\r
+  void LoadTextureFile(const char* filename)\r
+  {\r
+    std::string s_filename = filename;\r
+\r
+    char buffer[256];\r
+    strcpy(buffer, "textures/");\r
+\r
+    // append filename without trailing file extension (.tga or .jpg for example)\r
+    strncat(buffer, filename, s_filename.length() - 4);\r
+\r
+    std::set<std::string>::iterator iter;\r
+    iter = shaders.find(buffer);\r
+\r
+    // a shader with this name already exists\r
+    if(iter == shaders.end())\r
+    {\r
+      textures.insert(buffer);\r
+    }\r
+  }\r
+\r
+  typedef FreeCaller1<const char*, LoadTextureFile> LoadTextureFileCaller;\r
+\r
+  void GetTextures(char* extension)\r
+  {\r
+    GlobalFileSystem().forEachFile("textures/", extension, LoadTextureFileCaller(), 0);\r
+  }\r
+\r
+  void LoadShaderList(const char* filename)\r
+  {\r
+    if(string_equal_prefix(filename, "textures/"))\r
+    {\r
+      shaders.insert(filename);\r
+    }\r
+  }\r
+\r
+  typedef FreeCaller1<const char*, LoadShaderList> LoadShaderListCaller;\r
+\r
+  void GetAllShaders()\r
+  {\r
+    GlobalShaderSystem().foreachShaderName(LoadShaderListCaller());\r
+  }\r
+\r
+  void GetArchiveList()\r
+  {\r
+    GlobalFileSystem().forEachArchive(LoadArchiveFileCaller());\r
+    globalOutputStream() << "Shaderplug: " << (const Unsigned)Shaderplug::archives.size() << " archives found.\n";\r
+  }\r
+\r
+  void CreateTagFile()\r
+  {\r
+    const char* shader_type = GlobalRadiant().getGameDescriptionKeyValue("shaders");\r
+\r
+    GetAllShaders();\r
+    globalOutputStream() << "Shaderplug: " << (const Unsigned)shaders.size() << " shaders found.\n";\r
+\r
+    if(string_equal(shader_type, "quake3"))\r
+    {\r
+      GetTextures("jpg");\r
+      GetTextures("tga");\r
+      GetTextures("png");\r
+\r
+      globalOutputStream() << "Shaderplug: " << (const Unsigned)textures.size() << " textures found.\n";\r
+    }\r
+\r
+    if(shaders.size() || textures.size() != 0)\r
+    {\r
+      globalOutputStream() << "Shaderplug: Creating XML tag file.\n";\r
+\r
+      TagBuilder.CreateXmlDocument();\r
+\r
+      std::set<std::string>::reverse_iterator r_iter;\r
+\r
+      for(r_iter = textures.rbegin(); r_iter != textures.rend(); ++r_iter)\r
+      {\r
+        TagBuilder.AddShaderNode(const_cast<char*>((*r_iter).c_str()), STOCK, TEXTURE);\r
+      }\r
+\r
+      for(r_iter = shaders.rbegin(); r_iter != shaders.rend(); ++r_iter)\r
+      {\r
+        TagBuilder.AddShaderNode(const_cast<char*>((*r_iter).c_str()), STOCK, SHADER);\r
+      }\r
+\r
+      // Get the tag file        \r
+      StringOutputStream tagFileStream(256);\r
+         tagFileStream << GlobalRadiant().getLocalRcPath() << SHADERTAG_FILE;\r
+      char* tagFile = tagFileStream.c_str();\r
+\r
+      char message[256];\r
+      strcpy(message, "Tag file saved to\n");\r
+      strcat(message, tagFile);\r
+      strcat(message, "\nPlease restart Radiant now.\n");\r
+\r
+      if(file_exists(tagFile))\r
+      {\r
+        EMessageBoxReturn result = GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window),\r
+            "WARNING! A tag file already exists! Overwrite it?", "Overwrite tag file?",\r
+            eMB_NOYES,\r
+            eMB_ICONWARNING);\r
+\r
+        if(result == eIDYES)\r
+        {\r
+          TagBuilder.SaveXmlDoc(tagFile);\r
+          GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window), message, "INFO", eMB_OK, eMB_ICONASTERISK);\r
+        }\r
+      } else {\r
+        TagBuilder.SaveXmlDoc(tagFile);\r
+        GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window), message, "INFO", eMB_OK, eMB_ICONASTERISK);\r
+      }\r
+    } else {\r
+      GlobalRadiant().m_pfnMessageBox(GTK_WIDGET(g_window),\r
+          "No shaders or textures found. No XML tag file created!\n"\r
+          "",\r
+          "ERROR",\r
+          eMB_OK,\r
+          eMB_ICONERROR);\r
+    }\r
+  }\r
+} // namespace\r
+\r
+class ShaderPluginModule\r
+{\r
+  _QERPluginTable m_plugin;\r
+public:\r
+  typedef _QERPluginTable Type;\r
+  STRING_CONSTANT(Name, "ShaderPlug");\r
+\r
+  ShaderPluginModule()\r
+  {\r
+    m_plugin.m_pfnQERPlug_Init = &Shaderplug::init;\r
+    m_plugin.m_pfnQERPlug_GetName = &Shaderplug::getName;\r
+    m_plugin.m_pfnQERPlug_GetCommandList = &Shaderplug::getCommandList;\r
+    m_plugin.m_pfnQERPlug_GetCommandTitleList = &Shaderplug::getCommandTitleList;\r
+    m_plugin.m_pfnQERPlug_Dispatch = &Shaderplug::dispatch;\r
+  }\r
+  _QERPluginTable* getTable()\r
+  {\r
+    return &m_plugin;\r
+  }\r
+};\r
+\r
+typedef SingletonModule<ShaderPluginModule, ShaderPlugPluginDependencies> SingletonShaderPluginModule;\r
+\r
+SingletonShaderPluginModule g_ShaderPluginModule;\r
+\r
+extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer& server)\r
+{\r
+  initialiseModule(server);\r
+\r
+  g_ShaderPluginModule.selfRegister();\r
+}\r
+\r
+\r
+\r
+\r
diff --git a/contrib/shaderplug/shaderplug.def b/contrib/shaderplug/shaderplug.def
new file mode 100644 (file)
index 0000000..be71ca5
--- /dev/null
@@ -0,0 +1,7 @@
+; shaderplug.def : Declares the module parameters for the DLL.\r
+\r
+LIBRARY      "SHADERPLUG"\r
+\r
+EXPORTS\r
+    ; Explicit exports can go here\r
+       Radiant_RegisterModules @1\r
diff --git a/contrib/shaderplug/shaderplug.h b/contrib/shaderplug/shaderplug.h
new file mode 100644 (file)
index 0000000..4842cc2
--- /dev/null
@@ -0,0 +1,27 @@
+/*\r
+Copyright (C) 2006, Stefan Greven.\r
+All Rights Reserved.\r
+\r
+This file is part of GtkRadiant.\r
+\r
+GtkRadiant is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+GtkRadiant is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with GtkRadiant; if not, write to the Free Software\r
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
+*/\r
+\r
+#if !defined(INCLUDED_SHADERPLUG_H)\r
+#define INCLUDED_SHADERPLUG_H\r
+\r
+#include "xml/xmltextags.h"\r
+\r
+#endif\r
diff --git a/contrib/shaderplug/shaderplug.vcproj b/contrib/shaderplug/shaderplug.vcproj
new file mode 100644 (file)
index 0000000..1041b73
--- /dev/null
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="7.10"\r
+       Name="shaderplug"\r
+       ProjectGUID="{46B36F0C-5E17-458E-AE6F-AECE52F66EDE}"\r
+       Keyword="Win32Proj">\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"/>\r
+       </Platforms>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="Debug"\r
+                       IntermediateDirectory="Debug"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2">\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="../../include;../../libs;&quot;../../../STLPort-4.6/stlport&quot;;&quot;..\..\..\gtk2-2.4\include\glib-2.0&quot;;&quot;..\..\..\gtk2-2.4\include\gtk-2.0&quot;;&quot;..\..\..\gtk2-2.4\lib\glib-2.0\include&quot;;..\..\libs\generic;&quot;..\..\..\gtk2-2.4\include\pango-1.0&quot;;&quot;..\..\..\gtk2-2.4\lib\gtk-2.0\include&quot;;&quot;..\..\..\gtk2-2.4\include\atk-1.0&quot;;..\..\plugins\shaders;&quot;..\..\..\libxml2-2.6\include&quot;;&quot;..\..\..\iconv-1.9\include&quot;"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SHADERPLUG_EXPORTS"\r
+                               StringPooling="TRUE"\r
+                               MinimalRebuild="TRUE"\r
+                               ExceptionHandling="FALSE"\r
+                               BasicRuntimeChecks="0"\r
+                               RuntimeLibrary="3"\r
+                               BufferSecurityCheck="FALSE"\r
+                               ForceConformanceInForLoopScope="TRUE"\r
+                               UsePrecompiledHeader="0"\r
+                               BrowseInformation="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="TRUE"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4610;4510;4512;4505;4100;4127"/>\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"/>\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="gtk-win32-2.0.lib gobject-2.0.lib libxml2.lib glib-2.0.lib"\r
+                               OutputFile="$(OutDir)/shaderplug.dll"\r
+                               LinkIncremental="1"\r
+                               SuppressStartupBanner="TRUE"\r
+                               AdditionalLibraryDirectories="&quot;..\..\..\gtk2-2.4\lib&quot;;&quot;..\..\..\libxml2-2.6\lib&quot;"\r
+                               IgnoreDefaultLibraryNames="msvcprtd.lib"\r
+                               ModuleDefinitionFile="$(ProjectName).def"\r
+                               GenerateDebugInformation="TRUE"\r
+                               ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"\r
+                               SubSystem="2"\r
+                               ImportLibrary="$(OutDir)/shaderplug.lib"\r
+                               TargetMachine="1"/>\r
+                       <Tool\r
+                               Name="VCMIDLTool"/>\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                               CommandLine="copy &quot;$(TargetPath)&quot; &quot;$(SolutionDir)install\plugins&quot;"/>\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"/>\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"/>\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"/>\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"/>\r
+                       <Tool\r
+                               Name="VCManagedWrapperGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCAuxiliaryManagedWrapperGeneratorTool"/>\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="Release"\r
+                       IntermediateDirectory="Release"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       WholeProgramOptimization="TRUE">\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               GlobalOptimizations="TRUE"\r
+                               InlineFunctionExpansion="2"\r
+                               EnableIntrinsicFunctions="TRUE"\r
+                               FavorSizeOrSpeed="1"\r
+                               OptimizeForWindowsApplication="FALSE"\r
+                               AdditionalIncludeDirectories="../../include;..\..\libs;&quot;../../../STLPort-4.6/stlport&quot;;&quot;..\..\..\libxml2-2.6\include&quot;;&quot;..\..\..\iconv-1.9\include&quot;;&quot;..\..\..\gtk2-2.4\include\glib-2.0&quot;;&quot;..\..\..\gtk2-2.4\lib\glib-2.0\include&quot;;&quot;..\..\..\gtk2-2.4\include\gtk-2.0&quot;;&quot;..\..\..\gtk2-2.4\include\pango-1.0&quot;;&quot;..\..\..\gtk2-2.4\lib\gtk-2.0\include&quot;;&quot;..\..\..\gtk2-2.4\include\atk-1.0&quot;"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SHADERPLUG_EXPORTS"\r
+                               StringPooling="TRUE"\r
+                               ExceptionHandling="FALSE"\r
+                               RuntimeLibrary="2"\r
+                               BufferSecurityCheck="FALSE"\r
+                               ForceConformanceInForLoopScope="TRUE"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="4"\r
+                               Detect64BitPortabilityProblems="TRUE"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4610;4510;4512;4505;4100;4127"/>\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"/>\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="wsock32.lib gtk-win32-2.0.lib gobject-2.0.lib libxml2.lib"\r
+                               OutputFile="$(OutDir)/shaderplug.dll"\r
+                               LinkIncremental="1"\r
+                               SuppressStartupBanner="TRUE"\r
+                               AdditionalLibraryDirectories="&quot;..\..\..\gtk2-2.4\lib&quot;;&quot;..\..\..\libxml2-2.6\lib&quot;"\r
+                               IgnoreDefaultLibraryNames="msvcprt.lib"\r
+                               ModuleDefinitionFile="$(ProjectName).def"\r
+                               GenerateDebugInformation="TRUE"\r
+                               ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"\r
+                               SubSystem="2"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               ImportLibrary="$(OutDir)/shaderplug.lib"\r
+                               TargetMachine="1"\r
+                               FixedBaseAddress="0"/>\r
+                       <Tool\r
+                               Name="VCMIDLTool"/>\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                               CommandLine="copy &quot;$(TargetPath)&quot; &quot;$(SolutionDir)install\plugins&quot;"/>\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"/>\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"/>\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"/>\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"/>\r
+                       <Tool\r
+                               Name="VCManagedWrapperGeneratorTool"/>\r
+                       <Tool\r
+                               Name="VCAuxiliaryManagedWrapperGeneratorTool"/>\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="src"\r
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{22F2852C-8177-45D5-94D0-C5255C199F7F}">\r
+                       <File\r
+                               RelativePath=".\shaderplug.cpp">\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\shaderplug.h">\r
+                       </File>\r
+               </Filter>\r
+               <File\r
+                       RelativePath=".\shaderplug.def">\r
+               </File>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
index 2d6fce9..8cf2773 100644 (file)
@@ -31,7 +31,7 @@
                                BrowseInformation="0"\r
                                WarningLevel="3"\r
                                Detect64BitPortabilityProblems="TRUE"\r
-                               DebugInformationFormat="4"/>\r
+                               DebugInformationFormat="3"/>\r
                        <Tool\r
                                Name="VCCustomBuildTool"/>\r
                        <Tool\r
index 7f2f9ca..ea7be9e 100644 (file)
@@ -118,6 +118,7 @@ struct _QERFuncTable_1
   STRING_CONSTANT(Name, "radiant");
 
   const char* (*getEnginePath)();
+  const char* (*getLocalRcPath)();
   const char* (*getGameToolsPath)();
   const char* (*getAppPath)();
   const char* (*getSettingsPath)();
index 4820946..a3c5221 100644 (file)
@@ -32,7 +32,7 @@
                                BrowseInformation="0"
                                WarningLevel="3"
                                Detect64BitPortabilityProblems="TRUE"
-                               DebugInformationFormat="4"
+                               DebugInformationFormat="3"
                                DisableSpecificWarnings="4610;4510;4512;4505;4100;4127"/>
                        <Tool
                                Name="VCCustomBuildTool"/>
                                RelativePath=".\xml\xmlparser.h">
                        </File>
                        <File
+                               RelativePath=".\xml\xmltextags.cpp">
+                       </File>
+                       <File
+                               RelativePath=".\xml\xmltextags.h">
+                       </File>
+                       <File
                                RelativePath=".\xml\xmlwriter.cpp">
                        </File>
                        <File
diff --git a/libs/xml/xmltextags.cpp b/libs/xml/xmltextags.cpp
new file mode 100644 (file)
index 0000000..34b05d5
--- /dev/null
@@ -0,0 +1,593 @@
+/*\r
+Copyright (C) 2006, Stefan Greven.\r
+All Rights Reserved.\r
+\r
+This file is part of GtkRadiant.\r
+\r
+GtkRadiant is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+GtkRadiant is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with GtkRadiant; if not, write to the Free Software\r
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
+*/\r
+\r
+#include "xmltextags.h"\r
+\r
+#include <string>\r
+\r
+#include "qerplugin.h"\r
+#include "stream/stringstream.h"\r
+\r
+XmlTagBuilder::XmlTagBuilder()\r
+{\r
+}\r
+\r
+XmlTagBuilder::~XmlTagBuilder()\r
+{\r
+  // clean up\r
+  xmlFreeDoc(doc);\r
+  xmlXPathFreeContext(context);\r
+}\r
+\r
+bool XmlTagBuilder::CreateXmlDocument()\r
+{\r
+  /* Creates an XML file\r
+\r
+     returns TRUE if the file was created successfully or FALSE when failed\r
+  */\r
+\r
+  xmlTextWriterPtr writer;\r
+\r
+  writer = xmlNewTextWriterDoc(&doc, 0);\r
+\r
+  // begin a new UTF-8 formatted xml document\r
+  xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);\r
+\r
+  // create the root node with stock and custom elements\r
+  xmlTextWriterStartElement(writer, (xmlChar*)"root");\r
+  xmlTextWriterWriteString(writer, (xmlChar*)"\n  ");\r
+  xmlTextWriterStartElement(writer, (xmlChar*)"stock");\r
+  xmlTextWriterWriteString(writer, (xmlChar*)"\n  ");\r
+  xmlTextWriterEndElement(writer);\r
+  xmlTextWriterWriteString(writer, (xmlChar*)"\n  ");\r
+  xmlTextWriterStartElement(writer, (xmlChar*)"custom");\r
+  xmlTextWriterWriteString(writer, (xmlChar*)"\n  ");\r
+  xmlTextWriterEndElement(writer);\r
+  xmlTextWriterWriteString(writer, (xmlChar*)"\n");\r
+  xmlTextWriterEndElement(writer);\r
+\r
+  // end of the xml document\r
+  xmlTextWriterEndDocument(writer);\r
+  xmlFreeTextWriter(writer);\r
+\r
+  if(!doc)\r
+  {\r
+    return false;\r
+  } else {\r
+    context = xmlXPathNewContext(doc);\r
+       return true;\r
+  }\r
+}\r
+\r
+bool XmlTagBuilder::OpenXmlDoc(const char* file)\r
+{\r
+  /* Reads a XML document from a file\r
+\r
+     returns TRUE if the document was read successfully or FALSE when failed\r
+  */\r
+\r
+  filename = file;\r
+  doc = xmlParseFile(file);    // TODO error checking!\r
+\r
+  if(!doc)\r
+  {\r
+    return false;\r
+  } else {\r
+    context = xmlXPathNewContext(doc);\r
+       return true;\r
+  }\r
+}\r
+\r
+bool XmlTagBuilder::SaveXmlDoc(void)\r
+{\r
+       return SaveXmlDoc(filename.c_str());\r
+}\r
+\r
+bool XmlTagBuilder::SaveXmlDoc(const char* file)\r
+{\r
+  /* Writes the XML document\r
+\r
+     returns TRUE if the document was saved successfully or FALSE when saving failed\r
+  */\r
+  \r
+  xmlSaveNoEmptyTags = 1;\r
+\r
+  if(xmlSaveFile(file, doc) != -1)\r
+  {\r
+    return true;\r
+  }\r
+  return false;\r
+}\r
+\r
+bool XmlTagBuilder::AddShaderNode(const char* shader, TextureType textureType, NodeShaderType nodeShaderType)\r
+{\r
+  /* Adds a shader node\r
+\r
+     char* shader - the name of the shader or texture (without trailing .tga or something)\r
+\r
+     returns TRUE if the node was added successfully or FALSE when failed\r
+  */\r
+\r
+  xmlNodeSetPtr nodePtr = NULL;\r
+  xmlXPathObjectPtr xpathPtr = NULL;\r
+  \r
+  switch(textureType)\r
+  {\r
+    case STOCK:\r
+      xpathPtr = XpathEval("/root/stock");\r
+      break;\r
+    case CUSTOM:\r
+      xpathPtr = XpathEval("/root/custom");\r
+  };\r
+  \r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+    return false;\r
+\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    xmlNodePtr newnode, newtext;\r
+    xmlNodePtr nodeParent = nodePtr->nodeTab[0];\r
+\r
+    // create a new node and set the node attribute (shader path)\r
+    switch(nodeShaderType)\r
+    {\r
+      case SHADER:\r
+        newnode = xmlNewNode(NULL, (xmlChar*)"shader");\r
+        break;\r
+      case TEXTURE:\r
+        newnode = xmlNewNode(NULL, (xmlChar*)"texture");\r
+      };\r
+\r
+      newnode = xmlDocCopyNode(newnode, doc, 1);\r
+      xmlSetProp(newnode, (xmlChar*)"path", (xmlChar*)shader);\r
+      xmlNodeSetContent(newnode, (xmlChar*)"\n    ");\r
+\r
+      if(nodePtr->nodeTab[0]->children->next == NULL)   // there are no shaders yet\r
+      {\r
+        // add spaces\r
+        newtext = xmlNewText((xmlChar*)"  ");\r
+        xmlAddChild(nodeParent->children, newtext);\r
+\r
+        // add the new node\r
+        xmlAddNextSibling(nodeParent->children, newnode);\r
+\r
+        // append a new line\r
+        newtext = xmlNewText((xmlChar*)"\n  ");\r
+        xmlAddNextSibling(nodeParent->children->next, newtext);\r
+      } else {\r
+        // add the node\r
+        xmlAddNextSibling(nodeParent->children, newnode);\r
+\r
+        // append a new line and spaces\r
+        newtext = xmlNewText((xmlChar*)"\n    ");\r
+        xmlAddNextSibling(nodeParent->children->next, newtext);\r
+      }\r
+      \r
+      xmlXPathFreeObject(xpathPtr);\r
+      return true;\r
+  } else {\r
+    xmlXPathFreeObject(xpathPtr);\r
+    return false;\r
+  }\r
+}\r
+\r
+bool XmlTagBuilder::DeleteShaderNode(const char* shader)\r
+{\r
+  /* Deletes a shader node\r
+\r
+     char* shader - the name of the shader or texture (without trailing .tga or something)\r
+\r
+     returns TRUE if the node was deleted successfully or FALSE when failed\r
+  */\r
+\r
+  char buffer[256];\r
+  char* expression = GetTagsXpathExpression(buffer, shader, EMPTY);\r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  \r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+       nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return false;\r
+\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    xmlNodePtr ptrContent = nodePtr->nodeTab[0];\r
+    xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;\r
+\r
+    // delete the node\r
+    xmlUnlinkNode(ptrContent);\r
+    xmlFreeNode(ptrContent);\r
+\r
+    // delete leading whitespace node\r
+    xmlUnlinkNode(ptrWhitespace);\r
+    xmlFreeNode(ptrWhitespace);\r
+    xmlXPathFreeObject(xpathPtr);\r
+    return true;\r
+  }\r
+  xmlXPathFreeObject(xpathPtr);\r
+  return false;\r
+}\r
+\r
+bool XmlTagBuilder::CheckShaderTag(const char* shader)\r
+{\r
+  /* Checks whether there exists an entry for a shader/texture with at least one tag\r
+\r
+     char* shader  - the name of the shader or texture (without trailing .tga or something)\r
+\r
+     returns TRUE if the shader is already stored in the XML tag file and has at least one tag\r
+  */\r
+\r
+  // build the XPath expression to search for\r
+  char buffer[256];\r
+  strcpy(buffer, "/root/*/*[@path='");\r
+  strcat(buffer, shader);\r
+  strcat(buffer, "']");\r
+\r
+  char* expression = buffer;\r
+\r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return false;\r
+\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    xmlXPathFreeObject(xpathPtr);\r
+    return true;\r
+  } else {\r
+    xmlXPathFreeObject(xpathPtr);\r
+    return false;\r
+  }\r
+}\r
+\r
+bool XmlTagBuilder::CheckShaderTag(const char* shader, const char* content)\r
+{\r
+  /* Checks whether a tag with content already exists\r
+\r
+     char* shader  - the name of the shader or texture (without trailing .tga or something)\r
+     char* content - the node content (a tag name)\r
+\r
+     returns TRUE if the tag with content already exists or FALSE if not\r
+  */\r
+\r
+  // build the XPath expression to search for\r
+  // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";\r
+\r
+  char buffer[256];\r
+  strcpy(buffer, "/root/*/*[@path='");\r
+  strcat(buffer, shader);\r
+  strcat(buffer, "'][child::tag='");\r
+  strcat(buffer, content);\r
+  strcat(buffer, "']");\r
+\r
+  char* expression = buffer;\r
+\r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return false;\r
+\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    xmlXPathFreeObject(xpathPtr);\r
+    return true;\r
+  } else {\r
+    xmlXPathFreeObject(xpathPtr);\r
+    return false;\r
+  }\r
+}\r
+\r
+bool XmlTagBuilder::AddShaderTag(const char* shader, const char* content, NodeTagType nodeTagType)\r
+{\r
+  /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet\r
+\r
+     char* shader  - the name of the shader or texture (without trailing .tga or something)\r
+     char* content - the node content (a tag name)\r
+\r
+     returns TRUE if the node was added successfully or FALSE when failed\r
+  */\r
+\r
+  // build the XPath expression\r
+  char buffer[256];\r
+  char* expression = GetTagsXpathExpression(buffer, shader, EMPTY);\r
+  \r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return false;\r
+\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr)) // node was found\r
+  {\r
+    xmlNodePtr newnode = xmlNewNode(NULL, (xmlChar*)"tag");\r
+    xmlNodePtr nodeParent = nodePtr->nodeTab[0];\r
+    newnode = xmlDocCopyNode(newnode, doc, 1);\r
+    xmlNodeSetContent(newnode, (xmlChar*)content);\r
+\r
+    if(nodePtr->nodeTab[0]->children->next == NULL)    // shader node has NO children\r
+    {\r
+      // add spaces\r
+      xmlNodePtr newtext = xmlNewText((xmlChar*)"  ");\r
+      xmlAddChild(nodeParent->children, newtext);\r
+\r
+      // add new node\r
+      xmlAddNextSibling(nodeParent->children, newnode);\r
+\r
+      // append a new line + spaces\r
+      newtext = xmlNewText((xmlChar*)"\n    ");\r
+      xmlAddNextSibling(nodeParent->children->next, newtext);\r
+    } else { // shader node has children already - the new node will be the first sibling\r
+      xmlAddNextSibling(nodeParent->children, newnode);\r
+         xmlNodePtr newtext = xmlNewText((xmlChar*)"\n      ");\r
+      xmlAddNextSibling(nodeParent->children->next, newtext);\r
+    }\r
+       xmlXPathFreeObject(xpathPtr);\r
+    return true;\r
+  } else {\r
+    xmlXPathFreeObject(xpathPtr);\r
+    return false;\r
+  }\r
+}\r
+\r
+//int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)\r
+int XmlTagBuilder::RenameShaderTag(const char* oldtag, CopiedString newtag)\r
+{\r
+  /* Replaces tag node contents\r
+\r
+     char* oldtag - the <tag></tag> node content that sould be changed\r
+     char* newtag - the new <tag></tag> node content\r
+\r
+     returns the number of renamed shaders\r
+  */\r
+\r
+  int num = 0;\r
+\r
+  // build the XPath expression\r
+  char expression[256];\r
+  strcpy(expression, "/root/*/*[child::tag='");\r
+  strcat(expression, oldtag);\r
+  strcat(expression, "']/*");\r
+\r
+  xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*)expression, context);\r
+  if(!result)\r
+    return 0;\r
+  xmlNodeSetPtr nodePtr = result->nodesetval;\r
+\r
+  for(int i = 0; i < nodePtr->nodeNr; i++)\r
+  {\r
+    xmlNodePtr ptrContent = nodePtr->nodeTab[i];\r
+    char* content = (char*)xmlNodeGetContent(ptrContent);\r
+\r
+    if(strcmp(content, oldtag) == 0)   // found a node with old content?\r
+    {\r
+      xmlNodeSetContent(ptrContent, (xmlChar*)newtag.c_str());\r
+      num++;\r
+    }\r
+  }\r
+\r
+  SaveXmlDoc();\r
+  xmlXPathFreeObject(result);// CHANGED\r
+  return num;\r
+}\r
+\r
+bool XmlTagBuilder::DeleteShaderTag(const char* shader, const char* tag)\r
+{\r
+  /* Deletes a child node of a shader\r
+\r
+     char* shader  - the name of the shader or texture (without trailing .tga or something)\r
+     char* tag     - the tag being deleted\r
+\r
+     returns TRUE if the node was deleted successfully or FALSE when failed\r
+  */\r
+\r
+  char buffer[256];\r
+  char* expression = GetTagsXpathExpression(buffer, shader, TAG);\r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return false;\r
+\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    for(int i = 0; i < nodePtr->nodeNr; i++)\r
+    {\r
+      xmlNodePtr ptrContent = nodePtr->nodeTab[i];\r
+      char* content = (char*)(xmlChar*)xmlNodeGetContent(ptrContent);\r
+\r
+      if(strcmp(content, tag) == 0)    // find the node\r
+      {\r
+        xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;\r
+        // delete the node\r
+        xmlUnlinkNode(ptrContent);\r
+        xmlFreeNode(ptrContent);\r
+\r
+        // delete leading whitespace node\r
+        xmlUnlinkNode(ptrWhitespace);\r
+        xmlFreeNode(ptrWhitespace);\r
+        xmlXPathFreeObject(xpathPtr);\r
+        return true;\r
+      }\r
+    }\r
+  }\r
+  xmlXPathFreeObject(xpathPtr);\r
+  return false;\r
+}\r
+\r
+bool XmlTagBuilder::DeleteTag(const char* tag)\r
+{\r
+  /* Deletes a tag from all shaders\r
+\r
+     char* tag - the tag being deleted from all shaders\r
+\r
+     returns TRUE if the tag was deleted successfully or FALSE when failed\r
+  */\r
+\r
+  char expression[256];\r
+  strcpy(expression, "/root/*/*[child::tag='");\r
+  strcat(expression, tag);\r
+  strcat(expression, "']");\r
+\r
+  std::set<CopiedString> dellist;\r
+  TagSearch(expression, dellist);\r
+  std::set<CopiedString>::iterator iter;\r
+\r
+  for(iter = dellist.begin(); iter != dellist.end(); iter++)\r
+  {\r
+    DeleteShaderTag(iter->c_str(), tag);\r
+  }\r
+  SaveXmlDoc();\r
+\r
+  return true;\r
+}\r
+\r
+void XmlTagBuilder::GetShaderTags(const char* shader, std::vector<CopiedString>& tags)\r
+{\r
+  /* Gets the tags from a shader\r
+\r
+     char* shader - the name of the shader\r
+\r
+     returns a vector containing the tags\r
+  */\r
+\r
+  char* expression;\r
+\r
+  if(shader == NULL)   // get all tags from all shaders\r
+  {\r
+    expression = "/root/*/*/tag";\r
+  } else {\r
+    char buffer[256];\r
+    expression = GetTagsXpathExpression(buffer, shader, TAG);\r
+  }\r
+\r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return;\r
+\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    for(int i = 0; i < nodePtr->nodeNr; i++)\r
+    {\r
+      tags.push_back((CopiedString)(char*)xmlNodeGetContent(nodePtr->nodeTab[i]));\r
+    }\r
+  }\r
+  xmlXPathFreeObject(xpathPtr);\r
+}\r
+\r
+void XmlTagBuilder::GetUntagged(std::set<CopiedString>& shaders)\r
+{\r
+  /* Gets all textures and shaders listed in the xml file that don't have any tag\r
+\r
+     returns a set containing the shaders (with path)\r
+  */\r
+\r
+  char* expression = "/root/*/*[not(child::tag)]";\r
+\r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return;\r
\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    xmlNodePtr ptr;\r
+\r
+    for(int i = 0; i < nodePtr->nodeNr; i++)\r
+    {\r
+      ptr = nodePtr->nodeTab[i];\r
+         shaders.insert((char*)xmlGetProp(ptr, (xmlChar*)"path"));\r
+    }\r
+  }\r
+  \r
+  xmlXPathFreeObject(xpathPtr);\r
+}\r
+\r
+void XmlTagBuilder::GetAllTags(std::set<CopiedString>& tags)\r
+{\r
+  /* Gets a list of all tags that are used (assigned to any shader)\r
+\r
+     returns a set containing all used tags\r
+  */\r
+\r
+  char* expression = "/root/*/*/tag";\r
+\r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return;\r
\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    for(int i = 0; i < nodePtr->nodeNr; i++)\r
+    {\r
+      tags.insert((CopiedString)(char*)xmlNodeGetContent(nodePtr->nodeTab[i]));\r
+    }\r
+  }\r
+  \r
+  xmlXPathFreeObject(xpathPtr);\r
+}\r
+\r
+void XmlTagBuilder::TagSearch(const char* expression, std::set<CopiedString>& paths)\r
+{\r
+  /* Searches shaders by tags\r
+\r
+     char* expression - the XPath expression to search\r
+\r
+     returns a set containing the found shaders\r
+  */\r
+\r
+  xmlXPathObjectPtr xpathPtr = XpathEval(expression);\r
+  xmlNodeSetPtr nodePtr;\r
+  if(xpathPtr)\r
+    nodePtr = xpathPtr->nodesetval;\r
+  else\r
+       return;\r
+\r
+  if(!xmlXPathNodeSetIsEmpty(nodePtr))\r
+  {\r
+    xmlNodePtr ptr;\r
+    xmlChar* xmlattrib;\r
+    for(int i = 0; i < nodePtr->nodeNr; i++)\r
+    {\r
+      ptr = nodePtr->nodeTab[i];\r
+      xmlattrib = xmlGetProp(ptr, (xmlChar*)"path");\r
+         paths.insert((CopiedString)(char*)xmlattrib);\r
+    }\r
+  }\r
+  xmlXPathFreeObject(xpathPtr);\r
+}\r
+\r
diff --git a/libs/xml/xmltextags.h b/libs/xml/xmltextags.h
new file mode 100644 (file)
index 0000000..e58c1c1
--- /dev/null
@@ -0,0 +1,106 @@
+/*\r
+Copyright (C) 2006, Stefan Greven.\r
+All Rights Reserved.\r
+\r
+This file is part of GtkRadiant.\r
+\r
+GtkRadiant is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+GtkRadiant is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with GtkRadiant; if not, write to the Free Software\r
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
+*/\r
+\r
+#if !defined(INCLUDED_XMLTEXTAGS_H)\r
+#define INCLUDED_XMLTEXTAGS_H\r
+\r
+#include <set>\r
+#include <string/string.h>\r
+#include <vector>\r
+\r
+#include "iscriplib.h"\r
+\r
+#include <libxml/xpath.h>\r
+#include <libxml/xmlwriter.h>\r
+\r
+enum NodeTagType\r
+{\r
+  TAG,\r
+  EMPTY\r
+};\r
+\r
+enum NodeShaderType\r
+{\r
+  SHADER,\r
+  TEXTURE\r
+};\r
+\r
+enum TextureType\r
+{\r
+  STOCK,\r
+  CUSTOM\r
+};\r
+\r
+class XmlTagBuilder\r
+{\r
+private:\r
+  CopiedString filename;\r
+  xmlDocPtr doc;\r
+  xmlXPathContextPtr context;\r
+  xmlNodeSetPtr nodePtr;\r
+\r
+  xmlXPathObjectPtr XpathEval(const char* queryString)\r
+  {\r
+       xmlChar* expression = (xmlChar*)queryString;\r
+       xmlXPathObjectPtr result = xmlXPathEvalExpression(expression, context);\r
+       return result;\r
+  };\r
+\r
+  char* GetTagsXpathExpression(char* buffer, const char* shader, NodeTagType nodeTagType)\r
+  {\r
+       strcpy(buffer, "/root/*/*[@path='");\r
+       strcat(buffer, shader);\r
+       \r
+       switch(nodeTagType)\r
+       {\r
+       case TAG:\r
+               strcat(buffer, "']/tag");\r
+               break;\r
+       case EMPTY:\r
+               strcat(buffer, "']");\r
+       };\r
+\r
+       return buffer;\r
+  }\r
+\r
+public:\r
+  XmlTagBuilder();\r
+  ~XmlTagBuilder();\r
+\r
+  bool CreateXmlDocument();\r
+  bool OpenXmlDoc(const char* file);\r
+  bool SaveXmlDoc(const char* file);\r
+  bool SaveXmlDoc(void);\r
+  bool AddShaderNode(const char* shader, TextureType textureType, NodeShaderType nodeShaderType);\r
+  bool DeleteShaderNode(const char* shader);\r
+  bool CheckShaderTag(const char* shader);\r
+  bool CheckShaderTag(const char* shader, const char* content);\r
+  bool AddShaderTag(const char* shader, const char* content, NodeTagType nodeTagType);\r
+  bool DeleteTag(const char* tag);\r
+  int RenameShaderTag(const char* oldtag, CopiedString newtag);\r
+  bool DeleteShaderTag(const char* shader, const char* tag);\r
+  void GetShaderTags(const char* shader, std::vector<CopiedString>& tags);\r
+  void GetUntagged(std::set<CopiedString>& shaders);\r
+  void GetAllTags(std::set<CopiedString>& tags);\r
+  void TagSearch(const char* expression, std::set<CopiedString>& paths);\r
+};\r
+\r
+#endif\r
index 71dac52..1b1e5cc 100644 (file)
@@ -614,93 +614,6 @@ void DoAbout()
 }
 
 // =============================================================================
-// 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<const char*>(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 
 
 EMessageBoxReturn DoTextureLayout (float *fx, float *fy)
@@ -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 <gdk/gdkwin32.h>
index d14be79..3698991 100644 (file)
@@ -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();
index c7444c4..7c8a118 100644 (file)
@@ -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<CSG_Merge>(), Accelerator('U', (GdkModifierType)GDK_CONTROL_MASK));
   GlobalCommands_insert("CSGHollow", FreeCaller<CSG_MakeHollow>());
 
-  GlobalCommands_insert("TextureDirectoryList", FreeCaller<DoTextureListDlg>());
-
-  GlobalCommands_insert("RefreshShaders", FreeCaller<RefreshShaders>());
-
   Grid_registerCommands();
 
   GlobalCommands_insert("SnapToGrid", FreeCaller<Selection_SnapToGrid>(), Accelerator('G', (GdkModifierType)GDK_CONTROL_MASK));
index 53f3844..5997abd 100644 (file)
@@ -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
 
index d176387..ddbbffb 100644 (file)
@@ -116,7 +116,7 @@ struct BezierCurve
   Vector3 right;
 };
 
-const std::size_t BEZIERCURVETREE_MAX_INDEX = std::numeric_limits<unsigned int>::max() / 2 + 1;
+const std::size_t BEZIERCURVETREE_MAX_INDEX = std::size_t(1) << (std::numeric_limits<std::size_t>::digits - 1);
 
 struct BezierCurveTree
 {
index b4816b3..fad47ea 100644 (file)
@@ -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;
index 0447686..a036dbd 100644 (file)
@@ -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 <set>
+#include <string>
+#include <vector>
 
-#include <gtk/gtkmenuitem.h>
+#include <gtk/gtk.h>
 #include <gtk/gtkrange.h>
 #include <gtk/gtkframe.h>
 #include <gtk/gtkhbox.h>
 #include <gtk/gtkvbox.h>
 #include <gtk/gtkvscrollbar.h>
-#include <gtk/gtkmenu.h>
 
 #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<TextureMenuName> 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<char*>((*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<TextureGroupLoader> g_texture_group_loaders;
-
-void texturegroup_activated(GtkWidget* widget, gpointer data)
-{
-  reinterpret_cast<TextureGroupLoader*>(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<CopiedString> 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<TextureGroups, const char*, TextureGroups_addDirectory> 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<const BoolImportCallback&, TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport;
+
+void TextureBrowser_showShadersExport(const BoolImportCallback& importer);
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShadersExport> TextureBrowserShowShadersExport;
+
+void TextureBrowser_showShaderlistOnly(const BoolImportCallback& importer);
+typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShaderlistOnly> 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<CopiedString> m_all_tags;
+  GtkListStore* m_all_tags_list;
+  std::vector<CopiedString> m_copied_tags;
+  std::set<CopiedString> 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<TextureBrowser, &TextureBrowser::clearFilter> 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<CopiedString>::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<CopiedString>::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<TextureBrowser, bool, TextureBrowser_importShowScrollbar> 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<TextureBrowser, bool, TextureBrowser_importShowFilter> 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<const BoolImportCallback&, TextureBrowser_showShadersExport>
 
 void TextureBrowser_showShaderlistOnly(const BoolImportCallback& importer)
 {
-  importer(g_TexturesMenu_shaderlistOnly);
+  importer(g_TextureBrowser_shaderlistOnly);
 }
 typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShaderlistOnly> 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<CopiedString> 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<CopiedString>& allTags,
+                                                                                               TextureBrowser* textureBrowser)
+{
+  GtkTreeIter iterAssigned;
+  GtkTreeIter iterAvailable;
+  std::set<CopiedString>::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<int>(event->x), static_cast<int>(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<int>(event->x), static_cast<int>(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<TextureBrowser, TextureBrowser_activeShadersChanged>(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<CopiedString>::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<CopiedString>::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<CopiedString>::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<TextureBrowser, TextureBrowser_activeShadersChanged>(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<CopiedString>::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);
@@ -1644,11 +2471,6 @@ typedef ReferenceCaller1<TextureBrowser, const IntImportCallback&, TextureScaleE
 void TextureBrowser_constructPreferences(PreferencesPage& page)
 {
   page.appendCheckBox(
-    "", "Texture subsets",
-    TextureBrowserImportShowFilterCaller(GlobalTextureBrowser()),
-    BoolExportCaller(GlobalTextureBrowser().m_showTextureFilter)
-  );
-  page.appendCheckBox(
     "", "Texture scrollbar",
     TextureBrowserImportShowScrollbarCaller(GlobalTextureBrowser()),
     BoolExportCaller(GlobalTextureBrowser().m_showTextureScrollbar)
@@ -1664,7 +2486,7 @@ void TextureBrowser_constructPreferences(PreferencesPage& page)
   }
   page.appendEntry("Mousewheel Increment", GlobalTextureBrowser().m_mouseWheelScrollIncrement);
   {
-    const char* startup_shaders[] = { "None", TextureBrowser_getComonShadersName(), "All" };
+    const char* startup_shaders[] = { "None", TextureBrowser_getComonShadersName() };
     page.appendCombo("Load Shaders at Startup", reinterpret_cast<int&>(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<TextureBrowser_ToggleHideUnused>(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_hideunused_item), Accelerator('U'));
+  GlobalCommands_insert("ShaderInfo", FreeCaller<TextureBrowser_shaderInfo>());
+  GlobalCommands_insert("ShowUntagged", FreeCaller<TextureBrowser_showUntagged>());
+  GlobalCommands_insert("AddTag", FreeCaller<TextureBrowser_addTag>());
+  GlobalCommands_insert("RenameTag", FreeCaller<TextureBrowser_renameTag>());
+  GlobalCommands_insert("DeleteTag", FreeCaller<TextureBrowser_deleteTag>());
+  GlobalCommands_insert("CopyTag", FreeCaller<TextureBrowser_copyTag>());
+  GlobalCommands_insert("PasteTag", FreeCaller<TextureBrowser_pasteTag>());
+  GlobalCommands_insert("RefreshShaders", FreeCaller<RefreshShaders>());
+  GlobalToggles_insert("ShowInUse", FreeCaller<TextureBrowser_ToggleHideUnused>(), ToggleItem::AddCallbackCaller(g_TextureBrowser.m_hideunused_item), Accelerator('U'));
   GlobalCommands_insert("ShowAllTextures", FreeCaller<TextureBrowser_showAll>(), Accelerator('A', (GdkModifierType)GDK_CONTROL_MASK));
-  GlobalCommands_insert("ViewTextures", FreeCaller<TextureBrowser_toggleShown>(), Accelerator('T'));
-  GlobalToggles_insert("ToggleShowShaders", FreeCaller<TextureBrowser_ToggleShowShaders>(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_showshaders_item));
-  GlobalToggles_insert("ToggleShowShaderlistOnly", FreeCaller<TextureBrowser_ToggleShowShaderListOnly>(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_showshaderlistonly_item));
+  GlobalCommands_insert("ToggleTextures", FreeCaller<TextureBrowser_toggleShow>(), Accelerator('T'));
+  GlobalToggles_insert("ToggleShowShaders", FreeCaller<TextureBrowser_ToggleShowShaders>(), ToggleItem::AddCallbackCaller(g_TextureBrowser.m_showshaders_item));
+  GlobalToggles_insert("ToggleShowShaderlistOnly", FreeCaller<TextureBrowser_ToggleShowShaderListOnly>(), 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<int&>(GlobalTextureBrowser().m_startupShaders)), IntExportStringCaller(reinterpret_cast<int&>(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());
index dd871b3..c5dbf33 100644 (file)
@@ -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 (file)
index 0000000..c45c686
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 (file)
index 0000000..47e9c24
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 (file)
index 0000000..00b00a9
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 (file)
index 0000000..40c0c2c
Binary files /dev/null and b/setup/data/tools/bitmaps/texture_browser.bmp differ