/*
 * File:        wx_mbuf.cc
 * Purpose:     wxMediaBuffer implementation
 * Author:      Matthew Flatt
 * Created:     1995
 * Copyright:   (c) 1995-98, Matthew Flatt

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 */

#define  Uses_wxPrintSetup /* for wx_xt */
#include "wx_utils.h"
#include "wx_win.h"
#include "wx_dcps.h"
#ifndef OLD_WXWINDOWS
# include "wx_cmdlg.h"
#endif
#include "wx_print.h"
#ifdef wx_xt
# include "wx_types.h"
#endif

#include "wx_media.h"
#ifndef OLD_WXWINDOWS
# include "wx_clipb.h"
#else
# include "wx_gclip.h"
#endif
#include "wx_ptreq.h"
#include <ctype.h>
#include <string.h>

#define NUM_MAX_UNDOS 500

#if ALLOW_X_STYLE_SELECTION
Bool wxMediaXSelectionMode = TRUE;
wxMediaBuffer *wxMediaXSelectionOwner = NULL;
wxMediaBuffer *wxMediaXSelectionAllowed = NULL;
static Bool xSelectionCopied = FALSE;
static Bool xClipboardHack = FALSE;
#endif

static void InitCutNPaste(void);

static void MediaStyleNotify(wxStyle *which, wxMediaBuffer *media);

class wxMediaClipboardClient : public wxClipboardClient
{
 public:
  wxMediaClipboardClient();
  char *GetData(char *format, long *size);
  void BeingReplaced(void);
};
static wxMediaClipboardClient *TheMediaClipboardClient;

#if ALLOW_X_STYLE_SELECTION
class wxMediaXClipboardClient : public wxClipboardClient
{
 public:
  wxMediaXClipboardClient();
  char *GetData(char *format, long *size);
  void BeingReplaced(void);
};
static wxMediaXClipboardClient *TheMediaXClipboardClient;
#endif

# define MALLOC_CRP(n) new wxChangeRecord*[n]

// xformer doesn't handle static member variable declarations
#ifdef MZ_PRECISE_GC
START_XFORM_SKIP;
#endif

wxBitmap *wxMediaBuffer::bitmap;
wxMemoryDC *wxMediaBuffer::offscreen = NULL;
long wxMediaBuffer::bmHeight, wxMediaBuffer::bmWidth;
#if defined(wx_msw) && defined(USE_SEP_WINDOW_MGR)
# define OFFSCREEN_IN_USE_INIT TRUE  /* Until we fix Win32 bug */
#else
# define OFFSCREEN_IN_USE_INIT FALSE
#endif
Bool wxMediaBuffer::offscreenInUse = OFFSCREEN_IN_USE_INIT;
wxMediaBuffer *wxMediaBuffer::lastUsedOffscreen = NULL;

#ifdef MZ_PRECISE_GC
END_XFORM_SKIP;
#endif

typedef struct { short type; } Scheme_Object;
extern wxMediaBuffer *objscheme_unbundle_wxMediaBuffer(Scheme_Object *, const char*, int);
extern Scheme_Object *objscheme_bundle_wxMediaBuffer(wxMediaBuffer*);

/******************************************************************/

#ifndef EACH_BUFFER_OWN_OFFSCREEN
static int bcounter = 0;
#endif

wxMediaBuffer::wxMediaBuffer()
 : wxObject(WXGC_NO_CLEANUP)
{
  wxChangeRecord **crpa;

  map = new wxKeymap();
  // AddBufferFunctions(map);

  styleList = new wxStyleList;
  styleList->NewNamedStyle(STD_STYLE, NULL);
  notifyId = styleList->NotifyOnChange((wxStyleNotifyFunc)MediaStyleNotify, 
				       this, 1);

  filename = NULL;

  undomode = redomode = interceptmode = FALSE;
  maxUndos = NUM_MAX_UNDOS;
  crpa = MALLOC_CRP(maxUndos);
  changes = crpa;
  changes_start = changes_end = 0;
  crpa =  MALLOC_CRP(maxUndos);
  redochanges = crpa;
  redochanges_start = redochanges_end = 0;

  customCursor = NULL;

  loadoverwritesstyles = 1;

  noundomode = 0;

  ownCaret = FALSE;
  caretSnip = NULL; 

  pasteTextOnly = 0;

  InitCutNPaste();

  admin = NULL;
  // WXGC_IGNORE(admin);

#ifdef EACH_BUFFER_OWN_OFFSCREEN
  offscreen = NULL;
#endif

  if (!offscreen) {
    wxREGGLOB(offscreen);
    wxREGGLOB(bitmap);
    wxREGGLOB(lastUsedOffscreen);
    bitmap = NULL;
    offscreen = new wxMemoryDC();
    bmHeight = bmWidth = 0;
#ifndef wx_mac
    offscreen->SetOptimization(TRUE);
#endif
  }

  inactiveCaretThreshold = wxSNIP_DRAW_SHOW_INACTIVE_CARET;

#ifndef EACH_BUFFER_OWN_OFFSCREEN
  bcounter++;
#endif
}

wxMediaBuffer::~wxMediaBuffer()
{
#if ALLOW_X_STYLE_SELECTION
  if (wxMediaXSelectionOwner == this)
    wxMediaXSelectionOwner = NULL;
#endif

  if (map)
    SetKeymap(NULL);

  styleList->ForgetNotification(notifyId);

#ifndef EACH_BUFFER_OWN_OFFSCREEN
  --bcounter;
  if (!bcounter) {
#endif
    offscreen->SelectObject(NULL);
    DELETE_OBJ offscreen;
    offscreen = NULL;
    if (bitmap)
      DELETE_OBJ bitmap;
#ifndef EACH_BUFFER_OWN_OFFSCREEN
  }
#endif

  ClearUndos();
}

/******************************************************************/

void wxMediaBuffer::OnLocalEvent(wxMouseEvent *event)
{
  if (map) {
    Scheme_Object *edit;
    edit = objscheme_bundle_wxMediaBuffer(this);
    if (map->HandleMouseEvent(edit, event))
      return;
    else if (!event->Moving())
      map->BreakSequence();
  }

  OnDefaultEvent(event);
}

void wxMediaBuffer::OnLocalChar(wxKeyEvent *event)
{
  if (map) {
    Scheme_Object *edit;
    edit = objscheme_bundle_wxMediaBuffer(this);
    if (map->HandleKeyEvent(edit, event))
      return;
    else
      map->BreakSequence();
  }

  OnDefaultChar(event);
}

void wxMediaBuffer::OnFocus(Bool WXUNUSED(on))
{
}

/******************************************************************/

void wxMediaBuffer::SetAdmin(wxMediaAdmin *administrator)
{
  SettingAdmin(administrator);

  admin = administrator;
  if (!admin)
    ownCaret = FALSE;

  if (admin)
    InitNewAdmin();
}

void wxMediaBuffer::SettingAdmin(wxMediaAdmin *)
{
}

void wxMediaBuffer::InitNewAdmin()
{
}

wxMediaAdmin *wxMediaBuffer::GetAdmin(void)
{
  return admin;
}

/******************************************************************/

#define ShowsGhostCaret() 1

Bool wxMediaBuffer::DoOwnCaret(Bool ownit)
{
  Bool refresh;

  refresh = (!caretSnip && (((Bool)ownCaret != ownit) || ShowsGhostCaret()));

  ownCaret = ownit;
  if (caretSnip)
    caretSnip->OwnCaret(ownit);
  if (map && !ownit && refresh)
    map->BreakSequence();

#if ALLOW_X_STYLE_SELECTION
  if (ownit && !caretSnip)
    wxMediaXSelectionAllowed = this;
  else if (wxMediaXSelectionAllowed == this)
    wxMediaXSelectionAllowed = NULL;
#endif

  if (admin)
    admin->UpdateCursor();

  return refresh;
}

wxDC *wxMediaBuffer::GetDC()
{
  /* This can be called by snips to get a DC appropriate for
     sizing text, etc., outside of draws. It isn't the destination 
     for draws, though. */
  if (admin)
    return admin->GetDC(NULL, NULL);
  else
    return NULL;
}

