mirror of https://github.com/aseprite/aseprite.git
				
				
				
			
		
			
				
	
	
		
			460 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			460 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
| /* Jinete - a GUI library
 | |
|  * Copyright (c) 2003, 2004, 2005, 2007, David A. Capello
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions are
 | |
|  * met:
 | |
|  *
 | |
|  *   * Redistributions of source code must retain the above copyright
 | |
|  *     notice, this list of conditions and the following disclaimer.
 | |
|  *   * Redistributions in binary form must reproduce the above copyright
 | |
|  *     notice, this list of conditions and the following disclaimer in
 | |
|  *     the documentation and/or other materials provided with the
 | |
|  *     distribution.
 | |
|  *   * Neither the name of the Jinete nor the names of its contributors may
 | |
|  *     be used to endorse or promote products derived from this software
 | |
|  *     without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
|  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
|  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | |
|  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | |
|  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
|  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | |
|  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | |
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <allegro.h>
 | |
| 
 | |
| #include "jinete.h"
 | |
| 
 | |
| typedef struct ComboBox
 | |
| {
 | |
|   JWidget entry;
 | |
|   JWidget button;
 | |
|   JWidget window;
 | |
|   JList items;
 | |
|   int selected;
 | |
|   bool editable : 1;
 | |
|   bool clickopen : 1;
 | |
|   bool casesensitive : 1;
 | |
| } ComboBox;
 | |
| 
 | |
| #define COMBOBOX(widget) ((ComboBox *)jwidget_get_data(widget, JI_COMBOBOX))
 | |
| #define IS_VALID_ITEM(widget, index)					\
 | |
|   (index >= 0 && index < jlist_length(COMBOBOX(widget)->items))
 | |
| 
 | |
| static bool combobox_msg_proc(JWidget widget, JMessage msg);
 | |
| static bool combobox_entry_msg_proc(JWidget widget, JMessage msg);
 | |
| static bool combobox_listbox_msg_proc(JWidget widget, JMessage msg);
 | |
| static void combobox_button_cmd(JWidget widget, void *data);
 | |
| static void combobox_open_window(JWidget widget);
 | |
| static void combobox_close_window(JWidget widget);
 | |
| static void combobox_switch_window(JWidget widget);
 | |
| static JRect combobox_get_windowpos(ComboBox *combobox);
 | |
| 
 | |
| JWidget jcombobox_new (void)
 | |
| {
 | |
|   JWidget widget = jbox_new(JI_HORIZONTAL);
 | |
|   ComboBox *combobox = jnew(ComboBox, 1);
 | |
| 
 | |
|   combobox->entry = jentry_new (256, "");
 | |
|   combobox->button = jbutton_new ("^");
 | |
|   combobox->window = NULL;
 | |
|   combobox->items = jlist_new();
 | |
|   combobox->selected = 0;
 | |
|   combobox->editable = FALSE;
 | |
|   combobox->clickopen = TRUE;
 | |
|   combobox->casesensitive = TRUE;
 | |
| 
 | |
|   combobox->entry->user_data[0] = widget;
 | |
|   combobox->button->user_data[0] = widget;
 | |
| 
 | |
|   /* TODO this separation should be from the JTheme */
 | |
|   widget->child_spacing = 0;
 | |
| 
 | |
|   jwidget_focusrest(widget, TRUE);
 | |
|   jwidget_add_hook(widget, JI_COMBOBOX, combobox_msg_proc, combobox);
 | |
|   jwidget_add_hook(combobox->entry, JI_WIDGET, combobox_entry_msg_proc, NULL);
 | |
| 
 | |
|   jwidget_expansive(combobox->entry, TRUE);
 | |
|   jbutton_set_bevel(combobox->button, 0, 2, 0, 2);
 | |
|   jbutton_add_command_data(combobox->button, combobox_button_cmd, widget);
 | |
| 
 | |
|   jwidget_add_child(widget, combobox->entry);
 | |
|   jwidget_add_child(widget, combobox->button);
 | |
| 
 | |
|   jcombobox_editable(widget, combobox->editable);
 | |
| 
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| void jcombobox_editable (JWidget widget, bool state)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data (widget, JI_COMBOBOX);
 | |
| 
 | |
