Atrinik Client 2.5
toolkit/list.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 static list_struct *list_head = NULL;
00035 static list_struct *list_tail = NULL;
00037 static int old_scrollbar_pos = 0;
00038 
00039 static int list_handle_key(list_struct *list, SDLKey key);
00040 
00044 static void list_draw_frame(list_struct *list)
00045 {
00046     draw_frame(list->surface, list->x + list->frame_offset, LIST_ROWS_START(list) + list->frame_offset, list->width, LIST_ROWS_HEIGHT(list));
00047 }
00048 
00054 static void list_row_color(list_struct *list, int row, SDL_Rect box)
00055 {
00056     if (row & 1)
00057     {
00058         SDL_FillRect(list->surface, &box, SDL_MapRGB(list->surface->format, 0x55, 0x55, 0x55));
00059     }
00060     else
00061     {
00062         SDL_FillRect(list->surface, &box, SDL_MapRGB(list->surface->format, 0x45, 0x45, 0x45));
00063     }
00064 }
00065 
00070 static void list_row_highlight(list_struct *list, SDL_Rect box)
00071 {
00072     SDL_FillRect(list->surface, &box, SDL_MapRGB(list->surface->format, 0x00, 0x80, 0x00));
00073 }
00074 
00079 static void list_row_selected(list_struct *list, SDL_Rect box)
00080 {
00081     SDL_FillRect(list->surface, &box, SDL_MapRGB(list->surface->format, 0x00, 0x00, 0xef));
00082 }
00083 
00090 list_struct *list_get_focused()
00091 {
00092     list_struct *tmp;
00093 
00094     /* Try to find a focused list. */
00095     for (tmp = list_head; tmp; tmp = tmp->next)
00096     {
00097         if (tmp->focus)
00098         {
00099             return tmp;
00100         }
00101     }
00102 
00103     /* Try to set the focus to the servers list, if possible. */
00104     tmp = list_exists(LIST_SERVERS);
00105 
00106     if (tmp)
00107     {
00108         tmp->focus = 1;
00109         return tmp;
00110     }
00111 
00112     /* Failsafe in case there are lists, but none with active focus. */
00113     if (list_head)
00114     {
00115         list_head->focus = 1;
00116         return list_head;
00117     }
00118 
00119     return NULL;
00120 }
00121 
00125 void list_set_focus(list_struct *list)
00126 {
00127     list_struct *tmp;
00128 
00129     /* Already focused, nothing to do. */
00130     if (list->focus)
00131     {
00132         return;
00133     }
00134 
00135     /* Remove focus from previously focused list. */
00136     for (tmp = list_head; tmp; tmp = tmp->next)
00137     {
00138         if (tmp != list)
00139         {
00140             tmp->focus = 0;
00141         }
00142     }
00143 
00144     list->focus = 1;
00145 }
00146 
00152 void list_set_parent(list_struct *list, int px, int py)
00153 {
00154     list->px = px;
00155     list->py = py;
00156 }
00157 
00165 list_struct *list_create(uint32 id, uint32 max_rows, uint32 cols, int spacing)
00166 {
00167     list_struct *list = calloc(1, sizeof(list_struct));
00168 
00169     if (max_rows == 0)
00170     {
00171         LOG(llevBug, "list_create(): Attempted to create a list with 0 max rows, changing to 1.\n");
00172         max_rows = 1;
00173     }
00174 
00175     /* Store the values. */
00176     list->id = id;
00177     list->max_rows = max_rows;
00178     list->cols = cols;
00179     list->spacing = spacing;
00180     list->font = FONT_SANS10;
00181     list->surface = ScreenSurface;
00182 
00183     /* Initialize defaults. */
00184     list->frame_offset = -2;
00185     list->header_height = 12;
00186     list->row_selected = 1;
00187     list->repeat_key = -1;
00188 
00189     /* Generic functions. */
00190     list->draw_frame_func = list_draw_frame;
00191     list->row_color_func = list_row_color;
00192     list->row_highlight_func = list_row_highlight;
00193     list->row_selected_func = list_row_selected;
00194 
00195     /* Initialize column data. */
00196     list->col_widths = calloc(1, sizeof(*list->col_widths) * list->cols);
00197     list->col_spacings = calloc(1, sizeof(*list->col_spacings) * list->cols);
00198     list->col_names = calloc(1, sizeof(*list->col_names) * list->cols);
00199     list->col_centered = calloc(1, sizeof(*list->col_centered) * list->cols);
00200 
00201     /* First list. */
00202     if (!list_head)
00203     {
00204         /* As this is the first list, by default it will have the focus. */
00205         list->focus = 1;
00206         list_head = list;
00207         list->prev = NULL;
00208     }
00209     else
00210     {
00211         list_tail->next = list;
00212         list->prev = list_tail;
00213     }
00214 
00215     list_tail = list;
00216     list->next = NULL;
00217 
00218     return list;
00219 }
00220 
00228 void list_add(list_struct *list, uint32 row, uint32 col, const char *str)
00229 {
00230     if (!list)
00231     {
00232         return;
00233     }
00234 
00235     if (col > list->cols)
00236     {
00237         LOG(llevBug, "list_add(): Attempted to add column #%u, but columns max is %u.\n", col, list->cols);
00238         return;
00239     }
00240 
00241     /* Add new rows. */
00242     if (row + 1 > list->rows)
00243     {
00244         uint32 i;
00245 
00246         /* Update rows count and resize the array of rows. */
00247         list->rows = row + 1;
00248         list->text = realloc(list->text, sizeof(*list->text) * list->rows);
00249 
00250         /* Allocate columns for the new row(s). */
00251         for (i = row; i < list->rows; i++)
00252         {
00253             list->text[i] = calloc(1, sizeof(**list->text) * list->cols);
00254         }
00255     }
00256 
00257     list->text[row][col] = strdup(str);
00258 }
00259 
00264 void list_remove_row(list_struct *list, uint32 row)
00265 {
00266     uint32 col, row2;
00267 
00268     /* Sanity checks. */
00269     if (!list || !list->text || row >= list->rows)
00270     {
00271         return;
00272     }
00273 
00274     /* Free the columns of the row that is being removed. */
00275     for (col = 0; col < list->cols; col++)
00276     {
00277         free(list->text[row][col]);
00278     }
00279 
00280     /* If there are any rows below the one that is being removed, they
00281      * need to be moved up. */
00282     for (row2 = row + 1; row2 < list->rows; row2++)
00283     {
00284         for (col = 0; col < list->cols; col++)
00285         {
00286             list->text[row2 - 1][col] = list->text[row2][col];
00287         }
00288     }
00289 
00290     list->rows--;
00291     list->text = realloc(list->text, sizeof(*list->text) * list->rows);
00292 }
00293 
00303 void list_set_column(list_struct *list, uint32 col, int width, int spacing, const char *name, int centered)
00304 {
00305     if (col > list->cols)
00306     {
00307         LOG(llevBug, "list_set_column(): Attempted to change column #%u, but columns max is %u.\n", col, list->cols);
00308         return;
00309     }
00310 
00311     /* Set width. */
00312     if (width != -1)
00313     {
00314         list->col_widths[col] = width;
00315         list->width += width;
00316     }
00317 
00318     /* Set spacing. */
00319     if (spacing != -1)
00320     {
00321         list->col_spacings[col] = spacing;
00322         list->width += spacing;
00323     }
00324 
00325     /* Set the column's name. */
00326     if (name)
00327     {
00328         /* There shouldn't be one previously, but just in case. */
00329         if (list->col_names[col])
00330         {
00331             free(list->col_names[col]);
00332         }
00333 
00334         list->col_names[col] = strdup(name);
00335     }
00336 
00337     /* Is the column centered? */
00338     if (centered != -1)
00339     {
00340         list->col_centered[col] = centered;
00341     }
00342 }
00343 
00348 void list_set_font(list_struct *list, int font)
00349 {
00350     list->font = font;
00351 }
00352 
00356 void list_scrollbar_enable(list_struct *list)
00357 {
00358     list->scrollbar = 1;
00359 }
00360 
00366 static int list_scrollbar_get_size(list_struct *list, SDL_Rect *box)
00367 {
00368     uint32 col;
00369 
00370     if (!list->scrollbar)
00371     {
00372         return 0;
00373     }
00374 
00375     box->x = list->x + list->frame_offset + 1;
00376     box->y = LIST_ROWS_START(list) + list->frame_offset;
00377     box->w = LIST_SCROLLBAR_WIDTH;
00378     box->h = LIST_ROW_HEIGHT(list) * list->max_rows;
00379 
00380     for (col = 0; col < list->cols; col++)
00381     {
00382         box->x += list->col_widths[col] + list->col_spacings[col];
00383     }
00384 
00385     return 1;
00386 }
00387 
00394 static int list_slider_get_size(list_struct *list, SDL_Rect *box)
00395 {
00396     if (!list_scrollbar_get_size(list, box))
00397     {
00398         return 0;
00399     }
00400 
00401     box->x += 1;
00402     box->y += 1;
00403     box->w -= 1;
00404     box->h -= 1;
00405 
00406     if (list->rows > list->max_rows)
00407     {
00408         int scroll;
00409 
00410         scroll = list->max_rows + list->row_offset;
00411         list->scrollbar_h = box->h * list->max_rows / list->rows;
00412         list->scrollbar_y = ((scroll - list->max_rows) * box->h) / list->rows;
00413 
00414         if (list->scrollbar_h < 1)
00415         {
00416             list->scrollbar_h = 1;
00417         }
00418 
00419         if (scroll - list->max_rows > 0 && list->scrollbar_y + list->scrollbar_h < box->h)
00420         {
00421             list->scrollbar_y++;
00422         }
00423 
00424         box->h = list->scrollbar_h;
00425         box->y += list->scrollbar_y;
00426     }
00427 
00428     return 1;
00429 }
00430 
00434 static void list_scrollbar_render(list_struct *list)
00435 {
00436     SDL_Rect scrollbar_box;
00437     int mx, my;
00438 
00439     if (!list_scrollbar_get_size(list, &scrollbar_box))
00440     {
00441         return;
00442     }
00443 
00444     SDL_GetMouseState(&mx, &my);
00445 
00446     draw_frame(list->surface, scrollbar_box.x, scrollbar_box.y, scrollbar_box.w, scrollbar_box.h);
00447 
00448     list_slider_get_size(list, &scrollbar_box);
00449 
00450     mx -= list->px;
00451     my -= list->py;
00452 
00453     if (mx >= scrollbar_box.x && mx < scrollbar_box.x + scrollbar_box.w && my >= scrollbar_box.y && my < scrollbar_box.y + scrollbar_box.h)
00454     {
00455         SDL_FillRect(list->surface, &scrollbar_box, SDL_MapRGBA(list->surface->format, 175, 154, 110, 255));
00456     }
00457     else
00458     {
00459         SDL_FillRect(list->surface, &scrollbar_box, SDL_MapRGBA(list->surface->format, 157, 139, 98, 255));
00460     }
00461 }
00462 
00468 void list_show(list_struct *list, int x, int y)
00469 {
00470     uint32 row, col;
00471     int w = 0, extra_width = 0;
00472     SDL_Rect box;
00473 
00474     if (!list)
00475     {
00476         return;
00477     }
00478 
00479     list->x = x;
00480     list->y = y;
00481 
00482     /* Keys needing repeat? */
00483     if (list->repeat_key != -1)
00484     {
00485         if (list->repeat_key_ticks + KEY_REPEAT_DELAY - 5 < LastTick)
00486         {
00487             while ((list->repeat_key_ticks += KEY_REPEAT_DELAY - 5) < LastTick)
00488             {
00489                 list_handle_key(list, list->repeat_key);
00490             }
00491         }
00492     }
00493 
00494     /* Draw a frame, if needed. */
00495     if (list->draw_frame_func)
00496     {
00497         list->draw_frame_func(list);
00498     }
00499 
00500     /* Draw the column names. */
00501     for (col = 0; col < list->cols; col++)
00502     {
00503         extra_width = 0;
00504 
00505         /* Center it? */
00506         if (list->col_centered[col])
00507         {
00508             extra_width = list->col_widths[col] / 2 - string_get_width(list->font, list->col_names[col], 0) / 2;
00509         }
00510 
00511         /* Actually draw the column name. */
00512         if (list->col_names[col])
00513         {
00514             string_blt_shadow(list->surface, list->font, list->col_names[col], list->x + w + extra_width, list->y, list->focus ? COLOR_WHITE : COLOR_GRAY, COLOR_BLACK, 0, NULL);
00515         }
00516 
00517         w += list->col_widths[col] + list->col_spacings[col];
00518     }
00519 
00520     /* Initialize default values for coloring rows. */
00521     box.x = list->x + list->frame_offset;
00522     box.w = list->width;
00523     box.h = LIST_ROW_HEIGHT(list);
00524 
00525     list_scrollbar_render(list);
00526 
00527     /* Doing coloring of each row? */
00528     if (list->row_color_func)
00529     {
00530         for (row = 0; row < list->max_rows; row++)
00531         {
00532             box.y = LIST_ROWS_START(list) + (row * LIST_ROW_HEIGHT(list)) + list->frame_offset;
00533             list->row_color_func(list, row, box);
00534         }
00535     }
00536 
00537     /* Start printing out rows from the offset to the maximum. */
00538     for (row = list->row_offset; row < list->rows; row++)
00539     {
00540         /* Stop if we reached maximum number of visible rows. */
00541         if (LIST_ROW_OFFSET(row, list) == list->max_rows)
00542         {
00543             break;
00544         }
00545 
00546         /* Color selected row. */
00547         if (list->row_selected_func && (row + 1) == list->row_selected)
00548         {
00549             box.y = LIST_ROWS_START(list) + (LIST_ROW_OFFSET(row, list) * LIST_ROW_HEIGHT(list)) + list->frame_offset;
00550             list->row_selected_func(list, box);
00551         }
00552         /* Color highlighted row. */
00553         else if (list->row_highlight_func && (row + 1) == list->row_highlighted)
00554         {
00555             box.y = LIST_ROWS_START(list) + (LIST_ROW_OFFSET(row, list) * LIST_ROW_HEIGHT(list)) + list->frame_offset;
00556             list->row_highlight_func(list, box);
00557         }
00558 
00559         w = 0;
00560 
00561         /* Show all the columns. */
00562         for (col = 0; col < list->cols; col++)
00563         {
00564             /* Is there any text to show? */
00565             if (list->text[row][col])
00566             {
00567                 const char *text_color;
00568                 SDL_Rect text_rect;
00569 
00570                 extra_width = 0;
00571 
00572                 /* Center it. */
00573                 if (list->col_centered[col])
00574                 {
00575                     extra_width = list->col_widths[col] / 2 - string_get_width(list->font, list->text[row][col], TEXT_WORD_WRAP) / 2;
00576                 }
00577 
00578                 text_color = list->focus ? COLOR_WHITE : COLOR_GRAY;
00579 
00580                 if (list->text_color_hook)
00581                 {
00582                     text_color = list->text_color_hook(list, text_color, row, col);
00583                 }
00584 
00585                 /* Add width limit on the string. */
00586                 text_rect.w = list->col_widths[col] + list->col_spacings[col];
00587                 text_rect.h = LIST_ROW_HEIGHT(list);
00588                 /* Output the text. */
00589                 string_blt_shadow(list->surface, list->font, list->text[row][col], list->x + w + extra_width, LIST_ROWS_START(list) + (LIST_ROW_OFFSET(row, list) * LIST_ROW_HEIGHT(list)), text_color, COLOR_BLACK, TEXT_WORD_WRAP | list->text_flags, &text_rect);
00590             }
00591 
00592             if (list->post_column_func)
00593             {
00594                 list->post_column_func(list, row, col);
00595             }
00596 
00597             w += list->col_widths[col] + list->col_spacings[col];
00598         }
00599     }
00600 }
00601 
00605 void list_clear_rows(list_struct *list)
00606 {
00607     uint32 row, col;
00608 
00609     if (!list || !list->text)
00610     {
00611         return;
00612     }
00613 
00614     /* Free the texts. */
00615     for (row = 0; row < list->rows; row++)
00616     {
00617         for (col = 0; col < list->cols; col++)
00618         {
00619             if (list->text[row][col])
00620             {
00621                 free(list->text[row][col]);
00622             }
00623         }
00624 
00625         free(list->text[row]);
00626     }
00627 
00628     free(list->text);
00629     list->text = NULL;
00630     list->rows = 0;
00631 }
00632 
00636 void list_clear(list_struct *list)
00637 {
00638     list_clear_rows(list);
00639 
00640     list->row_selected = 1;
00641     list->row_highlighted = 0;
00642     list->row_offset = 0;
00643 }
00644 
00649 void list_offsets_ensure(list_struct *list)
00650 {
00651     if (list->row_selected >= list->rows)
00652     {
00653         list->row_selected = list->rows;
00654     }
00655 
00656     if (list->rows < list->max_rows)
00657     {
00658         list->row_offset = 0;
00659     }
00660     else if (list->row_offset >= list->rows - list->max_rows)
00661     {
00662         list->row_offset = list->rows - list->max_rows;
00663     }
00664 }
00665 
00670 void list_remove(list_struct *list)
00671 {
00672     uint32 col;
00673 
00674     if (!list)
00675     {
00676         return;
00677     }
00678 
00679     /* Remove it from the list. */
00680     if (!list->prev)
00681     {
00682         list_head = list->next;
00683     }
00684     else
00685     {
00686         list->prev->next = list->next;
00687     }
00688 
00689     if (!list->next)
00690     {
00691         list_tail = list->prev;
00692     }
00693     else
00694     {
00695         list->next->prev = list->prev;
00696     }
00697 
00698     /* Removing the focused list, try to update the focus. */
00699     if (list->focus)
00700     {
00701         list_get_focused();
00702     }
00703 
00704     list_clear(list);
00705 
00706     free(list->col_widths);
00707     free(list->col_spacings);
00708     free(list->col_centered);
00709 
00710     /* Free column names. */
00711     for (col = 0; col < list->cols; col++)
00712     {
00713         if (list->col_names[col])
00714         {
00715             free(list->col_names[col]);
00716         }
00717     }
00718 
00719     free(list->col_names);
00720     free(list);
00721 }
00722 
00725 void list_remove_all()
00726 {
00727     /* Loop until there is nothing left. */
00728     while (list_head)
00729     {
00730         list_remove(list_head);
00731     }
00732 }
00733 
00739 static void list_scroll(list_struct *list, int up, int scroll)
00740 {
00741     /* The actual values are unsigned. Changing them to signed here
00742      * makes it easier to check for overflows below. */
00743     sint32 row_selected = list->row_selected, row_offset = list->row_offset;
00744     sint32 max_rows, rows;
00745 
00746     /* Number of rows. */
00747     rows = list->rows;
00748     /* Number of visible rows. */
00749     max_rows = list->max_rows;
00750 
00751     /* Scrolling upward. */
00752     if (up)
00753     {
00754         row_selected -= scroll;
00755 
00756         /* Adjust row offset if needed. */
00757         if (row_offset > (row_selected - 1))
00758         {
00759             row_offset -= scroll;
00760         }
00761     }
00762     /* Downward otherwise. */
00763     else
00764     {
00765         row_selected += scroll;
00766 
00767         /* Adjust row offset if needed. */
00768         if (row_selected >= max_rows + row_offset)
00769         {
00770             row_offset += scroll;
00771         }
00772     }
00773 
00774     /* Make sure row offset is within bounds. */
00775     if (row_offset < 0 || rows < max_rows)
00776     {
00777         row_offset = 0;
00778     }
00779     else if (row_offset >= rows - max_rows)
00780     {
00781         row_offset = rows - max_rows;
00782     }
00783 
00784     /* Make sure selected row is within bounds. */
00785     if (row_selected < 1)
00786     {
00787         row_selected = 1;
00788     }
00789     else if (row_selected >= rows)
00790     {
00791         row_selected = list->rows;
00792     }
00793 
00794     /* Set the values. */
00795     list->row_selected = row_selected;
00796     list->row_offset = row_offset;
00797 }
00798 
00804 static int list_handle_key(list_struct *list, SDLKey key)
00805 {
00806     if (list->key_event_func)
00807     {
00808         int ret = list->key_event_func(list, key);
00809 
00810         if (ret != -1)
00811         {
00812             return ret;
00813         }
00814     }
00815 
00816     switch (key)
00817     {
00818         /* Up arrow. */
00819         case SDLK_UP:
00820             list_scroll(list, 1, 1);
00821             break;
00822 
00823         /* Down arrow. */
00824         case SDLK_DOWN:
00825             list_scroll(list, 0, 1);
00826             break;
00827 
00828         /* Page up. */
00829         case SDLK_PAGEUP:
00830             list_scroll(list, 1, list->max_rows);
00831             break;
00832 
00833         /* Page down. */
00834         case SDLK_PAGEDOWN:
00835             list_scroll(list, 0, list->max_rows);
00836             break;
00837 
00838         /* Esc, let the list creator handle this if they want to. */
00839         case SDLK_ESCAPE:
00840             if (list->handle_esc_func)
00841             {
00842                 list->handle_esc_func(list);
00843             }
00844 
00845             return 0;
00846 
00847         /* Enter. */
00848         case SDLK_RETURN:
00849         case SDLK_KP_ENTER:
00850             if (list->handle_enter_func)
00851             {
00852                 list->handle_enter_func(list);
00853             }
00854 
00855             return 0;
00856 
00857         /* Unhandled key. */
00858         default:
00859             return 0;
00860     }
00861 
00862     return 1;
00863 }
00864 
00870 int list_handle_keyboard(list_struct *list, SDL_KeyboardEvent *event)
00871 {
00872     if (event->type == SDL_KEYDOWN)
00873     {
00874         /* Rotate between lists using tab. */
00875         if (event->keysym.sym == SDLK_TAB)
00876         {
00877             /* Go backwards? */
00878             if (event->keysym.mod & KMOD_SHIFT)
00879             {
00880                 /* Previous list. */
00881                 if (list->prev)
00882                 {
00883                     list_set_focus(list->prev);
00884                 }
00885                 /* Last one. */
00886                 else
00887                 {
00888                     list_set_focus(list_tail);
00889                 }
00890             }
00891             else
00892             {
00893                 /* Next list exists? */
00894                 if (list->next)
00895                 {
00896                     list_set_focus(list->next);
00897                 }
00898                 /* First one otherwise. */
00899                 else
00900                 {
00901                     list_set_focus(list_head);
00902                 }
00903             }
00904 
00905             return 1;
00906         }
00907 
00908         /* Handle the key. */
00909         if (list_handle_key(list, event->keysym.sym))
00910         {
00911             /* Store the pressed key and ticks for repeating. */
00912             list->repeat_key = event->keysym.sym;
00913             list->repeat_key_ticks = LastTick + KEY_REPEAT_DELAY_INIT;
00914         }
00915 
00916         return 1;
00917     }
00918     /* Key was released. */
00919     else if (event->type == SDL_KEYUP)
00920     {
00921         /* If the key is the one we stored previously, reset it. */
00922         if (event->keysym.sym == (SDLKey) list->repeat_key)
00923         {
00924             list->repeat_key = -1;
00925         }
00926     }
00927 
00928     return 0;
00929 }
00930 
00935 int lists_handle_keyboard(SDL_KeyboardEvent *event)
00936 {
00937     list_struct *list = list_get_focused();
00938 
00939     /* No list exists. */
00940     if (!list)
00941     {
00942         return 0;
00943     }
00944 
00945     if (list->surface != ScreenSurface)
00946     {
00947         return 0;
00948     }
00949 
00950     return list_handle_keyboard(list, event);
00951 }
00952 
00961 int list_handle_mouse(list_struct *list, int mx, int my, SDL_Event *event)
00962 {
00963     uint32 row;
00964 
00965     if (!LIST_MOUSE_OVER(list, mx, my) && !list->scrollbar_dragging)
00966     {
00967         return 0;
00968     }
00969 
00970     /* Left mouse button was pressed, update focused list. */
00971     if (event->type == SDL_MOUSEBUTTONDOWN)
00972     {
00973         list_set_focus(list);
00974 
00975         if (event->button.button == SDL_BUTTON_LEFT)
00976         {
00977             SDL_Rect scrollbar_box;
00978 
00979             if (list_slider_get_size(list, &scrollbar_box) && mx >= scrollbar_box.x && mx < scrollbar_box.x + scrollbar_box.w && my >= scrollbar_box.y && my < scrollbar_box.y + list->scrollbar_h)
00980             {
00981                 old_scrollbar_pos = event->motion.y - list->scrollbar_y;
00982                 list->scrollbar_dragging = 1;
00983                 return 1;
00984             }
00985         }
00986     }
00987     else if (event->type == SDL_MOUSEBUTTONUP)
00988     {
00989         if (event->button.button == SDL_BUTTON_LEFT)
00990         {
00991             list->scrollbar_dragging = 0;
00992         }
00993 
00994         return 1;
00995     }
00996     else if (event->type == SDL_MOUSEMOTION)
00997     {
00998         if (list->scrollbar_dragging && !(SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_LEFT))
00999         {
01000             list->scrollbar_dragging = 0;
01001             return 0;
01002         }
01003 
01004         if (list->scrollbar_dragging)
01005         {
01006             SDL_Rect scrollbar_box;
01007 
01008             if (!list_scrollbar_get_size(list, &scrollbar_box))
01009             {
01010                 return 0;
01011             }
01012 
01013             list->scrollbar_y = event->motion.y - old_scrollbar_pos;
01014 
01015             if (list->scrollbar_y > scrollbar_box.h - list->scrollbar_h)
01016             {
01017                 list->scrollbar_y = scrollbar_box.h - list->scrollbar_h;
01018             }
01019 
01020             list->row_offset = MIN(list->rows - list->max_rows, MAX(0, list->scrollbar_y) * list->rows / scrollbar_box.h);
01021             list->row_selected = list->max_rows + list->row_offset - 1;
01022             return 1;
01023         }
01024     }
01025 
01026     if (mx >= list->x + list->width)
01027     {
01028         return 1;
01029     }
01030 
01031     /* No row is highlighted now. Will be switched back on as needed
01032      * below. */
01033     list->row_highlighted = 0;
01034 
01035     /* Handle mouse wheel for scrolling. */
01036     if (event->button.button == SDL_BUTTON_WHEELUP || event->button.button == SDL_BUTTON_WHEELDOWN)
01037     {
01038         list_scroll(list, event->button.button == SDL_BUTTON_WHEELUP, 1);
01039     }
01040 
01041     /* See which row the mouse is over. */
01042     for (row = list->row_offset; row < list->rows; row++)
01043     {
01044         /* Stop if we reached maximum number of visible rows. */
01045         if (LIST_ROW_OFFSET(row, list) == list->max_rows)
01046         {
01047             break;
01048         }
01049 
01050         /* Is the mouse over this row? */
01051         if ((uint32) my > (LIST_ROWS_START(list) + LIST_ROW_OFFSET(row, list) * LIST_ROW_HEIGHT(list)) + list->frame_offset && (uint32) my < LIST_ROWS_START(list) + (LIST_ROW_OFFSET(row, list) + 1) * LIST_ROW_HEIGHT(list))
01052         {
01053             if (list->handle_mouse_row_func)
01054             {
01055                 list->handle_mouse_row_func(list, row, event);
01056             }
01057 
01058             /* Mouse click? */
01059             if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_LEFT)
01060             {
01061                 /* See if we clicked on this row earlier, and whether this
01062                  * should be considered a double click. */
01063                 if (SDL_GetTicks() - list->click_tick < DOUBLE_CLICK_DELAY)
01064                 {
01065                     /* Double click, handle it as if enter was used. */
01066                     if (list->handle_enter_func)
01067                     {
01068                         list->handle_enter_func(list);
01069                         list->click_tick = 0;
01070                     }
01071 
01072                     /* Update selected row (in case enter handling
01073                      * function did not actually jump to another GUI,
01074                      * thus removing the need for this list). */
01075                     list->row_selected = row + 1;
01076                 }
01077                 /* Normal click. */
01078                 else
01079                 {
01080                     /* Update selected row and click ticks for above
01081                      * double click calculation. */
01082                     list->row_selected = row + 1;
01083                     list->click_tick = SDL_GetTicks();
01084                 }
01085             }
01086             /* Not a mouse click, so update highlighted row. */
01087             else
01088             {
01089                 list->row_highlighted = row + 1;
01090             }
01091 
01092             break;
01093         }
01094     }
01095 
01096     return 1;
01097 }
01098 
01106 int lists_handle_mouse(int mx, int my, SDL_Event *event)
01107 {
01108     list_struct *tmp;
01109 
01110     for (tmp = list_head; tmp; tmp = tmp->next)
01111     {
01112         if (tmp->surface == ScreenSurface && list_handle_mouse(tmp, mx, my, event))
01113         {
01114             return 1;
01115         }
01116     }
01117 
01118     return 0;
01119 }
01120 
01125 list_struct *list_exists(uint32 id)
01126 {
01127     list_struct *tmp;
01128 
01129     for (tmp = list_head; tmp; tmp = tmp->next)
01130     {
01131         if (tmp->id == id)
01132         {
01133             return tmp;
01134         }
01135     }
01136 
01137     return NULL;
01138 }
01139 
01145 static int list_compare_alpha(const void *a, const void *b)
01146 {
01147     return strcmp(((char ***) a)[0][0], ((char ***) b)[0][0]);
01148 }
01149 
01155 void list_sort(list_struct *list, int type)
01156 {
01157     if (!list->text)
01158     {
01159         return;
01160     }
01161 
01162     /* Alphabetical sort. */
01163     if (type == LIST_SORT_ALPHA)
01164     {
01165         qsort((void *) list->text, list->rows, sizeof(*list->text), (void *) (int (*)()) list_compare_alpha);
01166     }
01167 }
01168 
01175 int list_set_selected(list_struct *list, const char *str, uint32 col)
01176 {
01177     uint32 row;
01178 
01179     for (row = 0; row < list->rows; row++)
01180     {
01181         if (!strcmp(list->text[row][col], str))
01182         {
01183             list->row_selected = row + 1;
01184             list->row_offset = MIN(list->rows - list->max_rows, row);
01185             return 1;
01186         }
01187     }
01188 
01189     return 0;
01190 }