/*
* Copyright (c) 2013 Hypertriton, Inc.
*
* 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.
*/
/*
* Graphical user interface.
*/
#include
#include
#include "vislak.h"
#include "icons.h"
#include "icons_data.h"
#include
AG_Object vsVfsRoot; /* General-purpose VFS */
static int nEditorWindows = 0;
/*
* Display "Save changes?" dialog on exit.
*/
static void
CloseObject(AG_Event *event)
{
AG_Window *win = AG_PTR(1);
AG_Object *obj = AG_PTR(2);
int save = AG_INT(3);
if (save) {
if (AG_ObjectSave(obj) == -1) {
AG_TextMsgFromError(); /* TODO suggest "save as" */
return;
}
}
AG_ObjectDetach(win);
AG_ObjectDelete(obj);
if (--nEditorWindows == 0)
AG_Terminate(0);
}
static void
WindowClose(AG_Event *event)
{
AG_Window *win = AG_SELF();
AG_Object *obj = AG_PTR(1);
AG_Event ev;
AG_Button *bOpts[3];
AG_Window *wDlg;
if (!AG_ObjectChanged(obj)) {
AG_EventArgs(&ev, "%p,%p,%i", win, obj, 0);
CloseObject(&ev);
return;
}
wDlg = AG_TextPromptOptions(bOpts, 3, _("Save changes to %s?"), OBJECT(obj)->name);
AG_WindowAttach(win, wDlg);
AG_ButtonText(bOpts[0], _("Save"));
AG_SetEvent(bOpts[0], "button-pushed", CloseObject, "%p,%p,%i", win, obj, 1);
AG_WidgetFocus(bOpts[0]);
AG_ButtonText(bOpts[1], _("Discard"));
AG_SetEvent(bOpts[1], "button-pushed", CloseObject, "%p,%p,%i", win, obj, 0);
AG_ButtonText(bOpts[2], _("Cancel"));
AG_SetEvent(bOpts[2], "button-pushed", AGWINDETACH(wDlg));
}
/* Open a Vislak object for edition. */
AG_Window *
VS_GUI_OpenObject(void *p)
{
AG_Object *obj = p;
AG_Window *win = NULL;
AG_Widget *wEdit;
/* Invoke edit(), which may return a Window or some other Widget. */
Verbose("Opening %s (%s)\n", obj->name, obj->cls->name);
if ((wEdit = obj->cls->edit(obj)) == NULL) {
AG_SetError("%s no edit()", obj->cls->name);
return (NULL);
}
if (AG_OfClass(wEdit, "AG_Widget:AG_Window:*")) {
win = (AG_Window *)wEdit;
} else if (AG_OfClass(wEdit, "AG_Widget:*")) {
win = AG_WindowNew(AG_WINDOW_MAIN);
AG_ObjectAttach(win, wEdit);
} else {
AG_SetError("%s: edit() illegal object", obj->cls->name);
return (NULL);
}
AG_WindowSetCaptionS(win,
AG_Defined(obj,"archive-path") ?
AG_ShortFilename(AG_GetStringP(obj,"archive-path")) : obj->name);
AG_SetEvent(win, "window-close", WindowClose, "%p", obj);
AG_SetPointer(win, "object", obj);
AG_PostEvent(obj, "edit-open", NULL);
AG_WindowShow(win);
return (win);
}
/* Create a new instance of a native Vislak object class. */
void
VS_GUI_NewObject(AG_Event *event)
{
AG_ObjectClass *cl = AG_PTR(1);
AG_Object *obj;
obj = AG_ObjectNew(&vsVfsRoot, NULL, cl);
if (obj == NULL ||
VS_GUI_OpenObject(obj) == NULL) {
goto fail;
}
return;
fail:
AG_TextError(_("Failed to create object: %s"), AG_GetError());
if (obj != NULL) { AG_ObjectDelete(obj); }
}
/* Load a native Vislak object file. */
void
VS_GUI_LoadObject(AG_Event *event)
{
AG_ObjectClass *cl = AG_PTR(1);
char *path = AG_STRING(2);
AG_Object *obj;
Verbose("Loading %s object from %s\n", cl->name, path);
if ((obj = AG_ObjectNew(&vsVfsRoot, NULL, cl)) == NULL) {
goto fail;
}
if (AG_ObjectLoadFromFile(obj, path) == -1) {
AG_SetError("%s: %s", AG_ShortFilename(path), AG_GetError());
goto fail;
}
AG_ObjectSetArchivePath(obj, path);
AG_ObjectSetNameS(obj, AG_ShortFilename(path));
if (VS_GUI_OpenObject(obj) == NULL) {
goto fail;
}
return;
fail:
AG_TextError(_("Could not open object: %s"), AG_GetError());
/* if (obj != NULL) { AG_ObjectDelete(obj); } */
}
/* "Open..." dialog */
void
VS_GUI_OpenDlg(AG_Event *event)
{
AG_Window *win;
AG_FileDlg *fd;
int j;
win = AG_WindowNew(0);
AG_WindowSetCaptionS(win, _("Open..."));
fd = AG_FileDlgNewMRU(win, "vislak.mru.files",
AG_FILEDLG_LOAD|AG_FILEDLG_CLOSEWIN|AG_FILEDLG_EXPAND);
AG_FileDlgSetOptionContainer(fd, AG_BoxNewVert(win, AG_BOX_HFILL));
for (j = 0; j < agFileExtCount; j++) {
const AG_FileExtMapping *me = &agFileExtMap[j];
char lbl[64], ext[16];
snprintf(lbl, sizeof(lbl), _("Vislak %s file"), me->descr);
Strlcpy(ext, "*", sizeof(ext));
Strlcat(ext, me->ext, sizeof(ext));
AG_FileDlgAddType(fd, lbl, ext,
VS_GUI_LoadObject, "%p", me->cls);
}
AG_WindowShow(win);
}
/* Save an object file in native Vislak format. */
static void
SaveNativeObject(AG_Event *event)
{
AG_Object *obj = AG_PTR(1);
char *path = AG_STRING(2);
AG_Window *wEdit;
if (AG_ObjectSaveToFile(obj, path) == -1) {
AG_TextError("%s: %s", AG_ShortFilename(path), AG_GetError());
}
AG_ObjectSetArchivePath(obj, path);
AG_ObjectSetNameS(obj, AG_ShortFilename(path));
if ((wEdit = AG_WindowFindFocused()) != NULL)
AG_WindowSetCaptionS(wEdit, AG_ShortFilename(path));
}
/* "Save as..." dialog. */
void
VS_GUI_SaveAsDlg(AG_Event *event)
{
char defDir[AG_PATHNAME_MAX];
AG_Object *obj = AG_PTR(1);
AG_Window *win;
AG_FileDlg *fd;
int j;
if (obj == NULL) {
AG_TextError(_("No document is selected for saving."));
return;
}
AG_GetString(agConfig, "save-path", defDir, sizeof(defDir));
win = AG_WindowNew(0);
AG_WindowSetCaption(win, _("Save %s as..."), obj->name);
fd = AG_FileDlgNew(win, AG_FILEDLG_SAVE|AG_FILEDLG_CLOSEWIN|
AG_FILEDLG_EXPAND);
AG_FileDlgSetOptionContainer(fd, AG_BoxNewVert(win, AG_BOX_HFILL));
AG_FileDlgSetDirectoryMRU(fd, "vislak.mru.files", defDir);
for (j = 0; j < agFileExtCount; j++) {
const AG_FileExtMapping *me = &agFileExtMap[j];
AG_ObjectClass *cls = (AG_ObjectClass *)me->cls;
char lbl[64], ext[16];
if (AG_OfClass(obj, cls->hier)) {
snprintf(lbl, sizeof(lbl), _("Vislak %s file"),
me->descr);
Strlcpy(ext, "*", sizeof(ext));
Strlcat(ext, me->ext, sizeof(ext));
AG_FileDlgAddType(fd, lbl, ext,
SaveNativeObject, "%p", obj);
}
}
AG_WindowShow(win);
}
/* "Save" action */
void
VS_GUI_Save(AG_Event *event)
{
AG_Object *obj = AG_PTR(1);
if (obj == NULL) {
AG_TextError(_("No document is selected for saving."));
return;
}
if (!AG_Defined(obj, "archive-path")) {
VS_GUI_SaveAsDlg(event);
return;
}
if (AG_ObjectSave(obj) == -1) {
AG_TextError(_("Error saving object: %s"), AG_GetError());
} else {
AG_TextTmsg(AG_MSG_INFO, 1250, _("Saved %s successfully"),
AG_GetStringP(obj, "archive-path"));
}
}
static void
SelectedFont(AG_Event *event)
{
AG_Window *win = AG_PTR(1);
AG_SetString(agConfig, "font.face", OBJECT(agDefaultFont)->name);
AG_SetInt(agConfig, "font.size", agDefaultFont->spec.size);
AG_SetUint(agConfig, "font.flags", agDefaultFont->flags);
(void)AG_ConfigSave();
AG_TextWarning("default-font-changed",
_("The default font has been changed.\n"
"Please restart application for this change to take effect."));
AG_ObjectDetach(win);
}
/* "Select font" dialog */
void
VS_GUI_SelectFontDlg(AG_Event *event)
{
AG_Window *win;
AG_FontSelector *fs;
AG_Box *hBox;
win = AG_WindowNew(0);
AG_WindowSetCaptionS(win, _("Font selection"));
fs = AG_FontSelectorNew(win, AG_FONTSELECTOR_EXPAND);
AG_BindPointer(fs, "font", (void *)&agDefaultFont);
hBox = AG_BoxNewHoriz(win, AG_BOX_HFILL|AG_BOX_HOMOGENOUS);
AG_ButtonNewFn(hBox, 0, _("OK"), SelectedFont, "%p", win);
AG_ButtonNewFn(hBox, 0, _("Cancel"), AG_WindowCloseGenEv, "%p", win);
AG_WindowShow(win);
}
/* Build a generic "File" menu. */
void
VS_FileMenu(AG_MenuItem *m, void *obj)
{
const AG_FileExtMapping *me = NULL;
int j;
for (j = 0; j < agFileExtCount; j++) {
me = &agFileExtMap[j];
if (!me->editDirect) {
continue;
}
AG_MenuAction(m,
AG_Printf("New %s...", me->descr),
agIconDoc.s,
VS_GUI_NewObject, "%p", me->cls);
}
AG_MenuSeparator(m);
AG_MenuActionKb(m, _("Open..."), agIconLoad.s,
AG_KEY_O, AG_KEYMOD_CTRL,
VS_GUI_OpenDlg, NULL);
AG_MenuActionKb(m, _("Save"), agIconSave.s,
AG_KEY_S, AG_KEYMOD_CTRL,
VS_GUI_Save, "%p", obj);
AG_MenuAction(m, _("Save as..."), agIconSave.s,
VS_GUI_SaveAsDlg, "%p", obj);
}
/* Build a generic "Edit" menu. */
void
VS_EditMenu(AG_MenuItem *m, void *obj)
{
#if 0
AG_MenuActionKb(m, _("Undo"), agIconUp.s, AG_KEY_Z, AG_KEYMOD_CTRL,
VS_GUI_Undo, "%p", obj);
AG_MenuActionKb(m, _("Redo"), agIconDown.s, AG_KEY_R, AG_KEYMOD_CTRL,
VS_GUI_Redo, "%p", obj);
#endif
AG_MenuSeparator(m);
AG_MenuAction(m, _("Select font..."), agIconMagnifier.s,
VS_GUI_SelectFontDlg, NULL);
}
/* Initialize Vislak GUI globals. */
void
VS_InitGUI(void)
{
if (agGUI) {
AG_RegisterClass(&vsViewClass);
AG_RegisterClass(&vsPlayerClass);
vsIcon_Init();
}
AG_ObjectInitStatic(&vsVfsRoot, NULL);
AG_ObjectSetName(&vsVfsRoot, "Vislak VFS");
}
/* Release Vislak GUI globals. */
void
VS_DestroyGUI(void)
{
if (agGUI) {
// AG_UnregisterClass(&vsPlayerClass);
// AG_UnregisterClass(&vsViewClass);
}
// AG_ObjectDestroy(&vsVfsRoot);
}