/* 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); }