Atrinik Client 2.5
toolkit/text_input.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00033 char text_input_string[MAX_INPUT_STRING];
00035 int text_input_count;
00037 static int text_input_cursor_pos = 0;
00039 static int text_input_max;
00040 
00042 static char text_input_history[MAX_HISTORY_LINES][MAX_INPUT_STRING];
00046 static size_t text_input_history_pos = 0;
00047 
00049 int text_input_string_flag;
00051 int text_input_string_end_flag;
00053 int text_input_string_esc_flag;
00055 uint32 text_input_opened;
00056 
00060 int text_input_center_offset()
00061 {
00062     return Bitmaps[BITMAP_LOGIN_INP]->bitmap->w / 2;
00063 }
00064 
00071 void text_input_draw_background(SDL_Surface *surface, int x, int y, int bitmap)
00072 {
00073     _BLTFX bltfx;
00074 
00075     bltfx.surface = surface;
00076     bltfx.flags = 0;
00077     bltfx.alpha = 0;
00078     sprite_blt(Bitmaps[bitmap], x, y, NULL, &bltfx);
00079 }
00080 
00092 void text_input_draw_text(SDL_Surface *surface, int x, int y, int font, const char *text, const char *color_notation, uint64 flags, int bitmap, SDL_Rect *box)
00093 {
00094     if (!box)
00095     {
00096         SDL_Rect box2;
00097 
00098         box2.x = 0;
00099         box2.y = 0;
00100         box2.w = Bitmaps[bitmap]->bitmap->w;
00101         box2.h = Bitmaps[bitmap]->bitmap->h;
00102         box = &box2;
00103     }
00104 
00105     x += 6 + box->x;
00106     y += box->h / 2 - FONT_HEIGHT(font) / 2 + box->y;
00107 
00108     box->w = box->w - 13 - box->x;
00109     box->h = FONT_HEIGHT(font);
00110     box->x = 0;
00111     box->y = 0;
00112 
00113     string_blt(surface, font, text, x, y, color_notation, flags | TEXT_WIDTH, box);
00114 }
00115 
00127 void text_input_show(SDL_Surface *surface, int x, int y, int font, const char *text, const char *color_notation, uint64 flags, int bitmap, SDL_Rect *box)
00128 {
00129     char buf[HUGE_BUF];
00130     SDL_Rect box2;
00131     size_t pos = text_input_cursor_pos;
00132     const char *cp = text;
00133     int underscore_width = glyph_get_width(font, '_');
00134 
00135     box2.w = 0;
00136 
00137     /* Figure out the width by going backwards. */
00138     while (pos > 0)
00139     {
00140         /* Reached the maximum yet? */
00141         if (box2.w + glyph_get_width(font, *(cp + pos)) + underscore_width > Bitmaps[bitmap]->bitmap->w - 13 - (box ? box->x * 2 : 0))
00142         {
00143             break;
00144         }
00145 
00146         blt_character(&font, font, NULL, &box2, cp + pos, NULL, NULL, 0, NULL, NULL);
00147         pos--;
00148     }
00149 
00150     /* Adjust the text position if necessary. */
00151     if (pos)
00152     {
00153         text += pos;
00154     }
00155 
00156     /* Draw the background. */
00157     text_input_draw_background(surface, x, y, bitmap);
00158     strncpy(buf, text, text_input_cursor_pos - pos);
00159     buf[text_input_cursor_pos - pos] = '_';
00160     buf[text_input_cursor_pos - pos + 1] = '\0';
00161 
00162     if (text + (text_input_cursor_pos - pos))
00163     {
00164         strcpy(buf + (text_input_cursor_pos - pos + 1), text + (text_input_cursor_pos - pos));
00165     }
00166 
00167     /* Draw the text. */
00168     text_input_draw_text(surface, x, y, font, buf, color_notation, flags, bitmap, box);
00169 }
00170 
00173 void text_input_clear()
00174 {
00175     text_input_string[0] = '\0';
00176     text_input_count = 0;
00177     text_input_history_pos = 0;
00178     text_input_history[0][0] = '\0';
00179     text_input_cursor_pos = 0;
00180     text_input_string_flag = 0;
00181     text_input_string_end_flag = 0;
00182     text_input_string_esc_flag = 0;
00183 }
00184 
00188 void text_input_open(int maxchar)
00189 {
00190     int interval, delay;
00191 
00192     interval = 120 / (setting_get_int(OPT_CAT_CLIENT, OPT_KEY_REPEAT_SPEED) + 1);
00193     delay = interval + 300 / (setting_get_int(OPT_CAT_CLIENT, OPT_KEY_REPEAT_SPEED) + 1);
00194 
00195     text_input_clear();
00196     text_input_max = maxchar;
00197     SDL_EnableKeyRepeat(delay, interval);
00198 
00199     if (cpl.input_mode != INPUT_MODE_NUMBER)
00200     {
00201         cpl.inventory_win = IWIN_BELOW;
00202     }
00203 
00204     /* Raise the text/number input widget. */
00205     if (cpl.input_mode == INPUT_MODE_NUMBER)
00206     {
00207         SetPriorityWidget(cur_widget[IN_NUMBER_ID]);
00208     }
00209     else if (cpl.input_mode == INPUT_MODE_CONSOLE)
00210     {
00211         SetPriorityWidget(cur_widget[IN_CONSOLE_ID]);
00212     }
00213 
00214     text_input_string_flag = 1;
00215     text_input_opened = SDL_GetTicks();
00216 }
00217 
00221 static void text_input_history_add(const char *text)
00222 {
00223     size_t i;
00224 
00225     /* If new line is empty or identical to last inserted one, skip it */
00226     if (text[0] == '\0' || !strcmp(text_input_history[1], text))
00227     {
00228         return;
00229     }
00230 
00231     /* Shift history lines. */
00232     for (i = MAX_HISTORY_LINES - 1; i > 1; i--)
00233     {
00234         strncpy(text_input_history[i], text_input_history[i - 1], sizeof(*text_input_history) - 1);
00235         text_input_history[i][sizeof(*text_input_history) - 1] = '\0';
00236     }
00237 
00238     /* Insert new one. */
00239     strncpy(text_input_history[1], text, sizeof(*text_input_history) - 1);
00240     text_input_history[1][sizeof(*text_input_history) - 1] = '\0';
00241     /* Clear temporary editing line. */
00242     text_input_history[0][0] = '\0';
00243     text_input_history_pos = 0;
00244 }
00245 
00248 void text_input_history_clear()
00249 {
00250     size_t i;
00251 
00252     for (i = 0; i < MAX_HISTORY_LINES; i++)
00253     {
00254         text_input_history[i][0] = '\0';
00255     }
00256 
00257     text_input_history_pos = 0;
00258 }
00259 
00263 void text_input_add_string(const char *text)
00264 {
00265     /* Copy to input buffer. */
00266     strncpy(text_input_string, text, sizeof(text_input_string) - 1);
00267     text_input_string[sizeof(text_input_string) - 1] = '\0';
00268     /* Set cursor after inserted text. */
00269     text_input_cursor_pos = text_input_count = strlen(text);
00270 }
00271 
00276 static void text_input_skip_word(int *i, int left)
00277 {
00278     /* Skip whitespace. */
00279     while (text_input_string[*i] == ' ' && (left ? *i >= 0 : *i < text_input_count))
00280     {
00281         *i += left ? -1 : 1;
00282     }
00283 
00284     /* Skip a word. */
00285     while (text_input_string[*i] != ' ' && (left ? *i >= 0 : *i < text_input_count))
00286     {
00287         *i += left ? -1 : 1;
00288     }
00289 }
00290 
00295 int text_input_handle(SDL_KeyboardEvent *key)
00296 {
00297     int i;
00298 
00299     if (key->type != SDL_KEYDOWN)
00300     {
00301         return 0;
00302     }
00303 
00304     if (keybind_command_matches_event("?PASTE", key))
00305     {
00306         char *clipboard_contents;
00307 
00308         clipboard_contents = clipboard_get();
00309 
00310         if (clipboard_contents)
00311         {
00312             strncat(text_input_string, clipboard_contents, sizeof(text_input_string) - text_input_count - 1);
00313             text_input_cursor_pos = text_input_count = strlen(text_input_string);
00314 
00315             for (i = 0; i < text_input_count; i++)
00316             {
00317                 if (text_input_string[i] < ' ' || text_input_string[i] > '~')
00318                 {
00319                     text_input_string[i] = ' ';
00320                 }
00321             }
00322 
00323             free(clipboard_contents);
00324         }
00325 
00326         return 1;
00327     }
00328 
00329     switch (key->keysym.sym)
00330     {
00331         case SDLK_ESCAPE:
00332             SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
00333             text_input_string_esc_flag = 1;
00334             return 1;
00335 
00336         case SDLK_KP_ENTER:
00337         case SDLK_RETURN:
00338         case SDLK_TAB:
00339             if (key->keysym.sym != SDLK_TAB || GameStatus < GAME_STATUS_WAITFORPLAY)
00340             {
00341                 SDL_EnableKeyRepeat(0 , SDL_DEFAULT_REPEAT_INTERVAL);
00342                 text_input_string_flag = 0;
00343                 /* Mark that we've got something here. */
00344                 text_input_string_end_flag = 1;
00345 
00346                 /* Record this line in input history only if we are in console mode. */
00347                 if (cpl.input_mode == INPUT_MODE_CONSOLE)
00348                 {
00349                     text_input_history_add(text_input_string);
00350                 }
00351 
00352                 return 1;
00353             }
00354             else if (key->keysym.sym == SDLK_TAB)
00355             {
00356                 help_files_struct *help_files_tmp;
00357                 int possibilities = 0;
00358                 char cmd_buf[MAX_BUF];
00359 
00360                 if (text_input_string[0] != '/' || strrchr(text_input_string, ' '))
00361                 {
00362                     return 1;
00363                 }
00364 
00365                 for (help_files_tmp = help_files; help_files_tmp; help_files_tmp = help_files_tmp->next)
00366                 {
00367                     if (strcmp(help_files_tmp->title + strlen(help_files_tmp->title) - 8, " Command"))
00368                     {
00369                         continue;
00370                     }
00371 
00372                     if (!strncmp(help_files_tmp->helpname, text_input_string + 1, text_input_count - 1))
00373                     {
00374                         if ((help_files_tmp->dm_only && !cpl.dm) || !help_files_tmp->autocomplete)
00375                         {
00376                             continue;
00377                         }
00378 
00379                         if (possibilities == 0)
00380                         {
00381                             strncpy(cmd_buf, help_files_tmp->helpname, sizeof(cmd_buf));
00382                         }
00383                         else
00384                         {
00385                             if (possibilities == 1)
00386                             {
00387                                 draw_info_format(COLOR_WHITE, "\nMatching commands:\n%s", cmd_buf);
00388                             }
00389 
00390                             draw_info(COLOR_WHITE, help_files_tmp->helpname);
00391                         }
00392 
00393                         possibilities++;
00394                     }
00395                 }
00396 
00397                 if (possibilities == 1)
00398                 {
00399                     snprintf(text_input_string, sizeof(text_input_string), "/%s ", cmd_buf);
00400                     text_input_count = text_input_cursor_pos = (int) strlen(text_input_string);
00401                 }
00402 
00403                 return 1;
00404             }
00405 
00406             break;
00407 
00408         /* Erases the previous character or word if CTRL is pressed. */
00409         case SDLK_BACKSPACE:
00410             if (text_input_count && text_input_cursor_pos)
00411             {
00412                 int ii = text_input_cursor_pos;
00413 
00414                 /* Where we will end up, by default one character back. */
00415                 i = ii - 1;
00416 
00417                 if (key->keysym.mod & KMOD_CTRL)
00418                 {
00419                     text_input_skip_word(&i, 1);
00420                     /* We end up at the beginning of the current word. */
00421                     i++;
00422                 }
00423 
00424                 while (ii <= text_input_count)
00425                 {
00426                     text_input_string[i++] = text_input_string[ii++];
00427                 }
00428 
00429                 text_input_cursor_pos -= (ii - i);
00430                 text_input_count -= (ii - i);
00431             }
00432 
00433             return 1;
00434 
00435         case SDLK_DELETE:
00436         {
00437             int ii = text_input_cursor_pos;
00438 
00439             /* Where we will end up, by default one character ahead. */
00440             i = ii + 1;
00441 
00442             if (ii == text_input_count)
00443             {
00444                 return 1;
00445             }
00446 
00447             if (key->keysym.mod & KMOD_CTRL)
00448             {
00449                 text_input_skip_word(&i, 0);
00450             }
00451 
00452             while (i <= text_input_count)
00453             {
00454                 text_input_string[ii++] = text_input_string[i++];
00455             }
00456 
00457             text_input_count -= (i - ii);
00458             return 1;
00459         }
00460 
00461         /* Shifts a character or a word if CTRL is pressed. */
00462         case SDLK_LEFT:
00463             if (key->keysym.mod & KMOD_CTRL)
00464             {
00465                 i = text_input_cursor_pos - 1;
00466                 text_input_skip_word(&i, 1);
00467                 /* Places the cursor on the first letter of this word. */
00468                 text_input_cursor_pos = i + 1;
00469             }
00470             else if (text_input_cursor_pos > 0)
00471             {
00472                 text_input_cursor_pos--;
00473             }
00474 
00475             return 1;
00476 
00477         /* Shifts a character or a word if CTRL is pressed. */
00478         case SDLK_RIGHT:
00479             if (key->keysym.mod & KMOD_CTRL)
00480             {
00481                 i = text_input_cursor_pos;
00482                 text_input_skip_word(&i, 0);
00483                 /* Places the cursor right after the skipped word. */
00484                 text_input_cursor_pos = i;
00485             }
00486             else if (text_input_cursor_pos < text_input_count)
00487             {
00488                 text_input_cursor_pos++;
00489             }
00490 
00491             return 1;
00492 
00493         /* Scroll forward in history. */
00494         case SDLK_UP:
00495             if (cpl.input_mode == INPUT_MODE_CONSOLE && text_input_history_pos < MAX_HISTORY_LINES - 1 && text_input_history[text_input_history_pos + 1][0])
00496             {
00497                 /* First history line is special, it records what we were
00498                  * writing before scrolling back the history; so, by
00499                  * returning back to zero, we can continue our editing
00500                  * where we left it. */
00501                 if (text_input_history_pos == 0)
00502                 {
00503                     strncpy(text_input_history[0], text_input_string, text_input_count);
00504                 }
00505 
00506                 text_input_history_pos++;
00507                 text_input_add_string(text_input_history[text_input_history_pos]);
00508             }
00509 
00510             return 1;
00511 
00512         /* Scroll backward in history. */
00513         case SDLK_DOWN:
00514             if (cpl.input_mode == INPUT_MODE_CONSOLE && text_input_history_pos > 0)
00515             {
00516                 text_input_history_pos--;
00517                 text_input_add_string(text_input_history[text_input_history_pos]);
00518             }
00519 
00520             return 1;
00521 
00522         /* Go to the start of the text input. */
00523         case SDLK_HOME:
00524             text_input_cursor_pos = 0;
00525             return 1;
00526 
00527         /* Go to the end of the text input. */
00528         case SDLK_END:
00529             text_input_cursor_pos = text_input_count;
00530             return 1;
00531 
00532         default:
00533         {
00534             char c;
00535 
00536             if (text_input_count < text_input_max)
00537             {
00538                 c = 0;
00539 
00540                 /* We want only numbers in number mode - even when shift is held. */
00541                 if (cpl.input_mode == INPUT_MODE_NUMBER)
00542                 {
00543                     switch (key->keysym.sym)
00544                     {
00545                         case SDLK_0:
00546                         case SDLK_KP0:
00547                             c = '0';
00548                             break;
00549 
00550                         case SDLK_KP1:
00551                         case SDLK_1:
00552                             c = '1';
00553                             break;
00554 
00555                         case SDLK_KP2:
00556                         case SDLK_2:
00557                             c = '2';
00558                             break;
00559 
00560                         case SDLK_KP3:
00561                         case SDLK_3:
00562                             c = '3';
00563                             break;
00564 
00565                         case SDLK_KP4:
00566                         case SDLK_4:
00567                             c = '4';
00568                             break;
00569 
00570                         case SDLK_KP5:
00571                         case SDLK_5:
00572                             c = '5';
00573                             break;
00574 
00575                         case SDLK_KP6:
00576                         case SDLK_6:
00577                             c = '6';
00578                             break;
00579 
00580                         case SDLK_KP7:
00581                         case SDLK_7:
00582                             c = '7';
00583                             break;
00584 
00585                         case SDLK_KP8:
00586                         case SDLK_8:
00587                             c = '8';
00588                             break;
00589 
00590                         case SDLK_KP9:
00591                         case SDLK_9:
00592                             c = '9';
00593                             break;
00594 
00595                         default:
00596                             c = 0;
00597                             break;
00598                     }
00599 
00600                     if (c)
00601                     {
00602                         text_input_string[text_input_cursor_pos++] = c;
00603                         text_input_count++;
00604                         text_input_string[text_input_count] = 0;
00605                         return 1;
00606                     }
00607                 }
00608                 else
00609                 {
00610                     c = key->keysym.unicode & 0xff;
00611 
00612                     if (c >= 32)
00613                     {
00614                         if (key->keysym.mod & KMOD_SHIFT)
00615                         {
00616                             c = toupper(c);
00617                         }
00618 
00619                         i = text_input_count;
00620 
00621                         while (i >= text_input_cursor_pos)
00622                         {
00623                             text_input_string[i + 1] = text_input_string[i];
00624                             i--;
00625                         }
00626 
00627                         text_input_string[text_input_cursor_pos] = c;
00628                         text_input_cursor_pos++;
00629                         text_input_count++;
00630                         text_input_string[text_input_count] = 0;
00631                         return 1;
00632                     }
00633                 }
00634             }
00635 
00636             break;
00637         }
00638     }
00639 
00640     return 0;
00641 }