diff --git a/README.md b/README.md deleted file mode 100644 index 6c9a5ec..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# ToolsMenu - diff --git a/Readme b/Readme new file mode 100755 index 0000000..d6283f9 --- /dev/null +++ b/Readme @@ -0,0 +1,101 @@ +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on + + +New in version 1.6: + +- Minor UI adjustments +- New, "proper" requester for About +- Modularized the source code + + +ToolsMenu is a commodity that will let you add tools to the Tools menu on +your Workbench screen. Once a tool has been added, you can run it by +selecting it from the menu. Any icons selected on the Workbench at that +moment become arguments for the tool. + +For example, if you add MultiView, you can then select the icon of an image +on the Workbench, then select MultiView from the menu to view that image. + +Tools will be run as if started by the Workbench. You will not be able to +run tools that do not have a proper tool icons. + +Adding tools should be pretty self explanatory. Drag tool icons into the +window to add them, after which you can edit their names if you like. You +can also click the "New" button to manually enter a tool. You then write the +name of the tool (as you would like it to appear in the menu) under the +list, and type in the full path of the tool over on the right where it says +"Tool". The path must include the name of the tool's executable file. + +When you are happy with your selection of tools, you must remember to save +them from the pull down menu. The definitions will be saved as tool types +in the commodity's icon. + +ToolsMenu requires Kickstart 2 or later. + +The source code is included in the hope that it will be useful to those out +there who still enjoy programming for the Amiga. Elements of interest +include: + +- How to use Catcomp so a project can be localized easily +- How to implement a commodity sporting a GUI +- How to implement a dynamic GUI using Gadtools +- How to read and write tool types +- How to support dragging icons into a window +- How to add items to the Workbench Tools menu +- How to load and run other programs as DOS processes +- How to pretend to be Workbench + + +History: + +1.6 (Nov 26, 2018) + +- Minor UI adjustments +- New, "proper" requester for About +- Modularized the source code + +1.5 (Jul 28, 2015) + +- Busy pointer for Kickstart 2 +- Optimized and cleaned up a few things, made code more portable + +1.4 (Apr 22, 2015) + +- Improved layout for big fonts. +- Added "About..." + +1.3 (Feb 12, 2015) + +- Fixed an issue that caused some commodities (and possibly other tools) to + crash. + +1.2 (Feb 3, 2015) + +- Use the user's preferred screen font. + +1.1 (Jan 19, 2015) + +- Fixed an issue where trying to run a tool with an empty path would cause + a Guru Meditation. + +1.0 (Jan 12, 2015) + +- Initial release diff --git a/Resources/ToolsMenu.info b/Resources/ToolsMenu.info new file mode 100755 index 0000000..2abcd72 Binary files /dev/null and b/Resources/ToolsMenu.info differ diff --git a/SCOPTIONS b/SCOPTIONS new file mode 100755 index 0000000..6164b1b --- /dev/null +++ b/SCOPTIONS @@ -0,0 +1,12 @@ +PARAMETERS=REGISTERS +ANSI +SHORTINTEGERS +NOSTACKCHECK +STRINGMERGE +ERRORREXX +OPTIMIZE +OPTIMIZERINLINELOCAL +NOVERSION +MEMORYSIZE=HUGE +DEFINE USE_PRAGMAS +IGNORE=306 diff --git a/args.c b/args.c new file mode 100755 index 0000000..5de22e8 --- /dev/null +++ b/args.c @@ -0,0 +1,96 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include +#include +#include + +#include +#include +#include + +#ifdef USE_PRAGMAS +#include +#include +#endif + +#include +#include + +#include "args.h" + +#define DEF_CX_PRIORITY 0 +#define DEF_CX_POPUP YES +#define DEF_CX_POPKEY "control alt t" + +Args args; + +static UBYTE **tool_types; +static struct RDArgs *rdargs; + +static void get_wb_args(char **argv) +{ + struct WBArg *wb_arg = ((struct WBStartup *) argv)->sm_ArgList; + + args.exe.dir = wb_arg->wa_Lock; + args.exe.filename = wb_arg->wa_Name; + if ((tool_types = ArgArrayInit(0, argv)) == NULL) + exit(RETURN_FAIL); + args.cx_priority = ArgInt(tool_types, "CX_PRIORITY", DEF_CX_PRIORITY); + args.cx_popup = strcmp(ArgString(tool_types, "CX_POPUP", DEF_CX_POPUP ? + "YES" : "NO"), "NO") != 0; + args.cx_popkey = ArgString(tool_types, "CX_POPKEY", DEF_CX_POPKEY); +} + +static void get_shell_args(char **argv) +{ + LONG sh_args[3]; + + args.exe.dir = ((struct Process *) FindTask(NULL))->pr_CurrentDir; + args.exe.filename = argv[0]; + sh_args[0] = DEF_CX_PRIORITY; + sh_args[1] = (LONG) DEF_CX_POPKEY; + sh_args[2] = DEF_CX_POPUP ? (LONG) "YES" : (LONG) "NO"; + if ((rdargs = ReadArgs(TEMPLATE, sh_args, NULL)) == NULL) { + PrintFault(IoErr(), argv[0]); + exit(RETURN_ERROR); + } + args.cx_priority = sh_args[0]; + args.cx_popkey = (STRPTR) sh_args[1]; + args.cx_popup = strcmp((char *) sh_args[2], "YES") == 0; +} + +void args_set_up(int argc, char **argv) +{ + if (argc == 0) + get_wb_args(argv); + else + get_shell_args(argv); +} + +void args_clean_up() +{ + if (rdargs != NULL) + FreeArgs(rdargs); + if (tool_types != NULL) + ArgArrayDone(); +} diff --git a/args.h b/args.h new file mode 100755 index 0000000..d37431f --- /dev/null +++ b/args.h @@ -0,0 +1,46 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef ARGS_H +#define ARGS_H + +#include +#include + +#include "common.h" + +typedef struct { + struct { + BPTR dir; + char *filename; + } exe; + BYTE cx_priority; + Bool cx_popup; + STRPTR cx_popkey; +} Args; + +extern Args args; + +void args_set_up(int argc, char **argv); +void args_clean_up(void); + +#endif diff --git a/broker.c b/broker.c new file mode 100755 index 0000000..cafacec --- /dev/null +++ b/broker.c @@ -0,0 +1,135 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include +#include + +#include +#include + +#ifdef USE_PRAGMAS +#include +#include +#endif + +#include +#include + +#define CATCOMP_NUMBERS + +#include "args.h" +#include "broker.h" +#include "catalog.h" +#include "common.h" +#include "cx.h" +#include "error.h" +#include "strings.h" + +static struct MsgPort *broker_port; +static CxObj *broker; + +ULONG broker_set_up() +{ + struct NewBroker new_broker; + LONG error; + CxObj *filter; + + if ((broker_port = CreateMsgPort()) == NULL) + exit(RETURN_FAIL); + memset(&new_broker, 0, sizeof new_broker); + new_broker.nb_Version = NB_VERSION; + new_broker.nb_Name = APP_NAME; + new_broker.nb_Title = TITLE; + new_broker.nb_Descr = get_string(MSG_DESCRIPTION); + new_broker.nb_Unique = NBU_UNIQUE | NBU_NOTIFY; + new_broker.nb_Flags = COF_SHOW_HIDE; + new_broker.nb_Pri = args.cx_priority; + new_broker.nb_Port = broker_port; + if ((broker = CxBroker(&new_broker, &error)) == NULL) + exit((error == CBERR_DUP) ? RETURN_OK : RETURN_FAIL); + if ((filter = CxFilter(args.cx_popkey)) == NULL) + exit(RETURN_FAIL); + AttachCxObj(broker, filter); + AttachCxObj(filter, CxSender(broker_port, 0)); + AttachCxObj(filter, CxTranslate(NULL)); + if ((error = CxObjError(filter)) != 0) { + if (error == COERR_BADFILTER) { + error_message(get_string(MSG_ERR_HOTKEY), NULL); + exit(RETURN_ERROR); + } else + exit(RETURN_FAIL); + } + ActivateCxObj(broker, 1); + return 1UL << broker_port->mp_SigBit; +} + +static void handle_command(ULONG msg_id) +{ + switch (msg_id) { + case CXCMD_DISABLE: + ActivateCxObj(broker, 0); + cx_disable(); + break; + case CXCMD_ENABLE: + ActivateCxObj(broker, 1); + cx_enable(); + break; + case CXCMD_KILL: + cx_quit(); + break; + case CXCMD_UNIQUE: + case CXCMD_APPEAR: + cx_show(); + break; + case CXCMD_DISAPPEAR: + cx_hide(); + break; + } +} + +void broker_handle_signal() +{ + CxMsg *msg; + ULONG msg_type, msg_id; + + while ((msg = (CxMsg *) GetMsg(broker_port)) != NULL) { + msg_type = CxMsgType(msg); + msg_id = CxMsgID(msg); + ReplyMsg((struct Message *) msg); + switch (msg_type) { + case CXM_IEVENT: + /* Must be Hot Key event */ + cx_show(); + break; + case CXM_COMMAND: + handle_command(msg_id); + break; + } + } +} + +void broker_clean_up() +{ + if (broker != NULL) + DeleteCxObjAll(broker); + delete_port(broker_port); +} diff --git a/broker.h b/broker.h new file mode 100755 index 0000000..8fa848e --- /dev/null +++ b/broker.h @@ -0,0 +1,32 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef BROKER_H +#define BROKER_H + +#include + +ULONG broker_set_up(void); +void broker_handle_signal(void); +void broker_clean_up(void); + +#endif diff --git a/catalog.c b/catalog.c new file mode 100755 index 0000000..a09e7dc --- /dev/null +++ b/catalog.c @@ -0,0 +1,67 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include + +#include + +#ifdef USE_PRAGMAS +#include +#endif + +#include "catalog.h" + +#define CATCOMP_BLOCK +#include "strings.h" + +extern struct Library *LocaleBase; + +static APTR catalog; + +void open_catalog(STRPTR name) +{ + if (LocaleBase != NULL) + catalog = OpenCatalog(NULL, name, TAG_DONE); +} + +STRPTR get_string(ULONG id) +{ + LONG *l; + UWORD *w; + STRPTR built_in; + + l = (LONG *) CatCompBlock; + while (*l != id) { + w = (UWORD *) ((ULONG) l + 4); + l = (LONG *) ((ULONG) l + (ULONG) *w + 6); + } + built_in = (STRPTR) ((ULONG) l + 6); + return (catalog != NULL) ? GetCatalogStr(catalog,id,built_in) : built_in; +} + +void close_catalog() +{ + if (LocaleBase != NULL) { + CloseCatalog(catalog); + catalog = NULL; + } +} diff --git a/catalog.h b/catalog.h new file mode 100755 index 0000000..05dfc27 --- /dev/null +++ b/catalog.h @@ -0,0 +1,32 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef CATALOG_H +#define CATALOG_H + +#include + +void open_catalog(STRPTR catalog); +STRPTR get_string(ULONG id); +void close_catalog(void); + +#endif diff --git a/common.c b/common.c new file mode 100755 index 0000000..13f5362 --- /dev/null +++ b/common.c @@ -0,0 +1,95 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include +#include + +#include + +#ifdef USE_PRAGMAS +#include +#endif + +#include + +#include "common.h" + +char *copy_of(char *s) +{ + char *result; + + if ((result = AllocVec(strlen(s) + 1, MEMF_PUBLIC)) != NULL) + strcpy(result, s); + return result; +} + +void for_all(struct List *l, void (*f)(void *)) +{ + struct Node *n, *next; + + /* This form of iteration allows f() to free nodes */ + for (n = l->lh_Head; (next = n->ln_Succ) != NULL; n = next) + (*f)(n); +} + +Bool is_in_list(struct List *l, struct Node *node) +{ + struct Node *n; + + for (n = l->lh_Head; n->ln_Succ != NULL; n = n->ln_Succ) + if (n == node) + return YES; + return NO; +} + +struct Node *node_at(struct List *l, int i) +{ + struct Node *n; + + for (n = l->lh_Head; n->ln_Succ != NULL; n = n->ln_Succ) { + if (i == 0) + return n; + --i; + } + return NULL; +} + +int length_of(struct List *l) +{ + struct Node *n; + int result = 0; + + for (n = l->lh_Head; n->ln_Succ != NULL; n = n->ln_Succ) + ++result; + return result; +} + +void delete_port(struct MsgPort *port) +{ + struct Message *msg; + + if (port != NULL) { + while ((msg = GetMsg(port)) != NULL) + ReplyMsg(msg); + DeleteMsgPort(port); + } +} diff --git a/common.h b/common.h new file mode 100755 index 0000000..b23375e --- /dev/null +++ b/common.h @@ -0,0 +1,45 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef COMMON_H +#define COMMON_H + +#define APP_NAME "ToolsMenu" +#define VERSION "1.6" +#define DATE "26.11.2018" +#define COPYRIGHT "Copyright © 2015, 2018 Kim Fastrup Larsen" +#define TEMPLATE "CX_PRIORITY/N/K,CX_POPKEY/K,CX_POPUP/K" +#define CATALOG "toolsmenu.catalog" +#define TITLE APP_NAME " " VERSION + +#define MAX_PATH_LENGTH 127 + +typedef enum { NO, YES } Bool; + +char *copy_of(char *s); +void for_all(struct List *, void (*)(void *)); +Bool is_in_list(struct List *, struct Node *); +struct Node *node_at(struct List *, int index); +int length_of(struct List *); +void delete_port(struct MsgPort *); + +#endif diff --git a/compiler.h b/compiler.h new file mode 100755 index 0000000..5f5dcf5 --- /dev/null +++ b/compiler.h @@ -0,0 +1,28 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef COMPILER_H +#define COMPILER_H + +#define CHIP __chip + +#endif diff --git a/cx.c b/cx.c new file mode 100755 index 0000000..95c7c12 --- /dev/null +++ b/cx.c @@ -0,0 +1,213 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include +#include + +#include +#include + +#ifdef USE_PRAGMAS +#include +#endif + +#include + +#define CATCOMP_NUMBERS + +#include "args.h" +#include "catalog.h" +#include "common.h" +#include "cx.h" +#include "gui.h" +#include "io.h" +#include "strings.h" +#include "wb.h" + +struct List *tools; + +static Bool enabled = YES; + +static void free_tool(Tool *tool) +{ + FreeVec(tool->node.ln_Name); + FreeVec(tool->path); + FreeMem(tool, sizeof *tool); +} + +static Tool *create_tool(char *name, char *path) +{ + Tool *t; + + if ((t = AllocMem(sizeof *t, MEMF_PUBLIC | MEMF_CLEAR)) == NULL) + return NULL; + if ((t->node.ln_Name = copy_of(name)) == NULL) + goto create_tool_failed; + if ((t->path = copy_of(path)) == NULL) + goto create_tool_failed; + return t; + +create_tool_failed: + free_tool(t); + return NULL; +} + +static Tool *add_tool(char *name, char *path, struct Node *pred) +{ + Tool *tool; + + if ((tool = create_tool(name, path)) != NULL) { + Insert(tools, &tool->node, pred); + if (enabled) + wb_add_tool(tool); + } + return tool; +} + +static void remove_tool(Tool *tool) +{ + if (enabled) + wb_remove_tool(tool); + Remove(&tool->node); + free_tool(tool); +} + +static Tool *do_update(Tool *(*f)(Tool *, char *, char *), Tool *tool, + char *name, char *path) +{ + Tool *result; + + gui_begin_update(); + result = (*f)(tool, name, path); + gui_end_update(); + return result; +} + +static Tool *append(Tool *unused, char *name, char *path) +{ + return add_tool(name, path, tools->lh_TailPred); +} + +/* Actually creates a new tool and then deletes the old one, to simplify low + memory failure scenarios. If name is blank, it will simply delete the + tool. */ +static Tool *modify(Tool *tool, char *name, char *path) +{ + Tool *new_tool = NULL; + + if (*name != 0 && (new_tool = add_tool(name, path, &tool->node)) == NULL) + return tool; + remove_tool(tool); + return new_tool; + +} + +static void add_to_menu(void *tool) +{ + wb_add_tool((Tool *) tool); +} + +static void remove_from_menu(void *tool) +{ + wb_remove_tool((Tool *) tool); +} + +void cx_set_up() +{ + if ((tools = AllocMem(sizeof *tools, MEMF_PUBLIC)) == NULL) + exit(RETURN_FAIL); + NewList(tools); + io_read_definitions(args.exe.dir, args.exe.filename); +} + +Tool *cx_add_tool(char *name, char *path) +{ + Tool *tool = do_update(append, NULL, name, path); + + gui_edit(tool); + return tool; +} + +Tool *cx_modify_tool(Tool *tool, char *name, char *path) +{ + return do_update(modify, tool, name, path); +} + +void cx_enable() +{ + if (!enabled) { + enabled = YES; + for_all(tools, add_to_menu); + } +} + +void cx_disable() +{ + if (enabled) { + enabled = NO; + for_all(tools, remove_from_menu); + } +} + +void cx_show() +{ + gui_show(); +} + +void cx_hide() +{ + gui_hide(); +} + +void cx_save_tools() +{ + gui_begin_busy(); + io_write_definitions(args.exe.dir, args.exe.filename); + gui_end_busy(); +} + +void cx_about() +{ + gui_message(get_string(MSG_GAD_CONTINUE), + TITLE, + COPYRIGHT, + "", + "This program comes with ABSOLUTELY NO WARRANTY.", + "This is free software under the terms of", + "version 3 of the GNU General Public License.", + NULL); +} + +void cx_quit() +{ + cx_hide(); + cx_disable(); + exit(RETURN_OK); +} + +void cx_clean_up() +{ + if (tools != NULL) { + for_all(tools, free_tool); + FreeMem(tools, sizeof *tools); + } +} diff --git a/cx.h b/cx.h new file mode 100755 index 0000000..8cdce69 --- /dev/null +++ b/cx.h @@ -0,0 +1,51 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef CX_H +#define CX_H + +#include + +/* We will maintain a list of Tools as the main global state of this + commodity. The name of each Tool (ie. menu item) will be stored in + node.ln_Name. */ +typedef struct { + struct Node node; + char *path; + struct AppMenuItem *menu_item; +} Tool; + +extern struct List *tools; + +void cx_set_up(void); +Tool *cx_add_tool(char *name, char *path); +Tool *cx_modify_tool(Tool *, char *name, char *path); +void cx_enable(void); +void cx_disable(void); +void cx_show(void); +void cx_hide(void); +void cx_save_tools(void); +void cx_about(void); +void cx_quit(void); +void cx_clean_up(void); + +#endif diff --git a/dansk.ct b/dansk.ct new file mode 100755 index 0000000..e403f2e --- /dev/null +++ b/dansk.ct @@ -0,0 +1,72 @@ +## version $VER: toolsmenu.catalog 1.6 (26.11.2018) +## codeset 0 +## language dansk +; +MSG_ERR_OPEN +Kunne ikke åbne %s +; Could not open %s +; +MSG_ERR_HOTKEY +Ugyldig genvejstast +; Illegal hot key +; +MSG_WINDOW_TITLE +%s: Tryk <%s> +; %s: Hot Key = <%s> +; +MSG_DESCRIPTION +Tilføj værktøjer til Workbench-menuen +; Add tools to the Workbench Tools Menu +; +MSG_PROJECT_MENU +Projekt +; Project +; +MSG_PROJECT_SAVE +Gemme menuposter +; Save Menu Items +; +MSG_PROJECT_HIDE +Gemme væk +; Hide +; +MSG_PROJECT_ABOUT +Omkring... +; About... +; +MSG_PROJECT_QUIT +Afbryde +; Quit +; +MSG_GAD_CONTINUE +Fortsætte +; Continue +; +MSG_GAD_MENU_ITEMS +Menuposter +; Menu Items +; +MSG_GAD_NEW +Ny +; New +; +MSG_GAD_DELETE +Slette +; Delete +; +MSG_GAD_TOOL +Værktøj +; Tool +; +MSG_INFO_1 +Værktøjer kan tilføjes +; You can add tools +; +MSG_INFO_2 +ved at trække deres ikoner +; by dragging their icons +; +MSG_INFO_3 +ind i dette vindue. +; into this window. +; diff --git a/error.c b/error.c new file mode 100755 index 0000000..1109628 --- /dev/null +++ b/error.c @@ -0,0 +1,51 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include + +#include + +#ifdef USE_PRAGMAS +#include +#endif + +#define CATCOMP_NUMBERS + +#include "catalog.h" +#include "common.h" +#include "error.h" +#include "strings.h" + +extern struct IntuitionBase *IntuitionBase; + +void error_message(char *fmt, char *arg) +{ + static struct EasyStruct easy_struct = { + sizeof(struct EasyStruct), 0, APP_NAME + }; + + if (IntuitionBase == NULL) + return; + easy_struct.es_TextFormat = fmt; + easy_struct.es_GadgetFormat = get_string(MSG_GAD_CONTINUE); + EasyRequestArgs(NULL, &easy_struct, NULL, &arg); +} diff --git a/error.h b/error.h new file mode 100755 index 0000000..5b781bb --- /dev/null +++ b/error.h @@ -0,0 +1,31 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef ERROR_H +#define ERROR_H + +/* fmt can contain up to one %s. + If fmt contains a %s, arg must be non-NULL. +*/ +void error_message(char *fmt, char *arg); + +#endif diff --git a/gui.c b/gui.c new file mode 100755 index 0000000..f0811ac --- /dev/null +++ b/gui.c @@ -0,0 +1,618 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#define INTUI_V36_NAMES_ONLY + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef USE_PRAGMAS +#include +#include +#include +#endif + +#include +#include +#include +#include + +#define CATCOMP_NUMBERS + +#include "args.h" +#include "catalog.h" +#include "common.h" +#include "compiler.h" +#include "cx.h" +#include "gui.h" +#include "message.h" +#include "strings.h" +#include "wb.h" + +#define NOT_SET 0xffffffffU + +#define GAD_ID_LIST 1 +#define GAD_ID_NEW 2 +#define GAD_ID_DELETE 3 +#define GAD_ID_NAME 4 +#define GAD_ID_PATH 5 + +typedef struct { + int line_h; + int col1_x; + int list_y, list_w, list_h, scro_w; + int gadg_h; + int name_y, butn_y; + int col2_x; + int path_y, path_w; + int wind_w, wind_h; + int text_y; +} Layout; + +extern struct IntuitionBase *IntuitionBase; + +static struct IntuiText itexts[3] = { + { 0, 0, JAM1, 0, 0, NULL, NULL, itexts + 1 }, + { 0, 0, JAM1, 0, 0, NULL, NULL, itexts + 2 }, + { 0, 0, JAM1, 0, 0, NULL, NULL, NULL } +}; +static WORD zoom[4]; + +static struct MsgPort *user_port; +static struct Menu *menus; +static struct Requester busy_req; +static Message message; +static Bool message_constructed; +static struct Screen *screen; +static struct DrawInfo *draw_info; +static APTR visual_info; +static struct Gadget *gadgets; +static struct Window *window; +static struct AppWindow *app_window; + +static ULONG left_edge; +static ULONG top_edge = NOT_SET; + +static struct Gadget *list_gad, *name_gad, *del_gad, *path_gad; + +static Tool *current; + +static UBYTE *strgad_buf(struct Gadget *gadget) +{ + return ((struct StringInfo *) gadget->SpecialInfo)->Buffer; +} + +static void strip_messages_for(struct Window *window) +{ + struct IntuiMessage *msg, *succ; + + msg = (struct IntuiMessage *) window->UserPort->mp_MsgList.lh_Head; + while ((succ = (struct IntuiMessage *) msg->ExecMessage.mn_Node.ln_Succ) + != NULL) { + if (msg->IDCMPWindow == window) { + Remove(&msg->ExecMessage.mn_Node); + ReplyMsg(&msg->ExecMessage); + } + msg = succ; + } +} + +static void close_window_safely(struct Window *window) +{ + Forbid(); + strip_messages_for(window); + window->UserPort = NULL; + ModifyIDCMP(window, 0); + Permit(); + CloseWindow(window); +} + +static Bool set_up_menus(void) +{ + static struct NewMenu new_menus[] = { + { NM_TITLE, NULL, NULL, 0, 0, (APTR) MSG_PROJECT_MENU }, + { NM_ITEM, NULL, "S", 0, 0, (APTR) MSG_PROJECT_SAVE }, + { NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL }, + { NM_ITEM, NULL, "H", 0, 0, (APTR) MSG_PROJECT_HIDE }, + { NM_ITEM, NULL, NULL, 0, 0, (APTR) MSG_PROJECT_ABOUT }, + { NM_ITEM, NULL, "Q", 0, 0, (APTR) MSG_PROJECT_QUIT }, + { NM_END, NULL, NULL, 0, 0, NULL } + }; + struct NewMenu *nm; + + for (nm = new_menus; nm->nm_Type != NM_END; ++nm) + if (nm->nm_UserData != NULL) + nm->nm_Label = get_string((ULONG) nm->nm_UserData); + if ((menus = CreateMenus(new_menus, TAG_DONE)) == NULL) + return NO; + return YES; +} + +static void render(void) +{ + PrintIText(window->RPort, itexts, 0, 0); +} + +static void make_layout(Layout *layout) +{ + int font_w = draw_info->dri_Font->tf_XSize; + int font_h = draw_info->dri_Font->tf_YSize; + int pad_x = font_w; + int pad_y = font_h / 2; + int lab_h = font_h + 4; /* space taken up by label above a gadget */ + int path_th; /* total height of path gadget and label above */ + int col2_spc; /* vertical spacing around path gadget and text */ + + layout->line_h = font_h + 1; + layout->col1_x = pad_x; + layout->list_y = pad_y + lab_h; + layout->list_w = 24 * font_w + 12; /* should be divisible by 2 */ + layout->list_h = 6 * layout->line_h + 4; + layout->scro_w = font_w + 10; + layout->gadg_h = font_h + 6; + layout->name_y = layout->list_y + layout->list_h; + layout->butn_y = layout->name_y + layout->gadg_h; + layout->col2_x = layout->col1_x + layout->list_w + pad_x; + layout->path_w = 32 * font_w + 12; + layout->wind_w = layout->col2_x + layout->path_w + pad_x; + layout->wind_h = layout->butn_y + layout->gadg_h + pad_y; + path_th = layout->gadg_h + lab_h; + col2_spc = (layout->wind_h - path_th - 3 * layout->line_h) / 3; + layout->path_y = col2_spc + lab_h; + layout->text_y = 2 * col2_spc + path_th; +} + +static unsigned window_border_top(void) +{ + return screen->WBorTop + screen->Font->ta_YSize + 1U; +} + +static void make_newgadget(struct NewGadget *ng, WORD left_edge, + WORD top_edge, WORD width, WORD height, UBYTE *gadget_text, UWORD id, + ULONG flags) +{ + ng->ng_LeftEdge = left_edge + screen->WBorLeft; + ng->ng_TopEdge = top_edge + window_border_top(); + ng->ng_Width = width; + ng->ng_Height = height; + ng->ng_GadgetText = gadget_text; + ng->ng_GadgetID = id; + ng->ng_Flags = flags; +} + +static Bool create_gadgets(Layout *l) +{ + struct NewGadget ng; + struct Gadget *g = CreateContext(&gadgets); + int butn_w = l->list_w / 2; + + ng.ng_TextAttr = screen->Font; + ng.ng_VisualInfo = visual_info; + make_newgadget(&ng, l->col1_x, l->list_y, l->list_w, l->list_h, + get_string(MSG_GAD_MENU_ITEMS), GAD_ID_LIST, 0); + g = list_gad = CreateGadget(LISTVIEW_KIND, g, &ng, + GTLV_Labels, tools, + GTLV_ScrollWidth, (LONG) l->scro_w, + LAYOUTA_Spacing, 1L, + TAG_DONE); + make_newgadget(&ng, l->col1_x, l->name_y, l->list_w, l->gadg_h, + NULL, GAD_ID_NAME, 0); + g = name_gad = CreateGadget(STRING_KIND, g, &ng, + GA_Disabled, (LONG) TRUE, + TAG_DONE); + make_newgadget(&ng, l->col1_x, l->butn_y, butn_w, l->gadg_h, + get_string(MSG_GAD_NEW), GAD_ID_NEW, 0); + g = CreateGadget(BUTTON_KIND, g, &ng, + TAG_DONE); + make_newgadget(&ng, l->col1_x + butn_w, l->butn_y, butn_w, l->gadg_h, + get_string(MSG_GAD_DELETE), GAD_ID_DELETE, 0); + g = del_gad = CreateGadget(BUTTON_KIND, g, &ng, + GA_Disabled, (LONG) TRUE, + TAG_DONE); + make_newgadget(&ng, l->col2_x, l->path_y, l->path_w, l->gadg_h, + get_string(MSG_GAD_TOOL), GAD_ID_PATH, PLACETEXT_ABOVE); + g = path_gad = CreateGadget(STRING_KIND, g, &ng, + GTST_MaxChars, (LONG) MAX_PATH_LENGTH, + GA_Disabled, (LONG) TRUE, + TAG_DONE); + return g != NULL; +} + +static void layout_texts(Layout *layout) +{ + int left_edge = layout->col2_x; + int top_edge = layout->text_y + window_border_top(); + struct IntuiText *t; + + for (t = itexts; t != NULL; t = t->NextText) { + t->FrontPen = draw_info->dri_Pens[TEXTPEN]; + t->ITextFont = screen->Font; + t->LeftEdge = left_edge + (layout->path_w - IntuiTextLength(t)) / 2; + t->TopEdge = top_edge; + top_edge += layout->line_h; + } +} + +static Bool open_window(Layout *layout) +{ + static char window_title_buf[64]; + char *window_title = get_string(MSG_WINDOW_TITLE); + + if (strlen(window_title)+strlen(APP_NAME)+strlen(args.cx_popkey) > 64) + window_title = APP_NAME; + else { + sprintf(window_title_buf, window_title, APP_NAME, args.cx_popkey); + window_title = window_title_buf; + } + if ((window = OpenWindowTags(NULL, + WA_Left, left_edge, + WA_Top, top_edge, + WA_InnerWidth, (LONG) layout->wind_w, + WA_InnerHeight, (LONG) layout->wind_h, + WA_Flags, (ULONG) WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | + WFLG_ACTIVATE | WFLG_SIMPLE_REFRESH, + WA_PubScreen, screen, + WA_Zoom, zoom, + WA_Gadgets, gadgets, + WA_Title, window_title, + WA_AutoAdjust, (LONG) TRUE, + WA_NewLookMenus, (LONG) TRUE, + TAG_DONE)) == NULL) + return NO; + window->UserPort = user_port; + ModifyIDCMP(window, IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_REFRESHWINDOW + | LISTVIEWIDCMP | STRINGIDCMP | BUTTONIDCMP); + GT_RefreshWindow(window, NULL); + return YES; +} + +static void update_gadgets(void) +{ + Bool disable = (current == NULL); + + GT_SetGadgetAttrs(name_gad, window, NULL, + GA_Disabled, (LONG) disable, + GTST_String, disable ? "" : current->node.ln_Name, + TAG_DONE); + GT_SetGadgetAttrs(del_gad, window, NULL, + GA_Disabled, (LONG) disable, + TAG_DONE); + GT_SetGadgetAttrs(path_gad, window, NULL, + GA_Disabled, (LONG) disable, + GTST_String, disable ? "" : current->path, + TAG_DONE); +} + +static void update_current(void) +{ + if (current == NULL) + return; + if ((current = cx_modify_tool(current, strgad_buf(name_gad), + strgad_buf(path_gad))) == NULL) + update_gadgets(); +} + +static void select(int index) +{ + gui_edit((Tool *) node_at(tools, index)); +} + +static void new_tool(void) +{ + cx_add_tool("", ""); +} + +static void delete_tool(void) +{ + GT_SetGadgetAttrs(name_gad, window, NULL, + GTST_String, "", + TAG_DONE); + update_current(); +} + +static void handle_gadgetup(struct Gadget *gadget, UWORD code) +{ + switch (gadget->GadgetID) { + case GAD_ID_LIST: + select(code); + break; + case GAD_ID_NEW: + new_tool(); + break; + case GAD_ID_DELETE: + delete_tool(); + break; + case GAD_ID_NAME: + case GAD_ID_PATH: + update_current(); + break; + } +} + +static void handle_project_menu(UWORD code) +{ + switch (ITEMNUM(code)) { + case 0: /* Save Menu Items */ + cx_save_tools(); + break; + case 2: /* Hide */ + cx_hide(); + break; + case 3: /* About */ + cx_about(); + break; + case 4: /* Quit */ + cx_quit(); + break; + } +} + +static void handle_menupick(UWORD code) +{ + while (code != MENUNULL) { + switch (MENUNUM(code)) { + case 0: + handle_project_menu(code); + break; + } + code = ItemAddress(menus, code)->NextSelect; + } +} + +static void handle_closewindow(void) +{ + cx_hide(); +} + +static void handle_refreshwindow(void) +{ + GT_BeginRefresh(window); + render(); + GT_EndRefresh(window, TRUE); +} + +static void bring_to_front(void) +{ + ScreenToFront(window->WScreen); + WindowToFront(window); + ActivateWindow(window); + if (window->Height == zoom[3]) + ZipWindow(window); +} + +static void fill_itexts(struct IntuiText *text, va_list *ap) +{ + char *s; + + while ((s = va_arg(*ap, char *)) != NULL) { + text->IText = s; + text->NextText = text + 1; + ++text; + } + (text - 1)->NextText = NULL; +} + +ULONG gui_set_up() +{ + if (IntuitionBase->LibNode.lib_Version >= 39) + zoom[0] = zoom[1] = ~0; + if (!set_up_menus()) + exit(RETURN_FAIL); + if ((user_port = CreateMsgPort()) == NULL) + exit(RETURN_FAIL); + itexts[0].IText = get_string(MSG_INFO_1); + itexts[1].IText = get_string(MSG_INFO_2); + itexts[2].IText = get_string(MSG_INFO_3); + InitRequester(&busy_req); + if (!message_cons(&message)) + exit(RETURN_FAIL); + message_constructed = YES; + return 1UL << user_port->mp_SigBit; +} + +void gui_clean_up() +{ + if (message_constructed) + message_dest(&message); + DeleteMsgPort(user_port); + if (menus != NULL) /* Really checking if gadtools.library was opened */ + FreeMenus(menus); +} + +void gui_handle_signal() +{ + struct IntuiMessage *imsg; + ULONG clas; + UWORD code; + APTR iadd; + + while ((imsg = GT_GetIMsg(user_port)) != NULL) { + clas = imsg->Class; + code = imsg->Code; + iadd = imsg->IAddress; + GT_ReplyIMsg(imsg); + switch (clas) { + case IDCMP_MENUPICK: + handle_menupick(code); + break; + case IDCMP_CLOSEWINDOW: + handle_closewindow(); + break; + case IDCMP_REFRESHWINDOW: + handle_refreshwindow(); + break; + case IDCMP_GADGETUP: + handle_gadgetup(iadd, code); + break; + } + } +} + +void gui_show() +{ + Layout layout; + + if (window != NULL) { + bring_to_front(); + return; + } + if ((screen = LockPubScreen(NULL)) == NULL) + goto gui_show_failed; + ScreenToFront(screen); + if ((draw_info = GetScreenDrawInfo(screen)) == NULL) + goto gui_show_failed; + if ((visual_info = GetVisualInfo(screen, TAG_DONE)) == NULL) + goto gui_show_failed; + if (!LayoutMenus(menus, visual_info, + GTMN_NewLookMenus, (LONG) TRUE, + TAG_DONE)) + goto gui_show_failed; + if (top_edge == NOT_SET) + top_edge = screen->BarHeight + 1; + zoom[2] = 25 * draw_info->dri_Font->tf_XSize; + zoom[3] = window_border_top(); + make_layout(&layout); + if (!create_gadgets(&layout)) + goto gui_show_failed; + layout_texts(&layout); + if (!open_window(&layout)) + goto gui_show_failed; + render(); + app_window = wb_add_window(window); + SetMenuStrip(window, menus); + current = NULL; + return; + +gui_show_failed: + gui_hide(); +} + +void gui_hide() +{ + if (app_window != NULL) { + wb_remove_window(app_window); + app_window = NULL; + } + if (window != NULL) { + EndRequest(&message.requester, window); + if (window->MenuStrip != NULL) + ClearMenuStrip(window); + left_edge = window->LeftEdge; + top_edge = window->TopEdge; + close_window_safely(window); + window = NULL; + } + FreeGadgets(gadgets); + gadgets = NULL; + FreeVisualInfo(visual_info); + visual_info = NULL; + if (screen != NULL) { + FreeScreenDrawInfo(screen, draw_info); + draw_info = NULL; + UnlockPubScreen(NULL, screen); + screen = NULL; + } +} + +void gui_edit(Tool *tool) +{ + if (window == NULL) + return; + current = tool; + update_gadgets(); + if (current != NULL) + ActivateGadget(name_gad, window, NULL); +} + +/* Temporarily suspend the listview so we can update the list. */ +void gui_begin_update() +{ + if (window == NULL) + return; + GT_SetGadgetAttrs(list_gad, window, NULL, GTLV_Labels, ~0L, TAG_DONE); +} + +/* Resume normal listview operations. */ +void gui_end_update() +{ + if (window == NULL) + return; + GT_SetGadgetAttrs(list_gad, window, NULL, GTLV_Labels, tools, TAG_DONE); +} + +void gui_begin_busy() +{ + static UWORD CHIP busy_pointer[] = { + 0x0000, 0x0000, 0x0400, 0x07c0, 0x0000, 0x07c0, 0x0100, 0x0380, + 0x0000, 0x07e0, 0x07c0, 0x1ff8, 0x1ff0, 0x3fec, 0x3ff8, 0x7fde, + 0x3ff8, 0x7fbe, 0x7ffc, 0xff7f, 0x7efc, 0xffff, 0x7ffc, 0xffff, + 0x3ff8, 0x7ffe, 0x3ff8, 0x7ffe, 0x1ff0, 0x3ffc, 0x07c0, 0x1ff8, + 0x0000, 0x07e0, 0x0000, 0x0000 + }; + + if (window == NULL) + return; + Request(&busy_req, window); + if (IntuitionBase->LibNode.lib_Version < 39) + SetPointer(window, busy_pointer, 16, 16, -6, 0); + else + SetWindowPointer(window, + WA_BusyPointer, (LONG) TRUE, + WA_PointerDelay, (LONG) TRUE, + TAG_DONE); +} + +void gui_end_busy() +{ + if (window == NULL) + return; + if (IntuitionBase->LibNode.lib_Version < 39) + ClearPointer(window); + else + SetWindowPointer(window, TAG_DONE); + EndRequest(&busy_req, window); +} + +void gui_message(char *button_text, ...) +{ + static struct IntuiText texts[GUI_MESSAGE_MAX_LINES]; + va_list ap; + + va_start(ap, button_text); + if (window == NULL || message.requester.Flags & REQACTIVE) + goto gui_message_exit; + fill_itexts(texts, &ap); + message_layout(&message, window, draw_info, texts, button_text); + Request(&message.requester, window); + +gui_message_exit: + va_end(ap); +} diff --git a/gui.h b/gui.h new file mode 100755 index 0000000..83a70c0 --- /dev/null +++ b/gui.h @@ -0,0 +1,44 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef GUI_H +#define GUI_H + +#include + +#include "cx.h" + +#define GUI_MESSAGE_MAX_LINES 6 + +ULONG gui_set_up(void); +void gui_clean_up(void); +void gui_handle_signal(void); +void gui_show(void); +void gui_hide(void); +void gui_edit(Tool *); +void gui_begin_update(void); +void gui_end_update(void); +void gui_begin_busy(void); +void gui_end_busy(void); +void gui_message(char *button_text, ...); + +#endif diff --git a/io.c b/io.c new file mode 100755 index 0000000..e00d231 --- /dev/null +++ b/io.c @@ -0,0 +1,162 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include + +#include +#include +#include + +#ifdef USE_PRAGMAS +#include +#include +#include +#endif + +#include + +#include "common.h" +#include "cx.h" +#include "io.h" + +#define DEF_BEGIN '\xab' /* Left double angle quote */ +#define DEF_END '\xbb' /* Right double angle quote */ + +typedef void Disk_object_f(struct DiskObject *, char *); + +static void with_disk_object(Disk_object_f *f, BPTR dir, char *filename) +{ + BPTR old_dir = CurrentDir(dir); + struct DiskObject *disk_obj = GetDiskObject(filename); + + if (disk_obj != NULL) { + (*f)(disk_obj, filename); + FreeDiskObject(disk_obj); + } + CurrentDir(old_dir); +} + +static void read_definitions(struct DiskObject *disk_obj, char *filename) +{ + char **tt, *def_end; + + for (tt = disk_obj->do_ToolTypes; *tt != NULL; ++tt) + /* Identify the tool types that are menu item definitions and create + menu items from them. */ + if (**tt == DEF_BEGIN && (def_end = strchr(*tt, DEF_END)) != NULL && + *(def_end + 1) == ' ') { + /* Temporarily insert terminator to facilitate string copy. */ + *def_end = '\0'; + cx_add_tool(*tt + 1, def_end + 2); + /* Restore original char to leave string "untouched". */ + *def_end = DEF_END; + } +} + +/* Count the number of tool types that are not menu item definitions. */ +static int non_defs(char **tt) +{ + int result = 0; + + for ( ; *tt != NULL; ++tt) + if (**tt != DEF_BEGIN) + ++result; + return result; +} + +/* Create a tool type for a menu item definition. */ +static char *create_def(Tool *t) +{ + char *def; + size_t name_len = strlen(t->node.ln_Name); + + /* Allocate space for name, path, two delimiters, a space, and + string terminator */ + def = AllocVec(name_len + strlen(t->path) + 4, 0); + if (def != NULL) { + def[0] = DEF_BEGIN; + strcpy(def + 1, t->node.ln_Name); + def[name_len + 1] = DEF_END; + def[name_len + 2] = ' '; + strcpy(def + name_len + 3, t->path); + } + return def; +} + +/* Create a tool type for every menu item in the tools list. */ +static Bool create_defs(char **tt) +{ + struct Node *n; + + for (n = tools->lh_Head; n->ln_Succ != NULL; n = n->ln_Succ) + if ((*(tt++) = create_def((Tool *) n)) == NULL) + return NO; + return YES; +} + +/* Copy over those tool types that are not menu item definitions. */ +static void copy_non_defs(char **new_tt, char **old_tt) +{ + for ( ; *old_tt != NULL; ++old_tt) + if (**old_tt != DEF_BEGIN) + *(new_tt++) = *old_tt; +} + +static void free_tool_types(char **tt) +{ + char **i; + + for (i = tt ; *i != NULL; ++i) + if (**i == DEF_BEGIN) + FreeVec(*i); + FreeVec(tt); +} + +static void write_definitions(struct DiskObject *disk_obj, char *filename) +{ + int tools_size = length_of(tools); + char **old_tool_types = disk_obj->do_ToolTypes; + int size = tools_size + non_defs(old_tool_types) + 1; + char **new_tool_types = AllocVec(sizeof(char *) * size, MEMF_CLEAR); + + if (new_tool_types == NULL) + return; + if (!create_defs(new_tool_types)) + goto write_definitions_exit; + copy_non_defs(new_tool_types + tools_size, old_tool_types); + disk_obj->do_ToolTypes = new_tool_types; + PutDiskObject(filename, disk_obj); + disk_obj->do_ToolTypes = old_tool_types; + +write_definitions_exit: + free_tool_types(new_tool_types); +} + +void io_read_definitions(BPTR dir, char *filename) +{ + with_disk_object(read_definitions, dir, filename); +} + +void io_write_definitions(BPTR dir, char *filename) +{ + with_disk_object(write_definitions, dir, filename); +} diff --git a/io.h b/io.h new file mode 100755 index 0000000..9698281 --- /dev/null +++ b/io.h @@ -0,0 +1,31 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef IO_H +#define IO_H + +#include + +void io_read_definitions(BPTR dir, char *filename); +void io_write_definitions(BPTR dir, char *filename); + +#endif diff --git a/main.c b/main.c new file mode 100755 index 0000000..78219a7 --- /dev/null +++ b/main.c @@ -0,0 +1,154 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include + +#include + +#ifdef USE_PRAGMAS +#include +#endif + +#include + +#define CATCOMP_NUMBERS + +#include "args.h" +#include "broker.h" +#include "catalog.h" +#include "error.h" +#include "gui.h" +#include "strings.h" +#include "toolrun.h" +#include "wb.h" + +#define OS_REQUIRED 37 + +extern struct Library *SysBase; +extern struct Library *DOSBase; + +char *version = "\0$VER: " TITLE " (" DATE ")"; + +struct Library *CxBase; +struct Library *GadToolsBase; +struct Library *IconBase; +struct Library *IntuitionBase; +struct Library *LocaleBase; +struct Library *WorkbenchBase; + +static ULONG signals = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F; +static ULONG wb_signal, tr_signal, broker_signal, gui_signal; + +static void check_system_version(void) +{ + if (SysBase->lib_Versionlib_Version. + +The author can be contacted on +*/ + +#define INTUI_V36_NAMES_ONLY + +#include +#include + +#include + +#ifdef USE_PRAGMAS +#include +#endif + +#include "message.h" + +#define LINE_SPACING 1 + +static int max(int a, int b) +{ + return (a > b) ? a : b; +} + +static void button_dest(Button *button) +{ + DisposeObject(button->gadget.GadgetRender); +} + +static int button_cons(Button *button) +{ + if ((button->gadget.GadgetRender = NewObject(NULL, "frameiclass", + TAG_DONE)) == NULL) + return 0; + button->text.DrawMode = JAM1; + button->text.NextText = NULL; + button->gadget.Flags = GFLG_GADGHIMAGE | GFLG_GADGIMAGE; + button->gadget.Activation = GACT_RELVERIFY | GACT_ENDGADGET; + button->gadget.GadgetType = GTYP_BOOLGADGET | GTYP_REQGADGET; + button->gadget.SelectRender = button->gadget.GadgetRender; + button->gadget.GadgetText = &button->text; + return 1; +} + +static void button_layout(Button *button, struct TextAttr *font, + struct DrawInfo *draw_info, UBYTE *text) +{ + button->text.LeftEdge = draw_info->dri_Font->tf_XSize; + button->text.TopEdge = draw_info->dri_Font->tf_YSize * 3 / 8; + button->text.FrontPen = draw_info->dri_Pens[TEXTPEN]; + button->text.ITextFont = font; + button->text.IText = text; + button->gadget.Width = IntuiTextLength(&button->text) + 2 * + button->text.LeftEdge; + button->gadget.Height = font->ta_YSize + 2 * button->text.TopEdge; + SetAttrs(button->gadget.GadgetRender, + IA_Width, (LONG) button->gadget.Width, + IA_Height, (LONG) button->gadget.Height, + TAG_DONE); +} + +static void format_text(struct IntuiText *text, struct TextAttr *font, + struct DrawInfo *draw_info, int mar_x, int mar_y, int *width, int *height) +{ + int pad_x = draw_info->dri_Font->tf_XSize * 2; + int pad_y = draw_info->dri_Font->tf_YSize * 3 / 4; + int x = pad_x + mar_x; + int y = pad_y + mar_y; + int line_h = font->ta_YSize + LINE_SPACING; + + *width = 0, *height = 0; + for ( ; text != NULL; text = text->NextText) { + text->FrontPen = draw_info->dri_Pens[TEXTPEN]; + text->DrawMode = JAM1; + text->LeftEdge = x; + text->TopEdge = y; + text->ITextFont = font; + y += line_h; + *width = max(IntuiTextLength(text), *width); + *height += line_h; + } + *width += 2 * pad_x; + *height += 2 * pad_y - LINE_SPACING; +} + +static void dispose_images(struct Image *img) +{ + struct Image *next; + + while (img != NULL) { + next = img->NextImage; + DisposeObject(img); + img = next; + } +} + +int message_cons(Message *msg) +{ + static USHORT dither[] = { 0x5555, 0xAAAA }; + struct Image *background, *outer_frame, *inner_frame; + + InitRequester(&msg->requester); + msg->requester.ReqGadget = &msg->button.gadget; + msg->requester.Flags = USEREQIMAGE | SIMPLEREQ; + if ((background = NewObject(NULL, "fillrectclass", + IA_APattern, dither, + IA_APatSize, 1L, + TAG_DONE)) == NULL) + goto message_cons_failed; + msg->requester.ReqImage = background; + if ((outer_frame = NewObject(NULL, "frameiclass", + IA_EdgesOnly, (ULONG) TRUE, + TAG_DONE)) == NULL) + goto message_cons_failed; + background->NextImage = outer_frame; + if ((inner_frame = NewObject(NULL, "frameiclass", + IA_Recessed, (ULONG) TRUE, + TAG_DONE)) == NULL) + goto message_cons_failed; + outer_frame->NextImage = inner_frame; + if (!button_cons(&msg->button)) + goto message_cons_failed; + msg->button.gadget.NextGadget = NULL; + return 1; + +message_cons_failed: + dispose_images(msg->requester.ReqImage); + return 0; +} + +void message_layout(Message *msg, struct Window *window, + struct DrawInfo *draw_info, struct IntuiText *text, UBYTE *button_text) +{ + struct Image *background = msg->requester.ReqImage; + struct Image *outer_frame = background->NextImage; + struct Image *inner_frame = outer_frame->NextImage; + struct TextAttr *font = window->WScreen->Font; + int pad_x = draw_info->dri_Font->tf_XSize; + int pad_y = draw_info->dri_Font->tf_YSize / 2; + int text_w, text_h; + + msg->requester.ReqText = text; + button_layout(&msg->button, font, draw_info, button_text); + format_text(text, font, draw_info, pad_x, pad_y, &text_w, &text_h); + msg->requester.Width = 2 * pad_x + text_w; + msg->requester.Height = 3*pad_y - 1 + text_h + msg->button.gadget.Height; + msg->button.gadget.LeftEdge = (msg->requester.Width - + msg->button.gadget.Width) / 2; + msg->button.gadget.TopEdge = msg->requester.Height - + msg->button.gadget.Height - pad_y; + msg->requester.LeftEdge = max(0, window->BorderLeft + (window->Width - + window->BorderLeft - window->BorderRight - msg->requester.Width) / 2); + msg->requester.TopEdge = max(0, window->BorderTop + (window->Height - + window->BorderTop - window->BorderBottom - msg->requester.Height) / 2); + SetAttrs(inner_frame, + IA_Left, (LONG) pad_x, + IA_Top, (LONG) pad_y, + IA_Width, (LONG) text_w, + IA_Height, (LONG) text_h, + TAG_DONE); + SetAttrs(outer_frame, + IA_Width, (LONG) msg->requester.Width, + IA_Height, (LONG) msg->requester.Height, + TAG_DONE); + SetAttrs(background, + IA_Width, (LONG) msg->requester.Width, + IA_Height, (LONG) msg->requester.Height, + IA_FGPen, (ULONG) draw_info->dri_Pens[SHINEPEN], + TAG_DONE); +} + +void message_dest(Message *msg) +{ + button_dest(&msg->button); + dispose_images(msg->requester.ReqImage); +} diff --git a/message.h b/message.h new file mode 100755 index 0000000..6ceabcd --- /dev/null +++ b/message.h @@ -0,0 +1,43 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef MESSAGE_H +#define MESSAGE_H + +#include + +typedef struct { + struct Gadget gadget; + struct IntuiText text; +} Button; + +typedef struct { + struct Requester requester; + Button button; +} Message; + +int message_cons(Message *); +void message_layout(Message *, struct Window *, struct DrawInfo *, + struct IntuiText *text, UBYTE *button_text); +void message_dest(Message *); + +#endif diff --git a/smakefile b/smakefile new file mode 100755 index 0000000..65b600c --- /dev/null +++ b/smakefile @@ -0,0 +1,56 @@ +LIB = LIB:scs.lib LIB:amiga.lib +LINK = slink +LINKOPT = SMALLCODE SMALLDATA DEFINE @__chkabort=@__dummy DEFINE @_CXBRK=@__dummy QUIET + +STARTUP = LIB:c.o + +NAME = ToolsMenu +NAMELC = toolsmenu +OBJ = args.o broker.o catalog.o common.o cx.o error.o gui.o io.o main.o message.o sprintf.o toolrun.o wb.o +DANSKCAT = Catalogs/dansk/$(NAMELC).catalog + +all: $(NAME) $(NAME).info empty.ct $(DANSKCAT) + @Echo "*nBuild succeeded." + +clean: + -Delete >NIL: \#?.o $(NAME) $(NAME).info empty.ct $(DANSKCAT) + -Delete >NIL: Catalogs/dansk + -Delete >NIL: Catalogs + +$(NAME): $(OBJ) + $(LINK) FROM $(STARTUP) $(OBJ) TO $@ LIB $(LIB) $(LINKOPT) + +$(NAME).info: + @Copy Resources/$(NAME).info "" + +.asm.o: + GenAm FROM $< TO $@ ALINK QUIET + +args.o: args.c args.h common.h +broker.o: broker.c broker.h args.h catalog.h common.h cx.h error.h strings.h +catalog.o: catalog.c catalog.h strings.h +common.o: common.c common.h +cx.o: cx.c cx.h args.h common.h gui.h io.h wb.h +error.o: error.c error.h catalog.h common.h strings.h +gui.o: gui.c gui.h args.h catalog.h common.h compiler.h cx.h message.h strings.h wb.h +io.o: io.c io.h common.h cx.h +main.o: main.c args.h broker.h catalog.h common.h cx.h error.h gui.h strings.h toolrun.h wb.h +message.o: message.c message.h +sprintf.o: sprintf.asm +toolrun.o: toolrun.c toolrun.h common.h +wb.o: wb.c wb.h common.h cx.h toolrun.h + +strings.h: $(NAMELC).cd + CatComp $(NAMELC).cd CFILE strings.h + +empty.ct: $(NAMELC).cd + CatComp $(NAMELC).cd CTFILE empty.ct + +$(DANSKCAT): $(NAMELC).cd dansk.ct Catalogs/dansk + CatComp $(NAMELC).cd dansk.ct CATALOG $(DANSKCAT) + +Catalogs/dansk: Catalogs + @MakeDir Catalogs/dansk + +Catalogs: + @MakeDir Catalogs diff --git a/sprintf.asm b/sprintf.asm new file mode 100755 index 0000000..ea9d107 --- /dev/null +++ b/sprintf.asm @@ -0,0 +1,45 @@ +; ToolsMenu - Add tools to the Workbench Tools menu +; +; Copyright (C) 2015, 2018 Kim Fastrup Larsen +; +; 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 ver- +; sion 3 of the License, or (at your option) any later version. +; +; This program is distributed in the hope that it will be use- +; ful, but WITHOUT ANY WARRANTY; without even the implied war- +; ranty 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 Li- +; cense along with this program. If not, see +; . +; +; The author can be contacted on +; +; Simple version of the C "sprintf" function. Assumes C-style +; stack-based function conventions. + + XDEF _sprintf + + XREF _AbsExecBase + XREF _LVORawDoFmt + +_sprintf: + movem.l a2/a3/a6,-(sp) + + move.l 4*4(sp),a3 ; Get the output string pointer + move.l 5*4(sp),a0 ; Get the FormatString pointer + lea.l 6*4(sp),a1 ; Get the pointer to the DataStream + lea.l stuffChar(pc),a2 + move.l _AbsExecBase,a6 + jsr _LVORawDoFmt(a6) + + movem.l (sp)+,a2/a3/a6 + rts + +; PutChProc used by RawDoFmt +stuffChar: + move.b d0,(a3)+ + rts diff --git a/toolrun.c b/toolrun.c new file mode 100755 index 0000000..251f87f --- /dev/null +++ b/toolrun.c @@ -0,0 +1,183 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef USE_PRAGMAS +#include +#include +#include +#endif + +#include +#include + +#include "common.h" +#include "toolrun.h" + +#define MIN_STACK_SIZE 4000 + +/* We send this extended WBStartup to any tool that we launch. It will + contain the Workbench arguments passed to run_tool(). Once the tool quits, + it will reply to this message, and at that point we call the clean_up + function (also passed to run_tool()) to let it clean up the arguments. */ +typedef struct { + struct WBStartup wb_startup; + Tr_clean_up *clean_up; + void *user_data; +} Startmsg; + +/* The tools we run will be told to reply to this port when they quit. */ +static struct MsgPort *reply_port; +/* The number of tools started that haven't quit yet. */ +static int running; + +static void free_startmsg(Startmsg *msg) +{ + FreeVec(msg->wb_startup.sm_ToolWindow); + FreeMem(msg, sizeof *msg); +} + +/* Create the reply port and return its signal bit as a mask. */ +ULONG tr_set_up() +{ + if ((reply_port = CreateMsgPort()) == NULL) + exit(RETURN_FAIL); + return 1UL << reply_port->mp_SigBit; +} + +Bool tr_run_tool(struct WBArg *arg_list, LONG num_args, + Tr_clean_up *clean_up, void *user_data) +{ + BPTR old_dir; + struct DiskObject *disk_obj; + Startmsg *startmsg; + LONG stack_size; + BPTR home_dir; + struct Process *process; + + /* Load the tool's icon and check that it really is a Workbench tool. */ + old_dir = CurrentDir(arg_list[0].wa_Lock); + if ((disk_obj = GetDiskObject(arg_list[0].wa_Name)) == NULL) + goto run_tool_failed1; + if (disk_obj->do_Type != WBTOOL) + goto run_tool_failed2; + /* Use the stacksize specified in the icon unless it's too small. */ + if ((stack_size = disk_obj->do_StackSize) < MIN_STACK_SIZE) + stack_size = MIN_STACK_SIZE; + /* Set up startup message. */ + if ((startmsg=AllocMem(sizeof *startmsg,MEMF_PUBLIC|MEMF_CLEAR)) == NULL) + goto run_tool_failed2; + startmsg->wb_startup.sm_ArgList = arg_list; + startmsg->wb_startup.sm_NumArgs = num_args; + startmsg->clean_up = clean_up; + startmsg->user_data = user_data; + /* We copy the icon's ToolWindow if it isn't NULL. */ + if (disk_obj->do_ToolWindow != NULL && + (startmsg->wb_startup.sm_ToolWindow = copy_of(disk_obj->do_ToolWindow)) + == NULL) + goto run_tool_failed3; + /* Load the tool. */ + if ((startmsg->wb_startup.sm_Segment = NewLoadSegTags( + arg_list[0].wa_Name, TAG_DONE)) == NULL) + goto run_tool_failed3; + /* Get a lock for PROGDIR. */ + if ((home_dir = DupLock(arg_list[0].wa_Lock)) == 0) + goto run_tool_failed4; + /* Run the tool as a non-CLI process. */ + if ((process = CreateNewProcTags( + NP_Seglist, startmsg->wb_startup.sm_Segment, + NP_FreeSeglist, (LONG) TRUE, + NP_Input, NULL, + NP_CloseInput, (LONG) FALSE, + NP_Output, NULL, + NP_CloseOutput, (LONG) FALSE, + NP_Error, NULL, + NP_CloseError, (LONG) FALSE, + NP_CurrentDir, NULL, + NP_StackSize, stack_size, + NP_Name, arg_list[0].wa_Name, + NP_Priority, 0L, + NP_ConsoleTask, NULL, + NP_WindowPtr, NULL, + NP_HomeDir, home_dir, + TAG_DONE)) == NULL) + goto run_tool_failed5; + /* We have started the process, so we mustn't do anything after this point + that might fail. Send the startup message to the tool. */ + startmsg->wb_startup.sm_Process = &process->pr_MsgPort; + startmsg->wb_startup.sm_Message.mn_ReplyPort = reply_port; + PutMsg(startmsg->wb_startup.sm_Process, (struct Message *) startmsg); + ++running; + FreeDiskObject(disk_obj); + CurrentDir(old_dir); + return YES; + +run_tool_failed5: + UnLock(home_dir); + +run_tool_failed4: + UnLoadSeg(startmsg->wb_startup.sm_Segment); + +run_tool_failed3: + free_startmsg(startmsg); + +run_tool_failed2: + FreeDiskObject(disk_obj); + +run_tool_failed1: + CurrentDir(old_dir); + return NO; +} + +/* When a tool started by tr_run_tool() quits, we receive a reply to our + startup message. At this point we call the clean up function provided, + then clean up the message itself. */ +void tr_handle_signal() +{ + Startmsg *msg; + + while ((msg = (Startmsg *) GetMsg(reply_port)) != NULL) { + (*msg->clean_up)(msg->wb_startup.sm_ArgList, + msg->wb_startup.sm_NumArgs, msg->user_data); + free_startmsg(msg); + --running; + } +} + +/* Wait for all currently running tools to quit before cleaning up the + reply port and returning control to the caller. */ +void tr_clean_up() +{ + while (running > 0 ) { + WaitPort(reply_port); + tr_handle_signal(); + } + DeleteMsgPort(reply_port); +} diff --git a/toolrun.h b/toolrun.h new file mode 100755 index 0000000..1f54369 --- /dev/null +++ b/toolrun.h @@ -0,0 +1,60 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef TOOLRUN_H +#define TOOLRUN_H + +#include "common.h" + +/* The signature of the function that will be called when a tool quits (see + below). */ +typedef void Tr_clean_up(struct WBArg *, LONG num_args, void *user_data); + +/* This function must be called once as part of program initialization. Do + not call tr_run_tool() before calling this function. It sets up a message + port, for which the signal bit will be returned as a mask. Whenever you + receive this signal, call tr_handle_signal() to deal with it. */ +ULONG tr_set_up(void); + +/* Run a tool as if started from Workbench. + arg_list: An array of WBArg structures. The first element must + represent the tool to run, and the rest are its arguments. + num_args: The number of elements in arg_list. + clean_up: A pointer to a function that will be called when the tool + quits - it will be called from within tr_handle_signal() or + tr_clean_up(). arg_list, num_args and user_data will be passed + to clean_up. + user_data: This can be anything. It will be passed to clean_up. */ +Bool tr_run_tool(struct WBArg *arg_list, LONG num_args, + Tr_clean_up *clean_up, void *user_data); + +/* You must call this function whenever you receive the signal returned by + tr_set_up(). */ +void tr_handle_signal(void); + +/* You must call this once before quitting. Do not call tr_run_tool() after + calling this function. + NB: This function will wait around for all tools started by tr_run_tool() + to quit before returning. */ +void tr_clean_up(void); + +#endif diff --git a/toolsmenu.cd b/toolsmenu.cd new file mode 100755 index 0000000..4e93694 --- /dev/null +++ b/toolsmenu.cd @@ -0,0 +1,71 @@ +; ToolsMenu - Add tools to the Workbench Tools menu +; +; Copyright (C) 2015, 2018 Kim Fastrup Larsen +; +; 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 ver- +; sion 3 of the License, or (at your option) any later version. +; +; This program is distributed in the hope that it will be use- +; ful, but WITHOUT ANY WARRANTY; without even the implied war- +; ranty 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 Li- +; cense along with this program. If not, see +; . +; +; The author can be contacted on +; +MSG_ERR_OPEN (//) +Could not open %s +; +MSG_ERR_HOTKEY (//) +Illegal hot key +; +MSG_WINDOW_TITLE (//) +%s: Hot Key = <%s> +; +MSG_DESCRIPTION (//) +Add tools to the Workbench Tools Menu +; Description should be at most 40 characters +MSG_PROJECT_MENU (//) +Project +; +MSG_PROJECT_SAVE (//) +Save Menu Items +; +MSG_PROJECT_HIDE (//) +Hide +; +MSG_PROJECT_ABOUT (//) +About... +; +MSG_PROJECT_QUIT (//) +Quit +; +MSG_GAD_CONTINUE (//) +Continue +; +MSG_GAD_MENU_ITEMS (//) +Menu Items +; +MSG_GAD_NEW (//) +New +; +MSG_GAD_DELETE (//) +Delete +; +MSG_GAD_TOOL (//) +Tool +; +MSG_INFO_1 (//) +You can add tools +; +MSG_INFO_2 (//) +by dragging their icons +; +MSG_INFO_3 (//) +into this window. +; diff --git a/wb.c b/wb.c new file mode 100755 index 0000000..40f34cf --- /dev/null +++ b/wb.c @@ -0,0 +1,209 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef USE_PRAGMAS +#include +#include +#include +#include +#endif + +#include +#include + +#include "common.h" +#include "cx.h" +#include "toolrun.h" +#include "wb.h" + +static struct MsgPort *wb_port; + +static void add_tool_from_icon(char *name) +{ + char path[MAX_PATH_LENGTH + 1]; + BPTR lock; + + if ((lock = Lock(name, ACCESS_READ)) == NULL) + return; + if (NameFromLock(lock, path, MAX_PATH_LENGTH + 1)) + cx_add_tool(name, path); + UnLock(lock); +} + +static void handle_app_window_msg(struct AppMessage *msg) +{ + struct WBArg *arg; + struct WBArg *end = msg->am_ArgList + msg->am_NumArgs; + BPTR old_dir; + struct DiskObject *disk_obj; + + cx_show(); + for (arg = msg->am_ArgList; arg != end; ++arg) { + old_dir = CurrentDir(arg->wa_Lock); + /* Check that name isn't NULL and isn't empty, and that the icon is + a Workbench tool. */ + if (arg->wa_Name != NULL && *arg->wa_Name != '\0') + if ((disk_obj = GetDiskObject(arg->wa_Name)) != NULL) { + if (disk_obj->do_Type == WBTOOL) + add_tool_from_icon(arg->wa_Name); + FreeDiskObject(disk_obj); + } + CurrentDir(old_dir); + } + ReplyMsg((struct Message *) msg); +} + +static void free_args(struct WBArg *arg_list, LONG num_args) +{ + FreeVec(arg_list[0].wa_Name); + if (arg_list[0].wa_Lock != NULL) + UnLock(arg_list[0].wa_Lock); + FreeMem(arg_list, sizeof(struct WBArg) * num_args); +} + +/* Creates the directory locks and tool names to pass to tr_run_tool(). It + actually only creates the first element, ie. the home directory and name + of the tool itself, the rest are copied from the AppMessage that + workbench.library sent us. */ +static struct WBArg *create_args(STRPTR path, struct WBArg *arg_list, + LONG num_args) +{ + struct WBArg *result; + size_t args_size; + UBYTE t; + UBYTE *file_part; + + /* Create an array one element bigger than the one received from + workbench.library, so we can put the tool itself at the front. */ + args_size = sizeof(struct WBArg) * num_args; + result = AllocMem(args_size+sizeof(struct WBArg),MEMF_PUBLIC|MEMF_CLEAR); + if (result == NULL) + return NULL; + /* Temporarily separate the file part and directory part of the tool's + path with a string terminator. */ + t = *(file_part = FilePart(path)); + *file_part = '\0'; + /* Create the tool's directory lock. */ + result[0].wa_Lock = Lock(path, ACCESS_READ); + /* Restore original string. */ + *file_part = t; + if (result[0].wa_Lock == NULL) + goto create_args_failed; + /* Copy the tool's name. */ + if ((result[0].wa_Name = copy_of(file_part)) == NULL) + goto create_args_failed; + /* The rest of the array is just a copy of the AppMessage array. */ + if (args_size > 0) + memcpy(result + 1, arg_list, args_size); + return result; + +create_args_failed: + free_args(result, num_args + 1); + return NULL; +} +/* This function is called whenever a tool we started quits. At this point, + we reply to the workbench.library so it can free the arguments that we + borrowed. Called indirectly by tr_clean_up(), so we must not clean up + anything needed by reply_app_msg() until after the call to tr_clean_up(). +*/ +static void reply_app_msg(struct WBArg *arg_list, LONG num_args, + struct AppMessage *app_message) +{ + if (arg_list != NULL) + free_args(arg_list, num_args); + ReplyMsg((struct Message *) app_message); +} + +static void handle_app_menu_item_msg(struct AppMessage *msg) +{ + Tool *t = (Tool *) msg->am_UserData; + struct WBArg *args = NULL; + LONG num_args = msg->am_NumArgs + 1; + + /* Check that the path isn't empty and that the tool hasn't been removed + prior to handling this event, then create the Workbench arguments and + run the tool. */ + if (*t->path == '\0' || !is_in_list(tools, &t->node) || + (args = create_args(t->path,msg->am_ArgList,msg->am_NumArgs)) == NULL || + !tr_run_tool(args, num_args, reply_app_msg, msg)) + /* Failed to run, reply to the AppMessage right away. */ + reply_app_msg(args, num_args, msg); +} + +ULONG wb_set_up() +{ + if ((wb_port = CreateMsgPort()) == NULL) + exit(RETURN_FAIL); + return 1UL << wb_port->mp_SigBit; +} + +struct AppWindow *wb_add_window(struct Window *window) +{ + return AddAppWindow(0, 0, window, wb_port, TAG_DONE); +} + +void wb_remove_window(struct AppWindow *app_window) +{ + RemoveAppWindow(app_window); +} + +void wb_add_tool(Tool *tool) +{ + tool->menu_item = AddAppMenuItemA(0, (ULONG) tool, tool->node.ln_Name, + wb_port, TAG_DONE); +} + +void wb_remove_tool(Tool *tool) +{ + RemoveAppMenuItem(tool->menu_item); + tool->menu_item = NULL; +} + +void wb_handle_signal(void) +{ + struct AppMessage *msg; + + while ((msg = (struct AppMessage *) GetMsg(wb_port)) != NULL) + switch (msg->am_Type) { + case AMTYPE_APPWINDOW: + handle_app_window_msg(msg); + break; + case AMTYPE_APPMENUITEM: + handle_app_menu_item_msg(msg); + break; + } +} + +void wb_clean_up() +{ + delete_port(wb_port); +} diff --git a/wb.h b/wb.h new file mode 100755 index 0000000..b2adc40 --- /dev/null +++ b/wb.h @@ -0,0 +1,38 @@ +/* +ToolsMenu - Add tools to the Workbench Tools menu + +Copyright (C) 2015, 2018 Kim Fastrup Larsen + +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 ver- +sion 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be use- +ful, but WITHOUT ANY WARRANTY; without even the implied war- +ranty 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 Li- +cense along with this program. If not, see +. + +The author can be contacted on +*/ + +#ifndef WB_H +#define WB_H + +#include + +#include "cx.h" + +ULONG wb_set_up(void); +struct AppWindow *wb_add_window(struct Window *); +void wb_remove_window(struct AppWindow *); +void wb_add_tool(Tool *); +void wb_remove_tool(Tool *); +void wb_handle_signal(void); +void wb_clean_up(void); + +#endif