/* * Copyright (c) 2007-2016 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "config/datadir.h" #include "config/version.h" #include "config/enable_nls.h" #include "config/localedir.h" #include #include #include "rgedit.h" static AG_Object rgedit; static AG_Menu *appMenu = NULL; static RG_Tileset *tsFocused = NULL; static int exiting = 0; static int nEditorWindows = 0; typedef AG_Surface *(*AG_SurfaceFromFn)(const char *); static void CloseObject(AG_Event *event) { AG_Window *win = AG_PTR(1); RG_Tileset *ts = AG_PTR(2); int save = AG_INT(3); if (save) { if (AG_ObjectSave(ts) == -1) { AG_TextMsgFromError(); /* TODO suggest "save as" */ return; } } AG_ObjectPageOut(ts); AG_ObjectDetach(win); if (--nEditorWindows == 0) AG_Terminate(0); } static void WindowClose(AG_Event *event) { AG_Window *win = AG_SELF(); RG_Tileset *ts = AG_PTR(1); AG_Event ev; AG_Button *bOpts[3]; AG_Window *wDlg; if (!AG_ObjectChanged(ts)) { AG_EventArgs(&ev, "%p,%p,%i", win, ts, 0); CloseObject(&ev); return; } wDlg = AG_TextPromptOptions(bOpts, 3, _("Save changes to %s?"), AGOBJECT(ts)->name); AG_WindowAttach(win, wDlg); AG_ButtonText(bOpts[0], _("Save")); AG_SetEvent(bOpts[0], "button-pushed", CloseObject, "%p,%p,%i", win, ts, 1); AG_WidgetFocus(bOpts[0]); AG_ButtonText(bOpts[1], _("Discard")); AG_SetEvent(bOpts[1], "button-pushed", CloseObject, "%p,%p,%i", win, ts, 0); AG_ButtonText(bOpts[2], _("Cancel")); AG_SetEvent(bOpts[2], "button-pushed", AGWINDETACH(wDlg)); } static void WindowGainedFocus(AG_Event *event) { /* AG_Window *win = AG_SELF(); */ RG_Tileset *ts = AG_PTR(1); tsFocused = ts; } static void WindowLostFocus(AG_Event *event) { /* AG_Window *win = AG_SELF(); */ tsFocused = NULL; } static void CreateEditionWindow(RG_Tileset *ts) { extern AG_Menu *agAppMenu; AG_Window *win; if ((win = rgTilesetClass.edit(ts)) == NULL) { return; } AG_SetEvent(win, "window-close", WindowClose, "%p", ts); AG_AddEvent(win, "window-gainfocus", WindowGainedFocus, "%p", ts); AG_AddEvent(win, "window-lostfocus", WindowLostFocus, "%p", ts); AG_AddEvent(win, "window-hidden", WindowLostFocus, "%p", ts); AG_WindowSetGeometryAligned(win, AG_WINDOW_ML, 300, 500); AG_WindowShow(win); nEditorWindows++; } static void NewTileset(AG_Event *event) { RG_Tileset *ts; ts = AG_ObjectNew(&rgedit, NULL, &rgTilesetClass); CreateEditionWindow(ts); } static void OpenTilesetAGT(AG_Event *event) { char *path = AG_STRING(1); RG_Tileset *ts; ts = AG_ObjectNew(&rgedit, NULL, &rgTilesetClass); if (AG_ObjectLoadFromFile(ts, path) == -1) { AG_TextMsgFromError(); return; } AG_SetString(ts, "archive-path", path); CreateEditionWindow(ts); } static void OpenTilesetDlg(AG_Event *event) { AG_Window *win; AG_FileDlg *fd; if ((win = AG_WindowNew(0)) == NULL) { return; } AG_WindowSetCaption(win, _("Open tileset...")); fd = AG_FileDlgNewMRU(win, "rgedit.mru.tilesets", AG_FILEDLG_LOAD|AG_FILEDLG_CLOSEWIN|AG_FILEDLG_EXPAND); AG_FileDlgAddType(fd, _("AgarPaint tileset"), "*.agt", OpenTilesetAGT, NULL); AG_WindowShow(win); } static void SaveTilesetToAGT(AG_Event *event) { RG_Tileset *ts = AG_PTR(1); char *path = AG_STRING(2); if (AG_ObjectSaveToFile(ts, path) == -1) { AG_TextMsgFromError(); return; } AG_SetString(ts, "archive-path", path); } static __inline__ void TransformIconName(char *s) { char *c; for (c = s; *c != '\0'; c++) { if (*c == '#') { *c = 'n'; } else if (!isalnum(*c)) *c = '_'; } } static void ExportTilesetToC(AG_Event *event) { char pathData[AG_PATHNAME_MAX]; char iconID[64]; RG_Tileset *ts = AG_PTR(1); char *path = AG_STRING(2); AG_FileType *ft = AG_PTR(3); char *pkgName = AG_FileOptionString(ft,"pkg-name"); int cppDecls = AG_FileOptionBool(ft,"cpp-decls"); int beginCode = AG_FileOptionBool(ft,"begin-code"); int w, h, count = 0; RG_Tile *t; FILE *f; Uint8 *src; char *c; if ((f = fopen(path, "w")) == NULL) { AG_SetError(_("Unable to open %s"), path); AG_TextMsgFromError(); return; } fprintf(f, "/* Agar icon reference file */\n"); fprintf(f, "/* Generated by FreeSG rgedit %s */\n\n", VERSION); if (beginCode) { fprintf(f, "#include \"begin.h\"\n"); } if (cppDecls) { fprintf(f, "__BEGIN_DECLS\n"); } TAILQ_FOREACH(t, &ts->tiles, tiles) { Strlcpy(iconID, t->name, sizeof(iconID)); TransformIconName(iconID); fprintf(f, "extern AG_StaticIcon %s%s;\n", pkgName, iconID); } if (cppDecls) { fprintf(f, "__END_DECLS\n"); } if (beginCode) { fprintf(f, "#include \"close.h\"\n"); } fclose(f); Strlcpy(pathData, path, sizeof(pathData)); if ((c = strrchr(pathData, '.')) != NULL) { *c = '\0'; } Strlcat(pathData, "_data.h", sizeof(pathData)); if ((f = fopen(pathData, "w")) == NULL) { AG_SetError(_("Unable to open %s"), pathData); AG_TextMsgFromError(); return; } fprintf(f, "/* Agar icon data file */\n"); fprintf(f, "/* Generated by FreeSG rgedit %s */\n\n", VERSION); TAILQ_FOREACH(t, &ts->tiles, tiles) { Strlcpy(iconID, t->name, sizeof(iconID)); TransformIconName(iconID); fprintf(f, "static const Uint32 %s%s_Data[%u] = {", pkgName, iconID, t->su->w*t->su->h); src = t->su->pixels; for (h = 0; h < t->su->h; h++) { for (w = 0; w < t->su->w; w++) { Uint32 px; px = AG_SurfaceGet32_At(t->su, src); if (px == 0) { fprintf(f, "0,"); } else { fprintf(f, "0x%lx,", (Ulong)px); } src += t->su->format.BytesPerPixel; } } fprintf(f, "};\n"); fprintf(f, "AG_StaticIcon %s%s = {", pkgName, iconID); fprintf(f, "%lu,%lu,0x%.08lx,0x%.08lx,0x%.08lx,0x%.08lx,", (unsigned long)t->su->w, (unsigned long)t->su->h, (unsigned long)t->su->format.Rmask, (unsigned long)t->su->format.Gmask, (unsigned long)t->su->format.Bmask, (unsigned long)t->su->format.Amask); fprintf(f, "%s%s_Data, NULL", pkgName, iconID); fprintf(f, "};\n"); } fprintf(f, "\nstatic __inline__ void\n%s_Init(void)\n{\n", pkgName); TAILQ_FOREACH(t, &ts->tiles, tiles) { Strlcpy(iconID, t->name, sizeof(iconID)); TransformIconName(iconID); fprintf(f, "\tAG_InitStaticIcon(&%s%s);\n", pkgName, iconID); count++; } fprintf(f, "}\n"); fclose(f); AG_TextInfo("exported-tileset", _("Successfully exported %s to header (%s)"), AGOBJECT(ts)->name, pkgName); } static void SaveTilesetAsDlg(AG_Event *event) { RG_Tileset *ts = AG_PTR(1); AG_Window *win; AG_FileDlg *fd; AG_FileType *ft; if ((win = AG_WindowNew(0)) == NULL) { return; } AG_WindowSetCaption(win, _("Save tileset as...")); fd = AG_FileDlgNewMRU(win, "rgedit.mru.tilesets", AG_FILEDLG_SAVE|AG_FILEDLG_CLOSEWIN|AG_FILEDLG_EXPAND); AG_FileDlgSetOptionContainer(fd, AG_BoxNewVert(win, AG_BOX_HFILL)); AG_FileDlgAddType(fd, _("AgarPaint tileset"), "*.agt", SaveTilesetToAGT, "%p", ts); ft = AG_FileDlgAddType(fd, _("Agar icon header"), "*.h", ExportTilesetToC, "%p", ts); AG_FileOptionNewString(ft, _("Package name: "), "pkg-name", "fooIcon"); AG_FileOptionNewBool(ft, _("Use begin and close.h"), "begin-code", 1); AG_FileOptionNewBool(ft, _("Use __BEGIN_DECLS"), "cpp-decls", 1); AG_WindowShow(win); } static void ImportImageFrom(AG_Event *event) { RG_Tileset *ts = AG_PTR(1); AG_SurfaceFromFn *fn = AG_PTR(2); char *path = AG_STRING(3); AG_Surface *bmp; RG_Tile *t; RG_Pixmap *px; if ((bmp = (*fn)(path)) == NULL) { AG_TextMsgFromError(); return; } px = RG_PixmapNew(ts, "Bitmap", 0); px->su = AG_SurfaceConvert(bmp, ts->fmt); AG_SurfaceFree(bmp); t = RG_TileNew(ts, "Bitmap", px->su->w, px->su->h, RG_TILE_SRCALPHA); RG_TileAddPixmap(t, NULL, px, 0, 0); RG_TileGenerate(t); } static void LoadTileFromXCF(AG_Surface *xcf, const char *lbl, void *p) { RG_Tileset *ts = p; RG_Pixmap *px; RG_Tile *t; px = RG_PixmapNew(ts, lbl, 0); px->su = AG_SurfaceConvert(xcf, ts->fmt); t = RG_TileNew(ts, lbl, xcf->w, xcf->h, RG_TILE_SRCALPHA); RG_TileAddPixmap(t, NULL, px, 0, 0); RG_TileGenerate(t); } /* Load image layers from Gimp 1.x XCF file. */ static void ImportLayersFromXCF(AG_Event *event) { RG_Tileset *ts = AG_PTR(1); char *path = AG_STRING(2); AG_DataSource *ds; int rv; if ((ds = AG_OpenFile(path, "rb")) == NULL) { AG_TextMsgFromError(); return; } rv = AG_XCFLoad(ds, 0, LoadTileFromXCF, ts); AG_CloseFile(ds); } static void ImportImagesDlg(AG_Event *event) { RG_Tileset *ts = AG_PTR(1); AG_Window *win; AG_FileDlg *fd; if ((win = AG_WindowNew(0)) == NULL) { return; } AG_WindowSetCaption(win, _("Import images...")); fd = AG_FileDlgNewMRU(win, "rgedit.mru.tilesets", AG_FILEDLG_LOAD|AG_FILEDLG_CLOSEWIN|AG_FILEDLG_EXPAND); AG_FileDlgAddType(fd, _("Windows bitmap"), "*.bmp", ImportImageFrom, "%p,%p", ts, AG_SurfaceFromBMP); AG_FileDlgAddType(fd, _("JPEG image"), "*.jpg,*.jpeg", ImportImageFrom, "%p,%p", ts, AG_SurfaceFromJPEG); AG_FileDlgAddType(fd, _("PNG image"), "*.png", ImportImageFrom, "%p,%p", ts, AG_SurfaceFromJPEG); AG_FileDlgAddType(fd, _("Gimp 1 XCF image"), "*.xcf", ImportLayersFromXCF, "%p", ts); AG_WindowShow(win); } static void SaveTileset(AG_Event *event) { RG_Tileset *ts = AG_PTR(1); if (!AG_Defined(ts,"archive-path")) { SaveTilesetAsDlg(event); return; } if (AG_ObjectSave(ts) == -1) { AG_TextMsg(AG_MSG_ERROR, _("Error saving tileset: %s"), AG_GetError()); } else { AG_TextInfo("saved-tileset", _("Saved tileset %s successfully"), AGOBJECT(ts)->name); } } static void FileMenu(AG_Event *event) { AG_MenuItem *m = AG_SENDER(); AG_MenuActionKb(m, _("New"), agIconDoc.s, AG_KEY_N, AG_KEYMOD_CTRL, NewTileset, NULL); AG_MenuActionKb(m, _("Open..."), agIconLoad.s, AG_KEY_O, AG_KEYMOD_CTRL, OpenTilesetDlg, NULL); if (tsFocused == NULL) { AG_MenuDisable(m); } AG_MenuActionKb(m, _("Save"), agIconSave.s, AG_KEY_S, AG_KEYMOD_CTRL, SaveTileset, "%p", tsFocused); AG_MenuAction(m, _("Save as..."), agIconSave.s, SaveTilesetAsDlg, "%p", tsFocused); AG_MenuSeparator(m); AG_MenuActionKb(m, _("Import images..."), agIconDocImport.s, AG_KEY_O, AG_KEYMOD_CTRL, ImportImagesDlg, "%p", tsFocused); if (tsFocused == NULL) { AG_MenuEnable(m); } } static void Undo(AG_Event *event) { printf("undo!\n"); } static void EditMenu(AG_Event *event) { AG_MenuItem *m = AG_SENDER(); if (tsFocused == NULL) { AG_MenuDisable(m); } AG_MenuActionKb(m, "Undo", NULL, AG_KEY_Z, AG_KEYMOD_CTRL, Undo, "%p", tsFocused); if (tsFocused == NULL) { AG_MenuEnable(m); } } int main(int argc, char *argv[]) { int i, debug = 0; const char *fontSpec = NULL; const char *driverSpec = NULL; char *optArg = NULL; int optInd; RG_Tileset *ts; int c, openedFiles = 0; #ifdef ENABLE_NLS bindtextdomain("rgedit", LOCALEDIR); bind_textdomain_codeset("rgedit", "UTF-8"); textdomain("rgedit"); #endif if (AG_InitCore("rgedit", AG_CREATE_DATADIR) == -1) { fprintf(stderr, "%s\n", AG_GetError()); return (1); } while ((c = AG_Getopt(argc, argv, "?vDd:t:", &optArg, &optInd)) != -1) { switch (c) { case 'v': printf("rgedit %s\n", VERSION); return (0); case 'D': debug = 1; break; case 'd': driverSpec = optArg; break; case 't': fontSpec = optArg; break; case '?': default: printf("%s [-vD] [-d agar-driver-spec] " "[-t font,size,flags]\n", agProgName); return (1); } } if (fontSpec != NULL) { AG_TextParseFontSpec(fontSpec); } if (AG_InitGraphics(driverSpec) == -1) { fprintf(stderr, "%s\n", AG_GetError()); return (1); } AG_SetStringF(agConfig, "load-path", ".:%s", DATADIR); AG_BindStdGlobalKeys(); RG_InitSubsystem(); AG_ObjectInitStatic(&rgedit, NULL); if (agDriverSw != NULL) { /* Go MDI-style */ appMenu = AG_MenuNewGlobal(0); AG_MenuDynamicItem(appMenu->root, _("File"), NULL, FileMenu, NULL); AG_MenuDynamicItem(appMenu->root, _("Edit"), NULL, EditMenu, NULL); } for (i = optInd; i < argc; i++) { ts = AG_ObjectNew(&rgedit, NULL, &rgTilesetClass); if (AG_ObjectLoadFromFile(ts, argv[i]) == 0) { AG_SetString(ts, "archive-path", argv[i]); CreateEditionWindow(ts); openedFiles++; } else { AG_TextMsgFromError(); AG_ObjectDetach(ts); AG_ObjectDestroy(ts); } } if (!agDriverSw && !openedFiles) { ts = AG_ObjectNew(&rgedit, NULL, &rgTilesetClass); CreateEditionWindow(ts); } AG_EventLoop(); AG_ConfigSave(); AG_DestroyGraphics(); AG_Destroy(); return (0); }