void wxMediaBuffer::GetViewSize(float *w, float *h)
{
  if (admin)
    admin->GetView(NULL, NULL, w, h);
  else {
    if (w)
      *w = 0;
    if (h)
      *h = 0;
  }
}

Bool wxMediaBuffer::DoSetCaretOwner(wxSnip *snip, int dist)
{
  Bool hadCaret, visCaret;
  wxSnip *oldCaret;
  Bool refresh;

  if (PTREQ(snip, caretSnip)) {
    if (!admin || (dist == wxFOCUS_IMMEDIATE))
      return FALSE;
    admin->GrabCaret(dist);
  }

  refresh = FALSE;

  visCaret = ownCaret || ShowsGhostCaret();

  if (!snip || !(snip->flags & wxSNIP_HANDLES_EVENTS)) {
    wxSnip *_oldCaret = caretSnip;  
    caretSnip = NULL;
    if (_oldCaret) {
      _oldCaret->OwnCaret(FALSE);
      if (visCaret)
	refresh = TRUE;
    }
#if ALLOW_X_STYLE_SELECTION
    wxMediaXSelectionAllowed = this;
#endif
    if (admin)
      admin->UpdateCursor();
    return refresh;
  }

  if (!GetSnipLocation(snip, NULL, NULL))
    return refresh;

  if (!ownCaret)
    hadCaret = FALSE;
  else
    hadCaret = !caretSnip;

  oldCaret = caretSnip;
  caretSnip = snip;

  BeginEditSequence();
  if (oldCaret)
    oldCaret->OwnCaret(FALSE);
  else if (visCaret)
    refresh = TRUE;
  snip->OwnCaret(ownCaret);
  EndEditSequence();

  if (admin && (dist != wxFOCUS_IMMEDIATE))
    admin->GrabCaret(dist);

  if (admin)
    admin->UpdateCursor();

  return refresh;
}

static void ConvertCoords(wxMediaAdmin *admin, float *x, float *y, int toLocal)
{
  float lx = 0, ly = 0;

  if (admin) {
    if (admin->__type == wxTYPE_MEDIA_SNIP_MEDIA_ADMIN) {
      wxMediaSnip *snip;
      wxSnipAdmin *sa;

      snip = ((wxMediaSnipMediaAdmin *)admin)->GetSnip();
      sa = snip->GetAdmin();

      if (sa) {
	wxMediaBuffer *mbuf;
	mbuf = sa->GetMedia();
	if (mbuf) {
	  float bx = 0, by = 0;
	  int l, t, r, b;
	  
	  mbuf->LocalToGlobal(&bx, &by);
	  mbuf->GetSnipLocation(snip, &lx, &ly, 0);
	  lx += bx;
	  ly += by;

	  snip->GetMargin(&l, &t, &r, &b);
	  lx += l;
	  ly += t;
	}
      }
    } else {
      admin->GetDC(&lx, &ly);
      lx = -lx;
      ly = -ly;
    }
  }

  if (toLocal) {
    if (x)
      *x -= lx;
    if (y)
      *y -= ly;
  } else {
    if (x)
      *x += lx;
    if (y)
      *y += ly;
  }
}

void wxMediaBuffer::GlobalToLocal(float *x, float *y)
{
  ConvertCoords(admin, x, y, 1);
}

void wxMediaBuffer::LocalToGlobal(float *x, float *y)
{
  ConvertCoords(admin, x, y, 0);
}

void wxMediaBuffer::SetCursor(wxCursor *c, Bool override)
{  
  customCursor = c;
  customCursorOverrides = override;

  if (admin)
    admin->UpdateCursor();
}

/******************************************************************/

#ifndef ROUND
#include <math.h>
#define ROUND(x) (int)floor(x)
#endif

#define REDICULOUS_SIZE 2000

Bool wxMediaBuffer::ReadyOffscreen(float width, float height)
{
  if ((width > REDICULOUS_SIZE)
      || (height > REDICULOUS_SIZE))
    return FALSE;

  if (!offscreenInUse && (height > bmHeight || width > bmWidth)) {
    wxBitmap *oldbm = bitmap;

    bmWidth = ROUND(width);
    bmHeight = ROUND(height);

    bitmap = new wxBitmap(bmWidth, bmHeight);

    offscreen->SelectObject(NULL);
    if (oldbm)
      DELETE_OBJ oldbm;
  
    if (bitmap->Ok())
      offscreen->SelectObject(bitmap);

    return TRUE;
  } 
  
  return FALSE;
}

/******************************************************************/

void wxMediaBuffer::SetKeymap(wxKeymap *keymap)
{
  map = keymap;
}

wxKeymap *wxMediaBuffer::GetKeymap(void)
{
  return map;
}

wxStyleList *wxMediaBuffer::GetStyleList(void)
{
  return styleList;
}

void wxMediaBuffer::SetStyleList(wxStyleList *newList)
{
  styleList->ForgetNotification(notifyId);
  notifyId = newList->NotifyOnChange((wxStyleNotifyFunc)MediaStyleNotify, 
				     this, 1);
  styleList = newList;

  if (!styleList->FindNamedStyle(STD_STYLE))
    styleList->NewNamedStyle(STD_STYLE, NULL);
}

static void MediaStyleNotify(wxStyle *which, wxMediaBuffer *media)
{
  if (media)
    media->StyleHasChanged(which);
}

/******************************************************************/

int wxMediaBuffer::AppendEditItems(wxMenu *, int)
{
  return 0;
}

void wxMediaBuffer::DoEdit(int op, Bool recursive, long time)
{  
  if (recursive && caretSnip) {
    caretSnip->DoEdit(op, TRUE, time);
    return;
  }

  switch(op) {
  case wxEDIT_UNDO:
    Undo();
    break;
  case wxEDIT_REDO:
    Redo();
    break;
  case wxEDIT_CLEAR:
    Clear();
    break;
  case wxEDIT_CUT:
    Cut(FALSE, time);
    break;
  case wxEDIT_COPY:
    Copy(FALSE, time);
    break;
  case wxEDIT_PASTE:
    Paste(time);
    break;
  case wxEDIT_KILL:
    Kill(time);
    break;
  case wxEDIT_INSERT_TEXT_BOX:
    InsertBox(wxEDIT_BUFFER);
    break;
  case wxEDIT_INSERT_GRAPHIC_BOX:
    InsertBox(wxPASTEBOARD_BUFFER);
    break;
  case wxEDIT_INSERT_IMAGE:
    InsertImage();
    break;
  case wxEDIT_SELECT_ALL:
    SelectAll();
    break;
  }
}

int wxMediaBuffer::AppendFontItems(wxMenu *, int)
{
  return 0;
}

void wxMediaBuffer::DoFont(int, Bool)
{
  return;
}

void wxMediaBuffer::InsertBox(int type)
{
  wxSnip *snip;

  snip = OnNewBox(type);
  if (!snip)
    return;

  BeginEditSequence();
  snip->style = styleList->FindNamedStyle(STD_STYLE);
  if (!snip->style) {
    wxStyle *bs;
    bs = styleList->BasicStyle();
    snip->style = bs;
  }
  Insert(snip);
  SetCaretOwner(snip);
  EndEditSequence();
}

wxSnip *wxMediaBuffer::OnNewBox(int type)
{
  wxMediaSnip *snip;
  wxMediaBuffer *media;

  if (type == wxEDIT_BUFFER)
    media = new wxMediaEdit();
  else
    media = new wxMediaPasteboard();
  snip = new wxMediaSnip(media);

  media->SetKeymap(map);  
  media->SetStyleList(styleList);

  return snip;
}

void wxMediaBuffer::InsertImage(char *filename, long type, Bool relative, Bool inlineImg)
{
  wxImageSnip *snip;
      
  if (!filename) {
    filename = GetFile(NULL);
  }
  
  if (!filename)
    return;

  snip = OnNewImageSnip(filename, type, relative, inlineImg);
  Insert(snip);
}

