Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/graphics/graphics_engine.cc @ 10766

Last change on this file since 10766 was 10643, checked in by bknecht, 18 years ago

improvements by landauf on bsp, rotor and wireframe (does build, should work)

File size: 17.9 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Benjamin Grauer
13   co-programmer: ...
14*/
15
16#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_GRAPHICS
17
18#include "graphics_engine.h"
19#include "state.h"
20
21#include "world_entity.h"
22
23#include "render_2d.h"
24#include "text_engine.h"
25#include "light.h"
26#include "shader.h"
27#include "debug.h"
28
29#include "parser/preferences/preferences.h"
30#include "substring.h"
31#include "text.h"
32
33#include "orxonox_globals.h"
34#include "texture.h"
35
36#include "graphics_effect.h"
37
38#include "shell_command.h"
39#include "loading/load_param_xml.h"
40
41#include "parser/tinyxml/tinyxml.h"
42#include "util/loading/load_param.h"
43#include "util/loading/factory.h"
44
45#ifdef __WIN32__
46 #include "static_model.h"
47#endif
48
49SHELL_COMMAND(wireframe, GraphicsEngine, wireframe);
50SHELL_COMMAND(fps, GraphicsEngine, toggleFPSdisplay);
51
52ObjectListDefinition(GraphicsEngine);
53
54/**
55 * @brief standard constructor
56 */
57GraphicsEngine::GraphicsEngine ()
58{
59  this->registerObject(this, GraphicsEngine::_objectList);
60  this->setName("GraphicsEngine");
61
62  this->isInit = false;
63
64  this->bDisplayFPS = false;
65  this->bAntialiasing = false;
66  this->bDedicated = false;
67  this->minFPS = 9999;
68  this->maxFPS = 0;
69
70  this->geTextCFPS = NULL;
71  this->geTextMaxFPS = NULL;
72  this->geTextMinFPS = NULL;
73
74  this->fullscreenFlag = 0;
75  this->videoFlags = 0;
76  this->screen = NULL;
77
78  // initialize the Modules
79  TextEngine::getInstance();
80  this->graphicsEffects = NULL;
81
82}
83
84/**
85 * @brief The Pointer to this GraphicsEngine
86 */
87GraphicsEngine* GraphicsEngine::singletonRef = NULL;
88
89/**
90 * @brief destructs the graphicsEngine.
91*/
92GraphicsEngine::~GraphicsEngine ()
93{
94  // delete what has to be deleted here
95  this->displayFPS( false );
96
97  //TextEngine
98  delete TextEngine::getInstance();
99  // render 2D
100  delete Render2D::getInstance();
101
102  SDL_QuitSubSystem(SDL_INIT_VIDEO);
103  //   if (this->screen != NULL)
104  //     SDL_FreeSurface(this->screen);
105
106  GraphicsEngine::singletonRef = NULL;
107}
108
109
110/**
111 * @brief loads the GraphicsEngine Specific Parameters.
112 * @param root: the XML-Element to load the Data From
113 */
114void GraphicsEngine::loadParams(const TiXmlElement* root)
115{
116  LoadParamXML(root, "GraphicsEffect", this, GraphicsEngine, loadGraphicsEffects)
117   .describe("loads a graphics effect");
118}
119
120
121
122
123/**
124 * @param root The XML-element to load GraphicsEffects from
125 */
126void GraphicsEngine::loadGraphicsEffects(const TiXmlElement* root)
127{
128  LOAD_PARAM_START_CYCLE(root, element);
129  {
130    PRINTF(4)("element is: %s\n", element->Value());
131    Factory::fabricate(element);
132  }
133  LOAD_PARAM_END_CYCLE(element);
134}
135
136
137
138/**
139 * @brief initializes the GraphicsEngine with default settings.
140 */
141int GraphicsEngine::init()
142{
143  if (this->isInit)
144    return -1;
145  this->initVideo(640, 480, 16);
146  return 1;
147}
148
149/**
150 * @brief loads the GraphicsEngine's settings from a given ini-file and section
151 * @returns nothing usefull
152 */
153int GraphicsEngine::initFromPreferences()
154{
155  // looking if we are in fullscreen-mode
156  MultiType fullscreen = Preferences::getInstance()->getString(CONFIG_SECTION_VIDEO, CONFIG_NAME_FULLSCREEN, "0");
157
158  if (fullscreen.getBool())
159    this->fullscreenFlag = SDL_FULLSCREEN;
160
161  // looking if we are in fullscreen-mode
162  MultiType textures = Preferences::getInstance()->getString(CONFIG_SECTION_VIDEO_ADVANCED, CONFIG_NAME_TEXTURES, "1");
163  Texture::setTextureEnableState(textures.getBool());
164
165  // check it is a dedicated network node: so no drawings are made
166  MultiType dedicated = Preferences::getInstance()->getString(CONFIG_SECTION_VIDEO, CONFIG_NAME_NO_RENDER, "0");
167  this->bDedicated = dedicated.getBool();
168
169  // searching for a usefull resolution
170  SubString resolution(Preferences::getInstance()->getString(CONFIG_SECTION_VIDEO, CONFIG_NAME_RESOLUTION, "640x480").c_str(), 'x'); ///FIXME
171  //resolution.debug();
172  MultiType x = resolution.getString(0), y = resolution.getString(1);
173  return this->initVideo(x.getInt(), y.getInt(), 16);
174
175  //   GraphicsEffect* fe = new FogEffect(NULL);
176  //   this->loadGraphicsEffect(fe);
177  //   fe->activate();
178  //   PRINTF(0)("--------------------------------------------------------------\n");
179
180  //LenseFlare* ge = new LenseFlare();
181  //this->loadGraphicsEffect(ge);
182
183  //ge->addFlare("pictures/lense_flare/sun.png"); //sun
184  //ge->addFlare("pictures/lense_flare/lens2.png"); //first halo
185  //ge->addFlare("pictures/lense_flare/lens1.png"); //small birst
186  //ge->addFlare("pictures/lense_flare/lens3.png"); //second halo
187  //ge->addFlare("pictures/lense_flare/lens4.png");
188  //ge->addFlare("pictures/lense_flare/lens1.png");
189  //ge->addFlare("pictures/lense_flare/lens3.png");
190
191  //ge->activate();
192}
193
194
195
196/**
197 * @brief initializes the Video for openGL.
198 *
199 * This has to be done only once when starting orxonox.
200 */
201int GraphicsEngine::initVideo(unsigned int resX, unsigned int resY, unsigned int bbp)
202{
203  if (this->isInit)
204    return -1;
205  //   initialize SDL_VIDEO
206  if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1)
207  {
208    PRINTF(1)("could not initialize SDL Video\n");
209    return -1;
210  }
211  // initialize SDL_GL-settings
212  this->setGLattribs();
213
214  // setting the Video Flags.
215  this->videoFlags = SDL_OPENGL | SDL_HWPALETTE | SDL_RESIZABLE ;
216
217  /* query SDL for information about our video hardware */
218  const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo ();
219  if( videoInfo == NULL)
220  {
221    PRINTF(1)("Failed getting Video Info :%s\n", SDL_GetError());
222    SDL_Quit ();
223  }
224  if( videoInfo->hw_available)
225    this->videoFlags |= SDL_HWSURFACE;
226  else
227    this->videoFlags |= SDL_SWSURFACE;
228  /*
229  if(VideoInfo -> blit_hw)
230    VideoFlags |= SDL_HWACCEL;
231  */
232  // setting up the Resolution
233  this->setResolution(resX, resY, bbp);
234
235  // GRABBING ALL GL-extensions
236  this->grabHardwareSettings();
237
238  // Enable default GL stuff
239  glEnable(GL_DEPTH_TEST);
240
241  Render2D::getInstance();
242
243  this->isInit = true;
244  return 1;
245}
246
247/**
248 * @brief sets the Window Captions and the Name of the icon.
249 * @param windowName The name of the Window
250 * @param icon The name of the Icon on the Disc
251 */
252void GraphicsEngine::setWindowName(const std::string& windowName, const std::string& icon)
253{
254  SDL_Surface* iconSurf = SDL_LoadBMP(icon.c_str());
255  if (iconSurf != NULL)
256  {
257    Uint32 colorkey = SDL_MapRGB(iconSurf->format, 0, 0, 0);
258    SDL_SetColorKey(iconSurf, SDL_SRCCOLORKEY, colorkey);
259    SDL_WM_SetIcon(iconSurf, NULL);
260    SDL_FreeSurface(iconSurf);
261  }
262
263  SDL_WM_SetCaption (windowName.c_str(), icon.c_str());
264}
265
266
267/**
268 * @brief Sets the GL-attributes
269 */
270void GraphicsEngine::setGLattribs()
271{
272  // Set video mode
273  // TO DO: parse arguments for settings
274  //SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
275  //SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
276  //SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
277  //SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
278
279
280  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
281  SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16);
282  SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0);
283  SDL_GL_SetAttribute( SDL_GL_ACCUM_RED_SIZE, 0);
284  SDL_GL_SetAttribute( SDL_GL_ACCUM_GREEN_SIZE, 0);
285  SDL_GL_SetAttribute( SDL_GL_ACCUM_BLUE_SIZE, 0);
286  SDL_GL_SetAttribute( SDL_GL_ACCUM_ALPHA_SIZE, 0);
287
288  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);      //Use at least 5 bits of Red
289  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);    //Use at least 5 bits of Green
290  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);     //Use at least 5 bits of Blue
291  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);   //Use at least 16 bits for the depth buffer
292  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);  //Enable double buffering
293
294  // enable antialiasing?
295  if( this->bAntialiasing)
296  {
297    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,4);
298    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,1);
299  }
300
301  glEnable(GL_CULL_FACE);
302  glCullFace(GL_FRONT);
303}
304
305/**
306 * @brief grabs the Hardware Specifics
307 *
308 * checks for all the different HW-types
309 */
310void GraphicsEngine::grabHardwareSettings()
311{
312  const char* renderer = (const char*) glGetString(GL_RENDERER);
313  const char* vendor   = (const char*) glGetString(GL_VENDOR);
314  const char* version  = (const char*) glGetString(GL_VERSION);
315  const char* extensions = (const char*) glGetString(GL_EXTENSIONS);
316
317  //  printf("%s %s %s\n %s", renderer, vendor, version, extensions);
318
319  if (renderer != NULL)
320  {
321    this->hwRenderer == renderer;
322  }
323  if (vendor != NULL)
324  {
325    this->hwVendor == vendor;
326  }
327  if (version != NULL)
328  {
329    this->hwVersion == version;
330  }
331
332  if (extensions != NULL)
333    this->hwExtensions.split(extensions, " \n\t,");
334
335  PRINT(4)("Running on : vendor: %s,  renderer: %s,  version:%s\n", vendor, renderer, version);
336  PRINT(4)("Extensions:\n");
337  for (unsigned int i = 0; i < this->hwExtensions.size(); i++)
338    PRINT(4)("%d: %s\n", i, this->hwExtensions[i].c_str());
339
340
341  // inizializing GLEW
342  GLenum err = glewInit();
343  if (GLEW_OK != err)
344  {
345    /* Problem: glewInit failed, something is seriously wrong. */
346    PRINTF(1)("%s\n", glewGetErrorString(err));
347  }
348  PRINTF(4)("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
349}
350
351
352/**
353 * @brief sets the Resolution of the Screen to display the Graphics to.
354 * @param width The width of the window
355 * @param height The height of the window
356 * @param bpp bits per pixel
357 */
358int GraphicsEngine::setResolution(int width, int height, int bpp)
359{
360  this->resolutionX = width;
361  this->resolutionY = height;
362  this->bitsPerPixel = bpp;
363  State::setResolution( width, height);
364
365  if (this->screen != NULL)
366    SDL_FreeSurface(screen);
367  if((this->screen = SDL_SetVideoMode(this->resolutionX, this->resolutionY, this->bitsPerPixel, this->videoFlags | this->fullscreenFlag)) == NULL)
368  {
369    PRINTF(1)("Could not SDL_SetVideoMode(%d, %d, %d, %d): %s\n", this->resolutionX, this->resolutionY, this->bitsPerPixel, this->videoFlags, SDL_GetError());
370    //    SDL_Quit();
371    //    return -1;
372    return -1;
373  }
374  glViewport(0, 0, width, height);                     // Reset The Current Viewport
375
376#ifdef __WIN32__
377  // REBUILDING TEXTURES (ON WINDOWS CONTEXT SWITCH)
378  ObjectList<Texture>::const_iterator retex;
379  for (retex = Texture::objectList().begin(); retex != Texture::objectList().end(); ++retex)
380    (*retex)->rebuild();
381
382  // REBUILDING MODELS
383  ObjectList<StaticModel>::const_iterator remod;
384  for (remod = StaticModel::objectList().begin(); remod != StaticModel::objectList().end(); ++remod)
385    (*remod)->rebuild();
386
387#endif /* __WIN32__ */
388  return 1;
389}
390
391/**
392 * @brief sets Fullscreen mode
393 * @param fullscreen true if fullscreen, false if windowed
394*/
395void GraphicsEngine::setFullscreen(bool fullscreen)
396{
397  if (fullscreen)
398    this->fullscreenFlag = SDL_FULLSCREEN;
399  else
400    this->fullscreenFlag = 0;
401  this->setResolution(this->resolutionX, this->resolutionY, this->bitsPerPixel);
402}
403
404void GraphicsEngine::toggleFullscreen()
405{
406  if (this->fullscreenFlag == SDL_FULLSCREEN)
407    this->fullscreenFlag = 0;
408  else
409    this->fullscreenFlag = SDL_FULLSCREEN;
410  this->setResolution(this->resolutionX, this->resolutionY, this->bitsPerPixel);
411}
412
413
414/**
415 * @brief sets the background color
416 * @param red the red part of the background
417 * @param blue the blue part of the background
418 * @param green the green part of the background
419 * @param alpha the alpha part of the background
420 */
421void GraphicsEngine::setBackgroundColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
422{
423  glClearColor(red, green, blue, alpha);
424}
425
426/**
427 * @brief Signalhandler, for when the resolution has changed
428 * @param resizeInfo SDL information about the size of the new screen size
429 */
430void GraphicsEngine::resolutionChanged(const SDL_ResizeEvent& resizeInfo)
431{
432  this->setResolution(resizeInfo.w, resizeInfo.h, this->bitsPerPixel);
433}
434
435/**
436 * @brief entering 2D Mode
437 * this is a GL-Projection-mode, that is orthogonal, for placing the font in fron of everything else
438 */
439void GraphicsEngine::enter2DMode()
440{
441  //GraphicsEngine::storeMatrices();
442  SDL_Surface *screen = SDL_GetVideoSurface();
443
444  /* Note, there may be other things you need to change,
445     depending on how you have your OpenGL state set up.
446  */
447  glPushAttrib(GL_ENABLE_BIT);
448  glDisable(GL_DEPTH_TEST);
449  glDisable(GL_CULL_FACE);
450  glDisable(GL_LIGHTING);  // will be set back when leaving 2D-mode
451
452  glMatrixMode(GL_PROJECTION);
453  glPushMatrix();
454  glLoadIdentity();
455  glOrtho(0.0, (GLdouble)screen->w, (GLdouble)screen->h, 0.0, 0.0, 1.0);
456
457  glMatrixMode(GL_MODELVIEW);
458  glPushMatrix();
459  glLoadIdentity();
460}
461
462/**
463 * @brief leaves the 2DMode again also @see Font::enter2DMode()
464 */
465void GraphicsEngine::leave2DMode()
466{
467
468  glMatrixMode(GL_MODELVIEW);
469  glPopMatrix();
470
471  glMatrixMode(GL_PROJECTION);
472  glPopMatrix();
473
474  glPopAttrib();
475}
476
477/**
478 * @brief changes to wireframe-mode.
479 */
480void GraphicsEngine::wireframe()
481{
482  State::showWireframe(!State::showWireframe());
483  /*
484   if (State::showWireframe()){}
485       glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
486   else
487       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
488  */
489}
490
491/**
492 * @brief stores the GL_matrices
493 */
494void GraphicsEngine::storeMatrices()
495{
496  glGetDoublev(GL_PROJECTION_MATRIX, GraphicsEngine::projMat);
497  glGetDoublev(GL_MODELVIEW_MATRIX, GraphicsEngine::modMat);
498  glGetIntegerv(GL_VIEWPORT, GraphicsEngine::viewPort);
499}
500
501//! the stored ModelView Matrix.
502GLdouble GraphicsEngine::modMat[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
503//! the stored Projection Matrix
504GLdouble GraphicsEngine::projMat[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
505//! The ViewPort
506GLint GraphicsEngine::viewPort[4] = {0,0,0,0};
507
508
509
510/**
511 * @brief outputs all the Fullscreen modes.
512 */
513void GraphicsEngine::listModes()
514{
515  /* Get available fullscreen/hardware modes */
516  this->videoModes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE);
517
518  /* Check is there are any modes available */
519  if(this->videoModes == (SDL_Rect **)0)
520  {
521    PRINTF(1)("No modes available!\n");
522    exit(-1);
523  }
524
525  /* Check if our resolution is restricted */
526  if(this->videoModes == (SDL_Rect **)-1)
527  {
528    PRINTF(2)("All resolutions available.\n");
529  }
530  else
531  {
532    /* Print valid modes */
533    PRINT(0)("Available Resoulution Modes are\n");
534    for(int i = 0; this->videoModes[i]; ++i)
535      PRINT(4)(" |  %d x %d\n", this->videoModes[i]->w, this->videoModes[i]->h);
536  }
537}
538
539/**
540 * @brief checks wether a certain extension is availiable
541 * @param extension the Extension to check for (ex. GL_ARB_texture_env_dot3)
542 * @return true if it is, false otherwise
543 */
544bool GraphicsEngine::hwSupportsEXT(const std::string& extension)
545{
546  for (unsigned int i = 0; i < this->hwExtensions.size(); i++)
547    if ( this->hwExtensions.getString(i) == extension)
548      return true;
549  return false;
550}
551
552/**
553 * @brief updates everything that is to be updated in the GraphicsEngine
554 */
555void GraphicsEngine::update(float dt)
556{
557  Render2D::getInstance()->update(dt);
558}
559
560
561/**
562 * @brief ticks the Text
563 * @param dt the time passed
564 */
565void GraphicsEngine::tick(float dt)
566{
567  if( unlikely(this->bDisplayFPS))
568  {
569    this->currentFPS = 1.0/dt;
570    if( unlikely(this->currentFPS > this->maxFPS)) this->maxFPS = this->currentFPS;
571    if( unlikely(this->currentFPS < this->minFPS)) this->minFPS = this->currentFPS;
572
573#ifndef NO_TEXT
574    char tmpChar1[20];
575    sprintf(tmpChar1, "Current:  %4.0f", this->currentFPS);
576    this->geTextCFPS->setText(tmpChar1);
577    char tmpChar2[20];
578    sprintf(tmpChar2, "Max:    %4.0f", this->maxFPS);
579    this->geTextMaxFPS->setText(tmpChar2);
580    char tmpChar3[20];
581    sprintf(tmpChar3, "Min:    %4.0f", this->minFPS);
582    this->geTextMinFPS->setText(tmpChar3);
583#endif /* NO_TEXT */
584
585  }
586
587  Render2D::getInstance()->tick(dt);
588
589  // tick the graphics effects
590  for (ObjectList<GraphicsEffect>::const_iterator it = GraphicsEffect::objectList().begin();
591       it != GraphicsEffect::objectList().end();
592       ++it)
593    (*it)->tick(dt);
594}
595
596/**
597 * @brief draws all Elements that should be displayed on the Background.
598 */
599void GraphicsEngine::drawBackgroundElements() const
600{
601  GraphicsEngine::storeMatrices();
602
603  Render2D::getInstance()->draw(E2D_LAYER_BELOW_ALL, E2D_LAYER_BELOW_ALL);
604}
605
606/**
607 * this draws the graphics engines graphics effecs
608 */
609void GraphicsEngine::draw() const
610{
611  if( this->graphicsEffects != NULL)
612  {
613    //draw the graphics effects
614    std::list<BaseObject*>::const_iterator it;
615    for (it = this->graphicsEffects->begin(); it != this->graphicsEffects->end(); it++)
616      dynamic_cast<GraphicsEffect*>(*it)->draw();
617  }
618  Shader::suspendShader();
619  Render2D::getInstance()->draw(E2D_LAYER_BOTTOM, E2D_LAYER_ABOVE_ALL);
620  Shader::restoreShader();
621}
622
623
624void GraphicsEngine::toggleFPSdisplay()
625{
626  this->displayFPS(!this->bDisplayFPS);
627}
628
629
630/**
631 * @brief displays the Frames per second
632 * @param display if the text should be displayed
633*/
634void GraphicsEngine::displayFPS(bool display)
635{
636#ifndef NO_TEXT
637  if( display )
638  {
639    if (this->geTextCFPS == NULL)
640    {
641      this->geTextCFPS = new Text("fonts/final_frontier.ttf", 15);
642      this->geTextCFPS->setName("curFPS");
643      this->geTextCFPS->setAlignment(TEXT_ALIGN_LEFT);
644      this->geTextCFPS->setAbsCoor2D(5, 0);
645    }
646    if (this->geTextMaxFPS == NULL)
647    {
648      this->geTextMaxFPS = new Text("fonts/final_frontier.ttf", 15);
649      this->geTextMaxFPS->setName("MaxFPS");
650      this->geTextMaxFPS->setAlignment(TEXT_ALIGN_LEFT);
651      this->geTextMaxFPS->setAbsCoor2D(5, 20);
652    }
653    if (this->geTextMinFPS == NULL)
654    {
655      this->geTextMinFPS = new Text("fonts/final_frontier.ttf", 15);
656      this->geTextMinFPS->setName("MinFPS");
657      this->geTextMinFPS->setAlignment(TEXT_ALIGN_LEFT);
658      this->geTextMinFPS->setAbsCoor2D(5, 40);
659    }
660  }
661  else
662  {
663    delete this->geTextCFPS;
664    this->geTextCFPS = NULL;
665    delete this->geTextMaxFPS;
666    this->geTextMaxFPS = NULL;
667    delete this->geTextMinFPS;
668    this->geTextMinFPS = NULL;
669  }
670  this->bDisplayFPS = display;
671#else
672  this->bDisplayFPS = false;
673#endif /* NO_TEXT */
674}
675
676
677/**
678 * @brief processes the events for the GraphicsEngine class
679 * @param the event to handle
680 */
681void GraphicsEngine::process(const Event &event)
682{
683  switch (event.type)
684  {
685    case EV_VIDEO_RESIZE:
686    this->resolutionChanged(event.resize);
687    break;
688  }
689}
Note: See TracBrowser for help on using the repository browser.