|   combobox->editable = state;
 | |
| 
 | |
|   if (state) {
 | |
|     jentry_readonly (combobox->entry, FALSE);
 | |
|     jentry_show_cursor (combobox->entry);
 | |
|   }
 | |
|   else {
 | |
|     jentry_readonly (combobox->entry, TRUE);
 | |
|     jentry_hide_cursor (combobox->entry);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void jcombobox_clickopen (JWidget widget, bool state)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data (widget, JI_COMBOBOX);
 | |
| 
 | |
|   combobox->clickopen = state;
 | |
| }
 | |
| 
 | |
| void jcombobox_casesensitive (JWidget widget, bool state)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data (widget, JI_COMBOBOX);
 | |
| 
 | |
|   combobox->casesensitive = state;
 | |
| }
 | |
| 
 | |
| bool jcombobox_is_editable (JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data (widget, JI_COMBOBOX);
 | |
| 
 | |
|   return combobox->editable;
 | |
| }
 | |
| 
 | |
| bool jcombobox_is_clickopen (JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data (widget, JI_COMBOBOX);
 | |
| 
 | |
|   return combobox->clickopen;
 | |
| }
 | |
| 
 | |
| bool jcombobox_is_casesensitive (JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data (widget, JI_COMBOBOX);
 | |
| 
 | |
|   return combobox->casesensitive;
 | |
| }
 | |
| 
 | |
| void jcombobox_add_string(JWidget widget, const char *string)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
|   bool sel_first = jlist_empty(combobox->items);
 | |
| 
 | |
|   jlist_append(combobox->items, jstrdup(string));
 | |
| 
 | |
|   if (sel_first)
 | |
|     jcombobox_select_index(widget, 0);
 | |
| }
 | |
| 
 | |
| void jcombobox_del_string(JWidget widget, const char *string)
 | |
| {
 | |
|   jcombobox_del_index (widget, jcombobox_get_index (widget, string));
 | |
| }
 | |
| 
 | |
| void jcombobox_del_index(JWidget widget, int index)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
| 
 | |
|   jlist_remove(combobox->items, jlist_nth_data (combobox->items, index));
 | |
| }
 | |
| 
 | |
| void jcombobox_select_index(JWidget widget, int index)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data (widget, JI_COMBOBOX);
 | |
|   JLink link = jlist_nth_link (combobox->items, index);
 | |