wxImageSnip *wxMediaBuffer::OnNewImageSnip(char *filename, long type, 
					   Bool relative, Bool inlineImg)
{
  return new wxImageSnip(filename, type, relative, inlineImg);
}

/**********************************************************************/

wxBufferData *wxMediaBuffer::GetSnipData(wxSnip *)
{
  return NULL;
}

void wxMediaBuffer::SetSnipData(wxSnip *, wxBufferData *)
{
}

/**********************************************************************/

Bool wxReadMediaGlobalHeader(wxMediaStreamIn *f)
{
  f->scl->ResetHeaderFlags(f);
  if (!f->scl->Read(f))
    return FALSE;

  wxmbSetupStyleReadsWrites(f);

  return f->bdl->Read(f);
}

Bool wxReadMediaGlobalFooter(wxMediaStreamIn *f)
{
  wxmbDoneStyleReadsWrites(f);
  f->scl->ResetHeaderFlags(f);

  return TRUE;
}

Bool wxWriteMediaGlobalHeader(wxMediaStreamOut *f)
{
  f->scl->ResetHeaderFlags(f);
  if (!f->scl->Write(f))
    return FALSE;

  wxmbSetupStyleReadsWrites(f);

  return f->bdl->Write(f);
}

Bool wxWriteMediaGlobalFooter(wxMediaStreamOut *f)
{
  wxmbDoneStyleReadsWrites(f);
  f->scl->ResetHeaderFlags(f);

  return TRUE;
}

/**********************************************************************/

int wxmeCheckFormatAndVersion(wxMediaStream *s)
{
  if (strcmp(s->read_format, MRED_FORMAT_STR)) {
    wxmeError("Unknown format number.");
    return 0;
  }
  if (strcmp(s->read_version, MRED_VERSION_STR)
      && strcmp(s->read_version, "01")
      && strcmp(s->read_version, "02")) {
    wxmeError("Unknown version number.");
    return 0;
  }

  return 1;
}

Bool wxMediaBuffer::ReadHeaderFromFile(wxMediaStreamIn *, char *headerName)
{
  char buffer[256];

  sprintf(buffer, "Unknown header data: \"%.100s\"."
	  " The file will be loaded anyway.", headerName);

  wxmeError(buffer);

  return TRUE;
}

Bool wxMediaBuffer::ReadFooterFromFile(wxMediaStreamIn *, char *headerName)
{
  char buffer[256];

  sprintf(buffer, "Unknown header data: \"%.100s\"."
	  " The file will be loaded anyway.", headerName);

  wxmeError(buffer);

  return TRUE;
}

Bool wxMediaBuffer::WriteHeadersToFile(wxMediaStreamOut *WXUNUSED(f))
{
  return TRUE;
}

Bool wxMediaBuffer::WriteFootersToFile(wxMediaStreamOut *WXUNUSED(f))
{
  return TRUE;
}

Bool wxMediaBuffer::BeginWriteHeaderFooterToFile(wxMediaStreamOut *f, 
						 char *headerName,
						 long *dataBuffer)
{
  long db;
  db = f->Tell();
  *dataBuffer = db;
  f->PutFixed(0);
  f->Put(headerName);

  return TRUE;
}

Bool wxMediaBuffer::EndWriteHeaderFooterToFile(wxMediaStreamOut *f, 
					       long dataBuffer)
{
  long end, pos;

  end = f->Tell();

  f->JumpTo(dataBuffer);
  f->PutFixed(0);
  pos = f->Tell();
  
  f->JumpTo(dataBuffer);
  f->PutFixed(end - pos);

  f->JumpTo(end);

  numExtraHeaders++;
  
  return TRUE;
}

Bool wxMediaBuffer::ReadHeadersFooters(wxMediaStreamIn *f, Bool headers)
{
  char headerName[256];
  long len, hlen, i, pos, numHeaders;

  f->GetFixed(&numHeaders);
#if 0
  if (numHeaders > 1000) {
    if (wxMessageBox("File contains suspiciously large value for special"
		     " headers/footers. Give up?", "Warning",
		     wxYES_NO | wxCENTRE) == wxYES)
      return FALSE;
  }
#endif

  for (i = 0; i < numHeaders; i++) {
    f->GetFixed(&len);
    if (!f->Ok())
      return FALSE;
    if (len) {
      pos = f->Tell();
      f->SetBoundary(len);
      hlen = 256;
      f->Get((long *)&hlen, (char *)headerName);
      if (headers) {
	if (!ReadHeaderFromFile(f, headerName))
	  return FALSE;
      } else {
	if (!ReadFooterFromFile(f, headerName))
	  return FALSE;
      }
      if (!f->Ok())
	return FALSE;
      f->RemoveBoundary();
      if (len -= (f->Tell() - pos))
	f->Skip(len);
      if (!f->Ok())
	return FALSE;
    }
  }

  return TRUE;
}

Bool wxMediaBuffer::DoWriteHeadersFooters(wxMediaStreamOut *f, Bool headers)
{
  long allStart, allEnd;

  allStart = f->Tell();
  f->PutFixed(0);
  numExtraHeaders = 0;

  if (headers) {
    if (!WriteHeadersToFile(f))
      return FALSE;
  } else {
    if (!WriteFootersToFile(f))
      return FALSE;
  }

  if (numExtraHeaders) {
    allEnd = f->Tell();
    f->JumpTo(allStart);
    f->PutFixed(numExtraHeaders);
    f->JumpTo(allEnd);
  }

  return TRUE;
}

/**********************************************************************/

static wxBufferData *ReadBufferData(wxMediaStreamIn *f)
{
  wxBufferData *data, *newdata;
  wxBufferDataClass *dclass;
  int extraDataIndex;
  long datalen;

  data = NULL;
  do {
    f->Get(&extraDataIndex);
    if (extraDataIndex) {
      dclass = f->bdl->FindByMapPosition(f, extraDataIndex);
      
      if (!dclass || !dclass->required)
	f->Get(&datalen);
      else
	datalen = -1;
      
      if (dclass) {
	long start;
	start = f->Tell();
	if (datalen >= 0)
	  f->SetBoundary(datalen);
	if (!(newdata = dclass->Read(f)))
	  return FALSE;
	newdata->next = data;
	data = newdata;
	if (datalen >= 0) {
	  long rcount;
	  rcount = f->Tell() - start;
	  if (rcount < datalen) {
	    wxmeError("Warning: underread caused by file "
		      "corruption or unknown internal error.");
	    f->Skip(datalen - rcount);
	  }
	  f->RemoveBoundary();
	}
      } else {
	/* Unknown extra data */
	f->Skip(datalen);
      }
      if (!f->Ok())
	return FALSE;
    }
  } while (extraDataIndex);

  return data;
}

