ref: 1112e6d783cad35adf994f6281e619b27852c839
dir: /textscreen/txt_window.c/
// // 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 <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include "doomkeys.h" #include "txt_label.h" #include "txt_desktop.h" #include "txt_gui.h" #include "txt_io.h" #include "txt_main.h" #include "txt_separator.h" #include "txt_window.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <shellapi.h> #endif void TXT_SetWindowAction(txt_window_t *window, txt_horiz_align_t position, TXT_UNCAST_ARG(action)) { TXT_CAST_ARG(txt_widget_t, action); if (window->actions[position] != NULL) { TXT_DestroyWidget(window->actions[position]); } window->actions[position] = action; // Maintain parent pointer. if (action != NULL) { action->parent = &window->table.widget; } } txt_window_t *TXT_NewWindow(const char *title) { int i; txt_window_t *win; win = malloc(sizeof(txt_window_t)); TXT_InitTable(&win->table, 1); if (title == NULL) { win->title = NULL; } else { win->title = strdup(title); } win->x = TXT_SCREEN_W / 2; win->y = TXT_SCREEN_H / 2; win->horiz_align = TXT_HORIZ_CENTER; win->vert_align = TXT_VERT_CENTER; win->key_listener = NULL; win->mouse_listener = NULL; win->help_url = NULL; TXT_AddWidget(win, TXT_NewSeparator(NULL)); for (i=0; i<3; ++i) { win->actions[i] = NULL; } TXT_AddDesktopWindow(win); // Default actions TXT_SetWindowAction(win, TXT_HORIZ_LEFT, TXT_NewWindowEscapeAction(win)); TXT_SetWindowAction(win, TXT_HORIZ_RIGHT, TXT_NewWindowSelectAction(win)); return win; } void TXT_CloseWindow(txt_window_t *window) { int i; TXT_EmitSignal(window, "closed"); TXT_RemoveDesktopWindow(window); free(window->title); // Destroy all actions for (i=0; i<3; ++i) { if (window->actions[i] != NULL) { TXT_DestroyWidget(window->actions[i]); } } // Destroy table and window TXT_DestroyWidget(window); } static void CalcWindowPosition(txt_window_t *window) { switch (window->horiz_align) { case TXT_HORIZ_LEFT: window->window_x = window->x; break; case TXT_HORIZ_CENTER: window->window_x = window->x - (window->window_w / 2); break; case TXT_HORIZ_RIGHT: window->window_x = window->x - (window->window_w - 1); break; } switch (window->vert_align) { case TXT_VERT_TOP: window->window_y = window->y; break; case TXT_VERT_CENTER: window->window_y = window->y - (window->window_h / 2); break; case TXT_VERT_BOTTOM: window->window_y = window->y - (window->window_h - 1); break; } } static void LayoutActionArea(txt_window_t *window) { txt_widget_t *widget; int space_available; int space_left_offset; // We need to calculate the available horizontal space for the center // action widget, so that we can center it within it. // To start with, we have the entire action area available. space_available = window->window_w; space_left_offset = 0; // Left action if (window->actions[TXT_HORIZ_LEFT] != NULL) { widget = window->actions[TXT_HORIZ_LEFT]; TXT_CalcWidgetSize(widget); widget->x = window->window_x + 1; widget->y = window->window_y + window->window_h - widget->h - 1; // Adjust available space: space_available -= widget->w; space_left_offset += widget->w; TXT_LayoutWidget(widget); } // Draw the right action if (window->actions[TXT_HORIZ_RIGHT] != NULL) { widget = window->actions[TXT_HORIZ_RIGHT]; TXT_CalcWidgetSize(widget); widget->x = window->window_x + window->window_w - 1 - widget->w; widget->y = window->window_y + window->window_h - widget->h - 1; // Adjust available space: space_available -= widget->w; TXT_LayoutWidget(widget); } // Draw the center action if (window->actions[TXT_HORIZ_CENTER] != NULL) { widget = window->actions[TXT_HORIZ_CENTER]; TXT_CalcWidgetSize(widget); // The left and right widgets have left a space sandwiched between // them. Center this widget within that space. widget->x = window->window_x + space_left_offset + (space_available - widget->w) / 2; widget->y = window->window_y + window->window_h - widget->h - 1; TXT_LayoutWidget(widget); } } static void DrawActionArea(txt_window_t *window) { int i; for (i=0; i<3; ++i) { if (window->actions[i] != NULL) { TXT_DrawWidget(window->actions[i]); } } } static void CalcActionAreaSize(txt_window_t *window, unsigned int *w, unsigned int *h) { txt_widget_t *widget; int i; *w = 0; *h = 0; // Calculate the width of all the action widgets and use this // to create an overall min. width of the action area for (i=0; i<3; ++i) { widget = (txt_widget_t *) window->actions[i]; if (widget != NULL) { TXT_CalcWidgetSize(widget); *w += widget->w; if (widget->h > *h) { *h = widget->h; } } } } // Sets size and position of all widgets in a window void TXT_LayoutWindow(txt_window_t *window) { txt_widget_t *widgets = (txt_widget_t *) window; unsigned int widgets_w; unsigned int actionarea_w, actionarea_h; // Calculate size of table TXT_CalcWidgetSize(window); // Widgets area: add one character of padding on each side widgets_w = widgets->w + 2; // Calculate the size of the action area // Make window wide enough to action area CalcActionAreaSize(window, &actionarea_w, &actionarea_h); if (actionarea_w > widgets_w) widgets_w = actionarea_w; // Set the window size based on widgets_w window->window_w = widgets_w + 2; window->window_h = widgets->h + 1; // If the window has a title, add an extra two lines if (window->title != NULL) { window->window_h += 2; } // If the window has an action area, add extra lines if (actionarea_h > 0) { window->window_h += actionarea_h + 1; } // Use the x,y position as the centerpoint and find the location to // draw the window. CalcWindowPosition(window); // Set the table size and position widgets->w = widgets_w - 2; // widgets->h (already set) widgets->x = window->window_x + 2; widgets->y = window->window_y; if (window->title != NULL) { widgets->y += 2; } // Layout the table and action area LayoutActionArea(window); TXT_LayoutWidget(widgets); } void TXT_DrawWindow(txt_window_t *window) { txt_widget_t *widgets; TXT_LayoutWindow(window); if (window->table.widget.focused) { TXT_BGColor(TXT_ACTIVE_WINDOW_BACKGROUND, 0); } else { TXT_BGColor(TXT_INACTIVE_WINDOW_BACKGROUND, 0); } TXT_FGColor(TXT_COLOR_BRIGHT_WHITE); // Draw the window TXT_DrawWindowFrame(window->title, window->window_x, window->window_y, window->window_w, window->window_h); // Draw all widgets TXT_DrawWidget(window); // Draw an action area, if we have one widgets = (txt_widget_t *) window; if (widgets->y + widgets->h < window->window_y + window->window_h - 1) { // Separator for action area TXT_DrawSeparator(window->window_x, widgets->y + widgets->h, window->window_w); // Action area at the window bottom DrawActionArea(window); } } void TXT_SetWindowPosition(txt_window_t *window, txt_horiz_align_t horiz_align, txt_vert_align_t vert_align, int x, int y) { window->vert_align = vert_align; window->horiz_align = horiz_align; window->x = x; window->y = y; } static int MouseButtonPress(txt_window_t *window, int b) { int x, y; int i; txt_widget_t *widgets; txt_widget_t *widget; // Lay out the window, set positions and sizes of all widgets TXT_LayoutWindow(window); // Get the current mouse position TXT_GetMousePosition(&x, &y); // Try the mouse button listener // This happens whether it is in the window range or not if (window->mouse_listener != NULL) { // Mouse listener can eat button presses if (window->mouse_listener(window, x, y, b, window->mouse_listener_data)) { return 1; } } // Is it within the table range? widgets = (txt_widget_t *) window; if (x >= widgets->x && x < (signed) (widgets->x + widgets->w) && y >= widgets->y && y < (signed) (widgets->y + widgets->h)) { TXT_WidgetMousePress(window, x, y, b); return 1; } // Was one of the action area buttons pressed? for (i=0; i<3; ++i) { widget = window->actions[i]; if (widget != NULL && x >= widget->x && x < (signed) (widget->x + widget->w) && y >= widget->y && y < (signed) (widget->y + widget->h)) { int was_focused; // Main table temporarily loses focus when action area button // is clicked. This way, any active input boxes that depend // on having focus will save their values before the // action is performed. was_focused = window->table.widget.focused; TXT_SetWidgetFocus(window, 0); TXT_SetWidgetFocus(window, was_focused); // Pass through mouse press. TXT_WidgetMousePress(widget, x, y, b); return 1; } } return 0; } int TXT_WindowKeyPress(txt_window_t *window, int c) { int i; // Is this a mouse button ? if (c >= TXT_MOUSE_BASE && c < TXT_MOUSE_BASE + TXT_MAX_MOUSE_BUTTONS) { return MouseButtonPress(window, c); } // Try the window key spy if (window->key_listener != NULL) { // key listener can eat keys if (window->key_listener(window, c, window->key_listener_data)) { return 1; } } // Send to the currently selected widget: if (TXT_WidgetKeyPress(window, c)) { return 1; } // Try all of the action buttons for (i=0; i<3; ++i) { if (window->actions[i] != NULL && TXT_WidgetKeyPress(window->actions[i], c)) { return 1; } } return 0; } void TXT_SetKeyListener(txt_window_t *window, TxtWindowKeyPress key_listener, void *user_data) { window->key_listener = key_listener; window->key_listener_data = user_data; } void TXT_SetMouseListener(txt_window_t *window, TxtWindowMousePress mouse_listener, void *user_data) { window->mouse_listener = mouse_listener; window->mouse_listener_data = user_data; } void TXT_SetWindowFocus(txt_window_t *window, int focused) { TXT_SetWidgetFocus(window, focused); } void TXT_SetWindowHelpURL(txt_window_t *window, const char *help_url) { window->help_url = help_url; } #ifdef _WIN32 void TXT_OpenURL(const char *url) { ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); } #else void TXT_OpenURL(const char *url) { char *cmd; size_t cmd_len; int retval; cmd_len = strlen(url) + 30; cmd = malloc(cmd_len); #if defined(__MACOSX__) TXT_snprintf(cmd, cmd_len, "open \"%s\"", url); #else // The Unix situation sucks as usual, but the closest thing to a // standard that exists is the xdg-utils package. if (system("xdg-open --version 2>/dev/null") != 0) { fprintf(stderr, "xdg-utils is not installed. Can't open this URL:\n%s\n", url); free(cmd); return; } TXT_snprintf(cmd, cmd_len, "xdg-open \"%s\"", url); #endif retval = system(cmd); if (retval != 0) { fprintf(stderr, "TXT_OpenURL: error executing '%s'; return code %d\n", cmd, retval); } free(cmd); } #endif /* #ifndef _WIN32 */ void TXT_OpenWindowHelpURL(txt_window_t *window) { if (window->help_url != NULL) { TXT_OpenURL(window->help_url); } } txt_window_t *TXT_MessageBox(const char *title, const char *message, ...) { txt_window_t *window; char buf[256]; va_list args; va_start(args, message); TXT_vsnprintf(buf, sizeof(buf), message, args); va_end(args); window = TXT_NewWindow(title); TXT_AddWidget(window, TXT_NewLabel(buf)); TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL); TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TXT_NewWindowEscapeAction(window)); TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL); return window; }