root/OpenSceneGraph/trunk/src/osgWidget/Input.cpp @ 13041

Revision 13041, 19.6 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1// -*-c++-*- osgWidget - Code by: Jeremy Moles (cubicool) 2007-2008
2
3#include <osg/io_utils>
4#include <osgWidget/WindowManager>
5#include <osgWidget/Input>
6#include <iterator>
7
8#ifdef WIN32
9#include <windows.h>
10#endif
11
12namespace osgWidget {
13
14class BlinkCursorCallback: public osg::Drawable::DrawCallback
15{
16public:
17    BlinkCursorCallback(const bool& insertMode)
18        : _insertMode(insertMode)
19    {
20    }
21
22    virtual void drawImplementation( osg::RenderInfo & ri,const osg::Drawable* drawable ) const
23    {
24        static bool on = true;
25        static osg::Timer_t startTime = osg::Timer::instance()->tick();
26        osg::Timer_t now = osg::Timer::instance()->tick();
27
28        if (osg::Timer::instance()->delta_s(startTime,now)>(_insertMode?0.125:0.25))
29        {
30            on = !on;
31            startTime = now;
32        }
33        if (on)
34            drawable->drawImplementation(ri);
35    }
36protected:
37    const bool&    _insertMode;
38};
39
40Input::Input(const std::string& name, const std::string& label, unsigned int size):
41    Label(name, label),
42    _xoff(0.0f),
43    _yoff(0.0f),
44    _index(0),
45    _size(0),
46    _cursorIndex(0),
47    _maxSize(size),
48    _cursor(new Widget("cursor")),
49    _insertMode(false),
50    _selection(new Widget("selection")),
51    _selectionStartIndex(0),
52    _selectionEndIndex(0),
53    _selectionIndex(0),
54    _mouseClickX(0)
55{
56   _text->setAlignment(osgText::Text::LEFT_BOTTOM_BASE_LINE);
57   _text->setKerningType(osgText::KERNING_NONE);
58
59   // Make the cursor un-copyable.
60   _cursor->setCanClone(false);
61   _cursor->setDataVariance(osg::Object::DYNAMIC);
62   _cursor->setColor(0.0f, 0.0f, 0.0f, 1.0f);
63
64   _selection->setCanClone(false);
65   _selection->setDataVariance(osg::Object::DYNAMIC);
66
67   setEventMask(
68       // For showing/hiding the "cursor."
69       EVENT_MASK_FOCUS |
70       // For keypresses, obviously.
71       EVENT_MASK_KEY |
72       // For "click" focusing.
73       EVENT_MOUSE_PUSH |
74       EVENT_MASK_MOUSE_DRAG
75   );
76
77   _offsets.resize(_text->getText().size()+1, 0.0f);
78   _widths.resize(_text->getText().size()+1, 1.0f);
79
80   _text->update();
81
82   _cursor->setDrawCallback( new BlinkCursorCallback(_insertMode) );
83}
84
85void Input::_calculateSize(const XYCoord& size) {
86   // An Input cannot currently set it's own size RELIABLY until the osgText implementation
87   // is dratiscally improved. I'm getting wildly crazy results. :(
88   // point_type height = size.y() > _cursor->getHeight() ? size.y() : _cursor->getHeight();
89
90#if 0
91   point_type width  = size.x() + _cursor->getWidth();
92   point_type height = _cursor->getHeight();
93
94   if(width > getWidth()) setWidth(osg::round(width));
95
96   if(height > getHeight()) setHeight(osg::round(height));
97#endif
98}
99
100void Input::_calculateCursorOffsets() {
101   // Determine the "offset"
102
103   _offsets.resize(_text->getText().size()+1, 0.0f);
104   _widths.resize(_text->getText().size()+1, 1.0f);
105
106    if (_text->getText().size()==0)
107    {
108        _offsets[0] = 0;
109        _widths[0] = 1.f;
110        return;
111    }
112
113    osg::Vec3 pos = _text->getPosition();
114
115    osgText::Text::TextureGlyphQuadMap& tgqm = const_cast<osgText::Text::TextureGlyphQuadMap&>(_text->getTextureGlyphQuadMap());
116    osgText::Text::TextureGlyphQuadMap::iterator tgqmi = tgqm.begin();
117
118    std::vector<osg::Vec2>                coords;
119    std::vector<osgText::Glyph*>    glyphs;
120    for ( ; tgqmi != tgqm.end(); tgqmi++ )
121    {
122        const osgText::Text::GlyphQuads& gq = tgqmi->second;
123
124        //coords.insert(coords.end(),gq.getTransformedCoords(0).begin(),gq.getTransformedCoords(0).end());
125        coords.insert(coords.end(),gq.getCoords().begin(),gq.getCoords().end());
126        for (unsigned int i=0; i<gq.getGlyphs().size(); ++i)
127        {
128            glyphs.push_back(gq.getGlyphs().at(i));
129        }
130    }
131
132    std::list<unsigned int> keys;
133    for (unsigned int i=0; i<_text->getText().size(); ++i)
134    {
135        keys.push_back(_text->getText().at(i));
136    }
137    unsigned int idx=0;
138    osg::Vec2 lr;
139    osg::Vec2 ll;
140    while (!keys.empty())
141    {
142        unsigned int key = keys.front();
143        for (unsigned int i=0; i<glyphs.size(); ++i)
144        {
145            static osgText::Glyph* previous_g = 0;
146
147            osgText::Glyph* g = glyphs.at(i);
148            if (g->getGlyphCode()==key)
149            {
150                lr = coords[2 + (i * 4)];
151                ll = coords[1 + (i * 4)];
152
153                point_type width = lr.x() - ll.x();
154                _widths[idx] = width == 0 ? g->getHorizontalAdvance() : width;
155
156                _offsets[idx] = lr.x() + pos.x();
157
158                if (width == 0)
159                    _offsets[idx] += g->getHorizontalAdvance();
160                ++idx;
161
162                if (previous_g)
163                {
164                    {
165                        point_type& ref = _offsets[idx];
166                        ref += previous_g->getHorizontalAdvance();
167                    }
168                    {
169                        point_type& ref = _widths[idx];
170                        ref += previous_g->getHorizontalAdvance();
171                    }
172                }
173                previous_g = g;
174
175                glyphs.erase(glyphs.begin()+i);
176                coords.erase(coords.begin()+i*4);
177                coords.erase(coords.begin()+i*4);
178                coords.erase(coords.begin()+i*4);
179                coords.erase(coords.begin()+i*4);
180                break;
181            }
182        }
183        keys.pop_front();
184    }
185
186    _offsets[idx] = lr.x() + pos.x();
187    _widths[idx] = 1.f;
188
189    _wordsOffsets.clear();
190    for ( unsigned int i=0; i<_text->getText().size(); ++i )
191    {
192        while (i<_text->getText().size() && _text->getText().at(i)==' ') ++i;
193        if (i<_text->getText().size())_wordsOffsets.push_back(i);
194        while (i<_text->getText().size() && _text->getText().at(i)!=' ') ++i;
195    }
196
197    positioned();
198}
199
200bool Input::focus(const WindowManager*) {
201   _cursor->setColor(0.5f, 0.5f, 0.6f, 1.0f);
202   _selection->setColor(0.8f, 0.8f, 0.9f, 1.0f);
203
204   return true;
205}
206
207bool Input::unfocus(const WindowManager*) {
208   _cursor->setColor(0.0f, 0.0f, 0.0f, 0.0f);
209   _selection->setColor(0.0f, 0.0f, 0.0f, 0.0f);
210
211   return true;
212}
213
214void Input::parented(Window* parent) {
215   Label::parented(parent);
216
217   _cursor->setSize(_widths[_index], getHeight());
218
219   if(_cursorIndex) parent->getGeode()->setDrawable(_cursorIndex, _cursor.get());
220   else _cursorIndex = parent->addDrawableAndGetIndex(_cursor.get());
221
222   if(_selectionIndex) parent->getGeode()->setDrawable(_selectionIndex, _selection.get());
223   else _selectionIndex = parent->addDrawableAndGetIndex(_selection.get());
224}
225
226void Input::positioned()
227{
228    point_type ln = static_cast<point_type>(_text->getLineCount());
229
230    ln = ln == 0.0f ? 1.0f : ln;
231
232    // point_type th = (_text->getCharacterHeight() * ln) + (_text->getLineSpacing() * (ln - 1.0f));
233
234    point_type x = getX() + _xoff;
235    point_type y = getY() + _yoff;
236
237    // XYCoord size = getTextSize();
238
239    _text->setPosition(osg::Vec3(x, y, _calculateZ(LAYER_MIDDLE)));
240
241    point_type xoffset = _index > 0 ? _offsets[_index - 1] : 0.0f;
242
243    if (_insertMode)
244    {
245        if (_index < _text->getText().size())
246        {
247            _cursor->setSize(_widths[_index], getHeight());
248        }
249        else
250        {
251            // We're at the end of the string, perhaps the string is empty,
252            // so get the advance for any character, perhaps a large one, I chose 'A'.
253            osgText::Glyph* glyph = const_cast<osgText::Font*>(_text->getFont())->getGlyph(osgText::FontResolution(_text->getFontWidth(), _text->getFontHeight()), 'A');
254            _cursor->setSize(glyph->getHorizontalAdvance(), getHeight());
255        }
256    }
257    else
258    {
259        _cursor->setSize(1.f, getHeight());
260    }
261
262    _cursor->setOrigin(getX() + xoffset, getY() );
263    _cursor->setZ(_calculateZ(LAYER_MIDDLE-1));
264
265
266    unsigned int selectionMin = osg::minimum(_selectionStartIndex,_selectionEndIndex);
267    unsigned int selectionMax = osg::maximum(_selectionStartIndex,_selectionEndIndex);
268
269    if (selectionMax - selectionMin > 0)
270    {
271        point_type xstart = selectionMin > 0 ? _offsets[selectionMin - 1] : 0.0f;
272        point_type xend   = selectionMax > 0 ? _offsets[selectionMax - 1] : 0.0f;
273
274        _selection->setSize(xend-xstart, getHeight());
275        _selection->setOrigin(getX() + xstart, getY());
276        _selection->setZ(_calculateZ(LAYER_MIDDLE-2));
277    }
278    else
279    {
280        _selection->setSize(0, getHeight());
281    }
282}
283
284bool Input::keyUp(int key, int mask, const WindowManager*) {
285   return false;
286}
287
288bool Input::mouseDrag (double x, double y, const WindowManager*)
289{
290    _mouseClickX += x;
291    x = _mouseClickX;
292
293    for ( unsigned int i = 0; i < _offsets.size(); ++i )
294    {
295        point_type offset1 = i > 0 ? _offsets.at(i-1) : 0;
296        point_type offset2 = _offsets.at(i);
297        if ((x >= offset1 && x <= offset2) ||
298            i == _offsets.size() - 1)  // If we're at the last one, obviously it will be there.
299        {
300            _selectionEndIndex = _index = i;
301            positioned();
302            break;
303        }
304    }
305
306    return true;
307}
308
309bool Input::mousePush (double x, double y, const WindowManager* wm)
310{
311    double offset = getOrigin().x();
312    Window* window = getParent();
313    if (window)
314    {
315        offset += window->getOrigin().x();
316    }
317
318    x -= offset;
319    _mouseClickX = x;
320
321    for ( unsigned int i = 0; i < _offsets.size(); ++i )
322    {
323        point_type offset1 = i > 0 ? _offsets.at(i-1) : 0;
324        point_type offset2 = _offsets.at(i);
325        if ((x >= offset1 && x <= offset2) ||
326            i == _offsets.size() - 1)  // If we're at the last one, obviously it will be there.
327        {
328            _selectionStartIndex = _selectionEndIndex = _index = i;
329            positioned();
330            break;
331        }
332    }
333    return true;
334}
335
336bool Input::mouseRelease(double, double, const WindowManager*)
337{
338    return true;
339}
340
341bool Input::keyDown(int key, int mask, const WindowManager*)
342{
343   osgText::String& s = _text->getText();
344
345   switch (key)
346   {
347    case osgGA::GUIEventAdapter::KEY_Left:
348        if (mask & osgGA::GUIEventAdapter::MODKEY_CTRL)
349        {
350            bool found = false;
351            for (unsigned int i = 0; i < _wordsOffsets.size() - 1; ++i)
352            {
353                if (_wordsOffsets.at(i) < _index && _index <= _wordsOffsets.at(i+1))
354                {
355                    found = true;
356                    _index = _wordsOffsets.at(i);
357                    break;
358                }
359            }
360            if (!found && _wordsOffsets.size())
361            {
362                _index = _wordsOffsets.at(_wordsOffsets.size()-1);
363            }
364        }
365        else
366        if (_index > 0)
367        {
368            --_index;
369        }
370        if (mask & osgGA::GUIEventAdapter::MODKEY_SHIFT)
371        {
372            _selectionEndIndex = _index;
373        }
374        else
375        {
376            _selectionStartIndex = _selectionEndIndex = _index;
377        }
378        break;
379    case osgGA::GUIEventAdapter::KEY_Right:
380        if (mask & osgGA::GUIEventAdapter::MODKEY_CTRL)
381        {
382            bool found = false;
383            for (unsigned int i = 0; i < _wordsOffsets.size() - 1; ++i)
384            {
385                if (_wordsOffsets.at(i) <= _index && _index < _wordsOffsets.at(i+1))
386                {
387                    found = true;
388                    _index = _wordsOffsets.at(i+1);
389                    break;
390                }
391            }
392            if (!found && _wordsOffsets.size())
393            {
394                _index = _wordsOffsets.at(_wordsOffsets.size()-1);
395            }
396        }
397        else
398        if (_index < s.size())
399        {
400            ++_index;
401        }
402
403        if (mask & osgGA::GUIEventAdapter::MODKEY_SHIFT)
404        {
405            _selectionEndIndex = _index;
406        }
407        else
408        {
409            _selectionStartIndex = _selectionEndIndex = _index;
410        }
411        break;
412    case osgGA::GUIEventAdapter::KEY_Home:
413        _index = 0;
414        if (mask & osgGA::GUIEventAdapter::MODKEY_SHIFT)
415        {
416            _selectionEndIndex = _index;
417        }
418        else
419        {
420            _selectionStartIndex = _selectionEndIndex = _index;
421        }
422        break;
423    case osgGA::GUIEventAdapter::KEY_End:
424        _index = s.size();
425        if (mask & osgGA::GUIEventAdapter::MODKEY_SHIFT)
426        {
427            _selectionEndIndex = _index;
428        }
429        else
430        {
431            _selectionStartIndex = _selectionEndIndex = _index;
432        }
433        break;
434    case osgGA::GUIEventAdapter::KEY_Insert:
435        _insertMode = !_insertMode;
436        break;
437    case osgGA::GUIEventAdapter::KEY_Delete:
438        {
439            unsigned int deleteMin = osg::minimum(_selectionStartIndex,_selectionEndIndex);
440            unsigned int deleteMax = osg::maximum(_selectionStartIndex,_selectionEndIndex);
441
442            if (deleteMax - deleteMin > 0)
443            {
444            }
445            else if (mask & osgGA::GUIEventAdapter::MODKEY_CTRL)
446            {
447                deleteMin = 0;
448                deleteMax = 0;
449                for (unsigned int i  =0; i < _wordsOffsets.size() - 1; ++i)
450                {
451                    if (_wordsOffsets.at(i) <= _index && _index < _wordsOffsets.at(i+1))
452                    {
453                        deleteMin = _wordsOffsets.at(i);
454                        deleteMax = _wordsOffsets.at(i+1);
455                        break;
456                    }
457                }
458            }
459            else if (_index < s.size())
460            {
461                deleteMin = _index;
462                deleteMax = _index + 1;
463            }
464
465            if (deleteMin != deleteMax)
466                s.erase(s.begin() + deleteMin, s.begin() + deleteMax);
467
468            _text->update();
469
470            _calculateCursorOffsets();
471
472            _index = deleteMin;
473            _selectionStartIndex = _selectionEndIndex = _index;
474        }
475        break;
476    case osgGA::GUIEventAdapter::KEY_BackSpace:
477        {
478            unsigned int deleteMin = osg::minimum(_selectionStartIndex,_selectionEndIndex);
479            unsigned int deleteMax = osg::maximum(_selectionStartIndex,_selectionEndIndex);
480
481            if (deleteMax - deleteMin > 0)
482            {
483            }
484            else if(_index >= 1)
485            {
486                deleteMin = _index - 1;
487                deleteMax = _index;
488            }
489
490            if (deleteMin != deleteMax)
491                s.erase(s.begin() + deleteMin, s.begin() + deleteMax);
492
493            _text->update();
494
495            _calculateCursorOffsets();
496
497            _index = deleteMin;
498            _selectionStartIndex = _selectionEndIndex = _index;
499        }
500       break;
501   default:
502        if(key > 255 || _index >= _maxSize) return false;
503
504        if (((key=='v' || key=='V') && (mask & osgGA::GUIEventAdapter::MODKEY_CTRL)) || (key==22))
505        {
506            std::string data;
507// Data from clipboard
508#ifdef WIN32
509            if (::OpenClipboard(NULL))
510            {
511                HANDLE hData = ::GetClipboardData( CF_TEXT );
512                char* buff = (char*)::GlobalLock( hData );
513                if (buff) data = buff;
514                ::GlobalUnlock( hData );
515                ::CloseClipboard();
516            }
517#endif
518            if (!data.empty())
519            {
520                unsigned int deleteMin = osg::minimum(_selectionStartIndex,_selectionEndIndex);
521                unsigned int deleteMax = osg::maximum(_selectionStartIndex,_selectionEndIndex);
522
523                if (deleteMax - deleteMin > 0)
524                {
525                    data = data.substr(0, _maxSize-s.size()-(deleteMax - deleteMin));
526
527                    s.erase(s.begin() + deleteMin, s.begin() + deleteMax);
528                    std::copy(data.begin(), data.end(), std::inserter(s, s.begin() + deleteMin));
529
530                    _index = deleteMin + data.size();
531                }
532                else
533                {
534                    data = data.substr(0, _maxSize-s.size());
535
536                    std::copy(data.begin(), data.end(), std::inserter(s, s.begin() + _index));
537                    _index += data.length();
538                }
539
540                _selectionStartIndex = _selectionEndIndex = _index;
541
542                _text->update();
543
544                _calculateCursorOffsets();
545
546                _calculateSize(getTextSize());
547
548                getParent()->resize();
549
550                return false;
551            }
552        }
553        else
554        if (((key=='c' || key=='C' || key=='x' || key=='X') && (mask & osgGA::GUIEventAdapter::MODKEY_CTRL)) || (key==3) || (key==24))
555        {
556            unsigned int selectionMin = osg::minimum(_selectionStartIndex,_selectionEndIndex);
557            unsigned int selectionMax = osg::maximum(_selectionStartIndex,_selectionEndIndex);
558
559            if (selectionMax - selectionMin > 0)
560            {
561                std::string data;
562                std::copy(s.begin() + selectionMin, s.begin() + selectionMax, std::inserter(data, data.begin()));
563
564// Data to clipboard
565#ifdef WIN32
566                if(::OpenClipboard(NULL))
567                {
568                    ::EmptyClipboard();
569                    HGLOBAL clipbuffer = ::GlobalAlloc(GMEM_DDESHARE, data.length()+1);
570                    char* buffer = (char*)::GlobalLock(clipbuffer);
571                    strcpy(buffer, data.c_str());
572                    ::GlobalUnlock(clipbuffer);
573                    ::SetClipboardData(CF_TEXT,clipbuffer);
574                    ::CloseClipboard();
575                }
576#endif
577
578                if (key=='x' || key=='X' || key == 24)
579                {
580                    s.erase(s.begin() + selectionMin, s.begin() + selectionMax);
581
582                    _index = selectionMin;
583
584                    _selectionStartIndex = _selectionEndIndex = _index;
585
586                    _text->update();
587
588                    _calculateCursorOffsets();
589
590                    _calculateSize(getTextSize());
591
592                    getParent()->resize();
593                }
594            }
595            return false;
596        }
597
598        {
599            // If something is selected, we need to delete it and insert the character there.
600            unsigned int deleteMin = osg::minimum(_selectionStartIndex,_selectionEndIndex);
601            unsigned int deleteMax = osg::maximum(_selectionStartIndex,_selectionEndIndex);
602
603            if (deleteMax - deleteMin > 0)
604            {
605                s.erase(s.begin() + deleteMin, s.begin() + deleteMax);
606
607                _text->update();
608
609                _calculateCursorOffsets();
610
611                _index = deleteMin;
612                _selectionStartIndex = _selectionEndIndex = _index;
613            }
614        }
615
616        if (_insertMode && _index < s.size())
617        {
618            s[_index] = key;
619        }
620        else
621        {
622            if (_index < _maxSize)
623                s.insert(s.begin() + _index, key);
624        }
625
626        _text->update();
627
628        _calculateCursorOffsets();
629
630        _index++;
631
632        _selectionStartIndex = _selectionEndIndex = _index;
633    }
634
635    _calculateSize(getTextSize());
636
637    getParent()->resize();
638
639    return true;
640}
641
642void Input::setCursor(Widget*) {
643}
644
645unsigned int Input::calculateBestYOffset(const std::string& s)
646{
647    if (!_text->getFont()) return 0;
648
649   const osgText::FontResolution fr(static_cast<unsigned int>(_text->getCharacterHeight()),
650                                    static_cast<unsigned int>(_text->getCharacterHeight()));
651
652   osgText::String utf(s);
653
654   unsigned int descent = 0;
655
656   for(osgText::String::iterator i = utf.begin(); i != utf.end(); i++) {
657       osgText::Font*        font  = const_cast<osgText::Font*>(_text->getFont());
658       osgText::Glyph* glyph = font->getGlyph(fr, *i);
659       unsigned int          d     = abs((int)glyph->getHorizontalBearing().y());
660
661       if(d > descent) descent = d;
662   }
663
664   return descent;
665}
666
667void Input::clear()
668{
669    setLabel("");
670    _text->update();
671    _calculateCursorOffsets();
672
673    _index = 0;
674    _selectionStartIndex = _selectionEndIndex = _index;
675    _selectionIndex = _index;
676    _cursorIndex = _index;
677
678    _calculateSize(getTextSize());
679
680    getParent()->resize();
681}
682
683
684}
Note: See TracBrowser for help on using the browser.