Bool wxMediaBuffer::ReadSnipsFromFile(wxMediaStreamIn *f, Bool overwritestylename)
{
  long len, numHeaders, numSnips, i, listId;
  int styleIndex;
  short n;
  wxStyleList *newList;
  wxSnipClass *sclass;
  wxBufferData *data;
  wxSnip *snip;

  if (!ReadHeadersFooters(f, TRUE))
    return FALSE;

  if (!(newList = wxmbReadStylesFromFile(styleList, f, overwritestylename, &listId)))
    return FALSE;

  if (PTRNE(newList, styleList))
    SetStyleList(newList);
  
  f->GetFixed(&numHeaders);
  
#if 0
  if (numHeaders > 100) {
    if (wxMessageBox("File contains suspiciously large value for class"
		     " headers. Give up?", "Warning",
		     wxYES_NO | wxCENTRE) == wxYES)
      return FALSE;
  }
#endif

  for (i = 0; i < numHeaders; i++) {
    f->Get(&n);
    f->GetFixed(&len);
    if (!f->Ok())
      return FALSE;
    if (len) {
      sclass = f->scl->FindByMapPosition(f, n);
      if (sclass) {
	long start, rcount;
	start = f->Tell();

	f->SetBoundary(len);
	if (!sclass->ReadHeader(f))
	  return FALSE;
	if (!f->Ok())
	  return FALSE;
	f->SetHeaderFlag(sclass);

	rcount = f->Tell() - start;
	if (rcount < len) {
	  wxmeError("Warning: underread caused by file "
		    "corruption or unknown internal error.");
	  f->Skip(len - rcount);
	}
	
	f->RemoveBoundary();
      } else {
	f->Skip(len);
      }
      if (!f->Ok())
	return FALSE;
    }
  }

  f->Get(&numSnips);

  for (i = 0; i < numSnips; i++) {
    f->Get(&n);
    if (n >= 0) {
      sclass = f->scl->FindByMapPosition(f, n);
    } else
      sclass = NULL; /* -1 => unknown */
    if (!sclass || !sclass->required)
      f->GetFixed(&len);
    else
      len = -1;
    if (!f->Ok())
      return FALSE;
    if (len) {
      if (sclass) {
	long start;
	start = f->Tell();

	if (len >= 0)
	  f->SetBoundary(len);
	f->Get(&styleIndex);
	if ((snip = sclass->Read(f))) {
	  if (snip->flags & wxSNIP_OWNED)
	    snip->flags -= wxSNIP_OWNED;
	  snip->style = styleList->MapIndexToStyle(f, styleIndex, listId);
	  if (!snip->style) {
	    wxStyle *bs;
	    bs = styleList->BasicStyle();
	    snip->style = bs;
	  }
	  if (!ReadInsert(snip))
	    return FALSE;
	} else
	  return FALSE;

	data = ReadBufferData(f);
	if (!f->Ok())
	  return FALSE;

	if (data)
	  SetSnipData(snip, data);

	if (len >= 0) {
	  long rcount;
	  rcount = f->Tell() - start;
	  if (rcount < len) {
	    wxmeError("Warning: underread caused by file "
		      "corruption or unknown internal error.");
	    f->Skip(len - rcount);
	  }
	  f->RemoveBoundary();
	}
      } else {
	f->Skip(len);
      }
      if (!f->Ok())
	return FALSE;
    }
  }

  if (!ReadHeadersFooters(f, FALSE))
    return FALSE;

  return TRUE;
}

Bool wxmbWriteBufferData(wxMediaStreamOut *f, wxBufferData *data)
{
  long dataPos = 0, dataStart = 0, dataEnd;
  
  while (data) {
    short mp;
    mp = (short)f->MapPosition(data->dataclass);
    f->Put(mp);
    
    if (!data->dataclass->required) {
      dataStart = f->Tell();
      f->PutFixed(0);
      dataPos = f->Tell();
    }

    if (!data->Write(f))
      return FALSE;
    
    if (!data->dataclass->required) {
      dataEnd = f->Tell();
      f->JumpTo(dataStart);
      f->PutFixed(dataEnd - dataPos);
      f->JumpTo(dataEnd);
    }
    data = data->next;
  }

  f->Put(0);

  return TRUE;
}

Bool wxmbWriteSnipsToFile(wxMediaStreamOut *f, 
			  wxStyleList *styleList,
			  wxList *snipList, 
			  wxSnip *startSnip, wxSnip *endSnip,
			  wxList *extraData,
			  wxMediaBuffer *buffer)
{
  long allStart, allEnd, headerPos, headerStart, headerEnd;
  long snipCount, snipPos = 0, snipStart = 0, snipEnd;
  long numHeaders;
  int styleIndex;
  wxNode *node = NULL, *node2;
  wxSnip *snip;
  wxSnipClass *sclass;
  wxBufferData *data;

  if (!wxmbWriteStylesToFile(styleList, f))
    return FALSE;
  
  allStart = f->Tell();
  f->PutFixed(0);

  if (snipList) {
    node = snipList->First();
    if (!node)
      return FALSE;
    startSnip = (wxSnip *)node->Data();
    endSnip = NULL;
  } else
    node = NULL;

  numHeaders = 0;
  snipCount = 0;
  for (snip = startSnip; PTRNE(snip, endSnip); snipCount++) {
    sclass = snip->snipclass;
    if (!sclass) {
      wxmeError("There's a snip without a class."
		" Data will be lost.");
    } else if (!f->GetHeaderFlag(sclass)) {
      short mp;
      mp = (short)f->MapPosition(sclass);
      f->Put(mp);
      headerStart = f->Tell();
      f->PutFixed(0);
      headerPos = f->Tell();
      if (!sclass->WriteHeader(f))
	return FALSE;
      f->SetHeaderFlag(sclass);
      headerEnd = f->Tell();
      f->JumpTo(headerStart);
      f->PutFixed(headerEnd - headerPos);
      f->JumpTo(headerEnd);
      numHeaders++;
      if (!f->Ok())
	return FALSE;
    }

    if (snipList) {
      node = node->Next();
      if (node)
	snip = (wxSnip *)node->Data();
      else
	snip = NULL;
    } else
      snip = snip->next;
  }

  allEnd = f->Tell();
  f->JumpTo(allStart);
  f->PutFixed(numHeaders);
  f->JumpTo(allEnd);

  f->Put(snipCount);

  if (snipList)
    node = snipList->First();
  else
    node = NULL;
  if (extraData)
    node2 = extraData->First();
  else
    node2 = NULL;

  for (snip = startSnip; PTRNE(snip, endSnip); ) {
    sclass = snip->snipclass;

    if (sclass) {
      short mp;
      mp = (short)f->MapPosition(sclass);
      f->Put(mp);
    } else
      f->Put((short)(-1));

    if (!snip->snipclass || !snip->snipclass->required) {
      snipStart = f->Tell();
      f->PutFixed(0);
      snipPos = f->Tell();
    }

    styleIndex = styleList->StyleToIndex(snip->style);
    if (styleIndex < 0) {
      wxmeError("Bad style discovered.");
      styleIndex = 0;
    }
    f->Put(styleIndex);

    snip->Write(f);
    if (node2)
      data = (wxBufferData *)node2->Data();
    else
      data = buffer->GetSnipData(snip);

    if (!wxmbWriteBufferData(f, data))
      return FALSE;

    if (!snip->snipclass || !snip->snipclass->required) {
      snipEnd = f->Tell();
      f->JumpTo(snipStart);
      f->PutFixed(snipEnd - snipPos);
      f->JumpTo(snipEnd);
    }

    if (!f->Ok())
      return FALSE;

    if (snipList) {
      node = node->Next();
      if (node)
	snip = (wxSnip *)node->Data();
      else
	snip = NULL;
    } else
      snip = snip->next;
    if (extraData)
      node2 = node2->Next();
  }

  return TRUE;
}

char *wxMediaBuffer::GetFilename(Bool *temp)
{
  if (temp)
    *temp = tempFilename;
  return filename;
}

#ifndef wx_xt
class wxMediaPrintout : public wxPrintout
{
private:
  wxMediaBuffer *b;
  void *data;

  Bool fitToPage;

public:
  wxMediaPrintout(wxMediaBuffer *buffer, Bool fit);

  Bool HasPage(int page);
  Bool OnPrintPage(int page);
  Bool OnBeginDocument(int startPage, int endPage);
  void OnEndDocument();
};

wxMediaPrintout::wxMediaPrintout(wxMediaBuffer *buffer, Bool fit)
: wxPrintout()
{
  b = buffer;
  fitToPage = fit;
}

Bool wxMediaPrintout::HasPage(int page)
{
  return b->HasPrintPage(GetDC(), page);
}

Bool wxMediaPrintout::OnPrintPage(int page)
{
  b->PrintToDC(GetDC(), page);
  
  return TRUE;
}

Bool wxMediaPrintout::OnBeginDocument(int startPage, int endPage)
{
  if (wxPrintout::OnBeginDocument(startPage, endPage)) {
    b->printing = GetDC();
    data = b->BeginPrint(b->printing, fitToPage);
    return TRUE;
  } else
    return FALSE;
}

void wxMediaPrintout::OnEndDocument()
{
  wxDC *pr = b->printing;
  b->printing = NULL;
  b->EndPrint(pr, data);
  wxPrintout::OnEndDocument();
  b->InvalidateBitmapCache(0, 0, -1, -1);
}
#endif

