src/mui/AttachmentImage.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_Area
Description: Custom class to display an image for a specific attachment
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 "AttachmentImage_cl.h"
#include "SDI_hook.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <workbench/icon.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/layers.h>
#include <proto/icon.h>
#include <proto/wb.h>
#if defined(__amigaos4__)
#include <graphics/blitattr.h>
#ifndef MINTERM_ABC
#define MINTERM_ABC 0x80
#endif
#ifndef MINTERM_ABNC
#define MINTERM_ABNC 0x40
#endif
#ifndef MINTERM_ANBC
#define MINTERM_ANBC 0x20
#endif
#ifndef MINTERM_SRCMASK
#define MINTERM_SRCMASK (MINTERM_ABC | MINTERM_ABNC | MINTERM_ANBC)
#endif
#endif
#include "YAM.h"
#include "YAM_write.h"
#include "Locale.h"
#include "MUIObjects.h"
#include "mui/AttachmentGroup.h"
#include "Debug.h"
/* CLASSDATA
struct Data
{
struct DiskObject *diskObject;
struct DiskObject *drawDiskObject;
struct Part *mailPart;
struct BitMap *normalBitMap;
struct BitMap *selectedBitMap;
struct BitMap *normalBitMask;
struct BitMap *selectedBitMask;
char *dropPath;
struct MUI_EventHandlerNode ehnode;
Object *attachmentGroup;
BOOL lastDecodedStatus;
BOOL eventHandlerAdded;
ULONG selectSecs;
ULONG selectMicros;
ULONG scaledWidth;
ULONG scaledHeight;
ULONG maxWidth;
ULONG maxHeight;
};
*/
/* Private Functions */
/// SelectionMsg
struct SelectionMsg
{
struct Layer *layer;
LONG mx;
LONG my;
char *drawer;
char *destName;
BOOL finish;
};
///
/// SelectionHook
#if !defined(__amigaos4__)
HOOKPROTONO(SelectionFunc, ULONG, struct IconSelectMsg *ism)
{
struct SelectionMsg *msg = (struct SelectionMsg *)hook->h_Data;
struct Window *wnd = ism->ism_ParentWindow;
if(wnd == NULL)
return ISMACTION_Stop;
if(wnd->WLayer != msg->layer)
return ISMACTION_Stop;
msg->finish = TRUE;
if((ism->ism_Left + wnd->LeftEdge <= msg->mx) && (msg->mx <= wnd->LeftEdge + ism->ism_Left + ism->ism_Width - 1) &&
(ism->ism_Top + wnd->TopEdge <= msg->my) && (msg->my <= wnd->TopEdge + ism->ism_Top + ism->ism_Height - 1))
{
if(ism->ism_Type == WBDRAWER)
{
if((msg->destName = strdup(ism->ism_Name)) != NULL)
return ISMACTION_Select;
}
else if(ism->ism_Type == WBDISK)
{
if(asprintf(&msg->destName, "%s:", ism->ism_Name) != -1)
return ISMACTION_Select;
}
return ISMACTION_Stop;
}
return ISMACTION_Ignore;
}
MakeStaticHook(SelectionHook, SelectionFunc);
#endif
///
/// FindWriteWindow
// find an open write window which matches a given one
static BOOL FindWriteWindow(struct Window *win)
{
BOOL found = FALSE;
struct WriteMailData *wmData;
ENTER();
IterateList(&G->writeMailDataList, struct WriteMailData *, wmData)
{
if(wmData->window != NULL &&
xget(wmData->window, MUIA_Window_Open) == TRUE &&
(struct Window *)xget(wmData->window, MUIA_Window_Window) == win)
{
found = TRUE;
break;
}
}
RETURN(found);
return found;
}
///
/// UnloadImage
// unload and free all memory of a formerly loaded image
static void UnloadImage(struct Data *data)
{
if(data->normalBitMap != NULL)
{
FreeBitMap(data->normalBitMap);
data->normalBitMap = NULL;
}
if(data->normalBitMask != NULL)
{
FreeBitMap(data->normalBitMask);
data->normalBitMask = NULL;
}
if(data->selectedBitMap != NULL)
{
FreeBitMap(data->selectedBitMap);
data->selectedBitMap = NULL;
}
if(data->selectedBitMask != NULL)
{
FreeBitMap(data->selectedBitMask);
data->selectedBitMask = NULL;
}
if(data->drawDiskObject != NULL)
{
if(data->diskObject == data->drawDiskObject)
data->diskObject = NULL;
FreeDiskObject(data->drawDiskObject);
data->drawDiskObject = NULL;
}
if(data->diskObject != NULL)
{
FreeDiskObject(data->diskObject);
data->diskObject = NULL;
}
}
///
/// GetIconSize
// obtain the size of an icon
static void GetIconSize(struct DiskObject *dobj, struct TagItem *tags, LONG *width, LONG *height)
{
ENTER();
if(LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE)
{
struct Rectangle rect;
GetIconRectangleA(NULL, dobj, NULL, &rect, tags);
*width = rect.MaxX - rect.MinX + 1;
*height = rect.MaxY - rect.MinY + 1;
}
else
{
*width = ((struct Image *)dobj->do_Gadget.GadgetRender)->Width;
*height = ((struct Image *)dobj->do_Gadget.GadgetRender)->Height;
}
LEAVE();
}
///
/// LoadImage
// function that (re)loads the images for both selected and unselected
// state
static void LoadImage(struct IClass *cl, Object *obj)
{
#ifndef ICONGETA_SizeBounds
#define ICONGETA_SizeBounds TAG_IGNORE
#endif
GETDATA;
ENTER();
if(data->mailPart != NULL && data->maxWidth != 0 && data->maxHeight != 0)
{
struct Part *mailPart = data->mailPart;
char *iconFile = NULL;
struct DiskObject *diskObject = NULL;
struct Rectangle sizeBounds;
// we first make sure we have freed everything
UnloadImage(data);
if(isDecoded(mailPart) == TRUE && mailPart->Filename[0] != '\0')
{
iconFile = mailPart->Filename;
}
// Set up the minimum and maximum sizes for the icons to take the dirty work
// of scaling the icon images to the required size from us.
// This requires icon.library 53.7+ and will be ignored on previous versions.
sizeBounds.MinX = 8;
sizeBounds.MinY = 8;
sizeBounds.MaxX = data->maxWidth;
sizeBounds.MaxY = data->maxHeight;
D(DBF_ALWAYS, "max %ld %ld",data->maxWidth,data->maxHeight);
// only if we have at least icon.library >= v44 and we find deficons
// we try to identify the file with deficons
if(iconFile != NULL && LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE && G->DefIconsAvailable == TRUE)
{
D(DBF_GUI, "retrieving diskObject via DEFICONS for '%s'", iconFile);
diskObject = GetIconTags(iconFile,
ICONGETA_FailIfUnavailable, FALSE,
ICONGETA_Screen, _screen(obj),
ICONGETA_SizeBounds, &sizeBounds,
TAG_DONE);
#if defined(DEBUG)
if(diskObject == NULL)
W(DBF_GUI, "retrieving diskObject via DEFICONS for '%s' failed, error %ld", iconFile, IoErr());
#endif
}
// if we have still not obtained the diskObject we go
// and load a default icon for a specific ContentType
if(diskObject == NULL)
{
// use the supplied attachment file name no matter if the file
// itself is available or not
iconFile = mailPart->Name;
// with icon.library v44+ we can use GetIconTags again.
if(LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE && mailPart->ContentType != NULL)
{
const char *def = NULL;
// build the default name now
if(strnicmp(mailPart->ContentType, "image", 5) == 0)
{
def = "picture";
}
else if(strnicmp(mailPart->ContentType, "audio", 5) == 0)
{
def = "audio";
}
else if(strnicmp(mailPart->ContentType, "text", 4) == 0)
{
if(strlen(mailPart->ContentType) > 5)
{
if(strnicmp((mailPart->ContentType)+5, "html", 4) == 0)
def = "html";
else if(strnicmp((mailPart->ContentType)+5, "plain", 5) == 0)
def = "ascii";
else
def = "text";
}
else
def = "text";
}
else
{
// try the file name's extension if it exists
if(iconFile != NULL)
{
if((def = strrchr(iconFile, '.')) != NULL)
def++;
}
}
// fall back to "attach" if nothing else was appropriate
if(def == NULL)
def = "attach";
// try to retrieve the icon for that type with an automatic
// fallback to the default project icon
diskObject = GetIconTags(NULL,
ICONGETA_GetDefaultName, def,
ICONGETA_GetDefaultType, WBPROJECT,
ICONGETA_Screen, _screen(obj),
ICONGETA_SizeBounds, &sizeBounds,
TAG_DONE);
D(DBF_GUI, "diskobject for '%s' retrieved from default '%s' type", iconFile, def);
}
else
{
// on an old OS <= 3.1 we can only use a project icon for
// the attachment
diskObject = GetDefDiskObject(WBPROJECT);
D(DBF_GUI, "diskobject for '%s' retrieved from default WBPROJECT (OS3.1) type", iconFile);
}
}
// now that we should have the diskObject we get the image of it, blit it in
// a temporary rastport so that we scan scale it down
if(diskObject != NULL)
{
LONG orgWidth;
LONG orgHeight;
LONG scaleHeightDiff;
LONG scaleWidthDiff;
LONG newWidth;
LONG newHeight;
// prepare the drawIcon/GetIconRentagle tags
// defined starting from icon.lib v51+
#ifndef ICONDRAWA_Transparency
#define ICONDRAWA_Transparency TAG_IGNORE
#endif
struct TagItem drawIconTags[] = { { ICONDRAWA_Borderless, TRUE },
{ ICONDRAWA_Frameless, TRUE },
{ ICONDRAWA_Transparency, 255 },
{ ICONDRAWA_DrawInfo, (ULONG)_dri(obj) },
{ TAG_DONE, FALSE } };
// if this is an alternative part we draw it with
// transparency of 50%
if(isAlternativePart(mailPart))
drawIconTags[2].ti_Data = 128;
// get some information about our diskObject like width/height
GetIconSize(diskObject, drawIconTags, &orgWidth, &orgHeight);
scaleWidthDiff = orgWidth - data->maxWidth;
scaleHeightDiff = orgHeight - data->maxHeight;
// calculate the scale factors now that we have filled up our source bitmap
if((scaleHeightDiff > 0 && data->maxHeight > 0) ||
(scaleWidthDiff > 0 && data->maxWidth > 0))
{
double scaleFactor;
// make sure we are scaling proportional
if(scaleHeightDiff > scaleWidthDiff)
{
scaleFactor = (double)orgWidth / (double)orgHeight;
newWidth = (scaleFactor * data->maxHeight) + 0.5; // round up the value
newHeight = data->maxHeight;
}
else
{
scaleFactor = (double)orgHeight / (double)orgWidth;
newWidth = data->maxWidth;
newHeight = (scaleFactor * data->maxWidth) + 0.5; // round up the value
}
}
else
{
newWidth = orgWidth;
newHeight = orgHeight;
}
// first try to let icon.library scale the icon
if(LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE)
{
if(newWidth != orgWidth || newHeight != orgHeight)
{
struct DiskObject *scaledDiskObject;
#ifndef ICONDUPA_Width
#define ICONDUPA_Width TAG_IGNORE
#endif
#ifndef ICONDUPA_Height
#define ICONDUPA_Height TAG_IGNORE
#endif
if((scaledDiskObject = DupDiskObject(diskObject,
ICONDUPA_Width, newWidth,
ICONDUPA_Height, newHeight,
IA_Width, newWidth,
IA_Height, newHeight,
TAG_DONE)) != NULL)
{
LONG scaledWidth;
LONG scaledHeight;
// obtain the size of the scaled icon
GetIconSize(scaledDiskObject, drawIconTags, &scaledWidth, &scaledHeight);
// if the scaled icon has the demanded size then we will use this one
// during draw operations
if(scaledWidth == newWidth && scaledHeight == newHeight)
{
data->drawDiskObject = scaledDiskObject;
data->scaledWidth = newWidth;
data->scaledHeight = newHeight;
}
else
{
FreeDiskObject(scaledDiskObject);
}
}
}
else
{
data->drawDiskObject = diskObject;
data->scaledWidth = orgWidth;
data->scaledHeight = orgHeight;
}
}
if(data->drawDiskObject == NULL)
{
// obtaining a scaled icon failed
// we have to fall back to the bitmap based approach
struct BitMap *orgBitMap;
struct BitMap *screenBitMap = _screen(obj)->RastPort.BitMap;
ULONG screenDepth = GetBitMapAttr(screenBitMap, BMA_DEPTH);
PLANEPTR normalBitMask;
PLANEPTR selectedBitMask;
// obtain the bitmask for transparency drawing
if(LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE)
{
struct TagItem iconCtrlTags[] = { { ICONCTRLA_GetImageMask1, (ULONG)NULL },
{ ICONCTRLA_GetImageMask2, (ULONG)NULL },
{ TAG_DONE, FALSE } };
iconCtrlTags[0].ti_Data = (ULONG)&normalBitMask;
iconCtrlTags[1].ti_Data = (ULONG)&selectedBitMask;
// query the bitmask
IconControlA(diskObject, iconCtrlTags);
}
else
{
normalBitMask = NULL;
selectedBitMask = NULL;
}
// we first allocate a source bitmap with equal size to the icon size of the diskObject
if((orgBitMap = AllocBitMap(orgWidth, orgHeight, screenDepth, BMF_CLEAR | BMF_MINPLANES, screenBitMap)) != NULL)
{
// create a new layer info and a layer to get a clipping RastPort
struct Layer_Info *li;
if((li = NewLayerInfo()) != NULL)
{
struct Layer *l;
if((l = CreateUpfrontLayer(li, orgBitMap, 0, 0, orgWidth-1, orgHeight-1, LAYERSIMPLE, NULL)) != NULL)
{
// the layer now provides a RastPort with active clipping
// we use this RastPort to let icon.library render the icon image to
struct RastPort *rp = l->rp;
if(LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE)
DrawIconStateA(rp, diskObject, NULL, 0, 0, IDS_SELECTED, drawIconTags);
else
{
if(isFlagSet(diskObject->do_Gadget.Flags, GFLG_GADGHIMAGE))
DrawImage(rp, ((struct Image *)diskObject->do_Gadget.SelectRender), 0, 0);
else
DrawImage(rp, ((struct Image *)diskObject->do_Gadget.GadgetRender), 0, 0);
}
// now we can allocate a new bitmap which should carry the scaled selected image
if((data->selectedBitMap = AllocBitMap(newWidth, newHeight, screenDepth, BMF_CLEAR | BMF_MINPLANES, orgBitMap)) != NULL)
{
struct BitScaleArgs args;
args.bsa_SrcBitMap = orgBitMap;
args.bsa_DestBitMap = data->selectedBitMap;
args.bsa_Flags = 0;
args.bsa_SrcY = 0;
args.bsa_DestY = 0;
args.bsa_SrcWidth = orgWidth;
args.bsa_SrcHeight = orgHeight;
args.bsa_XSrcFactor = orgWidth;
args.bsa_XDestFactor = newWidth;
args.bsa_YSrcFactor = orgHeight;
args.bsa_YDestFactor = newHeight;
args.bsa_SrcX = 0;
args.bsa_DestX = 0;
// scale the image now with the arguments set
BitMapScale(&args);
// read out the scaled values
data->scaledWidth = args.bsa_DestWidth;
data->scaledHeight = args.bsa_DestHeight;
D(DBF_GUI, "AttachmentImage selected scale (w/h) from %ld/%ld to %ld/%ld", orgWidth,
orgHeight,
data->scaledWidth,
data->scaledHeight);
}
// now we also scale the selected BitMask down, if it exists
if(selectedBitMask != NULL &&
(data->selectedBitMask = AllocBitMap(newWidth, newHeight, 1L, BMF_CLEAR | BMF_MINPLANES, NULL)) != NULL)
{
struct BitScaleArgs args;
struct BitMap bm;
InitBitMap(&bm, 1L, orgWidth, orgHeight);
bm.Planes[0] = selectedBitMask;
args.bsa_SrcBitMap = &bm;
args.bsa_DestBitMap = data->selectedBitMask;
args.bsa_Flags = 0;
args.bsa_SrcY = 0;
args.bsa_DestY = 0;
args.bsa_SrcWidth = orgWidth;
args.bsa_SrcHeight = orgHeight;
args.bsa_XSrcFactor = orgWidth;
args.bsa_XDestFactor = newWidth;
args.bsa_YSrcFactor = orgHeight;
args.bsa_YDestFactor = newHeight;
args.bsa_SrcX = 0;
args.bsa_DestX = 0;
// scale the image now with the arguments set
BitMapScale(&args);
}
else
data->selectedBitMask = NULL;
// now that we have the selectedBitMap filled we have to scale down the unselected state
// of the icon as well.
if(LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE)
DrawIconStateA(rp, diskObject, NULL, 0, 0, IDS_NORMAL, drawIconTags);
else
DrawImage(rp, ((struct Image *)diskObject->do_Gadget.GadgetRender), 0, 0);
// now we can allocate a new bitmap which should carry the scaled unselected normal image
if((data->normalBitMap = AllocBitMap(newWidth, newHeight, screenDepth, BMF_CLEAR | BMF_MINPLANES, orgBitMap)) != NULL)
{
struct BitScaleArgs args;
args.bsa_SrcBitMap = orgBitMap;
args.bsa_DestBitMap = data->normalBitMap;
args.bsa_Flags = 0;
args.bsa_SrcY = 0;
args.bsa_DestY = 0;
args.bsa_SrcWidth = orgWidth;
args.bsa_SrcHeight = orgHeight;
args.bsa_XSrcFactor = orgWidth;
args.bsa_XDestFactor = newWidth;
args.bsa_YSrcFactor = orgHeight;
args.bsa_YDestFactor = newHeight;
args.bsa_SrcX = 0;
args.bsa_DestX = 0;
// scale the image now with the arguments set
BitMapScale(&args);
// read out the scaled values
data->scaledWidth = args.bsa_DestWidth;
data->scaledHeight = args.bsa_DestHeight;
D(DBF_GUI, "AttachmentImage normal scale (w/h) from %ld/%ld to %ld/%ld", orgWidth,
orgHeight,
data->scaledWidth,
data->scaledHeight);
}
// now we also scale the normal BitMask down, if it exists
if(normalBitMask != NULL &&
(data->normalBitMask = AllocBitMap(newWidth, newHeight, 1L, BMF_CLEAR | BMF_MINPLANES, NULL)) != NULL)
{
struct BitScaleArgs args;
struct BitMap bm;
InitBitMap(&bm, 1L, orgWidth, orgHeight);
bm.Planes[0] = normalBitMask;
args.bsa_SrcBitMap = &bm;
args.bsa_DestBitMap = data->normalBitMask;
args.bsa_Flags = 0;
args.bsa_SrcY = 0;
args.bsa_DestY = 0;
args.bsa_SrcWidth = orgWidth;
args.bsa_SrcHeight = orgHeight;
args.bsa_XSrcFactor = orgWidth;
args.bsa_XDestFactor = newWidth;
args.bsa_YSrcFactor = orgHeight;
args.bsa_YDestFactor = newHeight;
args.bsa_SrcX = 0;
args.bsa_DestX = 0;
// scale the image now with the arguments set
BitMapScale(&args);
}
else
data->normalBitMask = NULL;
DeleteLayer(0, l);
}
DisposeLayerInfo(li);
}
FreeBitMap(orgBitMap);
}
}
}
else
W(DBF_GUI, "retrieving any diskObject for file '%s' failed", iconFile);
// store the diskObject in our instance data for later
// reference
data->diskObject = diskObject;
data->lastDecodedStatus = mailPart != NULL && isDecoded(mailPart);
}
// there is no need to fill the background if we have no mask bitmap,
// but with a mask we should enable double buffering to reduce flickering
if(data->normalBitMask != NULL || data->selectedBitMask != NULL)
SetSuperAttrs(cl, obj, MUIA_DoubleBuffer, TRUE, MUIA_FillArea, TRUE, TAG_DONE);
else
SetSuperAttrs(cl, obj, MUIA_DoubleBuffer, FALSE, MUIA_FillArea, FALSE, TAG_DONE);
LEAVE();
}
///
/* Overloaded Methods */
/// OVERLOAD(OM_NEW)
OVERLOAD(OM_NEW)
{
ENTER();
if((obj = DoSuperNew(cl, obj,
MUIA_FillArea, FALSE, // do not care about background filling
MUIA_ShortHelp, TRUE,
TAG_MORE, inittags(msg))) != NULL)
{
GETDATA;
struct TagItem *tags = inittags(msg);
struct TagItem *tag;
while((tag = NextTagItem((APTR)&tags)) != NULL)
{
switch(tag->ti_Tag)
{
case ATTR(MailPart) : data->mailPart = (struct Part *)tag->ti_Data; break;
case ATTR(MaxHeight) : data->maxHeight = (ULONG)tag->ti_Data; break;
case ATTR(MaxWidth) : data->maxWidth = (ULONG)tag->ti_Data; break;
case ATTR(Group) : data->attachmentGroup = (Object *)tag->ti_Data; break;
}
}
}
RETURN((IPTR)obj);
return (IPTR)obj;
}
///
/// OVERLOAD(OM_DISPOSE)
OVERLOAD(OM_DISPOSE)
{
GETDATA;
free(data->dropPath);
data->dropPath = NULL;
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(DoubleClick) : *store = 1; return TRUE;
case ATTR(DropPath) : *store = (ULONG)data->dropPath; return TRUE;
case ATTR(MailPart) : *store = (ULONG)data->mailPart; return TRUE;
case ATTR(DiskObject) : *store = (ULONG)data->diskObject; return TRUE;
}
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(OM_SET)
OVERLOAD(OM_SET)
{
GETDATA;
struct TagItem *tags = inittags(msg), *tag;
BOOL refresh = FALSE;
while((tag = NextTagItem((APTR)&tags)) != NULL)
{
switch(tag->ti_Tag)
{
case ATTR(MaxHeight):
{
if(data->maxHeight == 0)
refresh = TRUE;
data->maxHeight = (ULONG)tag->ti_Data;
}
break;
case ATTR(MaxWidth):
{
if(data->maxWidth == 0)
refresh = TRUE;
data->maxWidth = (ULONG)tag->ti_Data;
}
break;
}
}
// reload the icon image if the maximum dimensions changed from invalid to valid values
if(refresh == TRUE)
LoadImage(cl, obj);
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(MUIM_Setup)
OVERLOAD(MUIM_Setup)
{
GETDATA;
IPTR result;
ENTER();
// call the method of the superclass first
result = DoSuperMethodA(cl, obj, msg);
if(result != 0 && data->mailPart != NULL)
{
// make sure to load/reload the attachment image
LoadImage(cl, obj);
// add an event handler for the drag&drop operations
// this object supports.
data->ehnode.ehn_Priority = -1;
data->ehnode.ehn_Flags = 0;
data->ehnode.ehn_Object = obj;
data->ehnode.ehn_Class = cl;
data->ehnode.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
DoMethod(_win(obj), MUIM_Window_AddEventHandler, &data->ehnode);
data->eventHandlerAdded = TRUE;
}
RETURN(result);
return result;
}
///
/// OVERLOAD(MUIM_Cleanup)
OVERLOAD(MUIM_Cleanup)
{
GETDATA;
IPTR result;
ENTER();
if(data->eventHandlerAdded == TRUE)
{
// remove the eventhandler first
DoMethod(_win(obj), MUIM_Window_RemEventHandler, &data->ehnode);
data->eventHandlerAdded = FALSE;
}
UnloadImage(data);
result = DoSuperMethodA(cl, obj, msg);
RETURN(result);
return result;
}
///
/// OVERLOAD(MUIM_AskMinMax)
OVERLOAD(MUIM_AskMinMax)
{
GETDATA;
struct MUI_MinMax *mi;
ENTER();
// call the supermethod first
DoSuperMethodA(cl, obj, msg);
mi = ((struct MUIP_AskMinMax *)msg)->MinMaxInfo;
mi->MinWidth += data->scaledWidth;
mi->DefWidth += data->scaledWidth;
mi->MaxWidth += data->scaledWidth;
mi->MinHeight += data->scaledHeight;
mi->DefHeight += data->scaledHeight;
mi->MaxHeight += data->scaledHeight;
RETURN(0);
return 0;
}
///
/// OVERLOAD(MUIM_Draw)
OVERLOAD(MUIM_Draw)
{
GETDATA;
struct MUIP_Draw *dmsg = (struct MUIP_Draw *)msg;
ENTER();
// call the super method first
DoSuperMethodA(cl, obj, msg);
if(isFlagSet(dmsg->flags, MADF_DRAWOBJECT) || isFlagSet(dmsg->flags, MADF_DRAWUPDATE))
{
LONG state;
struct BitMap *bitmap;
struct BitMap *bitmask;
// we have to check whether the decoded status
// of our mail part changed and if so we have to reload
// the image in case
if(data->mailPart != NULL && isDecoded(data->mailPart) == TRUE &&
data->lastDecodedStatus == FALSE && LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE && G->DefIconsAvailable == TRUE)
{
LoadImage(cl, obj);
}
// check the selected state
if(xget(obj, MUIA_Selected) == TRUE)
{
state = IDS_SELECTED;
bitmap = data->selectedBitMap;
bitmask = data->selectedBitMask;
}
else
{
state = IDS_NORMAL;
bitmap = data->normalBitMap;
bitmask = data->normalBitMask;
}
if(data->drawDiskObject != NULL && LIB_VERSION_IS_AT_LEAST(IconBase, 44, 0) == TRUE)
{
struct TagItem drawIconTags[] = { { ICONDRAWA_Borderless, TRUE },
{ ICONDRAWA_Frameless, TRUE },
{ ICONDRAWA_Transparency, 255 },
{ ICONDRAWA_DrawInfo, (ULONG)_dri(obj) },
{ TAG_DONE, FALSE } };
// if this is an alternative part we draw it with
// transparency of 50%
if(data->mailPart != NULL && isAlternativePart(data->mailPart))
drawIconTags[2].ti_Data = 128;
DrawIconStateA(_rp(obj), data->drawDiskObject, NULL, _mleft(obj), _mtop(obj), state, drawIconTags);
}
else if(bitmap != NULL)
{
#if defined(__amigaos4__)
if(bitmask != NULL)
{
BltBitMapTags(BLITA_Source, bitmap,
BLITA_Dest, _rp(obj),
BLITA_SrcType, BLITT_BITMAP,
BLITA_DestType, BLITT_RASTPORT,
BLITA_DestX, _mleft(obj),
BLITA_DestY, _mtop(obj),
BLITA_Width, _mwidth(obj),
BLITA_Height, _mheight(obj),
BLITA_Minterm, MINTERM_SRCMASK,
BLITA_MaskPlane, bitmask->Planes[0],
TAG_DONE);
}
else
{
BltBitMapTags(BLITA_Source, bitmap,
BLITA_Dest, _rp(obj),
BLITA_SrcType, BLITT_BITMAP,
BLITA_DestType, BLITT_RASTPORT,
BLITA_DestX, _mleft(obj),
BLITA_DestY, _mtop(obj),
BLITA_Width, _mwidth(obj),
BLITA_Height, _mheight(obj),
BLITA_Minterm, MINTERM_ABC | MINTERM_ABNC,
TAG_DONE);
}
#else
// we use an own BltMaskBitMapRastPort() implemenation to also support
// interleaved images.
if(bitmask != NULL)
MyBltMaskBitMapRastPort(bitmap, 0, 0, _rp(obj), _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj), (ABC|ABNC|ANBC), bitmask->Planes[0]);
else
BltBitMapRastPort(bitmap, 0, 0, _rp(obj), _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj), (ABC|ABNC));
#endif
}
}
RETURN(0);
return 0;
}
///
/// OVERLOAD(MUIM_HandleEvent)
OVERLOAD(MUIM_HandleEvent)
{
GETDATA;
struct IntuiMessage *imsg = ((struct MUIP_HandleEvent *)msg)->imsg;
ULONG rc = 0;
ENTER();
if(imsg != NULL)
{
switch(imsg->Class)
{
case IDCMP_MOUSEBUTTONS:
{
if(_isinobject(obj, imsg->MouseX, imsg->MouseY) == FALSE)
{
data->selectSecs = 0;
data->selectMicros = 0;
break;
}
// in case the image is selected
if(imsg->Code == SELECTDOWN)
{
// check if this has been a double click at the image
if(DoubleClick(data->selectSecs, data->selectMicros, imsg->Seconds, imsg->Micros))
{
xset(obj, ATTR(DoubleClick), TRUE,
MUIA_Selected, TRUE);
}
else
{
BOOL lastState = xget(obj, MUIA_Selected);
// only clear the selection if the user hasn't used
// the SHIFT key to select multiple items.
if(isAnyFlagSet(imsg->Qualifier, IEQUALIFIER_RSHIFT|IEQUALIFIER_LSHIFT) == FALSE)
DoMethod(data->attachmentGroup, MUIM_AttachmentGroup_ClearSelection);
// invert the selection state
set(obj, MUIA_Selected, !lastState);
}
// save the seconds/micros for the next handleEvent call
data->selectSecs = imsg->Seconds;
data->selectMicros = imsg->Micros;
if(LIB_VERSION_IS_AT_LEAST(WorkbenchBase, 45, 0) == TRUE && data->eventHandlerAdded == TRUE)
{
DoMethod(_win(obj), MUIM_Window_RemEventHandler, &data->ehnode);
data->ehnode.ehn_Events |= IDCMP_MOUSEMOVE;
DoMethod(_win(obj), MUIM_Window_AddEventHandler, &data->ehnode);
}
rc = MUI_EventHandlerRC_Eat;
}
// in case the image is unselected by the user
else if(imsg->Code == SELECTUP)
{
if(LIB_VERSION_IS_AT_LEAST(WorkbenchBase, 45, 0) == TRUE)
{
DoMethod(_win(obj), MUIM_Window_RemEventHandler, &data->ehnode);
data->ehnode.ehn_Events &= ~IDCMP_MOUSEMOVE;
DoMethod(_win(obj), MUIM_Window_AddEventHandler, &data->ehnode);
}
rc = MUI_EventHandlerRC_Eat;
}
}
break;
case IDCMP_MOUSEMOVE:
{
// in case this event is a mouse move we signal a dragging event, but only
// if it starts within our object region.
if(_isinobject(obj, imsg->MouseX, imsg->MouseY) == TRUE)
{
DoMethod(obj, MUIM_DoDrag, imsg->MouseX - _left(obj), imsg->MouseY - _top(obj));
}
}
break;
case IDCMP_RAWKEY:
{
switch(imsg->Code)
{
case IECODE_RETURN:
{
if(obj == (Object *)xget(_win(obj), MUIA_Window_ActiveObject))
{
set(obj, ATTR(DoubleClick), TRUE);
rc = MUI_EventHandlerRC_Eat;
}
}
break;
case IECODE_SPACE:
{
if(obj == (Object *)xget(_win(obj), MUIA_Window_ActiveObject))
{
BOOL lastState = xget(obj, MUIA_Selected);
// only clear the selection if the user hasn't used
// the SHIFT key to select multiple items.
if(isAnyFlagSet(imsg->Qualifier, IEQUALIFIER_RSHIFT|IEQUALIFIER_LSHIFT) == FALSE)
DoMethod(data->attachmentGroup, MUIM_AttachmentGroup_ClearSelection);
set(obj, MUIA_Selected, !lastState);
rc = MUI_EventHandlerRC_Eat;
}
}
break;
}
}
break;
}
}
RETURN(rc);
return rc;
}
///
/// OVERLOAD(MUIM_DeleteDragImage)
OVERLOAD(MUIM_DeleteDragImage)
{
GETDATA;
ULONG result;
struct Screen *wbscreen;
ENTER();
free(data->dropPath);
data->dropPath = NULL;
// The icon was not dropped on YAM's own write window, so now we check whether
// YAM is running on the workbench screen or not, because otherwise we skip our
// further operations.
if((wbscreen = LockPubScreen("Workbench")) != NULL)
{
struct Layer *layer;
if((layer = WhichLayer(&_screen(obj)->LayerInfo, _screen(obj)->MouseX, _screen(obj)->MouseY)) != NULL)
{
if(FindWriteWindow(layer->Window) == FALSE)
{
if(wbscreen == _screen(obj))
{
#if defined(__amigaos4__)
char *buf;
if((buf = (STRPTR)malloc(SIZE_PATHFILE)) != NULL)
{
ULONG which;
char name[SIZE_PATH];
ULONG type = ~0;
struct TagItem ti[] = { { WBOBJA_DrawerPath, (ULONG)buf },
{ WBOBJA_DrawerPathSize, SIZE_PATHFILE },
{ WBOBJA_Name, (ULONG)name },
{ WBOBJA_NameSize, (ULONG)sizeof(name) },
{ WBOBJA_Type, (ULONG)&type },
{ TAG_DONE, FALSE } };
buf[0] = '\0';
// Note that we use WhichWorkbenchObjectA() and not WhichWorkbenchObject()
// because the latter wasn't implemented in workbench.library < 51.9
which = WhichWorkbenchObjectA(NULL, _screen(obj)->MouseX, _screen(obj)->MouseY, ti);
if(which == WBO_ICON)
{
if(type == WBDRAWER)
AddPart(buf, name, SIZE_PATHFILE);
else if(type == WBDISK)
snprintf(buf, SIZE_PATHFILE, "%s:", name);
which = WBO_DRAWER;
}
if(which == WBO_DRAWER && buf[0] != '\0')
{
if((data->dropPath = strdup(buf)) != NULL)
{
D(DBF_GUI, "found dropPath '%s'", data->dropPath);
DoMethod(_app(obj), MUIM_Application_PushMethod, obj, 3, MUIM_Set, ATTR(DropPath), data->dropPath);
}
}
else
{
W(DBF_GUI, "couldn't find drop point of attachment image");
DisplayBeep(_screen(obj));
}
free(buf);
}
#else // __amigaos4__
// this stuff only works with Workbench v45+
if(LIB_VERSION_IS_AT_LEAST(WorkbenchBase, 45, 0) == TRUE)
{
struct List *path_list;
if(WorkbenchControl(NULL, WBCTRLA_GetOpenDrawerList, &path_list, TAG_DONE))
{
struct Hook hook;
struct SelectionMsg selMsg;
struct Node *n;
selMsg.layer = layer;
selMsg.mx = _screen(obj)->MouseX;
selMsg.my = _screen(obj)->MouseY;
selMsg.destName = NULL;
selMsg.finish = FALSE;
// initialise the selection hook with our data
InitHook(&hook, SelectionHook, &selMsg);
IterateList(path_list, struct Node *, n)
{
if((selMsg.drawer = strdup(n->ln_Name)) != NULL)
{
ChangeWorkbenchSelectionA(selMsg.drawer, &hook, NULL);
if(selMsg.finish == TRUE)
{
if(selMsg.destName == NULL)
{
data->dropPath = selMsg.drawer;
// don't free the path
selMsg.drawer = NULL;
}
else
{
int len = strlen(selMsg.destName) + strlen(selMsg.drawer) + 10;
if((data->dropPath = malloc(len)) != NULL)
AddPath(data->dropPath, selMsg.drawer, selMsg.destName, len);
free(selMsg.destName);
}
}
free(selMsg.drawer);
if(selMsg.finish == TRUE)
break;
}
}
WorkbenchControl(NULL, WBCTRLA_FreeOpenDrawerList, path_list, TAG_DONE);
if(selMsg.finish == FALSE)
{
selMsg.drawer = NULL;
ChangeWorkbenchSelectionA(NULL, &hook, NULL);
if(selMsg.finish == TRUE && selMsg.destName != NULL)
data->dropPath = selMsg.destName;
}
// signal other listening for the DropPath that we
// found out where to icon has dropped at exactly.
if(data->dropPath != NULL)
{
D(DBF_GUI, "found dropPath '%s'", data->dropPath);
DoMethod(_app(obj), MUIM_Application_PushMethod, obj, 3, MUIM_Set, ATTR(DropPath), data->dropPath);
}
else
{
W(DBF_GUI, "couldn't find drop point of attachment image");
DisplayBeep(_screen(obj));
}
}
else
{
W(DBF_GUI, "WorkbenchControl(WBCTRLA_GetOpenDrawerList) failed");
DisplayBeep(_screen(obj));
}
}
#endif // __amigaos4__
}
else
W(DBF_GUI, "YAM is not running on workbench, skipping drop operation");
}
}
UnlockPubScreen(NULL, wbscreen);
}
DoMethod(_win(obj), MUIM_Window_RemEventHandler, &data->ehnode);
data->ehnode.ehn_Events &= ~IDCMP_MOUSEMOVE;
DoMethod(_win(obj), MUIM_Window_AddEventHandler, &data->ehnode);
result = DoSuperMethodA(cl, obj, msg);
RETURN(result);
return result;
}
///
/// OVERLOAD(MUIM_CreateShortHelp)
// set up a text for the bubble help
OVERLOAD(MUIM_CreateShortHelp)
{
GETDATA;
char *shortHelp = NULL;
ENTER();
if(data->mailPart != NULL)
{
struct Part *mp = data->mailPart;
char sizestr[SIZE_DEFAULT];
FormatSize(mp->Size, sizestr, sizeof(sizestr), SF_AUTO);
if(asprintf(&shortHelp, tr(MSG_MA_MIMEPART_INFO), mp->Nr,
mp->Name,
mp->Description,
DescribeCT(mp->ContentType),
mp->ContentType,
sizestr) == -1)
{
shortHelp = NULL;
}
}
RETURN(shortHelp);
return (IPTR)shortHelp;
}
///
/// OVERLOAD(MUIM_DeleteShortHelp)
// free the bubble help text
OVERLOAD(MUIM_DeleteShortHelp)
{
struct MUIP_DeleteShortHelp *dsh = (struct MUIP_DeleteShortHelp *)msg;
ENTER();
free(dsh->help);
RETURN(0);
return 0;
}
///