olaf neophyte and de.po updates, valgrind tweaks, delete green lady, inkscape dpi=96
[goodguy/history.git] / cinelerra-5.1 / guicast / bctextbox.C
index cbd74e0623c45760221fe5dec44075d23d81ce04..a7c8f5f7570ff1ce45d55bb65aaf1fded2da3007 100644 (file)
 #include "bcsignals.h"
 #include "bctextbox.h"
 #include "clip.h"
-#include "colors.h"
+#include "bccolors.h"
 #include <ctype.h>
 #include "cursors.h"
 #include "filesystem.h"
 #include "keys.h"
+#include "language.h"
 #include <math.h>
 #include "bctimer.h"
 #include "vframe.h"
@@ -50,9 +51,9 @@ BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
        is_utf8 = 1;
        skip_cursor = 0;
        reset_parameters(rows, has_border, font, size);
-       if( size > 0 )
+       if( size >= 0 )
                tstrcpy(text);
-       else    // text referenced directly
+       else    // reference shared directly
                this->text = text;
 }
 
@@ -62,12 +63,9 @@ BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
 {
        is_utf8 = 1;
        skip_cursor = 0;
-       wsize = size > 0 ? size : wcslen(wtext);
-       if( size <= 0 ) size = 2*wsize;
        reset_parameters(rows, has_border, font, size);
-       this->wtext = new wchar_t[wsize+1];
+       wdemand(wcslen(wtext));
        wcsncpy(this->wtext, wtext, wsize);
-       this->wtext[wsize] = 0;
 }
 
 BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
@@ -133,9 +131,9 @@ BC_TextBox::~BC_TextBox()
        delete suggestions_popup;
        suggestions->remove_all_objects();
        delete suggestions;
-       delete [] positions;
+       delete menu;
        delete [] wtext;
-       if( size > 0 )
+       if( size >= 0 )
                delete [] text;
 }
 
@@ -148,9 +146,11 @@ int BC_TextBox::reset_parameters(int rows, int has_border, int font, int size)
        this->rows = rows;
        this->has_border = has_border;
        this->font = font;
+// size > 0: fixed buffer, size == 0: dynamic buffer
+// size < 0: fixed shared buffer via set_text
        this->size = size;
        this->tsize = size >= 0 ? size : -size;
-       this->text = size > 0 ? new char[size+1] : 0;
+       this->text = size > 0 ? new char[this->tsize+1] : 0;
        if( this->text ) this->text[0] = 0;
        text_start = 0;
        text_end = 0;
@@ -165,44 +165,44 @@ int BC_TextBox::reset_parameters(int rows, int has_border, int font, int size)
        text_x = 0;
        enabled = 1;
        highlighted = 0;
+       back_color = -1;
+       high_color = -1;
        precision = 4;
        if (!skip_cursor)
                skip_cursor = new Timer;
        wtext = 0;
        wsize = 0;
        wlen = 0;
-       positions = 0;
-       plen = 0;
        keypress_draw = 1;
        last_keypress = 0;
        separators = 0;
+       xscroll = 0;
        yscroll = 0;
+       menu = 0;
        dirty = 1;
+       selection_active = 0;
        return 0;
 }
 
 int BC_TextBox::tstrlen()
 {
-       if( !tsize ) return strlen(text);
-       return strnlen(text, tsize);
+       return !text ? 0 : strnlen(text, tsize);
 }
 
 int BC_TextBox::tstrcmp(const char *cp)
 {
-       if( !tsize ) return strcmp(text, cp);
-       return strncmp(text, cp, tsize);
+       const char *tp = get_text();
+       return strncmp(tp, cp, tsize);
 }
 
 char *BC_TextBox::tstrcpy(const char *cp)
 {
        dirty = 1;
        if( cp ) {
-               if( !tsize )
-                       return strcpy(text, cp);
+               if( !size ) tdemand(strlen(cp));
                strncpy(text, cp, tsize);
-               text[tsize-1] = 0;
        }
-       else
+       else if( text )
                text[0] = 0;
        return text;
 }
@@ -210,9 +210,8 @@ char *BC_TextBox::tstrcpy(const char *cp)
 char *BC_TextBox::tstrcat(const char *cp)
 {
        dirty = 1;
-       if( !tsize ) return strcat(text, cp);
+       if( !size ) tdemand(tstrlen() + strlen(cp));
        char *result = strncat(text, cp, tsize);
-       text[tsize-1] = 0;
        return result;
 }
 