#ifdef wx_x
# define WXUNUSED_X(x) /* empty */
#else
# define WXUNUSED_X(x) x
#endif

void wxMediaBuffer::Print(Bool interactive, Bool fitToPage, int WXUNUSED_X(output_mode), wxWindow *parent)
{
  int ps;

#ifndef wx_x
  ps = (output_mode == 1);
#else
  ps = 1;
#endif

  if (!parent) {
    if (admin && (admin->standard > 0)) {
      wxWindow *w = ((wxCanvasMediaAdmin *)admin)->GetCanvas();

      while (w && !wxSubType(w->__type, wxTYPE_FRAME)
	     && !wxSubType(w->__type, wxTYPE_DIALOG_BOX))
	w = w->GetParent();

      if (w)
	parent = w;
    }
  }

  if (ps) {
    wxDC *dc;
    void *data;
    
    dc = new wxPostScriptDC(interactive, parent);

    if (dc->Ok()) { 
      dc->StartDoc("Printing buffer");

      printing = dc;
      data = BeginPrint(dc, fitToPage);

      PrintToDC(dc);

      printing = NULL;
      EndPrint(dc, data);

      dc->EndDoc();

      InvalidateBitmapCache(0, 0, -1, -1);
    }

    DELETE_OBJ dc;
    
    return;
  } 

#ifndef wx_x
  wxPrinter *p = new wxPrinter();
  wxPrintout *o = new wxMediaPrintout(this, fitToPage);
  
  p->Print(parent, o, interactive);

  DELETE_OBJ o;
  DELETE_OBJ p;
#endif
}

/****************************************************************/

void wxMediaBuffer::Undo(void)
{
  if (!undomode && !redomode) {
    undomode = TRUE;
    
    PerformUndos(changes, FALSE);
    
    undomode = FALSE;
  }
}

void wxMediaBuffer::Redo(void)
{ 
  if (!undomode && !redomode) {
    redomode = TRUE;
    
    PerformUndos(redochanges, TRUE);
    
    redomode = FALSE;
  }
}

#define delete_cgrec(x)  DELETE_OBJ (x)

static void wxmeClearUndos(wxChangeRecord **changes, int start, int end,
			   int maxUndos)
{
  int i;

  for (i = start; i != end; i = (i + 1) % maxUndos) {
    delete_cgrec(changes[i]);
    changes[i] = NULL;
  }
}

void wxMediaBuffer::AddUndo(wxChangeRecord *rec)
{
  if (interceptmode)
    intercepted->Append((wxObject *)rec);
  else if (undomode)
    AppendUndo(rec, redochanges, TRUE);
  else if (!noundomode) {
    if (!redomode) {
      wxmeClearUndos(redochanges, redochanges_start,
		     redochanges_end, maxUndos);
      redochanges_start = redochanges_end = 0;
    }
    AppendUndo(rec, changes, FALSE);
  } else
    delete_cgrec(rec);
}

void wxMediaBuffer::AddSchemeUndo(void *proc)
{
  wxSchemeModifyRecord *modrec;
  modrec = new wxSchemeModifyRecord(proc);
  AddUndo(modrec);
}

void wxMediaBuffer::AppendUndo(wxChangeRecord *rec, wxChangeRecord **changes, 
			       Bool redos)
{
  if (maxUndos) {
    int start, end;

    if (redos) {
      start = redochanges_start;
      end = redochanges_end;
    } else {
      start = changes_start;
      end = changes_end;
    }

    changes[end] = rec;
    end = (end + 1) % maxUndos;
    if (end == start) {
      delete_cgrec(changes[start]);
      changes[start] = NULL;
      start = (start + 1) % maxUndos;
    }
    
    if (redos) {
      redochanges_start = start;
      redochanges_end = end;
    } else {
      changes_start = start;
      changes_end = end;
    }
  } else
    delete_cgrec(rec);
}

void wxMediaBuffer::PerformUndos(wxChangeRecord **changes, Bool redos)
{
  wxChangeRecord *rec;
  Bool cont;
  int start, end;
  
  BeginEditSequence();

  if (redos) {
    start = redochanges_start;
    end = redochanges_end;
  } else {
    start = changes_start;
    end = changes_end;
  }

  while (start != end) {
    end = (end - 1 + maxUndos) % maxUndos;
    rec = changes[end];
    changes[end] = NULL;

    if (redos) {
      redochanges_start = start;
      redochanges_end = end;
    } else {
      changes_start = start;
      changes_end = end;
    }
    
    cont = rec->Undo(this);
    delete_cgrec(rec);
    if (!cont)
      break;
  }

  EndEditSequence();
}

void wxMediaBuffer::PerformUndoList(wxList *changes)
{
  wxNode *node;
  wxChangeRecord *rec;
  Bool cont = FALSE;
  
  BeginEditSequence();
  do {
    node = changes->Last();
    if (node) {
      rec = (wxChangeRecord *)node->Data();
      cont = rec->Undo(this);
      delete_cgrec(rec);
      changes->DeleteNode(node);
    }
  } while (node && cont);
  EndEditSequence();
}

void wxMediaBuffer::ClearUndos()
{
  wxmeClearUndos(changes, changes_start, changes_end, maxUndos);
  changes_start = changes_end = 0;
  wxmeClearUndos(redochanges, redochanges_start, redochanges_end, maxUndos);
  redochanges_start = redochanges_end = 0;
}

void wxMediaBuffer::SetMaxUndoHistory(int v)
{
  wxChangeRecord **naya;
  int i, j;

  if (undomode || redomode || (v == maxUndos))
    return;

  naya = MALLOC_CRP(v);
  for (j = 0, i = changes_start; 
       (i != changes_end) && (j < v); 
       j++, i = (i + 1) % maxUndos) {
    naya[j] = changes[i];
  }
  changes_start = 0;
  changes_end = v ? (j % v) : 0;

  naya = MALLOC_CRP(v);
  for (j = 0, i = redochanges_start; 
       (i != redochanges_end) && (j < v); 
       j++, i = (i + 1) % maxUndos) {
    naya[j] = redochanges[i];
  }
  redochanges_start = 0;
  redochanges_end = v ? (j % v) : v;

  maxUndos = v;
}

int wxMediaBuffer::GetMaxUndoHistory()
{
  return maxUndos;
}

/****************************************************************/

static int copyDepth = 0;

static int copyRingSize = 30;
static int copyRingPos = 0, copyRingMax = 0;

static wxList **copyRingBuffer1, **copyRingBuffer2;
static wxStyleList **copyRingStyle;
static wxBufferData **copyRingData;

wxList *wxmb_commonCopyBuffer = NULL;
wxList *wxmb_commonCopyBuffer2 = NULL;
wxStyleList *wxmb_copyStyleList = NULL;
wxBufferData *wxmb_commonCopyRegionData = NULL;

static int copyingSelf;

static void InitCutNPaste()
{
  if (!copyRingBuffer1) {
    wxREGGLOB(copyRingBuffer1);
    wxREGGLOB(copyRingBuffer2);
    wxREGGLOB(copyRingStyle);
    wxREGGLOB(copyRingData);

    copyRingBuffer1 = new wxList*[copyRingSize];
    copyRingBuffer2 = new wxList*[copyRingSize];
    copyRingStyle = new wxStyleList*[copyRingSize];
    copyRingData = new wxBufferData*[copyRingSize];

    wxREGGLOB(wxmb_commonCopyBuffer);
    wxREGGLOB(wxmb_commonCopyBuffer2);
    wxmb_commonCopyBuffer = new wxList();
    wxmb_commonCopyBuffer2 = new wxList();

    wxREGGLOB(wxmb_copyStyleList);
    wxREGGLOB(wxmb_commonCopyRegionData);
  }

  if (!TheMediaClipboardClient) {
    wxREGGLOB(TheMediaClipboardClient);
    TheMediaClipboardClient = new wxMediaClipboardClient;
#if ALLOW_X_STYLE_SELECTION
    wxREGGLOB(TheMediaXClipboardClient);
    wxREGGLOB(wxMediaXSelectionOwner);
    wxREGGLOB(wxMediaXSelectionAllowed);
    TheMediaXClipboardClient = new wxMediaXClipboardClient;
#endif
  }
}

