/* * Copyright (c) 2021-2022 joshua stein * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "chatter.h" #include "irc.h" #include "settings.h" #include "tcp.h" #include "util.h" NMRec notification = { 0 }; struct settings settings; MenuHandle apple_menu, file_menu, edit_menu, view_menu; void update_menu(void); void handle_exit(void); bool handle_menu(long menu_id); void show_connect_dialog(void); short main(void) { Handle mbar; EventRecord event; WindowPtr event_win; GrafPtr old_port; AppFile finder_file; struct focusable *found_focusable; short event_in, n; char key; long wait_ticks; short wait_type; SetApplLimit(GetApplLimit() - (1024 * 8)); InitGraf(&thePort); InitFonts(); FlushEvents(everyEvent, 0); InitWindows(); InitMenus(); TEInit(); InitDialogs(0); InitCursor(); MaxApplZone(); util_init(); _atexit(handle_exit); if (!(mbar = GetNewMBar(MBAR_ID))) panic("no mbar"); SetMenuBar(mbar); if (!(apple_menu = GetMHandle(APPLE_MENU_ID))) panic("no apple menu"); AddResMenu(apple_menu, 'DRVR'); if (!(file_menu = GetMHandle(FILE_MENU_ID))) panic("no file menu"); if (!(edit_menu = GetMHandle(EDIT_MENU_ID))) panic("no edit menu"); if (!(view_menu = GetMHandle(VIEW_MENU_ID))) panic("no view menu"); update_menu(); DrawMenuBar(); show_connect_dialog(); for (;;) { wait_type = WAIT_TYPE_BACKGROUND; for (n = 0; n < nfocusables; n++) { if (focusables[n]->wait_type) wait_type |= focusables[n]->wait_type(focusables[n]); else if (focusables[n]->visible) wait_type |= WAIT_TYPE_FOREGROUND; } if (wait_type & WAIT_TYPE_URGENT) wait_ticks = 0; else if (wait_type & WAIT_TYPE_FOREGROUND) wait_ticks = 5L; else wait_ticks = 60L; WaitNextEvent(everyEvent, &event, wait_ticks, 0L); switch (event.what) { case nullEvent: for (n = 0; n < nfocusables; n++) { if (focusables[n]->idle) focusables[n]->idle(focusables[n], &event); } break; case keyDown: case autoKey: key = event.message & charCodeMask; if ((event.modifiers & cmdKey) != 0 && handle_menu(MenuKey(key)) == true) break; if (nfocusables && focusables[0]->visible && focusables[0]->key_down) focusables[0]->key_down(focusables[0], &event); break; case mouseDown: event_in = FindWindow(event.where, &event_win); found_focusable = focusable_find(event_win); switch (event_in) { case inMenuBar: handle_menu(MenuSelect(event.where)); break; case inSysWindow: SystemClick(&event, event_win); break; case inDrag: DragWindow(event_win, event.where, &screenBits.bounds); break; case inGrow: if (event_win != FrontWindow() && found_focusable) { cancel_notification(); focusable_show(found_focusable); } if (found_focusable && found_focusable->resize) found_focusable->resize(found_focusable, &event); break; case inGoAway: if (TrackGoAway(event_win, event.where) && found_focusable) focusable_close(found_focusable); break; case inContent: if (event_win != FrontWindow()) { if (found_focusable) { cancel_notification(); focusable_show(found_focusable); } } if (found_focusable && found_focusable->mouse_down) found_focusable->mouse_down(found_focusable, &event); break; } update_menu(); break; case updateEvt: case activateEvt: event_win = (WindowPtr)event.message; found_focusable = focusable_find(event_win); GetPort(&old_port); SetPort(event_win); if (event.what == updateEvt) BeginUpdate(event_win); if (found_focusable && found_focusable->update) found_focusable->update(found_focusable, &event); if (event.what == updateEvt) EndUpdate(event_win); SetPort(old_port); break; case app4Evt: if (HiWord(event.message) & (1 << 8)) { /* multifinder suspend/resume */ switch (event.message & (1 << 0)) { case 0: /* suspend */ for (n = 0; n < nfocusables; n++) { if (focusables[n]->suspend) focusables[n]->suspend(focusables[n], &event); } break; case 1: /* resume */ for (n = 0; n < nfocusables; n++) { if (focusables[n]->resume) focusables[n]->resume(focusables[n], &event); } break; } update_menu(); } break; } } return 0; } void show_connect_dialog(void) { bool valid; valid = settings_edit(!settings_load()); update_menu(); if (!valid) return; chatter_init(settings.server, settings.port, settings.password, settings.nick, settings.ident, settings.realname, settings.hide_motd, settings.channel); } bool handle_menu(long menu_id) { size_t n; bool ret = false; bool quit = true; switch (HiWord(menu_id)) { case APPLE_MENU_ID: switch (LoWord(menu_id)) { case APPLE_MENU_ABOUT_ID: about(PROGRAM_NAME); ret = true; break; default: { Str255 da; GrafPtr save_port; GetItem(apple_menu, LoWord(menu_id), &da); GetPort(&save_port); OpenDeskAcc(da); SetPort(save_port); ret = true; break; } } break; case FILE_MENU_ID: switch (LoWord(menu_id)) { case FILE_MENU_CONNECT_ID: show_connect_dialog(); ret = true; break; case FILE_MENU_QUIT_ID: ret = true; if (focusables_quit()) ExitToShell(); break; } break; case VIEW_MENU_ID: switch (LoWord(menu_id)) { case VIEW_MENU_HIDE_ID: { Str255 text; struct focusable **tfocusables = NULL; /* hiding and showing adjusts order, so duplicate order */ tfocusables = xmalloc(sizeof(struct focusable *) * nfocusables); memcpy(tfocusables, focusables, sizeof(struct focusable *) * nfocusables); GetItem(view_menu, VIEW_MENU_HIDE_ID, &text); if (memcmp((char *)text + 1, "Hide", 4) == 0) { for (n = 0; n < nfocusables; n++) { if (tfocusables[n]->visible) focusable_hide(tfocusables[n]); } } else { for (n = 0; n < nfocusables; n++) { if (!tfocusables[n]->visible) focusable_show(tfocusables[n]); } } xfree(&tfocusables); update_menu(); ret = true; break; } } break; default: if (nfocusables && focusables[0]->visible && focusables[0]->menu) ret = focusables[0]->menu(focusables[0], HiWord(menu_id), LoWord(menu_id)); } HiliteMenu(0); return ret; } void update_menu(void) { short n; bool hidden = false; for (n = 0; n < nfocusables; n++) { if (!focusables[n]->visible) { hidden = true; break; } } if (hidden) SetItem(view_menu, VIEW_MENU_HIDE_ID, "\pShow Windows"); else SetItem(view_menu, VIEW_MENU_HIDE_ID, "\pHide Windows"); if (nfocusables && focusables[0]->visible && focusables[0]->update_menu) { focusables[0]->update_menu(focusables[0]); return; } DisableItem(edit_menu, EDIT_MENU_CUT_ID); DisableItem(edit_menu, EDIT_MENU_COPY_ID); DisableItem(edit_menu, EDIT_MENU_PASTE_ID); } void handle_exit(void) { while (nfocusables > 0) { if (focusables[0]->atexit) focusables[0]->atexit(focusables[0]); else focusable_close(focusables[0]); } while (!SLIST_EMPTY(&irc_connections_list)) irc_dealloc_connection(SLIST_FIRST(&irc_connections_list)); } void notify(void) { memset(¬ification, 0, sizeof(notification)); notification.qType = nmType; notification.nmMark = 1; notification.nmSound = (Handle)-1; notification.nmIcon = GetResource('SICN', NOTIFICATION_ICON_ID); NMInstall(¬ification); } void cancel_notification(void) { if (notification.qType) NMRemove(¬ification); memset(¬ification, 0, sizeof(notification)); }