|   if (link != combobox->items->end) {
 | |
|     combobox->selected = index;
 | |
|     jwidget_set_text (combobox->entry, link->data);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void jcombobox_select_string(JWidget widget, const char *string)
 | |
| {
 | |
|   jcombobox_select_index(widget, jcombobox_get_index(widget, string));
 | |
| }
 | |
| 
 | |
| int jcombobox_get_selected_index(JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
| 
 | |
|   return combobox->items ? combobox->selected: -1;
 | |
| }
 | |
| 
 | |
| const char *jcombobox_get_selected_string(JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
| 
 | |
|   return jlist_nth_data(combobox->items, combobox->selected);
 | |
| }
 | |
| 
 | |
| const char *jcombobox_get_string(JWidget widget, int index)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
| 
 | |
|   if (index >= 0 && index < jlist_length(combobox->items))
 | |
|     return jlist_nth_link(combobox->items, index)->data;
 | |
|   else
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| int jcombobox_get_index(JWidget widget, const char *string)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
|   int index = 0;
 | |
|   JLink link;
 | |
| 
 | |
|   JI_LIST_FOR_EACH(combobox->items, link) {
 | |
|     if ((combobox->casesensitive && ustrcmp(link->data, string) == 0) ||
 | |
| 	(!combobox->casesensitive && ustricmp(link->data, string) == 0))
 | |
|       return index;
 | |
|     index++;
 | |
|   }
 | |
| 
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| int jcombobox_get_count(JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
| 
 | |
|   return jlist_length(combobox->items);
 | |
| }
 | |
| 
 | |
| JWidget jcombobox_get_entry_widget(JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
| 
 | |
|   return combobox->entry;
 | |
| }
 | |
| 
 | |
| static bool combobox_msg_proc(JWidget widget, JMessage msg)
 | |
| {
 | |
|   switch (msg->type) {
 | |
| 
 | |
|     case JM_CLOSE:
 | |
|       combobox_close_window(widget);
 | |
|       break;
 | |
| 
 | |
|     case JM_DESTROY:
 | |
|       {
 | |
| 	ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
| 	JLink link;
 | |
| 
 | |
| 	JI_LIST_FOR_EACH(combobox->items, link)
 | |
| 	  jfree(link->data);
 | |
| 
 | |
| 	jlist_free(combobox->items);
 | |
| 	jfree(combobox);
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case JM_WINMOVE:
 | |
|       {
 | |
| 	ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
| 	if (combobox->window) {
 | |
| 	  JRect rc = combobox_get_windowpos(combobox);
 | |
| 	  jwindow_move(combobox->window, rc);
 | |
| 	  jrect_free(rc);
 | |
| 	}
 | |
|       }
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static bool combobox_entry_msg_proc(JWidget widget, JMessage msg)
 | |
| {
 | |
|   JWidget combo_widget = widget->user_data[0];
 | |
| 
 | |
|   switch (msg->type) {
 | |
| 
 | |
|     case JM_CHAR:
 | |
|       if (jwidget_has_focus(widget)) {
 | |
| 	if (!jcombobox_is_editable(combo_widget)) {
 | |
| 	  if (msg->key.scancode == KEY_SPACE ||
 | |
| 	      msg->key.scancode == KEY_ENTER ||
 | |
| 	      msg->key.scancode == KEY_ENTER_PAD) {
 | |
| 	    combobox_switch_window(combo_widget);
 | |
| 	    return TRUE;
 | |
| 	  }
 | |
| 	}
 | |
| 	else {
 | |
| 	  if (msg->key.scancode == KEY_ENTER ||
 | |
| 	      msg->key.scancode == KEY_ENTER_PAD) {
 | |
| 	    combobox_switch_window(combo_widget);
 | |
| 	    return TRUE;
 | |
| 	  }
 | |
| 	}
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case JM_BUTTONPRESSED:
 | |
|       if (jcombobox_is_clickopen(combo_widget)) {
 | |
| 	combobox_switch_window(combo_widget);
 | |
| 	/* combobox_open_window(combo_widget); */
 | |
|       }
 | |
| 
 | |
|       if (jcombobox_is_editable(combo_widget)) {
 | |
| 	jmanager_set_focus(widget);
 | |
|       }
 | |
|       else
 | |
| 	return TRUE;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static bool combobox_listbox_msg_proc(JWidget widget, JMessage msg)
 | |
| {
 | |
|   JWidget combo_widget = widget->user_data[0];
 | |
| 
 | |
|   switch (msg->type) {
 | |
| 
 | |
|     case JM_SIGNAL:
 | |
|       if (msg->signal.num == JI_SIGNAL_LISTBOX_CHANGE) {
 | |
| 	int index = jlistbox_get_selected_index(widget);
 | |
| 
 | |
| 	if (IS_VALID_ITEM(combo_widget, index)) {
 | |
| 	  jcombobox_select_index(combo_widget, index);
 | |
| 
 | |
| 	  jwidget_emit_signal(combo_widget, JI_SIGNAL_COMBOBOX_CHANGE);
 | |
| 	}
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case JM_BUTTONRELEASED:
 | |
|       {
 | |
| 	int index = jcombobox_get_selected_index(combo_widget);
 | |
| 
 | |
| 	if (IS_VALID_ITEM(combo_widget, index))
 | |
| 	  jwidget_emit_signal(combo_widget, JI_SIGNAL_COMBOBOX_SELECT);
 | |
| 
 | |
| 	combobox_close_window(combo_widget);
 | |
|       }
 | |
|       return TRUE;
 | |
| 
 | |
| /*     case JM_IDLE: { */
 | |
| /*       /\* if the user clicks outside the listbox *\/ */
 | |
| /*       if (!jmouse_b(1) && jmouse_b(0) && !jwidget_has_mouse(widget)) { */
 | |
| /* 	ComboBox *combobox = jwidget_get_data(combo_widget, JI_COMBOBOX); */
 | |
| 
 | |
| /* 	if (combobox->entry && !jwidget_has_mouse(combobox->entry) && */
 | |
| /* 	    combobox->button && !jwidget_has_mouse(combobox->button) && */
 | |
| /* 	    combobox->window && !jwidget_has_mouse(combobox->window)) { */
 | |
| /* 	  combobox_close_window(combo_widget); */
 | |
| /* 	  return TRUE; */
 | |
| /* 	} */
 | |
| /*       } */
 | |
| /*       break; */
 | |
| /*     } */
 | |
| 
 | |
|     case JM_CHAR:
 | |
|       if (jwidget_has_focus(widget)) {
 | |
| 	if (msg->key.scancode == KEY_SPACE ||
 | |
| 	    msg->key.scancode == KEY_ENTER ||
 | |
| 	    msg->key.scancode == KEY_ENTER_PAD) {
 | |
| 	  combobox_close_window(combo_widget);
 | |
| 	  return TRUE;
 | |
| 	}
 | |
|       }
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static void combobox_button_cmd(JWidget widget, void *data)
 | |
| {
 | |
|   combobox_switch_window((JWidget)data);
 | |
| }
 | |
| 
 | |
| static void combobox_open_window(JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
|   if (!combobox->window) {
 | |
|     JWidget view, listbox;
 | |
|     JLink link;
 | |
|     int size;
 | |
|     JRect rc;
 | |
| 
 | |
|     combobox->window = jwindow_new(NULL);
 | |
|     view = jview_new();
 | |
|     listbox = jlistbox_new();
 | |
| 
 | |
|     listbox->user_data[0] = widget;
 | |
|     jwidget_add_hook(listbox, JI_WIDGET, combobox_listbox_msg_proc, NULL);
 | |
| 
 | |
|     JI_LIST_FOR_EACH(combobox->items, link)
 | |
|       jwidget_add_child(listbox, jlistitem_new(link->data));
 | |
| 
 | |
|     jwindow_ontop(combobox->window, TRUE);
 | |
|     jwidget_noborders(combobox->window);
 | |
| 
 | |
|     size = jlist_length(combobox->items);
 | |
|     jwidget_set_static_size
 | |
|       (view,
 | |
|        jrect_w(combobox->entry->rc),
 | |
|        2+(2+jwidget_get_text_height(listbox))*MID(1, size, 10)+2);
 | |
| 
 | |
|     jwidget_add_child(combobox->window, view);
 | |
|     jview_attach(view, listbox);
 | |
| 
 | |
|     jwidget_signal_off(listbox);
 | |
|     jlistbox_select_index(listbox, combobox->selected);
 | |
|     jwidget_signal_on(listbox);
 | |
| 
 | |
|     jwindow_remap(combobox->window);
 | |
| 
 | |
|     rc = combobox_get_windowpos(combobox);
 | |
|     jwindow_position(combobox->window, rc->x1, rc->y1);
 | |
|     jrect_free(rc);
 | |
| 
 | |
|     jwindow_open_bg(combobox->window);
 | |
|     jmanager_set_focus(listbox);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void combobox_close_window(JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
|   if (combobox->window) {
 | |
|     jwindow_close(combobox->window, widget);
 | |
|     combobox->window = NULL;
 | |
| 
 | |
|     jmanager_set_focus(combobox->entry);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void combobox_switch_window(JWidget widget)
 | |
| {
 | |
|   ComboBox *combobox = jwidget_get_data(widget, JI_COMBOBOX);
 | |
|   if (!combobox->window)
 | |
|     combobox_open_window(widget);
 | |
|   else
 | |
|     combobox_close_window(widget);
 | |
| }
 | |
| 
 | |
| static JRect combobox_get_windowpos(ComboBox *combobox)
 | |
| {
 | |
|   JRect rc = jrect_new(combobox->entry->rc->x1,
 | |
| 		       combobox->entry->rc->y2,
 | |
| 		       combobox->entry->rc->x2,
 | |
| 		       combobox->entry->rc->y2+
 | |
| 		       jrect_h(combobox->window->rc));
 | |
|   if (rc->y2 > JI_SCREEN_H)
 | |
|     jrect_displace(rc, 0, -(jrect_h(rc)+jrect_h(combobox->entry->rc)));
 | |
|   return rc;
 | |
| }
 |