src/mui/AttachmentGroup.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_Virtgroup
Description: Custom class to manage the attachments 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 "AttachmentGroup_cl.h"
#include <string.h>
#include <proto/muimaster.h>
#include "SDI_hook.h"
#include "YAM_mainFolder.h"
#include "mui/Attachment.h"
#include "Busy.h"
#include "Config.h"
#include "Locale.h"
#include "MUIObjects.h"
#include "Debug.h"
/* CLASSDATA
struct Data
{
struct Part *firstPart;
Object *contextMenu;
struct MUI_EventHandlerNode ehnode;
char menuTitle[SIZE_DEFAULT];
BOOL eventHandlerAdded;
};
*/
#define VERT_SPACING 4
#define HORIZ_SPACING 4
/* Private Hooks */
/// LayoutHook
HOOKPROTONH(LayoutFunc, ULONG, UNUSED Object *obj, struct MUI_LayoutMsg *lm)
{
ENTER();
switch(lm->lm_Type)
{
// MUI want's to know the min/max of the object so we
// need to calculate it accordingly.
case MUILM_MINMAX:
{
LONG maxMinWidth = 0;
LONG maxMinHeight = 0;
Object *cstate = (Object *)lm->lm_Children->mlh_Head;
Object *child;
while((child = NextObject(&cstate)) != NULL)
{
maxMinWidth = MAX(_minwidth(child), maxMinWidth);
maxMinHeight = MAX(_minheight(child), maxMinHeight);
}
maxMinWidth += 2 * HORIZ_SPACING;
maxMinHeight += 2 * VERT_SPACING;
// then set our calculated values
lm->lm_MinMax.MinWidth = maxMinWidth;
lm->lm_MinMax.MinHeight = maxMinHeight;
lm->lm_MinMax.DefWidth = maxMinWidth;
lm->lm_MinMax.DefHeight = maxMinHeight;
lm->lm_MinMax.MaxWidth = MUI_MAXMAX;
lm->lm_MinMax.MaxHeight = MUI_MAXMAX;
RETURN(0);
return 0;
}
break;
// MUI asks' us to draw/layout the actual content of the
// gadget, so we go and use MUI_Layout() to draw the gadget
// components at the right position.
case MUILM_LAYOUT:
{
Object *cstate = (Object *)lm->lm_Children->mlh_Head;
Object *child;
LONG left = 0;
LONG top = 0;
LONG lastItemHeight = 0;
LONG maxWidth = 0;
BOOL first = TRUE;
D(DBF_GUI, "attgroup layout: %08lx %ld/%ld", obj, lm->lm_Layout.Width, lm->lm_Layout.Height);
// Layout function. Here, we have to call MUI_Layout() for each
// our children. MUI wants us to place them in a rectangle
// defined by (0,0,lm->lm_Layout.Width-1,lm->lm_Layout.Height-1)
// We are free to put the children anywhere in this rectangle.
while((child = NextObject(&cstate)) != NULL)
{
LONG mw = _minwidth(child);
LONG mh = _minheight(child);
#if defined(DEBUG)
struct Part *mailPart = (struct Part *)xget(child, MUIA_Attachment_MailPart);
#endif
D(DBF_GUI, "layouting child %08lx '%s'", child, mailPart->Name);
if(first == TRUE)
first = FALSE;
else
{
if(left + mw + HORIZ_SPACING > lm->lm_Layout.Width)
{
// the current object doesn't fit in this row anymore, start a new row
D(DBF_GUI, "layout: putting object '%s' on new row", mailPart->Name);
// remember a possible new maximum width
if(left > maxWidth)
maxWidth = left;
// start again from the left border, but go down one line
left = 0;
top += lastItemHeight + VERT_SPACING;
lastItemHeight = mh;
}
}
D(DBF_GUI, "layout: x=%ld y=%ld w=%ld h=%ld '%s'", left, top+(mh-_minheight(child))/2, mw, mh, mailPart->Name);
if(!MUI_Layout(child, left, top+(mh-_minheight(child))/2, mw, _minheight(child), 0))
{
RETURN(FALSE);
return FALSE;
}
left += mw + HORIZ_SPACING;
lastItemHeight = MAX(mh, lastItemHeight);
}
top += lastItemHeight;
// update the layout dimensions in case we used more space than expected
if(lm->lm_Layout.Width < maxWidth)
lm->lm_Layout.Width = maxWidth;
if(lm->lm_Layout.Height < top)
lm->lm_Layout.Height = top;
D(DBF_GUI, "attgroup layout: %08lx %ld/%ld", obj, lm->lm_Layout.Width, lm->lm_Layout.Height);
RETURN(TRUE);
return TRUE;
}
break;
}
RETURN(MUILM_UNKNOWN);
return MUILM_UNKNOWN;
}
MakeStaticHook(LayoutHook, LayoutFunc);
///
/// Menu enumerations
enum
{
AMEN_SAVEALL=100,
AMEN_SAVESEL,
AMEN_DELETEALL,
AMEN_DELETESEL
};
///
/* Private functions */
/// CountSelectedAttachments
// count the number of selected attachments
static ULONG CountSelectedAttachments(Object *obj)
{
ULONG numSelected = 0;
struct List *childList;
ENTER();
// iterate through our child list
if((childList = (struct List *)xget(obj, MUIA_Group_ChildList)) != NULL)
{
Object *cstate = (Object *)childList->lh_Head;
Object *child;
while((child = NextObject(&cstate)) != NULL)
{
if(xget(child, MUIA_Selected) == TRUE)
numSelected++;
}
}
RETURN(numSelected);
return numSelected;
}
///
/* Overloaded Methods */
/// OVERLOAD(OM_NEW)
OVERLOAD(OM_NEW)
{
ENTER();
obj = DoSuperNew(cl, obj,
MUIA_Font, MUIV_Font_Tiny,
MUIA_Group_LayoutHook, &LayoutHook,
MUIA_ContextMenu, TRUE,
TAG_MORE, inittags(msg));
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_SET)
OVERLOAD(OM_SET)
{
struct TagItem *tags = inittags(msg), *tag;
while((tag = NextTagItem((APTR)&tags)) != NULL)
{
switch(tag->ti_Tag)
{
// we also catch foreign attributes
case MUIA_ShowMe:
{
// if the object should be hidden we clean it up also
if(tag->ti_Data == FALSE)
{
if(DoMethod(obj, MUIM_Group_InitChange))
{
DoMethod(obj, METHOD(Clear));
DoMethod(obj, MUIM_Group_ExitChange);
}
}
}
break;
}
}
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(OM_GET)
OVERLOAD(OM_GET)
{
IPTR *store = ((struct opGet *)msg)->opg_Storage;
switch(((struct opGet *)msg)->opg_AttrID)
{
case ATTR(NumSelectedAttachments): *store = CountSelectedAttachments(obj); return TRUE;
}
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(MUIM_Setup)
OVERLOAD(MUIM_Setup)
{
GETDATA;
IPTR result;
ENTER();
// add an event handler for clearing the selection state
// in case someone clicks in our area
if((result = DoSuperMethodA(cl, obj, msg)))
{
data->ehnode.ehn_Priority = -2; // attachmentimage has priority -1
data->ehnode.ehn_Flags = 0;
data->ehnode.ehn_Object = obj;
data->ehnode.ehn_Class = cl;
data->ehnode.ehn_Events = IDCMP_MOUSEBUTTONS;
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;
}
result = DoSuperMethodA(cl, obj, msg);
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;
}
strlcpy(data->menuTitle, tr(MSG_Attachments), sizeof(data->menuTitle));
data->contextMenu = MenustripObject,
Child, MenuObjectT(data->menuTitle),
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)
{
struct MUIP_ContextMenuChoice *m = (struct MUIP_ContextMenuChoice *)msg;
switch(xget(m->item, MUIA_UserData))
{
case AMEN_SAVEALL:
DoMethod(obj, MUIM_AttachmentGroup_SaveAll);
break;
case AMEN_SAVESEL:
DoMethod(obj, MUIM_AttachmentGroup_SaveSelected);
break;
case AMEN_DELETEALL:
DoMethod(obj, MUIM_AttachmentGroup_DeleteAll);
break;
case AMEN_DELETESEL:
DoMethod(obj, MUIM_AttachmentGroup_DeleteSelected);
break;
default:
return DoSuperMethodA(cl, obj, msg);
}
return 0;
}
///
/// OVERLOAD(MUIM_HandleEvent)
OVERLOAD(MUIM_HandleEvent)
{
struct IntuiMessage *imsg = ((struct MUIP_HandleEvent *)msg)->imsg;
IPTR result = 0;
ENTER();
if(imsg != NULL && imsg->Class == IDCMP_MOUSEBUTTONS)
{
// we clear the selection state in case the user clicked in
// our area (= not an attachment image)
if((imsg->Code == SELECTDOWN || imsg->Code == SELECTUP) &&
_isinobject(obj, imsg->MouseX, imsg->MouseY))
{
DoMethod(obj, METHOD(ClearSelection));
result = MUI_EventHandlerRC_Eat;
}
}
RETURN(result);
return result;
}
///
/* Private Functions */
/* Public Methods */
/// DECLARE(Clear)
DECLARE(Clear)
{
GETDATA;
struct List *childList = (struct List *)xget(obj, MUIA_Group_ChildList);
Object *cstate = (Object *)childList->lh_Head;
Object *child;
ENTER();
// iterate through our child list and remove all attachment objects
while((child = NextObject(&cstate)) != NULL)
{
DoMethod(obj, OM_REMMEMBER, child);
MUI_DisposeObject(child);
}
data->firstPart = NULL;
RETURN(0);
return 0;
}
///
/// DECLARE(Refresh)
DECLARE(Refresh) // struct Part *firstPart
{
GETDATA;
ULONG addedParts = 0;
ENTER();
// prepare the group for any change
if(DoMethod(obj, MUIM_Group_InitChange))
{
struct Part *rp;
// before we are going to add some new childs we have to clean
// out all old children
DoMethod(obj, METHOD(Clear));
// now we iterate through our message part list and
// generate an own attachment image for each attachment
for(rp = msg->firstPart; rp != NULL; rp = rp->Next)
{
if(rp->Nr > PART_RAW && rp->Nr != rp->rmData->letterPartNum && (C->DisplayAllAltPart ||
(isAlternativePart(rp) == FALSE || rp->Parent == NULL || rp->Parent->MainAltPart == rp)))
{
Object *attObject;
if((attObject = AttachmentObject,
MUIA_Attachment_MailPart, rp,
MUIA_Attachment_Group, obj,
End) != NULL)
{
DoMethod(obj, OM_ADDMEMBER, attObject);
D(DBF_GUI, "added attachment obj %08lx for attachment: %ld:%s mp: %08lx %08lx %08lx %08lx", attObject, rp->Nr, rp->Name, rp, rp->ContentType, rp->headerList, rp->rmData);
addedParts++;
}
}
}
if(addedParts != 0)
data->firstPart = msg->firstPart;
else
data->firstPart = NULL;
// signal that the group relayouting is finished
DoMethod(obj, MUIM_Group_ExitChange);
}
RETURN(addedParts);
return addedParts;
}
///
/// DECLARE(SaveAll)
DECLARE(SaveAll)
{
GETDATA;
ENTER();
if(data->firstPart != NULL && data->firstPart->Next != NULL)
{
struct FileReqCache *frc;
if((frc = ReqFile(ASL_DETACH, _win(obj), tr(MSG_RE_SaveMessage), (REQF_SAVEMODE|REQF_DRAWERSONLY), C->DetachDir, "")) != NULL)
{
struct BusyNode *busy;
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecSaving), "");
RE_SaveAll(data->firstPart->rmData, frc->drawer);
BusyEnd(busy);
}
}
RETURN(0);
return 0;
}
///
/// DECLARE(SaveSelected)
DECLARE(SaveSelected)
{
struct List *childList;
struct BusyNode *busy;
ENTER();
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecSaving), "");
// iterate through our child list
if((childList = (struct List *)xget(obj, MUIA_Group_ChildList)) != NULL)
{
Object *cstate = (Object *)childList->lh_Head;
Object *child;
// invoke the method for all selected items
while((child = NextObject(&cstate)) != NULL)
{
if(xget(child, MUIA_Selected) == TRUE)
DoMethod(child, MUIM_Attachment_Save);
}
}
BusyEnd(busy);
RETURN(0);
return 0;
}
///
/// DECLARE(DeleteAll)
DECLARE(DeleteAll)
{
GETDATA;
ENTER();
if(data->firstPart != NULL)
{
// remove all attachments now
MA_RemoveAttach(data->firstPart->rmData->mail, NULL, C->ConfirmRemoveAttachments);
}
RETURN(0);
return 0;
}
///
/// DECLARE(DeleteSelected)
DECLARE(DeleteSelected)
{
GETDATA;
struct List *childList;
ENTER();
// iterate through our child list
if((childList = (struct List *)xget(obj, MUIA_Group_ChildList)) != NULL)
{
ULONG numSelected = CountSelectedAttachments(obj);
struct Part **parts;
// build a list of selected attachments
if(numSelected > 0 && (parts = calloc(numSelected + 1, sizeof(*parts))) != NULL)
{
Object *cstate = (Object *)childList->lh_Head;
Object *child;
ULONG i = 0;
while((child = NextObject(&cstate)) != NULL)
{
if(xget(child, MUIA_Selected) == TRUE)
parts[i++] = (struct Part *)xget(child, MUIA_Attachment_MailPart);
}
// and finally remove the attachments
MA_RemoveAttach(data->firstPart->rmData->mail, parts, C->ConfirmRemoveAttachments);
free(parts);
}
}
RETURN(0);
return 0;
}
///
/// DECLARE(ClearSelection)
DECLARE(ClearSelection)
{
struct List *childList;
ENTER();
if((childList = (struct List *)xget(obj, MUIA_Group_ChildList)) != NULL)
{
Object *cstate = (Object *)childList->lh_Head;
Object *child;
while((child = NextObject(&cstate)) != NULL)
{
D(DBF_GUI, "clearing MUIA_Selected of object %08lx", child);
set(child, MUIA_Selected, FALSE);
}
}
RETURN(0);
return 0;
}
///
/// DECLARE(AttachmentDropped)
DECLARE(AttachmentDropped) // const char *dropPath
{
struct List *childList;
ENTER();
if((childList = (struct List *)xget(obj, MUIA_Group_ChildList)) != NULL)
{
Object *cstate = (Object *)childList->lh_Head;
Object *child;
// forward the drop path to all selected attachments
while((child = NextObject(&cstate)) != NULL)
{
if(xget(child, MUIA_Selected) == TRUE)
{
D(DBF_GUI, "forwarding drop path '%s' to selected object %08lx", msg->dropPath, child);
DoMethod(child, MUIM_Attachment_ImageDropped, msg->dropPath);
}
}
}
RETURN(0);
return 0;
}
///