summaryrefslogtreecommitdiff
path: root/fluid/shell_command.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'fluid/shell_command.cxx')
-rw-r--r--fluid/shell_command.cxx1005
1 files changed, 0 insertions, 1005 deletions
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 $<sourcefile>
-// - 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 <<EOD
- say "spark"
- EOD
-
- osascript <<EOD
- tell application "Xcode"
- build workspace document 1
- end tell
- EOD
-
- powershell -c "$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys('^{ESCAPE}')
- */
-
-#include "shell_command.h"
-
-#include "fluid.h"
-#include "file.h"
-#include "settings_panel.h"
-
-#include <FL/Fl_Double_Window.H>
-#include <FL/Fl_Menu_Bar.H>
-#include <FL/fl_message.H>
-#include <FL/fl_string_functions.h>
-
-#include <errno.h>
-
-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 &macro, 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, "<unnamed>");
- preferences_get(prefs, "label", label, "<no 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<list_size; i++) {
- delete list[i];
- }
- ::free(list);
- list_size = 0;
- list_capacity = 0;
- list = 0;
- }
-}
-
-/**
- remove all shell commands of the given storage location from the list.
- */
-void Fd_Shell_Command_List::clear(Fd_Tool_Store storage) {
- for (int i=list_size-1; 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; 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);
- 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; i<list_size; i++) {
- if (list[i]->storage == 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; i<list_size; i++) {
- if (list[i]->storage == FD_STORE_PROJECT)
- n_in_project_file++;
- }
- if (n_in_project_file > 0) {
- out->write_string("\nshell_commands {");
- for (int i=0; i<list_size; i++) {
- if (list[i]->storage == 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; i<list_size; i++) {
- if (list[i]->is_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; i<list_size; i++) {
- Fd_Shell_Command *cmd = list[i];
- if (cmd->is_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;
-