/*
* Copyright (c) 2003-2007 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.
*/
/*
* Generic object browser.
*/
#include
#include
#ifdef AG_NETWORK
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "dev.h"
struct objent {
AG_Object *obj;
AG_Window *win;
TAILQ_ENTRY(objent) objs;
};
static TAILQ_HEAD(,objent) dobjs;
static TAILQ_HEAD(,objent) gobjs;
static int editNowFlag = 1;
static void *lastSelectedParent = NULL;
static void
CreateObject(AG_Event *event)
{
char name[AG_OBJECT_NAME_MAX];
AG_Object *vfsRoot = AG_PTR(1);
AG_ObjectClass *cl = AG_PTR(2);
AG_Textbox *name_tb = AG_PTR(3);
AG_Tlist *tlObjs = AG_PTR(4);
AG_Window *dlg_win = AG_PTR(5);
AG_TlistItem *it;
AG_Object *pobj;
void *nobj;
if ((it = AG_TlistSelectedItem(tlObjs)) != NULL) {
pobj = it->p1;
} else {
pobj = vfsRoot;
}
AG_TextboxCopyString(name_tb, name, sizeof(name));
AG_ViewDetach(dlg_win);
if (name[0] == '\0')
AG_ObjectGenName(vfsRoot, cl, name, sizeof(name));
nobj = Malloc(cl->size);
AG_ObjectInit(nobj, cl);
AG_ObjectSetName(nobj, "%s", name);
AG_ObjectAttach(pobj, nobj);
AG_ObjectUnlinkDatafiles(nobj);
AG_PostEvent(NULL, nobj, "edit-create", NULL);
if (editNowFlag && cl->edit != NULL)
DEV_BrowserOpenData(nobj);
}
enum {
OBJEDIT_EDIT_DATA,
OBJEDIT_EDIT_GENERIC,
OBJEDIT_LOAD,
OBJEDIT_SAVE,
OBJEDIT_SAVE_ALL,
OBJEDIT_EXPORT,
OBJEDIT_FREE_DATASET,
OBJEDIT_DESTROY,
OBJEDIT_MOVE_UP,
OBJEDIT_MOVE_DOWN,
OBJEDIT_DUP,
OBJEDIT_RCS_UPDATE,
OBJEDIT_RCS_UPDATE_ALL,
OBJEDIT_RCS_COMMIT,
OBJEDIT_RCS_COMMIT_ALL,
OBJEDIT_RCS_IMPORT,
OBJEDIT_RCS_IMPORT_ALL
};
static void
CloseGenericDlg(AG_Event *event)
{
AG_Window *win = AG_SELF();
struct objent *oent = AG_PTR(1);
AG_ViewDetach(win);
TAILQ_REMOVE(&gobjs, oent, objs);
Free(oent);
}
void
DEV_BrowserOpenGeneric(AG_Object *ob)
{
struct objent *oent;
TAILQ_FOREACH(oent, &gobjs, objs) {
if (oent->obj == ob)
break;
}
if (oent != NULL) {
AG_WindowShow(oent->win);
agView->winToFocus = oent->win;
return;
}
oent = Malloc(sizeof(struct objent));
oent->obj = ob;
oent->win = DEV_ObjectEdit(ob);
TAILQ_INSERT_HEAD(&gobjs, oent, objs);
AG_WindowShow(oent->win);
AG_SetEvent(oent->win, "window-close", CloseGenericDlg, "%p", oent);
}
static void
SaveAndCloseObject(struct objent *oent, AG_Window *win, int save)
{
AG_WindowHide(win);
AG_PostEvent(NULL, oent->obj, "edit-close", NULL);
AG_ViewDetach(win);
TAILQ_REMOVE(&dobjs, oent, objs);
if (!save) {
agTerminating = 1;
}
AG_ObjectPageOut(oent->obj);
agTerminating = 0;
Free(oent);
}
static void
SaveChangesReturn(AG_Event *event)
{
AG_Window *win = AG_PTR(1);
struct objent *oent = AG_PTR(2);
int save = AG_INT(3);
SaveAndCloseObject(oent, win, save);
}
static void
SaveChangesDlg(AG_Event *event)
{
AG_Window *win = AG_SELF();
struct objent *oent = AG_PTR(1);
if (AG_ObjectChanged(oent->obj)) {
AG_Button *bOpts[3];
AG_Window *wDlg;
wDlg = AG_TextPromptOptions(bOpts, 3, _("Save changes to %s?"),
OBJECT(oent->obj)->name);
AG_WindowAttach(win, wDlg);
{
AG_ButtonText(bOpts[0], _("Save"));
AG_SetEvent(bOpts[0], "button-pushed",
SaveChangesReturn, "%p,%p,%i", win, oent, 1);
AG_WidgetFocus(bOpts[0]);
AG_ButtonText(bOpts[1], _("Discard"));
AG_SetEvent(bOpts[1], "button-pushed",
SaveChangesReturn, "%p,%p,%i", win, oent, 0);
AG_ButtonText(bOpts[2], _("Cancel"));
AG_SetEvent(bOpts[2], "button-pushed",
AGWINDETACH(wDlg));
}
} else {
SaveAndCloseObject(oent, win, 1);
}
}
void
DEV_BrowserOpenData(void *p)
{
AG_Object *ob = p;
struct objent *oent;
AG_Window *win;
int dataFound = 0;
TAILQ_FOREACH(oent, &dobjs, objs) {
if (oent->obj == ob)
break;
}
if (oent != NULL) {
AG_WindowShow(oent->win);
agView->winToFocus = oent->win;
return;
}
if (ob->cls->edit == NULL)
return;
if (OBJECT_PERSISTENT(ob) &&
!OBJECT_RESIDENT(ob)) {
if (AG_ObjectLoadGenericFromFile(ob, NULL) == -1 ||
AG_ObjectResolveDeps(ob) == -1 ||
AG_ObjectLoadDataFromFile(ob, &dataFound, NULL) == -1) {
if (!dataFound) {
/*
* Data not found in storage, so assume
* object is new. Mark it resident and save it.
*/
ob->flags |= AG_OBJECT_RESIDENT;
if (AG_ObjectSave(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s",
ob->name, AG_GetError());
return;
}
} else {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name,
AG_GetError());
return;
}
}
AG_PostEvent(NULL, ob, "edit-post-load", NULL);
}
if ((win = ob->cls->edit(ob)) == NULL) {
goto fail;
}
AG_PostEvent(NULL, ob, "edit-open", NULL);
oent = Malloc(sizeof(struct objent));
oent->obj = ob;
oent->win = win;
TAILQ_INSERT_HEAD(&dobjs, oent, objs);
AG_SetEvent(win, "window-close", SaveChangesDlg, "%p", oent);
AG_WindowShow(win);
return;
fail:
AG_ObjectPageOut(ob);
return;
}
static void
ExportObject(AG_Event *event)
{
AG_Object *ob = AG_PTR(1);
char *path = AG_STRING(3);
int loadedTmp = 0;
int dataFound;
/* Load the object temporarily if it is non-resident. */
if (!OBJECT_RESIDENT(ob)) {
if (AG_ObjectLoadData(ob, &dataFound) == -1) {
if (!dataFound) {
/* Object was never saved before. */
ob->flags |= AG_OBJECT_RESIDENT;
} else {
AG_TextMsg(AG_MSG_ERROR,
_("%s: Loading failed (non-resident): %s"),
ob->name, AG_GetError());
return;
}
}
AG_PostEvent(NULL, ob, "edit-post-load", NULL);
loadedTmp = 1;
}
if (AG_ObjectSaveToFile(ob, path) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name, AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1000,
_("Object `%s' was exported successfully."), ob->name);
}
if (loadedTmp)
AG_ObjectFreeDataset(ob);
}
static void
ImportObject(AG_Event *event)
{
AG_Object *ob = AG_PTR(1);
char *path = AG_STRING(3);
int loadedTmp = 0;
int dataFound;
/* Load the object temporarily if it is non-resident. */
if (!OBJECT_RESIDENT(ob)) {
if (AG_ObjectLoadData(ob, &dataFound) == -1) {
if (!dataFound) {
/* Object was never saved before. */
ob->flags |= AG_OBJECT_RESIDENT;
} else {
AG_TextMsg(AG_MSG_ERROR,
_("%s: Loading failed (non-resident): %s"),
ob->name, AG_GetError());
return;
}
}
loadedTmp = 1;
AG_PostEvent(NULL, ob, "edit-post-load", NULL);
}
if (AG_ObjectLoadFromFile(ob, path) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name, AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1000,
_("Object `%s' was imported successfully."), ob->name);
}
if (loadedTmp)
AG_ObjectFreeDataset(ob);
}
void
DEV_BrowserSaveTo(void *p, const char *name)
{
char ext[AG_OBJECT_HIER_MAX+3];
AG_Object *ob = p;
AG_Window *win;
AG_FileDlg *fd;
ext[0] = '*';
ext[1] = '.';
Strlcpy(&ext[2], ob->cls->name, sizeof(ext)-2);
win = AG_WindowNew(0);
AG_WindowSetCaption(win, _("Save %s to..."), ob->name);
fd = AG_FileDlgNewMRU(win, "dev.mru.object-import",
AG_FILEDLG_CLOSEWIN|AG_FILEDLG_SAVE| AG_FILEDLG_EXPAND);
AG_FileDlgAddType(fd, name, ext, ExportObject, "%p,%p", ob, win);
AG_FileDlgSetFilename(fd, "%s.%s", ob->name, ob->cls->name);
AG_WindowShow(win);
}
void
DEV_BrowserLoadFrom(void *p, const char *name)
{
char ext[AG_OBJECT_HIER_MAX+3];
AG_Object *ob = p;
AG_Window *win;
AG_FileDlg *fd;
ext[0] = '*';
ext[1] = '.';
Strlcpy(&ext[2], ob->cls->name, sizeof(ext)-2);
win = AG_WindowNew(0);
AG_WindowSetCaption(win, _("Load %s from..."), ob->name);
fd = AG_FileDlgNewMRU(win, "dev.mru.object-import",
AG_FILEDLG_CLOSEWIN|AG_FILEDLG_LOAD|AG_FILEDLG_EXPAND);
AG_FileDlgAddType(fd, name, ext, ImportObject, "%p,%p", ob, win);
AG_FileDlgSetFilename(fd, "%s.%s", ob->name, ob->cls->name);
AG_WindowShow(win);
}
static void
ObjectOp(AG_Event *event)
{
void *vfsRoot = AG_PTR(1);
AG_Tlist *tl = AG_PTR(2);
int op = AG_INT(3);
AG_TlistItem *it;
TAILQ_FOREACH(it, &tl->items, items) {
AG_Object *ob = it->p1;
if (!it->selected)
continue;
switch (op) {
case OBJEDIT_EDIT_DATA:
if (ob->cls->edit != NULL) {
DEV_BrowserOpenData(ob);
} else {
AG_TextTmsg(AG_MSG_ERROR, 750,
_("Object `%s' has no edit operation."),
ob->name);
}
break;
case OBJEDIT_EDIT_GENERIC:
DEV_BrowserOpenGeneric(ob);
break;
case OBJEDIT_LOAD:
if (AG_ObjectLoad(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name,
AG_GetError());
} else {
AG_PostEvent(NULL, ob, "edit-post-load", NULL);
}
break;
case OBJEDIT_SAVE:
if (AG_ObjectSave(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name,
AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1000,
_("Object `%s' was saved successfully."),
ob->name);
}
break;
case OBJEDIT_SAVE_ALL:
if (AG_ObjectSaveAll(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name,
AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1000,
_("Object `%s' was saved successfully."),
ob->name);
}
break;
case OBJEDIT_EXPORT:
if (!OBJECT_PERSISTENT(ob)) {
AG_SetError(
_("The `%s' object is non-persistent."),
ob->name);
} else {
DEV_BrowserSaveTo(ob, _("Agar object file"));
}
break;
case OBJEDIT_DUP:
{
char dupName[AG_OBJECT_NAME_MAX];
AG_Object *dob;
if (ob == vfsRoot || !OBJECT_PERSISTENT(ob)) {
AG_TextMsg(AG_MSG_ERROR,
_("%s: cannot duplicate."),
ob->name);
break;
}
AG_ObjectGenName(ob->parent, ob->cls, dupName,
sizeof(dupName));
if ((dob = AG_ObjectDuplicate(ob, dupName))
== NULL) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s",
ob->name, AG_GetError());
}
}
break;
case OBJEDIT_MOVE_UP:
AG_ObjectMoveUp(ob);
break;
case OBJEDIT_MOVE_DOWN:
AG_ObjectMoveDown(ob);
break;
case OBJEDIT_FREE_DATASET:
if (it->p1 == vfsRoot) {
continue;
}
AG_ObjectFreeDataset(ob);
break;
case OBJEDIT_DESTROY:
if (it->p1 == vfsRoot) {
continue;
}
if (AG_ObjectInUse(ob)) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name,
AG_GetError());
continue;
}
if (ob->flags & AG_OBJECT_INDESTRUCTIBLE) {
AG_TextMsg(AG_MSG_ERROR,
_("The `%s' object is indestructible."),
ob->name);
continue;
}
AG_ObjectDetach(ob);
AG_ObjectUnlinkDatafiles(ob);
AG_ObjectDestroy(ob);
break;
#ifdef AG_NETWORK
case OBJEDIT_RCS_IMPORT:
if (AG_ObjectSave(ob) == -1 ||
AG_RcsImport(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name,
AG_GetError());
}
break;
case OBJEDIT_RCS_IMPORT_ALL:
AG_RcsImportAll(ob);
break;
case OBJEDIT_RCS_COMMIT:
if (AG_ObjectSave(ob) == -1 ||
AG_RcsCommit(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name,
AG_GetError());
}
break;
case OBJEDIT_RCS_COMMIT_ALL:
AG_RcsCommitAll(ob);
break;
case OBJEDIT_RCS_UPDATE:
if (AG_ObjectSave(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR,
_("Save failed: %s: %s"), ob->name,
AG_GetError());
break;
}
if (AG_RcsUpdate(ob) == 0) {
if (AG_ObjectLoad(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s",
ob->name, AG_GetError());
}
} else {
AG_TextMsg(AG_MSG_ERROR, "%s", AG_GetError());
}
break;
case OBJEDIT_RCS_UPDATE_ALL:
AG_RcsUpdateAll(ob);
break;
#endif /* AG_NETWORK */
}
}
}
static void
DEV_BrowserGenericSave(AG_Event *event)
{
AG_Object *ob = AG_PTR(1);
if (AG_ObjectSave(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, _("Save failed: %s: %s"), ob->name,
AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1000, _("`%s' saved successfully."),
ob->name);
}
}
static void
DEV_BrowserGenericLoad(AG_Event *event)
{
AG_Object *ob = AG_PTR(1);
if (AG_ObjectLoad(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, _("Load failed: %s: %s"), ob->name,
AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1000, _("`%s' loaded successfully."),
ob->name);
}
}
static void
DEV_BrowserGenericSaveTo(AG_Event *event)
{
DEV_BrowserSaveTo(AG_PTR(1), _("Agar object file"));
}
static void
DEV_BrowserGenericLoadFrom(AG_Event *event)
{
DEV_BrowserLoadFrom(AG_PTR(1), _("Agar object file"));
}
void
DEV_BrowserGenericMenu(void *menup, void *obj)
{
AG_MenuItem *pitem = menup;
AG_MenuAction(pitem, _("Save"), agIconSave.s,
DEV_BrowserGenericSave, "%p", obj);
AG_MenuAction(pitem, _("Load"), agIconLoad.s,
DEV_BrowserGenericLoad, "%p", obj);
AG_MenuAction(pitem, _("Export to..."), agIconSave.s,
DEV_BrowserGenericSaveTo, "%p", obj);
AG_MenuAction(pitem, _("Import from..."), agIconLoad.s,
DEV_BrowserGenericLoadFrom, "%p", obj);
AG_MenuSeparator(pitem);
AG_MenuUintFlags(pitem, _("Persistence"), agIconLoad.s,
&OBJECT(obj)->flags, AG_OBJECT_NON_PERSISTENT, 1);
AG_MenuUintFlags(pitem, _("Destructible"), agIconTrash.s,
&OBJECT(obj)->flags, AG_OBJECT_INDESTRUCTIBLE, 1);
AG_MenuUintFlags(pitem, _("Editable"), NULL,
&OBJECT(obj)->flags, AG_OBJECT_READONLY, 1);
}
static AG_TlistItem *
PollObjectsFind(AG_Tlist *tl, AG_Object *pob, int depth)
{
char label[AG_TLIST_LABEL_MAX];
AG_Object *cob;
AG_TlistItem *it;
Strlcpy(label, pob->name, sizeof(label));
if (OBJECT_RESIDENT(pob)) {
Strlcat(label, _(" (resident)"), sizeof(label));
}
it = AG_TlistAddPtr(tl, AG_ObjectIcon(pob), label, pob);
it->depth = depth;
it->cat = "object";
if (!TAILQ_EMPTY(&pob->children)) {
it->flags |= AG_TLIST_HAS_CHILDREN;
if (AG_ObjectRoot(pob) == pob)
it->flags |= AG_TLIST_VISIBLE_CHILDREN;
}
if ((it->flags & AG_TLIST_HAS_CHILDREN) &&
AG_TlistVisibleChildren(tl, it)) {
TAILQ_FOREACH(cob, &pob->children, cobjs)
PollObjectsFind(tl, cob, depth+1);
}
return (it);
}
static void
PollObjects(AG_Event *event)
{
AG_Tlist *tl = AG_SELF();
AG_Object *pob = AG_PTR(1);
AG_Object *dob = AG_PTR(2);
AG_TlistItem *it;
AG_LockVFS(pob);
AG_TlistClear(tl);
PollObjectsFind(tl, pob, 0);
AG_TlistRestore(tl);
AG_UnlockVFS(pob);
if (AG_TlistSelectedItem(tl) == NULL) {
TAILQ_FOREACH(it, &tl->items, items) {
if (it->p1 == dob)
break;
}
if (it != NULL)
AG_TlistSelect(tl, it);
}
}
static void
LoadObject(AG_Event *event)
{
AG_Object *o = AG_PTR(1);
if (AG_ObjectLoad(o) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", OBJECT(o)->name,
AG_GetError());
}
}
static void
SaveObject(AG_Event *event)
{
AG_Object *ob = AG_PTR(1);
if (AG_ObjectSave(ob) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", ob->name, AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1000,
_("Object `%s' saved successfully."), ob->name);
}
}
static void
ExitProgram(AG_Event *event)
{
SDL_Event ev;
ev.type = SDL_QUIT;
SDL_PushEvent(&ev);
}
static void
ShowPreferences(AG_Event *event)
{
DEV_ConfigShow();
}
static void
CreateObjectDlg(AG_Event *event)
{
AG_Window *win;
AG_Object *vfsRoot = AG_PTR(1);
AG_ObjectClass *cl = AG_PTR(2);
AG_Window *pwin = AG_PTR(3);
AG_Tlist *tlParents;
AG_Box *bo;
AG_Textbox *tb;
win = AG_WindowNew(AG_WINDOW_NOCLOSE|AG_WINDOW_NOMINIMIZE);
AG_WindowSetCaption(win, _("New %s object"), cl->name);
AG_WindowSetPosition(win, AG_WINDOW_CENTER, 1);
bo = AG_BoxNew(win, AG_BOX_VERT, AG_BOX_HFILL);
{
AG_LabelNew(bo, 0, _("Class: %s"), cl->hier);
tb = AG_TextboxNew(bo, 0, _("Name: "));
AG_WidgetFocus(tb);
}
AG_SeparatorNew(win, AG_SEPARATOR_HORIZ);
bo = AG_BoxNew(win, AG_BOX_VERT, AG_BOX_HFILL|AG_BOX_VFILL);
AG_BoxSetPadding(bo, 0);
AG_BoxSetSpacing(bo, 0);
{
AG_LabelNewString(bo, 0, _("Parent object:"));
tlParents = AG_TlistNewPolled(bo,
AG_TLIST_POLL|AG_TLIST_TREE|AG_TLIST_EXPAND,
PollObjects, "%p,%p", vfsRoot, lastSelectedParent);
AG_TlistSizeHint(tlParents, "XXXXXXXXXXXXXXXXXXX", 5);
AG_BindPointer(tlParents, "selected",
(void **)&lastSelectedParent);
}
bo = AG_BoxNew(win, AG_BOX_VERT, AG_BOX_HFILL);
{
AG_CheckboxNewInt(win, 0, _("Edit now"), &editNowFlag);
}
bo = AG_BoxNew(win, AG_BOX_HORIZ, AG_BOX_HOMOGENOUS|AG_BOX_HFILL);
{
AG_ButtonNewFn(bo, 0, _("OK"),
CreateObject, "%p,%p,%p,%p,%p", vfsRoot, cl, tb, tlParents,
win);
AG_SetEvent(tb, "textbox-return",
CreateObject, "%p,%p,%p,%p,%p", vfsRoot, cl, tb, tlParents,
win);
AG_ButtonNewFn(bo, 0, _("Cancel"), AGWINDETACH(win));
}
AG_WindowAttach(pwin, win);
AG_WindowShow(win);
}
#ifdef AG_NETWORK
static void
PollRevisions(AG_Event *event)
{
AG_Tlist *tl = AG_PTR(1);
AG_TlistItem *it;
AG_RCSList list;
int i;
if (!agRcsMode) {
AG_TextMsg(AG_MSG_ERROR, _("RCS is currently disabled."));
return;
}
if (AG_RcsConnect() == -1 ||
AG_RcsGetList(&list) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s", AG_GetError());
goto out;
}
AG_TlistClear(tl);
it = AG_TlistAdd(tl, NULL, "/");
it->flags |= AG_TLIST_HAS_CHILDREN;
it->cat = "object";
it->depth = 0;
for (i = 0; i < list.nEnts; i++) {
AG_RCSListEntry *lent = &list.ents[i];
int depth = 0;
char *c;
for (c = &lent->name[0]; *c != '\0'; c++) {
if (*c == '/')
depth++;
}
it = AG_TlistAdd(tl, NULL, "%s", &lent->name[1]);
it->cat = "object";
it->depth = depth;
}
AG_TlistRestore(tl);
AG_RcsFreeList(&list);
out:
AG_RcsDisconnect();
}
static void
UpdateFromRepo(AG_Event *event)
{
void *vfsRoot = AG_PTR(1);
AG_Tlist *tl = AG_PTR(2);
AG_TlistItem *it;
TAILQ_FOREACH(it, &tl->items, items) {
if (!it->selected)
continue;
if (AG_RcsCheckout(vfsRoot, it->text) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", it->text,
AG_GetError());
}
}
}
static void
DeleteFromRepo(AG_Event *event)
{
AG_Tlist *tl = AG_PTR(1);
AG_TlistItem *it;
TAILQ_FOREACH(it, &tl->items, items) {
if (!it->selected)
continue;
if (AG_RcsDelete(it->text) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", it->text,
AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 500,
_("Object %s removed from repository."), it->text);
}
}
#if 0
if (AG_RcsConnect() == 0) {
AG_RcsList(tl);
AG_RcsDisconnect();
}
#endif
}
static void
RepoRenameObject(AG_Event *event)
{
char *from = AG_STRING(2);
char *to = AG_STRING(3);
if (AG_RcsRename(from, to) == -1) {
AG_TextMsg(AG_MSG_ERROR, "%s: %s", from, AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1000, _("Object %s renamed to %s."),
from, to);
}
#if 0
if (AG_RcsConnect() == 0) {
AG_RcsList(tl);
AG_RcsDisconnect();
}
#endif
}
static void
RepoRenameDlg(AG_Event *event)
{
char prompt[AG_LABEL_MAX];
AG_Tlist *tl = AG_PTR(1);
AG_TlistItem *it;
if ((it = AG_TlistSelectedItem(tl)) == NULL)
return;
Snprintf(prompt, sizeof(prompt), _("Rename %s to:"), it->text);
AG_TextPromptString(prompt, RepoRenameObject, "%p,%s", tl, it->text);
}
#endif /* AG_NETWORK */
static void
GenNewObjectMenu(AG_MenuItem *mParent, AG_ObjectClass *cls, AG_Object *vfsRoot,
AG_Window *win)
{
AG_ObjectClass *subcls;
AG_MenuItem *mNode;
mNode = AG_MenuNode(mParent, cls->name, NULL);
AG_MenuAction(mNode, _("Create instance..."), NULL,
CreateObjectDlg, "%p,%p,%p", vfsRoot, cls, win);
if (!TAILQ_EMPTY(&cls->sub)) {
AG_MenuSeparator(mNode);
TAILQ_FOREACH(subcls, &cls->sub, subclasses)
GenNewObjectMenu(mNode, subcls, vfsRoot, win);
}
}
/* Create the object browser window. */
AG_Window *
DEV_Browser(void *vfsRoot)
{
AG_Window *win;
AG_Tlist *tlObjs;
AG_Menu *me;
AG_MenuItem *mi, *mi_objs;
AG_Notebook *nb;
AG_NotebookTab *ntab;
win = AG_WindowNewNamed(0, "DEV_Browser");
AG_WindowSetCaption(win, "%s", OBJECT(vfsRoot)->name);
AG_WindowSetPosition(win, AG_WINDOW_UPPER_LEFT, 0);
tlObjs = AG_TlistNew(NULL, AG_TLIST_POLL|AG_TLIST_MULTI|AG_TLIST_TREE|
AG_TLIST_EXPAND);
AG_TlistSizeHint(tlObjs, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 10);
AG_SetEvent(tlObjs, "tlist-poll",
PollObjects, "%p,%p", vfsRoot, NULL);
AG_SetEvent(tlObjs, "tlist-dblclick",
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs, OBJEDIT_EDIT_DATA);
me = AG_MenuNew(win, AG_MENU_HFILL);
mi = AG_MenuAddItem(me, _("File"));
{
mi_objs = AG_MenuNode(mi, _("New object"), NULL);
GenNewObjectMenu(mi_objs, agClassTree, vfsRoot, win);
AG_MenuSeparator(mi);
AG_MenuAction(mi, _("Load VFS"), agIconLoad.s,
LoadObject, "%p", vfsRoot);
AG_MenuAction(mi, _("Save VFS"), agIconSave.s,
SaveObject, "%p", vfsRoot);
AG_MenuSeparator(mi);
AG_MenuAction(mi, _("Exit"), NULL, ExitProgram, NULL);
}
mi = AG_MenuAddItem(me, _("Edit"));
{
AG_MenuAction(mi, _("Edit object data..."), NULL,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_EDIT_DATA);
AG_MenuAction(mi, _("Edit generic information..."), NULL,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_EDIT_GENERIC);
AG_MenuSeparator(mi);
AG_MenuAction(mi, _("Duplicate"), NULL,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs, OBJEDIT_DUP);
AG_MenuActionKb(mi, _("Move up"), agIconUp.s,
SDLK_u, KMOD_SHIFT,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs, OBJEDIT_MOVE_UP);
AG_MenuActionKb(mi, _("Move down"), agIconDown.s,
SDLK_d, KMOD_SHIFT,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs, OBJEDIT_MOVE_DOWN);
AG_MenuSeparator(mi);
AG_MenuAction(mi, _("Agar settings..."), NULL,
ShowPreferences, NULL);
}
#ifdef AG_NETWORK
mi = AG_MenuAddItem(me, _("Repository"));
{
AG_MenuAction(mi, _("Commit"), agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs, OBJEDIT_RCS_COMMIT);
AG_MenuAction(mi, _("Update"), agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs, OBJEDIT_RCS_UPDATE);
AG_MenuAction(mi, _("Import"), agIconSave.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs, OBJEDIT_RCS_IMPORT);
AG_MenuSeparator(mi);
AG_MenuAction(mi, _("Commit all"), agIconSave.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_COMMIT_ALL);
AG_MenuAction(mi, _("Update all"), agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_UPDATE_ALL);
AG_MenuAction(mi, _("Import all"), agIconSave.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_IMPORT_ALL);
}
#endif /* AG_NETWORK */
#ifdef AG_DEBUG
mi = AG_MenuAddItem(me, _("Debug"));
DEV_ToolMenu(mi);
#endif /* AG_DEBUG */
nb = AG_NotebookNew(win, AG_NOTEBOOK_HFILL|AG_NOTEBOOK_VFILL);
ntab = AG_NotebookAddTab(nb, _("Working copy"), AG_BOX_VERT);
{
AG_MenuItem *mi;
#ifdef AG_NETWORK
AG_MenuItem *mi2;
#endif
AG_ObjectAttach(ntab, tlObjs);
mi = AG_TlistSetPopup(tlObjs, "object");
{
AG_MenuAction(mi, _("Edit object..."), NULL,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_EDIT_DATA);
AG_MenuAction(mi, _("Edit object information..."), NULL,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_EDIT_GENERIC);
AG_MenuSeparator(mi);
AG_MenuAction(mi, _("Load"), agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_LOAD);
AG_MenuAction(mi, _("Save"), agIconSave.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_SAVE);
AG_MenuAction(mi, _("Save all"), agIconSave.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_SAVE_ALL);
AG_MenuAction(mi, _("Save to..."), agIconSave.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_EXPORT);
#ifdef AG_NETWORK
if (agRcsMode) {
AG_MenuSeparator(mi);
mi2 = AG_MenuAction(mi, _("Repository"),
agIconLoad.s, NULL, NULL);
AG_MenuAction(mi2, _("Commit"), agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_COMMIT);
AG_MenuAction(mi2, _("Update"), agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_UPDATE);
AG_MenuAction(mi2, _("Import"), agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_IMPORT);
AG_MenuSeparator(mi2);
AG_MenuAction(mi2, _("Commit all"),
agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_COMMIT_ALL);
AG_MenuAction(mi2, _("Update all"),
agIconLoad.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_UPDATE_ALL);
AG_MenuAction(mi2, _("Import all"),
agIconSave.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_RCS_IMPORT_ALL);
}
#endif /* AG_NETWORK */
AG_MenuSeparator(mi);
AG_MenuAction(mi, _("Duplicate"), NULL,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs, OBJEDIT_DUP);
AG_MenuActionKb(mi, _("Move up"), agIconUp.s,
SDLK_u, KMOD_SHIFT,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_MOVE_UP);
AG_MenuActionKb(mi, _("Move down"), agIconDown.s,
SDLK_d, KMOD_SHIFT,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_MOVE_DOWN);
AG_MenuSeparator(mi);
AG_MenuAction(mi, _("Free dataset"), NULL,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_FREE_DATASET);
AG_MenuAction(mi, _("Destroy"), agIconTrash.s,
ObjectOp, "%p,%p,%i", vfsRoot, tlObjs,
OBJEDIT_DESTROY);
}
}
#ifdef AG_NETWORK
ntab = AG_NotebookAddTab(nb, _("Repository"), AG_BOX_VERT);
{
AG_Tlist *tl;
AG_Button *btn;
AG_MenuItem *mi;
tl = AG_TlistNew(ntab, AG_TLIST_MULTI|AG_TLIST_TREE|
AG_TLIST_EXPAND);
AG_TlistSetCompareFn(tl, AG_TlistCompareStrings);
mi = AG_TlistSetPopup(tl, "object");
{
AG_MenuAction(mi, _("Update from repository"),
agIconLoad.s, UpdateFromRepo, "%p,%p", vfsRoot, tl);
AG_MenuAction(mi, _("Delete from repository"),
agIconTrash.s, DeleteFromRepo, "%p", tl);
AG_MenuAction(mi, _("Rename"), NULL,
RepoRenameDlg, "%p", tl);
}
btn = AG_ButtonNewFn(ntab, AG_BUTTON_HFILL,
_("Refresh revisions"),
PollRevisions, "%p", tl);
if (agRcsMode)
AG_PostEvent(NULL, btn, "button-pushed", NULL);
}
#endif /* AG_NETWORK */
AG_WindowShow(win);
return (win);
}
/*
* Post AG_ObjectLoad() callback for all objects. If the REOPEN_ONLOAD
* flag is set, we automatically close and re-open all edition windows
* associated with the object after the load.
*/
static void
DEV_PostLoadDataCallback(AG_Event *event)
{
AG_Object *obj = AG_SENDER();
struct objent *oent;
if ((obj->flags & AG_OBJECT_REOPEN_ONLOAD) == 0)
return;
TAILQ_FOREACH(oent, &dobjs, objs) {
if (oent->obj == obj) {
AG_WindowHide(oent->win);
AG_PostEvent(NULL, oent->obj, "edit-close", NULL);
AG_ViewDetach(oent->win);
AG_PostEvent(NULL, oent->obj, "edit-open", NULL);
oent->win = obj->cls->edit(obj);
AG_WindowShow(oent->win);
AG_SetEvent(oent->win, "window-close",
SaveChangesDlg, "%p", oent);
}
}
}
/*
* Callback invoked whenever an object is paged out. We save the object
* unless the application is terminating.
*/
static void
DEV_PageOutCallback(AG_Event *event)
{
AG_Object *obj = AG_SENDER();
if (!agTerminating)
if (AG_ObjectSave(obj) == -1)
AG_TextMsgFromError();
}
static void
ConfirmQuit(AG_Event *event)
{
SDL_Event nev;
agTerminating = 0;
nev.type = SDL_USEREVENT;
SDL_PushEvent(&nev);
}
static void
AbortQuit(AG_Event *event)
{
AG_Window *win = AG_PTR(1);
agTerminating = 0;
AG_ViewDetach(win);
}
/*
* Callback invoked when the user has requested termination of the
* application. We check whether objects have been modified and not
* saved and prompt the user if that's the case.
*/
static void
DEV_QuitCallback(AG_Event *event)
{
AG_Object *vfsRoot = AG_PTR(1);
AG_Window *win;
AG_Box *bo;
agTerminating = 1;
if (!AG_ObjectChangedAll(vfsRoot)) {
SDL_Event nev;
nev.type = SDL_USEREVENT;
SDL_PushEvent(&nev);
return;
}
if ((win = AG_WindowNewNamed(AG_WINDOW_MODAL|AG_WINDOW_NOTITLE|
AG_WINDOW_NORESIZE, "DEV_QuitCallback")) == NULL) {
return;
}
AG_WindowSetCaption(win, _("Exit application?"));
AG_WindowSetPosition(win, AG_WINDOW_CENTER, 0);
AG_WindowSetSpacing(win, 8);
AG_LabelNewString(win, 0, _("Unsaved objects have been modified. "
"Exit application?"));
bo = AG_BoxNew(win, AG_BOX_HORIZ, AG_BOX_HOMOGENOUS|AG_VBOX_HFILL);
AG_BoxSetSpacing(bo, 0);
AG_BoxSetPadding(bo, 0);
AG_ButtonNewFn(bo, 0, _("Quit"), ConfirmQuit, NULL);
AG_WidgetFocus(AG_ButtonNewFn(bo, 0, _("Cancel"), AbortQuit,
"%p", win));
AG_WindowShow(win);
}
void
DEV_BrowserInit(void *vfsRoot)
{
TAILQ_INIT(&dobjs);
TAILQ_INIT(&gobjs);
AG_AddEvent(vfsRoot, "object-post-load-data", DEV_PostLoadDataCallback,
NULL);
AG_AddEvent(vfsRoot, "object-page-out", DEV_PageOutCallback, NULL);
AG_AddEvent(vfsRoot, "quit", DEV_QuitCallback, "%p", vfsRoot);
}
void
DEV_BrowserDestroy(void)
{
struct objent *oent, *noent;
for (oent = TAILQ_FIRST(&dobjs);
oent != TAILQ_END(&dobjs);
oent = noent) {
noent = TAILQ_NEXT(oent, objs);
Free(oent);
}
for (oent = TAILQ_FIRST(&gobjs);
oent != TAILQ_END(&gobjs);
oent = noent) {
noent = TAILQ_NEXT(oent, objs);
Free(oent);
}
}
void
DEV_BrowserCloseData(void *p)
{
AG_Object *obj = p;
struct objent *oent;
TAILQ_FOREACH(oent, &dobjs, objs) {
if (oent->obj != obj) {
continue;
}
AG_PostEvent(NULL, oent->win, "window-close", NULL);
}
}