/*
(c) Copyright 1988-1994 Microsoft Corporation.

Microsoft Bars&Pipes Professional Source Code License

This License governs use of the accompanying Software.  
Microsoft hopes you find this Software useful.

You are licensed to do anything you want with the Software. 

In return, we simply require that you agree:

1.      Not to remove any copyright notices from the Software.

2.      That the Software comes "as is", with no warranties.  
        None whatsoever. This means no implied warranty of merchantability or 
        fitness for a particular purpose or any warranty of non-infringement.  
        Also, you must pass this disclaimer on whenever you distribute the Software.

3.      That we will not be liable for any of those types of damages known as indirect, 
        special, consequential, or incidental related to the Software or this License, 
        to the maximum extent the law permits. Also, you must pass this limitation of 
        liability on whenever you distribute the Software.

4.      That if you sue anyone over patents that you think may apply to the Software 
        for that person’s use of the Software, your license to the Software ends automatically.

5.      That the patent rights Microsoft is licensing only apply to the Software, 
        not to any derivatives you make.

6.      That your rights under the License end automatically if you breach this in any way.
*/

#include "/CompilerSpecific.h"
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <graphics/gfxmacros.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <string.h>
#include <dos/dos.h>
#include "/v.h"
#include "all.h"

extern struct Functions functions;

struct ListNode {
    struct ListNode *next;
};
/*
 * This routine prints the two arrows that appear at the top and bottom of
 * the menu.
 */

static CHIP UWORD scrpopup[] =
{
/*-------- plane # 0 --------*/

    0x0000, 0xc000, 0x0000,
    0x0003, 0x3000, 0x0000,
    0x000c, 0x0c00, 0x0000,
    0x0030, 0x0300, 0x0000,
    0x00c0, 0x00c0, 0x0000,
    0x0300, 0x0030, 0x0000,
    0x0c00, 0x000c, 0x0000,
    0x3000, 0x0003, 0x0000,
    0xffe0, 0x01ff, 0xc000,
    0x0060, 0x0180, 0x0000,
    0x0060, 0x0180, 0x0000,
    0x0060, 0x0180, 0x0000,
    0x0060, 0x0180, 0x0000,

/*-------- plane # 1 --------*/

    0x0000, 0x0000, 0x0000,
    0x0003, 0xc000, 0x0000,
    0x000f, 0xf000, 0x0000,
    0x003f, 0xfc00, 0x0000,
    0x00ff, 0xff00, 0x0000,
    0x03ff, 0xffc0, 0x0000,
    0x0fff, 0xfff0, 0x0000,
    0x3fff, 0xfffc, 0x0000,
    0xffff, 0xfe00, 0x0000,
    0x007f, 0xfe00, 0x0000,
    0x007f, 0xfe00, 0x0000,
    0x007f, 0xfe00, 0x0000,
    0x007f, 0xfe00, 0x0000
};

static struct Image scrpopupimage =
{
    0, 0, 34, 13, 2,
    scrpopup,
    0x3, 0x0, NULL
};

static CHIP UWORD scrpopdown[] =
{
/*-------- plane # 0 --------*/

    0x0060, 0x0180, 0x0000,
    0x0060, 0x0180, 0x0000,
    0x0060, 0x0180, 0x0000,
    0x0060, 0x0180, 0x0000,
    0xffe0, 0x01ff, 0xc000,
    0x3000, 0x0003, 0x0000,
    0x0c00, 0x000c, 0x0000,
    0x0300, 0x0030, 0x0000,
    0x00c0, 0x00c0, 0x0000,
    0x0030, 0x0300, 0x0000,
    0x000c, 0x0c00, 0x0000,
    0x0003, 0x3000, 0x0000,
    0x0000, 0xc000, 0x0000,

/*-------- plane # 1 --------*/

    0x007f, 0xfe00, 0x0000,
    0x007f, 0xfe00, 0x0000,
    0x007f, 0xfe00, 0x0000,
    0x007f, 0xfe00, 0x0000,
    0xffff, 0xfe00, 0x0000,
    0x3fff, 0xfffc, 0x0000,
    0x0fff, 0xfff0, 0x0000,
    0x03ff, 0xffc0, 0x0000,
    0x00ff, 0xff00, 0x0000,
    0x003f, 0xfc00, 0x0000,
    0x000f, 0xf000, 0x0000,
    0x0003, 0xc000, 0x0000,
    0x0000, 0x0000, 0x0000
};

