// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include "txt_io.h" #include "txt_widget.h" #include "txt_gui.h" #include "txt_desktop.h" typedef struct { char *signal_name; TxtWidgetSignalFunc func; void *user_data; } txt_callback_t; struct txt_callback_table_s { int refcount; txt_callback_t *callbacks; int num_callbacks; }; txt_callback_table_t *TXT_NewCallbackTable(void) { txt_callback_table_t *table; table = malloc(sizeof(txt_callback_table_t)); table->callbacks = NULL; table->num_callbacks = 0; table->refcount = 1; return table; } void TXT_RefCallbackTable(txt_callback_table_t *table) { ++table->refcount; } void TXT_UnrefCallbackTable(txt_callback_table_t *table) { int i; --table->refcount; if (table->refcount == 0) { // No more references to this table for (i=0; inum_callbacks; ++i) { free(table->callbacks[i].signal_name); } free(table->callbacks); free(table); } } void TXT_InitWidget(TXT_UNCAST_ARG(widget), txt_widget_class_t *widget_class) { TXT_CAST_ARG(txt_widget_t, widget); widget->widget_class = widget_class; widget->callback_table = TXT_NewCallbackTable(); widget->parent = NULL; // Not focused until we hear otherwise. widget->focused = 0; // Visible by default. widget->visible = 1; // Align left by default widget->align = TXT_HORIZ_LEFT; } void TXT_SignalConnect(TXT_UNCAST_ARG(widget), const char *signal_name, TxtWidgetSignalFunc func, void *user_data) { TXT_CAST_ARG(txt_widget_t, widget); txt_callback_table_t *table; txt_callback_t *callback; table = widget->callback_table; // Add a new callback to the table table->callbacks = realloc(table->callbacks, sizeof(txt_callback_t) * (table->num_callbacks + 1)); callback = &table->callbacks[table->num_callbacks]; ++table->num_callbacks; callback->signal_name = strdup(signal_name); callback->func = func; callback->user_data = user_data; } void TXT_EmitSignal(TXT_UNCAST_ARG(widget), const char *signal_name) { TXT_CAST_ARG(txt_widget_t, widget); txt_callback_table_t *table; int i; table = widget->callback_table; // Don't destroy the table while we're searching through it // (one of the callbacks may destroy this window) TXT_RefCallbackTable(table); // Search the table for all callbacks with this name and invoke // the functions. for (i=0; inum_callbacks; ++i) { if (!strcmp(table->callbacks[i].signal_name, signal_name)) { table->callbacks[i].func(widget, table->callbacks[i].user_data); } } // Finished using the table TXT_UnrefCallbackTable(table); } void TXT_CalcWidgetSize(TXT_UNCAST_ARG(widget)) { TXT_CAST_ARG(txt_widget_t, widget); widget->widget_class->size_calc(widget); } void TXT_DrawWidget(TXT_UNCAST_ARG(widget)) { TXT_CAST_ARG(txt_widget_t, widget); txt_saved_colors_t colors; // The drawing function might change the fg/bg colors, // so make sure we restore them after it's done. TXT_SaveColors(&colors); // For convenience... TXT_GotoXY(widget->x, widget->y); // Call drawer method widget->widget_class->drawer(widget); TXT_RestoreColors(&colors); } void TXT_DestroyWidget(TXT_UNCAST_ARG(widget)) { TXT_CAST_ARG(txt_widget_t, widget); widget->widget_class->destructor(widget); TXT_UnrefCallbackTable(widget->callback_table); free(widget); } int TXT_WidgetKeyPress(TXT_UNCAST_ARG(widget), int key) { TXT_CAST_ARG(txt_widget_t, widget); if (widget->widget_class->key_press != NULL) { return widget->widget_class->key_press(widget, key); } return 0; } void TXT_SetWidgetFocus(TXT_UNCAST_ARG(widget), int focused) { TXT_CAST_ARG(txt_widget_t, widget); if (widget == NULL) { return; } if (widget->focused != focused) { widget->focused = focused; if (widget->widget_class->focus_change != NULL) { widget->widget_class->focus_change(widget, focused); } } } void TXT_SetWidgetAlign(TXT_UNCAST_ARG(widget), txt_horiz_align_t horiz_align) { TXT_CAST_ARG(txt_widget_t, widget); widget->align = horiz_align; } void TXT_WidgetMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b) { TXT_CAST_ARG(txt_widget_t, widget); if (widget->widget_class->mouse_press != NULL) { widget->widget_class->mouse_press(widget, x, y, b); } } void TXT_LayoutWidget(TXT_UNCAST_ARG(widget)) { TXT_CAST_ARG(txt_widget_t, widget); if (widget->widget_class->layout != NULL) { widget->widget_class->layout(widget); } } int TXT_AlwaysSelectable(TXT_UNCAST_ARG(widget)) { return 1; } int TXT_NeverSelectable(TXT_UNCAST_ARG(widget)) { return 0; } int TXT_SelectableWidget(TXT_UNCAST_ARG(widget)) { TXT_CAST_ARG(txt_widget_t, widget); if (widget->widget_class->selectable != NULL) { return widget->widget_class->selectable(widget); } else { return 0; } } int TXT_ContainsWidget(TXT_UNCAST_ARG(haystack), TXT_UNCAST_ARG(needle)) { TXT_CAST_ARG(txt_widget_t, haystack); TXT_CAST_ARG(txt_widget_t, needle); while (needle != NULL) { if (needle == haystack) { return 1; } needle = needle->parent; } return 0; } int TXT_HoveringOverWidget(TXT_UNCAST_ARG(widget)) { TXT_CAST_ARG(txt_widget_t, widget); txt_window_t *active_window; int x, y; // We can only be hovering over widgets in the active window. active_window = TXT_GetActiveWindow(); if (active_window == NULL || !TXT_ContainsWidget(active_window, widget)) { return 0; } // Is the mouse cursor within the bounds of the widget? TXT_GetMousePosition(&x, &y); return (x >= widget->x && x < widget->x + widget->w && y >= widget->y && y < widget->y + widget->h); } void TXT_SetWidgetBG(TXT_UNCAST_ARG(widget)) { TXT_CAST_ARG(txt_widget_t, widget); if (widget->focused) { TXT_BGColor(TXT_COLOR_GREY, 0); } else if (TXT_HoveringOverWidget(widget)) { TXT_BGColor(TXT_HOVER_BACKGROUND, 0); } else { // Use normal window background. } }