Files

1937 lines
47 KiB
C

/* -*- Mode: C; tab-width: 4; -*- */
/* npmngplg.c
* MNG browser plugin
* By Jason Summers
* Based on libmng by Gerard Juyn
*/
#include <windows.h>
#include <commdlg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdarg.h>
#include "resource.h"
#include "libmng.h"
#include "jversion.h" // part of libjpeg
#include "zlib.h" // for zlibVersion
#include "npapidefs.h"
#define MNGPLG_CMS
//#define MNGPLG_TRACE
#define IDBASE 47000
#define ID_SAVEAS (IDBASE+0)
#define ID_COPYIMAGE (IDBASE+1)
#define ID_COPYURL (IDBASE+2)
#define ID_VIEWIMAGE (IDBASE+3)
#define ID_ABOUT (IDBASE+4)
#define ID_FREEZE (IDBASE+5)
#define ID_RESTARTANIM (IDBASE+6)
#define ID_COPYLINKLOC (IDBASE+7)
#define ID_STOPANIM (IDBASE+8)
#define ID_SHOWERROR (IDBASE+9)
#define ID_PROPERTIES (IDBASE+10)
#define MNGPLGVERS "1.0.1"
/* instance-specific data */
typedef struct pluginstruct_
{
NPWindow* fWindow;
uint16 fMode;
HWND fhWnd;
WNDPROC fDefaultWindowProc;
NPP instance;
mng_handle mng;
#define STATE_INIT 0
#define STATE_LOADING 1 // stream opened
#define STATE_VALIDFRAME 2 // at least one frame has been displayed
#define STATE_LOADED 3 // image loaded; stream closed
#define MAXLEN_TEXT 5000
#define MAXLEN_URL 300
#define MAXLEN_TARGET 100
// I think I'm not doing this very well. Probably there really needs to be
// two state variables, one for loading from the network, and one for
// the libmng processing. (or use libmng's new getstate API?)
int loadstate;
int paintedyet;
int scrolling; // allow scrolling of the image?
int xscrollpos, yscrollpos;
int windowwidth, windowheight; // client size of current window
int diblinesize;
DWORD dibsize;
DWORD filesize;
DWORD libmngpos; // count of bytes that have been sent to libmng
DWORD byteswanted; // libmng asked for this many more bytes (add to libmngpos)
unsigned char *mngdata; // stores the MNG file in memory
DWORD bytesloaded; //
DWORD bytesalloc; // size of mngdata
int needresume; // if previous mng_readdisplay call returned NEEDMOREDATA
char *textdata;
int errorflag; // set if an error occurs that prevents displaying the image
char errormsg[256];
unsigned char *lpdib; // pointer to header section of dib
unsigned char *lpdibbits; // pointer to "bits" section of dib (follows the header)
LPBITMAPINFOHEADER lpdibinfo; // alias for lpdib
int frozen;
int timer_set;
int timer2_set;
int dynamicmng;
int mouse_over_mng;
int mouse_captured;
int force_bgcolor;
mng_uint16 bg_r,bg_g,bg_b; // background color
unsigned char url[MAX_PATH]; // the url of the stream
int islink;
HCURSOR linkcursor;
HBRUSH bkgdbrush;
unsigned char linkurl[MAXLEN_URL];
unsigned char linktarget[MAXLEN_TARGET];
} PluginInstance;
/* global variables */
#ifdef MNGPLG_TRACE
static FILE *tracefile;
#endif
static const char* gInstanceLookupString = "pdata";
static HMODULE g_hInst = NULL;
static HCURSOR hcurHandNS;
static HFONT hfontMsg;
/* function prototypes */
LRESULT CALLBACK DlgProcAbout(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK DlgProcProp(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PluginWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
void set_scrollbars(PluginInstance *This);
//////////////////////////// NPN_functions
static NPNetscapeFuncs* g_pNavigatorFuncs;
static NPPluginFuncs* g_pluginFuncs;
static const char* NPN_UserAgent(NPP instance)
{
return g_pNavigatorFuncs->uagent(instance);
}
static NPError NPN_GetURL(NPP instance, const char *url, const char *target)
{
return g_pNavigatorFuncs->geturl(instance, url, target);
}
/* ----------------------------------------------------------------------- */
BOOL APIENTRY DllMain(HANDLE hModule, DWORD reason, LPVOID lpReserved)
{
switch(reason) {
case DLL_PROCESS_ATTACH:
g_hInst=hModule;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
/* ----------------------------------------------------------------------- */
static void warn(PluginInstance *This, char *fmt, ...)
{
va_list ap;
char buf[2048];
HWND hwnd;
va_start(ap, fmt);
wvsprintf(buf,fmt, ap);
va_end(ap);
if(This) hwnd= This->fhWnd;
else hwnd=NULL;
MessageBox(hwnd,buf,"MNG Plug-in",MB_OK|MB_ICONWARNING);
}
static void set_error(PluginInstance *This, char *fmt, ...)
{
va_list ap;
char buf[2048];
HWND hwnd;
va_start(ap, fmt);
wvsprintf(buf,fmt, ap);
va_end(ap);
if(This) hwnd= This->fhWnd;
else hwnd=NULL;
This->errorflag=1;
lstrcpyn(This->errormsg,buf,256);
if(This->lpdib) {
free(This->lpdib);
This->lpdib=NULL;
}
This->xscrollpos = This->yscrollpos = 0;
if(This->fhWnd)
InvalidateRect(This->fhWnd,NULL,TRUE);
}
/* ----------------------------------------------------------------------- */
// MNG callbacks
#define MNGPLG_CALLBACK MNG_DECL
static mng_ptr MNGPLG_CALLBACK memallocfunc(mng_size_t n)
{
return (mng_ptr) calloc(n,1);
}
static void MNGPLG_CALLBACK memfreefunc(mng_ptr p, mng_size_t n)
{
free((void*)p);
}
static mng_bool MNGPLG_CALLBACK callback_openstream (mng_handle mng)
{
// PluginInstance *This;
// This = (PluginInstance*) mng_get_userdata(mng);
return MNG_TRUE;
}
static mng_bool MNGPLG_CALLBACK callback_closestream (mng_handle mng)
{
PluginInstance *This;
This = (PluginInstance*) mng_get_userdata(mng);
This->loadstate = STATE_LOADED; // this is probably redundant
return MNG_TRUE;
}
static mng_bool MNGPLG_CALLBACK callback_readdata (mng_handle mng,mng_ptr pBuf,
mng_uint32 Buflen,mng_uint32 *pRead)
{
int n;
PluginInstance *This;
This = (PluginInstance*) mng_get_userdata(mng);
#ifdef MNGPLG_TRACE
fprintf(tracefile,"readdata callback buflen=%d loadstate=%d bytesloaded=%d libmngpos=%d\n",
Buflen,This->loadstate,This->bytesloaded, This->libmngpos);
#endif
// do we have enough data available?
if(This->bytesloaded - This->libmngpos >= Buflen) {
CopyMemory(pBuf,&This->mngdata[This->libmngpos],Buflen);
(*pRead)= Buflen;
This->libmngpos += Buflen;
#ifdef MNGPLG_TRACE
fprintf(tracefile,"returning full: %d\n",Buflen);
#endif
This->byteswanted=0;
return MNG_TRUE;
}
else if(This->loadstate>=STATE_LOADED) {
// We don't have the data it asked for, but we're at the end
// of file, so send it anyway...?
n=This->bytesloaded-This->libmngpos;
if(n>0) {
CopyMemory(pBuf,&This->mngdata[This->libmngpos],n);
This->libmngpos+=n;
}
(*pRead)=n;
// so what do we return?
#ifdef MNGPLG_TRACE
fprintf(tracefile,"returning partial: %d\n",n);
#endif
This->byteswanted=0;
return MNG_TRUE;
}
// else we don't yet have the data it's requesting
#ifdef MNGPLG_TRACE
fprintf(tracefile,"returning 0\n");
#endif
(*pRead)=0;
This->byteswanted=Buflen;
return MNG_TRUE;
}
static mng_bool MNGPLG_CALLBACK callback_processheader(mng_handle mng,mng_uint32 iWidth,mng_uint32 iHeight)
{
PluginInstance *This;
This = (PluginInstance*) mng_get_userdata(mng);
This->diblinesize = (((iWidth * 24)+31)/32)*4;
This->dibsize = sizeof(BITMAPINFOHEADER) + This->diblinesize*iHeight;
This->lpdib = calloc(This->dibsize,1);
This->lpdibinfo = (LPBITMAPINFOHEADER)This->lpdib;
This->lpdibbits = &This->lpdib[sizeof(BITMAPINFOHEADER)];
ZeroMemory((void*)This->lpdib,sizeof(BITMAPINFOHEADER));
This->lpdibinfo->biSize = sizeof(BITMAPINFOHEADER);
This->lpdibinfo->biWidth = iWidth;
This->lpdibinfo->biHeight = iHeight;
This->lpdibinfo->biPlanes = 1;
This->lpdibinfo->biBitCount = 24;
mng_set_canvasstyle (mng, MNG_CANVAS_BGR8);
/* if(This->fhWnd) {
if((int)iWidth > This->windowwidth || (int)iHeight > This->windowheight) {
This->scrolling=1;
}
} */
set_scrollbars(This);
return MNG_TRUE;
}
static mng_ptr MNGPLG_CALLBACK callback_getcanvasline (mng_handle mng, mng_uint32 iLinenr)
{
unsigned char *pp;
PluginInstance *This;
This = (PluginInstance*) mng_get_userdata(mng);
pp = (&This->lpdibbits[(This->lpdibinfo->biHeight-1-iLinenr)*This->diblinesize]);
return (mng_ptr) pp;
}
static mng_bool MNGPLG_CALLBACK callback_refresh (mng_handle mng, mng_uint32 iLeft, mng_uint32 iTop,
mng_uint32 iRight, mng_uint32 iBottom)
{
PluginInstance *This;
RECT rect;
This = (PluginInstance*) mng_get_userdata(mng);
if(This->loadstate<STATE_VALIDFRAME) {
This->loadstate=STATE_VALIDFRAME;
}
if(This->fhWnd) {
if(This->paintedyet) {
rect.left= iLeft - This->xscrollpos;
rect.top= iTop - This->yscrollpos;
rect.right= iLeft+iRight;
rect.bottom= iTop+iBottom;
InvalidateRect(This->fhWnd,&rect,FALSE);
}
else {
// Make sure the first paint clears the whole plugin window
InvalidateRect(This->fhWnd,NULL,TRUE);
This->paintedyet=1;
}
UpdateWindow(This->fhWnd);
}
return MNG_TRUE;
}
static mng_uint32 MNGPLG_CALLBACK callback_gettickcount (mng_handle mng)
{
return GetTickCount();
}
static mng_bool MNGPLG_CALLBACK callback_settimer (mng_handle mng,mng_uint32 iMsecs)
{
PluginInstance *This;
This = (PluginInstance*) mng_get_userdata(mng);
if(This->fhWnd) {
if(!SetTimer(This->fhWnd,1,(UINT)iMsecs,NULL)) {
warn(This,"Unable to create a timer for animation");
This->frozen=1;
//return MNG_FALSE;
return MNG_TRUE;
}
This->timer_set=1;
}
return MNG_TRUE;
}
static mng_bool MNGPLG_CALLBACK callback_processtext(mng_handle mng,
mng_uint8 iType, mng_pchar zKeyword, mng_pchar zText,
mng_pchar zLanguage, mng_pchar zTranslation)
{
PluginInstance *This;
int pos,i;
This = (PluginInstance*) mng_get_userdata(mng);
if(!This->textdata) {
This->textdata=(char*)malloc(MAXLEN_TEXT+10);
if(!This->textdata) return MNG_TRUE;
lstrcpy(This->textdata,"");
}
pos=lstrlen(This->textdata);
if(pos>=(MAXLEN_TEXT-10)) return MNG_TRUE;
if(pos>0) { /* separate items with a blank line */
This->textdata[pos++]='\r';
This->textdata[pos++]='\n';
This->textdata[pos++]='\r';
This->textdata[pos++]='\n';
}
for(i=0;zKeyword[i];i++) {
if(pos<MAXLEN_TEXT)
This->textdata[pos++]=zKeyword[i];
}
This->textdata[pos++]=':';
This->textdata[pos++]=' ';
for(i=0;zText[i];i++) {
if(pos<MAXLEN_TEXT) {
if(zText[i]=='\n') {
This->textdata[pos++]='\r';
}
This->textdata[pos++]=zText[i];
}
}
This->textdata[pos++]='\0';
return MNG_TRUE;
}
#ifdef MNGPLG_TRACE
static mng_bool MNGPLG_CALLBACK callback_traceproc (mng_handle mng,
mng_int32 iFuncnr,
mng_int32 iFuncseq,
mng_pchar zFuncname)
{
if(tracefile) {
fprintf(tracefile,"%d\t%d\t%d\t%s\n",(int)mng,iFuncnr,iFuncseq,zFuncname);
}
return MNG_TRUE;
}
#endif
/* ----------------------------------------------------------------------- */
static int file_exists(const char *fn)
{
HANDLE h;
// try to open with no access
h=CreateFile(fn,0,FILE_SHARE_READ,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);
if(h == INVALID_HANDLE_VALUE) { return 0; }
CloseHandle(h);
return 1;
}
static void handle_read_error(PluginInstance *This, mng_retcode rv)
{
mng_int8 iSeverity;
mng_chunkid iChunkname;
mng_uint32 iChunkseq;
mng_int32 iExtra1;
mng_int32 iExtra2;
mng_pchar zErrortext;
#ifdef MNGPLG_TRACE
fprintf(tracefile,"returned: %d\n",rv);
#endif
switch(rv) {
case MNG_NOERROR: case MNG_NEEDTIMERWAIT:
break;
case MNG_NEEDMOREDATA:
if(This->loadstate>=STATE_LOADED) {
set_error(This,"Unexpected end of file");
}
else {
This->needresume=1;
}
break;
case MNG_INVALIDSIG:
set_error(This,"Invalid or missing MNG file (maybe a 404 Not Found error)");
break;
default:
mng_getlasterror(This->mng, &iSeverity,&iChunkname,&iChunkseq,&iExtra1,
&iExtra2,&zErrortext);
if(zErrortext) {
set_error(This,"Error reported by libmng (%d)\r\n\r\n%s",(int)rv,zErrortext);
}
else {
set_error(This,"Error %d reported by libmng",(int)rv);
}
}
}
#ifdef MNGPLG_CMS
static int init_color_management(PluginInstance *This)
{
mng_set_outputsrgb(This->mng);
return 1;
}
#endif
// return 1 if okay
static int my_init_mng(PluginInstance *This)
{
mng_retcode rv;
int err;
This->mng = mng_initialize((mng_ptr)This,memallocfunc,memfreefunc,NULL);
//(mng_memalloc) (mng_memfree)
#ifdef MNGPLG_CMS
init_color_management(This);
#endif
err=0;
rv=mng_setcb_openstream (This->mng, callback_openstream ); if(rv) err++;
rv=mng_setcb_closestream (This->mng, callback_closestream ); if(rv) err++;
rv=mng_setcb_readdata (This->mng, callback_readdata ); if(rv) err++;
rv=mng_setcb_processheader (This->mng, callback_processheader); if(rv) err++;
rv=mng_setcb_getcanvasline (This->mng, callback_getcanvasline); if(rv) err++;
rv=mng_setcb_refresh (This->mng, callback_refresh ); if(rv) err++;
rv=mng_setcb_gettickcount (This->mng, callback_gettickcount ); if(rv) err++;
rv=mng_setcb_settimer (This->mng, callback_settimer ); if(rv) err++;
rv=mng_setcb_processtext (This->mng, callback_processtext ); if(rv) err++;
#ifdef MNGPLG_TRACE
rv=mng_setcb_traceproc (This->mng, callback_traceproc ); if(rv) err++;
#endif
if(err) {
warn(This,"Error setting libmng callback functions");
return 0;
}
rv= mng_set_suspensionmode (This->mng,MNG_TRUE);
if(rv) {
warn(This,"Error setting suspension mode");
return 0;
}
// if the web page author provided a bgcolor, use it
if(This->force_bgcolor) {
rv=mng_set_bgcolor (This->mng, This->bg_r, This->bg_g, This->bg_b);
}
#ifdef MNGPLG_TRACE
fprintf(tracefile,"initial readdisplay\n");
#endif
handle_read_error(This, mng_readdisplay(This->mng) );
return 1;
}
/* Global initialization */
static NPError NPP_Initialize(void)
{
if(!g_hInst) {
warn(NULL,"MNG plugin error: Cannot load resources");
}
#ifdef MNGPLG_TRACE
tracefile=fopen("c:\\temp\\mngtrace.txt","w");
#endif
#ifndef IDC_HAND
#define IDC_HAND MAKEINTRESOURCE(32649)
#endif
hcurHandNS = LoadCursor(NULL,IDC_HAND);
if(!hcurHandNS) {
hcurHandNS=LoadCursor(g_hInst,"CURHAND_NS");
}
hfontMsg=CreateFont(-12,0,0,0,FW_DONTCARE,TRUE,0,0,ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DRAFT_QUALITY,
VARIABLE_PITCH|FF_SWISS,"Arial");
return NPERR_NO_ERROR;
}
/* Global shutdown */
static void NPP_Shutdown(void)
{
#ifdef MNGPLG_TRACE
if(tracefile) {
fclose(tracefile);
tracefile=NULL;
}
#endif
if(hfontMsg) DeleteObject((HGDIOBJ)hfontMsg);
return;
}
static unsigned char gethex(const char *s)
{
int v[2];
int i;
v[0]=v[1]=0;
for(i=0;i<2;i++) {
if(s[i]>='a' && s[i]<='f') v[i]=s[i]-87;
if(s[i]>='A' && s[i]<='F') v[i]=s[i]-55;
if(s[i]>='0' && s[i]<='9') v[i]=s[i]-48;
}
return (unsigned char)(v[0]*16+v[1]);
}
static void hexcolor2rgb(const char *s, mng_uint16 *r, mng_uint16 *g, mng_uint16 *b)
{
if(lstrlen(s)!=7) return;
if(s[0]!='#') return;
(*r)= gethex(&s[1]); (*r)= ((*r)<<8)|(*r);
(*g)= gethex(&s[3]); (*g)= ((*g)<<8)|(*g);
(*b)= gethex(&s[5]); (*b)= ((*b)<<8)|(*b);
}
static void find_window_size(PluginInstance *This)
{
RECT r;
if(This->scrolling) { // make sure scrollbars exist if needed
ShowScrollBar(This->fhWnd,SB_BOTH,TRUE);
}
GetClientRect(This->fhWnd, &r);
This->windowwidth=r.right;
This->windowheight=r.bottom;
}
static void set_scrollbars(PluginInstance *This)
{
SCROLLINFO si;
int maxpos;
if(!This->scrolling) return;
if(!This->fhWnd) return;
ZeroMemory(&si,sizeof(SCROLLINFO));
si.cbSize = sizeof(SCROLLINFO);
// horizontal
if(This->lpdib) {
maxpos=This->lpdibinfo->biWidth-This->windowwidth;
if(maxpos<0) maxpos=0;
if(This->xscrollpos>maxpos) This->xscrollpos=maxpos;
if(This->xscrollpos<0) This->xscrollpos=0;
si.fMask = SIF_ALL|SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = This->lpdibinfo->biWidth -1;
si.nPage = This->windowwidth;
si.nPos = This->xscrollpos;
}
else { // no image to display
si.fMask = SIF_ALL|SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = 0;
si.nPage = 1;
si.nPos = 0;
}
SetScrollInfo(This->fhWnd,SB_HORZ,&si,TRUE);
// vertical
if(This->lpdib) {
maxpos=This->lpdibinfo->biHeight-This->windowheight;
if(maxpos<0) maxpos=0;
if(This->yscrollpos>maxpos) This->yscrollpos=maxpos;
if(This->yscrollpos<0) This->yscrollpos=0;
si.fMask = SIF_ALL|SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = This->lpdibinfo->biHeight -1;
si.nPage = This->windowheight;
si.nPos = This->yscrollpos;
}
SetScrollInfo(This->fhWnd,SB_VERT,&si,TRUE);
}
#define SCROLLLINE 40
static void scrollmsg(PluginInstance *This, UINT msg,int code, short int pos)
{
int page;
int dx, dy; // amount of scrolling
int x_orig, y_orig;
if(!This->scrolling) return;
if(!This->lpdib) return;
x_orig=This->xscrollpos;
y_orig=This->yscrollpos;
if(msg==WM_HSCROLL) {
page=This->windowwidth-15;
if(page<SCROLLLINE) page=SCROLLLINE;
switch(code) {
case SB_LINELEFT: This->xscrollpos-=SCROLLLINE; break;
case SB_LINERIGHT: This->xscrollpos+=SCROLLLINE; break;
case SB_PAGELEFT: This->xscrollpos-=page; break;
case SB_PAGERIGHT: This->xscrollpos+=page; break;
case SB_LEFT: This->xscrollpos=0; break;
case SB_RIGHT: This->xscrollpos=This->lpdibinfo->biWidth; break;
case SB_THUMBTRACK: This->xscrollpos=pos; break;
default: return;
}
set_scrollbars(This);
}
else if(msg==WM_VSCROLL) {
page=This->windowheight-15;
if(page<SCROLLLINE) page=SCROLLLINE;
switch(code) {
case SB_LINEUP: This->yscrollpos-=SCROLLLINE; break;
case SB_LINEDOWN: This->yscrollpos+=SCROLLLINE; break;
case SB_PAGEUP: This->yscrollpos-=page; break;
case SB_PAGEDOWN: This->yscrollpos+=page; break;
case SB_TOP: This->yscrollpos=0; break;
case SB_BOTTOM: This->yscrollpos=This->lpdibinfo->biHeight; break;
case SB_THUMBTRACK: This->yscrollpos=pos; break;
default: return;
}
set_scrollbars(This);
}
dx= x_orig - This->xscrollpos;
dy= y_orig - This->yscrollpos;
if(dx || dy) { // if any change
// GetClientRect(This->fhWnd,&cliprect);
ScrollWindowEx(This->fhWnd,dx,dy,NULL,NULL /*&cliprect*/,NULL,NULL,SW_INVALIDATE);
}
}
/* Once-per-instance initialization */
static NPError NPP_New(NPMIMEType pluginType,NPP instance,uint16 mode,
int16 argc,char* argn[],char* argv[],NPSavedData* saved)
{
PluginInstance* This;
int i;
if (instance == NULL) {
return NPERR_INVALID_INSTANCE_ERROR;
}
instance->pdata = calloc(sizeof(PluginInstance),1);
This = (PluginInstance*) instance->pdata;
if (This == NULL) {
return NPERR_OUT_OF_MEMORY_ERROR;
}
This->force_bgcolor=1;
This->bg_r = This->bg_g = This->bg_b = 0xffff;
/* record some info for later lookup */
This->fWindow = NULL;
This->fMode = mode;
This->fhWnd = NULL;
This->fDefaultWindowProc = NULL;
This->instance = instance; /* save the instance id for reverse lookups */
This->scrolling = (mode==NP_FULL);
This->xscrollpos = This->yscrollpos = 0;
This->windowwidth = This->windowheight = 0;
This->loadstate = STATE_INIT;
This->paintedyet = 0;
This->mng=0;
This->lpdib=NULL;
lstrcpy(This->url,"");
This->frozen=0;
This->needresume=0;
This->errorflag=0;
lstrcpy(This->errormsg,"");
This->dibsize = This->filesize = 0;
lstrcpy(This->linkurl,"");
lstrcpy(This->linktarget,"_self");
This->islink=0;
This->timer_set=0;
This->timer2_set=0;
This->dynamicmng= -1;
This->mouse_over_mng=0;
This->mouse_captured=0;
This->linkcursor = hcurHandNS;
// examine the <embed> tag arguments
for(i=0;i<argc;i++) {
if(!_stricmp(argn[i],"bgcolor")) {
This->force_bgcolor=1;
hexcolor2rgb(argv[i],&This->bg_r,&This->bg_g,&This->bg_b);
}
else if(!_stricmp(argn[i],"href")) {
lstrcpyn(This->linkurl,argv[i],MAXLEN_URL);
This->islink=1;
}
else if(!_stricmp(argn[i],"target")) {
lstrcpyn(This->linktarget,argv[i],MAXLEN_TARGET);
}
}
This->bkgdbrush=NULL;
if(This->force_bgcolor)
This->bkgdbrush=CreateSolidBrush(RGB(This->bg_r,This->bg_g,This->bg_b));
return NPERR_NO_ERROR;
}
static void BeforeDestroyWindow(PluginInstance *This)
{
if(This->timer_set) {
KillTimer(This->fhWnd,1);
This->timer_set=0;
}
if(This->timer2_set) {
KillTimer(This->fhWnd,2);
This->timer2_set=0;
}
if(This->mouse_captured) {
ReleaseCapture();
This->mouse_captured=0;
}
SetWindowLong( This->fhWnd, GWL_WNDPROC, (LONG)This->fDefaultWindowProc); // unsubclass
This->fDefaultWindowProc = NULL;
This->fhWnd = NULL;
}
static NPError NPP_Destroy(NPP instance, NPSavedData** save)
{
PluginInstance* This;
if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR;
This = (PluginInstance*) instance->pdata;
if(!This) return NPERR_INVALID_INSTANCE_ERROR;
if(This->mng) {
This->dynamicmng=0;
mng_cleanup(&This->mng);
This->mng=0;
}
if(This->lpdib) {
free(This->lpdib);
This->lpdib=NULL;
}
if(This->mngdata) {
free(This->mngdata);
This->mngdata=NULL;
This->bytesalloc=0;
}
if(This->textdata) {
free(This->textdata);
This->textdata=NULL;
}
if( This->fhWnd ) { // un-subclass the plugin window
BeforeDestroyWindow(This);
}
if(This->bkgdbrush) DeleteObject((HGDIOBJ)This->bkgdbrush);
if(This) {
if(instance->pdata) free(instance->pdata);
instance->pdata = NULL;
}
return NPERR_NO_ERROR;
}
/* Browser is providing us with a window */
static NPError NPP_SetWindow(NPP instance, NPWindow* window)
{
NPError result = NPERR_NO_ERROR;
PluginInstance* This;
if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR;
This = (PluginInstance*) instance->pdata;
if( This->fhWnd != NULL ) { /* If we already have a window... */
if( (window == NULL) || ( window->window == NULL ) ) {
/* There is now no window to use. get rid of the old
* one and exit. */
BeforeDestroyWindow(This);
This->fWindow=window;
return NPERR_NO_ERROR;
}
else if ( This->fhWnd == (HWND) window->window ) {
/* The new window is the same as the old one. Redraw and get out. */
This->fWindow=window;
InvalidateRect( This->fhWnd, NULL, FALSE );
/* UpdateWindow( This->fhWnd ); */
return NPERR_NO_ERROR;
}
else {
/* Unsubclass the old window, so that we can subclass the new
* one later. */
BeforeDestroyWindow(This);
}
}
else if( (window == NULL) || ( window->window == NULL ) ) {
/* We can just get out of here if there is no current
* window and there is no new window to use. */
This->fWindow=window;
return NPERR_NO_ERROR;
}
/* Subclass the new window so that we can begin drawing and
* receiving window messages. */
This->fDefaultWindowProc = (WNDPROC)SetWindowLong( (HWND)window->window, GWL_WNDPROC, (LONG)PluginWindowProc);
This->fhWnd = (HWND) window->window;
SetProp( This->fhWnd, gInstanceLookupString, (HANDLE)This);
This->fWindow = window;
find_window_size(This);
set_scrollbars(This);
InvalidateRect( This->fhWnd, NULL, TRUE );
UpdateWindow( This->fhWnd );
return result;
}
// browser is announcing its intent to send data to us
static NPError NPP_NewStream(NPP instance,NPMIMEType type,NPStream *stream,
NPBool seekable,uint16 *stype) {
PluginInstance* This;
if(instance==NULL)
return NPERR_INVALID_INSTANCE_ERROR;
This = (PluginInstance*) instance->pdata;
if(!This)
return NPERR_GENERIC_ERROR;
/* save the URL for later */
lstrcpyn(This->url,stream->url,MAX_PATH);
This->libmngpos=0;
This->bytesloaded=0;
This->bytesalloc=0;
This->byteswanted=0;
This->mngdata=NULL;
This->textdata=NULL;
// if we know the total length of the stream in advance
// (most of the time we will, hopefully), allocate that amount.
if(stream->end > 0) {
This->mngdata = malloc(stream->end);
This->bytesalloc= stream->end;
}
my_init_mng(This);
This->loadstate=STATE_LOADING;
(*stype)=NP_NORMAL;
return NPERR_NO_ERROR;
}
static int32 NPP_WriteReady(NPP instance, NPStream *stream)
{
/* Number of bytes ready to accept in NPP_Write() */
/* We can handle any amount, so just return some really big number. */
return (int32)0X0FFFFFFF;
}
#define ALLOC_CHUNK_SIZE 131072
static int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
{
PluginInstance* This;
#ifdef MNGPLG_TRACE
fprintf(tracefile,"NPP_Write offs=%d len=%d\n",offset,len);
#endif
if(!instance) return -1;
This = (PluginInstance*) instance->pdata;
if(!This) return -1;
if(len<1) return len;
if(offset+len > (int)This->bytesalloc) { // oops, overflowed our memory buffer
This->bytesalloc += ALLOC_CHUNK_SIZE;
if(This->mngdata) {
This->mngdata=realloc(This->mngdata, This->bytesalloc);
}
else { // first time
This->mngdata=malloc(This->bytesalloc);
}
if(!This->mngdata) {
warn(This,"Cannot allocate memory for image (%d,%d,%p",offset,len,buffer);
return -1;
}
}
// now we should have enough room to copy the data to memory
CopyMemory(&This->mngdata[offset],buffer,len);
This->bytesloaded = offset+len;
// now, check if it's time to call mng_read_resume
if(This->needresume &&
(This->bytesloaded >= (This->libmngpos + This->byteswanted)) )
{
This->needresume=0;
// handle_read_error(This, mng_read_resume(This->mng) );
#ifdef MNGPLG_TRACE
fprintf(tracefile,"NPP_Write display_resume bytesloaded=%d libmngpos=%d byteswanted=%d\n",
This->bytesloaded,This->libmngpos,This->byteswanted);
#endif
handle_read_error(This, mng_display_resume(This->mng) );
}
return len; // The number of bytes accepted -- we always accept them all.
}
/* DestroyStream gets called after the file has finished loading,
*/
static NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason)
{
PluginInstance* This;
if(!instance) return NPERR_INVALID_INSTANCE_ERROR;
This = (PluginInstance*) instance->pdata;
// if(reason==NPRES_DONE) {
This->filesize = This->bytesloaded;
This->loadstate = STATE_LOADED;
// }
if(reason!=NPRES_DONE) {
set_error(This,"Image load failed or was canceled (%d)",(int)reason);
This->needresume=0;
if(This->timer_set) { KillTimer(This->fhWnd,1); This->timer_set=0; }
return NPERR_NO_ERROR;
}
#ifdef MNGPLG_TRACE
fprintf(tracefile,"NPP_DestroyStream reason=%d needresume=%d\n",reason,This->needresume);
#endif
if(This->needresume) {
This->needresume=0;
// handle_read_error(This, mng_read_resume(This->mng) );
handle_read_error(This, mng_display_resume(This->mng) );
// This->needresume=0;
}
return NPERR_NO_ERROR;
}
static void NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname)
{
return;
}
// Print embedded plug-in (via the browser's Print command)
static void NPP_Print(NPP instance, NPPrint* printInfo)
{
PluginInstance* This;
if (instance == NULL) return;
This = (PluginInstance*) instance->pdata;
if(printInfo == NULL) {
// Some browsers (Netscape) set printInfo to NULL to tell the plugin
// to print in full page mode (this may be a bug).
// PrintFullPage(); -- full page printing not implemented
return;
}
if (printInfo->mode == NP_FULL) {
/* the plugin is full-page, and the browser is giving it a chance
* to print in the manner of its choosing */
void* platformPrint = printInfo->print.fullPrint.platformPrint;
NPBool printOne = printInfo->print.fullPrint.printOne;
/* Setting this to FALSE and returning *should* cause the browser to
* call NPP_Print again, this time with mode=NP_EMBED.
* However, that doesn't happen with any browser I've ever seen :-(.
* Instead of the following line, you will probably need to implement
* printing yourself. You also might as well set pluginPrinted to TRUE,
* though every browser I've tested ignores it. */
printInfo->print.fullPrint.pluginPrinted = FALSE;
/* or */
/* PrintFullPage();
* printInfo->print.fullPrint.pluginPrinted = TRUE; */
}
else { // we are embedded, and the browser had provided a printer context
HDC pdc;
int prevstretchmode;
NPWindow* printWindow;
if(This->loadstate < STATE_VALIDFRAME) return;
printWindow= &(printInfo->print.embedPrint.window);
/* embedPrint.platformPrint is a Windows device context in disguise */
/* The definition of NPWindow changed between API verion 0.9 and 0.11,
* increasing in size from 28 to 32 bytes. This normally makes it
* impossible for version 0.9 browsers to print version 0.11 plugins
* (because the platformPrint field ends up at the wrong offset) --
* unless the plugin takes special care to detect this situation.
* To work around it, if we are compiled with API 0.11 or higher,
* and the browser is version 0.9 or earlier, we look for the HDC
* 4 bytes earlier, at offset 28 instead of 32 (of the embedPrint
* sub-structure).
*/
if(sizeof(NPWindow)>28 && /* i.e. is plugin API >= 0.11? */
HIBYTE(g_pNavigatorFuncs->version)==0 &&
LOBYTE(g_pNavigatorFuncs->version)<=9) {
char *tmpc;
HDC *tmph;
tmpc= (char*)&(printInfo->print.embedPrint);
tmph= (HDC*)&tmpc[28];
pdc= *tmph;
}
else {
pdc= (HDC) (printInfo->print.embedPrint.platformPrint);
}
if(!This->lpdib) return;
prevstretchmode=SetStretchBltMode(pdc,COLORONCOLOR);
StretchDIBits(pdc,
printWindow->x,printWindow->y,
printWindow->width,printWindow->height, /* dest coords */
0,0,This->lpdibinfo->biWidth, This->lpdibinfo->biHeight, /* source coords */
This->lpdibbits, (LPBITMAPINFO)This->lpdib,
DIB_RGB_COLORS,SRCCOPY);
if(prevstretchmode) SetStretchBltMode(pdc,prevstretchmode);
}
return;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++
* NPP_URLNotify:
* Notifies the instance of the completion of a URL request.
+++++++++++++++++++++++++++++++++++++++++++++++++*/
static void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
{
return;
}
/**********************************************************************/
/* Try to make a filename from the url. Caller must provide fn[MAX_PATH] buffer.
* This function attempts to extract a bitmap filename from a URL,
* but if it doesn't look like it contains an appropriate name,
* it leaves it blank. */
static void url2filename(char *fn, char *url)
{
int title,ext,i;
lstrcpy(fn,"");
ext=0; /* position of the file extention */
title=0; /* position of the base filename */
for(i=0;url[i];i++) {
if(url[i]=='.') ext=i+1;
if(url[i]=='/') title=i+1;
if(url[i]=='\\') title=i+1; // handle Microsoft's bogus file: "URLs"
if(url[i]==':') title=i+1;
if(url[i]=='=') title=i+1;
}
if (!_stricmp(&url[ext],"mng") ||
!_stricmp(&url[ext],"jng") ||
!_stricmp(&url[ext],"png") )
{
lstrcpyn(fn,&url[title],MAX_PATH);
}
}
// sanitize string and escape '&'s for use in a menu
static void escapeformenu(unsigned char *s1)
{
int f, t, len;
unsigned char s2[200];
t=0;
len=lstrlen(s1); if(len>50) len=50;
for(f=0;f<len;f++) {
if(s1[f]=='&') {
s2[t++]='&';
s2[t++]='&';
}
else if(s1[f]<32) {
s2[t++]='_';
}
else {
s2[t++]=s1[f];
}
}
s2[t]='\0';
lstrcpy(s1,s2);
}
static char *get_imagetype_name(mng_imgtype t)
{
switch(t) {
case mng_it_mng: return "MNG";
case mng_it_png: return "PNG";
case mng_it_jng: return "JNG";
}
return "Unknown";
}
/* Write the image to a local file */
static void SaveImage(PluginInstance *This)
{
OPENFILENAME ofn;
char fn[MAX_PATH];
HANDLE hfile;
BOOL b;
mng_imgtype t;
DWORD byteswritten;
if(!This->mng || This->loadstate<STATE_LOADED ||
This->bytesloaded != This->filesize)
{
warn(This,"Image not loaded -- can't save");
return;
}
if(lstrlen(This->url)) {
url2filename(fn,This->url);
}
else {
lstrcpy(fn,"");
}
ZeroMemory(&ofn,sizeof(OPENFILENAME));
ofn.lStructSize=sizeof(OPENFILENAME);
ofn.hwndOwner=This->fhWnd;
ofn.nFilterIndex=1;
ofn.lpstrTitle="Save Image As...";
ofn.lpstrFile=fn;
ofn.nMaxFile=MAX_PATH;
ofn.Flags=OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT;
t=mng_get_sigtype(This->mng);
if(t==mng_it_png) {
ofn.lpstrDefExt="png"; // FIXME also give an option of MNG
ofn.lpstrFilter="PNG (*.png)\0*.png\0\0";
}
else if(t==mng_it_jng) {
ofn.lpstrDefExt="jng"; // FIXME also give an option of MNG
ofn.lpstrFilter="JNG (*.jng)\0*.jng\0\0";
}
else {
ofn.lpstrFilter="MNG (*.mng)\0*.mng\0\0";
ofn.lpstrDefExt="mng";
}
if(GetSaveFileName(&ofn)) {
// save to filename: ofn.lpstrFile
hfile=CreateFile(ofn.lpstrFile,GENERIC_WRITE,FILE_SHARE_READ,
NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if(hfile==INVALID_HANDLE_VALUE) {
warn(This,"Unable to write file");
}
else {
b=WriteFile(hfile, This->mngdata, This->filesize,
&byteswritten,NULL);
if(!b || byteswritten != This->filesize) {
warn(This,"Error writing file");
}
CloseHandle(hfile);
}
}
}
static void CopyToClipboard(PluginInstance *This,unsigned char *mem,int size,UINT format)
{
HGLOBAL hClip;
LPVOID lpClip;
if(!mem) return;
if(!OpenClipboard(NULL)) {
warn(This,"Can't open the clipboard");
return;
}
if(EmptyClipboard()) {
hClip=GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE,size);
lpClip=GlobalLock(hClip);
if(lpClip) {
CopyMemory(lpClip,mem,size);
GlobalUnlock(hClip);
if(!SetClipboardData(format,hClip)) {
warn(This,"Can't set clipboard data");
}
}
else {
warn(This,"Can't allocate memory for clipboard");
}
}
else {
warn(This,"Can't clear the clipboard");
}
CloseClipboard();
}
static void AboutDialog(PluginInstance *This)
{
DialogBoxParam(g_hInst,"ABOUTDLG",This->fhWnd,(DLGPROC)DlgProcAbout,(LPARAM)This);
}
static void PropDialog(PluginInstance *This)
{
//if(This->textdata)
DialogBoxParam(g_hInst,"PROPDLG",This->fhWnd,(DLGPROC)DlgProcProp,(LPARAM)This);
}
static void display_last_error(PluginInstance *This)
{
if(This->errorflag) {
warn(This,"%s",This->errormsg);
}
}
static void DynamicMNG_FireEvent(PluginInstance *This, mng_uint8 eventtype, POINTS pos)
{
mng_retcode r;
if(!This->mng) return;
if(This->dynamicmng == 0) return;
if(This->dynamicmng == -1) {
r=mng_status_dynamic(This->mng);
if(r==MNG_FALSE) {
return;
}
else {
This->dynamicmng=1;
}
}
mng_trapevent(This->mng, eventtype, pos.x+This->xscrollpos, pos.y+This->yscrollpos);
}
static void ContextMenu(PluginInstance *This, HWND hwnd)
{
int cmd;
HMENU menu;
POINT pt;
unsigned char buf[MAX_PATH], buf2[200];
pt.x=0; pt.y=0;
GetCursorPos(&pt);
// create context menu dynamically
menu=CreatePopupMenu();
if(This->errorflag) {
AppendMenu(menu,MF_ENABLED,ID_SHOWERROR,"SHOW ERROR MESSAGE");
AppendMenu(menu,MF_SEPARATOR,0,NULL);
}
AppendMenu(menu,(This->loadstate>=STATE_LOADED?MF_ENABLED:MF_GRAYED),ID_SAVEAS,"Save Image &As...");
AppendMenu(menu,(This->lpdib?MF_ENABLED:MF_GRAYED),ID_COPYIMAGE,"&Copy Image");
AppendMenu(menu,MF_ENABLED,ID_COPYURL,"Cop&y Image Location");
if(This->islink) {
AppendMenu(menu,MF_ENABLED,ID_COPYLINKLOC,"Copy Link Location");
}
url2filename(buf,This->url);
escapeformenu(buf);
if(lstrlen(buf)) {
wsprintf(buf2,"View Image (%s)",buf);
}
else {
wsprintf(buf2,"View Image");
}
AppendMenu(menu,MF_ENABLED,ID_VIEWIMAGE,buf2);
AppendMenu(menu,MF_SEPARATOR,0,NULL);
// AppendMenu(menu,(This->mng?MF_ENABLED:MF_GRAYED),ID_STOPANIM,"Stop Animation");
AppendMenu(menu,(This->mng?MF_ENABLED:MF_GRAYED)|
(This->frozen?MF_CHECKED:MF_UNCHECKED),ID_FREEZE,"&Freeze Animation");
// AppendMenu(menu,(This->mng?MF_ENABLED:MF_GRAYED),ID_RESTARTANIM,"Restart Animation");
AppendMenu(menu,MF_SEPARATOR,0,NULL);
AppendMenu(menu,MF_ENABLED,ID_PROPERTIES,"Properties...");
AppendMenu(menu,MF_ENABLED,ID_ABOUT,"About MNG Plug-in...");
cmd=TrackPopupMenuEx(menu, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_NONOTIFY|TPM_RETURNCMD|
TPM_RIGHTBUTTON,pt.x,pt.y,hwnd,NULL);
DestroyMenu(menu);
switch(cmd) {
case ID_STOPANIM:
if(This->mng) {
KillTimer(This->fhWnd,1);
This->timer_set=0;
mng_display_freeze(This->mng);
}
break;
case ID_FREEZE:
This->frozen = !This->frozen;
if(This->frozen) {
KillTimer(This->fhWnd,1);
This->timer_set=0;
mng_display_freeze(This->mng);
}
else {
handle_read_error(This, mng_display_resume(This->mng) );
}
break;
case ID_RESTARTANIM:
if(!This->frozen) {
KillTimer(This->fhWnd,1);
This->timer_set=0;
mng_display_freeze(This->mng);
}
This->frozen=1;
mng_display_reset(This->mng);
This->frozen=0;
handle_read_error(This, mng_display_resume(This->mng) );
break;
case ID_SAVEAS:
SaveImage(This);
break;
case ID_COPYIMAGE:
if(This->lpdib) {
CopyToClipboard(This,(unsigned char*)This->lpdib,This->dibsize,CF_DIB);
}
else {
warn(This,"No image to copy");
}
break;
case ID_COPYURL:
CopyToClipboard(This,This->url,lstrlen(This->url)+1,CF_TEXT);
break;
case ID_COPYLINKLOC:
if(This->islink) {
CopyToClipboard(This,This->linkurl,lstrlen(This->linkurl)+1,CF_TEXT);
}
break;
case ID_VIEWIMAGE:
if(lstrlen(This->url))
NPN_GetURL(This->instance,This->url,"_self");
break;
case ID_PROPERTIES:
PropDialog(This);
break;
case ID_ABOUT:
AboutDialog(This);
break;
case ID_SHOWERROR:
display_last_error(This);
break;
}
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++
* PluginWindowProc
* Handle the Windows window-event loop.
+++++++++++++++++++++++++++++++++++++++++++++++++*/
static LRESULT CALLBACK PluginWindowProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
PluginInstance* This;
HDC hdc;
RECT rect;
This = (PluginInstance*) GetProp(hWnd, gInstanceLookupString);
if(!This) return DefWindowProc( hWnd, Msg, wParam, lParam);
switch(Msg) {
case WM_ERASEBKGND:
{
HBRUSH br;
hdc= (HDC)wParam;
if(This->bkgdbrush)
br=This->bkgdbrush;
else
br=GetStockObject(GRAY_BRUSH);
GetClientRect(hWnd,&rect);
FillRect(hdc,&rect,br);
return 1;
}
case WM_HSCROLL:
case WM_VSCROLL:
scrollmsg(This,Msg,(int)(LOWORD(wParam)),(short int)(HIWORD(wParam)));
return 0;
case WM_SIZE:
find_window_size(This);
set_scrollbars(This);
return 0;
case WM_CONTEXTMENU: case WM_RBUTTONUP:
ContextMenu(This, hWnd);
return 0;
case WM_SETCURSOR:
if(LOWORD(lParam)==HTCLIENT) {
if(This->islink) {
SetCursor(This->linkcursor);
return 1;
}
}
break;
case WM_LBUTTONDOWN:
SetCapture(This->fhWnd);
This->mouse_captured=1;
if(This->dynamicmng && This->mng && !This->errorflag) {
DynamicMNG_FireEvent(This,4,MAKEPOINTS(lParam));
}
return 0;
case WM_LBUTTONUP:
{
RECT rc;
POINT pt;
if(This->mouse_captured) {
ReleaseCapture();
This->mouse_captured=0;
}
if(This->dynamicmng && This->mng && !This->errorflag) {
DynamicMNG_FireEvent(This,5,MAKEPOINTS(lParam));
}
// if mouse is not over image, don't follow links, etc.
GetWindowRect(This->fhWnd,&rc);
GetCursorPos(&pt);
if(!PtInRect(&rc,pt)) return 0;
if(This->islink) {
NPN_GetURL(This->instance,This->linkurl,This->linktarget);
return 0;
}
else if(This->errorflag) {
display_last_error(This);
}
}
return 0;
case WM_MOUSEMOVE:
if(This->dynamicmng && This->mng && This->lpdib && !This->errorflag) {
POINTS pos;
int overimage;
pos=MAKEPOINTS(lParam);
overimage=0;
if(pos.x>=0 && pos.x<This->lpdibinfo->biWidth && pos.y>=0 && pos.y<This->lpdibinfo->biHeight) {
overimage=1;
}
if(overimage) {
if(This->mouse_over_mng) {
// mouse is still over image: mouse move event
DynamicMNG_FireEvent(This,2,pos); // 2=mouse move
}
else {
// mouse wasn't over the image but now it is: mouse-enter event
DynamicMNG_FireEvent(This,1,pos); // mouse enter
}
}
else { // mouse not now over image
if(This->mouse_over_mng) { // ... but it used to be
pos.x=0; pos.y=0;
DynamicMNG_FireEvent(This,3,pos); // 3=mouse leave
}
}
This->mouse_over_mng=overimage; // remember for next time
if(This->mouse_over_mng && (This->dynamicmng==1) ) {
#define MOUSE_POLL_INTERVAL 100 // milliseconds
SetTimer(This->fhWnd,2,MOUSE_POLL_INTERVAL,NULL);
This->timer2_set=0;
}
}
return 0;
case WM_PAINT:
{
PAINTSTRUCT paintStruct;
HDC hdc;
RECT rect2;
hdc = BeginPaint( hWnd, &paintStruct );
SetWindowOrgEx(hdc,This->xscrollpos,This->yscrollpos,NULL);
GetClientRect(hWnd,&rect);
if(This) {
if(This->errorflag || !This->lpdib) {
SelectObject(hdc,hfontMsg);
Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
rect2.left=rect.left+2;
rect2.top=rect.top+2;
rect2.right=rect.right-2;
rect2.bottom=rect.bottom-2;
if(This->errorflag) {
DrawText(hdc,"MNG PLUG-IN ERROR!",-1,&rect2,DT_LEFT|DT_WORDBREAK);
}
else {
if(This->loadstate>=STATE_LOADING) {
DrawText(hdc,"MNG image loading...",-1,&rect2,DT_LEFT|DT_WORDBREAK);
}
else {
DrawText(hdc,"MNG plug-in",-1,&rect2,DT_LEFT|DT_WORDBREAK);
}
}
}
else if(This->lpdib) {
StretchDIBits(hdc,
0,0,This->lpdibinfo->biWidth,This->lpdibinfo->biHeight,
0,0,This->lpdibinfo->biWidth,This->lpdibinfo->biHeight,
&((BYTE*)(This->lpdib))[sizeof(BITMAPINFOHEADER)],
(LPBITMAPINFO)This->lpdib,DIB_RGB_COLORS,SRCCOPY);
}
}
EndPaint( hWnd, &paintStruct );
}
return 0;
case WM_TIMER:
switch(wParam) {
case 1: // the main animation timer
KillTimer(hWnd,1);
This->timer_set=0;
#ifdef MNGPLG_TRACE
fprintf(tracefile,"WM_TIMER display_resume bytesloaded=%d\n",This->bytesloaded);
#endif
if(This->mng) {
if(!This->needresume) {
handle_read_error(This, mng_display_resume(This->mng) );
}
}
return 0;
case 2: // timer for polling mouse position
{
RECT rc;
POINT pt;
POINTS pos;
GetWindowRect(hWnd,&rc);
GetCursorPos(&pt);
if(!PtInRect(&rc,pt)) {
KillTimer(hWnd,2);
pos.x=0; pos.y=0;
DynamicMNG_FireEvent(This,3,pos); // 3=mouse leave
This->mouse_over_mng=0;
}
}
return 0;
}
break;
}
/* Forward unprocessed messages on to their original destination
* (the window proc we replaced) */
return This->fDefaultWindowProc(hWnd, Msg, wParam, lParam);
}
static LRESULT CALLBACK DlgProcProp(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
char buf[4096],buf2[1024];
switch(Msg) {
case WM_INITDIALOG:
{
DWORD tabs[1];
PluginInstance *This=(PluginInstance*)lParam;
tabs[0]= 60;
SendDlgItemMessage(hWnd,IDC_IMGINFO,EM_SETTABSTOPS,(WPARAM)1,(LPARAM)tabs);
wsprintf(buf,"URL:\t%s\r\n",This->url);
if(This->lpdib) {
wsprintf(buf2,"Dimensions:\t%d x %d\r\n",This->lpdibinfo->biWidth,
This->lpdibinfo->biHeight);
lstrcat(buf,buf2);
}
if(This->lpdib && This->fMode==NP_EMBED) {
wsprintf(buf2,"Window:\t%d x %d\r\n",This->windowwidth,
This->windowheight);
lstrcat(buf,buf2);
}
if(This->filesize) {
wsprintf(buf2,"File size:\t%u bytes\r\n",This->filesize);
lstrcat(buf,buf2);
}
#ifdef _DEBUG
if(This->mngdata && This->lpdib && This->bytesalloc && This->dibsize) {
// note this doesn't include memory used by libmng
wsprintf(buf2,"Memory used:\t%u bytes\r\n",
This->bytesalloc + This->dibsize);
lstrcat(buf,buf2);
}
#endif
if(This->islink) {
wsprintf(buf2,"Link to:\t%s\r\n",This->linkurl);
lstrcat(buf,buf2);
if(strcmp(This->linktarget,"_self")) {
wsprintf(buf2,"Link target:\t%s\r\n",This->linktarget);
lstrcat(buf,buf2);
}
}
if(This->loadstate >= STATE_VALIDFRAME) {
wsprintf(buf2,"Signature:\t%s\r\n",get_imagetype_name(mng_get_sigtype(This->mng)));
lstrcat(buf,buf2);
wsprintf(buf2,"Image type:\t%s\r\n",get_imagetype_name(mng_get_imagetype(This->mng)));
lstrcat(buf,buf2);
wsprintf(buf2,"Simplicity:\t0x%08x\r\n",mng_get_simplicity(This->mng));
lstrcat(buf,buf2);
wsprintf(buf2,"Frame count:\t%u\r\n",mng_get_framecount(This->mng));
lstrcat(buf,buf2);
wsprintf(buf2,"Layer count:\t%u\r\n",mng_get_layercount(This->mng));
lstrcat(buf,buf2);
wsprintf(buf2,"Play time:\t%u\r\n",mng_get_playtime(This->mng));
lstrcat(buf,buf2);
}
SetDlgItemText(hWnd,IDC_IMGINFO,buf);
if(This->textdata)
SetDlgItemText(hWnd,IDC_MNGTEXT,This->textdata);
}
return(TRUE);
case WM_CLOSE:
EndDialog(hWnd,0);
return(TRUE);
case WM_COMMAND:
switch(wParam) {
case IDOK:
case IDCANCEL:
EndDialog(hWnd,0);
return(TRUE);
}
}
return(FALSE);
}
static LRESULT CALLBACK DlgProcAbout(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
char buf[4096],buf2[1024],buf3[300];
switch(Msg) {
case WM_INITDIALOG:
{
//DWORD tabs[1];
PluginInstance *This=(PluginInstance*)lParam;
//tabs[0]= 60;
//SendDlgItemMessage(hWnd,IDC_IMGINFO,EM_SETTABSTOPS,(WPARAM)1,(LPARAM)tabs);
wsprintf(buf,"MNGPLG Plug-in, Version %s\r\n%s"
#ifdef _DEBUG
" DEBUG BUILD"
#endif
"\r\nCopyright (C) 2000-2002 by Jason Summers\r\n\r\n",MNGPLGVERS,__DATE__);
wsprintf(buf2,"Based on libmng by Gerard Juyn.\r\n");
lstrcat(buf,buf2);
wsprintf(buf2,"libmng version: %s\r\n\r\n",mng_version_text());
lstrcat(buf,buf2);
wsprintf(buf2,"Uses the zlib compression library.\r\n");
lstrcat(buf,buf2);
wsprintf(buf2,"zlib version: %s\r\n\r\n",zlibVersion());
lstrcat(buf,buf2);
wsprintf(buf2,"This software is based in part on the work of the "
"Independent JPEG Group.\r\n");
lstrcat(buf,buf2);
// This really only gives the version of the libjpeg header used when
// compiling this plugin, but I don't know how to query libjpeg for its
// version.
wsprintf(buf2,"IJG JPEG library version: %s\r\n%s\r\n\r\n",JVERSION,JCOPYRIGHT);
lstrcat(buf,buf2);
#ifdef MNGPLG_CMS
wsprintf(buf2,"Uses the lcms color management library by Mart Maria. "
"lcms is distributed under the terms of the GNU LESSER GENERAL PUBLIC LICENSE. "
"See the file COPYING-LCMS.\r\n\r\n");
lstrcat(buf,buf2);
#endif
if(GetModuleFileName(g_hInst,buf3,260)) {
wsprintf(buf2,"MNGPLG location: %s\r\n",buf3);
lstrcat(buf,buf2);
}
SetDlgItemText(hWnd,IDC_PRGINFO,buf);
}
return(TRUE);
case WM_CLOSE:
EndDialog(hWnd,0);
return(TRUE);
case WM_COMMAND:
switch(wParam) {
case IDOK:
case IDCANCEL:
EndDialog(hWnd,0);
return(TRUE);
}
}
return(FALSE);
}
/////////////////////
///////////////////// low-level plug-in NPAPI functions
static JRIGlobalRef Private_GetJavaClass(void)
{
return NULL;
}
NPError WINAPI NP_GetEntryPoints(NPPluginFuncs* pFuncs)
{
if(!pFuncs) return NPERR_INVALID_FUNCTABLE_ERROR;
pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
pFuncs->newp = NPP_New;
pFuncs->destroy = NPP_Destroy;
pFuncs->setwindow = NPP_SetWindow;
pFuncs->newstream = NPP_NewStream;
pFuncs->destroystream = NPP_DestroyStream;
pFuncs->asfile = NPP_StreamAsFile;
pFuncs->writeready = NPP_WriteReady;
pFuncs->write = NPP_Write;
pFuncs->print = NPP_Print;
pFuncs->event = NULL;
g_pluginFuncs = pFuncs;
return NPERR_NO_ERROR;
}
NPError WINAPI NP_Initialize(NPNetscapeFuncs* pFuncs)
{
int navMinorVers;
if(!pFuncs) return NPERR_INVALID_FUNCTABLE_ERROR;
g_pNavigatorFuncs = pFuncs; // save it for future reference
if(HIBYTE(pFuncs->version) > NP_VERSION_MAJOR)
return NPERR_INCOMPATIBLE_VERSION_ERROR;
navMinorVers = g_pNavigatorFuncs->version & 0xFF;
if(navMinorVers>=NPVERS_HAS_NOTIFICATION)
g_pluginFuncs->urlnotify = NPP_URLNotify;
if( navMinorVers>=NPVERS_HAS_LIVECONNECT)
g_pluginFuncs->javaClass = Private_GetJavaClass();
return NPP_Initialize();
}
NPError WINAPI NP_Shutdown()
{
NPP_Shutdown();
g_pNavigatorFuncs = NULL;
return NPERR_NO_ERROR;
}