static struct Image scrpopdownimage =
{
    0, 0, 34, 13, 2,
    scrpopdown,
    0x3, 0x0, NULL
};

static void draw_arrows (struct RastPort * rastport, short width, short height)
{
    short center = width >> 1;
    DrawImage (rastport, &scrpopupimage, center - (scrpopupimage.Width >> 1), 7);
    DrawImage (rastport, &scrpopdownimage, center - (scrpopdownimage.Width >> 1), height - 20);
}
/*
 * This draws the visible section of the list.  Since this can not know what
 * the elements of the list look like (this is a generic list handler,) a
 * routine is supplied by the calling program that knows how to draw a given
 * list item.  This routine is passed in the variable displayroutine.
 */

static void draw_list (struct RastPort * rastport, struct ListNode * list,
               void (*displayroutine) (), short itemheight, short itemwidth,
                           short count)
{
    short y;
    unsigned short index;
    y = 20;
    for (index = 0; index < count; ++index) {
        rectfill (rastport, 2, 2, y, itemwidth - 3, y + itemheight - 1);
        if (list) {
            (*displayroutine) (rastport, list, y);
            list = list->next;
        }
        y += itemheight;
    }
}


/*
 * This highlights a displayed list item by complementing all of the bits in
 * the rectangle that the item is displayed in. To deselect, or remove the
 * highlight, this routine is called a second time, and the complement
 * operation returns the bits to normal.
 */
static void complement_item (struct RastPort * rastport, short position,
                                 short itemheight, short itemwidth)
{
    if (position < 0)
        return;
    position = (position * itemheight) + 20;
    BNDRYOFF (rastport);
    SetDrMd (rastport, COMPLEMENT);
    SetWrMsk (rastport, 0xfc);
    //DM // SetAPen (rastport, 1);
    RectFill (rastport, 2, position, itemwidth - 3, position + itemheight - 1);
    SetWrMsk (rastport, 0xff);
}


static void send_false_button_up (void)
{
    register struct IOStdReq *ioreq;
    struct InputEvent event;
    register struct MsgPort *ioport;
    ioport = (struct MsgPort *) CreatePort ("let's fool intuition", 0);
    if (ioport) {
        ioreq = (struct IOStdReq *) CreateStdIO (ioport);
        if (ioreq) {
            if (!OpenDevice ("input.device", 0, (struct IORequest *)ioreq, 0)) {
                ioreq->io_Command = IND_WRITEEVENT;
                ioreq->io_Flags = 0;
                ioreq->io_Length = sizeof (struct InputEvent);
                ioreq->io_Data = (APTR) & event;
                event.ie_NextEvent = 0;
                event.ie_Class = IECLASS_RAWMOUSE;
                event.ie_X = 0;
                event.ie_Y = 0;
                event.ie_Code = IECODE_LBUTTON | IECODE_UP_PREFIX;
                DoIO ((struct IORequest *)ioreq);
                CloseDevice ((struct IORequest *)ioreq);
            }
            DeleteStdIO (ioreq);
        }
        DeletePort (ioport);
    }
}