void wxMediaBuffer::CopyRingNext(void)
{
  wxList *temp;
  wxStyleList *stemp;
  wxBufferData *dtemp;

  if (!copyRingMax)
    return;

  copyRingPos = copyRingPos - 1;
  if (copyRingPos < 1)
    copyRingPos = copyRingMax - 1;
  
  temp = wxmb_commonCopyBuffer;
  wxmb_commonCopyBuffer = copyRingBuffer1[copyRingPos];
  copyRingBuffer1[copyRingPos] = temp;

  temp = wxmb_commonCopyBuffer2;
  wxmb_commonCopyBuffer2 = copyRingBuffer2[copyRingPos];
  copyRingBuffer2[copyRingPos] = temp;
  
  dtemp = wxmb_commonCopyRegionData;
  wxmb_commonCopyRegionData = copyRingData[copyRingPos];
  copyRingData[copyRingPos] = dtemp;

  stemp = wxmb_copyStyleList;
  wxmb_copyStyleList = copyRingStyle[copyRingPos];
  copyRingStyle[copyRingPos] = stemp;
}

void wxMediaBuffer::BeginCopyBuffer(void)
{
  copyDepth++;
}

void wxMediaBuffer::EndCopyBuffer(void)
{
  --copyDepth;
}

void wxMediaBuffer::FreeOldCopies(void)
{
  if (!wxmb_copyStyleList)
    return;

  if (copyDepth > 1) {
    /* Delete current ring occupant: */
    wxmb_commonCopyBuffer->DeleteContents(TRUE);
    DELETE_OBJ wxmb_commonCopyBuffer;
    wxmb_commonCopyBuffer2->DeleteContents(TRUE);
    DELETE_OBJ wxmb_commonCopyBuffer2;

    if (wxmb_commonCopyRegionData)
      DELETE_OBJ wxmb_commonCopyRegionData;

    wxmb_commonCopyBuffer = new wxList();
    wxmb_commonCopyBuffer2 = new wxList();

    wxmb_commonCopyRegionData = NULL;

    wxmb_copyStyleList = NULL;

    return;
  }

  if (copyRingMax > copyRingPos) {
    /* Delete current ring occupant: */
    wxList *dl;
    dl = copyRingBuffer1[copyRingPos];
    dl->DeleteContents(TRUE);
    DELETE_OBJ dl;
    dl = copyRingBuffer2[copyRingPos];
    dl->DeleteContents(TRUE);
    DELETE_OBJ dl;

    if (copyRingData[copyRingPos]) {
      wxBufferData *data;
      data = copyRingData[copyRingPos];
      DELETE_OBJ data;
    }
  }

  copyRingBuffer1[copyRingPos] = wxmb_commonCopyBuffer;
  copyRingBuffer2[copyRingPos] = wxmb_commonCopyBuffer2;
  
  wxmb_commonCopyBuffer = new wxList();
  wxmb_commonCopyBuffer2 = new wxList();

  copyRingData[copyRingPos] = wxmb_commonCopyRegionData;
  wxmb_commonCopyRegionData = NULL;

  copyRingStyle[copyRingPos] = wxmb_copyStyleList;
  wxmb_copyStyleList = NULL;

  copyRingPos++;
  if (copyRingMax < copyRingPos)
    copyRingMax = copyRingPos;
  if (copyRingPos >= copyRingSize)
    copyRingPos = 0;
  if (copyRingMax >= copyRingSize)
    copyRingMax = 0;
}

void wxMediaBuffer::InstallCopyBuffer(long time, wxStyleList *sl)
{
  wxmb_copyStyleList = sl;

  if (copyingSelf != copyDepth) {
#if ALLOW_X_STYLE_SELECTION
    if (!xClipboardHack)
#endif
      wxTheClipboard->SetClipboardClient(TheMediaClipboardClient, time);
  }
}

void wxMediaBuffer::DoBufferPaste(long time, Bool local)
{
  wxClipboardClient *owner;
  wxNode *node, *node2;
  wxSnip *snip;
  wxBufferData *bd;

  owner = wxTheClipboard->GetClipboardClient();
  if (local || (!pasteTextOnly && PTREQ(owner, TheMediaClipboardClient))) {
    copyDepth++;
    for ((node = wxmb_commonCopyBuffer->First(),
	  node2 = wxmb_commonCopyBuffer2->First()); 
	 node; 
	 (node = node->Next(),
	  node2 = node2->Next())) {
      snip = (wxSnip *)node->Data();
      bd = (wxBufferData *)node2->Data();
      snip = snip->Copy();
      InsertPasteSnip(snip, bd);
    }
    --copyDepth;
    if (wxmb_commonCopyRegionData && bufferType == wxEDIT_BUFFER)
      ((wxMediaEdit *)this)->PasteRegionData(wxmb_commonCopyRegionData);
  } else {
    char *str;
    long len;

    if (!pasteTextOnly && (str = wxTheClipboard->GetClipboardData("WXME", &len, time))) {
      wxMediaStreamInStringBase *b;
      wxMediaStreamIn *mf;

      b = new wxMediaStreamInStringBase(str, len);
      mf = new wxMediaStreamIn(b);

      strcpy(mf->read_format, MRED_FORMAT_STR);
      strcpy(mf->read_version, MRED_VERSION_STR);

      if (wxReadMediaGlobalHeader(mf))
	if (mf->Ok())
	  if (ReadFromFile(mf)) {
	    wxBufferData *data;
	    data = ReadBufferData(mf);
	    if (data && bufferType == wxEDIT_BUFFER)
	      ((wxMediaEdit *)this)->PasteRegionData(data);
	  }
      wxReadMediaGlobalFooter(mf);
    } else {
      str = wxTheClipboard->GetClipboardString(time);
      InsertPasteString(str);
    }
  }
}

wxMediaClipboardClient::wxMediaClipboardClient()
{
  formats->Add("TEXT");
  formats->Add("WXME");
}

void wxMediaBuffer::CopySelfTo(wxMediaBuffer *m)
{
  /* Copy all the snips: */
  wxList *saveBuffer, *copySnips;
  wxList *saveBuffer2, *copySnips2;
  wxStyleList *saveStyles;
  wxBufferData *saveData;
  int save_cs;
  Bool t;
  char *f;
  wxNode *node, *node2;

  /* Copy style list */
  m->styleList->Copy(styleList);

  /* Copy all the snips: */
  saveBuffer = wxmb_commonCopyBuffer;
  saveBuffer2 = wxmb_commonCopyBuffer2;
  saveStyles = wxmb_copyStyleList;
  saveData = wxmb_commonCopyRegionData;
  save_cs = copyingSelf;

  m->BeginEditSequence();

  copySnips = new wxList();
  wxmb_commonCopyBuffer = copySnips;
  copySnips2 = new wxList();
  wxmb_commonCopyBuffer2 = copySnips2;
  wxmb_copyStyleList = NULL;
  wxmb_commonCopyRegionData = NULL;
  copyingSelf = copyDepth + 1;
  if (bufferType == wxEDIT_BUFFER) {
    wxMediaEdit *e = (wxMediaEdit *)this;
    int pos;
    pos = e->LastPosition();
    e->Copy(TRUE, 0, 0, pos);
  } else {
    wxMediaPasteboard *pb = (wxMediaPasteboard *)this;
    wxSnip *s;
    wxNode *n;
    wxList *unselect;
    unselect = new wxList();
    
    BeginEditSequence();
    for (s = pb->FindFirstSnip(); s; s = s->Next()) {
      if (!pb->IsSelected(s)) {
        pb->AddSelected(s);
        unselect->Append(s);
      }
    }
    pb->Copy(TRUE, 0);
    for (n = unselect->First(); n; n = n->Next()) {
      wxSnip *snp;
      snp = (wxSnip *)n->Data();
      pb->RemoveSelected(snp);
    }
    EndEditSequence();
  }
  wxmb_commonCopyBuffer = saveBuffer;
  wxmb_commonCopyBuffer2 = saveBuffer2;
  wxmb_copyStyleList = saveStyles;
  wxmb_commonCopyRegionData = saveData;
  copyingSelf = save_cs;

  node = copySnips->First();
  node2 = copySnips2->First();
  for (; node; node = node->Next(), node2 = node2->Next()) {
    wxSnip *s;
    wxBufferData *bfd;
    s = (wxSnip *)node->Data();
    if (m->bufferType == wxEDIT_BUFFER)
      m->Insert(s);
    else {
      wxMediaPasteboard *pb = (wxMediaPasteboard *)m;
      pb->Insert(s, s); /* before itself -> at end */
    }
    bfd = (wxBufferData *)node2->Data();
    m->SetSnipData(s, bfd);
  }

  /* Don't delete the snips themselves, though */
  DELETE_OBJ copySnips;
  DELETE_OBJ copySnips2;

  m->SizeCacheInvalid();

  {
    float mw, mh;
    mw = GetMinWidth();
    m->SetMinWidth(mw);
    mw = GetMaxWidth();
    m->SetMaxWidth(mw);
    mh = GetMinHeight();
    m->SetMinHeight(mh);
    mh = GetMaxHeight();
    m->SetMaxHeight(mh);
  }
 
  f = GetFilename(&t);
  m->SetFilename(f, t);

  m->SetMaxUndoHistory(GetMaxUndoHistory());

  m->SetKeymap(GetKeymap());

  m->SetInactiveCaretThreshold(GetInactiveCaretThreshold());
  m->SetLoadOverwritesStyles(GetLoadOverwritesStyles());

  m->EndEditSequence();
}

