Version 1.6

This commit is contained in:
Carsten Larsen 2018-12-03 21:09:59 +01:00
parent 1063d60252
commit 5f59b3d0da
32 changed files with 3015 additions and 2 deletions

View File

@ -1,2 +0,0 @@
# ToolsMenu

101
Readme Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
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

BIN
Resources/ToolsMenu.info Executable file

Binary file not shown.

12
SCOPTIONS Executable file
View File

@ -0,0 +1,12 @@
PARAMETERS=REGISTERS
ANSI
SHORTINTEGERS
NOSTACKCHECK
STRINGMERGE
ERRORREXX
OPTIMIZE
OPTIMIZERINLINELOCAL
NOVERSION
MEMORYSIZE=HUGE
DEFINE USE_PRAGMAS
IGNORE=306

96
args.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <workbench/startup.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#ifdef USE_PRAGMAS
#include <proto/dos.h>
#include <proto/exec.h>
#endif
#include <stdlib.h>
#include <string.h>
#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();
}

46
args.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef ARGS_H
#define ARGS_H
#include <dos/dos.h>
#include <exec/types.h>
#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

135
broker.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <dos/dos.h>
#include <libraries/commodities.h>
#include <clib/commodities_protos.h>
#include <clib/exec_protos.h>
#ifdef USE_PRAGMAS
#include <proto/commodities.h>
#include <proto/exec.h>
#endif
#include <stdlib.h>
#include <strings.h>
#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);
}

32
broker.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef BROKER_H
#define BROKER_H
#include <exec/types.h>
ULONG broker_set_up(void);
void broker_handle_signal(void);
void broker_clean_up(void);
#endif

67
catalog.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <exec/types.h>
#include <clib/locale_protos.h>
#ifdef USE_PRAGMAS
#include <proto/locale.h>
#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;
}
}

32
catalog.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef CATALOG_H
#define CATALOG_H
#include <exec/types.h>
void open_catalog(STRPTR catalog);
STRPTR get_string(ULONG id);
void close_catalog(void);
#endif

95
common.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <exec/lists.h>
#include <exec/memory.h>
#include <clib/exec_protos.h>
#ifdef USE_PRAGMAS
#include <proto/exec.h>
#endif
#include <string.h>
#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);
}
}

45
common.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#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

28
compiler.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef COMPILER_H
#define COMPILER_H
#define CHIP __chip
#endif

213
cx.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <dos/dos.h>
#include <exec/memory.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#ifdef USE_PRAGMAS
#include <proto/exec.h>
#endif
#include <stdlib.h>
#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);
}
}

