ref: 3ac144025a055a3b85c1f4b2ef05b6f2892788f5
parent: adbfa3216cf3ef6f71cc5b6b22c25a04ae737212
author: Simon Howard <[email protected]>
date: Sun Mar 31 14:46:25 EDT 2013
Add file selector widget to textscreen library. Subversion-branch: /trunk/chocolate-doom Subversion-revision: 2570
--- a/textscreen/Makefile.am
+++ b/textscreen/Makefile.am
@@ -16,6 +16,7 @@
txt_checkbox.c txt_checkbox.h \
txt_desktop.c txt_desktop.h \
txt_dropdown.c txt_dropdown.h \
+ txt_fileselect.c txt_fileselect.h \
txt_gui.c txt_gui.h \
txt_inputbox.c txt_inputbox.h \
txt_io.c txt_io.h \
--- a/textscreen/examples/guitest.c
+++ b/textscreen/examples/guitest.c
@@ -39,10 +39,13 @@
RADIO_VALUE_MUSHROOM,
RADIO_VALUE_SNAKE,
};
+char *extensions[] = { "wad", "lmp", "txt", NULL };
char *radio_values[] = { "Badger", "Mushroom", "Snake" };
char *textbox_value = NULL;
int numbox_value = 0;
int radiobutton_value;
+char *file_path = NULL;
+char *dir_path = NULL;
txt_label_t *value_label;
txt_window_t *firstwin;
int cheesy;
@@ -187,12 +190,20 @@
TXT_AddWidget(window, TXT_NewSeparator("Input boxes"));
table = TXT_NewTable(2);
TXT_AddWidget(window, table);
- TXT_AddWidget(table, TXT_NewLabel("String: "));
- TXT_AddWidget(table, TXT_NewInputBox(&textbox_value, 20));
- TXT_AddWidget(table, TXT_NewLabel("Int: "));
- TXT_AddWidget(table, TXT_NewIntInputBox(&numbox_value, 10));
- TXT_AddWidget(table, TXT_NewLabel("Spin control:"));
- TXT_AddWidget(table, TXT_NewSpinControl(&numbox_value, 0, 15));
+ TXT_AddWidgets(table,
+ TXT_NewLabel("String: "),
+ TXT_NewInputBox(&textbox_value, 20),
+ TXT_NewLabel("Int: "),
+ TXT_NewIntInputBox(&numbox_value, 10),
+ TXT_NewLabel("Spin control:"),
+ TXT_NewSpinControl(&numbox_value, 0, 15),
+ TXT_NewLabel("File:"),
+ TXT_NewFileSelector(&file_path, 28, "Select file:",
+ extensions),
+ TXT_NewLabel("Directory:"),
+ TXT_NewFileSelector(&dir_path, 28, "Select directory:",
+ TXT_DIRECTORY),
+ NULL);
TXT_AddWidget(window, TXT_NewSeparator("Scroll pane test"));
scrollpane = TXT_NewScrollPane(40, 5, TXT_NewLabel(
--- a/textscreen/textscreen.h
+++ b/textscreen/textscreen.h
@@ -29,6 +29,7 @@
#include "txt_checkbox.h"
#include "txt_desktop.h"
#include "txt_dropdown.h"
+#include "txt_fileselect.h"
#include "txt_inputbox.h"
#include "txt_label.h"
#include "txt_radiobutton.h"
--- /dev/null
+++ b/textscreen/txt_fileselect.c
@@ -1,0 +1,678 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2013 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Routines for selecting files.
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomkeys.h"
+
+#include "txt_fileselect.h"
+#include "txt_inputbox.h"
+#include "txt_main.h"
+#include "txt_widget.h"
+
+struct txt_fileselect_s {
+ txt_widget_t widget;
+ txt_inputbox_t *inputbox;
+ int size;
+ char *prompt;
+ char **extensions;
+};
+
+// Dummy value to select a directory.
+
+char *TXT_DIRECTORY[] = { "__directory__", NULL };
+
+#ifndef _WIN32
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+static char *ExecReadOutput(char **argv)
+{
+ char *result;
+ int completed;
+ int pid, status, result_len;
+ int pipefd[2];
+
+ if (pipe(pipefd) != 0)
+ {
+ return NULL;
+ }
+
+ pid = fork();
+
+ if (pid == 0)
+ {
+ dup2(pipefd[1], fileno(stdout));
+ execv(argv[0], argv);
+ exit(-1);
+ }
+
+ fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
+
+ // Read program output into 'result' string.
+ // Wait until the program has completed and (if it was successful)
+ // a full line has been read.
+
+ result = NULL;
+ result_len = 0;
+ completed = 0;
+
+ while (!completed
+ || (status == 0 && (result == NULL || strchr(result, '\n') == NULL)))
+ {
+ char buf[64];
+ int bytes;
+
+ if (!completed && waitpid(pid, &status, WNOHANG) != 0)
+ {
+ completed = 1;
+ }
+
+ bytes = read(pipefd[0], buf, sizeof(buf));
+
+ if (bytes < 0)
+ {
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ {
+ status = -1;
+ break;
+ }
+ }
+ else
+ {
+ result = realloc(result, result_len + bytes + 1);
+ memcpy(result + result_len, buf, bytes);
+ result_len += bytes;
+ result[result_len] = '\0';
+ }
+
+ TXT_Sleep(25);
+ TXT_UpdateScreen();
+ }
+
+ close(pipefd[0]);
+ close(pipefd[1]);
+
+ // Must have a success exit code.
+
+ if (WEXITSTATUS(status) != 0)
+ {
+ free(result);
+ result = NULL;
+ }
+
+ // Strip off newline from the end.
+
+ if (result != NULL && result[result_len - 1] == '\n')
+ {
+ result[result_len - 1] = '\0';
+ }
+
+ return result;
+}
+
+#endif
+
+#if defined(_WIN32)
+
+// Windows code. Use comdlg32 to pop up a dialog box.
+
+#include <windows.h>
+#include <shlobj.h>
+
+static BOOL WINAPI (*MyGetOpenFileName)(LPOPENFILENAME) = NULL;
+static PIDLIST_ABSOLUTE (*MySHBrowseForFolder)(LPBROWSEINFO) = NULL;
+static BOOL (*MySHGetPathFromIDList)(PCIDLIST_ABSOLUTE, LPTSTR) = NULL;
+
+// Load library functions from DLL files.
+
+static int LoadDLLs(void)
+{
+ HMODULE comdlg32 = LoadLibraryW(L"comdlg32.dll");
+ HMODULE shell32 = LoadLibraryW(L"shell32.dll");
+
+ if (comdlg32 == NULL || shell32 == NULL)
+ {
+ return 0;
+ }
+
+ MyGetOpenFileName =
+ (void *) GetProcAddress(comdlg32, "GetOpenFileNameA");
+ MySHBrowseForFolder =
+ (void *) GetProcAddress(shell32, "SHBrowseForFolder");
+ MySHGetPathFromIDList =
+ (void *) GetProcAddress(shell32, "SHGetPathFromIDList");
+
+ return MyGetOpenFileName != NULL
+ && MySHBrowseForFolder != NULL
+ && MySHGetPathFromIDList != NULL;
+}
+
+static InitLibraries(void)
+{
+ static int initted = 0, success = 0;
+
+ if (!initted)
+ {
+ success = LoadDLLs();
+ initted = 1;
+ }
+
+ return success;
+}
+
+// Generate the "filter" string from the list of extensions.
+
+static char *GenerateFilterString(char **extensions)
+{
+ unsigned int result_len = 1;
+ unsigned int i;
+ char *result, *out;
+
+ if (extensions == NULL)
+ {
+ return NULL;
+ }
+
+ for (i = 0; extensions[i] != NULL; ++i)
+ {
+ result_len += 16 + strlen(extensions[i]) * 3;
+ }
+
+ result = malloc(result_len);
+ out = result;
+
+ for (i = 0; extensions[i] != NULL; ++i)
+ {
+ // .wad files (*.wad)\0
+ out += 1 + sprintf(out, "%s files (*.%s)",
+ extensions[i], extensions[i]);
+ // *.wad\0
+ out += 1 + sprintf(out, "*.%s", extensions[i]);
+ }
+
+ *out = '\0';
+
+ return result;
+}
+
+int TXT_CanSelectFiles(void)
+{
+ return InitLibraries();
+}
+
+static char *SelectDirectory(char *window_title)
+{
+ LPITEMIDLIST pidl;
+ BROWSEINFO bi;
+ LPMALLOC allocator;
+ char selected[MAX_PATH] = "";
+ char *result;
+
+ ZeroMemory(&bi, sizeof(bi));
+ bi.hwndOwner = NULL;
+ bi.lpszTitle = window_title;
+ bi.pszDisplayName = selected;
+ bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
+
+ pidl = MySHBrowseForFolder(&bi);
+
+ result = NULL;
+
+ if (pidl != NULL)
+ {
+ if (MySHGetPathFromIDList(pidl, selected))
+ {
+ result = strdup(selected);
+ }
+
+ // TODO: Free pidl
+ }
+
+ return result;
+}
+
+char *TXT_SelectFile(char *window_title, char **extensions)
+{
+ OPENFILENAME fm;
+ char selected[MAX_PATH] = "";
+ char *filter_string, *result;
+
+ if (!InitLibraries())
+ {
+ return NULL;
+ }
+
+ if (extensions == TXT_DIRECTORY)
+ {
+ return SelectDirectory(window_title);
+ }
+
+ filter_string = GenerateFilterString(extensions);
+
+ ZeroMemory(&fm, sizeof(fm));
+ fm.lStructSize = sizeof(OPENFILENAME);
+ fm.hwndOwner = NULL;
+ fm.lpstrTitle = window_title;
+ fm.lpstrFilter = filter_string;
+ fm.lpstrFile = selected;
+ fm.nMaxFile = MAX_PATH;
+ fm.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+ fm.lpstrDefExt = "";
+
+ if (!MyGetOpenFileName(&fm))
+ {
+ result = NULL;
+ }
+ else
+ {
+ result = strdup(selected);
+ }
+
+ free(filter_string);
+
+ return result;
+}
+
+#elif defined(__MACOSX__)
+
+// Mac OS X code. Popping up a dialog requires Objective C/Cocoa
+// but we can get away with using AppleScript which avoids adding
+// an Objective C dependency. This is rather silly.
+
+// Printf format string for the "wrapper" portion of the AppleScript:
+
+#define APPLESCRIPT_WRAPPER \
+ "tell application (path to frontmost application as text)\n" \
+ " set theFile to (%s)\n" \
+ " copy POSIX path of theFile to stdout\n" \
+ "end tell\n"
+
+// "tell app appname\n"
+
+static char *EscapedString(char *s)
+{
+ char *result;
+ char *in, *out;
+
+ result = malloc(strlen(s) + 3);
+ out = result;
+ *out++ = '\"';
+ for (in = s; *in != '\0'; ++in)
+ {
+ if (*in == '\"' || *in == '\\')
+ {
+ *out++ = '\\';
+ }
+ *out++ = *in;
+ }
+ *out++ = '\"';
+ *out = '\0';
+
+ return result;
+}
+
+// Build list of extensions, like: {"wad","lmp","txt"}
+
+static char *ExtensionsList(char **extensions)
+{
+ char *result, *escaped;
+ unsigned int result_len;
+ unsigned int i;
+
+ if (extensions == NULL)
+ {
+ return NULL;
+ }
+
+ result_len = 3;
+ for (i = 0; extensions[i] != NULL; ++i)
+ {
+ result_len += 5 + strlen(extensions[i]) * 2;
+ }
+
+ result = malloc(result_len);
+ strcpy(result, "{");
+
+ for (i = 0; extensions[i] != NULL; ++i)
+ {
+ escaped = EscapedString(extensions[i]);
+ strcat(result, escaped);
+ free(escaped);
+
+ if (extensions[i + 1] != NULL)
+ strcat(result, ",");
+ }
+
+ strcat(result, "}");
+
+ return result;
+}
+
+static char *GenerateSelector(char *window_title, char **extensions)
+{
+ char *chooser, *ext_list, *result;
+ unsigned int result_len;
+
+ result_len = 64;
+
+ if (extensions == TXT_DIRECTORY)
+ {
+ chooser = "choose folder";
+ ext_list = NULL;
+ }
+ else
+ {
+ chooser = "choose file";
+ ext_list = ExtensionsList(extensions);
+ }
+
+ // Calculate size.
+
+ if (window_title != NULL)
+ {
+ window_title = EscapedString(window_title);
+ result_len += strlen(window_title);
+ }
+ if (ext_list != NULL)
+ {
+ result_len += strlen(ext_list);
+ }
+
+ result = malloc(result_len);
+
+ strcpy(result, chooser);
+
+ if (window_title != NULL)
+ {
+ strcat(result, " with prompt ");
+ strcat(result, window_title);
+ free(window_title);
+ }
+
+ if (ext_list != NULL)
+ {
+ strcat(result, "of type ");
+ strcat(result, ext_list);
+ free(ext_list);
+ }
+
+ return result;
+}
+
+static char *GenerateAppleScript(char *window_title, char **extensions)
+{
+ char *selector, *result;
+
+ selector = GenerateSelector(window_title, extensions);
+
+ result = malloc(strlen(APPLESCRIPT_WRAPPER) + strlen(selector));
+ sprintf(result, APPLESCRIPT_WRAPPER, selector);
+ free(selector);
+
+ return result;
+}
+
+int TXT_CanSelectFiles(void)
+{
+ return 1;
+}
+
+char *TXT_SelectFile(char *window_title, char **extensions)
+{
+ char *argv[4];
+ char *result, *applescript;
+
+ applescript = GenerateAppleScript(window_title, extensions);
+
+ argv[0] = "/usr/bin/osascript";
+ argv[1] = "-e";
+ argv[2] = applescript;
+ argv[3] = NULL;
+
+ result = ExecReadOutput(argv);
+
+ free(applescript);
+
+ return result;
+}
+
+#else
+
+// Linux version: invoke the Zenity command line program to pop up a
+// dialog box. This avoids adding Gtk+ as a compile dependency.
+
+#define ZENITY_BINARY "/usr/bin/zenity"
+
+static unsigned int NumExtensions(char **extensions)
+{
+ unsigned int result = 0;
+
+ if (extensions != NULL)
+ {
+ for (result = 0; extensions[result] != NULL; ++result);
+ }
+
+ return result;
+}
+
+static int ZenityAvailable(void)
+{
+ return system(ZENITY_BINARY " --help >/dev/null 2>&1") == 0;
+}
+
+int TXT_CanSelectFiles(void)
+{
+ return ZenityAvailable();
+}
+
+char *TXT_SelectFile(char *window_title, char **extensions)
+{
+ unsigned int i;
+ char *result;
+ char **argv;
+ int argc;
+
+ if (!ZenityAvailable())
+ {
+ return NULL;
+ }
+
+ argv = calloc(4 + NumExtensions(extensions), sizeof(char *));
+ argv[0] = ZENITY_BINARY;
+ argv[1] = "--file-selection";
+ argc = 2;
+
+ if (window_title != NULL)
+ {
+ argv[argc] = malloc(10 + strlen(window_title));
+ sprintf(argv[argc], "--title=%s", window_title);
+ ++argc;
+ }
+
+ if (extensions == TXT_DIRECTORY)
+ {
+ argv[argc] = strdup("--directory");
+ ++argc;
+ }
+ else if (extensions != NULL)
+ {
+ for (i = 0; extensions[i] != NULL; ++i)
+ {
+ argv[argc] = malloc(30 + strlen(extensions[i]) * 2);
+ sprintf(argv[argc], "--file-filter=.%s | *.%s",
+ extensions[i], extensions[i]);
+ ++argc;
+ }
+ }
+
+ argv[argc] = NULL;
+
+ result = ExecReadOutput(argv);
+
+ for (i = 2; i < argc; ++i)
+ {
+ free(argv[i]);
+ }
+
+ free(argv);
+
+ return result;
+}
+
+#endif
+
+static void TXT_FileSelectSizeCalc(TXT_UNCAST_ARG(fileselect))
+{
+ TXT_CAST_ARG(txt_fileselect_t, fileselect);
+
+ // Calculate widget size, but override the width to always
+ // be the configured size.
+
+ TXT_CalcWidgetSize(fileselect->inputbox);
+ fileselect->widget.w = fileselect->size;
+ fileselect->widget.h = fileselect->inputbox->widget.h;
+}
+
+static void TXT_FileSelectDrawer(TXT_UNCAST_ARG(fileselect))
+{
+ TXT_CAST_ARG(txt_fileselect_t, fileselect);
+
+ // Input box widget inherits all the properties of the
+ // file selector.
+
+ fileselect->inputbox->widget.x = fileselect->widget.x;
+ fileselect->inputbox->widget.y = fileselect->widget.y;
+ fileselect->inputbox->widget.w = fileselect->widget.w;
+ fileselect->inputbox->widget.h = fileselect->widget.h;
+
+ TXT_DrawWidget(fileselect->inputbox);
+}
+
+static void TXT_FileSelectDestructor(TXT_UNCAST_ARG(fileselect))
+{
+ TXT_CAST_ARG(txt_fileselect_t, fileselect);
+
+ TXT_DestroyWidget(fileselect->inputbox);
+}
+
+static int DoSelectFile(txt_fileselect_t *fileselect)
+{
+ char *path;
+ char **var;
+
+ if (TXT_CanSelectFiles())
+ {
+ path = TXT_SelectFile(fileselect->prompt,
+ fileselect->extensions);
+ var = fileselect->inputbox->value;
+ free(*var);
+ *var = path;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int TXT_FileSelectKeyPress(TXT_UNCAST_ARG(fileselect), int key)
+{
+ TXT_CAST_ARG(txt_fileselect_t, fileselect);
+
+ // When the enter key is pressed, pop up a file selection dialog,
+ // if file selectors work. Allow holding down 'alt' to override
+ // use of the native file selector, so the user can just type a path.
+
+ if (!fileselect->inputbox->editing
+ && !TXT_GetModifierState(TXT_MOD_ALT)
+ && key == KEY_ENTER)
+ {
+ if (DoSelectFile(fileselect))
+ {
+ return 1;
+ }
+ }
+
+ return TXT_WidgetKeyPress(fileselect->inputbox, key);
+}
+
+static void TXT_FileSelectMousePress(TXT_UNCAST_ARG(fileselect),
+ int x, int y, int b)
+{
+ TXT_CAST_ARG(txt_fileselect_t, fileselect);
+
+ if (!fileselect->inputbox->editing
+ && !TXT_GetModifierState(TXT_MOD_ALT)
+ && b == TXT_MOUSE_LEFT)
+ {
+ if (DoSelectFile(fileselect))
+ {
+ return;
+ }
+ }
+
+ return TXT_WidgetMousePress(fileselect->inputbox, x, y, b);
+}
+
+static void TXT_FileSelectFocused(TXT_UNCAST_ARG(fileselect), int focused)
+{
+ TXT_CAST_ARG(txt_fileselect_t, fileselect);
+
+ TXT_SetWidgetFocus(fileselect->inputbox, focused);
+}
+
+txt_widget_class_t txt_fileselect_class =
+{
+ TXT_AlwaysSelectable,
+ TXT_FileSelectSizeCalc,
+ TXT_FileSelectDrawer,
+ TXT_FileSelectKeyPress,
+ TXT_FileSelectDestructor,
+ TXT_FileSelectMousePress,
+ NULL,
+ TXT_FileSelectFocused,
+};
+
+txt_fileselect_t *TXT_NewFileSelector(char **variable, int size,
+ char *prompt, char **extensions)
+{
+ txt_fileselect_t *fileselect;
+
+ fileselect = malloc(sizeof(txt_fileselect_t));
+ TXT_InitWidget(fileselect, &txt_fileselect_class);
+ fileselect->inputbox = TXT_NewInputBox(variable, 1024);
+ fileselect->inputbox->widget.parent = &fileselect->widget;
+ fileselect->size = size;
+ fileselect->prompt = prompt;
+ fileselect->extensions = extensions;
+
+ return fileselect;
+}
+
--- /dev/null
+++ b/textscreen/txt_fileselect.h
@@ -1,0 +1,75 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2013 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Routines for selecting files, and the txt_fileselect_t widget.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef TXT_FILESELECT_H
+#define TXT_FILESELECT_H
+
+typedef struct txt_fileselect_s txt_fileselect_t;
+
+/**
+ * Returns non-zero if a native file selector is available on this
+ * platform.
+ */
+
+int TXT_CanSelectFiles(void);
+
+/**
+ * Open a native file selector to select a file.
+ *
+ * @param title Pointer to a string containing a prompt to display
+ * in the window.
+ * @param extensions NULL-terminated list of filename extensions for
+ * files that can be selected, or @ref TXT_DIRECTORY
+ * to select directories.
+ */
+
+char *TXT_SelectFile(char *window_title, char **extensions);
+
+/**
+ * Create a new txt_fileselect_t widget.
+ *
+ * @param variable Pointer to a char * variable in which the selected
+ * file should be stored.
+ * @param size Width of the file selector widget in characters.
+ * @param title Pointer to a string containing a prompt to display
+ * in the file selection window.
+ * @param extensions NULL-terminated list of filename extensions that
+ * can be used for this widget, or @ref TXT_DIRECTORY
+ * to select directories.
+ */
+
+txt_fileselect_t *TXT_NewFileSelector(char **variable, int size,
+ char *prompt, char **extensions);
+
+/**
+ * Special value to use for 'extensions' that selects a directory
+ * instead of a file.
+ */
+
+extern char *TXT_DIRECTORY[];
+
+#endif /* #ifndef TXT_FILESELECT_H */
+
--- a/textscreen/txt_inputbox.c
+++ b/textscreen/txt_inputbox.c
@@ -139,9 +139,21 @@
SetBufferFromValue(inputbox);
}
- TXT_DrawUTF8String(inputbox->buffer);
+ // If string size exceeds the widget's width, show only the end.
- chars = TXT_UTF8_Strlen(inputbox->buffer);
+ if (TXT_UTF8_Strlen(inputbox->buffer) > w - 1)
+ {
+ TXT_DrawString("\xae");
+ TXT_DrawUTF8String(
+ TXT_UTF8_SkipChars(inputbox->buffer,
+ TXT_UTF8_Strlen(inputbox->buffer) - w + 2));
+ chars = w - 1;
+ }
+ else
+ {
+ TXT_DrawUTF8String(inputbox->buffer);
+ chars = TXT_UTF8_Strlen(inputbox->buffer);
+ }
if (chars < w && inputbox->editing && focused)
{