/* * Copyright (c) 2008 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. */ /* * Interface to the library of generic schematic blocks. */ #include "core.h" #include #include #include #include #if defined(HAVE_GETPWUID) && defined(HAVE_GETUID) #include #endif AG_Object *esSchemLibrary = NULL; /* Schematic block library */ char **esSchemLibraryDirs; /* Search directories */ int esSchemLibraryDirCount; /* Load a schem from file. */ static int LoadSchemFile(const char *path, AG_Object *objParent) { AG_ObjectHeader oh; AG_DataSource *ds; AG_Object *obj; /* Fetch class information from the object file's header. */ if ((ds = AG_OpenFile(path, "rb")) == NULL) { return (-1); } if (AG_ObjectReadHeader(ds, &oh) == -1) { AG_CloseFile(ds); return (-1); } AG_CloseFile(ds); if ((obj = AG_ObjectNew(objParent, NULL, &esSchemClass)) == NULL) { return (-1); } if (AG_ObjectLoadFromFile(obj, path) == -1) { AG_SetError("%s: %s", path, AG_GetError()); AG_ObjectDelete(obj); return (-1); } AG_SetString(obj, "archive-path", path); AG_ObjectSetNameS(obj, AG_ShortFilename(path)); return (0); } /* Load schem files in specified directory (and subdirectories). */ static int LoadSchemsFromDisk(const char *schemDir, AG_Object *objParent) { char path[AG_PATHNAME_MAX]; AG_Object *objFolder; AG_Dir *dir; int j; #if 0 Debug(NULL, "Scanning schem directory: %s (under \"%s\")\n", schemDir, objParent->name); #endif if ((dir = AG_OpenDir(schemDir)) == NULL) { return (-1); } for (j = 0; j < dir->nents; j++) { char *file = dir->ents[j]; AG_FileInfo fileInfo; char *s; if (file[0] == '.') continue; Strlcpy(path, schemDir, sizeof(path)); Strlcat(path, PATHSEP, sizeof(path)); Strlcat(path, file, sizeof(path)); if (AG_GetFileInfo(path, &fileInfo) == -1) { AG_Verbose("Ignoring: %s (%s)\n", path, AG_GetError()); continue; } if (fileInfo.type == AG_FILE_DIRECTORY) { if ((objFolder = AG_ObjectFindChild(objParent, file)) == NULL) { /* Should not happen */ AG_Verbose("Ignoring folder: %s\n", file); continue; } if (LoadSchemsFromDisk(path, objFolder) == -1) { AG_Verbose("Ignoring schem dir: %s (%s)\n", path, AG_GetError()); } continue; } if ((s = strstr(file, ".esh")) == NULL || s[4] != '\0') { continue; } if (LoadSchemFile(path, objParent) == -1) AG_Verbose("Ignoring schem file: %s (%s)", path, AG_GetError()); } AG_CloseDir(dir); return (0); } /* Load schem "folder" objects from disk. */ static int LoadFoldersFromDisk(const char *schemDir, AG_Object *objParent) { char path[AG_PATHNAME_MAX]; AG_Dir *dir; AG_Object *objFolder; int j; #if 0 Debug(NULL, "Scanning for folders in: %s (under \"%s\")\n", schemDir, objParent->name); #endif if ((dir = AG_OpenDir(schemDir)) == NULL) { return (-1); } for (j = 0; j < dir->nents; j++) { char *file = dir->ents[j]; AG_FileInfo fileInfo; if (file[0] == '.') continue; Strlcpy(path, schemDir, sizeof(path)); Strlcat(path, PATHSEP, sizeof(path)); Strlcat(path, file, sizeof(path)); if (AG_GetFileInfo(path, &fileInfo) == -1) { AG_Verbose("Ignoring: %s (%s)\n", path, AG_GetError()); continue; } if (fileInfo.type != AG_FILE_DIRECTORY) { continue; } if ((objFolder = AG_ObjectNew(objParent, file, &agObjectClass)) == NULL) { AG_Verbose("Ignoring folder: %s (%s)\n", path, AG_GetError()); continue; } if (LoadFoldersFromDisk(path, objFolder) == -1) { AG_Verbose("Ignoring folder: %s (%s)\n", path, AG_GetError()); } } AG_CloseDir(dir); return (0); } /* Load the schem library from disk. */ int ES_SchemLibraryLoad(void) { int i; if (esSchemLibrary != NULL) { if (AG_ObjectInUse(esSchemLibrary)) { /* XXX */ AG_Verbose("Not freeing schem library; schem(s) are" "currently in use"); } else { // AG_ObjectDestroy(esSchemLibrary); } } /* Create the folder structure. */ esSchemLibrary = AG_ObjectNew(NULL, "Schematic library", &agObjectClass); for (i = 0; i < esSchemLibraryDirCount; i++) { if (LoadFoldersFromDisk(esSchemLibraryDirs[i], esSchemLibrary) == -1) AG_Verbose("Skipping: %s\n", AG_GetError()); } /* Load the schem files. */ for (i = 0; i < esSchemLibraryDirCount; i++) { if (LoadSchemsFromDisk(esSchemLibraryDirs[i], esSchemLibrary) == -1) AG_Verbose("Skipping: %s\n", AG_GetError()); } return (0); } /* Register a search path for schem files. */ void ES_SchemLibraryRegisterDir(const char *path) { char *s, *p; esSchemLibraryDirs = Realloc(esSchemLibraryDirs, (esSchemLibraryDirCount+1)*sizeof(char *)); esSchemLibraryDirs[esSchemLibraryDirCount++] = s = Strdup(path); if (*(p = &s[strlen(s)-1]) == PATHSEPCHAR) *p = '\0'; } /* Unregister a schem directory. */ void ES_SchemLibraryUnregisterDir(const char *path) { int i; for (i = 0; i < esSchemLibraryDirCount; i++) { if (strcmp(esSchemLibraryDirs[i], path) == 0) break; } if (i == esSchemLibraryDirCount) { return; } free(esSchemLibraryDirs[i]); if (i < esSchemLibraryDirCount-1) { memmove(&esSchemLibraryDirs[i], &esSchemLibraryDirs[i+1], (esSchemLibraryDirCount-i-1)*sizeof(char *)); } esSchemLibraryDirCount--; } /* Initialize the schem library. */ void ES_SchemLibraryInit(void) { char path[AG_PATHNAME_MAX]; esSchemLibrary = NULL; esSchemLibraryDirs = NULL; esSchemLibraryDirCount = 0; Strlcpy(path, DATADIR, sizeof(path)); Strlcat(path, "/Schematics", sizeof(path)); ES_SchemLibraryRegisterDir(path); #if defined(HAVE_GETPWUID) && defined(HAVE_GETUID) { struct passwd *pwd = getpwuid(getuid()); Strlcpy(path, pwd->pw_dir, sizeof(path)); Strlcat(path, PATHSEP, sizeof(path)); Strlcat(path, ".edacious", sizeof(path)); Strlcat(path, PATHSEP, sizeof(path)); Strlcat(path, "Schematics", sizeof(path)); if (!AG_FileExists(path)) { if (AG_MkPath(path) == -1) { AG_Verbose("Failed to create %s (%s)\n", path, AG_GetError()); } } ES_SchemLibraryRegisterDir(path); } #endif if (ES_SchemLibraryLoad() == -1) AG_Verbose("Loading library: %s", AG_GetError()); } /* Destroy the schematic library. */ void ES_SchemLibraryDestroy(void) { AG_ObjectDestroy(esSchemLibrary); esSchemLibrary = NULL; Free(esSchemLibraryDirs); } static AG_TlistItem * FindSchems(AG_Tlist *tl, AG_Object *pob, int depth) { AG_Object *chld; AG_TlistItem *it; it = AG_TlistAddPtr(tl, AG_OfClass(pob, "ES_Schem:*") ? esIconComponent.s : agIconDirectory.s, pob->name, pob); it->depth = depth; if (!TAILQ_EMPTY(&pob->children)) { it->flags |= AG_TLIST_HAS_CHILDREN; } if ((it->flags & AG_TLIST_HAS_CHILDREN) && AG_TlistVisibleChildren(tl, it)) { TAILQ_FOREACH(chld, &pob->children, cobjs) FindSchems(tl, chld, depth+1); } return (it); } /* Generate a Tlist tree for the schem library. */ static void PollLibrary(AG_Event *event) { AG_Tlist *tl = AG_TLIST_SELF(); AG_TlistItem *ti; AG_TlistClear(tl); AG_LockVFS(esSchemLibrary); ti = FindSchems(tl, OBJECT(esSchemLibrary), 0); ti->flags |= AG_TLIST_ITEM_EXPANDED; AG_UnlockVFS(esSchemLibrary); AG_TlistRestore(tl); } static void InsertSchematic(AG_Event *event) { VG_View *vv = VG_VIEW_PTR(1); AG_TlistItem *ti = AG_TLIST_ITEM_PTR(2); ES_Schem *schem = ti->p1; if (!AG_OfClass(schem, "ES_Schem:*")) return; AG_TextMsg(AG_MSG_ERROR, "Insert schem %s in %s", OBJECT(schem)->name, OBJECT(vv)->name); } static void RefreshLibrary(AG_Event *event) { if (ES_SchemLibraryLoad() == -1) { AG_TextMsgFromError(); return; } } static void EditSchem(AG_Event *event) { AG_Object *obj = AG_OBJECT_PTR(1); (void)ES_OpenObject(obj); } static void SaveSchem(AG_Event *event) { AG_Object *obj = AG_OBJECT_PTR(1); const char *name = AG_ShortFilename(AG_GetStringP(obj,"archive-path")); if (AG_ObjectSave(obj) == -1) { AG_TextMsgFromError(); return; } AG_TextTmsg(AG_MSG_INFO, 1250, _("Successfully saved schematic to %s"), name); } static void SaveSchemAs(AG_Event *event) { AG_Object *obj = AG_OBJECT_PTR(1); const char *path = AG_STRING(2); const char *name = AG_ShortFilename(path); if (AG_ObjectSaveToFile(obj, path) == -1) { AG_TextMsgFromError(); return; } AG_SetString(obj, "archive-path", path); AG_ObjectSetNameS(obj, name); AG_TextTmsg(AG_MSG_INFO, 1250, _("Successfully saved schematic to %s"), name); } static void SaveSchemAsDlg(AG_Event *event) { AG_Object *obj = AG_OBJECT_PTR(1); AG_Window *win; AG_FileDlg *fd; if ((win = AG_WindowNew(0)) == NULL) { return; } AG_WindowSetCaption(win, _("Save %s as..."), obj->name); fd = AG_FileDlgNewMRU(win, "edacious.mru.schems", AG_FILEDLG_SAVE | AG_FILEDLG_CLOSEWIN | AG_FILEDLG_EXPAND); AG_FileDlgSetOptionContainer(fd, AG_BoxNewVert(win, AG_BOX_HFILL)); AG_FileDlgAddType(fd, _("Edacious schematic"), "*.esh", SaveSchemAs, "%p", obj); AG_WindowShow(win); } static void SchemMenu(AG_Event *event) { AG_Tlist *tl = AG_TLIST_SELF(); AG_Object *obj = AG_TlistSelectedItemPtr(tl); AG_PopupMenu *pm; if (!AG_OfClass(obj, "ES_Schem:*")) return; if ((pm = AG_PopupNew(tl)) == NULL) return; AG_MenuAction(pm->root, _("Edit schematic..."), esIconCircuit.s, EditSchem, "%p", obj); AG_MenuSeparator(pm->root); AG_MenuAction(pm->root, _("Save"), agIconSave.s, SaveSchem, "%p,%s", obj, ""); AG_MenuAction(pm->root, _("Save as..."), agIconSave.s, SaveSchemAsDlg, "%p", obj); AG_PopupShow(pm); } ES_SchemLibraryEditor * ES_SchemLibraryEditorNew(void *parent, VG_View *vv, Uint flags) { AG_Box *box; AG_Button *btn; AG_Tlist *tl; box = AG_BoxNewVert(parent, AG_BOX_EXPAND); tl = AG_TlistNewPolled(box, AG_TLIST_TREE|AG_TLIST_EXPAND, PollLibrary, NULL); AG_WidgetSetFocusable(tl, 0); AG_TlistSizeHint(tl, "XXXXXXXXXXXXXXXXXXX", 10); AG_TlistSetPopupFn(tl, SchemMenu, NULL); AG_SetEvent(tl, "tlist-dblclick", InsertSchematic, "%p", vv); btn = AG_ButtonNewFn(box, AG_BUTTON_HFILL, _("Refresh list"), RefreshLibrary, NULL); AG_WidgetSetFocusable(btn, 0); RefreshLibrary(NULL); return (ES_SchemLibraryEditor *)box; }