]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/texwindow.cpp
fixed const inconsistencies
[xonotic/netradiant.git] / radiant / texwindow.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 //
23 // Texture Window
24 //
25 // Leonardo Zide (leo@lokigames.com)
26 //
27
28 #include "texwindow.h"
29
30 #include "debugging/debugging.h"
31 #include "warnings.h"
32
33 #include "iimage.h"
34 #include "ifilesystem.h"
35 #include "ishaders.h"
36 #include "iscriplib.h"
37 #include "iselection.h"
38 #include "iscenegraph.h"
39 #include "itextures.h"
40 #include "irender.h"
41 #include "iundo.h"
42 #include "igl.h"
43 #include "iarchive.h"
44 #include "moduleobserver.h"
45
46 #include <set>
47
48 #include <gtk/gtkmenuitem.h>
49 #include <gtk/gtkrange.h>
50 #include <gtk/gtkframe.h>
51 #include <gtk/gtkhbox.h>
52 #include <gtk/gtkvbox.h>
53 #include <gtk/gtkvscrollbar.h>
54 #include <gtk/gtkmenu.h>
55
56 #include "generic/callback.h"
57 #include "math/vector.h"
58 #include "texturelib.h"
59 #include "string/string.h"
60 #include "shaderlib.h"
61 #include "os/path.h"
62 #include "stream/memstream.h"
63 #include "stream/textfilestream.h"
64 #include "stream/stringstream.h"
65 #include "cmdlib.h"
66 #include "texmanip.h"
67 #include "textures.h"
68 #include "convert.h"
69
70 #include "gtkutil/menu.h"
71 #include "gtkutil/nonmodal.h"
72 #include "gtkutil/cursor.h"
73 #include "gtkutil/widget.h"
74 #include "gtkutil/glwidget.h"
75
76 #include "error.h"
77 #include "map.h"
78 #include "qgl.h"
79 #include "select.h"
80 #include "brush_primit.h"
81 #include "brushmanip.h"
82 #include "patchmanip.h"
83 #include "plugin.h"
84 #include "qe3.h"
85 #include "gtkdlgs.h"
86 #include "gtkmisc.h"
87 #include "mainframe.h"
88 #include "findtexturedialog.h"
89 #include "surfacedialog.h"
90 #include "patchdialog.h"
91 #include "groupdialog.h"
92 #include "preferences.h"
93 #include "shaders.h"
94 #include "commands.h"
95
96
97
98 bool TextureGroupsMenu_showWads()
99 {
100   return !string_empty(g_pGameDescription->getKeyValue("show_wads"));
101 }
102
103 // globals for textures
104 class TextureMenuName
105 {
106   enum { c_menuNameLength = 64 };
107   char m_name[c_menuNameLength];
108 public:
109   TextureMenuName(const char* name)
110   {
111     strncpy(m_name, name, c_menuNameLength - 1);
112     m_name[c_menuNameLength - 1] = '\0';
113   }
114   const char* c_str() const
115   {
116     return m_name;
117   }
118 };
119
120 typedef std::vector<TextureMenuName> TextureMenuNames;
121 TextureMenuNames texture_menunames;
122
123 const char* TextureGroupsMenu_GetName(std::size_t menunum)
124 {
125   return texture_menunames[menunum].c_str();
126 }
127
128 void TextureGroupsMenu_ListItems(GSList*& items)
129 {
130   for(TextureMenuNames::const_iterator i = texture_menunames.begin(); i != texture_menunames.end(); ++i)
131   {
132     items = g_slist_append(items, const_cast<char*>((*i).c_str()));
133   }
134 }
135
136 void TextureBrowser_queueDraw(TextureBrowser& textureBrower);
137
138 class TextureGroupLoader
139 {
140   std::size_t m_id;
141 public:
142   TextureGroupLoader(std::size_t id)
143     : m_id(id)
144   {
145   }
146   void loadGroup()
147   {
148     ScopeDisableScreenUpdates disableScreenUpdates(TextureGroupsMenu_GetName(m_id), "Loading Textures");
149
150     TextureBrowser_ShowDirectory(GlobalTextureBrowser(), TextureGroupsMenu_GetName(m_id));
151     TextureBrowser_queueDraw(GlobalTextureBrowser());
152   }
153 };
154
155 std::list<TextureGroupLoader> g_texture_group_loaders;
156
157 void texturegroup_activated(GtkWidget* widget, gpointer data)
158 {
159   reinterpret_cast<TextureGroupLoader*>(data)->loadGroup();
160 }
161
162 bool string_equal_start(const char* string, const char* start)
163 {
164   return string_equal_n(string, start, string_length(start));
165 }
166
167 GtkMenuItem* MenuItem_create(const char* name)
168 {
169   StringOutputStream buffer(64);
170   buffer << ConvertLocaleToUTF8(name);
171   return GTK_MENU_ITEM(gtk_menu_item_new_with_label(buffer.c_str()));
172 }
173
174 GtkMenuItem* Menu_addItem(GtkMenu* menu, const char* name)
175 {
176   GtkMenuItem* item = MenuItem_create(name);
177   gtk_widget_show(GTK_WIDGET(item));
178   menu_add_item(menu, item);
179   return item;
180 }
181
182 void TextureGroupsMenu_addItem(GtkMenu* menu, const char* dirName)
183 {
184   GtkMenuItem* item = Menu_addItem(menu, dirName);
185
186   g_texture_group_loaders.push_back(TextureGroupLoader(texture_menunames.size()));
187         g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(texturegroup_activated), &g_texture_group_loaders.back());
188
189   if(TextureGroupsMenu_showWads())
190   {
191     texture_menunames.push_back(dirName);
192   }
193   else
194   {
195     char buffer[1024];
196     strcpy(buffer, dirName);
197     strcat(buffer, "/");
198     texture_menunames.push_back(buffer);
199   }
200 }
201
202 typedef std::set<CopiedString> TextureGroups;
203
204 void TextureGroupsMenu_Construct(GtkMenu* menu, const TextureGroups& groups)
205 {
206   texture_menunames.clear();
207
208   TextureGroups::const_iterator i = groups.begin();
209   while(i != groups.end())
210   {
211     const char* dirName = (*i).c_str();
212     const char* firstUnderscore = strchr(dirName, '_');
213     CopiedString dirRoot(dirName, (firstUnderscore == 0) ? dirName : firstUnderscore + 1);
214
215     // do we shrink the menus?
216     // we shrink only if we have at least two things to shrink :-)
217     TextureGroups::const_iterator next = i;
218     ++next;
219     if(firstUnderscore != 0
220       && next != groups.end()
221       && string_equal_start((*next).c_str(), dirRoot.c_str()))
222     {
223       CopiedString itemName(dirName, firstUnderscore);
224             GtkMenuItem* item = Menu_addItem(menu, itemName.c_str());
225
226             GtkMenu *pSubMenu = GTK_MENU(gtk_menu_new());
227       gtk_menu_item_set_submenu(item, GTK_WIDGET(pSubMenu));
228
229             // keep going...
230             while(i != groups.end() && string_equal_start((*i).c_str(), dirRoot.c_str()))
231             {
232               TextureGroupsMenu_addItem(pSubMenu, (*i).c_str());
233
234               ++i;
235             }
236     }
237     else
238     {
239       TextureGroupsMenu_addItem(menu, dirName);
240
241       ++i;
242     }
243   }
244 }
245
246
247 void TextureGroups_addWad(TextureGroups& groups, const char* archive)
248 {
249   if(extension_equal(path_get_extension(archive), "wad"))
250   {
251 #if 1
252     groups.insert(archive);
253 #else
254     CopiedString archiveBaseName(path_get_filename_start(archive), path_get_filename_base_end(archive));
255     groups.insert(archiveBaseName);
256 #endif
257   }
258 }
259 typedef ReferenceCaller1<TextureGroups, const char*, TextureGroups_addWad> TextureGroupsAddWadCaller;
260
261 void TextureGroups_addShader(TextureGroups& groups, const char* shaderName)
262 {
263   const char* texture = path_make_relative(shaderName, "textures/");
264   if(texture != shaderName)
265   {
266     const char* last = path_remove_directory(texture);
267     if(!string_empty(last))
268     {
269       groups.insert(CopiedString(texture, --last));
270     }
271   }
272 }
273 typedef ReferenceCaller1<TextureGroups, const char*, TextureGroups_addShader> TextureGroupsAddShaderCaller;
274
275 void TextureGroups_addDirectory(TextureGroups& groups, const char* directory)
276 {
277   groups.insert(directory);
278 }
279 typedef ReferenceCaller1<TextureGroups, const char*, TextureGroups_addDirectory> TextureGroupsAddDirectoryCaller;
280
281 GtkMenu* g_textures_menu = 0;
282 GtkMenuItem* g_textures_menu_separator = 0;
283 namespace
284 {
285   bool g_TexturesMenu_shaderlistOnly = false;
286 }
287 void TextureGroupsMenu_Construct()
288 {
289   TextureGroups groups;
290
291   if(TextureGroupsMenu_showWads())
292   {
293     GlobalFileSystem().forEachArchive(TextureGroupsAddWadCaller(groups));
294   }
295   else
296   {
297     // scan texture dirs and pak files only if not restricting to shaderlist
298     if(g_pGameDescription->mGameType != "doom3" && !g_TexturesMenu_shaderlistOnly)
299     {
300       GlobalFileSystem().forEachDirectory("textures/", TextureGroupsAddDirectoryCaller(groups));
301     }
302
303     GlobalShaderSystem().foreachShaderName(TextureGroupsAddShaderCaller(groups));
304   }
305
306   TextureGroupsMenu_Construct(g_textures_menu, groups);
307 }
308
309 void TextureGroupsMenu_Destroy()
310 {
311   // delete everything
312   GtkMenu* menu = g_textures_menu;
313   GtkMenuItem* sep = g_textures_menu_separator;
314   GList* lst = g_list_find(gtk_container_children(GTK_CONTAINER(menu)), GTK_WIDGET(sep));
315   while(lst->next)
316   {
317     // these delete functions are recursive, it's gonna free all submenus
318     gtk_widget_destroy(GTK_WIDGET (lst->next->data));
319     // lst is no longer relevant, need to get it again
320     lst = g_list_find(gtk_container_children(GTK_CONTAINER(menu)), GTK_WIDGET(sep));
321   }
322 }
323
324
325 class TextureGroupsMenu : public ModuleObserver
326 {
327   std::size_t m_unrealised;
328 public:
329   TextureGroupsMenu() : m_unrealised(2)
330   {
331   }
332   void realise()
333   {
334     if(--m_unrealised == 0)
335     {
336       if(g_textures_menu != 0)
337       {
338         TextureGroupsMenu_Construct();
339       }
340     }
341   }
342   void unrealise()
343   {
344     if(++m_unrealised == 1)
345     {
346       if(g_textures_menu != 0)
347       {
348         TextureGroupsMenu_Destroy();
349       }
350     }
351   }
352 };
353
354 TextureGroupsMenu g_TextureGroupsMenu;
355
356 class DeferredAdjustment
357 {
358   gdouble m_value;
359   guint m_handler;
360   typedef void (*ValueChangedFunction)(void* data, gdouble value);
361   ValueChangedFunction m_function;
362   void* m_data;
363
364   static gboolean deferred_value_changed(gpointer data)
365   {
366     reinterpret_cast<DeferredAdjustment*>(data)->m_function(
367       reinterpret_cast<DeferredAdjustment*>(data)->m_data,
368       reinterpret_cast<DeferredAdjustment*>(data)->m_value
369     );
370     reinterpret_cast<DeferredAdjustment*>(data)->m_handler = 0;
371     reinterpret_cast<DeferredAdjustment*>(data)->m_value = 0;
372     return FALSE;
373   }
374 public:
375   DeferredAdjustment(ValueChangedFunction function, void* data) : m_value(0), m_handler(0), m_function(function), m_data(data)
376   {
377   }
378   void flush()
379   {
380     if(m_handler != 0)
381     {
382       g_source_remove(m_handler);
383       deferred_value_changed(this);
384     }
385   }
386   void value_changed(gdouble value)
387   {
388     m_value = value;
389     if(m_handler == 0)
390     {
391       m_handler = g_idle_add(deferred_value_changed, this);
392     }
393   }
394   static void adjustment_value_changed(GtkAdjustment *adjustment, DeferredAdjustment* self)
395   {
396     self->value_changed(adjustment->value);
397   }
398 };
399
400
401
402 class TextureBrowser;
403
404 typedef ReferenceCaller<TextureBrowser, TextureBrowser_queueDraw> TextureBrowserQueueDrawCaller;
405
406 void TextureBrowser_scrollChanged(void* data, gdouble value);
407
408
409 enum StartupShaders
410 {
411   STARTUPSHADERS_NONE = 0,
412   STARTUPSHADERS_COMMON,
413   STARTUPSHADERS_ALL,
414 };
415
416 class TextureBrowser
417 {
418 public:
419         int width, height;
420         int originy;
421         int m_nTotalHeight;
422
423   CopiedString shader;
424
425   GtkEntry* m_filter;
426   NonModalEntry m_filterEntry;
427
428   GtkWindow* m_parent;
429   GtkWidget* m_gl_widget;
430
431   guint m_sizeHandler;
432   guint m_exposeHandler;
433
434   GtkWidget* m_texture_scroll;
435
436   bool m_heightChanged;
437   bool m_originInvalid;
438
439   DeferredAdjustment m_scrollAdjustment;
440   FreezePointer m_freezePointer;
441
442   Vector3 color_textureback;
443   // the increment step we use against the wheel mouse
444   std::size_t m_mouseWheelScrollIncrement;
445   std::size_t m_textureScale;
446   bool m_showTextureFilter;
447   // make the texture increments match the grid changes
448   bool m_showShaders;
449   bool m_showTextureScrollbar;
450   StartupShaders m_startupShaders;
451   // if true, the texture window will only display in-use shaders
452   // if false, all the shaders in memory are displayed
453   bool m_hideUnused;
454
455
456   void clearFilter()
457   {
458     gtk_entry_set_text(m_filter, "");
459     TextureBrowser_queueDraw(*this);
460   }
461   typedef MemberCaller<TextureBrowser, &TextureBrowser::clearFilter> ClearFilterCaller;
462
463   TextureBrowser() :
464     m_filter(0),
465     m_filterEntry(TextureBrowserQueueDrawCaller(*this), ClearFilterCaller(*this)),
466     m_texture_scroll(0),
467     m_heightChanged(true),
468     m_originInvalid(true),
469     m_scrollAdjustment(TextureBrowser_scrollChanged, this),
470     color_textureback(0.25f, 0.25f, 0.25f),
471     m_mouseWheelScrollIncrement(64),
472     m_textureScale(50),
473     m_showTextureFilter(false),
474     m_showShaders(true),
475     m_showTextureScrollbar(true),
476     m_startupShaders(STARTUPSHADERS_NONE),
477     m_hideUnused(false)
478   {
479   }
480 };
481
482 void(*TextureBrowser_textureSelected)(const char* shader);
483
484
485 void TextureBrowser_updateScroll(TextureBrowser& textureBrowser);
486
487
488 const char* TextureBrowser_getComonShadersName()
489 {
490   const char* value = g_pGameDescription->getKeyValue("common_shaders_name");
491   if(!string_empty(value))
492   {
493     return value;
494   }
495   return "Common";
496 }
497
498 const char* TextureBrowser_getComonShadersDir()
499 {
500   const char* value = g_pGameDescription->getKeyValue("common_shaders_dir");
501   if(!string_empty(value))
502   {
503     return value;
504   }
505   return "common/";
506 }
507
508
509 void TextureBrowser_setShowFilter(TextureBrowser& textureBrowser, bool show)
510 {
511   widget_set_visible(GTK_WIDGET(textureBrowser.m_filter), show);
512 }
513
514 const char* TextureBrowser_getFilter(TextureBrowser& textureBrowser)
515 {
516   if(textureBrowser.m_showTextureFilter)
517   {
518     return gtk_entry_get_text(textureBrowser.m_filter);
519   }
520   return 0;
521 }
522
523 inline int TextureBrowser_fontHeight(TextureBrowser& textureBrowser)
524 {
525   return GlobalOpenGL().m_fontHeight;
526 }
527
528 const char* TextureBrowser_GetSelectedShader(TextureBrowser& textureBrowser)
529 {
530   return textureBrowser.shader.c_str();
531 }
532
533 void TextureBrowser_SetStatus(TextureBrowser& textureBrowser, const char* name)
534 {
535   IShader* shader = QERApp_Shader_ForName( name);
536   qtexture_t* q = shader->getTexture();
537   StringOutputStream strTex(256);
538   strTex << name << " W: " << Unsigned(q->width) << " H: " << Unsigned(q->height);
539   shader->DecRef();
540   g_pParentWnd->SetStatusText(g_pParentWnd->m_texture_status, strTex.c_str());
541 }
542
543 void TextureBrowser_Focus(TextureBrowser& textureBrowser, const char* name);
544
545 void TextureBrowser_SetSelectedShader(TextureBrowser& textureBrowser, const char* shader)
546 {
547   textureBrowser.shader = shader;
548   TextureBrowser_SetStatus(textureBrowser, shader);
549   TextureBrowser_Focus(textureBrowser, shader);
550
551   if(FindTextureDialog_isOpen())
552   {
553     FindTextureDialog_selectTexture(shader);
554   }
555 }
556
557
558 CopiedString g_TextureBrowser_currentDirectory;
559
560 /*
561 ============================================================================
562
563 TEXTURE LAYOUT
564
565 TTimo: now based on a rundown through all the shaders
566 NOTE: we expect the Active shaders count doesn't change during a Texture_StartPos .. Texture_NextPos cycle
567   otherwise we may need to rely on a list instead of an array storage
568 ============================================================================
569 */
570
571 class TextureLayout
572 {
573 public:
574   // texture layout functions
575   // TTimo: now based on shaders
576   int current_x, current_y, current_row;
577 };
578
579 void Texture_StartPos(TextureLayout& layout)
580 {
581   layout.current_x = 8;
582   layout.current_y = -8;
583   layout.current_row = 0;
584 }
585
586 void Texture_NextPos(TextureBrowser& textureBrowser, TextureLayout& layout, qtexture_t* current_texture, int *x, int *y)
587 {
588   qtexture_t* q = current_texture;
589
590   int nWidth = (int)(q->width * ((float)textureBrowser.m_textureScale / 100));
591   int nHeight = (int)(q->height * ((float)textureBrowser.m_textureScale / 100));
592   if (layout.current_x + nWidth > textureBrowser.width-8 && layout.current_row)
593   { // go to the next row unless the texture is the first on the row
594     layout.current_x = 8;
595     layout.current_y -= layout.current_row + TextureBrowser_fontHeight(textureBrowser) + 4;
596     layout.current_row = 0;
597   }
598
599   *x = layout.current_x;
600   *y = layout.current_y;
601
602   // Is our texture larger than the row? If so, grow the
603   // row height to match it
604
605   if (layout.current_row < nHeight)
606     layout.current_row = nHeight;
607
608   // never go less than 64, or the names get all crunched up
609   layout.current_x += nWidth < 64 ? 64 : nWidth;
610   layout.current_x += 8;
611 }
612
613 // if texture_showinuse jump over non in-use textures
614 bool Texture_IsShown(IShader* shader, bool show_shaders, bool hideUnused, const char* filter)
615 {
616   if(!shader_equal_prefix(shader->getName(), "textures/"))
617     return false;
618
619   if (!show_shaders && !shader->IsDefault())
620     return false;
621
622   if(hideUnused && !shader->IsInUse())
623     return false;
624
625   if(!string_empty(g_TextureBrowser_currentDirectory.c_str()))
626   {
627     if(!shader_equal_prefix(shader_get_textureName(shader->getName()), g_TextureBrowser_currentDirectory.c_str()))
628     {
629       return false;
630     }
631   }
632
633   if (filter != 0)
634   {
635     // some basic filtering
636     if (strstr( shader_get_textureName(shader->getName()), filter ) == 0)
637       return false;
638   }
639
640   return true;
641 }
642
643 void TextureBrowser_heightChanged(TextureBrowser& textureBrowser)
644 {
645   textureBrowser.m_heightChanged = true;
646
647   TextureBrowser_updateScroll(textureBrowser);
648   TextureBrowser_queueDraw(textureBrowser);
649 }
650
651 void TextureBrowser_evaluateHeight(TextureBrowser& textureBrowser)
652 {
653   if(textureBrowser.m_heightChanged)
654   {
655     textureBrowser.m_heightChanged = false;
656
657     textureBrowser.m_nTotalHeight = 0;
658
659     TextureLayout layout;
660     Texture_StartPos(layout);
661     for(QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement())
662     {
663       IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
664
665       if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused, TextureBrowser_getFilter(textureBrowser)))
666         continue;
667
668       int   x, y;
669       Texture_NextPos(textureBrowser, layout, shader->getTexture(), &x, &y);
670       textureBrowser.m_nTotalHeight = std::max(textureBrowser.m_nTotalHeight, abs(layout.current_y) + TextureBrowser_fontHeight(textureBrowser) + (int)(shader->getTexture()->height * ((float)textureBrowser.m_textureScale / 100)) + 4);
671     }
672   }
673 }
674
675 int TextureBrowser_TotalHeight(TextureBrowser& textureBrowser)
676 {
677   TextureBrowser_evaluateHeight(textureBrowser);
678   return textureBrowser.m_nTotalHeight;
679 }
680
681 inline const int& min_int(const int& left, const int& right)
682 {
683   return std::min(left, right);
684 }
685
686 void TextureBrowser_clampOriginY(TextureBrowser& textureBrowser)
687 {
688   if(textureBrowser.originy > 0)
689   {
690     textureBrowser.originy = 0;
691   }
692   int lower = min_int(textureBrowser.height - TextureBrowser_TotalHeight(textureBrowser), 0);
693   if(textureBrowser.originy < lower)
694   {
695     textureBrowser.originy = lower;
696   }
697 }
698
699 int TextureBrowser_getOriginY(TextureBrowser& textureBrowser)
700 {
701   if(textureBrowser.m_originInvalid)
702   {
703     textureBrowser.m_originInvalid = false;
704     TextureBrowser_clampOriginY(textureBrowser);
705     TextureBrowser_updateScroll(textureBrowser);
706   }
707   return textureBrowser.originy;
708 }
709
710 void TextureBrowser_setOriginY(TextureBrowser& textureBrowser, int originy)
711 {
712   textureBrowser.originy = originy;
713   TextureBrowser_clampOriginY(textureBrowser);
714   TextureBrowser_updateScroll(textureBrowser);
715   TextureBrowser_queueDraw(textureBrowser);
716 }
717
718
719 std::set<Callback> g_activeShadersChangedCallbacks;
720
721 void TextureBrowser_addActiveShadersChangedCallback(const Callback& callback)
722 {
723   g_activeShadersChangedCallbacks.insert(callback);
724 }
725
726 class ShadersObserver : public ModuleObserver
727 {
728   std::set<Callback> m_realiseCallbacks;
729 public:
730   void realise()
731   {
732     std::for_each(m_realiseCallbacks.begin(), m_realiseCallbacks.end(), CallbackInvoke());
733   }
734   void unrealise()
735   {
736   }
737   void insert(const Callback& callback)
738   {
739     m_realiseCallbacks.insert(callback);
740   }
741 };
742
743 namespace
744 {
745   ShadersObserver g_ShadersObserver;
746 }
747
748 void TextureBrowser_addShadersRealiseCallback(const Callback& callback)
749 {
750   g_ShadersObserver.insert(callback);
751 }
752
753 void TextureBrowser_activeShadersChanged(TextureBrowser& textureBrowser)
754 {
755   TextureBrowser_heightChanged(textureBrowser);
756   textureBrowser.m_originInvalid = true;
757
758   std::for_each(g_activeShadersChangedCallbacks.begin(), g_activeShadersChangedCallbacks.end(), CallbackInvoke());
759 }
760
761 void TextureBrowser_importShowScrollbar(TextureBrowser& textureBrowser, bool value)
762 {
763   textureBrowser.m_showTextureScrollbar = value;
764   if(textureBrowser.m_texture_scroll != 0)
765   {
766     widget_set_visible(textureBrowser.m_texture_scroll, textureBrowser.m_showTextureScrollbar);
767     TextureBrowser_updateScroll(textureBrowser);
768   }
769 }
770 typedef ReferenceCaller1<TextureBrowser, bool, TextureBrowser_importShowScrollbar> TextureBrowserImportShowScrollbarCaller;
771
772 void TextureBrowser_importShowFilter(TextureBrowser& textureBrowser, bool value)
773 {
774   textureBrowser.m_showTextureFilter = value;
775   if(textureBrowser.m_filter != 0)
776   {
777     TextureBrowser_setShowFilter(textureBrowser, textureBrowser.m_showTextureFilter);
778   }
779 }
780 typedef ReferenceCaller1<TextureBrowser, bool, TextureBrowser_importShowFilter> TextureBrowserImportShowFilterCaller;
781
782 /*
783 ==============
784 TextureBrowser_ShowDirectory
785 relies on texture_directory global for the directory to use
786 1) Load the shaders for the given directory
787 2) Scan the remaining texture, load them and assign them a default shader (the "noshader" shader)
788 NOTE: when writing a texture plugin, or some texture extensions, this function may need to be overriden, and made
789   available through the IShaders interface
790 NOTE: for texture window layout:
791   all shaders are stored with alphabetical order after load
792   previously loaded and displayed stuff is hidden, only in-use and newly loaded is shown
793   ( the GL textures are not flushed though)
794 ==============
795 */
796 bool texture_name_ignore(const char* name)
797 {
798   StringOutputStream strTemp(string_length(name));
799   strTemp << LowerCase(name);
800
801   return strstr(strTemp.c_str(), ".specular") != 0 ||
802     strstr(strTemp.c_str(), ".glow") != 0 ||
803     strstr(strTemp.c_str(), ".bump") != 0 ||
804     strstr(strTemp.c_str(), ".diffuse") != 0 ||
805     strstr(strTemp.c_str(), ".blend") != 0 ||
806           strstr(strTemp.c_str(), ".alpha") != 0;
807 }
808
809 class LoadShaderVisitor : public Archive::Visitor
810 {
811 public:
812   void visit(const char* name)
813   {
814     CopiedString shaderName(name, path_get_filename_base_end(name));
815     IShader* shader = QERApp_Shader_ForName(shaderName.c_str());
816     shader->DecRef();
817   }
818 };
819
820 void TextureBrowser_SetHideUnused(TextureBrowser& textureBrowser, bool hideUnused);
821
822 GtkWidget* g_page_textures;
823
824 void TextureBrowser_toggleShown() 
825 {
826   GroupDialog_showPage(g_page_textures);
827 }
828
829
830 void TextureBrowser_updateTitle()
831 {
832   GroupDialog_updatePageTitle(g_page_textures);
833 }
834
835
836
837 class TextureCategoryLoadShader
838 {
839   const char* m_directory;
840   std::size_t& m_count;
841 public:
842   typedef const char* first_argument_type;
843
844   TextureCategoryLoadShader(const char* directory, std::size_t& count)
845     : m_directory(directory), m_count(count)
846   {
847     m_count = 0;
848   }
849   void operator()(const char* name) const
850   {
851     if(shader_equal_prefix(name, "textures/")
852       && shader_equal_prefix(name + string_length("textures/"), m_directory))
853     {
854       ++m_count;
855       // request the shader, this will load the texture if needed
856       // this Shader_ForName call is a kind of hack
857       IShader *pFoo = QERApp_Shader_ForName(name);
858       pFoo->DecRef();
859     }
860   }
861 };
862
863 void TextureDirectory_loadTexture(const char* directory, const char* texture)
864 {
865   StringOutputStream name(256);
866   name << directory << StringRange(texture, path_get_filename_base_end(texture));
867
868   if(texture_name_ignore(name.c_str()))
869   {
870     return;
871   }
872
873   if (!texdef_name_valid(name.c_str()))
874   {
875     globalOutputStream() << "Skipping invalid texture name: [" << name.c_str() << "]\n";
876     return;
877   }
878
879   // if a texture is already in use to represent a shader, ignore it
880   IShader* shader = QERApp_Shader_ForName(name.c_str());
881   shader->DecRef();
882 }
883 typedef ConstPointerCaller1<char, const char*, TextureDirectory_loadTexture> TextureDirectoryLoadTextureCaller;
884
885 class LoadTexturesByTypeVisitor : public ImageModules::Visitor
886 {
887   const char* m_dirstring;
888 public:
889   LoadTexturesByTypeVisitor(const char* dirstring)
890     : m_dirstring(dirstring)
891   {
892   }
893   void visit(const char* minor, const _QERPlugImageTable& table)
894   {
895     GlobalFileSystem().forEachFile(m_dirstring, minor, TextureDirectoryLoadTextureCaller(m_dirstring));
896   }
897 };
898
899 void TextureBrowser_ShowDirectory(TextureBrowser& textureBrowser, const char* directory)
900 {
901   if(TextureGroupsMenu_showWads())
902   {
903     Archive* archive = GlobalFileSystem().getArchive(directory);
904     ASSERT_NOTNULL(archive);
905     LoadShaderVisitor visitor;
906     archive->forEachFile(Archive::VisitorFunc(visitor, Archive::eFiles, 0), "textures/");
907   }
908   else
909   {
910     g_TextureBrowser_currentDirectory = directory;
911     TextureBrowser_heightChanged(textureBrowser);
912
913     std::size_t shaders_count;
914     GlobalShaderSystem().foreachShaderName(makeCallback1(TextureCategoryLoadShader(directory, shaders_count)));
915     globalOutputStream() << "Showing " << Unsigned(shaders_count) << " shaders.\n";
916
917     if(g_pGameDescription->mGameType != "doom3")
918     {
919       // load remaining texture files
920
921       StringOutputStream dirstring(64);
922       dirstring << "textures/" << directory;
923
924       {
925         LoadTexturesByTypeVisitor visitor(dirstring.c_str());
926         Radiant_getImageModules().foreachModule(visitor);
927       }
928     }
929   }
930
931   // we'll display the newly loaded textures + all the ones already in use
932   TextureBrowser_SetHideUnused(textureBrowser, false);
933
934   TextureBrowser_updateTitle();
935 }
936
937
938 bool TextureBrowser_hideUnused();
939
940 void TextureBrowser_hideUnusedExport(const BoolImportCallback& importer)
941 {
942   importer(TextureBrowser_hideUnused());
943 }
944 typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_hideUnusedExport> TextureBrowserHideUnusedExport;
945
946 void TextureBrowser_showShadersExport(const BoolImportCallback& importer)
947 {
948   importer(GlobalTextureBrowser().m_showShaders);
949 }
950 typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShadersExport> TextureBrowserShowShadersExport;
951
952 void TextureBrowser_showShaderlistOnly(const BoolImportCallback& importer)
953 {
954   importer(g_TexturesMenu_shaderlistOnly);
955 }
956 typedef FreeCaller1<const BoolImportCallback&, TextureBrowser_showShaderlistOnly> TextureBrowserShowShaderlistOnlyExport;
957
958 class TexturesMenu
959 {
960 public:
961   ToggleItem m_hideunused_item;
962   ToggleItem m_showshaders_item;
963   ToggleItem m_showshaderlistonly_item;
964
965   TexturesMenu() :
966     m_hideunused_item(TextureBrowserHideUnusedExport()),
967     m_showshaders_item(TextureBrowserShowShadersExport()),
968     m_showshaderlistonly_item(TextureBrowserShowShaderlistOnlyExport())
969   {
970   }
971 };
972
973 TexturesMenu g_TexturesMenu;
974
975 void TextureBrowser_SetHideUnused(TextureBrowser& textureBrowser, bool hideUnused)
976 {
977   if(hideUnused)
978   {
979     textureBrowser.m_hideUnused = true;
980   }
981   else
982   {
983     textureBrowser.m_hideUnused = false;
984   }
985
986   g_TexturesMenu.m_hideunused_item.update();
987
988   TextureBrowser_heightChanged(textureBrowser);
989   textureBrowser.m_originInvalid = true;
990 }
991
992 void TextureBrowser_ShowStartupShaders(TextureBrowser& textureBrowser)
993 {
994   if(textureBrowser.m_startupShaders == STARTUPSHADERS_COMMON)
995   {
996     TextureBrowser_ShowDirectory(textureBrowser, TextureBrowser_getComonShadersDir());
997   }
998   else if(textureBrowser.m_startupShaders == STARTUPSHADERS_ALL)
999   {
1000     for(TextureMenuNames::const_iterator i = texture_menunames.begin(); i != texture_menunames.end(); ++i)
1001     {
1002       TextureBrowser_ShowDirectory(textureBrowser, (*i).c_str());
1003     }
1004   }
1005 }
1006
1007
1008 //++timo NOTE: this is a mix of Shader module stuff and texture explorer
1009 // it might need to be split in parts or moved out .. dunno
1010 // scroll origin so the specified texture is completely on screen
1011 // if current texture is not displayed, nothing is changed
1012 void TextureBrowser_Focus(TextureBrowser& textureBrowser, const char* name)
1013 {
1014   TextureLayout layout;
1015   // scroll origin so the texture is completely on screen
1016   Texture_StartPos(layout);
1017   
1018   for(QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement())
1019   {
1020     IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
1021
1022     if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused, TextureBrowser_getFilter(textureBrowser)))
1023       continue;
1024
1025     int x, y;
1026     Texture_NextPos(textureBrowser, layout, shader->getTexture(), &x, &y);
1027     qtexture_t* q = shader->getTexture();
1028     if (!q)
1029       break;
1030
1031     // we have found when texdef->name and the shader name match
1032     // NOTE: as everywhere else for our comparisons, we are not case sensitive
1033     if (shader_equal(name, shader->getName()))
1034     {
1035       int textureHeight = (int)(q->height * ((float)textureBrowser.m_textureScale / 100))
1036         + 2 * TextureBrowser_fontHeight(textureBrowser);
1037
1038       int originy = TextureBrowser_getOriginY(textureBrowser);
1039       if (y > originy)
1040       {
1041         originy = y;
1042       }
1043
1044       if (y - textureHeight < originy - textureBrowser.height)
1045       {
1046         originy = (y - textureHeight) + textureBrowser.height;
1047       }
1048
1049       TextureBrowser_setOriginY(textureBrowser, originy);
1050       return;
1051     }
1052   }
1053 }
1054
1055 IShader* Texture_At(TextureBrowser& textureBrowser, int mx, int my)
1056 {
1057   my += TextureBrowser_getOriginY(textureBrowser) - textureBrowser.height;
1058
1059   TextureLayout layout;
1060   Texture_StartPos(layout);
1061   for(QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement())
1062   {
1063     IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
1064
1065     if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused, TextureBrowser_getFilter(textureBrowser)))
1066       continue;
1067
1068     int   x, y;
1069     Texture_NextPos(textureBrowser, layout, shader->getTexture(), &x, &y);
1070     qtexture_t  *q = shader->getTexture();
1071     if (!q)
1072       break;
1073
1074     int nWidth = (int)(q->width * ((float)textureBrowser.m_textureScale / 100));
1075     int nHeight = (int)(q->height * ((float)textureBrowser.m_textureScale / 100));
1076     if (mx > x && mx - x < nWidth
1077       && my < y && y - my < nHeight + TextureBrowser_fontHeight(textureBrowser))
1078     {
1079       return shader;
1080     }
1081   }
1082
1083   return 0;
1084 }
1085
1086 /*
1087 ==============
1088 SelectTexture
1089
1090   By mouse click
1091 ==============
1092 */
1093 void SelectTexture(TextureBrowser& textureBrowser, int mx, int my, bool bShift)
1094 {
1095   IShader* shader = Texture_At(textureBrowser, mx, my);
1096   if(shader != 0)
1097   {
1098     if (bShift)
1099     {
1100       if (shader->IsDefault())
1101         globalOutputStream() << "ERROR: " << shader->getName() << " is not a shader, it's a texture.\n";
1102       else
1103         ViewShader( shader->getShaderFileName(), shader->getName() );
1104     }
1105     else
1106     {
1107       TextureBrowser_SetSelectedShader(textureBrowser, shader->getName());
1108       TextureBrowser_textureSelected(shader->getName());
1109
1110       if (!FindTextureDialog_isOpen())
1111       {
1112         UndoableCommand undo("textureNameSetSelected");
1113         Select_SetShader(shader->getName());
1114       }
1115     }
1116   }
1117 }
1118
1119 /*
1120 ============================================================================
1121
1122   MOUSE ACTIONS
1123
1124 ============================================================================
1125 */
1126
1127 void TextureBrowser_trackingDelta(int x, int y, unsigned int state, void* data)
1128 {
1129   TextureBrowser& textureBrowser = *reinterpret_cast<TextureBrowser*>(data);
1130   if(y != 0)
1131   {
1132     int scale = 1;
1133
1134     if(state & GDK_SHIFT_MASK)
1135       scale = 4;
1136
1137     int originy = TextureBrowser_getOriginY(textureBrowser);
1138     originy += y * scale;
1139     TextureBrowser_setOriginY(textureBrowser, originy);
1140   }
1141 }
1142
1143 void TextureBrowser_Tracking_MouseDown(TextureBrowser& textureBrowser)
1144 {
1145   textureBrowser.m_freezePointer.freeze_pointer(textureBrowser.m_parent, TextureBrowser_trackingDelta, &textureBrowser);
1146 }
1147
1148 void TextureBrowser_Tracking_MouseUp(TextureBrowser& textureBrowser)
1149 {
1150   textureBrowser.m_freezePointer.unfreeze_pointer(textureBrowser.m_parent);
1151 }
1152
1153 void TextureBrowser_Selection_MouseDown(TextureBrowser& textureBrowser, guint32 flags, int pointx, int pointy)
1154 {
1155   SelectTexture(textureBrowser, pointx, textureBrowser.height - 1 - pointy, (flags & GDK_SHIFT_MASK) != 0);
1156 }
1157
1158 /*
1159 ============================================================================
1160
1161 DRAWING
1162
1163 ============================================================================
1164 */
1165
1166 /*
1167 ============
1168 Texture_Draw
1169 TTimo: relying on the shaders list to display the textures
1170 we must query all qtexture_t* to manage and display through the IShaders interface
1171 this allows a plugin to completely override the texture system
1172 ============
1173 */
1174 void Texture_Draw(TextureBrowser& textureBrowser)
1175 {
1176   int originy = TextureBrowser_getOriginY(textureBrowser);
1177
1178   glClearColor(textureBrowser.color_textureback[0],
1179     textureBrowser.color_textureback[1],
1180     textureBrowser.color_textureback[2],
1181     0);
1182   glViewport(0, 0, textureBrowser.width, textureBrowser.height);
1183   glMatrixMode(GL_PROJECTION);
1184   glLoadIdentity();
1185
1186   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1187   glDisable (GL_DEPTH_TEST);
1188   glDisable(GL_BLEND);
1189   glOrtho (0, textureBrowser.width, originy-textureBrowser.height, originy, -100, 100);
1190   glEnable (GL_TEXTURE_2D);
1191
1192   glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1193
1194   int last_y = 0, last_height = 0;
1195
1196   TextureLayout layout;
1197   Texture_StartPos(layout);
1198   for(QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement())
1199   {
1200     IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
1201
1202     if(!Texture_IsShown(shader, textureBrowser.m_showShaders, textureBrowser.m_hideUnused, TextureBrowser_getFilter(textureBrowser)))
1203       continue;
1204
1205     int x, y;
1206     Texture_NextPos(textureBrowser, layout, shader->getTexture(), &x, &y);
1207     qtexture_t *q = shader->getTexture();
1208     if (!q)
1209       break;
1210
1211     int nWidth = (int)(q->width * ((float)textureBrowser.m_textureScale / 100));
1212     int nHeight = (int)(q->height * ((float)textureBrowser.m_textureScale / 100));
1213
1214     if (y != last_y)
1215     {
1216       last_y = y;
1217       last_height = 0;
1218     }
1219     last_height = std::max (nHeight, last_height);
1220
1221     // Is this texture visible?
1222     if ((y-nHeight-TextureBrowser_fontHeight(textureBrowser) < originy)
1223         && (y > originy - textureBrowser.height))
1224     {
1225       // borders rules:
1226       // if it's the current texture, draw a thick red line, else:
1227       // shaders have a white border, simple textures don't
1228       // if !texture_showinuse: (some textures displayed may not be in use)
1229       // draw an additional square around with 0.5 1 0.5 color
1230       if (shader_equal(TextureBrowser_GetSelectedShader(textureBrowser), shader->getName()))
1231       {
1232               glLineWidth (3);
1233               glColor3f (1,0,0);
1234               glDisable (GL_TEXTURE_2D);
1235
1236               glBegin (GL_LINE_LOOP);
1237               glVertex2i (x-4,y-TextureBrowser_fontHeight(textureBrowser)+4);
1238               glVertex2i (x-4,y-TextureBrowser_fontHeight(textureBrowser)-nHeight-4);
1239               glVertex2i (x+4+nWidth,y-TextureBrowser_fontHeight(textureBrowser)-nHeight-4);
1240               glVertex2i (x+4+nWidth,y-TextureBrowser_fontHeight(textureBrowser)+4);
1241               glEnd();
1242
1243               glEnable (GL_TEXTURE_2D);
1244               glLineWidth (1);
1245       }
1246       else
1247       {
1248               glLineWidth (1);
1249               // shader border:
1250               if (!shader->IsDefault())
1251               {
1252                 glColor3f (1,1,1);
1253                 glDisable (GL_TEXTURE_2D);
1254
1255                 glBegin (GL_LINE_LOOP);
1256                 glVertex2i (x-1,y+1-TextureBrowser_fontHeight(textureBrowser));
1257                 glVertex2i (x-1,y-nHeight-1-TextureBrowser_fontHeight(textureBrowser));
1258                 glVertex2i (x+1+nWidth,y-nHeight-1-TextureBrowser_fontHeight(textureBrowser));
1259                 glVertex2i (x+1+nWidth,y+1-TextureBrowser_fontHeight(textureBrowser));
1260                 glEnd();
1261                 glEnable (GL_TEXTURE_2D);
1262               }
1263
1264               // highlight in-use textures
1265               if (!textureBrowser.m_hideUnused && shader->IsInUse())
1266               {
1267                 glColor3f (0.5,1,0.5);
1268                 glDisable (GL_TEXTURE_2D);
1269                 glBegin (GL_LINE_LOOP);
1270                 glVertex2i (x-3,y+3-TextureBrowser_fontHeight(textureBrowser));
1271                 glVertex2i (x-3,y-nHeight-3-TextureBrowser_fontHeight(textureBrowser));
1272                 glVertex2i (x+3+nWidth,y-nHeight-3-TextureBrowser_fontHeight(textureBrowser));
1273                 glVertex2i (x+3+nWidth,y+3-TextureBrowser_fontHeight(textureBrowser));
1274                 glEnd();
1275                 glEnable (GL_TEXTURE_2D);
1276               }
1277       }
1278
1279       // Draw the texture
1280       glBindTexture (GL_TEXTURE_2D, q->texture_number);
1281       GlobalOpenGL_debugAssertNoErrors();
1282       glColor3f (1,1,1);
1283       glBegin (GL_QUADS);
1284       glTexCoord2i (0,0);
1285       glVertex2i (x,y-TextureBrowser_fontHeight(textureBrowser));
1286       glTexCoord2i (1,0);
1287       glVertex2i (x+nWidth,y-TextureBrowser_fontHeight(textureBrowser));
1288       glTexCoord2i (1,1);
1289       glVertex2i (x+nWidth,y-TextureBrowser_fontHeight(textureBrowser)-nHeight);
1290       glTexCoord2i (0,1);
1291       glVertex2i (x,y-TextureBrowser_fontHeight(textureBrowser)-nHeight);
1292       glEnd();
1293
1294       // draw the texture name
1295       glDisable (GL_TEXTURE_2D);
1296       glColor3f (1,1,1);
1297
1298       glRasterPos2i (x, y-TextureBrowser_fontHeight(textureBrowser)+2);
1299
1300       // don't draw the directory name
1301       const char* name = shader->getName();
1302       name += strlen(name);
1303       while(name != shader->getName() && *(name-1) != '/' && *(name-1) != '\\')
1304         name--;
1305
1306       GlobalOpenGL().drawString(name);
1307       glEnable (GL_TEXTURE_2D);
1308     }
1309
1310     //int totalHeight = abs(y) + last_height + TextureBrowser_fontHeight(textureBrowser) + 4;
1311   }
1312
1313
1314   // reset the current texture
1315   glBindTexture(GL_TEXTURE_2D, 0);
1316   //qglFinish();
1317 }
1318
1319 void TextureBrowser_queueDraw(TextureBrowser& textureBrowser)
1320 {
1321   if(textureBrowser.m_gl_widget != 0)
1322   {
1323     gtk_widget_queue_draw(textureBrowser.m_gl_widget);
1324   }
1325 }
1326
1327
1328 void TextureBrowser_setScale(TextureBrowser& textureBrowser, std::size_t scale)
1329 {
1330   textureBrowser.m_textureScale = scale;
1331
1332   TextureBrowser_queueDraw(textureBrowser);
1333 }
1334
1335
1336 void TextureBrowser_MouseWheel(TextureBrowser& textureBrowser, bool bUp)
1337 {
1338   int originy = TextureBrowser_getOriginY(textureBrowser);
1339
1340   if (bUp)
1341   {
1342     originy += int(textureBrowser.m_mouseWheelScrollIncrement);
1343   }
1344   else
1345   {
1346     originy -= int(textureBrowser.m_mouseWheelScrollIncrement);
1347   }
1348
1349   TextureBrowser_setOriginY(textureBrowser, originy);
1350 }
1351
1352
1353
1354 gboolean TextureBrowser_button_press(GtkWidget* widget, GdkEventButton* event, TextureBrowser* textureBrowser)
1355 {
1356   if(event->type == GDK_BUTTON_PRESS)
1357   {
1358     if(event->button == 3)
1359     {
1360       TextureBrowser_Tracking_MouseDown(*textureBrowser);
1361     }
1362     else if(event->button == 1)
1363     {
1364       TextureBrowser_Selection_MouseDown(*textureBrowser, event->state, static_cast<int>(event->x), static_cast<int>(event->y));
1365     }
1366   }
1367   return FALSE;
1368 }
1369
1370 gboolean TextureBrowser_button_release(GtkWidget* widget, GdkEventButton* event, TextureBrowser* textureBrowser)
1371 {
1372   if(event->type == GDK_BUTTON_RELEASE)
1373   {
1374     if(event->button == 3)
1375     {
1376       TextureBrowser_Tracking_MouseUp(*textureBrowser);
1377     }
1378   }
1379   return FALSE;
1380 }
1381
1382 gboolean TextureBrowser_motion(GtkWidget *widget, GdkEventMotion *event, TextureBrowser* textureBrowser)
1383 {
1384   return FALSE;
1385 }
1386
1387 gboolean TextureBrowser_scroll(GtkWidget* widget, GdkEventScroll* event, TextureBrowser* textureBrowser)
1388 {
1389   if(event->direction == GDK_SCROLL_UP)
1390   {
1391     TextureBrowser_MouseWheel(*textureBrowser, true);
1392   }
1393   else if(event->direction == GDK_SCROLL_DOWN)
1394   {
1395     TextureBrowser_MouseWheel(*textureBrowser, false);
1396   }
1397   return FALSE;
1398 }
1399
1400 void TextureBrowser_scrollChanged(void* data, gdouble value)
1401 {
1402   //globalOutputStream() << "vertical scroll\n";
1403   TextureBrowser_setOriginY(*reinterpret_cast<TextureBrowser*>(data), -(int)value);
1404 }
1405
1406 static void TextureBrowser_verticalScroll(GtkAdjustment *adjustment, TextureBrowser* textureBrowser)
1407 {
1408   textureBrowser->m_scrollAdjustment.value_changed(adjustment->value);
1409 }
1410
1411 void TextureBrowser_updateScroll(TextureBrowser& textureBrowser)
1412 {
1413   if(textureBrowser.m_showTextureScrollbar)
1414   {
1415     int totalHeight = TextureBrowser_TotalHeight(textureBrowser);
1416
1417     totalHeight = std::max(totalHeight, textureBrowser.height);
1418
1419     GtkAdjustment *vadjustment = gtk_range_get_adjustment(GTK_RANGE(textureBrowser.m_texture_scroll));
1420
1421     vadjustment->value = -TextureBrowser_getOriginY(textureBrowser);
1422     vadjustment->page_size = textureBrowser.height;
1423     vadjustment->page_increment = textureBrowser.height/2;
1424     vadjustment->step_increment = 20;
1425     vadjustment->lower = 0;
1426     vadjustment->upper = totalHeight;
1427
1428     g_signal_emit_by_name(G_OBJECT (vadjustment), "changed");
1429   }
1430 }
1431
1432 gboolean TextureBrowser_size_allocate(GtkWidget* widget, GtkAllocation* allocation, TextureBrowser* textureBrowser)
1433 {
1434   textureBrowser->width = allocation->width;
1435   textureBrowser->height = allocation->height;
1436   TextureBrowser_heightChanged(*textureBrowser);
1437   textureBrowser->m_originInvalid = true;
1438   TextureBrowser_queueDraw(*textureBrowser);
1439   return FALSE;
1440 }
1441
1442 gboolean TextureBrowser_expose(GtkWidget* widget, GdkEventExpose* event, TextureBrowser* textureBrowser)
1443 {
1444   if(glwidget_make_current(textureBrowser->m_gl_widget) != FALSE)
1445   {
1446     GlobalOpenGL_debugAssertNoErrors();
1447     TextureBrowser_evaluateHeight(*textureBrowser);
1448     Texture_Draw(*textureBrowser);
1449     GlobalOpenGL_debugAssertNoErrors();
1450     glwidget_swap_buffers(textureBrowser->m_gl_widget);
1451   }
1452   return FALSE;
1453 }
1454
1455
1456 TextureBrowser g_TextureBrowser;
1457
1458 TextureBrowser& GlobalTextureBrowser()
1459 {
1460   return g_TextureBrowser;
1461 }
1462
1463 bool TextureBrowser_hideUnused()
1464 {
1465   return g_TextureBrowser.m_hideUnused;
1466 }
1467
1468 void TextureBrowser_ToggleHideUnused()
1469 {
1470   if(g_TextureBrowser.m_hideUnused)
1471   {
1472     TextureBrowser_SetHideUnused(g_TextureBrowser, false);
1473   }
1474   else
1475   {
1476     TextureBrowser_SetHideUnused(g_TextureBrowser, true);
1477   }
1478 }
1479
1480 GtkWidget* TextureBrowser_constructWindow(GtkWindow* toplevel)
1481 {
1482   GlobalShaderSystem().setActiveShadersChangedNotify(ReferenceCaller<TextureBrowser, TextureBrowser_activeShadersChanged>(g_TextureBrowser));
1483
1484   GtkWidget* hbox = gtk_hbox_new (FALSE, 0);
1485
1486   g_TextureBrowser.m_parent = toplevel;
1487
1488   {
1489           GtkWidget* w = gtk_vscrollbar_new (GTK_ADJUSTMENT (gtk_adjustment_new (0,0,0,1,1,1)));
1490           gtk_widget_show (w);
1491           gtk_box_pack_end (GTK_BOX (hbox), w, FALSE, TRUE, 0);
1492           g_TextureBrowser.m_texture_scroll = w;
1493
1494     GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_TextureBrowser.m_texture_scroll));
1495     g_signal_connect(G_OBJECT(vadjustment), "value_changed", G_CALLBACK(TextureBrowser_verticalScroll), &g_TextureBrowser);
1496
1497     widget_set_visible(g_TextureBrowser.m_texture_scroll, g_TextureBrowser.m_showTextureScrollbar);
1498   }
1499   {
1500           GtkWidget* texbox = gtk_vbox_new (FALSE, 0);
1501           gtk_widget_show(texbox);
1502           gtk_box_pack_start(GTK_BOX(hbox), texbox, TRUE, TRUE, 0);
1503
1504           {
1505                   GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
1506                   gtk_box_pack_start(GTK_BOX(texbox), GTK_WIDGET(entry), FALSE, FALSE, 0);
1507
1508                   g_TextureBrowser.m_filter = entry;
1509       if(g_TextureBrowser.m_showTextureFilter)
1510       {
1511         gtk_widget_show(GTK_WIDGET(g_TextureBrowser.m_filter));
1512       }
1513
1514       g_TextureBrowser.m_filterEntry.connect(entry);
1515           }
1516
1517           {
1518       g_TextureBrowser.m_gl_widget = glwidget_new(FALSE);
1519       gtk_widget_ref(g_TextureBrowser.m_gl_widget);
1520
1521       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);
1522       GTK_WIDGET_SET_FLAGS(g_TextureBrowser.m_gl_widget, GTK_CAN_FOCUS);
1523
1524                   gtk_box_pack_start(GTK_BOX(texbox), g_TextureBrowser.m_gl_widget, TRUE, TRUE, 0);
1525                   gtk_widget_show(g_TextureBrowser.m_gl_widget);
1526
1527       g_TextureBrowser.m_sizeHandler = g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "size_allocate", G_CALLBACK(TextureBrowser_size_allocate), &g_TextureBrowser);
1528       g_TextureBrowser.m_exposeHandler = g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "expose_event", G_CALLBACK(TextureBrowser_expose), &g_TextureBrowser);
1529
1530       g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "button_press_event", G_CALLBACK(TextureBrowser_button_press), &g_TextureBrowser);
1531       g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "button_release_event", G_CALLBACK(TextureBrowser_button_release), &g_TextureBrowser);
1532       g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "motion_notify_event", G_CALLBACK(TextureBrowser_motion), &g_TextureBrowser);
1533       g_signal_connect(G_OBJECT(g_TextureBrowser.m_gl_widget), "scroll_event", G_CALLBACK(TextureBrowser_scroll), &g_TextureBrowser);
1534           }
1535         }
1536   TextureBrowser_updateScroll(g_TextureBrowser);
1537
1538   gtk_container_set_focus_chain(GTK_CONTAINER(hbox), NULL);
1539
1540   return hbox;
1541 }
1542
1543 void TextureBrowser_destroyWindow()
1544 {
1545   GlobalShaderSystem().setActiveShadersChangedNotify(Callback());
1546
1547   g_signal_handler_disconnect(G_OBJECT(g_TextureBrowser.m_gl_widget), g_TextureBrowser.m_sizeHandler);
1548   g_signal_handler_disconnect(G_OBJECT(g_TextureBrowser.m_gl_widget), g_TextureBrowser.m_exposeHandler);
1549
1550   gtk_widget_unref(g_TextureBrowser.m_gl_widget);
1551 }
1552
1553 const Vector3& TextureBrowser_getBackgroundColour(TextureBrowser& textureBrowser)
1554 {
1555   return textureBrowser.color_textureback;
1556 }
1557
1558 void TextureBrowser_setBackgroundColour(TextureBrowser& textureBrowser, const Vector3& colour)
1559 {
1560   textureBrowser.color_textureback = colour;
1561   TextureBrowser_queueDraw(textureBrowser);
1562 }
1563
1564
1565 void TextureBrowser_ToggleShowShaders() 
1566 {
1567   g_TextureBrowser.m_showShaders ^= 1;
1568   g_TexturesMenu.m_showshaders_item.update();
1569   TextureBrowser_queueDraw(g_TextureBrowser);
1570 }
1571
1572 void TextureBrowser_ToggleShowShaderListOnly() 
1573 {
1574   g_TexturesMenu_shaderlistOnly ^= 1;
1575   g_TexturesMenu.m_showshaderlistonly_item.update();
1576   TextureGroupsMenu_Destroy();
1577   TextureGroupsMenu_Construct();
1578 }
1579
1580 void TextureBrowser_showAll()
1581 {
1582   g_TextureBrowser_currentDirectory = "";
1583   TextureBrowser_heightChanged(g_TextureBrowser);
1584   TextureBrowser_updateTitle();
1585 }
1586
1587 void TextureBrowser_exportTitle(const StringImportCallback& importer)
1588 {
1589   StringOutputStream buffer(64);
1590   buffer << "Textures: ";
1591   if(!string_empty(g_TextureBrowser_currentDirectory.c_str()))
1592   {
1593     buffer << g_TextureBrowser_currentDirectory.c_str();
1594   }
1595   else
1596   {
1597     buffer << "all";
1598   }
1599   importer(buffer.c_str());
1600 }
1601
1602
1603 void TextureScaleImport(TextureBrowser& textureBrowser, int value)
1604 {
1605   switch(value)
1606   {
1607   case 0:
1608     TextureBrowser_setScale(textureBrowser, 10);
1609     break;
1610   case 1:
1611     TextureBrowser_setScale(textureBrowser, 25);
1612     break;
1613   case 2:
1614     TextureBrowser_setScale(textureBrowser, 50);
1615     break;
1616   case 3:
1617     TextureBrowser_setScale(textureBrowser, 100);
1618     break;
1619   case 4:
1620     TextureBrowser_setScale(textureBrowser, 200);
1621     break;
1622   }
1623 }
1624 typedef ReferenceCaller1<TextureBrowser, int, TextureScaleImport> TextureScaleImportCaller;
1625
1626 void TextureScaleExport(TextureBrowser& textureBrowser, const IntImportCallback& importer)
1627 {
1628   switch(textureBrowser.m_textureScale)
1629   {
1630   case 10:
1631     importer(0);
1632     break;
1633   case 25:
1634     importer(1);
1635     break;
1636   case 50:
1637     importer(2);
1638     break;
1639   case 100:
1640     importer(3);
1641     break;
1642   case 200:
1643     importer(4);
1644     break;
1645   }
1646 }
1647 typedef ReferenceCaller1<TextureBrowser, const IntImportCallback&, TextureScaleExport> TextureScaleExportCaller;
1648
1649 void TextureBrowser_constructPreferences(PreferencesPage& page)
1650 {
1651   page.appendCheckBox(
1652     "", "Texture subsets",
1653     TextureBrowserImportShowFilterCaller(GlobalTextureBrowser()),
1654     BoolExportCaller(GlobalTextureBrowser().m_showTextureFilter)
1655   );
1656   page.appendCheckBox(
1657     "", "Texture scrollbar",
1658     TextureBrowserImportShowScrollbarCaller(GlobalTextureBrowser()),
1659     BoolExportCaller(GlobalTextureBrowser().m_showTextureScrollbar)
1660   );
1661   {
1662     const char* texture_scale[] = { "10%", "25%", "50%", "100%", "200%" };
1663     page.appendCombo(
1664       "Texture Thumbnail Scale",
1665       STRING_ARRAY_RANGE(texture_scale),
1666       IntImportCallback(TextureScaleImportCaller(GlobalTextureBrowser())),
1667       IntExportCallback(TextureScaleExportCaller(GlobalTextureBrowser()))
1668     );
1669   }
1670   page.appendEntry("Mousewheel Increment", GlobalTextureBrowser().m_mouseWheelScrollIncrement);
1671   {
1672     const char* startup_shaders[] = { "None", TextureBrowser_getComonShadersName(), "All" };
1673     page.appendCombo("Load Shaders at Startup", reinterpret_cast<int&>(GlobalTextureBrowser().m_startupShaders), STRING_ARRAY_RANGE(startup_shaders));
1674   }
1675 }
1676 void TextureBrowser_constructPage(PreferenceGroup& group)
1677 {
1678   PreferencesPage page(group.createPage("Texture Browser", "Texture Browser Preferences"));
1679   TextureBrowser_constructPreferences(page);
1680 }
1681 void TextureBrowser_registerPreferencesPage()
1682 {
1683   PreferencesDialog_addSettingsPage(FreeCaller1<PreferenceGroup&, TextureBrowser_constructPage>());
1684 }
1685
1686
1687 #include "preferencesystem.h"
1688 #include "stringio.h"
1689
1690 typedef ReferenceCaller1<TextureBrowser, std::size_t, TextureBrowser_setScale> TextureBrowserSetScaleCaller;
1691
1692
1693
1694 void TextureClipboard_textureSelected(const char* shader);
1695
1696 void TextureBrowser_Construct()
1697 {
1698   GlobalToggles_insert("ShowInUse", FreeCaller<TextureBrowser_ToggleHideUnused>(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_hideunused_item), Accelerator('U'));
1699   GlobalCommands_insert("ShowAllTextures", FreeCaller<TextureBrowser_showAll>(), Accelerator('A', (GdkModifierType)GDK_CONTROL_MASK));
1700   GlobalCommands_insert("ViewTextures", FreeCaller<TextureBrowser_toggleShown>(), Accelerator('T'));
1701   GlobalToggles_insert("ToggleShowShaders", FreeCaller<TextureBrowser_ToggleShowShaders>(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_showshaders_item));
1702   GlobalToggles_insert("ToggleShowShaderlistOnly", FreeCaller<TextureBrowser_ToggleShowShaderListOnly>(), ToggleItem::AddCallbackCaller(g_TexturesMenu.m_showshaderlistonly_item));
1703
1704   GlobalPreferenceSystem().registerPreference("TextureScale",
1705     makeSizeStringImportCallback(TextureBrowserSetScaleCaller(g_TextureBrowser)),
1706     SizeExportStringCaller(g_TextureBrowser.m_textureScale)
1707   );
1708   GlobalPreferenceSystem().registerPreference("NewTextureWindowStuff",
1709     makeBoolStringImportCallback(TextureBrowserImportShowFilterCaller(g_TextureBrowser)),
1710     BoolExportStringCaller(GlobalTextureBrowser().m_showTextureFilter)
1711   );
1712   GlobalPreferenceSystem().registerPreference("TextureScrollbar",
1713     makeBoolStringImportCallback(TextureBrowserImportShowScrollbarCaller(g_TextureBrowser)),
1714     BoolExportStringCaller(GlobalTextureBrowser().m_showTextureScrollbar)
1715   );
1716   GlobalPreferenceSystem().registerPreference("ShowShaders", BoolImportStringCaller(GlobalTextureBrowser().m_showShaders), BoolExportStringCaller(GlobalTextureBrowser().m_showShaders));
1717   GlobalPreferenceSystem().registerPreference("ShowShaderlistOnly", BoolImportStringCaller(g_TexturesMenu_shaderlistOnly), BoolExportStringCaller(g_TexturesMenu_shaderlistOnly));
1718   GlobalPreferenceSystem().registerPreference("LoadShaders", IntImportStringCaller(reinterpret_cast<int&>(GlobalTextureBrowser().m_startupShaders)), IntExportStringCaller(reinterpret_cast<int&>(GlobalTextureBrowser().m_startupShaders)));
1719   GlobalPreferenceSystem().registerPreference("WheelMouseInc", SizeImportStringCaller(GlobalTextureBrowser().m_mouseWheelScrollIncrement), SizeExportStringCaller(GlobalTextureBrowser().m_mouseWheelScrollIncrement));
1720   GlobalPreferenceSystem().registerPreference("SI_Colors0", Vector3ImportStringCaller(GlobalTextureBrowser().color_textureback), Vector3ExportStringCaller(GlobalTextureBrowser().color_textureback));
1721
1722   g_TextureBrowser.shader = texdef_name_default();
1723
1724   Textures_setModeChangedNotify(ReferenceCaller<TextureBrowser, TextureBrowser_queueDraw>(g_TextureBrowser));
1725
1726   TextureBrowser_registerPreferencesPage();
1727
1728   GlobalShaderSystem().attach(g_ShadersObserver);
1729   GlobalShaderSystem().attach(g_TextureGroupsMenu);
1730   GlobalFileSystem().attach(g_TextureGroupsMenu);
1731
1732   TextureBrowser_textureSelected = TextureClipboard_textureSelected;
1733 }
1734 void TextureBrowser_Destroy()
1735 {
1736   GlobalFileSystem().detach(g_TextureGroupsMenu);
1737   GlobalShaderSystem().detach(g_TextureGroupsMenu);
1738   GlobalShaderSystem().detach(g_ShadersObserver);
1739
1740   Textures_setModeChangedNotify(Callback());
1741 }