picon dirs, new cinfinity picons, plugin info, rework text drawing, show msg, compres...
[goodguy/history.git] / cinelerra-5.1 / guicast / bctextbox.C
index c76f67ef0a3fa653a6cc538f082162c270d026e5..d377a448a0039398ee493a8ce2ced415b8bf05b3 100644 (file)
@@ -133,7 +133,6 @@ BC_TextBox::~BC_TextBox()
        delete suggestions_popup;
        suggestions->remove_all_objects();
        delete suggestions;
-       delete [] positions;
        delete [] wtext;
        if( size > 0 )
                delete [] text;
@@ -171,11 +170,10 @@ int BC_TextBox::reset_parameters(int rows, int has_border, int font, int size)
        wtext = 0;
        wsize = 0;
        wlen = 0;
-       positions = 0;
-       plen = 0;
        keypress_draw = 1;
        last_keypress = 0;
        separators = 0;
+       xscroll = 0;
        yscroll = 0;
        dirty = 1;
        return 0;
@@ -226,12 +224,6 @@ int BC_TextBox::wtext_update()
                        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),
                        (char*)wtext, wsize*sizeof(wchar_t)) / sizeof(wchar_t);
@@ -314,143 +306,91 @@ void BC_TextBox::set_precision(int precision)
 }
 
 // Compute suggestions for a path
