Add tools to the Workbench Tools menu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gui.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. /*
  2. ToolsMenu - Add tools to the Workbench Tools menu
  3. Copyright (C) 2015, 2018 Kim Fastrup Larsen
  4. This program is free software: you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation, either ver-
  7. sion 3 of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be use-
  9. ful, but WITHOUT ANY WARRANTY; without even the implied war-
  10. ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. See the GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public Li-
  13. cense along with this program. If not, see
  14. <http://www.gnu.org/licenses/>.
  15. The author can be contacted on <kimflarsen@hotmail.com>
  16. */
  17. #define INTUI_V36_NAMES_ONLY
  18. #include <dos/dos.h>
  19. #include <exec/memory.h>
  20. #include <exec/ports.h>
  21. #include <intuition/gadgetclass.h>
  22. #include <intuition/intuition.h>
  23. #include <intuition/intuitionbase.h>
  24. #include <libraries/gadtools.h>
  25. #include <clib/exec_protos.h>
  26. #include <clib/gadtools_protos.h>
  27. #include <clib/intuition_protos.h>
  28. #ifdef USE_PRAGMAS
  29. #include <proto/exec.h>
  30. #include <proto/gadtools.h>
  31. #include <proto/intuition.h>
  32. #endif
  33. #include <stdarg.h>
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <strings.h>
  37. #define CATCOMP_NUMBERS
  38. #include "args.h"
  39. #include "catalog.h"
  40. #include "common.h"
  41. #include "compiler.h"
  42. #include "cx.h"
  43. #include "gui.h"
  44. #include "message.h"
  45. #include "strings.h"
  46. #include "wb.h"
  47. #define NOT_SET 0xffffffffU
  48. #define GAD_ID_LIST 1
  49. #define GAD_ID_NEW 2
  50. #define GAD_ID_DELETE 3
  51. #define GAD_ID_NAME 4
  52. #define GAD_ID_PATH 5
  53. typedef struct {
  54. int line_h;
  55. int col1_x;
  56. int list_y, list_w, list_h, scro_w;
  57. int gadg_h;
  58. int name_y, butn_y;
  59. int col2_x;
  60. int path_y, path_w;
  61. int wind_w, wind_h;
  62. int text_y;
  63. } Layout;
  64. extern struct IntuitionBase *IntuitionBase;
  65. static struct IntuiText itexts[3] = {
  66. { 0, 0, JAM1, 0, 0, NULL, NULL, itexts + 1 },
  67. { 0, 0, JAM1, 0, 0, NULL, NULL, itexts + 2 },
  68. { 0, 0, JAM1, 0, 0, NULL, NULL, NULL }
  69. };
  70. static WORD zoom[4];
  71. static struct MsgPort *user_port;
  72. static struct Menu *menus;
  73. static struct Requester busy_req;
  74. static Message message;
  75. static Bool message_constructed;
  76. static struct Screen *screen;
  77. static struct DrawInfo *draw_info;
  78. static APTR visual_info;
  79. static struct Gadget *gadgets;
  80. static struct Window *window;
  81. static struct AppWindow *app_window;
  82. static ULONG left_edge;
  83. static ULONG top_edge = NOT_SET;
  84. static struct Gadget *list_gad, *name_gad, *del_gad, *path_gad;
  85. static Tool *current;
  86. static UBYTE *strgad_buf(struct Gadget *gadget)
  87. {
  88. return ((struct StringInfo *) gadget->SpecialInfo)->Buffer;
  89. }
  90. static void strip_messages_for(struct Window *window)
  91. {
  92. struct IntuiMessage *msg, *succ;
  93. msg = (struct IntuiMessage *) window->UserPort->mp_MsgList.lh_Head;
  94. while ((succ = (struct IntuiMessage *) msg->ExecMessage.mn_Node.ln_Succ)
  95. != NULL) {
  96. if (msg->IDCMPWindow == window) {
  97. Remove(&msg->ExecMessage.mn_Node);
  98. ReplyMsg(&msg->ExecMessage);
  99. }
  100. msg = succ;
  101. }
  102. }
  103. static void close_window_safely(struct Window *window)
  104. {
  105. Forbid();
  106. strip_messages_for(window);
  107. window->UserPort = NULL;
  108. ModifyIDCMP(window, 0);
  109. Permit();
  110. CloseWindow(window);
  111. }
  112. static Bool set_up_menus(void)
  113. {
  114. static struct NewMenu new_menus[] = {
  115. { NM_TITLE, NULL, NULL, 0, 0, (APTR) MSG_PROJECT_MENU },
  116. { NM_ITEM, NULL, "S", 0, 0, (APTR) MSG_PROJECT_SAVE },
  117. { NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL },
  118. { NM_ITEM, NULL, "H", 0, 0, (APTR) MSG_PROJECT_HIDE },
  119. { NM_ITEM, NULL, NULL, 0, 0, (APTR) MSG_PROJECT_ABOUT },
  120. { NM_ITEM, NULL, "Q", 0, 0, (APTR) MSG_PROJECT_QUIT },
  121. { NM_END, NULL, NULL, 0, 0, NULL }
  122. };
  123. struct NewMenu *nm;
  124. for (nm = new_menus; nm->nm_Type != NM_END; ++nm)
  125. if (nm->nm_UserData != NULL)
  126. nm->nm_Label = get_string((ULONG) nm->nm_UserData);
  127. if ((menus = CreateMenus(new_menus, TAG_DONE)) == NULL)
  128. return NO;
  129. return YES;
  130. }
  131. static void render(void)
  132. {
  133. PrintIText(window->RPort, itexts, 0, 0);
  134. }
  135. static void make_layout(Layout *layout)
  136. {
  137. int font_w = draw_info->dri_Font->tf_XSize;
  138. int font_h = draw_info->dri_Font->tf_YSize;
  139. int pad_x = font_w;
  140. int pad_y = font_h / 2;
  141. int lab_h = font_h + 4; /* space taken up by label above a gadget */
  142. int path_th; /* total height of path gadget and label above */
  143. int col2_spc; /* vertical spacing around path gadget and text */
  144. layout->line_h = font_h + 1;
  145. layout->col1_x = pad_x;
  146. layout->list_y = pad_y + lab_h;
  147. layout->list_w = 24 * font_w + 12; /* should be divisible by 2 */
  148. layout->list_h = 6 * layout->line_h + 4;
  149. layout->scro_w = font_w + 10;
  150. layout->gadg_h = font_h + 6;
  151. layout->name_y = layout->list_y + layout->list_h;
  152. layout->butn_y = layout->name_y + layout->gadg_h;
  153. layout->col2_x = layout->col1_x + layout->list_w + pad_x;
  154. layout->path_w = 32 * font_w + 12;
  155. layout->wind_w = layout->col2_x + layout->path_w + pad_x;
  156. layout->wind_h = layout->butn_y + layout->gadg_h + pad_y;
  157. path_th = layout->gadg_h + lab_h;
  158. col2_spc = (layout->wind_h - path_th - 3 * layout->line_h) / 3;
  159. layout->path_y = col2_spc + lab_h;
  160. layout->text_y = 2 * col2_spc + path_th;
  161. }
  162. static unsigned window_border_top(void)
  163. {
  164. return screen->WBorTop + screen->Font->ta_YSize + 1U;
  165. }
  166. static void make_newgadget(struct NewGadget *ng, WORD left_edge,
  167. WORD top_edge, WORD width, WORD height, UBYTE *gadget_text, UWORD id,
  168. ULONG flags)
  169. {
  170. ng->ng_LeftEdge = left_edge + screen->WBorLeft;
  171. ng->ng_TopEdge = top_edge + window_border_top();
  172. ng->ng_Width = width;
  173. ng->ng_Height = height;
  174. ng->ng_GadgetText = gadget_text;
  175. ng->ng_GadgetID = id;
  176. ng->ng_Flags = flags;
  177. }
  178. static Bool create_gadgets(Layout *l)
  179. {
  180. struct NewGadget ng;
  181. struct Gadget *g = CreateContext(&gadgets);
  182. int butn_w = l->list_w / 2;
  183. ng.ng_TextAttr = screen->Font;
  184. ng.ng_VisualInfo = visual_info;
  185. make_newgadget(&ng, l->col1_x, l->list_y, l->list_w, l->list_h,
  186. get_string(MSG_GAD_MENU_ITEMS), GAD_ID_LIST, 0);
  187. g = list_gad = CreateGadget(LISTVIEW_KIND, g, &ng,
  188. GTLV_Labels, tools,
  189. GTLV_ScrollWidth, (LONG) l->scro_w,
  190. LAYOUTA_Spacing, 1L,
  191. TAG_DONE);
  192. make_newgadget(&ng, l->col1_x, l->name_y, l->list_w, l->gadg_h,
  193. NULL, GAD_ID_NAME, 0);
  194. g = name_gad = CreateGadget(STRING_KIND, g, &ng,
  195. GA_Disabled, (LONG) TRUE,
  196. TAG_DONE);
  197. make_newgadget(&ng, l->col1_x, l->butn_y, butn_w, l->gadg_h,
  198. get_string(MSG_GAD_NEW), GAD_ID_NEW, 0);
  199. g = CreateGadget(BUTTON_KIND, g, &ng,
  200. TAG_DONE);
  201. make_newgadget(&ng, l->col1_x + butn_w, l->butn_y, butn_w, l->gadg_h,
  202. get_string(MSG_GAD_DELETE), GAD_ID_DELETE, 0);
  203. g = del_gad = CreateGadget(BUTTON_KIND, g, &ng,
  204. GA_Disabled, (LONG) TRUE,
  205. TAG_DONE);
  206. make_newgadget(&ng, l->col2_x, l->path_y, l->path_w, l->gadg_h,
  207. get_string(MSG_GAD_TOOL), GAD_ID_PATH, PLACETEXT_ABOVE);
  208. g = path_gad = CreateGadget(STRING_KIND, g, &ng,
  209. GTST_MaxChars, (LONG) MAX_PATH_LENGTH,
  210. GA_Disabled, (LONG) TRUE,
  211. TAG_DONE);
  212. return g != NULL;
  213. }
  214. static void layout_texts(Layout *layout)
  215. {
  216. int left_edge = layout->col2_x;
  217. int top_edge = layout->text_y + window_border_top();
  218. struct IntuiText *t;
  219. for (t = itexts; t != NULL; t = t->NextText) {
  220. t->FrontPen = draw_info->dri_Pens[TEXTPEN];
  221. t->ITextFont = screen->Font;
  222. t->LeftEdge = left_edge + (layout->path_w - IntuiTextLength(t)) / 2;
  223. t->TopEdge = top_edge;
  224. top_edge += layout->line_h;
  225. }
  226. }
  227. static Bool open_window(Layout *layout)
  228. {
  229. static char window_title_buf[64];
  230. char *window_title = get_string(MSG_WINDOW_TITLE);
  231. if (strlen(window_title)+strlen(APP_NAME)+strlen(args.cx_popkey) > 64)
  232. window_title = APP_NAME;
  233. else {
  234. sprintf(window_title_buf, window_title, APP_NAME, args.cx_popkey);
  235. window_title = window_title_buf;
  236. }
  237. if ((window = OpenWindowTags(NULL,
  238. WA_Left, left_edge,
  239. WA_Top, top_edge,
  240. WA_InnerWidth, (LONG) layout->wind_w,
  241. WA_InnerHeight, (LONG) layout->wind_h,
  242. WA_Flags, (ULONG) WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET |
  243. WFLG_ACTIVATE | WFLG_SIMPLE_REFRESH,
  244. WA_PubScreen, screen,
  245. WA_Zoom, zoom,
  246. WA_Gadgets, gadgets,
  247. WA_Title, window_title,
  248. WA_AutoAdjust, (LONG) TRUE,
  249. WA_NewLookMenus, (LONG) TRUE,
  250. TAG_DONE)) == NULL)
  251. return NO;
  252. window->UserPort = user_port;
  253. ModifyIDCMP(window, IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_REFRESHWINDOW
  254. | LISTVIEWIDCMP | STRINGIDCMP | BUTTONIDCMP);
  255. GT_RefreshWindow(window, NULL);
  256. return YES;
  257. }
  258. static void update_gadgets(void)
  259. {
  260. Bool disable = (current == NULL);
  261. GT_SetGadgetAttrs(name_gad, window, NULL,
  262. GA_Disabled, (LONG) disable,
  263. GTST_String, disable ? "" : current->node.ln_Name,
  264. TAG_DONE);
  265. GT_SetGadgetAttrs(del_gad, window, NULL,
  266. GA_Disabled, (LONG) disable,
  267. TAG_DONE);
  268. GT_SetGadgetAttrs(path_gad, window, NULL,
  269. GA_Disabled, (LONG) disable,
  270. GTST_String, disable ? "" : current->path,
  271. TAG_DONE);
  272. }
  273. static void update_current(void)
  274. {
  275. if (current == NULL)
  276. return;
  277. if ((current = cx_modify_tool(current, strgad_buf(name_gad),
  278. strgad_buf(path_gad))) == NULL)
  279. update_gadgets();
  280. }
  281. static void select(int index)
  282. {
  283. gui_edit((Tool *) node_at(tools, index));
  284. }
  285. static void new_tool(void)
  286. {
  287. cx_add_tool("", "");
  288. }
  289. static void delete_tool(void)
  290. {
  291. GT_SetGadgetAttrs(name_gad, window, NULL,
  292. GTST_String, "",
  293. TAG_DONE);
  294. update_current();
  295. }
  296. static void handle_gadgetup(struct Gadget *gadget, UWORD code)
  297. {
  298. switch (gadget->GadgetID) {
  299. case GAD_ID_LIST:
  300. select(code);
  301. break;
  302. case GAD_ID_NEW:
  303. new_tool();
  304. break;
  305. case GAD_ID_DELETE:
  306. delete_tool();
  307. break;
  308. case GAD_ID_NAME:
  309. case GAD_ID_PATH:
  310. update_current();
  311. break;
  312. }
  313. }
  314. static void handle_project_menu(UWORD code)
  315. {
  316. switch (ITEMNUM(code)) {
  317. case 0: /* Save Menu Items */
  318. cx_save_tools();
  319. break;
  320. case 2: /* Hide */
  321. cx_hide();
  322. break;
  323. case 3: /* About */
  324. cx_about();
  325. break;
  326. case 4: /* Quit */
  327. cx_quit();
  328. break;
  329. }
  330. }
  331. static void handle_menupick(UWORD code)
  332. {
  333. while (code != MENUNULL) {
  334. switch (MENUNUM(code)) {
  335. case 0:
  336. handle_project_menu(code);
  337. break;
  338. }
  339. code = ItemAddress(menus, code)->NextSelect;
  340. }
  341. }
  342. static void handle_closewindow(void)
  343. {
  344. cx_hide();
  345. }
  346. static void handle_refreshwindow(void)
  347. {
  348. GT_BeginRefresh(window);
  349. render();
  350. GT_EndRefresh(window, TRUE);
  351. }
  352. static void bring_to_front(void)
  353. {
  354. ScreenToFront(window->WScreen);
  355. WindowToFront(window);
  356. ActivateWindow(window);
  357. if (window->Height == zoom[3])
  358. ZipWindow(window);
  359. }
  360. static void fill_itexts(struct IntuiText *text, va_list *ap)
  361. {
  362. char *s;
  363. while ((s = va_arg(*ap, char *)) != NULL) {
  364. text->IText = s;
  365. text->NextText = text + 1;
  366. ++text;
  367. }
  368. (text - 1)->NextText = NULL;
  369. }
  370. ULONG gui_set_up()
  371. {
  372. if (IntuitionBase->LibNode.lib_Version >= 39)
  373. zoom[0] = zoom[1] = ~0;
  374. if (!set_up_menus())
  375. exit(RETURN_FAIL);
  376. if ((user_port = CreateMsgPort()) == NULL)
  377. exit(RETURN_FAIL);
  378. itexts[0].IText = get_string(MSG_INFO_1);
  379. itexts[1].IText = get_string(MSG_INFO_2);
  380. itexts[2].IText = get_string(MSG_INFO_3);
  381. InitRequester(&busy_req);
  382. if (!message_cons(&message))
  383. exit(RETURN_FAIL);
  384. message_constructed = YES;
  385. return 1UL << user_port->mp_SigBit;
  386. }
  387. void gui_clean_up()
  388. {
  389. if (message_constructed)
  390. message_dest(&message);
  391. DeleteMsgPort(user_port);
  392. if (menus != NULL) /* Really checking if gadtools.library was opened */
  393. FreeMenus(menus);
  394. }
  395. void gui_handle_signal()
  396. {
  397. struct IntuiMessage *imsg;
  398. ULONG clas;
  399. UWORD code;
  400. APTR iadd;
  401. while ((imsg = GT_GetIMsg(user_port)) != NULL) {
  402. clas = imsg->Class;
  403. code = imsg->Code;
  404. iadd = imsg->IAddress;
  405. GT_ReplyIMsg(imsg);
  406. switch (clas) {
  407. case IDCMP_MENUPICK:
  408. handle_menupick(code);
  409. break;
  410. case IDCMP_CLOSEWINDOW:
  411. handle_closewindow();
  412. break;
  413. case IDCMP_REFRESHWINDOW:
  414. handle_refreshwindow();
  415. break;
  416. case IDCMP_GADGETUP:
  417. handle_gadgetup(iadd, code);
  418. break;
  419. }
  420. }
  421. }
  422. void gui_show()
  423. {
  424. Layout layout;
  425. if (window != NULL) {
  426. bring_to_front();
  427. return;
  428. }
  429. if ((screen = LockPubScreen(NULL)) == NULL)
  430. goto gui_show_failed;
  431. ScreenToFront(screen);
  432. if ((draw_info = GetScreenDrawInfo(screen)) == NULL)
  433. goto gui_show_failed;
  434. if ((visual_info = GetVisualInfo(screen, TAG_DONE)) == NULL)
  435. goto gui_show_failed;
  436. if (!LayoutMenus(menus, visual_info,
  437. GTMN_NewLookMenus, (LONG) TRUE,
  438. TAG_DONE))
  439. goto gui_show_failed;
  440. if (top_edge == NOT_SET)
  441. top_edge = screen->BarHeight + 1;
  442. zoom[2] = 25 * draw_info->dri_Font->tf_XSize;
  443. zoom[3] = window_border_top();
  444. make_layout(&layout);
  445. if (!create_gadgets(&layout))
  446. goto gui_show_failed;
  447. layout_texts(&layout);
  448. if (!open_window(&layout))
  449. goto gui_show_failed;
  450. render();
  451. app_window = wb_add_window(window);
  452. SetMenuStrip(window, menus);
  453. current = NULL;
  454. return;
  455. gui_show_failed:
  456. gui_hide();
  457. }
  458. void gui_hide()
  459. {
  460. if (app_window != NULL) {
  461. wb_remove_window(app_window);
  462. app_window = NULL;
  463. }
  464. if (window != NULL) {
  465. EndRequest(&message.requester, window);
  466. if (window->MenuStrip != NULL)
  467. ClearMenuStrip(window);
  468. left_edge = window->LeftEdge;
  469. top_edge = window->TopEdge;
  470. close_window_safely(window);
  471. window = NULL;
  472. }
  473. FreeGadgets(gadgets);
  474. gadgets = NULL;
  475. FreeVisualInfo(visual_info);
  476. visual_info = NULL;
  477. if (screen != NULL) {
  478. FreeScreenDrawInfo(screen, draw_info);
  479. draw_info = NULL;
  480. UnlockPubScreen(NULL, screen);
  481. screen = NULL;
  482. }
  483. }
  484. void gui_edit(Tool *tool)
  485. {
  486. if (window == NULL)
  487. return;
  488. current = tool;
  489. update_gadgets();
  490. if (current != NULL)
  491. ActivateGadget(name_gad, window, NULL);
  492. }
  493. /* Temporarily suspend the listview so we can update the list. */
  494. void gui_begin_update()
  495. {
  496. if (window == NULL)
  497. return;
  498. GT_SetGadgetAttrs(list_gad, window, NULL, GTLV_Labels, ~0L, TAG_DONE);
  499. }
  500. /* Resume normal listview operations. */
  501. void gui_end_update()
  502. {
  503. if (window == NULL)
  504. return;
  505. GT_SetGadgetAttrs(list_gad, window, NULL, GTLV_Labels, tools, TAG_DONE);
  506. }
  507. void gui_begin_busy()
  508. {
  509. static UWORD CHIP busy_pointer[] = {
  510. 0x0000, 0x0000, 0x0400, 0x07c0, 0x0000, 0x07c0, 0x0100, 0x0380,
  511. 0x0000, 0x07e0, 0x07c0, 0x1ff8, 0x1ff0, 0x3fec, 0x3ff8, 0x7fde,
  512. 0x3ff8, 0x7fbe, 0x7ffc, 0xff7f, 0x7efc, 0xffff, 0x7ffc, 0xffff,
  513. 0x3ff8, 0x7ffe, 0x3ff8, 0x7ffe, 0x1ff0, 0x3ffc, 0x07c0, 0x1ff8,
  514. 0x0000, 0x07e0, 0x0000, 0x0000
  515. };
  516. if (window == NULL)
  517. return;
  518. Request(&busy_req, window);
  519. if (IntuitionBase->LibNode.lib_Version < 39)
  520. SetPointer(window, busy_pointer, 16, 16, -6, 0);
  521. else
  522. SetWindowPointer(window,
  523. WA_BusyPointer, (LONG) TRUE,
  524. WA_PointerDelay, (LONG) TRUE,
  525. TAG_DONE);
  526. }
  527. void gui_end_busy()
  528. {
  529. if (window == NULL)
  530. return;
  531. if (IntuitionBase->LibNode.lib_Version < 39)
  532. ClearPointer(window);
  533. else
  534. SetWindowPointer(window, TAG_DONE);
  535. EndRequest(&busy_req, window);
  536. }
  537. void gui_message(char *button_text, ...)
  538. {
  539. static struct IntuiText texts[GUI_MESSAGE_MAX_LINES];
  540. va_list ap;
  541. va_start(ap, button_text);
  542. if (window == NULL || message.requester.Flags & REQACTIVE)
  543. goto gui_message_exit;
  544. fill_itexts(texts, &ap);
  545. message_layout(&message, window, draw_info, texts, button_text);
  546. Request(&message.requester, window);
  547. gui_message_exit:
  548. va_end(ap);
  549. }