From 1985aefc0e502048f92b91beef87c0dfbe669fed Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Fri, 7 Mar 2025 16:34:35 +0100 Subject: Restructuring Fluid source files. --- fluid/shell_command.cxx | 1005 ----------------------------------------------- 1 file changed, 1005 deletions(-) delete mode 100644 fluid/shell_command.cxx (limited to 'fluid/shell_command.cxx') diff --git a/fluid/shell_command.cxx b/fluid/shell_command.cxx deleted file mode 100644 index e961fc24c..000000000 --- a/fluid/shell_command.cxx +++ /dev/null @@ -1,1005 +0,0 @@ -// -// FLUID main entry for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// in progress: -// FLUID comes with example shell commands to build the current project file -// and run the project. This is accomplished by calling `fltk-config` on the -// files generated by FLUID, and by calling the executable directly. -// -// If the user wants more complex commands, he can add or modify them in the -// "Shell" settings panel. Modified shell commands are saved with the .fl -// file. - -// The Shell panel has a list of shell commands in the upper half. Under the -// list are buttons to add, duplicate, and delete shell commands. A popup -// menu offers import and export functionality and a list of sample scripts. -// We may want to add up and down buttons, so the user can change the -// order of commands. - -// Selecting any shell command in the list fills in and activates a list of -// options in the lower half of the panel. Those settings are: -// - Name: the name of the shell command in the list -// - Label: the label in the pulldown menu (could be the same as name?) -// - Shortcut: shortcut key to launch the command -// - Storage: where to store this shell command -// - Condition: pulldown menu to make the entry conditional for various -// target platforms, for example, a "Windows only" entry would only be added -// to the Shell menu on a Windows machine. Other options could be: -// - Linux only, macOS only, never (to make a list header!?), inactive? -// - Command: a multiline input for the actual shell command -// - Variables: a pulldown menu that insert variable names like $ -// - options to save project, code, and strings before running -// - test-run button - -// TODO: add @APPDIR@? -// TODO: get a macro to find `fltk-config` @FLTK_CONFIG@ -// TODO: add an input field so the user can insert their preferred file and path for fltk-config (user setting) -// `fltk-config` is actually tricky to find -// for live builds, we could check the program launch directory -// if we know where build/Xcode/bin/Debug/fluid is, we -// may or may not find ./build/Xcode/fltk-config -// on macOS with homebrew, we find /opt/homebrew/bin/fltk-config but the user -// can set their own install path. -// We can query the shell path, but that requires knowing the users shell (echo $SHELL). -// We can run the shell as a login shell with `-l`, so the user $PTH is set: /bin/bash -l -c 'fltk-config' -// The shell should output the path of the fltk-config that it found and why it is using that one. -// This can also output the fltk-config version. -// TODO: add a bunch of sensible sample shell commands -// TODO: when this new feature is used for the very first time, import two or three samples as initial user setting -// TODO: make the settings dialog resizable -// TODO: make g_shell_config static, not a pointer, but don't load anything in batch mode - -// FEATURE: Fd_Tool_Store icons are currently redundant with @file and @save and could be improved -// FEATURE: hostname, username, getenv support? -// FEATURE: add the files ./fluid.prefs and ./fluid.user.prefs as tool locations -// FEATURE: interpret compiler output, for example: clang, and highlight errors and warnings -// `.../shell_command.cxx:71:2: error: test` -// `71 | #error test` -// `clang++: error: no such file or directory: '.../shell_command.o'` -// would make the error message clickable in the shell window and could select the widget, -// open the matching editor in the widget panel, and highlight the line in SourceView. - -/* - Some ideas: - - default shell is in $SHELL on linux and macOS - - On macOS, we can write Apple Scripts: - - #!/usr/bin/env osascript - say "@BASENAME@" - - osascript < -#include -#include -#include - -#include - -static std::string fltk_config_cmd; -static Fl_Process s_proc; - -/** - See if shell command is running (public) - */ -bool shell_command_running() { - return s_proc.desc() ? true : false; -} - -/** - Reads an entry from the group. A default value must be - supplied. The return value indicates if the value was available - (non-zero) or the default was used (0). - - \param[in] prefs preference group - \param[in] key name of entry - \param[out] value returned from preferences or default value if none was set - \param[in] defaultValue default value to be used if no preference was set - \return 0 if the default value was used - */ -char preferences_get(Fl_Preferences &prefs, const char *key, std::string &value, const std::string &defaultValue) { - char *v = NULL; - char ret = prefs.get(key, v, defaultValue.c_str()); - value = v; - ::free(v); - return ret; -} - -/** - Sets an entry (name/value pair). The return value indicates if there - was a problem storing the data in memory. However it does not - reflect if the value was actually stored in the preference file. - - \param[in] prefs preference group - \param[in] entry name of entry - \param[in] value set this entry to value (stops at the first nul character). - \return 0 if setting the value failed - */ -char preferences_set(Fl_Preferences &prefs, const char *key, const std::string &value) { - return prefs.set(key, value.c_str()); -} - - -/** \class Fl_Process - Launch an external shell command. - */ - -/** - Create a process manager - */ -Fl_Process::Fl_Process() { - _fpt= NULL; -} - -/** - Destroy the project manager. - */ -Fl_Process::~Fl_Process() { - // TODO: check what we need to do if a task is still running - if (_fpt) close(); -} - -/** - Open a process. - - \param[in] cmd the shell command that we want to run - \param[in] mode "r" or "w" for creating a stream that can read or write - \return a stream that is redirected from the shell command stdout - */ -FILE * Fl_Process::popen(const char *cmd, const char *mode) { -#if defined(_WIN32) && !defined(__CYGWIN__) - // PRECONDITIONS - if (!mode || !*mode || (*mode!='r' && *mode!='w') ) return NULL; - if (_fpt) close(); // close first before reuse - - ptmode = *mode; - pin[0] = pin[1] = pout[0] = pout[1] = perr[0] = perr[1] = INVALID_HANDLE_VALUE; - // stderr to stdout wanted ? - int fusion = (strstr(cmd,"2>&1") !=NULL); - - // Create windows pipes - if (!createPipe(pin) || !createPipe(pout) || (!fusion && !createPipe(perr) ) ) - return freeHandles(); // error - - // Initialize Startup Info - ZeroMemory(&si, sizeof(STARTUPINFO)); - si.cb = sizeof(STARTUPINFO); - si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = pin[0]; - si.hStdOutput = pout[1]; - si.hStdError = fusion ? pout[1] : perr [1]; - - if ( CreateProcess(NULL, (LPTSTR) cmd,NULL,NULL,TRUE, - DETACHED_PROCESS,NULL,NULL, &si, &pi)) { - // don't need theses handles inherited by child process: - clean_close(pin[0]); clean_close(pout[1]); clean_close(perr[1]); - HANDLE & h = *mode == 'r' ? pout[0] : pin[1]; - _fpt = _fdopen(_open_osfhandle((fl_intptr_t) h,_O_BINARY),mode); - h= INVALID_HANDLE_VALUE; // reset the handle pointer that is shared - // with _fpt so we don't free it twice - } - - if (!_fpt) freeHandles(); - return _fpt; -#else - _fpt=::popen(cmd,mode); - return _fpt; -#endif -} - -/** - Close the current process. - */ -int Fl_Process::close() { -#if defined(_WIN32) && !defined(__CYGWIN__) - if (_fpt) { - fclose(_fpt); - clean_close(perr[0]); - clean_close(pin[1]); - clean_close(pout[0]); - _fpt = NULL; - return 0; - } - return -1; -#else - int ret = ::pclose(_fpt); - _fpt=NULL; - return ret; -#endif -} - -/** - non-null if file is open. - - \return the current file descriptor of the process' stdout - */ -FILE *Fl_Process::desc() const { - return _fpt; -} - -/** - Receive a single line from the current process. - - \param[out] line buffer to receive the line - \param[in] s size of the provided buffer - \return NULL if an error occurred, otherwise a pointer to the string - */ -char *Fl_Process::get_line(char * line, size_t s) const { - return _fpt ? fgets(line, (int)s, _fpt) : NULL; -} - -// returns fileno(FILE*): -// (file must be open, i.e. _fpt must be non-null) -// *FIXME* we should find a better solution for the 'fileno' issue -// non null if file is open -int Fl_Process::get_fileno() const { -#ifdef _MSC_VER - return _fileno(_fpt); // suppress MSVC warning -#else - return fileno(_fpt); -#endif -} - -#if defined(_WIN32) && !defined(__CYGWIN__) - -bool Fl_Process::createPipe(HANDLE * h, BOOL bInheritHnd) { - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = bInheritHnd; - return CreatePipe (&h[0],&h[1],&sa,0) ? true : false; -} - -FILE *Fl_Process::freeHandles() { - clean_close(pin[0]); clean_close(pin[1]); - clean_close(pout[0]); clean_close(pout[1]); - clean_close(perr[0]); clean_close(perr[1]); - return NULL; // convenient for error management -} - -void Fl_Process::clean_close(HANDLE& h) { - if (h!= INVALID_HANDLE_VALUE) CloseHandle(h); - h = INVALID_HANDLE_VALUE; -} - -#endif - - -/** - Prepare FLUID for running a shell command according to the command flags. - - \param[in] flags set various flags to save the project, code, and string before running the command - \return false if the previous command is still running - */ -static bool prepare_shell_command(int flags) { -// settings_window->hide(); - if (s_proc.desc()) { - fl_alert("Previous shell command still running!"); - return false; - } - if (flags & Fd_Shell_Command::SAVE_PROJECT) { - save_cb(0, 0); - } - if (flags & Fd_Shell_Command::SAVE_SOURCECODE) { - write_code_files(true); - } - if (flags & Fd_Shell_Command::SAVE_STRINGS) { - write_strings_cb(0, 0); - } - return true; -} - -/** - Called by the file handler when the command is finished. - */ -void shell_proc_done() { - shell_run_terminal->append("... END SHELL COMMAND ...\n"); - shell_run_button->activate(); - shell_run_window->label("FLUID Shell"); - fl_beep(); -} - -void shell_timer_cb(void*) { - if (!s_proc.desc()) { - shell_proc_done(); - } else { - Fl::add_timeout(0.25, shell_timer_cb); - } -} - -// Support the full piped shell command... -void shell_pipe_cb(FL_SOCKET, void*) { - char line[1024]=""; // Line from command output... - - if (s_proc.get_line(line, sizeof(line)) != NULL) { - // Add the line to the output list... - shell_run_terminal->append(line); - } else { - // End of file; tell the parent... - Fl::remove_timeout(shell_timer_cb); - Fl::remove_fd(s_proc.get_fileno()); - s_proc.close(); - shell_proc_done(); - } -} - -/** Find the script `fltk-config` that most closely relates to this version of FLUID. - This is not implemented yet. - */ -//static void find_fltk_config() { -// -//} - -static void expand_macro(std::string &cmd, const std::string ¯o, const std::string &content) { - for (int i=0;;) { - i = (int)cmd.find(macro, i); - if (i==(int)std::string::npos) break; - cmd.replace(i, macro.size(), content); - } -} - -static void expand_macros(std::string &cmd) { - expand_macro(cmd, "@BASENAME@", g_project.basename()); - expand_macro(cmd, "@PROJECTFILE_PATH@", g_project.projectfile_path()); - expand_macro(cmd, "@PROJECTFILE_NAME@", g_project.projectfile_name()); - expand_macro(cmd, "@CODEFILE_PATH@", g_project.codefile_path()); - expand_macro(cmd, "@CODEFILE_NAME@", g_project.codefile_name()); - expand_macro(cmd, "@HEADERFILE_PATH@", g_project.headerfile_path()); - expand_macro(cmd, "@HEADERFILE_NAME@", g_project.headerfile_name()); - expand_macro(cmd, "@TEXTFILE_PATH@", g_project.stringsfile_path()); - expand_macro(cmd, "@TEXTFILE_NAME@", g_project.stringsfile_name()); -// TODO: implement finding the script `fltk-config` for all platforms -// if (cmd.find("@FLTK_CONFIG@") != std::string::npos) { -// find_fltk_config(); -// expand_macro(cmd, "@FLTK_CONFIG@", fltk_config_cmd.c_str()); -// } - if (cmd.find("@TMPDIR@") != std::string::npos) - expand_macro(cmd, "@TMPDIR@", get_tmpdir()); -} - -/** - Show the terminal window where it was last positioned. - */ -void show_terminal_window() { - Fl_Preferences pos(fluid_prefs, "shell_run_Window_pos"); - int x, y, w, h; - pos.get("x", x, -1); - pos.get("y", y, 0); - pos.get("w", w, 640); - pos.get("h", h, 480); - if (x!=-1) { - shell_run_window->resize(x, y, w, h); - } - shell_run_window->show(); -} - -/** - Prepare for and run a shell command. - - \param[in] cmd the command that is sent to `/bin/sh -c ...` or `cmd.exe` on Windows machines - \param[in] flags various flags in preparation of the command - */ -void run_shell_command(const std::string &cmd, int flags) { - if (cmd.empty()) { - fl_alert("No shell command entered!"); - return; - } - - if (!prepare_shell_command(flags)) return; - - std::string expanded_cmd = cmd; - expand_macros(expanded_cmd); - - if ( ((flags & Fd_Shell_Command::DONT_SHOW_TERMINAL) == 0) - && (!shell_run_window->visible())) - { - show_terminal_window(); - } - - // Show the output window and clear things... - if (flags & Fd_Shell_Command::CLEAR_TERMINAL) - shell_run_terminal->printf("\033[2J\033[H"); - if (flags & Fd_Shell_Command::CLEAR_HISTORY) - shell_run_terminal->printf("\033[3J"); - shell_run_terminal->scrollbar->value(0); - shell_run_terminal->printf("\033[0;32m%s\033[0m\n", expanded_cmd.c_str()); - shell_run_window->label(expanded_cmd.c_str()); - - if (s_proc.popen((char *)expanded_cmd.c_str()) == NULL) { - shell_run_terminal->printf("\033[1;31mUnable to run shell command: %s\033[0m\n", - strerror(errno)); - shell_run_window->label("FLUID Shell"); - return; - } - shell_run_button->deactivate(); - - // if the function below does not for some reason, we will check periodically - // to see if the command is done - Fl::add_timeout(0.25, shell_timer_cb); - // this will tell us when the shell command is done - Fl::add_fd(s_proc.get_fileno(), shell_pipe_cb); -} - -/** - Create an empty shell command structure. - */ -Fd_Shell_Command::Fd_Shell_Command() -: shortcut(0), - storage(FD_STORE_USER), - condition(0), - flags(0), - shell_menu_item_(NULL) -{ -} - -/** - Copy the aspects of a shell command dataset into a new shell command. - - \param[in] rhs copy from this prototype - */ -Fd_Shell_Command::Fd_Shell_Command(const Fd_Shell_Command *rhs) -: name(rhs->name), - label(rhs->label), - shortcut(rhs->shortcut), - storage(rhs->storage), - condition(rhs->condition), - condition_data(rhs->condition_data), - command(rhs->command), - flags(rhs->flags), - shell_menu_item_(NULL) -{ -} - -/** - Create a default storage for a shell command and how it is accessible in FLUID. - - \param[in] name is used as a stand-in for the command name and label - */ -Fd_Shell_Command::Fd_Shell_Command(const std::string &in_name) -: name(in_name), - label(in_name), - shortcut(0), - storage(FD_STORE_USER), - condition(Fd_Shell_Command::ALWAYS), - command("echo \"Hello, FLUID!\""), - flags(Fd_Shell_Command::SAVE_PROJECT|Fd_Shell_Command::SAVE_SOURCECODE), - shell_menu_item_(NULL) -{ -} - -/** - Create a storage for a shell command and how it is accessible in FLUID. - - \param[in] in_name name of this command in the command list in the settings panel - \param[in] in_label label text in the main pulldown menu - \param[in] in_shortcut a keyboard shortcut that will also appear in the main menu - \param[in] in_storage storage location for this command - \param[in] in_condition commands can be hidden for certain platforms by setting a condition - \param[in] in_condition_data more details for future conditions, i.e. per user, per host, etc. - \param[in] in_command the shell command that we want to run - \param[in] in_flags some flags to tell FLUID to save the project, code, or strings before running the command - */ -Fd_Shell_Command::Fd_Shell_Command(const std::string &in_name, - const std::string &in_label, - Fl_Shortcut in_shortcut, - Fd_Tool_Store in_storage, - int in_condition, - const std::string &in_condition_data, - const std::string &in_command, - int in_flags) -: name(in_name), - label(in_label), - shortcut(in_shortcut), - storage(in_storage), - condition(in_condition), - condition_data(in_condition_data), - command(in_command), - flags(in_flags), - shell_menu_item_(NULL) -{ -} - -/** - Run this command now. - - Will open the Shell Panel and execute the command if no other command is - currently running. - */ -void Fd_Shell_Command::run() { - if (!command.empty()) - run_shell_command(command, flags); -} - -/** - Update the shell submenu in main menu with the shortcut and a copy of the label. - */ -void Fd_Shell_Command::update_shell_menu() { - if (shell_menu_item_) { - const char *old_label = shell_menu_item_->label(); // can be NULL - const char *new_label = label.c_str(); // never NULL - if (!old_label || (old_label && strcmp(old_label, new_label))) { - if (old_label) ::free((void*)old_label); - shell_menu_item_->label(fl_strdup(new_label)); - } - shell_menu_item_->shortcut(shortcut); - } -} - -/** - Check if the set condition is met. - - \return true if this command appears in the main menu - */ -bool Fd_Shell_Command::is_active() { - switch (condition) { - case ALWAYS: return true; - case NEVER: return false; -#ifdef _WIN32 - case MAC_ONLY: return false; - case UX_ONLY: return false; - case WIN_ONLY: return true; - case MAC_AND_UX_ONLY: return false; -#elif defined(__APPLE__) - case MAC_ONLY: return true; - case UX_ONLY: return false; - case WIN_ONLY: return false; - case MAC_AND_UX_ONLY: return true; -#else - case MAC_ONLY: return false; - case UX_ONLY: return true; - case WIN_ONLY: return false; - case MAC_AND_UX_ONLY: return true; -#endif - case USER_ONLY: return false; // TODO: get user name - case HOST_ONLY: return false; // TODO: get host name - case ENV_ONLY: { - const char *value = fl_getenv(condition_data.c_str()); - if (value && *value) return true; - return false; - } - } - return false; -} - -void Fd_Shell_Command::read(Fl_Preferences &prefs) { - int tmp; - preferences_get(prefs, "name", name, ""); - preferences_get(prefs, "label", label, ""); - prefs.get("shortcut", tmp, 0); - shortcut = (Fl_Shortcut)tmp; - prefs.get("storage", tmp, -1); - if (tmp != -1) storage = (Fd_Tool_Store)tmp; - prefs.get("condition", condition, ALWAYS); - preferences_get(prefs, "condition_data", condition_data, ""); - preferences_get(prefs, "command", command, ""); - prefs.get("flags", flags, 0); -} - -void Fd_Shell_Command::write(Fl_Preferences &prefs, bool save_location) { - preferences_set(prefs, "name", name); - preferences_set(prefs, "label", label); - if (shortcut != 0) prefs.set("shortcut", (int)shortcut); - if (save_location) prefs.set("storage", (int)storage); - if (condition != ALWAYS) prefs.set("condition", condition); - if (!condition_data.empty()) preferences_set(prefs, "condition_data", condition_data); - if (!command.empty()) preferences_set(prefs, "command", command); - if (flags != 0) prefs.set("flags", flags); -} - -void Fd_Shell_Command::read(class Fd_Project_Reader *in) { - const char *c = in->read_word(1); - if (strcmp(c, "{")!=0) return; // expecting start of group - storage = FD_STORE_PROJECT; - for (;;) { - c = in->read_word(1); - if (strcmp(c, "}")==0) break; // end of command list - else if (strcmp(c, "name")==0) - name = in->read_word(); - else if (strcmp(c, "label")==0) - label = in->read_word(); - else if (strcmp(c, "shortcut")==0) - shortcut = in->read_int(); - else if (strcmp(c, "condition")==0) - condition = in->read_int(); - else if (strcmp(c, "condition_data")==0) - condition_data = in->read_word(); - else if (strcmp(c, "command")==0) - command = in->read_word(); - else if (strcmp(c, "flags")==0) - flags = in->read_int(); - else - in->read_word(); // skip an unknown word - } -} - -void Fd_Shell_Command::write(class Fd_Project_Writer *out) { - out->write_string("\n command {"); - out->write_string("\n name "); out->write_word(name.c_str()); - out->write_string("\n label "); out->write_word(label.c_str()); - if (shortcut) out->write_string("\n shortcut %d", shortcut); - if (condition) out->write_string("\n condition %d", condition); - if (!condition_data.empty()) { - out->write_string("\n condition_data "); out->write_word(condition_data.c_str()); - } - if (!command.empty()) { - out->write_string("\n command "); out->write_word(command.c_str()); - } - if (flags) out->write_string("\n flags %d", flags); - out->write_string("\n }"); -} - - -/** - Manage a list of shell commands and their parameters. - */ -Fd_Shell_Command_List::Fd_Shell_Command_List() -: list(NULL), - list_size(0), - list_capacity(0), - shell_menu_(NULL) -{ -} - -/** - Release all shell commands and destroy this class. - */ -Fd_Shell_Command_List::~Fd_Shell_Command_List() { - clear(); -} - -/** - Return the shell command at the given index. - - \param[in] index must be between 0 and list_size-1 - \return a pointer to the shell command data - */ -Fd_Shell_Command *Fd_Shell_Command_List::at(int index) const { - return list[index]; -} - -/** - Clear all shell commands. - */ -void Fd_Shell_Command_List::clear() { - if (list) { - for (int i=0; i=0; i--) { - if (list[i]->storage == storage) { - remove(i); - } - } -} - -/** - Read shell configuration from a preferences group. - */ -void Fd_Shell_Command_List::read(Fl_Preferences &prefs, Fd_Tool_Store storage) { - // import the old shell commands from previous user settings - if (&fluid_prefs == &prefs) { - int version; - prefs.get("shell_commands_version", version, 0); - if (version == 0) { - int save_fl, save_code, save_strings; - Fd_Shell_Command *cmd = new Fd_Shell_Command(); - cmd->storage = FD_STORE_USER; - cmd->name = "Sample Shell Command"; - cmd->label = "Sample Shell Command"; - cmd->shortcut = FL_ALT+'g'; - preferences_get(fluid_prefs, "shell_command", cmd->command, "echo \"Sample Shell Command\""); - fluid_prefs.get("shell_savefl", save_fl, 1); - fluid_prefs.get("shell_writecode", save_code, 1); - fluid_prefs.get("shell_writemsgs", save_strings, 0); - if (save_fl) cmd->flags |= Fd_Shell_Command::SAVE_PROJECT; - if (save_code) cmd->flags |= Fd_Shell_Command::SAVE_SOURCECODE; - if (save_strings) cmd->flags |= Fd_Shell_Command::SAVE_STRINGS; - add(cmd); - } - version = 1; - prefs.set("shell_commands_version", version); - } - Fl_Preferences shell_commands(prefs, "shell_commands"); - int n = shell_commands.groups(); - for (int i=0; istorage = FD_STORE_USER; - cmd->read(cmd_prefs); - add(cmd); - } -} - -/** - Write shell configuration to a preferences group. - */ -void Fd_Shell_Command_List::write(Fl_Preferences &prefs, Fd_Tool_Store storage) { - Fl_Preferences shell_commands(prefs, "shell_commands"); - shell_commands.delete_all_groups(); - int index = 0; - for (int i=0; istorage == FD_STORE_USER) { - Fl_Preferences cmd(shell_commands, Fl_Preferences::Name(index++)); - list[i]->write(cmd); - } - } -} - -/** - Read shell configuration from a project file. - */ -void Fd_Shell_Command_List::read(Fd_Project_Reader *in) { - const char *c = in->read_word(1); - if (strcmp(c, "{")!=0) return; // expecting start of group - clear(FD_STORE_PROJECT); - for (;;) { - c = in->read_word(1); - if (strcmp(c, "}")==0) break; // end of command list - else if (strcmp(c, "command")==0) { - Fd_Shell_Command *cmd = new Fd_Shell_Command(); - add(cmd); - cmd->read(in); - } else { - in->read_word(); // skip an unknown group - } - } -} - -/** - Write shell configuration to a project file. - */ -void Fd_Shell_Command_List::write(Fd_Project_Writer *out) { - int n_in_project_file = 0; - for (int i=0; istorage == FD_STORE_PROJECT) - n_in_project_file++; - } - if (n_in_project_file > 0) { - out->write_string("\nshell_commands {"); - for (int i=0; istorage == FD_STORE_PROJECT) - list[i]->write(out); - } - out->write_string("\n}"); - } -} - -/** - Add a previously created shell command to the end of the list. - - \param[in] cmd a pointer to the command that we want to add - */ -void Fd_Shell_Command_List::add(Fd_Shell_Command *cmd) { - if (list_size == list_capacity) { - list_capacity += 16; - list = (Fd_Shell_Command**)::realloc(list, list_capacity * sizeof(Fd_Shell_Command*)); - } - list[list_size++] = cmd; -} - -/** - Insert a newly created shell command at the given position in the list. - - \param[in] index must be between 0 and list_size-1 - \param[in] cmd a pointer to the command that we want to add - */ -void Fd_Shell_Command_List::insert(int index, Fd_Shell_Command *cmd) { - if (list_size == list_capacity) { - list_capacity += 16; - list = (Fd_Shell_Command**)::realloc(list, list_capacity * sizeof(Fd_Shell_Command*)); - } - ::memmove(list+index+1, list+index, (list_size-index)*sizeof(Fd_Shell_Command**)); - list_size++; - list[index] = cmd; -} - -/** - Remove and delete the command at the given index. - - \param[in] index must be between 0 and list_size-1 - */ -void Fd_Shell_Command_List::remove(int index) { - delete list[index]; - list_size--; - ::memmove(list+index, list+index+1, (list_size-index)*sizeof(Fd_Shell_Command**)); -} - -/** - This is called whenever the user clicks a shell command menu in the main menu. - - \param[in] u cast tp long to get the index of the shell command - */ -void menu_shell_cmd_cb(Fl_Widget*, void *u) { - long index = (long)(fl_intptr_t)u; - g_shell_config->list[index]->run(); -} - -/** - This is called when the user selects the menu to edit the shell commands. - It pops up the setting panel at the shell settings tab. - */ -void menu_shell_customize_cb(Fl_Widget*, void*) { - settings_window->show(); - w_settings_tabs->value(w_settings_shell_tab); -} - -/** - Rebuild the entire shell submenu from scratch and replace the old menu. - */ -void Fd_Shell_Command_List::rebuild_shell_menu() { - static Fl_Menu_Item *shell_submenu = NULL; - if (!shell_submenu) - shell_submenu = (Fl_Menu_Item*)main_menubar->find_item(menu_marker); - - int i, j, num_active_items = 0; - // count the active commands - for (i=0; iis_active()) num_active_items++; - } - // allocate a menu item array - Fl_Menu_Item *mi = (Fl_Menu_Item*)::calloc(num_active_items+2, sizeof(Fl_Menu_Item)); - // set the menu item pointer for all active commands - for (i=j=0; iis_active()) { - cmd->shell_menu_item_ = mi + j; - mi[j].callback(menu_shell_cmd_cb); - mi[j].argument(i); - cmd->update_shell_menu(); - j++; - } - } - if (j>0) mi[j-1].flags |= FL_MENU_DIVIDER; - mi[j].label(fl_strdup("Customize...")); - mi[j].shortcut(FL_ALT+'x'); - mi[j].callback(menu_shell_customize_cb); - // replace the old menu array with the new one - Fl_Menu_Item *mi_old = shell_menu_; - shell_menu_ = mi; - shell_submenu->user_data(shell_menu_); - // free all resources from the old menu - if (mi_old && (mi_old != default_menu)) { - for (i=0; ; i++) { - const char *label = mi_old[i].label(); - if (!label) break; - ::free((void*)label); - } - ::free(mi_old); - } -} - -/** - Tell the settings dialog to query this list and update its GUI elements. - */ -void Fd_Shell_Command_List::update_settings_dialog() { - if (w_settings_shell_tab) - w_settings_shell_tab->do_callback(w_settings_shell_tab, LOAD); -} - -/** - The default shell submenu in batch mode. - */ -Fl_Menu_Item Fd_Shell_Command_List::default_menu[] = { - { "Customize...", FL_ALT+'x', menu_shell_customize_cb }, - { NULL } -}; - -/** - Used to find the shell submenu within the main menu tree. - */ -void Fd_Shell_Command_List::menu_marker(Fl_Widget*, void*) { - // intentionally left empty -} - -/** - Export all selected shell commands to an external file. - - Verify that g_shell_config and w_settings_shell_list are not NULL. Open a - file chooser and export all items that are selected in w_settings_shell_list - into an external file. - */ -void Fd_Shell_Command_List::export_selected() { - if (!g_shell_config || (g_shell_config->list_size == 0)) return; - if (!w_settings_shell_list) return; - - Fl_Native_File_Chooser dialog; - dialog.title("Export selected shell commands:"); - dialog.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); - dialog.filter("FLUID Files\t*.flcmd\n"); - dialog.directory(g_project.projectfile_path().c_str()); - dialog.preset_file((g_project.basename() + ".flcmd").c_str()); - if (dialog.show() != 0) return; - - Fl_Preferences file(dialog.filename(), "flcmd.fluid.fltk.org", NULL, (Fl_Preferences::Root)(Fl_Preferences::C_LOCALE|Fl_Preferences::CLEAR)); - Fl_Preferences shell_commands(file, "shell_commands"); - int i, index = 0, n = w_settings_shell_list->size(); - for (i = 0; i < n; i++) { - if (w_settings_shell_list->selected(i+1)) { - Fl_Preferences cmd(shell_commands, Fl_Preferences::Name(index++)); - g_shell_config->list[i]->write(cmd, true); - } - } -} - -/** - Import shell commands from an external file and add them to the list. - - Verify that g_shell_config and w_settings_shell_list are not NULL. Open a - file chooser and import all items. - */ -void Fd_Shell_Command_List::import_from_file() { - if (!g_shell_config || (g_shell_config->list_size == 0)) return; - if (!w_settings_shell_list) return; - - Fl_Native_File_Chooser dialog; - dialog.title("Import shell commands:"); - dialog.type(Fl_Native_File_Chooser::BROWSE_FILE); - dialog.filter("FLUID Files\t*.flcmd\n"); - dialog.directory(g_project.projectfile_path().c_str()); - dialog.preset_file((g_project.basename() + ".flcmd").c_str()); - if (dialog.show() != 0) return; - - Fl_Preferences file(dialog.filename(), "flcmd.fluid.fltk.org", NULL, Fl_Preferences::C_LOCALE); - Fl_Preferences shell_commands(file, "shell_commands"); - int i, n = shell_commands.groups(); - for (i = 0; i < n; i++) { - Fl_Preferences cmd_prefs(shell_commands, Fl_Preferences::Name(i)); - Fd_Shell_Command *cmd = new Fd_Shell_Command(); - cmd->storage = FD_STORE_USER; - cmd->read(cmd_prefs); - g_shell_config->add(cmd); - } - w_settings_shell_list->do_callback(w_settings_shell_list, LOAD); - w_settings_shell_cmd->do_callback(w_settings_shell_cmd, LOAD); - w_settings_shell_toolbox->do_callback(w_settings_shell_toolbox, LOAD); - g_shell_config->rebuild_shell_menu(); -} - -/** - A pointer to the list of shell commands if we are not in batch mode. - */ -Fd_Shell_Command_List *g_shell_config = NULL; - -- cgit v1.2.3