SAVEDS  struct ListNode *ScrollingPopUpMenu (struct Screen * screen, struct ListNode * list,
                                    void (*displayroutine) (), short itemheight,
                                    short itemwidth, short count)
{
    struct ListNode *topdisplay;
    short indent = 0;
    static struct NewWindow scrollNewWindowStructure =
    {
        0, 0, 10, 10, 2, 2,
        MOUSEBUTTONS + MOUSEMOVE + INTUITICKS,
        BORDERLESS + ACTIVATE + NOCAREREFRESH + REPORTMOUSE,
        0, 0, 0, 0, 0, 5, 5, -1, -1, CUSTOMSCREEN
    };
    struct IntuiMessage *message;
    struct Window *window;
    struct ListNode *activeitem;
    short height;
    long class;
    short position,
        newposition;
    short mousex,
        mousey;
    short scrolling = 0;
    short left,
        top;
    /* geta4(); */
    topdisplay = list;
    activeitem = 0;
    if (!list)
        return (0);
    if (!list->next)
        return (list);
    if (List_Len ((struct ListItem *)list) < count)
        count = List_Len ((struct ListItem *)list);
    height = (itemheight * count) + 40;
    top = screen->MouseY - (height >> 1);
    if (top < 0)
        top = 0;
    else if ((top + height) > screen->Height)
        top = screen->Height - height;
    left = screen->MouseX - (itemwidth >> 1);
    if (left < 0)
        left = 0;
    else if ((left + itemwidth) > screen->Width)
        left = screen->Width - itemwidth;
    scrollNewWindowStructure.Width = itemwidth;
    scrollNewWindowStructure.Height = height;
    scrollNewWindowStructure.TopEdge = top;
    scrollNewWindowStructure.LeftEdge = left;
    scrollNewWindowStructure.Screen = screen;
    send_false_button_up ();
    window = (struct Window *) FlashyOpenWindow (&scrollNewWindowStructure);
    if (window) {
        DrawEmbossedWindow (window, 1);
        position = window->MouseY - 20;
        position = position / itemheight;
        activeitem = (struct ListNode *) List_Index ((struct ListItem *)topdisplay, position);
        draw_list (window->RPort, topdisplay, displayroutine, itemheight, itemwidth, count);
        complement_item (window->RPort, position, itemheight, itemwidth);
        draw_arrows (window->RPort, itemwidth, height);
        for (;;) {
            message = (struct IntuiMessage *) GetIntuiMessage (window);
            class = message->Class;
            mousex = message->MouseX;
            mousey = message->MouseY;
            ReplyMsg ((struct Message *) message);
            if (class == MOUSEMOVE) {
                if ((mousex < 0) || (mousex > itemwidth) || (mousey < 0) || (mousey > height)) {
                    activeitem = 0;
                    break;
                }
                if (mousey < 20) {
                    if (activeitem) {
                        complement_item (window->RPort, position, itemheight, itemwidth);
                        activeitem = 0;
                        position = -1;
                    }
                    scrolling = 1;
                }
                else if (mousey > (height - 20)) {
                    if (activeitem) {
                        complement_item (window->RPort, position, itemheight, itemwidth);
                        activeitem = 0;
                        position = -1;
                    }
                    scrolling = 2;
                }
                else {
                    scrolling = 0;
                    newposition = (mousey - 20) / itemheight;
                    if ((newposition != position) && (newposition < count) && (newposition >= 0)) {
                        if (position >= 0)
                            complement_item (window->RPort, position, itemheight, itemwidth);
                        activeitem = (struct ListNode *) List_Index ((struct ListItem *)topdisplay, newposition);
                        position = newposition;
                        complement_item (window->RPort, position, itemheight, itemwidth);
                    }
                }
            }
            else if (class == MOUSEBUTTONS)
                break;
            else if (class == INTUITICKS) {
                if (scrolling == 1) {
                    if (indent) {
                        indent--;
                        topdisplay = (struct ListNode *) List_Index ((struct ListItem *)list, indent);
                        draw_list (window->RPort, topdisplay, displayroutine, itemheight, itemwidth, count);
                    }
                }
                else if (scrolling == 2) {
                    if (List_Len ((struct ListItem *)topdisplay) > count) {
                        indent++;
                        topdisplay = topdisplay->next;
                        draw_list (window->RPort, topdisplay, displayroutine, itemheight, itemwidth, count);
                    }
                }
            }
        }
        CloseWindow (window);
    }
    return (activeitem);
}