char *wxMediaClipboardClient::GetData(char *format, long *size)
{
  wxNode *node;
  wxSnip *snip;
  long l, length = 0, sz = 0;
  char *str, *total = NULL, *old;

  if (!strcmp(format, "TEXT")) {
    for (node = wxmb_commonCopyBuffer->First(); node; node = node->Next()) {
      snip = (wxSnip *)node->Data();

      str = snip->GetText(0, snip->count, TRUE);
      l = strlen(str);

      if (total) {
	if (length + l + 1 >= sz) {
	  sz = (2 * sz) + length + l + 1;
	  old = total;
	  total = new char[sz];
	  memcpy(total, old, length);
	}
	memcpy(total + length, str, l);
      } else
	total = str;
      length += l;
    }
    
    if (!total)
      total = new char[1];
    
    total[length] = 0;
    
    *size = length;

    return total;
  } else if (!strcmp(format, "WXME")) {
    wxMediaStreamOutStringBase *b;
    wxMediaStreamOut *mf;
    char *result;

    b = new wxMediaStreamOutStringBase();
    mf = new wxMediaStreamOut(b);

    wxWriteMediaGlobalHeader(mf);
    if (mf->Ok()) {
      mf->PutFixed(0);
      if (!wxmbWriteSnipsToFile(mf, wxmb_copyStyleList, wxmb_commonCopyBuffer, 
				NULL, NULL, wxmb_commonCopyBuffer2, NULL))
	return FALSE;
      mf->PutFixed(0);
      wxmbWriteBufferData(mf, wxmb_commonCopyRegionData);
    }
    wxWriteMediaGlobalFooter(mf);

    result = b->GetString(size);
    return result;
  } else {
    *size = 0;
    return "";
  }
}  

void wxMediaClipboardClient::BeingReplaced(void)
{
}

#if ALLOW_X_STYLE_SELECTION

wxMediaXClipboardClient::wxMediaXClipboardClient()
{
  formats->Add("TEXT");
  formats->Add("WXME");
}

char *wxMediaXClipboardClient::GetData(char *format, long *size)
{
  if (!xSelectionCopied && !wxMediaXSelectionOwner) {
    *size = 0;
    return "";
  }

  if (!xSelectionCopied || wxMediaXSelectionOwner) {
    xClipboardHack = TRUE;
    wxMediaXSelectionOwner->Copy(FALSE, 0L);
    xClipboardHack = FALSE;
  }

  /* If nothing is copied (e.g., DoCopy is overriden to not copy anything
     or copies directly to clipboard): */
  if (!wxmb_copyStyleList) {
    if (wxTheClipboard->GetClipboardClient() == this)
      return NULL;
    else
      return wxTheClipboard->GetClipboardData(format, size, 0);
  }

  return TheMediaClipboardClient->GetData(format, size);
}

void wxMediaXClipboardClient::BeingReplaced(void)
{
  if (wxMediaXSelectionOwner) {
    wxMediaBuffer *b = wxMediaXSelectionOwner;
    wxMediaXSelectionOwner= NULL;
    xSelectionCopied = FALSE;
    b->OwnXSelection(FALSE, TRUE, FALSE);
  } else
    xSelectionCopied = FALSE;
}

#endif

/****************************************************************/

#if ALLOW_X_STYLE_SELECTION

Bool wxMediaBuffer::DoOwnXSelection(Bool on, Bool force)
{
  if (on) {
    if (!force && wxMediaXSelectionAllowed != this)
      return FALSE;
    if (wxMediaXSelectionOwner) {
      wxMediaXSelectionOwner->OwnXSelection(FALSE, TRUE, FALSE);
      wxMediaXSelectionOwner = NULL; // should be redundant
    }
    xSelectionCopied = FALSE;
    wxTheClipboard->SetClipboardClient(TheMediaXClipboardClient, 0L);
    wxMediaXSelectionOwner = this;
  } else if (this == wxMediaXSelectionOwner) {
    wxMediaXSelectionOwner = NULL;
    if (!xSelectionCopied
	&& PTREQ(wxTheClipboard->GetClipboardClient(), 
		 TheMediaXClipboardClient)) {
      wxTheClipboard->SetClipboardString("", 0L);
    }
  }

  return TRUE;
}

void wxMediaBuffer::CopyOutXSelection(void)
{
  if (this == wxMediaXSelectionOwner) {
    xClipboardHack = TRUE;
    wxMediaXSelectionOwner->Copy(FALSE, 0L);
    xClipboardHack = FALSE;
    xSelectionCopied = TRUE;
  }
}

#endif

void wxMediaSetXSelectionMode(Bool on)
{
#if ALLOW_X_STYLE_SELECTION
  wxMediaXSelectionMode = on;
  if (!on && PTREQ(wxTheClipboard->GetClipboardClient(), 
		   TheMediaXClipboardClient))
    wxTheClipboard->SetClipboardString("", 0L);
#endif
}

Bool wxMediaBuffer::GetPasteTextOnly(void)
{
  return pasteTextOnly;
}

void wxMediaBuffer::SetPasteTextOnly(Bool pto)
{
  pasteTextOnly = pto;
}


/****************************************************************/

void wxMediaBuffer::Lock(Bool lock)
{
  userLocked = lock;
}

Bool wxMediaBuffer::IsLocked()
{
  return userLocked;
}

Bool wxMediaBuffer::Modified(void)
{
  return modified;
}

void wxMediaBuffer::SetModified(Bool mod)
{
  modified = mod;

  if (!mod && !undomode) {
    /* Get rid of undos that reset the modification state. */
    int i;
    for (i = changes_end; i != changes_start; ) {
      wxChangeRecord *cr;
      i = (i - 1 + maxUndos) % maxUndos;
      cr = changes[i];
      cr->DropSetUnmodified();
    }
    for (i = redochanges_end; i != redochanges_start; ) {
      wxChangeRecord *cr;
      i = (i - 1 + maxUndos) % maxUndos;
      cr = redochanges[i];
      cr->DropSetUnmodified();
    }
  }
}

int wxMediaBuffer::GetInactiveCaretThreshold(void)
{
  return inactiveCaretThreshold;
}

void wxMediaBuffer::SetInactiveCaretThreshold(int v)
{
  inactiveCaretThreshold = v;
}