@@ -221,19 +220,10 @@ int BC_TextBox::wtext_update()
        if( dirty ) {
                const char *src_enc = is_utf8 ? "UTF8" : BC_Resources::encoding;
                const char *dst_enc = BC_Resources::wide_encoding;
-               int nsize = tsize > 0 ? tsize : strlen(text) + BCTEXTLEN;
-               if( nsize > wsize || !wtext ) {
-                       wchar_t *ntext = new wchar_t[nsize+1];
-                       memcpy(ntext, wtext, wsize*sizeof(wtext[0]));
-                       delete [] wtext;  wtext = ntext;  wsize = nsize;
-                       int *npositions = new int[nsize+1];
-                       if( plen > 0 )
-                               memcpy(npositions, positions, (plen+1)*sizeof(positions[0]));
-                       else
-                               npositions[0] = 0;
-                       delete [] positions;  positions = npositions;  plen = nsize;
-               }
-               wlen = BC_Resources::encode(src_enc, dst_enc, text, strlen(text),
+               int tlen = tstrlen();
+               int nsize = tsize > 0 ? tsize : tlen + BCTEXTLEN;
+               wdemand(nsize);
+               wlen = BC_Resources::encode(src_enc, dst_enc, text, tlen,
                        (char*)wtext, wsize*sizeof(wchar_t)) / sizeof(wchar_t);
                dirty = 0;
        }
@@ -254,6 +244,7 @@ int BC_TextBox::text_update(const wchar_t *wcp, int wsz, char *tcp, int tsz)
 
 int BC_TextBox::initialize()
 {
+
        if (!skip_cursor)
                skip_cursor = new Timer;
        skip_cursor->update();
@@ -283,18 +274,22 @@ int BC_TextBox::initialize()
        BC_Resources *resources = get_resources();
        if(has_border)
        {
-               back_color = resources->text_background;
-               high_color = resources->text_background_hi;
+               if( back_color < 0 ) back_color = resources->text_background;
+               if( high_color < 0 ) high_color = resources->text_background_hi;
        }
        else
        {
-               high_color = resources->text_background_noborder_hi;
-               back_color = bg_color;
+               if( back_color < 0 ) back_color = bg_color;
+               if( high_color < 0 ) high_color = resources->text_background_noborder_hi;
        }
 
        draw(0);
        set_cursor(IBEAM_CURSOR, 0, 0);
        show_window(0);
+
+       add_subwindow(menu = new BC_TextMenu(this));
+       menu->create_objects();
+
        return 0;
 }
 
@@ -400,13 +395,12 @@ void BC_TextBox::set_suggestions(ArrayList<char*> *suggestions, int column)
 // Show the highlighted text
                if( suggestions->size() == 1 ) {
                        highlight_letter1 = wtext_update();
-                       text_update(wtext,wlen, text,tsize);
+                       int len = text_update(wtext,wlen, text,tsize);
                        char *current_suggestion = suggestions->get(0);
-                       int col = highlight_letter1 - suggestion_column;
+                       int col = len - suggestion_column;
                        if( col < 0 ) col = 0;
                        char *cur = current_suggestion + col;
                        tstrcat(cur);
-                       draw(0);  // update positions
                        highlight_letter2 = wtext_update();
 //printf("BC_TextBox::set_suggestions %d %d\n", __LINE__, suggestion_column);
 
@@ -455,7 +449,8 @@ int BC_TextBox::update(const char *text)
 {
 //printf("BC_TextBox::update 1 %d %s %s\n", tstrcmp(text), text, this->text);
 // Don't update if contents are the same
-       if(!tstrcmp(text)) return 0;
+       int bg_color = has_border || !highlighted ? back_color : high_color;
+       if( bg_color == background_color && !tstrcmp(text)) return 0;
        tstrcpy(text);
        int wtext_len = wtext_update();
        if(highlight_letter1 > wtext_len) highlight_letter1 = wtext_len;
@@ -468,9 +463,9 @@ int BC_TextBox::update(const char *text)
 int BC_TextBox::update(const wchar_t *wtext)
 {
        int wtext_len = wcslen(wtext);
-       if( wtext_len >= wsize ) wtext_len = wsize;
-       wcsncpy(this->wtext, wtext, wtext_len);
-       wlen = wtext_len;
+       wdemand(wtext_len);
+       wcsncpy(this->wtext, wtext, wsize);
+       this->wlen = wtext_len;
        if(highlight_letter1 > wtext_len) highlight_letter1 = wtext_len;
        if(highlight_letter2 > wtext_len) highlight_letter2 = wtext_len;
        if(ibeam_letter > wtext_len) ibeam_letter = wtext_len;
@@ -513,10 +508,13 @@ void BC_TextBox::enable()
        }
 }
 
-int BC_TextBox::get_enabled()
-{
-       return enabled;
-}
+int BC_TextBox::get_enabled() { return enabled; }
+int BC_TextBox::get_text_x() { return text_x; }
+int BC_TextBox::get_text_y() { return text_y; }
+void BC_TextBox::set_text_x(int v) { text_x = v; }
+void BC_TextBox::set_text_y(int v) { text_y = v; }
+int BC_TextBox::get_back_color() { return back_color; }
+void BC_TextBox::set_back_color(int v) { back_color = v; }
 
 int BC_TextBox::pixels_to_rows(BC_WindowBase *window, int font, int pixels)
 {
@@ -551,13 +549,14 @@ const wchar_t* BC_TextBox::get_wtext()
 
 void BC_TextBox::set_text(char *text, int isz)
 {
-       if( size > 0 || isz < 0 ) return;
-       this->text = text;
-       tsize = isz;
-       size = -isz;
-       dirty = 1;
-       wtext_update();
-       draw(1);
+       if( size < 0 && isz > 0 ) {
+               this->text = text;
+               tsize = isz;
+               size = -isz;
+               dirty = 1;
+               wtext_update();
+               draw(1);
+       }
 }
 
 int BC_TextBox::get_text_rows()
@@ -645,7 +644,7 @@ void BC_TextBox::draw_cursor()
 
 void BC_TextBox::draw(int flush)
 {
-       int i, j, k;
+       int i, k;
        int row_begin, row_end;
        int highlight_x1, highlight_x2;
        int need_ibeam = 1;
@@ -653,12 +652,7 @@ void BC_TextBox::draw(int flush)
 
 //printf("BC_TextBox::draw %d %s\n", __LINE__, text);
 // Background
-       if(has_border)
-               background_color = resources->text_background;
-       else if(highlighted)
-               background_color = high_color;
-       else
-               background_color = back_color;
+       background_color = has_border || !highlighted ? back_color : high_color;
        set_color(background_color);
        draw_box(0, 0, w, h);
 
@@ -667,11 +661,11 @@ void BC_TextBox::draw(int flush)
 // Draw text with selection
        set_font(font);
 
-       for(i=0, j=0, k=text_y; i < wtext_len && k < get_h(); k += text_height) {
+       for(i=0, k=text_y; i < wtext_len && k < get_h(); k += text_height) {
 // Draw row of text
                row_begin = i;
                wchar_t *wtext_row = &wtext[i];
-               for(j=0; j<BCTEXTLEN-1 && i<wtext_len && wtext[i]!='\n'; ++i, ++j);
+               for( ; i<wtext_len && wtext[i]!='\n'; ++i );
                if( (row_end=i) < wtext_len ) ++i;
 
                if(k > top_margin-text_height && k < get_h()-bottom_margin) {
@@ -681,19 +675,21 @@ void BC_TextBox::draw(int flush)
                            highlight_letter1 <= row_end ) {
                                int color = active && enabled && get_has_focus() ?
                                        resources->text_highlight :
+                                   selection_active ?
+                                       resources->text_selected_highlight :
                                        resources->text_inactive_highlight;
                                if( unicode_active >= 0 )
                                        color ^= LTBLUE;
                                set_color(color);
                                if(highlight_letter1 >= row_begin &&
                                        highlight_letter1 <= row_end)
-                                       highlight_x1 = positions[highlight_letter1];
+                                       highlight_x1 = get_x_position(highlight_letter1, row_begin);
                                else
                                        highlight_x1 = 0;
 
                                if(highlight_letter2 > row_begin &&
                                        highlight_letter2 <= row_end)
-                                       highlight_x2 = positions[highlight_letter2];
+                                       highlight_x2 = get_x_position(highlight_letter2, row_begin);
                                else
                                        highlight_x2 = get_w();
 
@@ -705,17 +701,14 @@ void BC_TextBox::draw(int flush)
                        int len = row_end - row_begin;
                        if( len > 0 ) {
                                set_color(enabled ? resources->text_default : DMGREY);
-                               draw_wtext(text_x, k + text_ascent, wtext_row, len,
-                                       0, &positions[wtext_row - wtext]);
+                               draw_single_text(1, font, text_x, k + text_ascent, wtext_row, len);
                        }
-                       else
-                               positions[wtext_row - wtext] = 0;
 
 // Get ibeam location
                        if(ibeam_letter >= row_begin && ibeam_letter <= row_end) {
                                need_ibeam = 0;
                                ibeam_y = k - text_y;
-                               ibeam_x = positions[ibeam_letter];
+                               ibeam_x = get_x_position(ibeam_letter, row_begin);
                        }
                }
        }
@@ -753,7 +746,6 @@ int BC_TextBox::cursor_enter_event()
        if( top_level->event_win == win && enabled &&
            !(top_level->get_resources()->textbox_focus_policy & CLICK_ACTIVATE) )
        {
-               tooltip_done = 0;
                if( !active ) {
                        top_level->deactivate();
                        activate();
@@ -777,7 +769,7 @@ int BC_TextBox::cursor_leave_event()
                draw_border();
                flash(1);
        }
-       if( !suggestions_popup && !get_buttonpress() &&
+       if( !suggestions_popup && !get_button_down() &&
            !(top_level->get_resources()->textbox_focus_policy & CLICK_DEACTIVATE) )
                deactivate();
        return 0;
@@ -788,12 +780,10 @@ int BC_TextBox::button_press_event()
        const int debug = 0;
 
        if(!enabled) return 0;
-       if(get_buttonpress() != WHEEL_UP &&
-               get_buttonpress() != WHEEL_DOWN &&
-               get_buttonpress() != LEFT_BUTTON &&
-               get_buttonpress() != MIDDLE_BUTTON) return 0;
-
-
+//     if(get_buttonpress() != WHEEL_UP &&
+//             get_buttonpress() != WHEEL_DOWN &&
+//             get_buttonpress() != LEFT_BUTTON &&
+//             get_buttonpress() != MIDDLE_BUTTON) return 0;
 
        if(debug) printf("BC_TextBox::button_press_event %d\n", __LINE__);
 
@@ -831,6 +821,11 @@ int BC_TextBox::button_press_event()
                        update_scroll = 1;
                }
                else
+               if(get_buttonpress() == RIGHT_BUTTON)
+               {
+                       menu->activate_menu();
+               }
+               else
                {
 
                cursor_letter = get_cursor_letter(top_level->cursor_x, top_level->cursor_y);
@@ -1014,7 +1009,6 @@ int BC_TextBox::repeat_event(int64_t duration)
                tooltip_text && tooltip_text[0] != 0 && highlighted)
        {
                show_tooltip();
-               tooltip_done = 1;
                result = 1;
        }
 
@@ -1126,15 +1120,28 @@ int BC_TextBox::repeat_event(int64_t duration)
 void BC_TextBox::default_keypress(int &dispatch_event, int &result)
 {
        int key = top_level->get_keypress(), len;
-       if( (key == RETURN) || ( key >= 32 && key <= 255 ) ) {
-               wchar_t *wkeys = top_level->get_wkeystring(&len);
-               if( key == RETURN ) { wkeys[0] = '\n';  wkeys[1] = 0;  len = 1; }
-               insert_text(wkeys, len);
-               find_ibeam(1);
-               draw(1);
-               dispatch_event = 1;
-               result = 1;
+       wchar_t *wkeys = top_level->get_wkeystring(&len);
+       switch( key ) {
+       case KPENTER:   key = '\n';     goto kpchr;
+       case KPMINUS:   key = '-';      goto kpchr;
+       case KPPLUS:    key = '+';      goto kpchr;
+       case KPDEL:     key = '.';      goto kpchr;
+       case RETURN:    key = '\n';     goto kpchr;
+       case KPINS:     key = '0';      goto kpchr;
+       case KP1: case KP2: case KP3: case KP4: case KP5:
+       case KP6: case KP7: case KP8: case KP9:
+               key = key - KP1 + '1';
+       kpchr: {
+               wkeys[0] = key;  wkeys[1] = 0;  len = 1;
+               break; }
+       default:
+               if( key < 32 || key > 255 ) return;
        }
+       insert_text(wkeys, len);
+       find_ibeam(1);
+       draw(1);
+       dispatch_event = 1;
+       result = 1;
 }
 
 int BC_TextBox::keypress_event()
@@ -1183,6 +1190,10 @@ int BC_TextBox::keypress_event()
                        result = 1;
                        wlen = 0;
                        break; }
+               case KPINS: last_keypress = KP1-'1'+'0'; // fall thru
+               case KP1: case KP2: case KP3: case KP4: case KP5:
+               case KP6: case KP7: case KP8: case KP9:
+                       last_keypress = last_keypress-KP1 + '1';
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
@@ -1616,31 +1627,17 @@ int BC_TextBox::keypress_event()
 
                default: {
                        if( ctrl_down() ) {
-                               switch( get_keypress() ) {
+                               switch( last_keypress ) {
                                case 'c': case 'C': {
-                                       if(highlight_letter1 != highlight_letter2) {
-                                               copy_selection(SECONDARY_SELECTION);
-                                               result = 1;
-                                       }
+                                       result = copy(0);
                                        break; }
                                case 'v': case 'V': {
-                                       paste_selection(SECONDARY_SELECTION);
-                                       find_ibeam(1);
-                                       if(keypress_draw) draw(1);
+                                       result = paste(0);
                                        dispatch_event = 1;
-                                       result = 1;
                                        break; }
                                case 'x': case 'X': {
-                                       if(highlight_letter1 != highlight_letter2) {
-                                               copy_selection(SECONDARY_SELECTION);
-                                               delete_selection(highlight_letter1, highlight_letter2, wtext_len);
-                                               highlight_letter2 = ibeam_letter = highlight_letter1;
-                                       }
-
-                                       find_ibeam(1);
-                                       if(keypress_draw) draw(1);
+                                       result = cut(0);
                                        dispatch_event = 1;
-                                       result = 1;
                                        break; }
                                case 'u': case 'U': {
                                        if( shift_down() ) {
@@ -1671,6 +1668,53 @@ int BC_TextBox::keypress_event()
 }
 
 
+int BC_TextBox::cut(int do_update)
+{
+       if( highlight_letter1 != highlight_letter2 ) {
+               int wtext_len = wtext_update();
+               copy_selection(SECONDARY_SELECTION);
+               delete_selection(highlight_letter1, highlight_letter2, wtext_len);
+               highlight_letter2 = ibeam_letter = highlight_letter1;
+       }
+
+       find_ibeam(1);
+       if( keypress_draw )
+               draw(1);
+       
+       if( do_update ) {
+               skip_cursor->update();
+               handle_event();
+       }
+       return 1;
+}
+
+int BC_TextBox::copy(int do_update)
+{
+       int result = 0;
+       if( highlight_letter1 != highlight_letter2 ) {
+               copy_selection(SECONDARY_SELECTION);
+               result = 1;
+               if( do_update ) {
+                       skip_cursor->update();
+               }
+       }
+       return result;
+}
+
+int BC_TextBox::paste(int do_update)
+{
+       paste_selection(SECONDARY_SELECTION);
+       find_ibeam(1);
+       if( keypress_draw )
+               draw(1);
+       if( do_update ) {
+               skip_cursor->update();
+               handle_event();
+       }
+       return 1;
+}
+
+
 int BC_TextBox::uses_text()
 {
        return 1;
@@ -1689,10 +1733,34 @@ void BC_TextBox::delete_selection(int letter1, int letter2, int wtext_len)
        do_separators(1);
 }
 
+int BC_TextBox::wdemand(int len)
+{
+       if( wtext && wsize >= len ) return 0;
+       int nsize = len + wlen/2 + BCTEXTLEN;
+       wchar_t *ntext = new wchar_t[nsize+1];
+       ntext[nsize] = 0;
+       memcpy(ntext, wtext, wsize*sizeof(wtext[0]));
+       delete [] wtext;  wtext = ntext;  wsize = nsize;
+       return 1;
+}
+
+int BC_TextBox::tdemand(int len)
+{
+       if( text && tsize >= len ) return 0;
+       int tlen = !text ? 0 : strlen(text);
+       int nsize = len + tlen/2 + BCTEXTLEN;
+       char *ntext = new char[nsize+1];
+       ntext[nsize] = 0;
+       memcpy(ntext, text, tsize*sizeof(text[0]));
+       delete [] text;  text = ntext;  tsize = nsize;
+       return 1;
+}
+
 void BC_TextBox::insert_text(const wchar_t *wcp, int len)
 {
        if( len < 0 ) len = wcslen(wcp);
        int wtext_len = wtext_update();
+       wdemand(wtext_len + len + 1);
        if( unicode_active < 0 && highlight_letter1 < highlight_letter2 ) {
                delete_selection(highlight_letter1, highlight_letter2, wtext_len);
                highlight_letter2 = ibeam_letter = highlight_letter1;
@@ -1700,15 +1768,11 @@ void BC_TextBox::insert_text(const wchar_t *wcp, int len)
        }
 
        int i, j;
-       for(i=wtext_len-1, j=wtext_len+len-1; i>=ibeam_letter; i--, j--) {
-               if( j >= wsize ) continue;
-               wtext[j] = wtext[i];
-       }
+       for( i=wtext_len, j=wtext_len+len; --i>=ibeam_letter; )
+               if( --j < wsize ) wtext[j] = wtext[i];
+       for( i=ibeam_letter,j=0; j<len; ++i, ++j )
+               if( i < wsize ) wtext[i] = wcp[j];
 
-       for(i = ibeam_letter, j = 0; j < len; j++, i++) {
-               if( i >= wsize ) break;
-               wtext[i] = wcp[j];
-       }
        if( (wlen+=len) > wsize ) wlen = wsize;
        if( (ibeam_letter+=len) > wsize ) ibeam_letter = wsize;
        wtext[wlen] = 0;  // wtext allocated wsize+1
@@ -1769,6 +1833,11 @@ void BC_TextBox::do_separators(int ibeam_left)
        }
 }
 
+int BC_TextBox::get_x_position(int i, int start)
+{
+       return get_text_width(font, &wtext[start], i - start);
+}
+
 void BC_TextBox::get_ibeam_position(int &x, int &y)
 {
        int i, row_begin, row_end;
@@ -1781,7 +1850,7 @@ void BC_TextBox::get_ibeam_position(int &x, int &y)
                row_end = i;
 
                if( ibeam_letter >= row_begin && ibeam_letter <= row_end ) {
-                       x = get_text_width(font,  &wtext[row_begin], ibeam_letter - row_begin);
+                       x = get_x_position(ibeam_letter, row_begin);
 //printf("BC_TextBox::get_ibeam_position %d %d %d %d %d\n", ibeam_letter, row_begin, row_end, x, y);
                        return;
                }
@@ -2015,21 +2084,30 @@ void BC_TextBox::copy_selection(int clipboard_num)
 //printf(" BC_TextBox::copy_selection %d %d %d\n",highlight_letter1, highlight_letter2, clip_len);
        char ctext[4*clip_len+4];
        clip_len = text_update(&wtext[highlight_letter1],clip_len, ctext,4*clip_len+4);
-       get_clipboard()->to_clipboard(ctext, clip_len, clipboard_num);
+       to_clipboard(ctext, clip_len, clipboard_num);
+       selection_active = 1;
 }
 
+int BC_TextBox::selection_clear_event()
+{
+       if( !is_event_win() ) return 0;
+       selection_active = 0;
+       draw(1);
+       return 1;
+}
 
 void BC_TextBox::paste_selection(int clipboard_num)
 {
-       int len = get_clipboard()->clipboard_len(clipboard_num);
+       int len = clipboard_len(clipboard_num);
        if( len > 0 )
        {
                char cstring[len];  wchar_t wstring[len];
-               get_clipboard()->from_clipboard(cstring, len, clipboard_num);  --len;
+               from_clipboard(cstring, len, clipboard_num);  --len;
 //printf("BC_TextBox::paste_selection %d '%*.*s'\n",len,len,len,cstring);
                len = BC_Resources::encode(BC_Resources::encoding, BC_Resources::wide_encoding,
                        cstring,len, (char *)wstring,(len+1)*sizeof(wchar_t)) / sizeof(wchar_t);
                insert_text(wstring, len);
+               last_keypress = 0;
        }
 }
 
@@ -2126,6 +2204,7 @@ BC_ScrollTextBox::BC_ScrollTextBox(BC_WindowBase *parent_window,
        this->y = y;
        this->w = w;
        this->rows = rows;
+       xscroll = 0;  yscroll = 0;
        this->default_text = default_text;
        this->default_wtext = 0;
        this->default_size = default_size;
@@ -2140,6 +2219,7 @@ BC_ScrollTextBox::BC_ScrollTextBox(BC_WindowBase *parent_window,
        this->y = y;
        this->w = w;
        this->rows = rows;
+       xscroll = 0;  yscroll = 0;
        this->default_text = 0;
        this->default_wtext = default_wtext;
        this->default_size = default_size;
@@ -2147,6 +2227,7 @@ BC_ScrollTextBox::BC_ScrollTextBox(BC_WindowBase *parent_window,
 
 BC_ScrollTextBox::~BC_ScrollTextBox()
 {
+       delete xscroll;
        delete yscroll;
        if(text) {
                text->gui = 0;
@@ -2160,44 +2241,32 @@ void BC_ScrollTextBox::create_objects()
        parent_window->add_subwindow(text = default_wtext ?
                new BC_ScrollTextBoxText(this, default_wtext) :
                new BC_ScrollTextBoxText(this, default_text));
-       parent_window->add_subwindow(yscroll = new BC_ScrollTextBoxYScroll(this));
-       text->yscroll = yscroll;
-       yscroll->bound_to = text;
        set_text_row(0);
 }
 
 void BC_ScrollTextBox::set_text(char *text, int isz)
 {
        this->text->set_text(text, isz);
-       yscroll->update_length(this->text->get_text_rows(),
-               this->text->get_text_row(),
-               yscroll->get_handlelength(),
-               1);
+       update_scrollbars();
 }
 
 int BC_ScrollTextBox::set_text_row(int n)
 {
        text->set_text_row(n);
-       yscroll->update_value(n);
+       update_scrollbars();
        return 1;
 }
 
 void BC_ScrollTextBox::update(const char *text)
 {
        this->text->update(text);
-       yscroll->update_length(this->text->get_text_rows(),
-               this->text->get_text_row(),
-               yscroll->get_handlelength(),
-               1);
+       update_scrollbars();
 }
 
 void BC_ScrollTextBox::update(const wchar_t *wtext)
 {
        this->text->update(wtext);
-       yscroll->update_length(this->text->get_text_rows(),
-               this->text->get_text_row(),
-               yscroll->get_handlelength(),
-               1);
+       update_scrollbars();
 }
 
 void BC_ScrollTextBox::reposition_window(int x, int y, int w, int rows)
@@ -2206,19 +2275,7 @@ void BC_ScrollTextBox::reposition_window(int x, int y, int w, int rows)
        this->y = y;
        this->w = w;
        this->rows = rows;
-
-       text->reposition_window(x,
-               y,
-               w - yscroll->get_span(),
-               rows);
-       yscroll->reposition_window(x + w - yscroll->get_span(),
-               y,
-               BC_TextBox::calculate_row_h(rows,
-                       parent_window));
-       yscroll->update_length(text->get_text_rows(),
-               text->get_text_row(),
-               rows,
-               0);
+       update_scrollbars();
 }
 
 int BC_ScrollTextBox::button_press_event()
@@ -2250,6 +2307,15 @@ int BC_ScrollTextBox::get_ibeam_letter()
 {
        return text->get_ibeam_letter();
 }
+int BC_ScrollTextBox::get_x_pos()
+{
+       return text->left_margin - text->get_text_x();
+}
+void BC_ScrollTextBox::set_x_pos(int x)
+{
+       text->set_text_x(text->left_margin - x);
+       text->draw(1);
+}
 
 BC_ScrollTextBoxText::BC_ScrollTextBoxText(BC_ScrollTextBox *gui, const char *text)
  : BC_TextBox(gui->x, gui->y,
@@ -2276,35 +2342,111 @@ BC_ScrollTextBoxText::~BC_ScrollTextBoxText()
        }
 }
 
+void BC_ScrollTextBox::update_scrollbars()
+{
+       int view_w = w, view_rows = rows;
+       int view_h = BC_TextBox::calculate_row_h(view_rows, parent_window);
+       int text_rows = text->get_text_rows();
+       int text_width = parent_window->get_text_width(text->font, text->get_wtext());
+       BC_Resources *resources = parent_window->get_resources();
+       int need_xscroll = 0, need_yscroll = 0;
+
+// Create scrollbars as needed
+       int resize = 1;
+       while( resize ) {
+               resize = 0;
+               if( !need_xscroll && (text->get_text_x() != text->left_margin ||
+                     text_width >= view_w - text->left_margin - text->right_margin) ) {
+                       resize = need_xscroll = 1;
+                       view_h -= resources->hscroll_data[SCROLL_HANDLE_UP]->get_h();
+                       view_rows = BC_TextBox::pixels_to_rows(parent_window, text->font, view_h);
+               }
+               if( !need_yscroll && (text->get_text_y() != text->top_margin ||
+                     text_rows > view_rows) ) {
+                       resize = need_yscroll = 1;
+                       view_w -= resources->vscroll_data[SCROLL_HANDLE_UP]->get_w();
+               }
+       }
+
+       if( !need_xscroll && xscroll ) {
+               text->yscroll = 0;
+               delete xscroll;  xscroll = 0;
+       }
+       if( !need_yscroll && yscroll ) {
+               text->yscroll = 0;
+               delete yscroll;  yscroll = 0;
+       }
+
+       if( view_rows != text->get_rows() || view_w != text->get_w() ) {
+               text->reposition_window(x, y, view_w, view_rows);
+       }
+       if( need_xscroll && !xscroll ) {
+               xscroll = new BC_ScrollTextBoxXScroll(this);
+               parent_window->add_subwindow(xscroll);
+               text->xscroll = xscroll;
+               xscroll->bound_to = text;
+               xscroll->show_window();
+       }
+       if( need_yscroll && !yscroll ) {
+               yscroll = new BC_ScrollTextBoxYScroll(this);
+               parent_window->add_subwindow(yscroll);
+               text->yscroll = yscroll;
+               yscroll->bound_to = text;
+               yscroll->show_window();
+       }
+       if( xscroll ) {
+               xscroll->reposition_window(x, y + text->get_h(), view_w);
+               int xpos = get_x_pos();
+               if( xpos != xscroll->get_value() )
+                       xscroll->update_value(xpos);
+               if( text_width != xscroll->get_length() ||
+                   view_w != xscroll->get_handlelength() )
+                       xscroll->update_length(text_width, xpos, view_w, 0);
+       }
+       if( yscroll ) {
+               yscroll->reposition_window(x + w - yscroll->get_span(), y, text->get_h());
+               int text_row = text->get_text_row();
+               if( text_row != yscroll->get_value() )
+                       yscroll->update_value(text_row);
+               if( text_rows != yscroll->get_length() ||
+                   view_rows != yscroll->get_handlelength() )
+                       yscroll->update_length(text_rows, text_row, view_rows, 0);
+       }
+}
+
 int BC_ScrollTextBoxText::handle_event()
 {
-       gui->yscroll->update_length(get_text_rows(),
-               get_text_row(),
-               gui->yscroll->get_handlelength(),
-               1);
+       gui->update_scrollbars();
        return gui->handle_event();
 }
 
 int BC_ScrollTextBoxText::motion_event()
 {
-       gui->yscroll->update_length(get_text_rows(),
-               get_text_row(),
-               gui->yscroll->get_handlelength(),
-               1);
+       gui->update_scrollbars();
+       return 1;
+}
+
+BC_ScrollTextBoxXScroll::BC_ScrollTextBoxXScroll(BC_ScrollTextBox *gui)
+ : BC_ScrollBar(gui->x, gui->y + gui->text->get_h(), SCROLL_HORIZ + SCROLL_STRETCH,
+       gui->text->get_w(), gui->text->get_text_width(MEDIUMFONT, gui->get_wtext()),
+       0, gui->w)
+{
+       this->gui = gui;
+}
+
+BC_ScrollTextBoxXScroll::~BC_ScrollTextBoxXScroll()
+{
+}
+
+int BC_ScrollTextBoxXScroll::handle_event()
+{
+       gui->set_x_pos(get_position());
        return 1;
 }
 
 BC_ScrollTextBoxYScroll::BC_ScrollTextBoxYScroll(BC_ScrollTextBox *gui)
- : BC_ScrollBar(gui->x +
-                       gui->w -
-                       get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
-               gui->y,
-               SCROLL_VERT,
-               BC_TextBox::calculate_row_h(gui->rows,
-                       gui->parent_window),
-               gui->text->get_text_rows(),
-               0,
-               gui->rows)
+ : BC_ScrollBar(gui->x + gui->text->get_w(), gui->y, SCROLL_VERT,
+       gui->text->get_h(), gui->text->get_text_rows(), 0, gui->rows)
 {
        this->gui = gui;
 }
@@ -2362,6 +2504,7 @@ int BC_PopupTextBoxList::handle_event()
        if(item)
        {
                popup->textbox->update(item->get_text());
+               popup->textbox->set_text_row(0);
                popup->handle_event();
        }
        return 1;
@@ -2410,14 +2553,12 @@ int BC_PopupTextBox::create_objects()
 void BC_PopupTextBox::update(const char *text)
 {
        textbox->update(text);
+       textbox->set_text_row(0);
 }
 
 void BC_PopupTextBox::update_list(ArrayList<BC_ListBoxItem*> *data)
 {
-       listbox->update(data,
-               0,
-               0,
-               1);
+       listbox->update(data, 0, 0, 1);
 }
 
 
@@ -2456,6 +2597,16 @@ int BC_PopupTextBox::get_h()
        return textbox->get_h();
 }
 
+int BC_PopupTextBox::get_show_query()
+{
+       return listbox->get_show_query();
+}
+
+void BC_PopupTextBox::set_show_query(int v)
+{
+       listbox->set_show_query(v);
+}
+
 int BC_PopupTextBox::handle_event()
 {
        return 1;
@@ -2496,8 +2647,8 @@ BC_TumbleTextBoxText::BC_TumbleTextBoxText(BC_TumbleTextBox *popup,
 }
 
 BC_TumbleTextBoxText::BC_TumbleTextBoxText(BC_TumbleTextBox *popup,
-       float default_value, int x, int y)
- : BC_TextBox(x, y, popup->text_w, 1, default_value)
+       float default_value, int x, int y, int precision)
+ : BC_TextBox(x, y, popup->text_w, 1, default_value, 1, MEDIUMFONT, precision)
 {
        this->popup = popup;
 }
@@ -2522,19 +2673,10 @@ int BC_TumbleTextBoxText::handle_event()
 
 int BC_TumbleTextBoxText::button_press_event()
 {
-       if(is_event_win())
-       {
-               if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
-
-               if(get_buttonpress() == 4)
-               {
-                       popup->tumbler->handle_up_event();
-               }
-               else
-               if(get_buttonpress() == 5)
-               {
-                       popup->tumbler->handle_down_event();
-               }
+       if( get_enabled() && is_event_win() ) {
+               if( get_buttonpress() < 4 ) return BC_TextBox::button_press_event();
+               if( get_buttonpress() == 4 )      popup->tumbler->handle_up_event();
+               else if( get_buttonpress() == 5 ) popup->tumbler->handle_down_event();
                return 1;
        }
        return 0;
@@ -2649,31 +2791,17 @@ int BC_TumbleTextBox::create_objects()
 {
        int x = this->x, y = this->y;
 
-       if(use_float)
-       {
-               parent_window->add_subwindow(textbox = new BC_TumbleTextBoxText(this,
-                       default_value_f, x, y));
-               textbox->set_precision(precision);
-       }
-       else
-               parent_window->add_subwindow(textbox = new BC_TumbleTextBoxText(this,
-                       default_value, x, y));
+       textbox = use_float ?
+               new BC_TumbleTextBoxText(this, default_value_f, x, y, precision) :
+               new BC_TumbleTextBoxText(this, default_value, x, y);
 
+       parent_window->add_subwindow(textbox);
        x += textbox->get_w();
 
-       if(use_float)
-               parent_window->add_subwindow(tumbler = new BC_FTumbler(textbox,
-                       min_f,
-                       max_f,
-                       x,
-                       y));
-       else
-               parent_window->add_subwindow(tumbler = new BC_ITumbler(textbox,
-                       min,
-                       max,
-                       x,
-                       y));
-
+       tumbler = use_float ?
+               (BC_Tumbler *)new BC_FTumbler(textbox, min_f, max_f, x, y) :
+               (BC_Tumbler *)new BC_ITumbler(textbox, min, max, x, y);
+       parent_window->add_subwindow(tumbler);
        tumbler->set_increment(increment);
        return 0;
 }
@@ -2696,18 +2824,21 @@ BC_TextBox* BC_TumbleTextBox::get_textbox()
 int BC_TumbleTextBox::update(const char *value)
 {
        textbox->update(value);
+       textbox->set_text_row(0);
        return 0;
 }
 
 int BC_TumbleTextBox::update(int64_t value)
 {
        textbox->update(value);
+       textbox->set_text_row(0);
        return 0;
 }
 
 int BC_TumbleTextBox::update(float value)
 {
        textbox->update(value);
+       textbox->set_text_row(0);
        return 0;
 }
 
@@ -2786,3 +2917,69 @@ void BC_TumbleTextBox::set_boundaries(float min, float max)
 {
        tumbler->set_boundaries(min, max);
 }
+
+
+
+BC_TextMenu::BC_TextMenu(BC_TextBox *textbox)
+ : BC_PopupMenu(0, 0, 0, "", 0)
+{
+       this->textbox = textbox;
+}
+
+BC_TextMenu::~BC_TextMenu()
+{
+}
+
+void BC_TextMenu::create_objects()
+{
+       add_item(new BC_TextMenuCut(this));
+       add_item(new BC_TextMenuCopy(this));
+       add_item(new BC_TextMenuPaste(this));
+}
+
+
+BC_TextMenuCut::BC_TextMenuCut(BC_TextMenu *menu) 
+ : BC_MenuItem(_("Cut"))
+{
+       this->menu = menu;
+}
+
+int BC_TextMenuCut::handle_event()
+{
+       menu->textbox->cut(1);
+       
+       return 0;
+}
+
+
+BC_TextMenuCopy::BC_TextMenuCopy(BC_TextMenu *menu) 
+ : BC_MenuItem(_("Copy"))
+{
+       this->menu = menu;
+}
+
+int BC_TextMenuCopy::handle_event()
+{
+       menu->textbox->copy(1);
+       return 0;
+}
+
+
+BC_TextMenuPaste::BC_TextMenuPaste(BC_TextMenu *menu) 
+ : BC_MenuItem(_("Paste"))
+{
+       this->menu = menu;
+}
+
+int BC_TextMenuPaste::handle_event()
+{
+       menu->textbox->paste(1);
+       return 0;
+}
+
+
+void BC_TumbleTextBox::set_tooltip(const char *text)
+{
+       textbox->set_tooltip(text);
+}
+