1 /**
2 * @file
3 * @brief Texture Window
4 * @author Leonardo Zide (leo@lokigames.com)
5 */
6
7 /*
8 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
9 For a list of contributors, see the accompanying CONTRIBUTORS file.
10
11 This file is part of GtkRadiant.
12
13 GtkRadiant is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 GtkRadiant is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with GtkRadiant; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 */
27
28 #include "texturebrowser.h"
29 #include "radiant_i18n.h"
30
31 #include "debugging/debugging.h"
32
33 #include "ifilesystem.h"
34 #include "iundo.h"
35 #include "igl.h"
36 #include "iarchive.h"
37 #include "moduleobserver.h"
38 #include "ieventmanager.h"
39
40 #include <set>
41 #include <string>
42 #include <vector>
43
44 #include "math/Vector3.h"
45 #include "texturelib.h"
46 #include "string/string.h"
47 #include "shaderlib.h"
48 #include "os/file.h"
49 #include "os/path.h"
50 #include "stream/memstream.h"
51 #include "stream/textfilestream.h"
52 #include "stream/stringstream.h"
53 #include "imaterial.h"
54
55 #include "gtkutil/image.h"
56 #include "gtkutil/menu.h"
57 #include "gtkutil/cursor.h"
58 #include "gtkutil/widget.h"
59 #include "gtkutil/glwidget.h"
60 #include "gtkutil/GLWidgetSentry.h"
61 #include "gtkutil/messagebox.h"
62 #include "gtkutil/TreeModel.h"
63 #include "gtkutil/ModalProgressDialog.h"
64
65 #include "../ui/common/ToolbarCreator.h"
66 #include "../ui/mainframe/mainframe.h"
67 #include "../map/map.h"
68 #include "../render/OpenGLRenderSystem.h"
69 #include "../select.h"
70 #include "../brush/TextureProjection.h"
71 #include "../brush/brushmanip.h"
72 #include "../plugin.h"
73
74 namespace {
75 const std::string RKEY_TEXTURES_HIDE_UNUSED = "user/ui/textures/browser/hideUnused";
76 const std::string RKEY_TEXTURES_HIDE_INVALID = "user/ui/textures/browser/hideInvalid";
77 const std::string RKEY_TEXTURES_UNIFORM_SIZE = "user/ui/textures/browser/uniformSize";
78 const std::string RKEY_TEXTURES_THUMBNAIL_SCALE = "user/ui/textures/browser/thumbnailScale";
79 const std::string RKEY_TEXTURES_LICENSE_PATH = "user/ui/textures/browser/licensepath";
80 const std::string RKEY_TEXTURES_SKIPCOMMON = "user/ui/textures/skipCommon";
81 }
82
83 static void TextureBrowser_scrollChanged (void* data, gdouble value);
84
TextureBrowser()85 TextureBrowser::TextureBrowser () :
86 _glWidget(false), m_texture_scroll(NULL), m_heightChanged(true), m_originInvalid(true), m_scrollAdjustment(
87 TextureBrowser_scrollChanged, this), m_rmbSelected(false), m_resizeTextures(true)
88 {
89 GlobalRegistry().addKeyObserver(this, RKEY_TEXTURES_HIDE_UNUSED);
90 GlobalRegistry().addKeyObserver(this, RKEY_TEXTURES_HIDE_INVALID);
91 GlobalRegistry().addKeyObserver(this, RKEY_TEXTURES_UNIFORM_SIZE);
92 GlobalRegistry().addKeyObserver(this, RKEY_TEXTURES_THUMBNAIL_SCALE);
93
94 GlobalShaderSystem().setActiveShadersChangedNotify(MemberCaller<TextureBrowser,
95 &TextureBrowser::activeShadersChanged> (*this));
96
97 GlobalPreferenceSystem().addConstructor(this);
98 keyChanged("", "");
99 setSelectedShader("");
100 }
101
createWidget()102 void TextureBrowser::createWidget() {
103 _widget = gtk_vbox_new(FALSE, 0);
104
105 gtk_box_pack_start(GTK_BOX(_widget), createMenuBar(), false, true, 0);
106 gtk_box_pack_start(GTK_BOX(_widget), createToolBar(), false, true, 0);
107
108 GtkWidget* texhbox = gtk_hbox_new(FALSE, 0);
109 gtk_box_pack_end(GTK_BOX(_widget), texhbox, true, true, 0);
110
111 m_texture_scroll = gtk_vscrollbar_new(GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 1, 1, 1)));
112
113 GtkAdjustment *vadjustment = gtk_range_get_adjustment(GTK_RANGE(m_texture_scroll));
114 g_signal_connect(G_OBJECT(vadjustment), "value_changed", G_CALLBACK(onVerticalScroll),
115 this);
116
117 widget_set_visible(m_texture_scroll, true);
118
119 gtk_box_pack_end(GTK_BOX(texhbox), m_texture_scroll, false, true, 0);
120
121 { // gl_widget
122 GtkWidget *glWidget = _glWidget;
123
124 gtk_widget_set_events(glWidget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
125 | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK);
126 GTK_WIDGET_SET_FLAGS(glWidget, GTK_CAN_FOCUS);
127 gtk_widget_show(glWidget);
128
129 g_signal_connect(G_OBJECT(glWidget), "size_allocate",
130 G_CALLBACK(onSizeAllocate), this);
131 g_signal_connect(G_OBJECT(glWidget), "expose_event",
132 G_CALLBACK(onExpose), this);
133 g_signal_connect(G_OBJECT(glWidget), "button_press_event", G_CALLBACK(
134 onButtonPress), this);
135 g_signal_connect(G_OBJECT(glWidget), "button_release_event", G_CALLBACK(
136 onButtonRelease), this);
137 g_signal_connect(G_OBJECT(glWidget), "motion_notify_event", G_CALLBACK(
138 onMouseMotion), this);
139 g_signal_connect(G_OBJECT(glWidget), "scroll_event", G_CALLBACK(onMouseScroll),
140 this);
141 gtk_box_pack_start(GTK_BOX(texhbox), glWidget, true, true, 0);
142 }
143
144 gtk_container_unset_focus_chain(GTK_CONTAINER(_widget));
145
146 gtk_widget_show_all(_widget);
147 }
148
textureScaleImport(int value)149 void TextureBrowser::textureScaleImport (int value)
150 {
151 int val;
152 switch (value) {
153 case 0:
154 val = 10;
155 break;
156 case 1:
157 val = 25;
158 break;
159 case 2:
160 val = 50;
161 break;
162 case 3:
163 val = 100;
164 break;
165 case 4:
166 val = 200;
167 break;
168 default:
169 return;
170 }
171 m_textureScale = val;
172
173 queueDraw();
174 }
175
keyChanged(const std::string & changedKey,const std::string & newValue)176 void TextureBrowser::keyChanged (const std::string& changedKey, const std::string& newValue)
177 {
178 m_hideUnused = GlobalRegistry().getBool(RKEY_TEXTURES_HIDE_UNUSED);
179 m_hideInvalid = GlobalRegistry().getBool(RKEY_TEXTURES_HIDE_INVALID);
180 m_uniformTextureSize = GlobalRegistry().getInt(RKEY_TEXTURES_UNIFORM_SIZE);
181
182 textureScaleImport(GlobalRegistry().getInt(RKEY_TEXTURES_THUMBNAIL_SCALE));
183
184 heightChanged();
185 m_originInvalid = true;
186 }
187
getFontHeight()188 int TextureBrowser::getFontHeight ()
189 {
190 return GlobalOpenGL().m_fontHeight;
191 }
192
getSelectedShader() const193 const std::string& TextureBrowser::getSelectedShader () const
194 {
195 return shader;
196 }
197
198 /**
199 * @brief Updates statusbar with texture information
200 */
setStatusText(const std::string & name)201 void TextureBrowser::setStatusText (const std::string& name)
202 {
203 if (g_pParentWnd == 0)
204 return;
205 IShader* shaderPtr = GlobalShaderSystem().getShaderForName(name);
206 GLTexture* q = shaderPtr->getTexture();
207 StringOutputStream strTex(256);
208 strTex << name << " W: " << string::toString(q->width) << " H: " << string::toString(q->height);
209 shaderPtr->DecRef();
210 g_pParentWnd->SetStatusText(g_pParentWnd->m_texture_status, shader_get_textureName(strTex.toString()));
211 }
212
setSelectedShader(const std::string & _shader)213 void TextureBrowser::setSelectedShader (const std::string& _shader)
214 {
215 shader = _shader;
216 if (shader.empty())
217 shader = GlobalTexturePrefix_get() + "tex_common/nodraw";
218 setStatusText(shader);
219 focus(shader);
220 }
221
222 // Return the display width of a texture in the texture browser
getTextureWidth(const GLTexture * tex)223 int TextureBrowser::getTextureWidth(const GLTexture* tex) {
224 int width;
225 if (!m_resizeTextures) {
226 // Don't use uniform size
227 width = (int) (tex->width * ((float) m_textureScale / 100));
228 } else if (tex->width >= tex->height) {
229 // Texture is square, or wider than it is tall
230 width = m_uniformTextureSize;
231 } else {
232 // Otherwise, preserve the texture's aspect ratio
233 width = (int) (m_uniformTextureSize * ((float) tex->width / tex->height));
234 }
235 return width;
236 }
237 // Return the display height of a texture in the texture browser
getTextureHeight(const GLTexture * tex)238 int TextureBrowser::getTextureHeight(const GLTexture* tex) {
239 int height;
240 if (!m_resizeTextures) {
241 // Don't use uniform size
242 height = (int) (tex->height * ((float) m_textureScale / 100));
243 } else if (tex->height >= tex->width) {
244 // Texture is square, or taller than it is wide
245 height = m_uniformTextureSize;
246 } else {
247 // Otherwise, preserve the texture's aspect ratio
248 height = (int) (m_uniformTextureSize * ((float) tex->height / tex->width));
249 }
250 return height;
251 }
252
getNextTexturePosition(TextureLayout & layout,const GLTexture * q,int * x,int * y)253 void TextureBrowser::getNextTexturePosition (TextureLayout& layout, const GLTexture* q, int* x, int* y)
254 {
255 const int nWidth = getTextureWidth(q);
256 const int nHeight = getTextureHeight(q);
257 // go to the next row unless the texture is the first on the row
258 if (layout.current_x + nWidth > width - 8 && layout.current_row) {
259 layout.current_x = 8;
260 layout.current_y -= layout.current_row + getFontHeight() + 4;
261 layout.current_row = 0;
262 }
263
264 *x = layout.current_x;
265 *y = layout.current_y;
266
267 // Is our texture larger than the row? If so, grow the
268 // row height to match it
269
270 if (layout.current_row < nHeight)
271 layout.current_row = nHeight;
272
273 // never go less than 96, or the names get all crunched up
274 layout.current_x += nWidth < 96 ? 96 : nWidth;
275 layout.current_x += 8;
276 }
277
278 // if texture_showinuse jump over non in-use textures
isTextureShown(const IShader * shader) const279 bool TextureBrowser::isTextureShown (const IShader* shader) const
280 {
281 if (!shader_equal_prefix(shader->getName(), GlobalTexturePrefix_get()))
282 return false;
283
284 if (m_hideUnused && !shader->isInUse())
285 return false;
286
287 if (m_hideInvalid && !shader->isValid())
288 return false;
289
290 if (!shader_equal_prefix(shader_get_textureName(shader->getName()), currentDirectory))
291 return false;
292
293 return true;
294 }
295
heightChanged()296 void TextureBrowser::heightChanged ()
297 {
298 m_heightChanged = true;
299
300 updateScroll();
301 queueDraw();
302 }
303
evaluateHeight()304 void TextureBrowser::evaluateHeight ()
305 {
306 if (!m_heightChanged)
307 return;
308
309 m_heightChanged = false;
310 m_nTotalHeight = 0;
311
312 TextureLayout layout;
313 ShaderSystem &s = GlobalShaderSystem();
314 for (s.beginActiveShadersIterator(); !s.endActiveShadersIterator(); s.incrementActiveShadersIterator()) {
315 const IShader* shader = s.dereferenceActiveShadersIterator();
316
317 if (!isTextureShown(shader))
318 continue;
319
320 int x, y;
321 getNextTexturePosition(layout, shader->getTexture(), &x, &y);
322 m_nTotalHeight = std::max(m_nTotalHeight, abs(layout.current_y) + getFontHeight() + getTextureHeight(
323 shader->getTexture()) + 4);
324 }
325 }
326
getTotalHeight()327 int TextureBrowser::getTotalHeight ()
328 {
329 evaluateHeight();
330 return m_nTotalHeight;
331 }
332
clampOriginY()333 void TextureBrowser::clampOriginY ()
334 {
335 if (originy > 0) {
336 originy = 0;
337 }
338 const int lower = std::min(height - getTotalHeight(), 0);
339 if (originy < lower) {
340 originy = lower;
341 }
342 }
343
getOriginY()344 int TextureBrowser::getOriginY ()
345 {
346 if (m_originInvalid) {
347 m_originInvalid = false;
348 clampOriginY();
349 updateScroll();
350 }
351 return originy;
352 }
353
setOriginY(int _originy)354 void TextureBrowser::setOriginY (int _originy)
355 {
356 originy = _originy;
357 clampOriginY();
358 updateScroll();
359 queueDraw();
360 }
361
addActiveShadersChangedCallback(const SignalHandler & handler)362 void TextureBrowser::addActiveShadersChangedCallback (const SignalHandler& handler)
363 {
364 g_activeShadersChangedCallbacks.connectLast(handler);
365 }
366
367 class ShadersObserver: public ModuleObserver
368 {
369 Signal0 m_realiseCallbacks;
370 public:
realise()371 void realise ()
372 {
373 m_realiseCallbacks();
374 }
unrealise()375 void unrealise ()
376 {
377 }
insert(const SignalHandler & handler)378 void insert (const SignalHandler& handler)
379 {
380 m_realiseCallbacks.connectLast(handler);
381 }
382 };
383
384 namespace {
385 ShadersObserver g_ShadersObserver;
386 }
387
activeShadersChanged()388 void TextureBrowser::activeShadersChanged ()
389 {
390 heightChanged();
391 m_originInvalid = true;
392
393 g_activeShadersChangedCallbacks();
394 }
395
396 /**
397 * @note relies on texture_directory global for the directory to use
398 * 1) Load the shaders for the given directory
399 * 2) Scan the remaining texture, load them and assign them a default shader (the "noshader" shader)
400 * NOTE: when writing a texture plugin, or some texture extensions, this function may need to be overriden, and made
401 * available through the IShaders interface
402 * NOTE: for texture window layout:
403 * all shaders are stored with alphabetical order after load
404 * previously loaded and displayed stuff is hidden, only in-use and newly loaded is shown
405 * ( the GL textures are not flushed though)
406 */
texture_name_ignore(const std::string & name)407 static bool texture_name_ignore (const std::string& name)
408 {
409 if (string::contains(name, "_nm"))
410 return true;
411 if (string::contains(name, "_gm"))
412 return true;
413 if (string::contains(name, "_sm"))
414 return true;
415
416 return string::contains(name, "tex_terrain") && !string::contains(name, "dummy");
417 }
418
419 class LoadTexturesByTypeVisitor: public ImageModules::Visitor
420 {
421 private:
422 const std::string m_dirstring;
423 // Modal dialog window to display progress
424 gtkutil::ModalProgressDialog _dialog;
425
426 struct TextureDirectoryLoadTextureCaller
427 {
428 typedef const std::string& first_argument_type;
429
430 const std::string& _directory;
431 const gtkutil::ModalProgressDialog& _dialog;
432
433 // Constructor
TextureDirectoryLoadTextureCallerLoadTexturesByTypeVisitor::TextureDirectoryLoadTextureCaller434 TextureDirectoryLoadTextureCaller (const std::string& directory,
435 const gtkutil::ModalProgressDialog& dialog) :
436 _directory(directory), _dialog(dialog)
437 {
438 }
439
440 // Functor operator
operator ()LoadTexturesByTypeVisitor::TextureDirectoryLoadTextureCaller441 void operator() (const std::string& texture)
442 {
443 std::string name = _directory + os::stripExtension(texture);
444
445 if (texture_name_ignore(name))
446 return;
447
448 if (!shader_valid(name)) {
449 g_warning("Skipping invalid texture name: [%s]\n", name.c_str());
450 return;
451 }
452
453 // Update the text in the dialog
454 _dialog.setText(name);
455
456 // if a texture is already in use to represent a shader, ignore it
457 IShader* shaderPtr = GlobalShaderSystem().getShaderForName(name);
458 shaderPtr->DecRef();
459 }
460 };
461
462 public:
LoadTexturesByTypeVisitor(const std::string & dirstring)463 LoadTexturesByTypeVisitor (const std::string& dirstring) :
464 m_dirstring(dirstring), _dialog(MainFrame_getWindow(), _("Loading textures"))
465 {
466 }
visit(const std::string & minor,const IImageModule & table) const467 void visit (const std::string& minor, const IImageModule& table) const
468 {
469 TextureDirectoryLoadTextureCaller functor(m_dirstring, _dialog);
470 GlobalFileSystem().forEachFile(m_dirstring, minor, makeCallback1(functor));
471 }
472 };
473
showDirectory(const std::string & directory)474 void TextureBrowser::showDirectory (const std::string& directory)
475 {
476 currentDirectory = directory;
477 heightChanged();
478
479 // load remaining texture files
480 Radiant_getImageModules().foreachModule(LoadTexturesByTypeVisitor(GlobalTexturePrefix_get() + directory));
481
482 // we'll display the newly loaded textures + all the ones already in use
483 GlobalRegistry().set(RKEY_TEXTURES_HIDE_UNUSED, "0");
484 GlobalRegistry().set(RKEY_TEXTURES_HIDE_INVALID, "0");
485 }
486
showStartupShaders()487 void TextureBrowser::showStartupShaders ()
488 {
489 showDirectory("tex_common/");
490 }
491
492 //++timo NOTE: this is a mix of Shader module stuff and texture explorer
493 // it might need to be split in parts or moved out .. dunno
494 // scroll origin so the specified texture is completely on screen
495 // if current texture is not displayed, nothing is changed
focus(const std::string & name)496 void TextureBrowser::focus (const std::string& name)
497 {
498 TextureLayout layout;
499 // scroll origin so the texture is completely on screen
500 for (GlobalShaderSystem().beginActiveShadersIterator(); !GlobalShaderSystem().endActiveShadersIterator(); GlobalShaderSystem().incrementActiveShadersIterator()) {
501 const IShader* shader = GlobalShaderSystem().dereferenceActiveShadersIterator();
502
503 if (!isTextureShown(shader))
504 continue;
505
506 int x, y;
507 getNextTexturePosition(layout, shader->getTexture(), &x, &y);
508 const GLTexture* q = shader->getTexture();
509 if (!q)
510 break;
511
512 // we have found when texdef->name and the shader name match
513 // NOTE: as everywhere else for our comparisons, we are not case sensitive
514 if (shader_equal(name, shader->getName())) {
515 const int textureHeight = getTextureHeight(q);
516
517 int originy = getOriginY();
518 if (y > originy) {
519 originy = y;
520 }
521
522 if (y - textureHeight < originy - height) {
523 originy = (y - textureHeight) + height;
524 }
525
526 setOriginY(originy);
527 return;
528 }
529 }
530 }
531
getTextureAt(int mx,int my)532 const IShader* TextureBrowser::getTextureAt (int mx, int my)
533 {
534 my += getOriginY() - height;
535
536 TextureLayout layout;
537 for (GlobalShaderSystem().beginActiveShadersIterator(); !GlobalShaderSystem().endActiveShadersIterator(); GlobalShaderSystem().incrementActiveShadersIterator()) {
538 const IShader* shader = GlobalShaderSystem().dereferenceActiveShadersIterator();
539
540 if (!isTextureShown(shader))
541 continue;
542
543 int x, y;
544 getNextTexturePosition(layout, shader->getTexture(), &x, &y);
545 GLTexture *q = shader->getTexture();
546 if (!q)
547 break;
548
549 int nWidth = getTextureWidth(q);
550 int nHeight = getTextureHeight(q);
551 if (mx > x && mx - x < nWidth && my < y && y - my < nHeight + getFontHeight()) {
552 return shader;
553 }
554 }
555
556 return 0;
557 }
558
selectTextureAt(int mx,int my)559 void TextureBrowser::selectTextureAt (int mx, int my)
560 {
561 const IShader* shader = getTextureAt(mx, my);
562 if (shader != 0) {
563 setSelectedShader(shader->getName());
564
565 if (!m_rmbSelected) {
566 UndoableCommand undo("textureNameSetSelected");
567 Select_SetShader(shader->getName(), GlobalRegistry().getBool(RKEY_TEXTURES_SKIPCOMMON));
568 }
569 }
570 }
571
572 /**
573 * @brief relying on the shaders list to display the textures
574 * we must query all GLTexture* to manage and display through the IShaders interface
575 * this allows a plugin to completely override the texture system
576 */
draw()577 void TextureBrowser::draw ()
578 {
579 const int originy = getOriginY();
580
581 Vector3 colorBackground = ColourSchemes().getColourVector3("texture_background");
582 glClearColor(colorBackground[0], colorBackground[1], colorBackground[2], 0);
583 glViewport(0, 0, width, height);
584 glMatrixMode(GL_PROJECTION);
585 glLoadIdentity();
586
587 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
588 glDisable(GL_DEPTH_TEST);
589 glDisable(GL_BLEND);
590 glOrtho(0, width, originy - height, originy, -100, 100);
591 glEnable(GL_TEXTURE_2D);
592
593 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
594
595 int last_y = 0, last_height = 0;
596
597 TextureLayout layout;
598 for (GlobalShaderSystem().beginActiveShadersIterator(); !GlobalShaderSystem().endActiveShadersIterator(); GlobalShaderSystem().incrementActiveShadersIterator()) {
599 const IShader* shader = GlobalShaderSystem().dereferenceActiveShadersIterator();
600
601 if (!isTextureShown(shader))
602 continue;
603
604 int x, y;
605 getNextTexturePosition(layout, shader->getTexture(), &x, &y);
606 const GLTexture *q = shader->getTexture();
607 if (!q)
608 break;
609
610 const int nWidth = getTextureWidth(q);
611 const int nHeight = getTextureHeight(q);
612
613 if (y != last_y) {
614 last_y = y;
615 last_height = 0;
616 }
617 last_height = std::max(nHeight, last_height);
618
619 // Is this texture visible?
620 if ((y - nHeight - getFontHeight() < originy) && (y > originy - height)) {
621 // borders rules:
622 // if it's the current texture, draw a thick red line, else:
623 // shaders have a white border, simple textures don't
624 // if !texture_showinuse: (some textures displayed may not be in use)
625 // draw an additional square around with 0.5 1 0.5 color
626 if (shader_equal(getSelectedShader(), shader->getName())) {
627 glLineWidth(3);
628 if (m_rmbSelected) {
629 glColor3f(0, 0, 1);
630 } else {
631 glColor3f(1, 0, 0);
632 }
633 glDisable(GL_TEXTURE_2D);
634
635 glBegin(GL_LINE_LOOP);
636 glVertex2i(x - 4, y - getFontHeight() + 4);
637 glVertex2i(x - 4, y - getFontHeight() - nHeight - 4);
638 glVertex2i(x + 4 + nWidth, y - getFontHeight() - nHeight - 4);
639 glVertex2i(x + 4 + nWidth, y - getFontHeight() + 4);
640 glEnd();
641
642 glEnable(GL_TEXTURE_2D);
643 glLineWidth(1);
644 } else {
645 glLineWidth(1);
646 // shader border:
647 // TODO: Highlight if material is available for this texture (mattn)
648 if (!shader->isDefault()) {
649 glColor3f(1, 1, 1);
650 glDisable(GL_TEXTURE_2D);
651
652 glBegin(GL_LINE_LOOP);
653 glVertex2i(x - 1, y + 1 - getFontHeight());
654 glVertex2i(x - 1, y - nHeight - 1 - getFontHeight());
655 glVertex2i(x + 1 + nWidth, y - nHeight - 1 - getFontHeight());
656 glVertex2i(x + 1 + nWidth, y + 1 - getFontHeight());
657 glEnd();
658 glEnable(GL_TEXTURE_2D);
659 }
660
661 // highlight in-use textures
662 if (!m_hideUnused && shader->isInUse()) {
663 glColor3f(0.5, 1, 0.5);
664 glDisable(GL_TEXTURE_2D);
665 glBegin(GL_LINE_LOOP);
666 glVertex2i(x - 3, y + 3 - getFontHeight());
667 glVertex2i(x - 3, y - nHeight - 3 - getFontHeight());
668 glVertex2i(x + 3 + nWidth, y - nHeight - 3 - getFontHeight());
669 glVertex2i(x + 3 + nWidth, y + 3 - getFontHeight());
670 glEnd();
671 glEnable(GL_TEXTURE_2D);
672 }
673 if (!m_hideInvalid && !shader->isValid()) {
674 glColor3f(1, 0, 0);
675 glDisable(GL_TEXTURE_2D);
676 glBegin(GL_LINE_LOOP);
677 glVertex2i(x - 3, y + 3 - getFontHeight());
678 glVertex2i(x - 3, y - nHeight - 3 - getFontHeight());
679 glVertex2i(x + 3 + nWidth, y - nHeight - 3 - getFontHeight());
680 glVertex2i(x + 3 + nWidth, y + 3 - getFontHeight());
681 glEnd();
682 glEnable(GL_TEXTURE_2D);
683 }
684 }
685
686 // Draw the texture
687 glBindTexture(GL_TEXTURE_2D, q->texture_number);
688 glColor3f(1, 1, 1);
689 glBegin(GL_QUADS);
690 glTexCoord2i(0, 0);
691 glVertex2i(x, y - getFontHeight());
692 glTexCoord2i(1, 0);
693 glVertex2i(x + nWidth, y - getFontHeight());
694 glTexCoord2i(1, 1);
695 glVertex2i(x + nWidth, y - getFontHeight() - nHeight);
696 glTexCoord2i(0, 1);
697 glVertex2i(x, y - getFontHeight() - nHeight);
698 glEnd();
699
700 // draw the texture name
701 glDisable(GL_TEXTURE_2D);
702 glColor3f(1, 1, 1);
703
704 glRasterPos2i(x, y - getFontHeight() + 5);
705
706 // don't draw the directory name
707 std::string name = std::string(shader->getName());
708 name = name.substr(name.rfind("/") + 1);
709
710 // Ellipsize the name if it's too long
711 unsigned int maxNameLength = 32;
712 if (name.size() > maxNameLength) {
713 name = name.substr(0, maxNameLength / 2) + "..." + name.substr(name.size() - maxNameLength / 2);
714 }
715
716 GlobalOpenGL().drawString(name);
717 glEnable(GL_TEXTURE_2D);
718 }
719
720 //int totalHeight = abs(y) + last_height + fontHeight() + 4;
721 }
722
723 // reset the current texture
724 glBindTexture(GL_TEXTURE_2D, 0);
725 }
726
queueDraw()727 void TextureBrowser::queueDraw ()
728 {
729 GtkWidget *glWidget = _glWidget;
730 gtk_widget_queue_draw(glWidget);
731 }
732
onMouseWheel(bool bUp)733 void TextureBrowser::onMouseWheel (bool bUp)
734 {
735 int originy = getOriginY();
736
737 if (bUp) {
738 originy += 64;
739 } else {
740 originy -= 64;
741 }
742
743 setOriginY(originy);
744 }
745
746 // GTK callback for toggling uniform texture sizing
onToggleResizeTextures(GtkToggleToolButton * button,TextureBrowser * textureBrowser)747 void TextureBrowser::onToggleResizeTextures (GtkToggleToolButton* button, TextureBrowser* textureBrowser)
748 {
749 if (gtk_toggle_tool_button_get_active(button) == TRUE) {
750 textureBrowser->m_resizeTextures = true;
751 } else {
752 textureBrowser->m_resizeTextures = false;
753 }
754
755 // Update texture browser
756 textureBrowser->heightChanged();
757 }
758
onButtonPress(GtkWidget * widget,GdkEventButton * event,TextureBrowser * textureBrowser)759 gboolean TextureBrowser::onButtonPress (GtkWidget* widget, GdkEventButton* event, TextureBrowser* textureBrowser)
760 {
761 if (event->type == GDK_BUTTON_PRESS) {
762 if (event->button == 1) {
763 const int pointx = static_cast<int> (event->x);
764 const int pointy = static_cast<int> (event->y);
765 textureBrowser->selectTextureAt(pointx, textureBrowser->height - 1 - pointy);
766 }
767 }
768 return FALSE;
769 }
770
onButtonRelease(GtkWidget * widget,GdkEventButton * event,TextureBrowser * textureBrowser)771 gboolean TextureBrowser::onButtonRelease (GtkWidget* widget, GdkEventButton* event, TextureBrowser* textureBrowser)
772 {
773 if (event->type == GDK_BUTTON_RELEASE) {
774 if (event->button == 1) {
775 }
776 }
777 return FALSE;
778 }
779
onMouseMotion(GtkWidget * widget,GdkEventMotion * event,TextureBrowser * textureBrowser)780 gboolean TextureBrowser::onMouseMotion (GtkWidget *widget, GdkEventMotion *event, TextureBrowser* textureBrowser)
781 {
782 return FALSE;
783 }
784
onMouseScroll(GtkWidget * widget,GdkEventScroll * event,TextureBrowser * textureBrowser)785 gboolean TextureBrowser::onMouseScroll (GtkWidget* widget, GdkEventScroll* event, TextureBrowser* textureBrowser)
786 {
787 if (event->direction == GDK_SCROLL_UP) {
788 textureBrowser->onMouseWheel(true);
789 } else if (event->direction == GDK_SCROLL_DOWN) {
790 textureBrowser->onMouseWheel(false);
791 }
792 return FALSE;
793 }
794
TextureBrowser_scrollChanged(void * data,gdouble value)795 void TextureBrowser_scrollChanged (void* data, gdouble value)
796 {
797 TextureBrowser *textureBrowser = reinterpret_cast<TextureBrowser*> (data);
798 textureBrowser->setOriginY(-(int) value);
799 }
800
onVerticalScroll(GtkAdjustment * adjustment,TextureBrowser * textureBrowser)801 void TextureBrowser::onVerticalScroll (GtkAdjustment *adjustment, TextureBrowser* textureBrowser)
802 {
803 textureBrowser->m_scrollAdjustment.value_changed(adjustment->value);
804 }
805
updateScroll()806 void TextureBrowser::updateScroll ()
807 {
808 if (!m_texture_scroll)
809 return;
810 int totalHeight = getTotalHeight();
811
812 totalHeight = std::max(totalHeight, height);
813
814 GtkAdjustment *vadjustment = gtk_range_get_adjustment(GTK_RANGE(m_texture_scroll));
815 if (!vadjustment)
816 return;
817 vadjustment->value = -getOriginY();
818 vadjustment->page_size = height;
819 vadjustment->page_increment = height / 2;
820 vadjustment->step_increment = 20;
821 vadjustment->lower = 0;
822 vadjustment->upper = totalHeight;
823
824 g_signal_emit_by_name(G_OBJECT (vadjustment), "changed");
825 }
826
onSizeAllocate(GtkWidget * widget,GtkAllocation * allocation,TextureBrowser * textureBrowser)827 gboolean TextureBrowser::onSizeAllocate (GtkWidget* widget, GtkAllocation* allocation, TextureBrowser* textureBrowser)
828 {
829 textureBrowser->width = allocation->width;
830 textureBrowser->height = allocation->height;
831 textureBrowser->heightChanged();
832 textureBrowser->m_originInvalid = true;
833 textureBrowser->queueDraw();
834 return FALSE;
835 }
836
onExpose(GtkWidget * widget,GdkEventExpose * event,TextureBrowser * textureBrowser)837 gboolean TextureBrowser::onExpose (GtkWidget* widget, GdkEventExpose* event, TextureBrowser* textureBrowser)
838 {
839 GtkWidget* glWidget = textureBrowser->_glWidget;
840 gtkutil::GLWidgetSentry sentry(glWidget);
841 textureBrowser->evaluateHeight();
842 textureBrowser->draw();
843 return FALSE;
844 }
845
846 namespace {
847 struct TextureFunctor
848 {
849 typedef const std::string& first_argument_type;
850
851 // TextureGroups to populate
852 TextureGroups& _groups;
853
854 // Constructor
TextureFunctor__anonbe4590f60311::TextureFunctor855 TextureFunctor (TextureGroups& groups) :
856 _groups(groups)
857 {
858 }
859
860 // Functor operator
operator ()__anonbe4590f60311::TextureFunctor861 void operator() (const std::string& file)
862 {
863 const std::string texture = os::makeRelative(file, GlobalTexturePrefix_get());
864 if (texture != file) {
865 std::string filename = os::getFilenameFromPath(file);
866 if (!filename.empty()) {
867 std::string path = os::stripFilename(texture);
868 _groups.insert(path);
869 }
870 }
871 }
872 };
873
874 struct TextureDirectoryFunctor
875 {
876 typedef const std::string& first_argument_type;
877
878 // TextureGroups to populate
879 TextureGroups& _groups;
880
881 // Constructor
TextureDirectoryFunctor__anonbe4590f60311::TextureDirectoryFunctor882 TextureDirectoryFunctor (TextureGroups& groups) :
883 _groups(groups)
884 {
885 }
886
887 // Functor operator
operator ()__anonbe4590f60311::TextureDirectoryFunctor888 void operator() (const std::string& directory)
889 {
890 // skip none texture subdirs
891 if (!string::startsWith(directory, "tex_"))
892 return;
893
894 _groups.insert(directory);
895 }
896 };
897 }
898
constructTreeView()899 void TextureBrowser::constructTreeView ()
900 {
901 TextureDirectoryFunctor functorDirs(groups);
902 GlobalFileSystem().forEachDirectory(GlobalTexturePrefix_get(), makeCallback1(functorDirs));
903 TextureFunctor functorTextures(groups);
904 GlobalShaderSystem().foreachShaderName(makeCallback1(functorTextures));
905 }
906
onActivateDirectoryChange(GtkMenuItem * item,TextureBrowser * textureBrowser)907 void TextureBrowser::onActivateDirectoryChange (GtkMenuItem* item, TextureBrowser* textureBrowser)
908 {
909 std::string const& dirname = *static_cast<std::string const*>(g_object_get_data(G_OBJECT(item), "directory")) + "/";
910 textureBrowser->showDirectory(dirname);
911 textureBrowser->queueDraw();
912 }
913
constructDirectoriesMenu(GtkMenu * menu)914 GtkMenuItem* TextureBrowser::constructDirectoriesMenu (GtkMenu* menu)
915 {
916 GtkMenuItem* textures_menu_item = new_sub_menu_item_with_mnemonic(_("_Directories"));
917
918 constructTreeView();
919
920 TextureGroups::const_iterator i = groups.begin();
921 while (i != groups.end()) {
922 GtkWidget* const item = gtk_menu_item_new_with_label(i->c_str());
923 g_object_set_data(G_OBJECT(item), "directory", const_cast<std::string*>(&*i));
924 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(onActivateDirectoryChange), this);
925 gtk_container_add(GTK_CONTAINER(menu), item);
926 ++i;
927 }
928
929 return textures_menu_item;
930 }
931
createMenuBar()932 GtkWidget* TextureBrowser::createMenuBar ()
933 {
934 GtkWidget* menu_bar = gtk_menu_bar_new();
935
936 GtkWidget* menu_directories = gtk_menu_new();
937 GtkWidget* directories_item = (GtkWidget*) constructDirectoriesMenu(GTK_MENU(menu_directories));
938 gtk_menu_item_set_submenu(GTK_MENU_ITEM(directories_item), menu_directories);
939 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), directories_item);
940
941 return menu_bar;
942 }
943
createToolBar()944 GtkWidget* TextureBrowser::createToolBar ()
945 {
946 // Load the texture toolbar from the registry
947 ui::ToolbarCreator toolbarCreator;
948 GtkToolbar* textureToolbar = toolbarCreator.getToolbar("texture");
949 if (textureToolbar != NULL) {
950 gtk_widget_show(GTK_WIDGET(textureToolbar));
951
952 // Button for toggling the resizing of textures
953 GtkToolItem* sizeToggle = gtk_toggle_tool_button_new();
954 GdkPixbuf* pixBuf = gtkutil::getLocalPixbuf("texwindow_uniformsize.png");
955 GtkWidget* toggle_image = GTK_WIDGET(gtk_image_new_from_pixbuf(pixBuf));
956
957 GtkTooltips* barTips = gtk_tooltips_new();
958 gtk_tool_item_set_tooltip(sizeToggle, barTips, _("Clamp texture thumbnails to constant size"), "");
959
960 gtk_tool_button_set_label(GTK_TOOL_BUTTON(sizeToggle), _("Constant size"));
961 gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(sizeToggle), toggle_image);
962 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(sizeToggle), TRUE);
963
964 // Insert button and connect callback
965 gtk_toolbar_insert(GTK_TOOLBAR(textureToolbar), sizeToggle, 0);
966 g_signal_connect(G_OBJECT(sizeToggle),
967 "toggled",
968 G_CALLBACK(onToggleResizeTextures),
969 this);
970
971 g_object_unref(pixBuf);
972 }
973
974 return GTK_WIDGET(textureToolbar);
975 }
976
getWidget() const977 GtkWidget* TextureBrowser::getWidget () const
978 {
979 return _widget;
980 }
981
getTitle() const982 const std::string TextureBrowser::getTitle() const
983 {
984 return _("Textures");
985 }
986
showAll()987 void TextureBrowser::showAll ()
988 {
989 currentDirectory = "";
990 heightChanged();
991 }
992
refreshShaders()993 void TextureBrowser::refreshShaders ()
994 {
995 ScopeDisableScreenUpdates disableScreenUpdates(_("Processing..."), _("Loading Shaders"));
996 GlobalShaderSystem().refresh();
997 showDirectory(currentDirectory);
998 UpdateAllWindows();
999 }
1000
constructPreferencePage(PreferenceGroup & group)1001 void TextureBrowser::constructPreferencePage(PreferenceGroup& group) {
1002 // Add a page to the given group
1003 PreferencesPage* page(group.createPage(_("Texture Browser"), _("Texture Browser Preferences")));
1004
1005 // Create the string list containing the texture scale options
1006 std::list<std::string> textureScaleDescriptions;
1007 textureScaleDescriptions.push_back("10%");
1008 textureScaleDescriptions.push_back("25%");
1009 textureScaleDescriptions.push_back("50%");
1010 textureScaleDescriptions.push_back("100%");
1011 textureScaleDescriptions.push_back("200%");
1012
1013 page->appendCombo(_("Texture thumbnail scale"), RKEY_TEXTURES_THUMBNAIL_SCALE, textureScaleDescriptions);
1014 page->appendEntry(_("Uniform texture thumbnail size (pixels)"), RKEY_TEXTURES_UNIFORM_SIZE);
1015 page->appendPathEntry(_("License Path"), RKEY_TEXTURES_LICENSE_PATH, false);
1016 }
1017
registerCommands()1018 void TextureBrowser::registerCommands ()
1019 {
1020 GlobalEventManager().addCommand("RefreshShaders", MemberCaller<TextureBrowser, &TextureBrowser::refreshShaders> (
1021 *this));
1022 GlobalEventManager().addCommand("ShowAllTextures", MemberCaller<TextureBrowser, &TextureBrowser::showAll> (*this));
1023 }
1024
1025 #include "preferencesystem.h"
1026 #include "stringio.h"
1027
TextureBrowser_Construct()1028 void TextureBrowser_Construct ()
1029 {
1030 GlobalTextureBrowser().registerCommands();
1031
1032 GlobalEventManager().addRegistryToggle("ShowInUse", RKEY_TEXTURES_HIDE_UNUSED);
1033 GlobalEventManager().addRegistryToggle("ShowInvalid", RKEY_TEXTURES_HIDE_INVALID);
1034 GlobalEventManager().addRegistryToggle("SkipCommon", RKEY_TEXTURES_SKIPCOMMON);
1035
1036 GlobalShaderSystem().attach(g_ShadersObserver);
1037 }
1038
TextureBrowser_Destroy()1039 void TextureBrowser_Destroy ()
1040 {
1041 GlobalShaderSystem().detach(g_ShadersObserver);
1042
1043 GlobalShaderSystem().setActiveShadersChangedNotify(Callback());
1044 }
1045
GlobalTextureBrowser()1046 TextureBrowser& GlobalTextureBrowser ()
1047 {
1048 static TextureBrowser g_textureBrowser;
1049 return g_textureBrowser;
1050 }
1051