void wxMediaBuffer::OnPaint(Bool WXUNUSED(pre),
			    wxDC *WXUNUSED(dc), 
			    float WXUNUSED(l), float WXUNUSED(t), 
			    float WXUNUSED(r), float WXUNUSED(b), 
			    float WXUNUSED(dx), float WXUNUSED(dy),
			    int WXUNUSED(show_caret))
{
  /* Do nothing */
}

Bool wxMediaBuffer::CanSaveFile(char *WXUNUSED(filename), int WXUNUSED(format))
{
  return TRUE;
}

void wxMediaBuffer::OnSaveFile(char *WXUNUSED(filename), int WXUNUSED(format))
{
  /* do nothing */
}

void wxMediaBuffer::AfterSaveFile(Bool WXUNUSED(success))
{
  /* do nothing */
}

Bool wxMediaBuffer::CanLoadFile(char *WXUNUSED(filename), int WXUNUSED(format))
{
  return TRUE;
}

void wxMediaBuffer::OnLoadFile(char *WXUNUSED(filename), int WXUNUSED(format))
{
  /* do nothing */
}

void wxMediaBuffer::AfterLoadFile(Bool WXUNUSED(success))
{
  /* do nothing */
}

void wxMediaBuffer::OnEditSequence(void)
{
  /* Do nothing */
}

void wxMediaBuffer::AfterEditSequence(void)
{
  /* Do nothing */
}

void wxMediaBuffer::OnDisplaySize(void)
{
  /* Do nothing */
}

#ifdef wx_msw
#define WILDCARD "*.*"
#else
#define WILDCARD "*"
#endif

char *wxMediaBuffer::GetFile(char *path)
{
  return wxFileSelector("Choose a file", path, NULL,
			NULL, WILDCARD, wxOPEN, 
			NULL, 0, 0);
}
 
char *wxMediaBuffer::PutFile(char *path, char *suggested_name)
{
  return wxFileSelector("Save file as", path,
			suggested_name, NULL, WILDCARD, wxSAVE, 
			NULL, 0, 0);
}

void wxMediaBuffer::SetLoadOverwritesStyles(Bool b)
{
  loadoverwritesstyles = b;
}

Bool wxMediaBuffer::GetLoadOverwritesStyles()
{
  return loadoverwritesstyles;
}


/****************************************************************/

#define edf(name, action, kname) \
     static Bool ed_##name(void *vb, wxEvent *kname, void *) \
     { wxMediaBuffer *b; \
       b = objscheme_unbundle_wxMediaBuffer((Scheme_Object *)vb, NULL, 0); \
       if (!b) \
        return FALSE; \
       b->action; \
       return TRUE; } \

edf(copy, Copy(FALSE, event->timeStamp), event)
edf(copyappend, Copy(TRUE, event->timeStamp), event)
edf(paste, Paste(event->timeStamp), event)
edf(cut, Cut(FALSE, event->timeStamp), event)
edf(kill, Kill(event->timeStamp), event)
edf(cutappend, Cut(TRUE, event->timeStamp), event)
edf(undo, Undo(), WXUNUSED(event))
edf(redo, Redo(), WXUNUSED(event))
edf(delete, Clear(), WXUNUSED(event))
edf(select_all, SelectAll(), WXUNUSED(event))

void wxMediaBuffer::AddBufferFunctions(wxKeymap *tab)
{
  wxAddMediaBufferFunctions(tab);
}

void wxAddMediaBufferFunctions(wxKeymap *tab)
{
#define setf(name, func) tab->AddFunction(name, ed_##func, NULL)

  setf("copy-clipboard", copy);
  setf("copy-append-clipboard", copyappend);
  setf("cut-clipboard", cut);
  setf("cut-append-clipboard", cutappend);
  setf("paste-clipboard", paste);
  setf("delete-selection", delete);
  setf("clear-selection", delete);
  setf("delete-to-end-of-line", kill);

  setf("undo", undo);
  setf("redo", redo);

  setf("select-all", select_all);
}

/****************************************************************/

#ifdef MEMORY_USE_METHOD
long wxMediaBuffer::MemoryUse(void)
{
  return ((filename ? strlen(filename) + 1 : 0)
	  + (maxUndos * 2 * sizeof(wxChangeRecord*))
	  + wxObject::MemoryUse());
}
#endif

/****************************************************************/

wxStandardSnipAdmin::wxStandardSnipAdmin(wxMediaBuffer *m)  
{
#if USE_OLD_TYPE_SYSTEM
  __type = wxTYPE_MEDIA_SNIP_ADMIN;
#endif

  media = m;
  // WXGC_IGNORE(media);
}

wxMediaBuffer *wxStandardSnipAdmin::GetMedia(void)
{
  return media;
}

wxDC *wxStandardSnipAdmin::GetDC()
{
  return media->GetDC();
}

void wxStandardSnipAdmin::GetViewSize(float *w, float *h)
{
  GetView(NULL, NULL, w, h, NULL);
}

void wxStandardSnipAdmin::GetView(float *x, float *y, float *w, float *h, wxSnip *snip)
{
  wxMediaAdmin *admin;
  admin = media->GetAdmin();
    
  if (snip) {
    if (admin) {
      float mx, my, mh, mw, mr, mb, sl, st, sr, sb;

      admin->GetView(&mx, &my, &mw, &mh, FALSE);

      mb = my + mh;
      mr = mx + mw;
      if (media->GetSnipLocation(snip, &sl, &st, FALSE)) {
	float l, t, r, b;

	media->GetSnipLocation(snip, &sr, &sb, TRUE);
	
	l = (mx > sl ? mx : sl);
	t = (my > st ? my : st);
	r = (mr > sr ? sr : mr);
	b = (mb > sb ? sb : mb);
	
	if (x)
	  *x = l - sl;
	if (y)
	  *y = t - st;
	if (w)
	  *w = (r - l);
	if (h)
	  *h = (b - t);
	
	return;
      }
    }
  } else {
    if (admin) {
      admin->GetView(x, y, w, h, TRUE);
      return;
    }
  }

  if (x) *x = 0;
  if (y) *y = 0;
  if (w) *w = 0;
  if (h) *h = 0;
}

Bool wxStandardSnipAdmin::ScrollTo(wxSnip *s, float localx, float localy, 
				   float w, float h, Bool refresh, int bias)
{
  if (s->GetAdmin() == this)
    return media->ScrollTo(s, localx, localy, w, h, refresh, bias);
  else
    return FALSE;
}

void wxStandardSnipAdmin::SetCaretOwner(wxSnip *s, int dist)
{
  if (s->GetAdmin() == this)
    media->SetCaretOwner(s, dist);
}
 
void wxStandardSnipAdmin::Resized(wxSnip *s, Bool redraw_now)
{
  if (s->GetAdmin() == this)
    media->Resized(s, redraw_now);
}

Bool wxStandardSnipAdmin::Recounted(wxSnip *s, Bool redraw_now)
{
  if (s->GetAdmin() == this)
    return media->Recounted(s, redraw_now);
  else
    return FALSE;
}

void wxStandardSnipAdmin::NeedsUpdate(wxSnip *s, float localx, float localy, 
				      float w, float h)
{
  if (s->GetAdmin() == this)
    media->NeedsUpdate(s, localx, localy, w, h);
}

Bool wxStandardSnipAdmin::ReleaseSnip(wxSnip *snip)
{
  if (snip->GetAdmin() == this)
    return media->ReleaseSnip(snip);
  else
    return FALSE;
}

void wxStandardSnipAdmin::UpdateCursor()
{
  if (media->admin)
    media->admin->UpdateCursor();
}

#ifdef wx_mac

#include <Files.h>

long wxMediaCreatorId = 'WXME';

void wxMediaSetFileCreatorType(char *file, Bool is_binary)
{
  FSSpec spec;
  FInfo info;
  Str255 filename;

  memcpy(filename, file, 255);
  filename[255] = 0;
  CtoPstr((char *)filename);

  FSMakeFSSpec(0, 0, filename, &spec);

  FSpGetFInfo(&spec, &info);
  info.fdCreator = wxMediaCreatorId;
  info.fdType = is_binary ? 'WXME' : 'TEXT';
  FSpSetFInfo(&spec, &info);
}

#endif
