src/mui/Attachment.c
/***************************************************************************
YAM - Yet Another Mailer
Copyright (C) 1995-2000 Marcel Beck
Copyright (C) 2000-2022 YAM Open Source Team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
YAM Official Support Site : http://www.yam.ch
YAM OpenSource project : http://sourceforge.net/projects/yamos/
$Id$
Superclass: MUIC_Group
Description: Custom class to manage a single attachment of a mail
Credits: This class was highly inspired by the similar attachment group &
image functionality available in Thunderbird and SimpleMail. Large
code portions where borrowed by the iconclass implementation of
SimpleMail to allow loading of the default icons via icon.library
and supporting Drag&Drop on the workbench. Thanks sba! :)
***************************************************************************/
#include "Attachment_cl.h"
#include <string.h>
#include <proto/dos.h>
#include <proto/icon.h>
#include <proto/muimaster.h>
#include <proto/wb.h>
#include <libraries/gadtools.h>
#include "YAM_mainFolder.h"
#include "YAM_write.h"
#include "mui/AttachmentGroup.h"
#include "mui/AttachmentImage.h"
#include "Busy.h"
#include "Config.h"
#include "Locale.h"
#include "MUIObjects.h"
#include "Debug.h"
/* CLASSDATA
struct Data
{
Object *contextMenu;
Object *imageObject;
Object *textObject;
Object *attGroupObject;
struct Part *mailPart;
char menuTitle[SIZE_DEFAULT];
char descriptionBuffer[SIZE_PATH + SIZE_DEFAULT * 2];
};
*/
#define TEXTROWS 3 // how many text rows does a attachmentimage normally have?
/// Menu enumerations
enum
{
AMEN_DISPLAY=100,
AMEN_SAVEAS,
AMEN_DELETE,
AMEN_PRINT,
AMEN_SAVEALL,
AMEN_SAVESEL,
AMEN_DELETEALL,
AMEN_DELETESEL
};
///
/* Overloaded Methods */
/// OVERLOAD(OM_NEW)
OVERLOAD(OM_NEW)
{
Object *imageObject;
Object *textObject;
Object *attGroupObject = NULL;
struct Part *mailPart = NULL;
struct TagItem *tags = inittags(msg);
struct TagItem *tag;
ENTER();
// check for some tags present at OM_NEW
while((tag = NextTagItem((APTR)&tags)) != NULL)
{
switch(tag->ti_Tag)
{
case ATTR(MailPart): mailPart = (struct Part *)tag->ti_Data; break;
case ATTR(Group): attGroupObject = (Object *)tag->ti_Data; break;
}
}
// create the object
if((obj = DoSuperNew(cl, obj,
MUIA_ContextMenu, TRUE,
MUIA_Group_Horiz, TRUE,
MUIA_Group_Spacing, 2,
Child, imageObject = AttachmentImageObject,
MUIA_CycleChain, TRUE,
MUIA_AttachmentImage_MailPart, mailPart,
MUIA_AttachmentImage_Group, attGroupObject,
End,
Child, textObject = TextObject,
MUIA_Font, MUIV_Font_Tiny,
MUIA_Text_SetMax, FALSE,
MUIA_Text_Copy, FALSE,
End,
TAG_MORE, inittags(msg))) != NULL)
{
GETDATA;
data->imageObject = imageObject;
data->textObject = textObject;
data->mailPart = mailPart;
data->attGroupObject = attGroupObject;
// connect some notifies which we might be interested in
DoMethod(imageObject, MUIM_Notify, MUIA_AttachmentImage_DoubleClick, TRUE, obj, 1, METHOD(Display));
DoMethod(imageObject, MUIM_Notify, MUIA_AttachmentImage_DropPath, MUIV_EveryTime, attGroupObject, 2, MUIM_AttachmentGroup_AttachmentDropped, MUIV_TriggerValue);
}
RETURN((IPTR)obj);
return (IPTR)obj;
}
///
/// OVERLOAD(OM_DISPOSE)
OVERLOAD(OM_DISPOSE)
{
GETDATA;
// make sure that our context menus are also disposed
if(data->contextMenu != NULL)
MUI_DisposeObject(data->contextMenu);
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(OM_GET)
OVERLOAD(OM_GET)
{
GETDATA;
IPTR *store = ((struct opGet *)msg)->opg_Storage;
switch(((struct opGet *)msg)->opg_AttrID)
{
case ATTR(ImageObject): *store = (ULONG)data->imageObject; return TRUE;
case ATTR(MailPart): *store = (ULONG)data->mailPart; return TRUE;
case MUIA_Selected: *store = xget(data->imageObject, MUIA_Selected); return TRUE;
}
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(OM_SET)
OVERLOAD(OM_SET)
{
GETDATA;
struct TagItem *tags = inittags(msg), *tag;
while((tag = NextTagItem((APTR)&tags)) != NULL)
{
switch(tag->ti_Tag)
{
case MUIA_Selected:
{
// forward the information to the image object, but ignore it for ourself
set(data->imageObject, MUIA_Selected, tag->ti_Data);
tag->ti_Tag = TAG_IGNORE;
}
break;
}
}
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(MUIM_Setup)
OVERLOAD(MUIM_Setup)
{
GETDATA;
ULONG result;
ENTER();
if((result = DoSuperMethodA(cl, obj, msg)) != 0)
{
xset(data->imageObject, MUIA_AttachmentImage_MaxHeight, TEXTROWS*_font(obj)->tf_YSize+4,
MUIA_AttachmentImage_MaxWidth, TEXTROWS*_font(obj)->tf_YSize+4);
if(data->mailPart != NULL)
DoMethod(obj, METHOD(UpdateDescription));
}
RETURN(result);
return result;
}
///
/// OVERLOAD(MUIM_ContextMenuBuild)
OVERLOAD(MUIM_ContextMenuBuild)
{
GETDATA;
ENTER();
// dispose the old context_menu if it still exists
if(data->contextMenu != NULL)
{
MUI_DisposeObject(data->contextMenu);
data->contextMenu = NULL;
}
if(data->mailPart != NULL)
{
BOOL isDeleted;
snprintf(data->menuTitle, sizeof(data->menuTitle), tr(MSG_MA_MIMEPART_MENU), data->mailPart->Nr);
isDeleted = (data->mailPart->ContentType != NULL && stricmp(data->mailPart->ContentType, "text/deleted") == 0);
data->contextMenu = MenustripObject,
Child, MenuObjectT(data->menuTitle),
Child, MenuitemObject, MUIA_Menuitem_Title, tr(MSG_MA_ATTACHMENT_DISPLAY), MUIA_Menuitem_CopyStrings, FALSE, MUIA_UserData, AMEN_DISPLAY, End,
Child, MenuitemObject, MUIA_Menuitem_Title, tr(MSG_MA_ATTACHMENT_SAVEAS), MUIA_Menuitem_CopyStrings, FALSE, MUIA_UserData, AMEN_SAVEAS, End,
Child, MenuitemObject, MUIA_Menuitem_Title, tr(MSG_MA_ATTACHMENT_DELETE), MUIA_Menuitem_CopyStrings, FALSE, MUIA_UserData, AMEN_DELETE, MUIA_Menuitem_Enabled, isDeleted == FALSE && !isAlternativePart(data->mailPart), End,
Child, MenuitemObject, MUIA_Menuitem_Title, tr(MSG_MA_ATTACHMENT_PRINT), MUIA_Menuitem_CopyStrings, FALSE, MUIA_UserData, AMEN_PRINT, MUIA_Menuitem_Enabled, isPrintable(data->mailPart), End,
Child, MenuBarLabel,
Child, MenuitemObject, MUIA_Menuitem_Title, tr(MSG_MA_ATTACHMENT_SAVEALL), MUIA_Menuitem_CopyStrings, FALSE, MUIA_UserData, AMEN_SAVEALL, End,
Child, MenuitemObject, MUIA_Menuitem_Title, tr(MSG_MA_ATTACHMENT_SAVESEL), MUIA_Menuitem_CopyStrings, FALSE, MUIA_UserData, AMEN_SAVESEL, End,
Child, MenuitemObject, MUIA_Menuitem_Title, tr(MSG_MA_ATTACHMENT_DELETEALL), MUIA_Menuitem_CopyStrings, FALSE, MUIA_UserData, AMEN_DELETEALL, End,
Child, MenuitemObject, MUIA_Menuitem_Title, tr(MSG_MA_ATTACHMENT_DELETESEL), MUIA_Menuitem_CopyStrings, FALSE, MUIA_UserData, AMEN_DELETESEL, End,
End,
End;
}
RETURN(data->contextMenu);
return (IPTR)data->contextMenu;
}
///
/// OVERLOAD(MUIM_ContextMenuChoice)
OVERLOAD(MUIM_ContextMenuChoice)
{
GETDATA;
struct MUIP_ContextMenuChoice *m = (struct MUIP_ContextMenuChoice *)msg;
ENTER();
switch(xget(m->item, MUIA_UserData))
{
case AMEN_DISPLAY:
DoMethod(obj, METHOD(Display));
break;
case AMEN_SAVEAS:
DoMethod(obj, METHOD(Save));
break;
case AMEN_DELETE:
DoMethod(obj, METHOD(Delete));
break;
case AMEN_PRINT:
DoMethod(obj, METHOD(Print));
break;
case AMEN_SAVEALL:
DoMethod(data->attGroupObject, MUIM_AttachmentGroup_SaveAll);
break;
case AMEN_SAVESEL:
DoMethod(data->attGroupObject, MUIM_AttachmentGroup_SaveSelected);
break;
case AMEN_DELETEALL:
DoMethod(data->attGroupObject, MUIM_AttachmentGroup_DeleteAll);
break;
case AMEN_DELETESEL:
DoMethod(data->attGroupObject, MUIM_AttachmentGroup_DeleteSelected);
break;
default:
return DoSuperMethodA(cl, obj, msg);
}
RETURN(0);
return 0;
}
///
/* Private Functions */
/* Public Methods */
/// DECLARE(Display)
DECLARE(Display)
{
GETDATA;
ENTER();
if(data->mailPart != NULL)
{
BOOL oldDecoded = isDecoded(data->mailPart);
struct BusyNode *busy;
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecDisplaying), "");
// try to decode the message part
if(RE_DecodePart(data->mailPart) == TRUE)
{
char *fileName;
// get the suggested filename for the mail part
fileName = SuggestPartFileName(data->mailPart);
// run our MIME routines for displaying the part to the user
RE_DisplayMIME(data->mailPart->Filename, fileName,
data->mailPart->ContentType, isPrintable(data->mailPart));
// free the generated name again
free(fileName);
// if the part was decoded in RE_DecodePart() then
// we issue a full refresh of the attachment image
if(oldDecoded == FALSE && isDecoded(data->mailPart) == TRUE)
{
// issue a full redraw of the group which in fact
// will issue a refresh of all images as well in
// case they have changed.
MUI_Redraw(data->imageObject, MADF_DRAWOBJECT);
DoMethod(obj, METHOD(UpdateDescription));
}
}
BusyEnd(busy);
}
RETURN(0);
return 0;
}
///
/// DECLARE(Save)
DECLARE(Save)
{
GETDATA;
struct Part *rp = data->mailPart;
ENTER();
if(rp != NULL)
{
BOOL oldDecoded = isDecoded(rp);
struct BusyNode *busy;
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecSaving), "");
// export the mail part only if the decoding succeeded
if(RE_DecodePart(rp) == TRUE)
{
char *fileName;
fileName = SuggestPartFileName(rp);
RE_Export(rp->rmData,
rp->Filename,
"",
fileName,
data->mailPart->Nr,
FALSE,
FALSE,
rp->ContentType);
free(fileName);
if(oldDecoded == FALSE)
{
// now we know the exact size of the file and can redraw ourself
MUI_Redraw(data->imageObject, MADF_DRAWOBJECT);
DoMethod(obj, METHOD(UpdateDescription));
}
}
BusyEnd(busy);
}
RETURN(0);
return 0;
}
///
/// DECLARE(Delete)
DECLARE(Delete)
{
GETDATA;
ENTER();
if(data->mailPart != NULL)
{
struct Part *parts[2];
// create a NULL terminated list with just this single part
parts[0] = data->mailPart;
parts[1] = NULL;
MA_RemoveAttach(data->mailPart->rmData->mail, parts, C->ConfirmRemoveAttachments);
}
RETURN(0);
return 0;
}
///
/// DECLARE(Print)
DECLARE(Print)
{
GETDATA;
ENTER();
if(data->mailPart != NULL)
{
struct BusyNode *busy;
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecPrinting), "");
RE_PrintFile(data->mailPart->Filename, _win(obj));
BusyEnd(busy);
}
RETURN(0);
return 0;
}
///
/// DECLARE(ImageDropped)
DECLARE(ImageDropped) // const char *dropPath
{
GETDATA;
BOOL result = FALSE;
BOOL oldDecoded;
struct BusyNode *busy;
char *fileName = NULL;
char filePathBuf[SIZE_PATHFILE];
ENTER();
D(DBF_GUI, "image of Part %ld was dropped at [%s]", data->mailPart->Nr, msg->dropPath);
oldDecoded = isDecoded(data->mailPart);
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecSaving), "");
// export the mail part only if the decoding succeeded
if(RE_DecodePart(data->mailPart) == TRUE)
{
// make sure the drawer is opened upon the drag operation
if(LIB_VERSION_IS_AT_LEAST(WorkbenchBase, 44, 0) == TRUE)
OpenWorkbenchObjectA((char *)msg->dropPath, NULL);
// get the suggested filename of the message part
fileName = SuggestPartFileName(data->mailPart);
// then add the file name to the drop path
AddPath(filePathBuf, msg->dropPath, fileName, sizeof(filePathBuf));
result = RE_Export(data->mailPart->rmData,
data->mailPart->Filename,
filePathBuf,
"",
data->mailPart->Nr,
FALSE,
FALSE,
data->mailPart->ContentType);
if(oldDecoded == FALSE)
{
// now we know the exact size of the file and can redraw ourself
MUI_Redraw(data->imageObject, MADF_DRAWOBJECT);
DoMethod(obj, METHOD(UpdateDescription));
}
}
// let the workbench know about the change
if(result == TRUE)
{
struct DiskObject *diskObject;
// make sure to write out the diskObject of our attachment as well
// but only if the filename doesn't end with a ".info" itself or it
// clearly suggests that this might be a diskobject itself.
if((diskObject = (struct DiskObject *)xget(data->imageObject, MUIA_AttachmentImage_DiskObject)) != NULL)
{
char ext[SIZE_FILE];
// extract the file extension.
stcgfe(ext, fileName);
if(stricmp(ext, "info") != 0)
PutDiskObject(filePathBuf, diskObject);
#if defined(__amigaos4__)
else
{
// the following code makes sure that the workbench
// gets notified of the .info file
BPTR dlock;
if((dlock = Lock(msg->dropPath, SHARED_LOCK)) != 0)
{
char *p;
// strip an eventually existing extension
if((p = strrchr(fileName, '.')) != NULL)
*p = '\0';
// UpdateWorkbench() seems to be only supported
// by OS4. That's why this stuff is OS4 only.
UpdateWorkbench(fileName, dlock, UPDATEWB_ObjectAdded);
UnLock(dlock);
}
}
#endif
}
// Now that the workbench knows about the new object we also have to make sure the icon
// is actually visible in the window
if(LIB_VERSION_IS_AT_LEAST(WorkbenchBase, 44, 0) == TRUE)
MakeWorkbenchObjectVisibleA(filePathBuf, NULL);
free(fileName);
}
else
DisplayBeep(_screen(obj));
BusyEnd(busy);
RETURN(0);
return 0;
}
///
/// DECLARE(UpdateDescription)
DECLARE(UpdateDescription)
{
GETDATA;
ENTER();
if(data->mailPart != NULL)
{
char sizeBuffer[SIZE_DEFAULT];
char *p;
// first line: the attachment name (filename or description)
// italic style in case it is an alternative part
p = data->mailPart->Name[0] != '\0' ? data->mailPart->Name : data->mailPart->Description;
if(isAlternativePart(data->mailPart))
snprintf(data->descriptionBuffer, sizeof(data->descriptionBuffer), MUIX_I "%s" MUIX_N "\n", p);
else
snprintf(data->descriptionBuffer, sizeof(data->descriptionBuffer), "%s\n", p);
// second line: the attachment size
if(isDecoded(data->mailPart))
{
FormatSize(data->mailPart->Size, sizeBuffer, sizeof(sizeBuffer), SF_AUTO);
}
else
{
sizeBuffer[0] = '~';
FormatSize(data->mailPart->Size, &sizeBuffer[1], sizeof(sizeBuffer)-1, SF_AUTO);
}
strlcat(data->descriptionBuffer, sizeBuffer, sizeof(data->descriptionBuffer));
strlcat(data->descriptionBuffer, "\n", sizeof(data->descriptionBuffer));
// third line: the attachment description
strlcat(data->descriptionBuffer, DescribeCT(data->mailPart->ContentType), sizeof(data->descriptionBuffer));
set(data->textObject, MUIA_Text_Contents, data->descriptionBuffer);
}
RETURN(0);
return 0;
}
///