1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is Mozilla Communicator client code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998
20
* the Initial Developer. All Rights Reserved.
23
* Josh Aas <josh@mozilla.com>
25
* Alternatively, the contents of this file may be used under the terms of
26
* either the GNU General Public License Version 2 or later (the "GPL"), or
27
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
* in which case the provisions of the GPL or the LGPL are applicable instead
29
* of those above. If you wish to allow use of your version of this file only
30
* under the terms of either the GPL or the LGPL, and not to allow others to
31
* use your version of this file under the terms of the MPL, indicate your
32
* decision by deleting the provisions above and replace them with the notice
33
* and other provisions required by the GPL or the LGPL. If you do not delete
34
* the provisions above, a recipient may use your version of this file under
35
* the terms of any one of the MPL, the GPL or the LGPL.
37
* ***** END LICENSE BLOCK ***** */
42
#include <Carbon/Carbon.h>
43
#include <CoreFoundation/CoreFoundation.h>
49
#define PLUGINFINDER_COMMAND_BEGINNING "javascript:window.open(\""
50
#define PLUGINFINDER_COMMAND_END "\",\"plugin\",\"toolbar=no,status=no,resizable=no,scrollbars=no,height=252,width=626\");"
51
#define PLUGINFINDER_COMMAND_END2 "\",\"plugin\",\"toolbar=no,status=no,resizable=yes,scrollbars=yes,height=252,width=626\");"
54
// Instance state information about the plugin.
58
enum HiliteState { kUnhilited = 0, kHilited = 1 };
60
static NPError Initialize();
61
static void Shutdown();
63
// no ctor because CPlugin is allocated and constructed by hand.
64
// ideally, this should use placement |new|.
66
void Constructor(NPP instance, NPMIMEType type, uint16 mode, int16 argc, char* argn[], char* argv[]);
69
void SetWindow(NPWindow* window);
70
void Print(NPPrint* printInfo);
71
Boolean HandleEvent(EventRecord*);
74
void Draw(HiliteState hilite);
75
void DrawString(const unsigned char* text, short width, short height, short centerX, Rect drawRect);
81
void DetermineURL(int16 argc, char* argn[], char* argv[]);
82
char* MakeDefaultURL(void);
83
void AddMimeTypeToList(StringPtr cTypeString);
84
Boolean CheckMimeTypes();
86
void RefreshPluginPage();
91
Boolean IsPluginHidden(int16 argc, char* argn[], char* argv[]);
94
static CIconHandle sIconHandle;
95
static CursHandle sHandCursor;
96
static char* sAltText;
97
static char* sInstallCommand;
98
static char* sDefaultPage;
99
static char* sRefreshText;
100
static char* sJavaScriptPage;
109
NPBool m_bJavaScript;
116
Boolean fUserInstalledPlugin;
117
Boolean fHiddenPlugin;
118
Boolean fAskedLoadURL;
121
CIconHandle CPlugin::sIconHandle = NULL;
122
CursHandle CPlugin::sHandCursor = NULL;
123
char* CPlugin::sAltText = NULL;
124
char* CPlugin::sInstallCommand = NULL;
125
char* CPlugin::sDefaultPage = NULL;
126
char* CPlugin::sRefreshText = NULL;
127
char* CPlugin::sJavaScriptPage = NULL;
129
extern short gResFile;
132
const short rBrokenPluginIcon = 326;
135
const short rHandCursor = 128;
138
const short rDefaultPluginURL = 128;
139
const short rAltTextString = 129;
140
const short rJavaScriptInstallCommand = 130;
141
const short rRefreshTextString = 131;
142
const short rJavaScriptPageURL = 132;
145
const short rTypeListStrings = 129;
147
static const char szPluginFinderCommandBeginning[] = PLUGINFINDER_COMMAND_BEGINNING;
148
static const char szPluginFinderCommandEnd[] = PLUGINFINDER_COMMAND_END;
151
NPError NPP_Initialize(void)
153
return CPlugin::Initialize();
157
void NPP_Shutdown(void)
163
NPError NPP_New(NPMIMEType type, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData*)
166
return NPERR_INVALID_INSTANCE_ERROR;
168
CPlugin* This = (CPlugin*) (char*)NPN_MemAlloc(sizeof(CPlugin));
169
instance->pdata = This;
171
This->Constructor(instance, type, mode, argc, argn, argv);
172
return NPERR_NO_ERROR;
175
return NPERR_OUT_OF_MEMORY_ERROR;
181
NPP_Destroy(NPP instance, NPSavedData** /*save*/)
184
return NPERR_INVALID_INSTANCE_ERROR;
186
CPlugin* This = (CPlugin*) instance->pdata;
190
instance->pdata = NULL;
193
return NPERR_NO_ERROR;
197
NPError NPP_SetWindow(NPP instance, NPWindow* window)
200
return NPERR_INVALID_INSTANCE_ERROR;
202
CPlugin* This = (CPlugin*) instance->pdata;
204
This->SetWindow(window);
206
return NPERR_NO_ERROR;
211
NPP_NewStream(NPP instance,
213
NPStream* /*stream*/,
218
return NPERR_INVALID_INSTANCE_ERROR;
220
return NPERR_NO_ERROR;
224
int32 STREAMBUFSIZE = 0X0FFFFFFF; // If we are reading from a file in NPAsFile
225
// mode so we can take any size stream in our
226
// write call (since we ignore it)
230
NPP_WriteReady(NPP /*instance*/, NPStream* /*stream*/)
232
return STREAMBUFSIZE; // Number of bytes ready to accept in NPP_Write()
237
NPP_Write(NPP /*instance*/, NPStream* /*stream*/, int32 /*offset*/, int32 len, void* /*buffer*/)
239
return len; // The number of bytes accepted
244
NPP_DestroyStream(NPP instance, NPStream* /*stream*/, NPError /*reason*/)
247
return NPERR_INVALID_INSTANCE_ERROR;
249
return NPERR_NO_ERROR;
254
NPP_StreamAsFile(NPP /*instance*/, NPStream */*stream*/, const char* /*fname*/)
260
NPP_Print(NPP instance, NPPrint* printInfo)
266
if (printInfo->mode == NP_FULL) {
267
printInfo->print.fullPrint.pluginPrinted = FALSE; // Do the default
269
else { // If not fullscreen, we must be embedded
270
CPlugin* This = (CPlugin*)instance->pdata;
272
This->Print(printInfo);
279
int16 NPP_HandleEvent(NPP instance, void* event)
282
CPlugin* This = (CPlugin*) instance->pdata;
284
return This->HandleEvent((EventRecord*) event);
291
void NPP_URLNotify(NPP /*instance*/, const char* /*url*/, NPReason /*reason*/, void* /*notifyData*/)
297
jref NPP_GetJavaClass(void)
304
NPError CPlugin::Initialize()
307
short saveResFile = CurResFile();
309
UseResFile(gResFile);
312
CPlugin::sIconHandle = GetCIcon(rBrokenPluginIcon);
313
CPlugin::sHandCursor = GetCursor(rHandCursor);
315
// Get "alt text" string
316
string = Get1Resource('STR ', rAltTextString);
317
if (string && *string) {
318
short stringLen = (*string)[0];
319
CPlugin::sAltText = (char*)NPN_MemAlloc(stringLen +1);
320
if (CPlugin::sAltText) {
323
while (src <= stringLen)
324
CPlugin::sAltText[dest++] = (*string)[src++];
325
CPlugin::sAltText[dest++] = 0;
328
ReleaseResource(string);
330
// Get "refresh text" string
331
string = Get1Resource('STR ', rRefreshTextString);
332
if (string && *string) {
333
short stringLen = (*string)[0];
334
CPlugin::sRefreshText = (char*)NPN_MemAlloc(stringLen + 1);
335
if (CPlugin::sRefreshText) {
338
while (src <= stringLen)
339
CPlugin::sRefreshText[dest++] = (*string)[src++];
340
CPlugin::sRefreshText[dest++] = 0;
343
ReleaseResource(string);
345
// Get JavaScript install command string
346
string = Get1Resource('STR ', rJavaScriptInstallCommand);
347
if (string && *string) {
348
short stringLen = (*string)[0];
349
CPlugin::sInstallCommand = (char*)NPN_MemAlloc(stringLen + 1);
350
if (CPlugin::sInstallCommand) {
353
while (src <= stringLen)
354
CPlugin::sInstallCommand[dest++] = (*string)[src++];
355
CPlugin::sInstallCommand[dest++] = 0;
358
ReleaseResource(string);
360
// Get default plug-in page URL
361
string = Get1Resource('STR ', rDefaultPluginURL);
362
if (string && *string) {
363
short stringLen = (*string)[0];
364
CPlugin::sDefaultPage = (char*)NPN_MemAlloc(stringLen + 1);
365
if (CPlugin::sDefaultPage) {
368
while (src <= stringLen)
369
CPlugin::sDefaultPage[dest++] = (*string)[src++];
370
CPlugin::sDefaultPage[dest++] = 0;
373
ReleaseResource(string);
375
// Get javascript plug-in page URL
376
string = Get1Resource('STR ', rJavaScriptPageURL);
377
if (string && *string) {
378
short stringLen = (*string)[0];
379
CPlugin::sJavaScriptPage = (char*)NPN_MemAlloc(stringLen + 1);
380
if (CPlugin::sJavaScriptPage) {
383
while (src <= stringLen)
384
CPlugin::sJavaScriptPage[dest++] = (*string)[src++];
385
CPlugin::sJavaScriptPage[dest++] = 0;
388
ReleaseResource(string);
390
UseResFile(saveResFile);
392
return NPERR_NO_ERROR;
396
void CPlugin::Shutdown()
398
if (CPlugin::sIconHandle)
399
::ReleaseResource((Handle) CPlugin::sIconHandle);
400
if (CPlugin::sHandCursor)
401
::ReleaseResource((Handle) CPlugin::sHandCursor);
403
if (CPlugin::sAltText)
404
NPN_MemFree(CPlugin::sAltText);
405
if (CPlugin::sInstallCommand)
406
NPN_MemFree(CPlugin::sInstallCommand);
407
if (CPlugin::sDefaultPage)
408
NPN_MemFree(CPlugin::sDefaultPage);
409
if (CPlugin::sRefreshText)
410
NPN_MemFree(CPlugin::sRefreshText);
414
void CPlugin::Constructor(NPP instance, NPMIMEType type, uint16 mode, int16 argc, char* argn[], char* argv[])
419
fInstance = instance;
420
fMode = mode; // Mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h)
421
fAskedLoadURL = false;
422
fUserInstalledPlugin = false;
424
// Save a copy of our mime type string
425
short typeLength = strlen(type);
426
fType = (char*)NPN_MemAlloc(typeLength+1);
430
// Make a handy region for use in FocusDraw
431
fSaveClip = NewRgn();
433
// determine if the plugin is specified as HIDDEN
434
fHiddenPlugin = IsPluginHidden(argc, argn, argv);
436
// Get some information about our environment
437
NPN_GetValue(fInstance, NPNVisOfflineBool, (void *)&m_bOffline);
438
NPN_GetValue(fInstance, NPNVjavascriptEnabledBool, (void *)&m_bJavaScript);
440
// Figure out what URL we will go to
441
DetermineURL(argc, argn, argv);
445
void CPlugin::Destructor()
448
DisposeRgn(fSaveClip);
453
NPN_MemFree(fFileURL);
455
NPN_MemFree(fPageURL);
459
void CPlugin::SetWindow(NPWindow* window)
465
// To print, we need to retrieve the printing window from the printInfo,
466
// temporarily make it our window, draw into it, and restore the window.
467
void CPlugin::Print(NPPrint* printInfo)
469
NPWindow* printWindow = &(printInfo->print.embedPrint.window);
471
NPWindow* oldWindow = fWindow;
472
fWindow = printWindow;
483
Boolean CPlugin::HandleEvent(EventRecord* ev)
485
Boolean eventHandled = false;
502
case NPEventType_AdjustCursorEvent:
503
if (CPlugin::sHandCursor)
504
SetCursor(*CPlugin::sHandCursor);
505
if (fUserInstalledPlugin) {
506
if (CPlugin::sRefreshText)
507
NPN_Status(fInstance, CPlugin::sRefreshText);
510
if (CPlugin::sAltText)
511
NPN_Status(fInstance, CPlugin::sAltText);
517
// NOTE: We have to wait until idle time
518
// to ask the user if they want to visit
519
// the URL to avoid reentering XP code.
520
if (!fAskedLoadURL) {
521
if (CheckMimeTypes())
523
fAskedLoadURL = true;
534
void CPlugin::Draw(HiliteState hilite)
537
SInt32 height = fWindow->height;
538
SInt32 width = fWindow->width;
539
SInt32 centerX = (width) >> 1;
540
SInt32 centerY = (height) >> 1;
542
RGBColor black = { 0x0000, 0x0000, 0x0000 };
543
RGBColor white = { 0xFFFF, 0xFFFF, 0xFFFF };
544
RGBColor hiliteColor = { 0x0000, 0x0000, 0x0000 };
549
drawRect.bottom = height;
550
drawRect.right = width;
552
if (height < 4 && width < 4)
556
RGBForeColor(&black);
557
RGBBackColor(&white);
560
FillRect(&drawRect, GetQDGlobalsWhite(&qdWhite));
562
if (hilite == kHilited) {
563
hiliteColor.red = 0xFFFF;
564
transform = ttSelected;
567
hiliteColor.blue = 0xFFFF;
571
RGBForeColor(&hiliteColor);
572
FrameRect(&drawRect);
574
if (height > 32 && width > 32 && CPlugin::sIconHandle) {
575
drawRect.top = centerY - 16;
576
drawRect.bottom = centerY + 16;
577
drawRect.left = centerX - 16;
578
drawRect.right = centerX + 16;
579
PlotCIconHandle(&drawRect, atAbsoluteCenter, transform, CPlugin::sIconHandle);
582
if (fUserInstalledPlugin) {
583
pTheText = (unsigned char*)CPlugin::sRefreshText;
586
pTheText = (unsigned char*)CPlugin::sAltText;
588
DrawString(pTheText, width, height, centerX, drawRect);
592
// Track the click in our plugin by drawing the icon enabled or disabled
593
// as the user moves the mouse in and out with the button held down. If
594
// they let up the mouse while still inside, get the URL.
595
void CPlugin::MouseDown()
599
Boolean inside = true;
601
// evil CPU-hogging loop on Mac OS X!
602
while (StillDown()) {
604
GetMouse(&localMouse);
605
Boolean insideNow = ::PtInRect(localMouse, &fRevealedRect);
607
if (insideNow != inside) {
608
Draw(insideNow ? kHilited : kUnhilited);
615
if (!fUserInstalledPlugin)
626
Boolean CPlugin::FocusDraw()
631
NP_Port* npport = (NP_Port*) fWindow->window;
632
CGrafPtr ourPort = npport->port;
634
if (fWindow->clipRect.left < fWindow->clipRect.right) {
636
SetPort((GrafPtr) ourPort);
638
GetPortBounds(ourPort, &portRect);
639
fSavePortTop = portRect.top;
640
fSavePortLeft = portRect.left;
643
fRevealedRect.top = fWindow->clipRect.top + npport->porty;
644
fRevealedRect.left = fWindow->clipRect.left + npport->portx;
645
fRevealedRect.bottom = fWindow->clipRect.bottom + npport->porty;
646
fRevealedRect.right = fWindow->clipRect.right + npport->portx;
647
SetOrigin(npport->portx, npport->porty);
648
ClipRect(&fRevealedRect);
658
void CPlugin::RestoreDraw()
660
SetOrigin(fSavePortLeft, fSavePortTop);
666
// Get a URL from either the parameters passed from the EMBED.
667
// Append "?" and our mime type and save for later use.
668
void CPlugin::DetermineURL(int16 argc, char* argn[], char* argv[])
671
SInt32 additionalLength = 0;
674
// Appended to the URL will be a "?" and the mime type of this instance. This lets the server
675
// do something intelligent with a CGI script.
678
additionalLength += (strlen(fType) + 1); // Add 1 for '?'
680
// The page designer can specify a URL where the plugin for this type can be downloaded. Here
681
// we scan the arguments for this attribute and save it away if we
682
// find it for later use by LoadPluginURL().
683
for (i = 0; i < argc; i++) {
684
if ((strcasecmp(argn[i], "PLUGINSPAGE") == 0) || (strcasecmp(argn[i], "CODEBASE") == 0)) {
686
fPageURL = (char*)NPN_MemAlloc(strlen(url) + 1 + additionalLength); // Add 1 for '\0'
688
if (additionalLength > 0) {
689
sprintf(fPageURL, "%s?%s", url, fType);
692
strcpy(fPageURL, url);
696
} else if ((strcasecmp(argn[i], "PLUGINURL") == 0) || (strcasecmp(argn[i], "CLASSID") == 0)) {
698
if (CPlugin::sInstallCommand) {
699
// Allocate a new string
700
fFileURL = (char*)NPN_MemAlloc(strlen(CPlugin::sInstallCommand) + 1 + strlen(url));
702
sprintf(fFileURL, CPlugin::sInstallCommand, url);
711
// Get a URL from our resources. Append "?" and our mime type and save for later use.
712
char *CPlugin::MakeDefaultURL(void)
714
char *pDefURL = NULL;
715
SInt32 additionalLength = 0;
717
// Appended to the URL will be a "?" and the mime type of this instance. This lets the server
718
// do something intelligent with a CGI script.
721
additionalLength += (strlen(fType) + 1); // Add 1 for '?'
723
if (!m_bJavaScript) {
724
if (CPlugin::sDefaultPage) {
725
pDefURL = (char*)NPN_MemAlloc(strlen(CPlugin::sDefaultPage) + 1 + additionalLength);
727
if (additionalLength > 0) {
728
sprintf(pDefURL, "%s?%s", CPlugin::sDefaultPage, fType);
731
strcpy(pDefURL, CPlugin::sDefaultPage);
737
if (CPlugin::sJavaScriptPage) {
738
pDefURL = (char*)NPN_MemAlloc(strlen(szPluginFinderCommandBeginning) +
739
strlen(CPlugin::sJavaScriptPage) +
740
additionalLength + strlen(szPluginFinderCommandEnd) + 1);
742
sprintf(pDefURL, "%s%s%s%s", szPluginFinderCommandBeginning,
743
CPlugin::sJavaScriptPage, fType, szPluginFinderCommandEnd);
752
// Check the mime type of this instance against our list
753
// of types we�ve seen before. If we find our type in the
754
// list, return false; otherwise, return true.
757
// integer = $$Countof(StringArray);
758
// array StringArray {
761
void CPlugin::AddMimeTypeToList(StringPtr cTypeString)
763
CFStringRef pluginKey = CFSTR("DefaultPluginSeenTypes"); // don't release this
764
CFStringRef mimeType = ::CFStringCreateWithPascalString(kCFAllocatorDefault, cTypeString, kCFStringEncodingASCII);
765
CFArrayRef prefsList = (CFArrayRef)::CFPreferencesCopyAppValue(pluginKey, kCFPreferencesCurrentApplication);
766
Boolean foundType = false;
769
CFStringRef stringArray[1];
770
stringArray[0] = mimeType;
772
prefsList = ::CFArrayCreate(kCFAllocatorDefault, (const void **)stringArray, 1, &kCFTypeArrayCallBacks);
774
::CFPreferencesSetAppValue(pluginKey, prefsList, kCFPreferencesCurrentApplication);
775
::CFRelease(prefsList);
779
if (::CFGetTypeID(prefsList) == ::CFArrayGetTypeID()) {
780
// first make sure it's not in the list
781
CFIndex count = ::CFArrayGetCount(prefsList);
782
for (CFIndex i = 0; i < count; i ++) {
783
CFStringRef item = (CFStringRef)::CFArrayGetValueAtIndex(prefsList, i); // does not retain
784
if (item && (::CFGetTypeID(item) == ::CFStringGetTypeID()) &&
785
(::CFStringCompareWithOptions(item, mimeType,
786
CFRangeMake(0, ::CFStringGetLength(item)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
792
if (!foundType && !fHiddenPlugin) {
793
CFMutableArrayRef typesArray = ::CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, (CFArrayRef)prefsList);
795
::CFArrayAppendValue(typesArray, mimeType);
796
::CFPreferencesSetAppValue(pluginKey, typesArray, kCFPreferencesCurrentApplication);
800
::CFRelease(prefsList);
802
::CFRelease(mimeType);
806
// Check the mime type of this instance against our list
807
// of types we�ve seen before. If we find our type in the
808
// list, return false; otherwise, return true.
811
// integer = $$Countof(StringArray);
812
// array StringArray {
815
Boolean CPlugin::CheckMimeTypes()
817
Boolean failedToFind = true;
819
CFStringRef pluginKey = CFSTR("DefaultPluginSeenTypes"); // don't release this
820
CFStringRef mimeType = ::CFStringCreateWithCString(kCFAllocatorDefault, fType, kCFStringEncodingASCII);
821
CFArrayRef prefsList = (CFArrayRef)::CFPreferencesCopyAppValue(pluginKey, kCFPreferencesCurrentApplication);
823
if (::CFGetTypeID(prefsList) == ::CFArrayGetTypeID()) {
824
CFIndex count = ::CFArrayGetCount(prefsList);
825
for (CFIndex i = 0; i < count; i ++) {
826
CFStringRef item = (CFStringRef)::CFArrayGetValueAtIndex(prefsList, i); // does not retain
828
(::CFGetTypeID(item) == ::CFStringGetTypeID()) &&
829
(::CFStringCompareWithOptions(item, mimeType,
830
CFRangeMake(0, ::CFStringGetLength(item)), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
831
failedToFind = false;
836
::CFRelease(prefsList);
838
::CFRelease(mimeType);
843
void CPlugin::AskAndLoadURL()
850
// Convert the mime-type C string to a Pascal string.
851
dwLen = strlen(fType);
852
if (dwLen > 255) { // don't blow out the Str255
855
BlockMoveData(fType, &ourType[1], dwLen);
858
// NOTE: We need to set the cursor because almost always we will have set it to the
859
// hand cursor before we get here.
861
SetCursor(GetQDGlobalsArrow(&qdArrow));
863
// Now that we�ve queried the user about this mime type,
864
// add it to our list so we won�t bug them again.
865
AddMimeTypeToList(ourType);
867
// If the user clicked "Get the Plug-in", either execute the
868
// JavaScript file-installation URL, or ask Netscape to open
869
// a new window with the page URL. The title of the window
870
// is arbitrary since it has nothing to do with the actual
871
// window title shown to the user (it�s only used internally).
872
NPN_PushPopupsEnabledState(fInstance, true);
875
(void) NPN_GetURL(fInstance, fFileURL, "_current");
878
NPN_GetURL(fInstance, fPageURL, "_blank");
881
pTheURL = MakeDefaultURL();
883
NPN_GetURL(fInstance, pTheURL, "_blank");
885
NPN_GetURL(fInstance, pTheURL, NULL);
886
NPN_MemFree(pTheURL);
889
NPN_PopPopupsEnabledState(fInstance);
891
fUserInstalledPlugin = true;
900
void CPlugin::RefreshPluginPage()
902
(void)NPN_GetURL(fInstance, "javascript:navigator.plugins.refresh(true);", "_self");
906
void CPlugin::DrawString(const unsigned char* text, short width, short height, short centerX, Rect drawRect)
908
short length, textHeight, textWidth;
913
length = strlen((char*)text);
920
GetFontInfo(&fontInfo);
922
textHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
923
textWidth = TextWidth(text, 0, length);
925
if (width > textWidth && height > textHeight + 32) {
926
MoveTo(centerX - (textWidth >> 1), drawRect.bottom + textHeight);
927
DrawText(text, 0, length);
932
Boolean CPlugin::IsPluginHidden(int16 argc, char* argn[], char* argv[])
934
for (int i = 0; i < argc; i++) {
935
if (!strcasecmp(argn[i], "HIDDEN")) {
936
if (!strcasecmp(argv[i], "TRUE"))