-int BC_TextBox::calculate_suggestions(ArrayList<BC_ListBoxItem*> *entries)
+int BC_TextBox::calculate_suggestions(ArrayList<BC_ListBoxItem*> *entries, const char *filter)
 {
 // Let user delete suggestion
-       if(get_last_keypress() != BACKSPACE)
-       {
-
+       if(get_last_keypress() != BACKSPACE) {
 // Compute suggestions
                FileSystem fs;
                ArrayList<char*> suggestions;
                const char *current_text = get_text();
-
+               int suggestion_column = 0;
+               char dirname[BCTEXTLEN];  dirname[0] = 0;
+               if( current_text[0] == '/' || current_text[0] == '~' )
+                       strncpy(dirname, current_text, sizeof(dirname));
+               else if( !entries )
+                       getcwd(dirname, sizeof(dirname));
 // If directory, tabulate it
-               if(current_text[0] == '/' ||
-                       current_text[0] == '~')
-               {
-//printf("BC_TextBox::calculate_suggestions %d\n", __LINE__);
-                       char string[BCTEXTLEN];
-                       strncpy(string, current_text, sizeof(string));
-                       char *ptr = strrchr(string, '/');
-                       if(!ptr) ptr = strrchr(string, '~');
-
-//printf("BC_TextBox::calculate_suggestions %d\n", __LINE__);
-                       *(ptr + 1) = 0;
-                       int suggestion_column = ptr + 1 - string;
-
-                       fs.set_filter(get_resources()->filebox_filter);
-//                     fs.set_sort_order(filebox->sort_order);
-//                     fs.set_sort_field(filebox->column_type[filebox->sort_column]);
-
-
-//printf("BC_TextBox::calculate_suggestions %d %c %s\n", __LINE__, *ptr, string);
-                       if(current_text[0] == '~' && *ptr != '/')
-                       {
-                               fs.update("/home");
+               if( dirname[0] ) {
+                       if( filter ) fs.set_filter(filter);
+                       char *prefix, *cp;
+                       strncpy(dirname, current_text, sizeof(dirname));
+                       if( (cp=strrchr(dirname, '/')) ||
+                           (cp=strrchr(dirname, '~')) ) *++cp = 0;
+                       fs.parse_tildas(dirname);
+                       fs.update(dirname);
+                       cp = (char *)current_text;
+                       if( (prefix=strrchr(cp, '/')) ||
+                           (prefix=strrchr(cp, '~')) ) ++prefix;
+                       suggestion_column = !prefix ? 0 : prefix - cp;
+                       int prefix_len = prefix ? strlen(prefix) : 0;
+// only include items where the filename matches the basename prefix
+                       for(int i = 0; i < fs.total_files(); i++) {
+                               char *current_name = fs.get_entry(i)->name;
+                               if( prefix_len>0 && strncmp(prefix, current_name, prefix_len) ) continue;
+                               suggestions.append(current_name);
                        }
-                       else
-                       {
-                               fs.parse_tildas(string);
-                               fs.update(string);
-                       }
-//printf("BC_TextBox::calculate_suggestions %d %d\n", __LINE__, fs.total_files());
-
-
-// Accept only entries with matching trailing characters
-                       ptr = strrchr((char*)current_text, '/');
-                       if(!ptr) ptr = strrchr((char*)current_text, '~');
-                       if(ptr) ptr++;
-//printf("BC_TextBox::calculate_suggestions %d %s %p\n", __LINE__, current_text, ptr);
-
-
-                       if(ptr && *ptr)
-                       {
-                               for(int i = 0; i < fs.total_files(); i++)
-                               {
-                                       char *current_name = fs.get_entry(i)->name;
-                                       if(!strncmp(ptr, current_name, strlen(ptr)))
-                                       {
-                                               suggestions.append(current_name);
-       //printf("BC_TextBox::calculate_suggestions %d %s\n", __LINE__, current_name);
-                                       }
-                               }
-                       }
-                       else
-       // Accept all entries
-                       for(int i = 0; i < fs.total_files(); i++)
-                       {
-       //printf("BC_TextBox::calculate_suggestions %d %s\n", __LINE__, fs.get_entry(i)->name);
-                               suggestions.append(fs.get_entry(i)->name);
-                       }
-//printf("BC_TextBox::calculate_suggestions %d\n", __LINE__);
-
-// Add 1 to column to keep /
-                       set_suggestions(&suggestions, suggestion_column);
-//printf("BC_TextBox::calculate_suggestions %d\n", __LINE__);
                }
-               else
-// Get entries from current listbox with matching trailing characters
-               if(entries)
-               {
-// printf("BC_TextBox::calculate_suggestions %d %d\n",
-// __LINE__,
-// entries->size());
-                       for(int i = 0; i < entries->size(); i++)
-                       {
+               else if(entries) {
+                       char *prefix = (char *)current_text;
+                       int prefix_len = strlen(prefix);
+                       for(int i = 0; i < entries->size(); i++) {
                                char *current_name = entries->get(i)->get_text();
-
-//printf("BC_TextBox::calculate_suggestions %d %s %s\n", __LINE__, current_text, current_name);
-                               if(!strncmp(current_text, current_name, strlen(current_text)))
-                               {
-                                       suggestions.append(current_name);
-                               }
+                               if( prefix_len>0 && strncmp(prefix, current_name, prefix_len) ) continue;
+                               suggestions.append(current_name);
                        }
-
-                       set_suggestions(&suggestions, 0);
                }
+               set_suggestions(&suggestions, suggestion_column);
        }
 
        return 1;
 }
 
+void BC_TextBox::no_suggestions()
+{
+       if( suggestions_popup ) {
+               delete suggestions_popup;
+               suggestions_popup = 0;
+               activate();
+       }
+}
+
 void BC_TextBox::set_suggestions(ArrayList<char*> *suggestions, int column)
 {
 // Copy the array
        this->suggestions->remove_all_objects();
        this->suggestion_column = column;
 
-       if(suggestions)
-       {
-               for(int i = 0; i < suggestions->size(); i++)
-               {
+       if(suggestions) {
+               for(int i = 0; i < suggestions->size(); i++) {
                        this->suggestions->append(new BC_ListBoxItem(suggestions->get(i)));
                }
 
 // Show the popup without taking focus
-               if(suggestions->size() > 1)
-               {
-                       if(!suggestions_popup)
-                       {
-
-                               get_parent()->add_subwindow(suggestions_popup =
-                                       new BC_TextBoxSuggestions(this, x, y));
+               if( suggestions->size() > 1 ) {
+                       if( !suggestions_popup ) {
+                               suggestions_popup = new BC_TextBoxSuggestions(this, x, y);
+                               get_parent()->add_subwindow(suggestions_popup);
                                suggestions_popup->set_is_suggestions(1);
-                               suggestions_popup->activate(0);
                        }
-                       else
-                       {
-                               suggestions_popup->update(this->suggestions,
-                                       0,
-                                       0,
-                                       1);
-                               suggestions_popup->activate(0);
+                       else {
+                               suggestions_popup->update(this->suggestions, 0, 0, 1);
                        }
+                       suggestions_popup->activate(0);
                }
                else
 // Show the highlighted text
-               if(suggestions->size() == 1)
-               {
+               if( suggestions->size() == 1 ) {
                        highlight_letter1 = wtext_update();
                        text_update(wtext,wlen, text,tsize);
                        char *current_suggestion = suggestions->get(0);
@@ -462,18 +402,13 @@ void BC_TextBox::set_suggestions(ArrayList<char*> *suggestions, int column)
 //printf("BC_TextBox::set_suggestions %d %d\n", __LINE__, suggestion_column);
 
                        draw(1);
-
-                       delete suggestions_popup;
-                       suggestions_popup = 0;
+                       no_suggestions();
                }
        }
 
 // Clear the popup
-       if(!suggestions || !this->suggestions->size())
-       {
-               delete suggestions_popup;
-               suggestions_popup = 0;
-       }
+       if( !suggestions || !this->suggestions->size() )
+               no_suggestions();
 }
 
 void BC_TextBox::wset_selection(int char1, int char2, int ibeam)
@@ -554,10 +489,8 @@ void BC_TextBox::disable()
 {
        if(enabled) {
                enabled = 0;
-               if(top_level) {
-                       if(active) top_level->deactivate();
-                       draw(1);
-               }
+               deactivate();
+               draw(1);
        }
 }
 
@@ -571,10 +504,11 @@ 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::pixels_to_rows(BC_WindowBase *window, int font, int pixels)
 {
@@ -745,13 +679,13 @@ void BC_TextBox::draw(int flush)
                                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();
 
@@ -763,29 +697,22 @@ 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);
                        }
 
 // 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);
                        }
                }
        }
 
 //printf("BC_TextBox::draw 3 %d\n", ibeam_y);
        if(need_ibeam) {
-               if( wtext_len == 0 ) {
-                       ibeam_x = 0;
-                       ibeam_y = 0;
-               }
-               else {
-                       ibeam_x = -1;
-                       ibeam_y = -1;
-               }
+//             ibeam_x = ibeam_y = !wtext_len ? 0 : -1;
+               ibeam_x = 0;  ibeam_y = k - text_y;
        }
 
 //printf("BC_TextBox::draw 4 %d\n", ibeam_y);
@@ -812,10 +739,13 @@ int BC_TextBox::focus_out_event()
 
 int BC_TextBox::cursor_enter_event()
 {
-       if(top_level->event_win == win && enabled)
+       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();
+               }
                if(!highlighted)
                {
                        highlighted = 1;
@@ -831,10 +761,13 @@ int BC_TextBox::cursor_leave_event()
        if(highlighted)
        {
                highlighted = 0;
-               draw_border();
                hide_tooltip();
+               draw_border();
                flash(1);
        }
+       if( !suggestions_popup && !get_button_down() &&
+           !(top_level->get_resources()->textbox_focus_policy & CLICK_DEACTIVATE) )
+               deactivate();
        return 0;
 }
 
@@ -952,28 +885,15 @@ int BC_TextBox::button_press_event()
                return 1;
        }
        else
-       if(active && (!yscroll || !yscroll->is_event_win()))
-       {
-//printf("BC_TextBox::button_press_event %d\n", __LINE__);
-// Suggestion popup is not active but must be deactivated.
-               if(suggestions_popup)
-               {
-                       return 0;
-// printf("BC_TextBox::button_press_event %d\n", __LINE__);
-// // Pass event to suggestions popup
-//                     if(!suggestions_popup->button_press_event())
-//                     {
-// printf("BC_TextBox::button_press_event %d\n", __LINE__);
-//                             top_level->deactivate();
-//                     }
-               }
-               else
-               {
-                       top_level->deactivate();
+       if( active ) {
+               if( suggestions_popup && (!yscroll || !yscroll->is_event_win())) {
+                       if( suggestions_popup->button_press_event() )
+                               return suggestions_popup->handle_event();
                }
+               else if( (top_level->get_resources()->textbox_focus_policy & CLICK_DEACTIVATE) )
+                       deactivate();
        }
 
-
        return 0;
 }
 
@@ -1051,27 +971,23 @@ int BC_TextBox::cursor_motion_event()
 
 int BC_TextBox::activate()
 {
-       top_level->active_subwindow = this;
-       active = 1;
+       if( !active ) {
+               active = 1;
+               top_level->set_active_subwindow(this);
+               top_level->set_repeat(top_level->get_resources()->blink_rate);
+       }
        draw(1);
-       top_level->set_repeat(top_level->get_resources()->blink_rate);
        return 0;
 }
 
 int BC_TextBox::deactivate()
 {
-//printf("BC_TextBox::deactivate %d suggestions_popup=%p\n", __LINE__, suggestions_popup);
-       active = 0;
-       top_level->unset_repeat(top_level->get_resources()->blink_rate);
-       if(suggestions_popup)
-       {
-// Must deactivate instead of delete since this is called from BC_ListBox::button_press_event
-//             suggestions_popup->deactivate();
-
-               delete suggestions_popup;
-               suggestions_popup = 0;
+       if( active ) {
+               active = 0;
+               text_selected = word_selected = line_selected = 0;
+               top_level->set_active_subwindow(0);
+               top_level->unset_repeat(top_level->get_resources()->blink_rate);
        }
-
        draw(1);
        return 0;
 }
@@ -1086,7 +1002,6 @@ int BC_TextBox::repeat_event(int64_t duration)
                tooltip_text && tooltip_text[0] != 0 && highlighted)
        {
                show_tooltip();
-               tooltip_done = 1;
                result = 1;
        }
 
@@ -1197,15 +1112,11 @@ int BC_TextBox::repeat_event(int64_t duration)
 
 void BC_TextBox::default_keypress(int &dispatch_event, int &result)
 {
-       if( (top_level->get_keypress() == RETURN) ||
-           (top_level->get_keypress() > 30 && top_level->get_keypress() <= 255)) {
-               int len;
-               wchar_t *temp_string = top_level->get_wkeystring(&len);
-               if(top_level->get_keypress() == RETURN) {
-                       temp_string[0] = '\n';  temp_string[1] = 0;
-                       len = 1;
-               }
-               insert_text(temp_string, len);
+       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;
@@ -1293,9 +1204,8 @@ int BC_TextBox::keypress_event()
                switch(last_keypress) {
                case ESC: {
 // Deactivate the suggestions
-                       if(suggestions && suggestions_popup) {
-                               delete suggestions_popup;
-                               suggestions_popup = 0;
+                       if( suggestions_popup ) {
+                               no_suggestions();
                                result = 1;
                        }
                        else {
@@ -1306,6 +1216,8 @@ int BC_TextBox::keypress_event()
 
                case RETURN: {
                        if( rows == 1 ) {
+                               highlight_letter1 = highlight_letter2 = 0;
+                               ibeam_letter = wtext_update();
                                top_level->deactivate();
                                dispatch_event = 1;
                                result = 0;
@@ -1409,7 +1321,7 @@ int BC_TextBox::keypress_event()
                        break; }
 
                case UP: {
-                       if(suggestions && suggestions_popup) {
+                       if( suggestions && suggestions_popup ) {
 // Pass to suggestions popup
 //printf("BC_TextBox::keypress_event %d\n", __LINE__);
                                suggestions_popup->activate(1);
@@ -1496,7 +1408,7 @@ int BC_TextBox::keypress_event()
 // __LINE__,
 // suggestions,
 // suggestions_popup);
-                       if(suggestions && suggestions_popup) {
+                       if( suggestions && suggestions_popup ) {
 // Pass to suggestions popup
                                suggestions_popup->activate(1);
                                suggestions_popup->keypress_event();
@@ -1582,8 +1494,7 @@ int BC_TextBox::keypress_event()
                        break; }
 
                case END: {
-                       delete suggestions_popup;
-                       suggestions_popup = 0;
+                       no_suggestions();
 
                        int old_ibeam_letter = ibeam_letter;
 
@@ -1615,8 +1526,7 @@ int BC_TextBox::keypress_event()
                        break; }
 
                case HOME: {
-                       delete suggestions_popup;
-                       suggestions_popup = 0;
+                       no_suggestions();
 
                        int old_ibeam_letter = ibeam_letter;
 
@@ -1654,10 +1564,7 @@ int BC_TextBox::keypress_event()
                        break; }
 
                case BACKSPACE: {
-                       if(suggestions_popup) {
-                               delete suggestions_popup;
-                               suggestions_popup = 0;
-                       }
+                       no_suggestions();
 
                        if(highlight_letter1 == highlight_letter2) {
                                if(ibeam_letter > 0) {
@@ -1849,20 +1756,24 @@ 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;
        int wtext_len = wtext_update();
+       x = y = 0;
 
-       y = 0;
-       x = 0;
        for( i=0; i<wtext_len; ) {
                row_begin = i;
                for(; i<wtext_len && wtext[i]!='\n'; i++);
                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;
                }
@@ -2154,20 +2065,9 @@ int BC_TextBox::get_rows()
 
 
 
-BC_TextBoxSuggestions::BC_TextBoxSuggestions(BC_TextBox *text_box,
-       int x,
-       int y)
- : BC_ListBox(x,
-       y,
-       text_box->get_w(),
-       200,
-       LISTBOX_TEXT,
-       text_box->suggestions,
-       0,
-       0,
-       1,
-       0,
-       1)
+BC_TextBoxSuggestions::BC_TextBoxSuggestions(BC_TextBox *text_box, int x, int y)
+ : BC_ListBox(x, y, text_box->get_w(), 200, LISTBOX_TEXT,
+       text_box->suggestions, 0, 0, 1, 0, 1)
 {
        this->text_box = text_box;
        set_use_button(0);
@@ -2178,35 +2078,6 @@ BC_TextBoxSuggestions::~BC_TextBoxSuggestions()
 {
 }
 
-int BC_TextBoxSuggestions::selection_changed()
-{
-#if 0
-//printf("BC_TextBoxSuggestions::selection_changed %d\n", __LINE__);
-       BC_ListBoxItem *item = get_selection(0, 0);
-//printf("BC_TextBoxSuggestions::selection_changed %d\n", __LINE__);
-
-       if(item)
-       {
-               char *current_suggestion = item->get_text();
-//printf("BC_TextBoxSuggestions::selection_changed %d\n", __LINE__);
-//             int text_box_len = strlen(text_box->text);
-//printf("BC_TextBoxSuggestions::selection_changed %d\n", __LINE__);
-               strcpy(text_box->text + text_box->suggestion_column, current_suggestion);
-//printf("BC_TextBoxSuggestions::selection_changed %d\n", __LINE__);
-               *(text_box->text + text_box->suggestion_column + strlen(current_suggestion)) = 0;
-
-//printf("BC_TextBoxSuggestions::selection_changed %d\n", __LINE__);
-               text_box->draw(1);
-               text_box->handle_event();
-       }
-
-       return 1;
-#else
-       return 0;
-#endif
-}
-
-
 int BC_TextBoxSuggestions::handle_event()
 {
        char *current_suggestion = 0;
@@ -2247,6 +2118,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;
@@ -2261,6 +2133,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;
@@ -2268,6 +2141,7 @@ BC_ScrollTextBox::BC_ScrollTextBox(BC_WindowBase *parent_window,
 
 BC_ScrollTextBox::~BC_ScrollTextBox()
 {
+       delete xscroll;
        delete yscroll;
        if(text) {
                text->gui = 0;
@@ -2281,85 +2155,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;
-
-}
-
-int BC_ScrollTextBox::handle_event()
-{
-       return 1;
-}
-
-int BC_ScrollTextBox::get_x()
-{
-       return x;
-}
-
-int BC_ScrollTextBox::get_y()
-{
-       return y;
-}
-
-int BC_ScrollTextBox::get_w()
-{
-       return w;
-}
-
-int BC_ScrollTextBox::get_h()
-{
-       return this->text->get_h();
-}
-
-int BC_ScrollTextBox::get_rows()
-{
-       return rows;
-}
-
-
-const char* BC_ScrollTextBox::get_text()
-{
-       return text->get_text();
-}
-
-const wchar_t* BC_ScrollTextBox::get_wtext()
-{
-       return text->get_wtext();
+       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)
@@ -2368,41 +2189,47 @@ 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();
 }
 
-void BC_ScrollTextBox::set_selection(int char1, int char2, int ibeam)
+int BC_ScrollTextBox::button_press_event()
 {
-       this->text->set_selection(char1, char2, ibeam);
+       return text->BC_TextBox::button_press_event();
 }
+int BC_ScrollTextBox::button_release_event()
+{
+       return text->BC_TextBox::button_release_event();
+}
+
+int BC_ScrollTextBox::get_h() { return text->get_h(); }
+const char *BC_ScrollTextBox::get_text() { return text->get_text(); }
+const wchar_t *BC_ScrollTextBox::get_wtext() { return text->get_wtext(); }
 
+int BC_ScrollTextBox::get_buttonpress()
+{
+       return text->BC_TextBox::get_buttonpress();
+}
 void BC_ScrollTextBox::wset_selection(int char1, int char2, int ibeam)
 {
-       this->text->wset_selection(char1, char2, ibeam);
+       text->wset_selection(char1, char2, ibeam);
+}
+void BC_ScrollTextBox::set_selection(int char1, int char2, int ibeam)
+{
+       text->set_selection(char1, char2, ibeam);
 }
-
 int BC_ScrollTextBox::get_ibeam_letter()
 {
-       return this->text->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,
@@ -2429,36 +2256,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;
 }
@@ -2475,13 +2377,6 @@ int BC_ScrollTextBoxYScroll::handle_event()
 
 
 
-
-
-
-
-
-
-
 BC_PopupTextBoxText::BC_PopupTextBoxText(BC_PopupTextBox *popup, int x, int y, const char *text)
  : BC_TextBox(x, y, popup->text_w, 1, text, BCTEXTLEN)
 {
@@ -2523,6 +2418,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;
@@ -2571,14 +2467,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);
 }
 
 
@@ -2617,6 +2511,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;
@@ -2650,23 +2554,15 @@ void BC_PopupTextBox::reposition_window(int x, int y)
 
 
 BC_TumbleTextBoxText::BC_TumbleTextBoxText(BC_TumbleTextBox *popup,
-       int64_t default_value,
-       int64_t min,
-       int64_t max,
-       int x,
-       int y)
+       int64_t default_value, int x, int y)
  : BC_TextBox(x, y, popup->text_w, 1, default_value)
 {
        this->popup = popup;
 }
 
 BC_TumbleTextBoxText::BC_TumbleTextBoxText(BC_TumbleTextBox *popup,
-       float default_value,
-       float min,
-       float max,
-       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;
 }
@@ -2691,19 +2587,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;
@@ -2818,39 +2705,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,
-                       min_f,
-                       max_f,
-                       x,
-                       y));
-               textbox->set_precision(precision);
-       }
-       else
-               parent_window->add_subwindow(textbox = new BC_TumbleTextBoxText(this,
-                       default_value,
-                       min,
-                       max,
-                       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;
 }
@@ -2873,18 +2738,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;
 }
 
@@ -2909,17 +2777,23 @@ int BC_TumbleTextBox::get_h()
        return textbox->get_h();
 }
 
-void BC_TumbleTextBox::disable()
+void BC_TumbleTextBox::disable(int hide_text)
 {
+       if( hide_text && !textbox->is_hidden() )
+               textbox->hide_window(0);
+       if( !tumbler->is_hidden() )
+               tumbler->hide_window(0);
        if( !get_enabled() ) return;
-       tumbler->hide_window(0);
        return textbox->disable();
 }
 
 void BC_TumbleTextBox::enable()
 {
+       if( textbox->is_hidden() )
+               textbox->show_window(0);
+       if( tumbler->is_hidden() )
+               tumbler->show_window(0);
        if( get_enabled() ) return;
-       tumbler->show_window(0);
        return textbox->enable();
 }
 
@@ -2957,3 +2831,9 @@ void BC_TumbleTextBox::set_boundaries(float min, float max)
 {
        tumbler->set_boundaries(min, max);
 }
+
+void BC_TumbleTextBox::set_tooltip(const char *text)
+{
+       textbox->set_tooltip(text);
+}
+