Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/orxonox/overlays/console/InGameConsole.cc @ 1638

Last change on this file since 1638 was 1638, checked in by rgrieder, 16 years ago

merged input branch into gui test branch (was about time)
svn save (it's still a mess and CMLs haven't been updated)
I'll have to create a special project to create the tolua_bind files for tolua itself anyway..

  • Property svn:eol-style set to native
File size: 22.4 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Felix Schulthess
24 *   Co-authors:
25 *      Fabian 'x3n' Landau
26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30
31#include "InGameConsole.h"
32
33#include <string>
34#include <OgreOverlay.h>
35#include <OgreOverlayElement.h>
36#include <OgreOverlayManager.h>
37#include <OgreOverlayContainer.h>
38#include <OgreFontManager.h>
39#include <OgreFont.h>
40
41#include "util/Math.h"
42#include "util/Convert.h"
43#include "core/Debug.h"
44#include "core/CoreIncludes.h"
45#include "core/ConfigValueIncludes.h"
46#include "core/ConsoleCommand.h"
47#include "core/input/InputManager.h"
48#include "GraphicsEngine.h"
49
50#define LINES 30
51#define CHAR_WIDTH 7.45 // fix this please - determine the char-width dynamically
52
53namespace orxonox
54{
55    SetConsoleCommand(InGameConsole, openConsole, true);
56    SetConsoleCommand(InGameConsole, closeConsole, true);
57
58    /**
59        @brief Constructor: Creates and initializes the InGameConsole.
60    */
61    InGameConsole::InGameConsole()
62        : consoleOverlay_(0)
63        , consoleOverlayContainer_(0)
64        , consoleOverlayNoise_(0)
65        , consoleOverlayCursor_(0)
66        , consoleOverlayBorder_(0)
67        , consoleOverlayTextAreas_(0)
68        , emptySceneManager_(0)
69        , emptyCamera_(0)
70        , viewport_(0)
71    {
72        RegisterObject(InGameConsole);
73
74        this->bActive_ = false;
75        this->cursor_ = 0.0f;
76        this->cursorSymbol_ = '|';
77        this->inputWindowStart_ = 0;
78        this->numLinesShifted_ = LINES - 1;
79        // for the beginning, don't scroll
80        this->scroll_ = 0;
81
82        this->setConfigValues();
83    }
84
85    /**
86        @brief Destructor: Destroys the TextAreas.
87    */
88    InGameConsole::~InGameConsole(void)
89    {
90        this->destroy();
91    }
92
93    /**
94        @brief Returns a reference to the only existing instance of InGameConsole.
95    */
96    InGameConsole& InGameConsole::getInstance()
97    {
98        static InGameConsole instance;
99        return instance;
100    }
101
102    /**
103        @brief Sets the config values, describing the size of the console.
104    */
105    void InGameConsole::setConfigValues()
106    {
107        SetConfigValue(relativeWidth, 0.8);
108        SetConfigValue(relativeHeight, 0.4);
109        SetConfigValue(blinkTime, 0.5);
110        SetConfigValue(scrollSpeed_, 3.0f);
111        SetConfigValue(noiseSize_, 1.0f);
112        SetConfigValue(cursorSymbol_, '|');
113    }
114
115    /**
116        @brief Initializes the InGameConsole.
117    */
118    void InGameConsole::initialise()
119    {
120        // create overlay and elements
121        Ogre::OverlayManager* ovMan = Ogre::OverlayManager::getSingletonPtr();
122
123        // create actual overlay
124        this->consoleOverlay_ = ovMan->create("InGameConsoleConsole");
125
126        // create a container
127        this->consoleOverlayContainer_ = static_cast<Ogre::OverlayContainer*>(ovMan->createOverlayElement("Panel", "InGameConsoleContainer"));
128        this->consoleOverlayContainer_->setMetricsMode(Ogre::GMM_RELATIVE);
129        this->consoleOverlayContainer_->setPosition((1 - this->relativeWidth) / 2, 0);
130        this->consoleOverlayContainer_->setDimensions(this->relativeWidth, this->relativeHeight);
131        this->consoleOverlay_->add2D(this->consoleOverlayContainer_);
132
133        // create BorderPanel
134        this->consoleOverlayBorder_ = static_cast<Ogre::BorderPanelOverlayElement*>(ovMan->createOverlayElement("BorderPanel", "InGameConsoleBorderPanel"));
135        this->consoleOverlayBorder_->setMetricsMode(Ogre::GMM_PIXELS);
136        this->consoleOverlayBorder_->setMaterialName("ConsoleCenter");
137        this->consoleOverlayBorder_->setBorderSize(16, 16, 0, 16);
138        this->consoleOverlayBorder_->setBorderMaterialName("ConsoleBorder");
139        this->consoleOverlayBorder_->setLeftBorderUV(0.0, 0.49, 0.5, 0.51);
140        this->consoleOverlayBorder_->setRightBorderUV(0.5, 0.49, 1.0, 0.5);
141        this->consoleOverlayBorder_->setBottomBorderUV(0.49, 0.5, 0.51, 1.0);
142        this->consoleOverlayBorder_->setBottomLeftBorderUV(0.0, 0.5, 0.5, 1.0);
143        this->consoleOverlayBorder_->setBottomRightBorderUV(0.5, 0.5, 1.0, 1.0);
144        this->consoleOverlayContainer_->addChild(this->consoleOverlayBorder_);
145
146        // create a new font to match the requested size exactly
147        Ogre::FontPtr font = static_cast<Ogre::FontPtr>
148            (Ogre::FontManager::getSingleton().create("MonofurConsole", "General"));
149        font->setType(Ogre::FT_TRUETYPE);
150        font->setSource("Monofur.ttf");
151        font->setTrueTypeSize(18);
152        // reto: I don't know why, but setting the resolution twice as high makes the font look a lot clearer
153        font->setTrueTypeResolution(192);
154        font->addCodePointRange(Ogre::Font::CodePointRange(33, 126));
155        font->addCodePointRange(Ogre::Font::CodePointRange(161, 255));
156
157        // create the text lines
158        this->consoleOverlayTextAreas_ = new Ogre::TextAreaOverlayElement*[LINES];
159        for (int i = 0; i < LINES; i++)
160        {
161            this->consoleOverlayTextAreas_[i] = static_cast<Ogre::TextAreaOverlayElement*>(ovMan->createOverlayElement("TextArea", "InGameConsoleTextArea" + convertToString(i)));
162            this->consoleOverlayTextAreas_[i]->setMetricsMode(Ogre::GMM_PIXELS);
163            this->consoleOverlayTextAreas_[i]->setFontName("MonofurConsole");
164            this->consoleOverlayTextAreas_[i]->setCharHeight(18);
165            this->consoleOverlayTextAreas_[i]->setParameter("colour_top", "0.21 0.69 0.21");
166            this->consoleOverlayTextAreas_[i]->setLeft(8);
167            this->consoleOverlayTextAreas_[i]->setCaption("");
168            this->consoleOverlayContainer_->addChild(this->consoleOverlayTextAreas_[i]);
169        }
170
171        // create cursor (also a text area overlay element)
172        this->consoleOverlayCursor_ = static_cast<Ogre::TextAreaOverlayElement*>(ovMan->createOverlayElement("TextArea", "InGameConsoleCursor"));
173        this->consoleOverlayCursor_->setMetricsMode(Ogre::GMM_PIXELS);
174        this->consoleOverlayCursor_->setFontName("MonofurConsole");
175        this->consoleOverlayCursor_->setCharHeight(18);
176        this->consoleOverlayCursor_->setParameter("colour_top", "0.21 0.69 0.21");
177        this->consoleOverlayCursor_->setLeft(7);
178        this->consoleOverlayCursor_->setCaption(std::string(this->cursorSymbol_, 1));
179        this->consoleOverlayContainer_->addChild(this->consoleOverlayCursor_);
180
181        // create noise
182        this->consoleOverlayNoise_ = static_cast<Ogre::PanelOverlayElement*>(ovMan->createOverlayElement("Panel", "InGameConsoleNoise"));
183        this->consoleOverlayNoise_->setMetricsMode(Ogre::GMM_PIXELS);
184        this->consoleOverlayNoise_->setPosition(5,0);
185        this->consoleOverlayNoise_->setMaterialName("ConsoleNoiseSmall");
186        // comment following line to disable noise
187        this->consoleOverlayContainer_->addChild(this->consoleOverlayNoise_);
188
189        this->windowResized(GraphicsEngine::getSingleton().getWindowWidth(), GraphicsEngine::getSingleton().getWindowHeight());
190
191        // move overlay "above" the top edge of the screen
192        // we take -1.2 because the border makes the panel bigger
193        this->consoleOverlayContainer_->setTop(-1.2 * this->relativeHeight);
194
195        Shell::getInstance().addOutputLevel(true);
196
197        // create a sceneManager in order to render in our own viewport
198        this->emptySceneManager_ = Ogre::Root::getSingleton()
199            .createSceneManager(Ogre::ST_GENERIC, "Console/EmptySceneManager");
200        this->emptyCamera_ = this->emptySceneManager_->createCamera("Console/EmptyCamera");
201        this->viewport_ = GraphicsEngine::getSingleton().getRenderWindow()->addViewport(emptyCamera_, 10);
202        this->viewport_->setOverlaysEnabled(true);
203        this->viewport_->setClearEveryFrame(false);
204
205        COUT(4) << "Info: InGameConsole initialized" << std::endl;
206    }
207
208    /**
209        @brief Destroys all the elements if necessary.
210    */
211    void InGameConsole::destroy()
212    {
213        Ogre::OverlayManager* ovMan = Ogre::OverlayManager::getSingletonPtr();
214        if (ovMan)
215        {
216            if (this->consoleOverlayNoise_)
217                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayNoise_);
218            if (this->consoleOverlayCursor_)
219                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayCursor_);
220            if (this->consoleOverlayBorder_)
221                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayBorder_);
222            if (this->consoleOverlayTextAreas_)
223            {
224                for (int i = 0; i < LINES; i++)
225                {
226                    if (this->consoleOverlayTextAreas_[i])
227                      Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayTextAreas_[i]);
228                    this->consoleOverlayTextAreas_[i] = 0;
229                }
230
231            }
232            if (this->consoleOverlayContainer_)
233                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayContainer_);
234        }
235        if (this->consoleOverlayTextAreas_)
236        {
237            delete[] this->consoleOverlayTextAreas_;
238            this->consoleOverlayTextAreas_ = 0;
239        }
240    }
241
242    // ###############################
243    // ###  ShellListener methods  ###
244    // ###############################
245
246    /**
247        @brief Called if all output-lines have to be redrawn.
248    */
249    void InGameConsole::linesChanged()
250    {
251        std::list<std::string>::const_iterator it = Shell::getInstance().getNewestLineIterator();
252        int max = 0;
253        for (int i = 1; i < LINES; ++i)
254        {
255            if (it != Shell::getInstance().getEndIterator())
256            {
257                ++it;
258                max = i;
259            }
260            else
261                break;
262        }
263
264        for (int i = LINES - 1; i > max; --i)
265            this->print("", i, true);
266
267        for (int i = max; i >= 1; --i)
268        {
269            --it;
270            this->print(*it, i, true);
271        }
272    }
273
274    /**
275        @brief Called if only the last output-line has changed.
276    */
277    void InGameConsole::onlyLastLineChanged()
278    {
279        if (LINES > 1)
280            this->print(*Shell::getInstance().getNewestLineIterator(), 1);
281    }
282
283    /**
284        @brief Called if a new output-line was added.
285    */
286    void InGameConsole::lineAdded()
287    {
288        this->numLinesShifted_ = 0;
289        this->shiftLines();
290        this->onlyLastLineChanged();
291    }
292
293    /**
294        @brief Called if the text in the input-line has changed.
295    */
296    void InGameConsole::inputChanged()
297    {
298        if (LINES > 0)
299            this->print(Shell::getInstance().getInput(), 0);
300
301        if (Shell::getInstance().getInput() == "" || Shell::getInstance().getInput().size() == 0)
302            this->inputWindowStart_ = 0;
303    }
304
305    /**
306        @brief Called if the position of the cursor in the input-line has changed.
307    */
308    void InGameConsole::cursorChanged()
309    {
310        unsigned int pos = Shell::getInstance().getCursorPosition() - inputWindowStart_;
311        if (pos > maxCharsPerLine_)
312            pos = maxCharsPerLine_;
313        else if (pos < 0)
314            pos = 0;
315
316        this->consoleOverlayCursor_->setCaption(std::string(pos,' ') + cursorSymbol_);
317        this->consoleOverlayCursor_->setTop((int) this->windowH_ * this->relativeHeight - 24);
318    }
319
320    /**
321        @brief Called if the console gets closed.
322    */
323    void InGameConsole::exit()
324    {
325        this->deactivate();
326    }
327
328    // ###############################
329    // ###  other external calls   ###
330    // ###############################
331
332    /**
333        @brief Used to control the actual scrolling and the cursor.
334    */
335    void InGameConsole::tick(float dt)
336    {
337        if (this->scroll_ != 0)
338        {
339            float oldTop = this->consoleOverlayContainer_->getTop();
340
341            if (this->scroll_ > 0)
342            {
343                // scrolling down
344                // enlarge oldTop a little bit so that this exponential function
345                // reaches 0 before infinite time has passed...
346                float deltaScroll = (oldTop - 0.01) * dt * this->scrollSpeed_;
347                if (oldTop - deltaScroll >= 0)
348                {
349                    // window has completely scrolled down
350                    this->consoleOverlayContainer_->setTop(0);
351                    this->scroll_ = 0;
352                }
353                else
354                    this->consoleOverlayContainer_->setTop(oldTop - deltaScroll);
355            }
356
357            else
358            {
359                // scrolling up
360                // note: +0.01 for the same reason as when scrolling down
361                float deltaScroll = (1.2 * this->relativeHeight + 0.01 + oldTop) * dt * this->scrollSpeed_;
362                if (oldTop - deltaScroll <= -1.2 * this->relativeHeight)
363                {
364                    // window has completely scrolled up
365                    this->consoleOverlayContainer_->setTop(-1.2 * this->relativeHeight);
366                    this->scroll_ = 0;
367                    this->consoleOverlay_->hide();
368                }
369                else
370                    this->consoleOverlayContainer_->setTop(oldTop - deltaScroll);
371            }
372        }
373
374        if (this->bActive_)
375        {
376            this->cursor_ += dt;
377            if (this->cursor_ >= this->blinkTime)
378            {
379                this->cursor_ = 0;
380                bShowCursor_ = !bShowCursor_;
381                if (bShowCursor_)
382                    this->consoleOverlayCursor_->show();
383                else
384                    this->consoleOverlayCursor_->hide();
385            }
386
387            // this creates a flickering effect (extracts exactly 80% of the texture at a random location)
388            float uRand = (rand() & 1023) / 1023.0f * 0.2f;
389            float vRand = (rand() & 1023) / 1023.0f * 0.2f;
390            this->consoleOverlayNoise_->setUV(uRand, vRand, 0.8f + uRand, 0.8f + vRand);
391        }
392    }
393
394    /**
395        @brief Resizes the console elements. Call if window size changes.
396    */
397    void InGameConsole::windowResized(int newWidth, int newHeight)
398    {
399        this->windowW_ = newWidth;
400        this->windowH_ = newHeight;
401        this->consoleOverlayBorder_->setWidth((int) this->windowW_* this->relativeWidth);
402        this->consoleOverlayBorder_->setHeight((int) this->windowH_ * this->relativeHeight);
403        this->consoleOverlayNoise_->setWidth((int) this->windowW_ * this->relativeWidth - 10);
404        this->consoleOverlayNoise_->setHeight((int) this->windowH_ * this->relativeHeight - 5);
405        this->consoleOverlayNoise_->setTiling(consoleOverlayNoise_->getWidth() / (50.0f * this->noiseSize_), consoleOverlayNoise_->getHeight() / (50.0f * this->noiseSize_));
406
407        // now adjust the text lines...
408        this->desiredTextWidth_ = (int) (this->windowW_ * this->relativeWidth) - 12;
409
410        if (LINES > 0)
411            this->maxCharsPerLine_ = max((unsigned int)10, (unsigned int) ((float)this->desiredTextWidth_ / CHAR_WIDTH));
412        else
413            this->maxCharsPerLine_ = 10;
414
415        for (int i = 0; i < LINES; i++)
416        {
417            this->consoleOverlayTextAreas_[i]->setWidth(this->desiredTextWidth_);
418            this->consoleOverlayTextAreas_[i]->setTop((int) this->windowH_ * this->relativeHeight - 24 - 14*i);
419        }
420
421        this->linesChanged();
422        this->cursorChanged();
423    }
424
425    // ###############################
426    // ###    internal methods     ###
427    // ###############################
428
429    /**
430        @brief Prints string to bottom line.
431        @param s String to be printed
432    */
433    void InGameConsole::print(const std::string& text, int index, bool alwaysShift)
434    {
435        char level = 0;
436        if (text.size() > 0)
437            level = text[0];
438
439        std::string output = text;
440
441        if (level >= -1 && level <= 5)
442            output.erase(0, 1);
443
444        if (LINES > index)
445        {
446            this->colourLine(level, index);
447
448            if (index > 0)
449            {
450                unsigned int linesUsed = 1;
451                while (output.size() > this->maxCharsPerLine_)
452                {
453                    ++linesUsed;
454                    this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output.substr(0, this->maxCharsPerLine_)));
455                    output.erase(0, this->maxCharsPerLine_);
456                    output.insert(0, 1, ' ');
457                    if (linesUsed > numLinesShifted_ || alwaysShift)
458                        this->shiftLines();
459                    this->colourLine(level, index);
460                }
461                this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output));
462                this->displayedText_ = output;
463                this->numLinesShifted_ = linesUsed;
464            }
465            else
466            {
467                if (output.size() > this->maxCharsPerLine_)
468                {
469                    if (Shell::getInstance().getInputBuffer().getCursorPosition() < this->inputWindowStart_)
470                        this->inputWindowStart_ = Shell::getInstance().getInputBuffer().getCursorPosition();
471                    else if (Shell::getInstance().getInputBuffer().getCursorPosition() >= (this->inputWindowStart_ + this->maxCharsPerLine_ - 1))
472                        this->inputWindowStart_ = Shell::getInstance().getInputBuffer().getCursorPosition() - this->maxCharsPerLine_ + 1;
473
474                    output = output.substr(this->inputWindowStart_, this->maxCharsPerLine_);
475                }
476                else
477                  this->inputWindowStart_ = 0;
478                this->displayedText_ = output;
479                this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output));
480            }
481        }
482    }
483
484    /**
485        @brief Shows the InGameConsole.
486    */
487    void InGameConsole::activate()
488    {
489        if (!this->bActive_)
490        {
491            this->bActive_ = true;
492            InputManager::requestEnterState("console");
493            Shell::getInstance().registerListener(this);
494
495            this->windowResized(this->windowW_, this->windowH_);
496            this->linesChanged();
497            this->cursorChanged();
498            this->consoleOverlay_->show();
499
500            // scroll down
501            this->scroll_ = 1;
502            // the rest is done by tick
503        }
504    }
505
506    /**
507    @brief Hides the InGameConsole.
508    */
509    void InGameConsole::deactivate()
510    {
511        if (this->bActive_)
512        {
513            this->bActive_ = false;
514            InputManager::requestLeaveState("console");
515            Shell::getInstance().unregisterListener(this);
516
517            // scroll up
518            this->scroll_ = -1;
519            // the rest is done by tick
520        }
521    }
522
523    /**
524        @brief Shifts all output lines one line up
525    */
526    void InGameConsole::shiftLines()
527    {
528        for (unsigned int i = LINES - 1; i > 1; --i)
529        {
530            this->consoleOverlayTextAreas_[i]->setCaption(this->consoleOverlayTextAreas_[i - 1]->getCaption());
531            this->consoleOverlayTextAreas_[i]->setColourTop(this->consoleOverlayTextAreas_[i - 1]->getColourTop());
532            this->consoleOverlayTextAreas_[i]->setColourBottom(this->consoleOverlayTextAreas_[i - 1]->getColourBottom());
533        }
534    }
535
536    void InGameConsole::colourLine(int colourcode, int index)
537    {
538        if (colourcode == -1)
539        {
540            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.90, 0.90, 0.90, 1.00));
541            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 1.00, 1.00, 1.00));
542        }
543        else if (colourcode == 1)
544        {
545            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.95, 0.25, 0.25, 1.00));
546            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.50, 0.50, 1.00));
547        }
548        else if (colourcode == 2)
549        {
550            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.95, 0.50, 0.20, 1.00));
551            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.70, 0.50, 1.00));
552        }
553        else if (colourcode == 3)
554        {
555            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.50, 0.50, 0.95, 1.00));
556            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 0.80, 1.00, 1.00));
557        }
558        else if (colourcode == 4)
559        {
560            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.65, 0.48, 0.44, 1.00));
561            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.90, 0.90, 1.00));
562        }
563        else if (colourcode == 5)
564        {
565            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.40, 0.20, 0.40, 1.00));
566            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 0.60, 0.80, 1.00));
567        }
568        else
569        {
570            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.21, 0.69, 0.21, 1.00));
571            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 1.00, 0.80, 1.00));
572        }
573    }
574
575    // ###############################
576    // ###      satic methods      ###
577    // ###############################
578
579    /**
580        @brief Activates the console.
581    */
582    /*static*/ void InGameConsole::openConsole()
583    {
584        InGameConsole::getInstance().activate();
585    }
586
587    /**
588        @brief Deactivates the console.
589    */
590    /*static*/ void InGameConsole::closeConsole()
591    {
592        InGameConsole::getInstance().deactivate();
593    }
594
595    /**
596        @brief Converts a string into an Ogre::UTFString.
597        @param s The string to convert
598        @return The converted string
599    */
600    /*static*/ Ogre::UTFString InGameConsole::convert2UTF(std::string s)
601    {
602        Ogre::UTFString utf;
603        Ogre::UTFString::code_point cp;
604        for (unsigned int i = 0; i < s.size(); ++i)
605        {
606          cp = s[i];
607          cp &= 0xFF;
608          utf.append(1, cp);
609        }
610        return utf;
611    }
612}
Note: See TracBrowser for help on using the repository browser.