51
cx.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef CX_H
#define CX_H
#include <exec/lists.h>
/* 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

72
dansk.ct Executable file
View File

@ -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.
;

51
error.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <intuition/intuition.h>
#include <clib/intuition_protos.h>
#ifdef USE_PRAGMAS
#include <proto/intuition.h>
#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);
}

31
error.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#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

618
gui.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#define INTUI_V36_NAMES_ONLY
#include <dos/dos.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <intuition/gadgetclass.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/gadtools.h>
#include <clib/exec_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/intuition_protos.h>
#ifdef USE_PRAGMAS
#include <proto/exec.h>
#include <proto/gadtools.h>
#include <proto/intuition.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#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);
}

44
gui.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef GUI_H
#define GUI_H
#include <exec/types.h>
#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

162
io.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <exec/memory.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/icon_protos.h>
#ifdef USE_PRAGMAS
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/icon.h>
#endif
#include <string.h>
#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);
}

31
io.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef IO_H
#define IO_H
#include <dos/dos.h>
void io_read_definitions(BPTR dir, char *filename);
void io_write_definitions(BPTR dir, char *filename);
#endif

154
main.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <exec/libraries.h>
#include <clib/exec_protos.h>
#ifdef USE_PRAGMAS
#include <proto/exec.h>
#endif
#include <stdlib.h>
#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_Version<OS_REQUIRED||DOSBase->lib_Version<OS_REQUIRED)
exit(RETURN_FAIL);
}
static struct Library *open_library(STRPTR name)
{
struct Library *base;
if ((base = OpenLibrary(name, OS_REQUIRED)) == NULL) {
error_message(get_string(MSG_ERR_OPEN), name);
exit(RETURN_FAIL);
}
return base;
}
static void open_libraries(void)
{
IntuitionBase = open_library("intuition.library");
IconBase = open_library("icon.library");
CxBase = open_library("commodities.library");
GadToolsBase = open_library("gadtools.library");
WorkbenchBase = open_library("workbench.library");
}
static void set_up(int argc, char **argv)
{
LocaleBase = OpenLibrary("locale.library", 38);
open_catalog(CATALOG);
open_libraries();
args_set_up(argc, argv);
signals |= wb_signal = wb_set_up();
signals |= tr_signal = tr_set_up();
signals |= broker_signal = broker_set_up();
signals |= gui_signal = gui_set_up();
cx_set_up();
}
static void main_loop(void)
{
ULONG received;
for (;;) {
received = Wait(signals);
if (received & wb_signal)
wb_handle_signal();
if (received & tr_signal)
tr_handle_signal();
if (received & broker_signal)
broker_handle_signal();
if (received & gui_signal)
gui_handle_signal();
if (received & SIGBREAKF_CTRL_C)
cx_quit();
if (received & SIGBREAKF_CTRL_F)
cx_show();
}
}
static void close_libraries(void)
{
CloseLibrary(WorkbenchBase);
CloseLibrary(GadToolsBase);
CloseLibrary(CxBase);
CloseLibrary(IconBase);
CloseLibrary(IntuitionBase);
}
/* Clean up as much as possible, then wait for the all tools started by us to
quit before finishing up. */
static void clean_up(void)
{
cx_clean_up();
gui_clean_up();
broker_clean_up();
/* tr_clean_up() won't return until all tools have quit. */
tr_clean_up();
wb_clean_up();
args_clean_up();
close_libraries();
close_catalog();
CloseLibrary(LocaleBase);
}
int main(int argc, char **argv)
{
check_system_version();
atexit(clean_up);
set_up(argc, argv);
if (args.cx_popup)
cx_show();
main_loop();
}

194
message.c Executable file
View File

@ -0,0 +1,194 @@
/*
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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#define INTUI_V36_NAMES_ONLY
#include <intuition/imageclass.h>
#include <intuition/intuition.h>
#include <clib/intuition_protos.h>
#ifdef USE_PRAGMAS
#include <proto/intuition.h>
#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);
}

43
message.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef MESSAGE_H
#define MESSAGE_H
#include <intuition/intuition.h>
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

56
smakefile Executable file
View File

@ -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

45
sprintf.asm Executable file
View File

@ -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
; <http://www.gnu.org/licenses/>.
;
; The author can be contacted on <kimflarsen@hotmail.com>
;
; 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

183
toolrun.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <dos/dos.h>
#include <dos/dostags.h>
#include <exec/memory.h>
#include <workbench/startup.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/icon_protos.h>
#ifdef USE_PRAGMAS
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/icon.h>
#endif
#include <stdlib.h>
#include <string.h>
#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);
}

60
toolrun.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#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

71
toolsmenu.cd Executable file
View File

@ -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
; <http://www.gnu.org/licenses/>.
;
; The author can be contacted on <kimflarsen@hotmail.com>
;
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.
;

209
wb.c Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <dos/dos.h>
#include <exec/memory.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/icon_protos.h>
#include <clib/wb_protos.h>
#ifdef USE_PRAGMAS
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/icon.h>
#include <proto/wb.h>
#endif
#include <stdlib.h>
#include <strings.h>
#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);
}

38
wb.h Executable file
View File

@ -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
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#ifndef WB_H
#define WB_H
#include <exec/types.h>
#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