/* Copyright (C) 2003-2013 Runtime Revolution Ltd.
This file is part of LiveCode.
LiveCode is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License v3 as published by the Free
Software Foundation.
LiveCode 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 LiveCode. If not see . */
#include "prefix.h"
#include "core.h"
#include "globdefs.h"
#include "filedefs.h"
#include "objdefs.h"
#include "parsedef.h"
#include "mcio.h"
#include "execpt.h"
#include "dispatch.h"
#include "stack.h"
#include "tooltip.h"
#include "card.h"
#include "field.h"
#include "button.h"
#include "image.h"
#include "aclip.h"
#include "vclip.h"
#include "stacklst.h"
#include "mcerror.h"
#include "hc.h"
#include "util.h"
#include "param.h"
#include "debug.h"
#include "statemnt.h"
#include "funcs.h"
#include "magnify.h"
#include "sellst.h"
#include "undolst.h"
#include "styledtext.h"
#include "external.h"
#include "osspec.h"
#include "flst.h"
#include "globals.h"
#include "license.h"
#include "mode.h"
#include "redraw.h"
#include "printer.h"
#include "font.h"
#include "stacksecurity.h"
#include "scriptpt.h"
#include "graphics_util.h"
#define UNLICENSED_TIME 6.0
#ifdef _DEBUG_MALLOC_INC
#define LICENSED_TIME 1.0
#else
#define LICENSED_TIME 3.0
#endif
MCImage *MCDispatch::imagecache;
#define VERSION_OFFSET 11
#define HEADERSIZE 255
static char header[HEADERSIZE] = "#!/bin/sh\n# MetaCard 2.4 stack\n# The following is not ASCII text,\n# so now would be a good time to q out of more\f\nexec mc $0 \"$@\"\n";
#define NEWHEADERSIZE 8
static const char *newheader = "REVO2700";
static const char *newheader5500 = "REVO5500";
MCDispatch::MCDispatch()
{
license = NULL;
stacks = NULL;
fonts = NULL;
setname_cstring("dispatch");
handling = False;
menu = NULL;
panels = NULL;
startdir = NULL;
enginedir = NULL;
flags = 0;
m_drag_source = false;
m_drag_target = false;
m_drag_end_sent = false;
m_externals = nil;
m_transient_stacks = nil;
}
MCDispatch::~MCDispatch()
{
delete license;
while (stacks != NULL)
{
MCStack *sptr = stacks->prev()->remove(stacks);
delete sptr;
}
while (imagecache != NULL)
{
MCImage *iptr = imagecache->remove(imagecache);
delete iptr;
}
delete fonts;
delete startdir;
delete enginedir;
delete m_externals;
}
bool MCDispatch::isdragsource(void)
{
return m_drag_source;
}
bool MCDispatch::isdragtarget(void)
{
return m_drag_target;
}
Exec_stat MCDispatch::getprop(uint4 parid, Properties which, MCExecPoint &ep, Boolean effective)
{
switch (which)
{
#ifdef /* MCDispatch::getprop */ LEGACY_EXEC
case P_BACK_PIXEL:
ep.setint(MCscreen->background_pixel.pixel & 0xFFFFFF);
return ES_NORMAL;
case P_TOP_PIXEL:
ep.setint(MCscreen->white_pixel.pixel & 0xFFFFFF);
return ES_NORMAL;
case P_HILITE_PIXEL:
case P_FORE_PIXEL:
case P_BORDER_PIXEL:
case P_BOTTOM_PIXEL:
case P_SHADOW_PIXEL:
case P_FOCUS_PIXEL:
ep.setint(MCscreen->black_pixel.pixel & 0xFFFFFF);
return ES_NORMAL;
case P_BACK_COLOR:
case P_HILITE_COLOR:
ep.setstaticcstring("white");
return ES_NORMAL;
case P_FORE_COLOR:
case P_BORDER_COLOR:
case P_TOP_COLOR:
case P_BOTTOM_COLOR:
case P_SHADOW_COLOR:
case P_FOCUS_COLOR:
ep.setstaticcstring("black");
return ES_NORMAL;
case P_FORE_PATTERN:
case P_BACK_PATTERN:
case P_HILITE_PATTERN:
case P_BORDER_PATTERN:
case P_TOP_PATTERN:
case P_BOTTOM_PATTERN:
case P_SHADOW_PATTERN:
case P_FOCUS_PATTERN:
ep.clear();
return ES_NORMAL;
case P_TEXT_ALIGN:
ep.setstaticcstring(MCleftstring);
return ES_NORMAL;
case P_TEXT_FONT:
ep.setstaticcstring(DEFAULT_TEXT_FONT);
return ES_NORMAL;
case P_TEXT_HEIGHT:
ep.setint(heightfromsize(DEFAULT_TEXT_SIZE));
return ES_NORMAL;
case P_TEXT_SIZE:
ep.setint(DEFAULT_TEXT_SIZE);
return ES_NORMAL;
case P_TEXT_STYLE:
ep.setstaticcstring(MCplainstring);
return ES_NORMAL;
#endif /* MCDispatch::getprop */
default:
MCeerror->add(EE_OBJECT_GETNOPROP, 0, 0);
return ES_ERROR;
}
}
Exec_stat MCDispatch::setprop(uint4 parid, Properties which, MCExecPoint &ep, Boolean effective)
{
#ifdef /* MCDispatch::setprop */ LEGACY_EXEC
return ES_NORMAL;
#endif /* MCDispatch::setprop */
}
// bogus "cut" call actually checks license
Boolean MCDispatch::cut(Boolean home)
{
if (home)
return True;
return MCnoui || (flags & F_WAS_LICENSED) != 0;
}
extern Exec_stat MCHandlePlatformMessage(Handler_type htype, const MCString& mess, MCParameter *params);
Exec_stat MCDispatch::handle(Handler_type htype, MCNameRef mess, MCParameter *params, MCObject *pass_from)
{
Exec_stat stat = ES_NOT_HANDLED;
bool t_has_passed;
t_has_passed = false;
if (MCcheckstack && MCU_abs(MCstackbottom - (char *)&stat) > MCrecursionlimit)
{
MCeerror->add(EE_RECURSION_LIMIT, 0, 0);
MCerrorptr = stacks;
return ES_ERROR;
}
// MW-2011-06-30: Move handling of library stacks from MCStack::handle.
if (MCnusing > 0)
{
for (uint32_t i = MCnusing; i > 0 && (stat == ES_PASS || stat == ES_NOT_HANDLED); i -= 1)
{
stat = MCusing[i - 1]->handle(htype, mess, params, nil);
// MW-2011-08-22: [[ Bug 9686 ]] Make sure we exit as soon as the
// message is handled.
if (stat != ES_NOT_HANDLED && stat != ES_PASS)
return stat;
if (stat == ES_PASS)
t_has_passed = true;
}
if (t_has_passed && stat == ES_NOT_HANDLED)
stat = ES_PASS;
}
if ((stat == ES_NOT_HANDLED || stat == ES_PASS) && MCbackscripts != NULL)
{
MCObjectList *optr = MCbackscripts;
do
{
if (!optr->getremoved())
{
stat = optr->getobject()->handle(htype, mess, params, nil);
if (stat != ES_NOT_HANDLED && stat != ES_PASS)
return stat;
if (stat == ES_PASS)
t_has_passed = true;
}
optr = optr->next();
}
while (optr != MCbackscripts);
}
if ((stat == ES_NOT_HANDLED || stat == ES_PASS) && m_externals != nil)
{
Exec_stat oldstat = stat;
stat = m_externals -> Handle(this, htype, mess, params);
// MW-2011-08-22: [[ Bug 9686 ]] Make sure we exit as soon as the
// message is handled.
if (stat != ES_NOT_HANDLED && stat != ES_PASS)
return stat;
if (oldstat == ES_PASS && stat == ES_NOT_HANDLED)
stat = ES_PASS;
}
#ifdef TARGET_SUBPLATFORM_IPHONE
extern Exec_stat MCIPhoneHandleMessage(MCNameRef message, MCParameter *params);
if (stat == ES_NOT_HANDLED || stat == ES_PASS)
{
stat = MCIPhoneHandleMessage(mess, params);
if (stat != ES_NOT_HANDLED && stat != ES_PASS)
return stat;
}
#endif
#ifdef _MOBILE
if (stat == ES_NOT_HANDLED || stat == ES_PASS)
{
stat = MCHandlePlatformMessage(htype, MCNameGetOldString(mess), params);
// MW-2011-08-22: [[ Bug 9686 ]] Make sure we exit as soon as the
// message is handled.
if (stat != ES_NOT_HANDLED && stat != ES_PASS)
return stat;
}
#endif
if (MCmessagemessages && stat != ES_PASS)
MCtargetptr->sendmessage(htype, mess, False);
if (t_has_passed)
return ES_PASS;
return stat;
}
void MCDispatch::getmainstacknames(MCExecPoint &ep)
{
ep.clear();
MCExecPoint ep2(ep);
MCStack *tstk = stacks;
bool first;
first = true;
do
{
tstk->getprop(0, P_SHORT_NAME, ep2, False);
ep.concatmcstring(ep2.getsvalue(), EC_RETURN, first);
first = false;
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
void MCDispatch::appendstack(MCStack *sptr)
{
sptr->appendto(stacks);
// MW-2013-03-20: [[ MainStacksChanged ]]
MCmainstackschanged = True;
}
void MCDispatch::removestack(MCStack *sptr)
{
sptr->remove(stacks);
// MW-2013-03-20: [[ MainStacksChanged ]]
MCmainstackschanged = True;
}
void MCDispatch::destroystack(MCStack *sptr, Boolean needremove)
{
if (needremove)
removestack(sptr);
if (sptr == MCstaticdefaultstackptr)
MCstaticdefaultstackptr = stacks;
if (sptr == MCdefaultstackptr)
MCdefaultstackptr = MCstaticdefaultstackptr;
if (MCacptr != NULL && MCacptr->getmessagestack() == sptr)
MCacptr->setmessagestack(NULL);
Boolean oldstate = MClockmessages;
MClockmessages = True;
delete sptr;
MClockmessages = oldstate;
}
Boolean MCDispatch::openstartup(const char *sname,
char **outpath, IO_handle &stream)
{
if (enginedir == nil)
return False;
uint4 l = MCU_max((uint4)strlen(enginedir), (uint4)strlen(startdir)) + strlen(sname) + 11;
char *fullpath = new char[l];
sprintf(fullpath, "%s/%s", startdir, sname);
if ((stream = MCS_open(fullpath, IO_READ_MODE, True, False, 0)) != NULL)
{
*outpath = fullpath;
return True;
}
sprintf(fullpath, "%s/%s", enginedir, sname);
if ((stream = MCS_open(fullpath, IO_READ_MODE, True, False, 0)) != NULL)
{
*outpath = fullpath;
return True;
}
delete fullpath;
return False;
}
Boolean MCDispatch::openenv(const char *sname, const char *env,
char **outpath, IO_handle &stream, uint4 offset)
{
if ((env = MCS_getenv(env)) != NULL)
{
char *pathstring = strclone(env);
char *fullpath = new char[strlen(env) + strlen(sname) + 2];
char *eptr = pathstring;
while (eptr != NULL)
{
char *path = eptr;
eptr = strchr(eptr, ENV_SEPARATOR);
if (eptr != NULL)
*eptr++ = '\0';
#ifdef _WIN32
sprintf(fullpath, "%s\\%s", path, sname);
#else
sprintf(fullpath, "%s/%s", path, sname);
#endif
if ((stream = MCS_open(fullpath, IO_READ_MODE, True, False,
offset)) != NULL)
{
delete pathstring;
*outpath = fullpath;
return True;
}
}
delete pathstring;
delete fullpath;
}
return False;
}
IO_stat readheader(IO_handle& stream, char *version)
{
char tnewheader[NEWHEADERSIZE];
uint4 size = NEWHEADERSIZE;
if (IO_read(tnewheader, sizeof(char), size, stream) == IO_NORMAL)
{
// MW-2012-03-04: [[ StackFile5500 ]] Check for either the 2.7 or 5.5 header.
if (strncmp(tnewheader, newheader, NEWHEADERSIZE) == 0 ||
strncmp(tnewheader, newheader5500, NEWHEADERSIZE) == 0)
{
sprintf(version, "%c.%c.%c.%c", tnewheader[4], tnewheader[5], tnewheader[6], tnewheader[7]);
if (tnewheader[7] == '0')
{
version[5] = '\0';
if (tnewheader[6] == '0')
version[3] = '\0';
}
}
else
{
char theader[HEADERSIZE + 1];
uint4 size = HEADERSIZE - NEWHEADERSIZE;
theader[HEADERSIZE] = '\0';
uint4 offset;
strncpy(theader, tnewheader, NEWHEADERSIZE);
if (IO_read(theader + NEWHEADERSIZE, sizeof(char), size, stream) == IO_NORMAL
&& MCU_offset(SIGNATURE, theader, offset))
{
if (theader[offset - 1] != '\n' || theader[offset - 2] == '\r')
{
MCresult->sets("stack was corrupted by a non-binary file transfer");
return IO_ERROR;
}
strncpy(version, &theader[offset + VERSION_OFFSET], 3);
version[3] = '\0';
}
else
return IO_ERROR;
}
}
return IO_NORMAL;
}
// This method reads a stack from the given stream. The stack is set to
// have parent MCDispatch, and filename MCcmd. It is designed to be used
// for embedded stacks/deployed stacks/revlet stacks.
IO_stat MCDispatch::readstartupstack(IO_handle stream, MCStack*& r_stack)
{
char version[8];
uint1 charset, type;
char *newsf;
if (readheader(stream, version) != IO_NORMAL
|| IO_read_uint1(&charset, stream) != IO_NORMAL
|| IO_read_uint1(&type, stream) != IO_NORMAL
|| IO_read_string(newsf, stream) != IO_NORMAL)
return IO_ERROR;
// MW-2008-10-20: [[ ParentScripts ]] Set the boolean flag that tells us whether
// parentscript resolution is required to false.
s_loaded_parent_script_reference = false;
MCtranslatechars = charset != CHARSET;
delete newsf; // stackfiles is obsolete
MCStack *t_stack = nil;
/* UNCHECKED */ MCStackSecurityCreateStack(t_stack);
t_stack -> setparent(this);
// MM-2013-10-30: [[ Bug 11333 ]] Set the filename of android mainstack to apk/mainstack (previously was just apk).
// This solves relative file path referencing issues.
#ifdef TARGET_SUBPLATFORM_ANDROID
char *t_filename;
/* UNCHECKED */ MCMemoryNewArray(MCCStringLength(MCcmd) + 11, t_filename);
MCCStringFormat(t_filename, "%s/mainstack", MCcmd);
t_stack -> setfilename(t_filename);
#else
t_stack -> setfilename(strclone(MCcmd));
#endif
if (IO_read_uint1(&type, stream) != IO_NORMAL
|| type != OT_STACK && type != OT_ENCRYPT_STACK
|| t_stack->load(stream, version, type) != IO_NORMAL)
{
delete t_stack;
return IO_ERROR;
}
if (t_stack->load_substacks(stream, version) != IO_NORMAL
|| IO_read_uint1(&type, stream) != IO_NORMAL
|| type != OT_END)
{
delete t_stack;
return IO_ERROR;
}
// We are reading the startup stack, so this becomes the root of the
// stack list.
stacks = t_stack;
r_stack = t_stack;
#ifndef _MOBILE
// Make sure parent script references are up to date.
if (s_loaded_parent_script_reference)
t_stack -> resolveparentscripts();
#else
// Mark the stack as needed parentscript resolution. This is done after
// aux stacks have been loaded.
if (s_loaded_parent_script_reference)
t_stack -> setextendedstate(True, ECS_USES_PARENTSCRIPTS);
#endif
return IO_NORMAL;
}
// MW-2012-02-17: [[ LogFonts ]] Load a stack file, ensuring we clear up any
// font table afterwards - regardless of errors.
IO_stat MCDispatch::readfile(const char *openpath, const char *inname, IO_handle &stream, MCStack *&sptr)
{
IO_stat stat;
stat = doreadfile(openpath, inname, stream, sptr);
MCLogicalFontTableFinish();
return stat;
}
// MW-2012-02-17: [[ LogFonts ]] Actually load the stack file (wrapped by readfile
// to handle font table cleanup).
IO_stat MCDispatch::doreadfile(const char *openpath, const char *inname, IO_handle &stream, MCStack *&sptr)
{
Boolean loadhome = False;
char version[8];
sptr = NULL;
// MW-2014-09-30: [[ ScriptOnlyStack ]] First see if it is a binary stack.
if (readheader(stream, version) == IO_NORMAL)
{
if (strcmp(version, MCversionstring) > 0)
{
MCresult->sets("stack was produced by a newer version");
return IO_ERROR;
}
// MW-2008-10-20: [[ ParentScripts ]] Set the boolean flag that tells us whether
// parentscript resolution is required to false.
s_loaded_parent_script_reference = false;
uint1 charset, type;
char *newsf;
if (IO_read_uint1(&charset, stream) != IO_NORMAL
|| IO_read_uint1(&type, stream) != IO_NORMAL
|| IO_read_string(newsf, stream) != IO_NORMAL)
{
MCresult->sets("stack is corrupted, check for ~ backup file");
return IO_ERROR;
}
delete newsf; // stackfiles is obsolete
MCtranslatechars = charset != CHARSET;
sptr = nil;
/* UNCHECKED */ MCStackSecurityCreateStack(sptr);
if (stacks == NULL)
sptr->setparent(this);
else
sptr->setparent(stacks);
sptr->setfilename(strclone(openpath));
if (MCModeCanLoadHome() && type == OT_HOME)
{
char *lstring = NULL;
char *cstring = NULL;
IO_read_string(lstring, stream);
IO_read_string(cstring, stream);
delete lstring;
delete cstring;
}
MCresult -> clear();
if (IO_read_uint1(&type, stream) != IO_NORMAL
|| type != OT_STACK && type != OT_ENCRYPT_STACK
|| sptr->load(stream, version, type) != IO_NORMAL)
{
if (MCresult -> isclear())
MCresult->sets("stack is corrupted, check for ~ backup file");
destroystack(sptr, False);
sptr = NULL;
return IO_ERROR;
}
// MW-2011-08-09: [[ Groups ]] Make sure F_GROUP_SHARED is set
// appropriately.
sptr -> checksharedgroups();
if (sptr->load_substacks(stream, version) != IO_NORMAL
|| IO_read_uint1(&type, stream) != IO_NORMAL
|| type != OT_END)
{
if (MCresult -> isclear())
MCresult->sets("stack is corrupted, check for ~ backup file");
destroystack(sptr, False);
sptr = NULL;
return IO_ERROR;
}
}
// MW-2014-09-30: [[ ScriptOnlyStack ]] If we failed to load a stack from that step
// then check to see if it is a script file stack.
if (sptr == NULL)
{
// Clear the error return.
MCresult -> clear();
// Reset to position 0.
MCS_seek_set(stream, 0);
// Load the file into memory - we need to process a byteorder mark and any
// line endings.
int64_t t_size;
t_size = MCS_fsize(stream);
uint8_t *t_script;
/* UNCHECKED */ MCMemoryAllocate(t_size, t_script);
if (IO_read_bytes(t_script, t_size, stream) == IO_ERROR)
{
MCresult -> sets("unable to read file");
return IO_ERROR;
}
MCExecPoint ep;
if (t_size >= 3 && t_script[0] == 0xEF && t_script[1] == 0xBB && t_script[2] == 0xBF)
{
// UTF-8
ep . setsvalue(MCString((char *)t_script + 3, t_size - 3));
ep . utf8tonative();
}
else if (t_size >= 2 && t_script[0] == 0xFE && t_script[1] == 0xFF)
{
// UTF-16BE
for(int i = 2; i < t_size; i += 2)
{
uint8_t t;
t = t_script[i];
t_script[i] = t_script[i + 1];
t_script[i + 1] = t;
}
ep . setsvalue(MCString((char *)t_script + 2, t_size - 2));
ep . utf16tonative();
}
else if (t_size >= 2 && t_script[0] == 0xFF && t_script[1] == 0xFE)
{
// UTF-16LE
ep . setsvalue(MCString((char *)t_script + 2, t_size - 2));
ep . utf16tonative();
}
else
{
// Assume native.
ep . setsvalue(MCString((char *)t_script, t_size));
}
// Normalize line endings - this implicitly grabs the buffer into ep...
ep . texttobinary();
// ... so we can delete t_script;
MCMemoryDeallocate(t_script);
// Now attempt to parse the header line:
// 'script'
MCScriptPoint sp(ep);
// Parse 'script' token.
if (sp . skip_token(SP_FACTOR, TT_PROPERTY, P_SCRIPT) == PS_NORMAL)
{
// Parse token.
Symbol_type t_type;
if (sp . next(t_type) == PS_NORMAL &&
t_type == ST_LIT)
{
MCNameRef t_script_name;
MCNameClone(sp . gettoken_nameref(), t_script_name);
// Parse end of line.
Parse_stat t_stat;
t_stat = sp . next(t_type);
if (t_stat == PS_EOL || t_stat == PS_EOF)
{
// Now trim the ep down to the remainder of the script.
const uint1 *t_cur;
t_cur = sp . getcurptr();
// Skip the newline (if any)
if (*t_cur == '\n')
t_cur++;
// Trim the header.
ep.tail(t_cur - (const uint1 *)sp . getscript());
// Create a stack.
/* UNCHECKED */ MCStackSecurityCreateStack(sptr);
// Set its parent.
if (stacks == NULL)
sptr->setparent(this);
else
sptr->setparent(stacks);
// Set its filename.
sptr->setfilename(strclone(openpath));
// Set its name.
sptr -> setname(t_script_name);
// Make it invisible
sptr -> setflag(False, F_VISIBLE);
// Set it up as script only.
sptr -> setasscriptonly(ep);
}
}
}
}
// MW-2014-09-30: [[ ScriptOnlyStack ]] If we managed to load a stack as either binary
// or script, then do the normal processing.
if (sptr != NULL)
{
if (stacks != NULL)
{
MCStack *tstk = stacks;
do
{
if (sptr->hasname(tstk->getname()))
{
MCAutoNameRef t_stack_name;
/* UNCHECKED */ t_stack_name . Clone(sptr -> getname());
delete sptr;
sptr = NULL;
if (strequal(tstk->getfilename(), openpath))
sptr = tstk;
else
{
MCdefaultstackptr->getcard()->message_with_args(MCM_reload_stack, MCNameGetOldString(tstk->getname()), openpath);
tstk = stacks;
do
{
if (MCNameIsEqualTo(t_stack_name, tstk->getname(), kMCCompareCaseless))
{
sptr = tstk;
break;
}
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
return IO_NORMAL;
}
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
appendstack(sptr);
sptr->extraopen(false);
// MW-2008-10-28: [[ ParentScript ]]
// We just loaded a stackfile, so check to see if parentScript resolution
// is required and if so do it.
// MW-2009-01-28: [[ Inherited parentScripts ]]
// Resolving parentScripts may allocate memory, so 'resolveparentscripts'
// will return false if it fails to allocate what it needs. At some point
// this needs to be dealt with by deleting the stack and returning an error,
// *However* at the time of writing, 'readfile' isn't designed to handle
// this - so we just ignore the result for now (note that all the 'load'
// methods *fail* to check for no-memory errors!).
if (s_loaded_parent_script_reference)
sptr -> resolveparentscripts();
return IO_NORMAL;
}
// MW-2014-09-30: [[ ScriptOnlyStack ]] Finally attempt to load the script in legacy
// modes - either as a single script, or as a HyperCard conversion.
MCS_seek_set(stream, 0);
if (stacks == NULL)
{
MCnoui = True;
MCscreen = new MCUIDC;
/* UNCHECKED */ MCStackSecurityCreateStack(stacks);
MCdefaultstackptr = MCstaticdefaultstackptr = stacks;
stacks->setparent(this);
stacks->setname_cstring("revScript");
uint4 size = (uint4)MCS_fsize(stream);
char *script = new char[size + 2];
script[size] = '\n';
script[size + 1] = '\0';
if (IO_read(script, sizeof(char), size, stream) != IO_NORMAL
|| !stacks->setscript(script))
{
delete script;
return IO_ERROR;
}
}
else
{
char *tname = strclone(inname);
// MW-2008-06-12: [[ Bug 6476 ]] Media won't open HC stacks
if (!MCdispatcher->cut(True) || hc_import(tname, stream, sptr) != IO_NORMAL)
{
MCresult->sets("file is not a stack");
delete tname;
return IO_ERROR;
}
}
return IO_NORMAL;
}
IO_stat MCDispatch::loadfile(const char *inname, MCStack *&sptr)
{
IO_handle stream;
char *openpath = NULL;
char *fname = strclone(inname);
if ((stream = MCS_open(fname, IO_READ_MODE, True, False, 0)) != NULL)
if (fname[0] != PATH_SEPARATOR && fname[1] != ':')
{
char *curpath = MCS_getcurdir();
if (curpath[strlen(curpath) - 1] == '/')
curpath[strlen(curpath) - 1] = '\0';
openpath = new char[strlen(curpath) + strlen(fname) + 2];
sprintf(openpath, "%s/%s", curpath, fname);
delete curpath;
}
else
openpath = strclone(fname);
else
{
char *tmparray = new char[strlen(fname) + 1];
strcpy(tmparray, fname);
char *tname = strrchr(tmparray, PATH_SEPARATOR);
if (tname == NULL)
tname = tmparray;
else
tname++;
if ((stream = MCS_open(tname, IO_READ_MODE, True, False, 0)) != NULL)
{
char *curpath = MCS_getcurdir();
openpath = new char[strlen(curpath) + strlen(tname) + 2];
sprintf(openpath, "%s/%s", curpath, tname);
delete curpath;
}
else
{
if (!openstartup(tname, &openpath, stream)
&& !openenv(tname, "MCPATH", &openpath, stream, 0)
&& !openenv(tname, "PATH", &openpath, stream, 0))
{
char *homename;
if ((homename = MCS_getenv("HOME")) != NULL)
{
openpath = new char[strlen(homename) + strlen(tname) + 13];
if (homename[strlen(homename) - 1] == '/')
homename[strlen(homename) - 1] = '\0';
sprintf(openpath, "%s/%s", homename, tname);
if ((stream = MCS_open(openpath, IO_READ_MODE, True,
False, 0)) == NULL)
{
sprintf(openpath, "%s/stacks/%s", homename, tname);
if ((stream = MCS_open(openpath, IO_READ_MODE, True,
False, 0)) == NULL)
{
sprintf(openpath, "%s/components/%s", homename, tname);
if ((stream = MCS_open(openpath, IO_READ_MODE, True,
False, 0)) == NULL)
{
delete openpath;
openpath = NULL;
}
}
}
}
}
}
delete tmparray;
}
if (stream == NULL)
{
if (openpath != NULL)
delete openpath;
delete fname;
return IO_ERROR;
}
delete fname;
IO_stat stat = readfile(openpath, inname, stream, sptr);
delete openpath;
MCS_close(stream);
return stat;
}
void MCDispatch::cleanup(IO_handle stream, char *linkname, char *bname)
{
if (stream != NULL)
MCS_close(stream);
MCS_unlink(linkname);
if (bname != NULL)
MCS_unbackup(bname, linkname);
delete linkname;
delete bname;
}
IO_stat MCDispatch::savestack(MCStack *sptr, const MCString& fname)
{
IO_stat stat;
// MW-2014-09-30: [[ ScriptOnlyStack ]] If the stack is scriptOnly, then save
// it differently.
if (sptr -> isscriptonly())
{
stat = dosavescriptonlystack(sptr, fname);
}
else
{
stat = dosavestack(sptr, fname);
MCLogicalFontTableFinish();
}
return stat;
}
// MW-2014-09-30: [[ ScriptOnlyStack ]] Script only stacks get saved as a text file.
// Everything but the stack script is lost.
IO_stat MCDispatch::dosavescriptonlystack(MCStack *sptr, const MCString& fname)
{
if (MCModeCheckSaveStack(sptr, fname) != IO_NORMAL)
return IO_ERROR;
MCAutoPointer linkname;
if (fname.getlength() != 0)
linkname = fname.clone();
else
if ((linkname = strclone(sptr->getfilename())) == NULL)
{
MCresult->sets("stack does not have a filename");
return IO_ERROR;
}
if (*linkname == NULL)
{
MCresult->sets("can't open stack script file, bad path");
return IO_ERROR;
}
if (MCS_noperm(*linkname))
{
MCresult->sets("can't open stack script file, no permission");
return IO_ERROR;
}
// Compute the body of the script file.
MCExecPoint ep;
// Write out the standard script stack header.
ep . setstringf("script \"%s\"\n", sptr -> getname_cstring());
// Append the actual script.
ep . appendcstring(sptr -> getscript());
// Convert to UTF-8.
ep . nativetoutf8();
// Convert line endings - but only if the native line ending isn't CR!
#ifndef __CR__
ep . binarytotext();
#endif
// Open the output stream.
IO_handle stream;
if ((stream = MCS_open(*linkname, IO_WRITE_MODE, True, False, 0)) == NULL)
{
MCresult->sets("can't open stack script file");
return IO_ERROR;
}
// Write out the byte-order mark, followed by the script body.
if (IO_write("\xEF\xBB\xBF", 1, 3, stream) != IO_NORMAL ||
IO_write(ep . getsvalue() . getstring(), 1, ep . getsvalue() . getlength(), stream) != IO_NORMAL)
{
MCresult -> sets("error writing stack script file");
MCS_close(stream);
return IO_ERROR;
}
// Close the stream.
MCS_close(stream);
// Set the filename.
sptr->setfilename(strclone(*linkname));
return IO_NORMAL;
}
IO_stat MCDispatch::dosavestack(MCStack *sptr, const MCString &fname)
{
if (MCModeCheckSaveStack(sptr, fname) != IO_NORMAL)
return IO_ERROR;
char *linkname;
if (fname.getlength() != 0)
linkname = fname.clone();
else
if ((linkname = strclone(sptr->getfilename())) == NULL)
{
MCresult->sets("stack does not have a filename");
return IO_ERROR;
}
if (linkname == NULL)
{
MCresult->sets("can't open stack file, bad path");
return IO_ERROR;
}
if (MCS_noperm(linkname))
{
MCresult->sets("can't open stack file, no permission");
delete linkname;
return IO_ERROR;
}
char *oldfiletype = MCfiletype;
MCfiletype = MCstackfiletype;
char *backup = new char[strlen(linkname) + 2];
strcpy(backup, linkname);
strcat(backup, "~");
MCS_unlink(backup);
if (MCS_exists(linkname, True) && !MCS_backup(linkname, backup))
{
MCresult->sets("can't open stack backup file");
MCfiletype = oldfiletype;
delete linkname;
delete backup;
return IO_ERROR;
}
IO_handle stream;
if ((stream = MCS_open(linkname, IO_WRITE_MODE, True, False, 0)) == NULL)
{
MCresult->sets("can't open stack file");
cleanup(stream, linkname, backup);
MCfiletype = oldfiletype;
return IO_ERROR;
}
MCfiletype = oldfiletype;
MCString errstring = "Error writing stack (disk full?)";
// MW-2012-03-04: [[ StackFile5500 ]] Work out what header to emit, and the size.
const char *t_header;
uint32_t t_header_size;
if (MCstackfileversion >= 5500)
t_header = newheader5500, t_header_size = 8;
else if (MCstackfileversion >= 2700)
t_header = newheader, t_header_size = 8;
else
t_header = header, t_header_size = HEADERSIZE;
if (IO_write(t_header, sizeof(char), t_header_size, stream) != IO_NORMAL
|| IO_write_uint1(CHARSET, stream) != IO_NORMAL)
{
MCresult->sets(errstring);
cleanup(stream, linkname, backup);
return IO_ERROR;
}
if (IO_write_uint1(OT_NOTHOME, stream) != IO_NORMAL
|| IO_write_string(NULL, stream) != IO_NORMAL)
{ // was stackfiles
MCresult->sets(errstring);
cleanup(stream, linkname, backup);
return IO_ERROR;
}
// MW-2012-02-22; [[ NoScrollSave ]] Adjust the rect by the current group offset.
MCgroupedobjectoffset . x = 0;
MCgroupedobjectoffset . y = 0;
MCresult -> clear();
if (sptr->save(stream, 0, false) != IO_NORMAL
|| IO_write_uint1(OT_END, stream) != IO_NORMAL)
{
if (MCresult -> isclear())
MCresult->sets(errstring);
cleanup(stream, linkname, backup);
return IO_ERROR;
}
MCS_close(stream);
uint2 oldmask = MCS_umask(0);
uint2 newmask = ~oldmask & 00777;
if (oldmask & 00400)
newmask &= ~00100;
if (oldmask & 00040)
newmask &= ~00010;
if (oldmask & 00004)
newmask &= ~00001;
MCS_umask(oldmask);
MCS_chmod(linkname, newmask);
if (sptr->getfilename() != NULL && !strequal(linkname, sptr->getfilename()))
MCS_copyresourcefork(sptr->getfilename(), linkname);
else if (sptr -> getfilename() != NULL)
MCS_copyresourcefork(backup, linkname);
sptr->setfilename(linkname);
if (backup != NULL)
{
MCS_unlink(backup);
delete backup;
}
return IO_NORMAL;
}
#ifdef FEATURE_RELAUNCH_SUPPORT
extern bool relaunch_startup(const char *p_id);
#endif
void send_relaunch(void)
{
#ifdef FEATURE_RELAUNCH_SUPPORT
bool t_do_relaunch;
t_do_relaunch = false;
const char *t_id;
t_do_relaunch = MCModeHandleRelaunch(t_id);
if (t_do_relaunch)
if (relaunch_startup(t_id))
exit(0);
#endif
}
void send_startup_message(bool p_do_relaunch = true)
{
if (p_do_relaunch)
send_relaunch();
MCdefaultstackptr -> setextendedstate(true, ECS_DURING_STARTUP);
MCdefaultstackptr -> getcard() -> message(MCM_start_up);
MCdefaultstackptr -> setextendedstate(false, ECS_DURING_STARTUP);
}
void MCDispatch::wclose(Window w)
{
MCStack *target = findstackd(w);
if (target != NULL && !target -> getextendedstate(ECS_DISABLED_FOR_MODAL))
{
Exec_stat stat = target->getcurcard()->message(MCM_close_stack_request);
if (stat == ES_NOT_HANDLED || stat == ES_PASS)
{
target->kunfocus();
target->close();
target->checkdestroy();
}
}
}
void MCDispatch::wkfocus(Window w)
{
MCStack *target = findstackd(w);
if (target != NULL)
target->kfocus();
}
void MCDispatch::wkunfocus(Window w)
{
MCStack *target = findstackd(w);
if (target != NULL)
target->kunfocus();
}
Boolean MCDispatch::wkdown(Window w, const char *string, KeySym key)
{
if (menu != NULL)
return menu->kdown(string, key);
MCStack *target = findstackd(w);
if (target == NULL || !target->kdown(string, key))
{
if (MCmodifierstate & MS_MOD1)
{
MCButton *bptr = MCstacks->findmnemonic(MCS_tolower(string[0]));
if (bptr != NULL)
{
bptr->activate(True, (uint2)key);
return True;
}
}
}
else
if (target != NULL)
return True;
return False;
}
void MCDispatch::wkup(Window w, const char *string, KeySym key)
{
if (menu != NULL)
menu->kup(string, key);
else
{
MCStack *target = findstackd(w);
if (target != NULL)
target->kup(string, key);
}
}
void MCDispatch::wmfocus_stack(MCStack *target, int2 x, int2 y)
{
// IM-2013-09-23: [[ FullscreenMode ]] transform view -> stack coordinates
MCPoint t_stackloc;
t_stackloc = MCPointMake(x, y);
// IM-2014-02-12: [[ StackScale ]] mfocus will translate target stack to menu stack coords
// so in both cases we pass target stack coords.
// IM-2014-02-14: [[ StackScale ]] Don't try to convert if target is null
if (target != nil)
t_stackloc = target->windowtostackloc(t_stackloc);
if (menu != NULL)
menu->mfocus(t_stackloc.x, t_stackloc.y);
else if (target != NULL)
target->mfocus(t_stackloc.x, t_stackloc.y);
}
void MCDispatch::wmfocus(Window w, int2 x, int2 y)
{
MCStack *target = findstackd(w);
wmfocus_stack(target, x, y);
}
void MCDispatch::wmunfocus(Window w)
{
MCStack *target = findstackd(w);
if (target != NULL)
target->munfocus();
}
void MCDispatch::wmdrag(Window w)
{
if (!MCModeMakeLocalWindows())
return;
if (isdragsource())
return;
MCStack *target = findstackd(w);
if (target != NULL)
target->mdrag();
MCPasteboard *t_pasteboard;
t_pasteboard = MCdragdata -> GetSource();
// OK-2009-03-13: [[Bug 7776]] - Check for null MCdragtargetptr to hopefully fix crash.
if (t_pasteboard != NULL && MCdragtargetptr != NULL)
{
m_drag_source = true;
m_drag_end_sent = false;
// MW-2009-02-02: [[ Improved image search ]]
// Search for the appropriate image object using the standard method - note
// here we search relative to the target of the dragStart message.
MCImage *t_image;
t_image = NULL;
if (MCdragimageid != 0)
t_image = MCdragtargetptr != NULL ? MCdragtargetptr -> resolveimageid(MCdragimageid) : resolveimageid(MCdragimageid);
MCdragsource = MCdragtargetptr;
// PLATFORM-TODO: This is needed at the moment to make sure that we don't
// get the selection 'going away' when we start dragging. At the moment
// MouseRelease is mapped to mup without messages, which isn't quite
// correct from the point of view of the field.
if (MCdragtargetptr->gettype() > CT_CARD)
{
MCControl *cptr = (MCControl *)MCdragtargetptr;
cptr->munfocus();
cptr->getcard()->ungrab();
}
MCdragtargetptr->getstack()->resetcursor(True);
MCdragtargetptr -> getstack() -> munfocus();
MCdragaction = MCscreen -> dodragdrop(w, t_pasteboard, MCallowabledragactions, t_image, t_image != NULL ? &MCdragimageoffset : NULL);
dodrop(true);
MCdragdata -> ResetSource();
MCdragsource = NULL;
MCdragdest = NULL;
MCdropfield = NULL;
MCdragtargetptr = NULL;
m_drag_source = false;
}
else
{
MCdragdata -> ResetSource();
MCdragsource = NULL;
MCdragdest = NULL;
MCdropfield = NULL;
MCdragtargetptr = NULL;
m_drag_source = false;
}
}
void MCDispatch::wmdown_stack(MCStack *target, uint2 which)
{
if (menu != NULL)
menu -> mdown(which);
else
{
if (!isdragsource())
{
MCallowabledragactions = DRAG_ACTION_COPY;
MCdragaction = DRAG_ACTION_NONE;
MCdragimageid = 0;
MCdragimageoffset . x = 0;
MCdragimageoffset . y = 0;
MCdragdata -> ResetSource();
}
if (target != NULL)
target->mdown(which);
}
}
void MCDispatch::wmdown(Window w, uint2 which)
{
MCStack *target = findstackd(w);
wmdown_stack(target, which);
}
void MCDispatch::wmup_stack(MCStack *target, uint2 which)
{
if (menu != NULL)
menu->mup(which, false);
else
{
if (target != NULL)
target->mup(which, false);
}
}
void MCDispatch::wmup(Window w, uint2 which)
{
MCStack *target = findstackd(w);
wmup_stack(target, which);
}
void MCDispatch::wdoubledown(Window w, uint2 which)
{
if (menu != NULL)
menu->doubledown(which);
else
{
MCStack *target = findstackd(w);
if (target != NULL)
target->doubledown(which);
}
}
void MCDispatch::wdoubleup(Window w, uint2 which)
{
if (menu != NULL)
menu->doubleup(which);
else
{
MCStack *target = findstackd(w);
if (target != NULL)
target->doubleup(which);
}
}
void MCDispatch::kfocusset(Window w)
{
MCStack *target = findstackd(w);
if (target != NULL)
target->kfocusset(NULL);
}
void MCDispatch::wmdragenter(Window w, MCPasteboard *p_data)
{
MCStack *target = findstackd(w);
m_drag_target = true;
if (m_drag_source)
MCdragdata -> SetTarget(MCdragdata -> GetSource());
else
MCdragdata -> SetTarget(p_data);
if (MCmousestackptr != NULL && target != MCmousestackptr)
MCmousestackptr -> munfocus();
MCmousestackptr = target;
}
MCDragAction MCDispatch::wmdragmove(Window w, int2 x, int2 y)
{
// We must also issue a new focus event if the modifierstate
// changes.
static uint4 s_old_modifiers = 0;
MCStack *target = findstackd(w);
// IM-2013-10-08: [[ FullscreenMode ]] Translate mouse location to stack coords
MCPoint t_mouseloc;
t_mouseloc = target->windowtostackloc(MCPointMake(x, y));
if (MCmousex != t_mouseloc.x || MCmousey != t_mouseloc.y || MCmodifierstate != s_old_modifiers)
{
MCmousex = t_mouseloc.x;
MCmousey = t_mouseloc.y;
s_old_modifiers = MCmodifierstate;
target -> mfocus(t_mouseloc.x, t_mouseloc.y);
}
return MCdragaction;
}
void MCDispatch::wmdragleave(Window w)
{
MCStack *target = findstackd(w);
if (target != NULL && target == MCmousestackptr)
{
MCmousestackptr -> munfocus();
MCmousestackptr = NULL;
}
MCdragdata -> ResetTarget();
m_drag_target = false;
}
MCDragAction MCDispatch::wmdragdrop(Window w)
{
MCStack *target;
target = findstackd(w);
// MW-2011-02-08: Make sure we store the drag action that is in effect now
// otherwise it can change as a result of message sends which is bad :o)
uint32_t t_drag_action;
t_drag_action = MCdragaction;
if (t_drag_action != DRAG_ACTION_NONE)
dodrop(false);
MCmousestackptr = NULL;
MCdragdata -> ResetTarget();
m_drag_target = false;
return t_drag_action;
}
void MCDispatch::property(Window w, Atom atom)
{
}
void MCDispatch::configure(Window w)
{
MCStack *target = findstackd(w);
if (target != NULL)
target->view_configure(true);
}
void MCDispatch::enter(Window w)
{
MCStack *target = findstackd(w);
if (target != NULL)
target->enter();
}
void MCDispatch::redraw(Window w, MCRegionRef p_dirty_region)
{
MCStack *target = findstackd(w);
if (target == NULL)
return;
target -> updatewindow(p_dirty_region);
}
MCFontStruct *MCDispatch::loadfont(const MCString &fname, uint2 &size,
uint2 style, Boolean printer)
{
#if defined(_LINUX_DESKTOP)
if (fonts == NULL)
fonts = MCFontlistCreateNew();
if (fonts == NULL)
fonts = MCFontlistCreateOld();
#elif defined(_LINUX_SERVER)
// MM-2013-09-13: [[ RefactorGraphics ]] Server font support.
if (fonts == NULL)
fonts = MCFontlistCreateNew();
#else
if (fonts == nil)
fonts = new MCFontlist;
#endif
return fonts->getfont(fname, size, style, printer);
}
MCStack *MCDispatch::findstackname(const MCString &s)
{
if (s.getlength() == 0)
return NULL;
MCStack *tstk = stacks;
if (tstk != NULL)
{
do
{
MCStack *foundstk;
if ((foundstk = (MCStack *)tstk->findsubstackname(s)) != NULL)
return foundstk;
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
tstk = stacks;
if (tstk != NULL)
{
do
{
MCStack *foundstk;
if ((foundstk = (MCStack *)tstk->findstackfile(s)) != NULL)
return foundstk;
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
char *sname = s.clone();
if (loadfile(sname, tstk) != IO_NORMAL)
{
char *buffer = new char[s.getlength() + 5];
MCU_lower(buffer, s);
strcpy(&buffer[s.getlength()], ".mc");
delete sname;
char *sptr = buffer;
while (*sptr)
{
if (strchr("\r\n\t *?*<>/\\()[]{}|'`\"", *sptr) != NULL)
*sptr = '_';
sptr++;
}
if (loadfile(buffer, tstk) != IO_NORMAL)
{
strcpy(&buffer[s.getlength()], ".rev");
if (loadfile(buffer, tstk) != IO_NORMAL)
{
delete buffer;
return NULL;
}
}
delete buffer;
}
else
delete sname;
return tstk;
}
MCStack *MCDispatch::findstackid(uint4 fid)
{
if (fid == 0)
return NULL;
MCStack *tstk = stacks;
do
{
MCStack *foundstk;
if ((foundstk = (MCStack *)tstk->findsubstackid(fid)) != NULL)
return foundstk;
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
return NULL;
}
bool MCDispatch::foreachchildstack(MCStack *p_stack, MCStackForEachCallback p_callback, void *p_context)
{
bool t_continue;
t_continue = true;
if (stacks)
{
MCStack *t_stack;
t_stack = stacks;
do
{
t_continue = t_stack->foreachchildstack(p_callback, p_context);
t_stack = (MCStack*)t_stack->next();
}
while (t_continue && t_stack != stacks);
}
return t_continue;
}
MCStack *MCDispatch::findstackwindowid(uint32_t p_win_id)
{
if (p_win_id == 0)
return NULL;
if (stacks != NULL)
{
MCStack *tstk = stacks;
do
{
MCStack *foundstk;
if ((foundstk = tstk->findstackwindowid(p_win_id)) != NULL)
return foundstk;
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
if (panels != NULL)
{
MCStack *tstk = panels;
do
{
MCStack *foundstk;
if ((foundstk = tstk->findstackwindowid(p_win_id)) != NULL)
return foundstk;
tstk = (MCStack *)tstk->next();
}
while (tstk != panels);
}
if (m_transient_stacks != nil)
{
MCStack *tstk = m_transient_stacks;
do
{
MCStack *foundstk;
if ((foundstk = tstk -> findstackwindowid(p_win_id)) != NULL)
return foundstk;
tstk = (MCStack *)tstk->next();
}
while(tstk != m_transient_stacks);
}
return NULL;
}
MCStack *MCDispatch::findstackd(Window w)
{
// IM-2014-07-09: [[ Bug 12225 ]] Use window ID to find stack
return findstackwindowid(MCscreen->dtouint4((Drawable)w));
}
MCObject *MCDispatch::getobjid(Chunk_term type, uint4 inid)
{
if (stacks != NULL)
{
MCStack *tstk = stacks;
do
{
MCObject *optr;
if ((optr = tstk->getsubstackobjid(type, inid)) != NULL)
return optr;
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
return NULL;
}
MCObject *MCDispatch::getobjname(Chunk_term type, const MCString &s)
{
if (stacks != NULL)
{
MCStack *tstk = stacks;
do
{
MCObject *optr;
if ((optr = tstk->getsubstackobjname(type, s)) != NULL)
return optr;
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
if (type == CT_IMAGE)
{
const char *sptr = s.getstring();
uint4 l = s.getlength();
MCAutoNameRef t_image_name;
if (MCU_strchr(sptr, l, ':'))
/* UNCHECKED */ t_image_name . CreateWithOldString(s);
MCImage *iptr = imagecache;
if (iptr != NULL)
{
do
{
check:
if (t_image_name != nil && iptr -> hasname(t_image_name))
return iptr;
if (!iptr->getopened())
{
iptr->remove(imagecache);
delete iptr;
iptr = imagecache;
if (iptr == NULL)
break;
goto check;
}
iptr = (MCImage *)iptr->next();
}
while (iptr != imagecache);
}
if (MCU_strchr(sptr, l, ':'))
{
MCresult->clear(False);
MCExecPoint ep(MCdefaultstackptr, NULL, NULL);
MCExecPoint *epptr = MCEPptr == NULL ? &ep : MCEPptr;
epptr->setsvalue(s);
MCU_geturl(*epptr);
if (MCresult->isempty())
{
iptr = new MCImage;
iptr->appendto(imagecache);
iptr->setprop(0, P_TEXT, *epptr, False);
iptr->setname(t_image_name);
return iptr;
}
}
}
return NULL;
}
MCStack *MCDispatch::gethome()
{
return stacks;
}
Boolean MCDispatch::ismainstack(MCStack *sptr)
{
if (stacks != NULL)
{
MCStack *tstk = stacks;
do
{
if (tstk == sptr)
return True;
tstk = (MCStack *)tstk->next();
}
while (tstk != stacks);
}
return False;
}
void MCDispatch::addmenu(MCObject *target)
{
menu = target;
target->getcard()->ungrab();
}
void MCDispatch::removemenu()
{
// menu->getstack()->mfocus(MCmousex, MCmousey); //disrupts card kfocus
menu = NULL;
}
void MCDispatch::closemenus()
{
if (menu != NULL)
menu->closemenu(True, True);
}
void MCDispatch::appendpanel(MCStack *sptr)
{
sptr->appendto(panels);
}
void MCDispatch::removepanel(MCStack *sptr)
{
sptr->remove(panels);
}
bool MCDispatch::is_transient_stack(MCStack *sptr)
{
if (m_transient_stacks != NULL)
{
MCStack *tstk = m_transient_stacks;
do
{
if (tstk == sptr)
return true;
tstk = (MCStack *)tstk->next();
}
while (tstk != m_transient_stacks);
}
return false;
}
void MCDispatch::add_transient_stack(MCStack *sptr)
{
sptr->appendto(m_transient_stacks);
}
void MCDispatch::remove_transient_stack(MCStack *sptr)
{
sptr->remove(m_transient_stacks);
}
///////////////////////////////////////////////////////////////////////////////
bool MCDispatch::loadexternal(const char *p_external)
{
char *t_filename;
#if defined(TARGET_SUBPLATFORM_ANDROID)
extern bool revandroid_loadExternalLibrary(const char *p_external, char*& r_filename);
// MW-2013-08-07: [[ ExternalsApiV5 ]] Make sure we only use the leaf name
// of the external when loading.
if (strrchr(p_external, '/') != nil)
p_external = strrchr(p_external, '/') + 1;
if (!revandroid_loadExternalLibrary(p_external, t_filename))
return false;
// Don't try and load any drivers as externals.
if (strncmp(p_external, "db", 2) == 0)
{
delete t_filename;
return true;
}
#elif !defined(_SERVER)
if (p_external[0] == '/')
{
if (!MCCStringClone(p_external, t_filename))
return false;
}
else if (!MCCStringFormat(t_filename, "%.*s/%s", strrchr(MCcmd, '/') - MCcmd, MCcmd, p_external))
return false;
#else
if (!MCCStringClone(p_external, t_filename))
return false;
#endif
if (m_externals == nil)
m_externals = new MCExternalHandlerList;
bool t_loaded;
t_loaded = m_externals -> Load(t_filename);
delete t_filename;
if (m_externals -> IsEmpty())
{
delete m_externals;
m_externals = nil;
}
return t_loaded;
}
///////////////////////////////////////////////////////////////////////////////
// We have three contexts to be concerned with:
// - text editing : MCactivefield != NULL
// - image editing : MCactiveimage != NULL
// - stack editing
//
// We try each of these in turn, attempting appropriate things in each case.
//
bool MCDispatch::dopaste(MCObject*& r_objptr, bool p_explicit)
{
r_objptr = NULL;
if (MCactivefield != NULL)
{
MCParagraph *t_paragraphs;
t_paragraphs = MCclipboarddata -> FetchParagraphs(MCactivefield);
//
if (t_paragraphs != NULL)
{
// MW-2012-03-16: [[ Bug ]] Fetch the current active field since it can be
// unset as a result of pasting (due to scrollbarDrag).
MCField *t_field;
t_field = MCactivefield;
// MW-2012-02-16: [[ Bug ]] Bracket any actions that result in
// textChanged message by a lock screen pair.
MCRedrawLockScreen();
t_field -> pastetext(t_paragraphs, true);
MCRedrawUnlockScreen();
// MW-2012-02-08: [[ TextChanged ]] Invoke textChanged as this method
// was called as a result of a user action (paste cmd, paste key).
t_field -> textchanged();
return true;
}
}
if (MCactiveimage != NULL && MCclipboarddata -> HasImage())
{
MCSharedString *t_data;
t_data = MCclipboarddata -> Fetch(TRANSFER_TYPE_IMAGE);
if (t_data != NULL)
{
MCExecPoint ep(NULL, NULL, NULL);
ep . setsvalue(t_data -> Get());
MCImage *t_image;
t_image = new MCImage;
t_image -> open();
t_image -> openimage();
t_image -> setprop(0, P_TEXT, ep, False);
MCactiveimage -> pasteimage(t_image);
t_image -> closeimage();
t_image -> close();
delete t_image;
t_data -> Release();
}
return true;
}
if (MCdefaultstackptr != NULL && (p_explicit || MCdefaultstackptr -> gettool(MCdefaultstackptr) == T_POINTER))
{
MCObject *t_objects;
t_objects = NULL;
if (!MCclipboarddata -> Lock())
return false;
if (MCclipboarddata -> HasObjects())
{
MCSharedString *t_data;
t_data = MCclipboarddata -> Fetch(TRANSFER_TYPE_OBJECTS);
if (t_data != NULL)
{
t_objects = MCObject::unpickle(t_data, MCdefaultstackptr);
t_data -> Release();
}
}
else if (MCclipboarddata -> HasImage())
{
MCSharedString *t_data;
t_data = MCclipboarddata -> Fetch(TRANSFER_TYPE_IMAGE);
if (t_data != NULL)
{
MCExecPoint ep(NULL, NULL, NULL);
ep . setsvalue(t_data -> Get());
t_objects = new MCImage(*MCtemplateimage);
t_objects -> open();
t_objects -> setprop(0, P_TEXT, ep, False);
t_objects -> close();
t_data -> Release();
}
}
MCclipboarddata -> Unlock();
//
if (t_objects != NULL)
{
MCselected -> clear(False);
MCselected -> lockclear();
while(t_objects != NULL)
{
MCObject *t_object;
t_object = t_objects -> remove(t_objects);
t_object -> paste();
// OK-2009-04-02: [[Bug 7881]] - Parentscripts broken by cut and pasting object
t_object -> resolveparentscript();
if (t_object -> getparent() == NULL)
delete t_object;
else
r_objptr = t_object;
}
MCselected -> unlockclear();
return true;
}
}
return false;
}
void MCDispatch::dodrop(bool p_source)
{
if (!m_drag_end_sent && MCdragsource != NULL && (MCdragdest == NULL || MCdragaction == DRAG_ACTION_NONE))
{
// We are only the source
m_drag_end_sent = true;
MCdragsource -> message(MCM_drag_end);
// OK-2008-10-21 : [[Bug 7316]] - Cursor in script editor follows mouse after dragging to non-Revolution target.
// I have no idea why this apparently only happens in the script editor, but this seems to fix it and doesn't seem too risky :)
// MW-2008-10-28: [[ Bug 7316 ]] - This happens because the script editor is doing stuff with drag messages
// causing the default engine behaviour to be overriden. In this case, some things have to happen to the field
// when the drag is over. Note that we have to check that the source was a field in this case since we don't
// need to do anything if it is not!
// IM-2014-02-28: [[ Bug 11715 ]] dragsource may have changed or unset after sending message so check for valid ptr
if (MCdragsource != nil && MCdragsource -> gettype() == CT_FIELD)
{
MCField *t_field;
t_field = static_cast(MCdragsource);
t_field -> setstate(False, CS_DRAG_TEXT);
t_field -> computedrag();
t_field -> getstack() -> resetcursor(True);
}
return;
}
if (p_source)
return;
// Setup global variables for a field drop
MCdropfield = NULL;
MCdropchar = 0;
int4 t_start_index, t_end_index;
t_start_index = t_end_index = 0;
if (MCdragdest != NULL && MCdragdest -> gettype() == CT_FIELD)
{
MCdropfield = static_cast(MCdragdest);
if (MCdragdest -> getstate(CS_DRAG_TEXT))
{
MCdropfield -> locmark(False, False, False, False, True, t_start_index, t_end_index);
MCdropchar = t_start_index;
}
}
// If source is a field and the engine handled the start of the drag operation
bool t_auto_source;
t_auto_source = MCdragsource != NULL && MCdragsource -> gettype() == CT_FIELD && MCdragsource -> getstate(CS_SOURCE_TEXT);
// If dest is a field and the engine handled the accepting of the operation
bool t_auto_dest;
t_auto_dest = MCdragdest != NULL && MCdragdest -> gettype() == CT_FIELD && MCdragdest -> getstate(CS_DRAG_TEXT);
if (t_auto_source && t_auto_dest && MCdragsource == MCdragdest)
{
// Source and target is the same field
MCField *t_field;
t_field = static_cast(MCdragsource);
int4 t_from_start_index, t_from_end_index;
t_field -> selectedmark(False, t_from_start_index, t_from_end_index, False);
// We are dropping in the target selection - so just send the messages and do nothing
if (t_start_index >= t_from_start_index && t_start_index < t_from_end_index)
{
t_field -> message(MCM_drag_drop);
t_field -> message(MCM_drag_end);
t_field -> setstate(False, CS_DRAG_TEXT);
t_field -> computedrag();
t_field -> getstack() -> resetcursor(True);
return;
}
if (t_field -> message(MCM_drag_drop) != ES_NORMAL)
{
MCParagraph *t_paragraphs;
t_paragraphs = MCdragdata -> FetchParagraphs(MCdropfield);
// MW-2012-02-16: [[ Bug ]] Bracket any actions that result in
// textChanged message by a lock screen pair.
MCRedrawLockScreen();
if (MCdragaction == DRAG_ACTION_MOVE)
{
MCdropfield -> movetext(t_paragraphs, t_start_index);
Ustruct *us = MCundos->getstate();
if (us != NULL && us->type == UT_MOVE_TEXT)
MCdropfield->seltext(us -> ud.text.index, us -> ud.text.index + us->ud.text.newchars, False, True);
}
else
{
MCdropfield -> seltext(t_start_index, t_start_index, True);
MCdropfield -> pastetext(t_paragraphs, true);
Ustruct *us = MCundos->getstate();
if (us != NULL && us->type == UT_TYPE_TEXT)
MCdropfield->seltext(t_start_index, t_start_index + us->ud.text.newchars, False, True);
}
// MW-2012-02-16: [[ Bug ]] Bracket any actions that result in
// textChanged message by a lock screen pair.
MCRedrawUnlockScreen();
// MW-2012-02-08: [[ TextChanged ]] Invoke textChanged as this method
// was called as a result of a user action (result of drop in field).
MCactivefield -> textchanged();
}
MCdropfield->setstate(False, CS_DRAG_TEXT);
MCdropfield->computedrag();
MCdropfield -> getstack() -> resetcursor(True);
return;
}
int4 t_src_start, t_src_end;
t_src_start = t_src_end = 0;
if (t_auto_source)
static_cast(MCdragsource) -> selectedmark(False, t_src_start, t_src_end, False);
bool t_auto_drop;
t_auto_drop = MCdragdest != NULL && MCdragdest -> message(MCM_drag_drop) != ES_NORMAL;
if (t_auto_dest && t_auto_drop && MCdragdata != NULL && MCdropfield != NULL)
{
// MW-2012-02-16: [[ Bug ]] Bracket any actions that result in
// textChanged message by a lock screen pair.
MCRedrawLockScreen();
// Process an automatic drop action
MCdropfield -> seltext(t_start_index, t_start_index, True);
MCParagraph *t_paragraphs;
t_paragraphs = MCdragdata -> FetchParagraphs(MCdropfield);
MCdropfield -> pastetext(t_paragraphs, true);
Ustruct *us = MCundos->getstate();
if (us != NULL && us->type == UT_TYPE_TEXT)
MCdropfield->seltext(t_start_index, t_start_index + us->ud.text.newchars, False, True);
MCdropfield->setstate(False, CS_DRAG_TEXT);
MCdropfield->computedrag();
MCdropfield -> getstack() -> resetcursor(True);
// MW-2012-02-16: [[ Bug ]] Bracket any actions that result in
// textChanged message by a lock screen pair.
MCRedrawUnlockScreen();
// MW-2012-02-08: [[ TextChanged ]] Invoke textChanged as this method
// was called as a result of a user action (drop from different field).
MCactivefield -> textchanged();
}
else if (MCdropfield != NULL)
{
MCdropfield->setstate(False, CS_DRAG_TEXT);
MCdropfield->computedrag();
MCdropfield -> getstack() -> resetcursor(True);
}
bool t_auto_end;
if (MCdragsource != NULL)
{
m_drag_end_sent = true;
t_auto_end = MCdragsource -> message(MCM_drag_end) != ES_NORMAL;
}
else
t_auto_end = false;
if (t_auto_source && t_auto_end && MCdragsource != NULL && MCdragaction == DRAG_ACTION_MOVE)
{
// MW-2012-02-16: [[ Bug ]] Bracket any actions that result in
// textChanged message by a lock screen pair.
MCRedrawLockScreen();
static_cast(MCdragsource) -> deletetext(t_src_start, t_src_end);
MCRedrawUnlockScreen();
// MW-2012-02-08: [[ TextChanged ]] Invoke textChanged as this method
// was called as a result of a user action (move from one field to another).
MCactivefield -> textchanged();
}
}
////////////////////////////////////////////////////////////////////////////////
void MCDispatch::clearcursors(void)
{
for(uint32_t i = 0; i < PI_NCURSORS; i++)
{
if (MCcursor == MCcursors[i])
MCcursor = nil;
if (MCdefaultcursor = MCcursors[i])
MCdefaultcursor = nil;
}
MCStack *t_stack;
t_stack = stacks;
do
{
t_stack -> clearcursor();
t_stack = t_stack -> next();
}
while(t_stack != stacks -> prev());
}
////////////////////////////////////////////////////////////////////////////////
void MCDispatch::changehome(MCStack *stack)
{
MCStack *t_stack;
t_stack = stacks;
do
{
if (t_stack -> getparent() == stacks)
t_stack -> setparent(stack);
t_stack = t_stack -> next();
}
while(t_stack != stacks -> prev());
stack -> setparent(this);
stack -> totop(stacks);
}
////////////////////////////////////////////////////////////////////////////////
#ifdef _WINDOWS_DESKTOP
void MCDispatch::freeprinterfonts()
{
fonts->freeprinterfonts();
}
#endif
void MCDispatch::flushfonts(void)
{
delete fonts;
fonts = nil;
}
MCFontlist *MCFontlistGetCurrent(void)
{
return MCdispatcher -> getfontlist();
}
////////////////////////////////////////////////////////////////////////////////