Esempio n. 1
0
CVHoughCircles::CVHoughCircles()
	: CVDataFilter(CVFilterID::HOUGH_CIRCLES)
{
	//minimumDistance, param1, param2, minimumRadius, maximumRadius
	method = new CVFilterSetting("Method", 3);
	minimumDistance = new CVFilterSetting("Min distance", 10);
	param1 = new CVFilterSetting("param1", 50.f);
	param2 = new CVFilterSetting("param2", 50.f);
	minimumRadius = new CVFilterSetting("Min radius", 10.f);
	maximumRadius = new CVFilterSetting("Max radius", 100.f);
	dp = new CVFilterSetting("dp", 1.f);

	settings.Add(method);
	settings.Add(dp);
	settings.Add(minimumDistance);
	settings.Add(param1);
	settings.Add(param2);
	settings.Add(minimumRadius);
	settings.Add(maximumRadius);
	
	List<String> rows;
	rows += "Yo";
	rows += "CV_HOUGH_STANDARD =0,";
    rows += "CV_HOUGH_PROBABILISTIC =1,";
    rows += "CV_HOUGH_MULTI_SCALE =2,";
    rows += "CV_HOUGH_GRADIENT =3";
	about = MergeLines(rows, "\n");
}
	//=============================================================================
	//
	//
	//
	//=============================================================================
	void SetReferences()
	{
		for(unsigned i = 0; i < SectionLines.Size(); i++)
		{
			FGLSectionLine *ln = &SectionLines[i];
			seg_t *seg = ln->refseg;

			if (seg != NULL)
			{
				seg_t *partner = seg->PartnerSeg;

				if (seg->PartnerSeg == NULL)
				{
					ln->otherside = -1;
				}
				else
				{
					ln->otherside = section_for_segs[partner-segs];
				}
			}
			else
			{
				ln->otherside = -1;
			}
		}

		for(unsigned i = 0; i < SectionLoops.Size(); i++)
		{
			MergeLines(&SectionLoops[i]);
		}
	}
Esempio n. 3
0
inline void
WaveComputer::FoundWave(const WaveInfo &new_wave)
{
  /* check if we can merge the new wave with an existing one to
     unclutter the screen */

  FlatProjection projection(new_wave.location);
  const double new_length = new_wave.GetLength();

  FlatLine new_line(projection.ProjectFloat(new_wave.a),
                    projection.ProjectFloat(new_wave.b));
  if (new_line.a.x > new_line.b.x)
    /* rearrange a and b to get well-defined order */
    std::swap(new_line.a, new_line.b);

  for (auto &i : waves)
    if (MergeLines(i, new_wave, new_length, new_line, projection))
      return;

  waves.push_front(new_wave);
}
Esempio n. 4
0
/**
 * Check if the two wave lines match, and attempt to merge them.
 *
 * TODO: this is clumsy and unoptimised code; please improve!
 *
 * @return true if the lines have been merged into #i
 */
static bool
MergeLines(WaveInfo &i, const WaveInfo &new_wave, double new_length,
           FlatLine new_line, const FlatProjection &projection)
{
  Angle delta_angle = (new_wave.normal - i.normal).AsDelta();
  if (delta_angle > Angle::QuarterCircle())
    delta_angle -= Angle::HalfCircle();
  else if (delta_angle < -Angle::QuarterCircle())
    delta_angle += Angle::HalfCircle();

  /* basic direction check */
  constexpr Angle max_delta_angle = Angle::Degrees(20);
  if (delta_angle > max_delta_angle || delta_angle < -max_delta_angle)
    return false;

  /* basic range check */
  const double max_distance = std::max(new_length, i.GetLength());
  if (i.a.DistanceS(new_wave.a) > max_distance &&
      i.b.DistanceS(new_wave.a) > max_distance &&
      i.a.DistanceS(new_wave.b) > max_distance &&
      i.a.DistanceS(new_wave.b) > max_distance)
    return false;

  FlatLine other_line(projection.ProjectFloat(i.a),
                      projection.ProjectFloat(i.b));
  if (other_line.a.x > other_line.b.x)
    /* rearrange a and b to get well-defined order */
    std::swap(other_line.a, other_line.b);

  if (!MergeLines(other_line, new_line))
    return false;

  i.a = projection.Unproject(other_line.a);
  i.b = projection.Unproject(other_line.b);
  Angle bearing = i.a.Bearing(i.b);
  i.normal = (bearing + Angle::QuarterCircle()).AsBearing();
  i.time = new_wave.time;

  return true;
}
Esempio n. 5
0
CVShiTomasiCorners::CVShiTomasiCorners()
	: CVDataFilter(CVFilterID::SHI_TOMASI_CORNERS)
{
	maxCorners = new CVFilterSetting("Max corners", 32);
	settings.Add(maxCorners);
	qualityLevel = new CVFilterSetting("Quality", 0.01f);
	settings.Add(qualityLevel);
	minimumDistance = new CVFilterSetting("Min distance", 10.f);
	settings.Add(minimumDistance);
	blockSize = new CVFilterSetting("Block size", 3);
	settings.Add(blockSize);
	k = new CVFilterSetting("k", 0.04f);
	settings.Add(k);
	useHarrisDetector = new CVFilterSetting("Use Harris detector", 0);
	settings.Add(useHarrisDetector);

//	CVFilterSetting * maxCorners, * qualityLevel, * minimumDistance, * blockSize, * k, * useHarrisDetector;
	
	List<String> strings;
	strings.Add("Quality denotes how to filter away lower quality corners depending");
	strings.Add("on the value of the corner with best quality. (percentage filtering)");
	about = MergeLines(strings, "\n");

}
Esempio n. 6
0
long Undo(struct InstData *data)
{
    ENTER();

    D(DBF_UNDO, "undolevel: %ld undocur: %ld undofill: %ld", data->undolevel, data->undocur, data->undofill);

    // check if there is something in the undo
    // buffer available
    if(data->undolevel > 0 && data->undocur > 0)
    {
        struct UserAction *buffer;
        BOOL  crsr_move = TRUE;

        if(Enabled(data))
        {
            data->blockinfo.enabled = FALSE;
            MarkText(data->blockinfo.startx, data->blockinfo.startline, data->blockinfo.stopx, data->blockinfo.stopline, data);
        }

        // as the redo operation automatically
        // becomes available when undo is used we just
        // check here if we didn't yet set RedoAvailable
        // as we only want to set it once
        if(data->undocur == data->undofill)
            set(data->object, MUIA_TextEditor_RedoAvailable, TRUE);

        data->undopointer = (APTR)((char *)data->undopointer - sizeof(struct UserAction));
        data->undocur--;
        buffer = (struct UserAction *)data->undopointer;

//    if(data->actualline != LineNode(buffer->y, data) || data->CPos_X != buffer->x)
        SetCursor(data->CPos_X, data->actualline, FALSE, data);

        data->CPos_X = buffer->x;
        data->actualline = LineNode(buffer->y, data);
        ScrollIntoDisplay(data);

        switch(buffer->type)
        {
        case ET_PASTECHAR:
        {
            buffer->del.character = *(data->actualline->line.Contents+data->CPos_X);
            buffer->del.style = GetStyle(data->CPos_X, data->actualline);
            buffer->del.flow = data->actualline->line.Flow;
            buffer->del.separator = data->actualline->line.Separator;
            RemoveChars(data->CPos_X, data->actualline, 1, data);
        }
        break;

        case ET_BACKSPACECHAR:
        {
            PasteChars(data->CPos_X++, data->actualline, 1, (char *)&buffer->del.character, buffer, data);
        }
        break;

        case ET_DELETECHAR:
        {
            PasteChars(data->CPos_X, data->actualline, 1, (char *)&buffer->del.character, buffer, data);
        }
        break;

        case ET_SPLITLINE:
        {
            MergeLines(data->actualline, data);
        }
        break;

        case ET_MERGELINES:
        {
            SplitLine(data->CPos_X, data->actualline, FALSE, buffer, data);
        }
        break;

        case ET_BACKSPACEMERGE:
        {
            SplitLine(data->CPos_X, data->actualline, TRUE, buffer, data);
        }
        break;

        case ET_PASTEBLOCK:
        {
            struct marking block =
            {
                TRUE,
                LineNode(buffer->y, data),
                buffer->x,
                LineNode(buffer->blk.y, data),
                buffer->blk.x
            };
            char *clip = GetBlock(&block, data);

            CutBlock2(data, FALSE, FALSE, &block, TRUE);
            buffer->clip = (unsigned char *)clip;
        }
        break;

        case ET_DELETEBLOCK_NOMOVE:
            crsr_move = FALSE;
        // continue

        case ET_DELETEBLOCK:
        {
            struct Hook *oldhook = data->ImportHook;
            char *clip = (char *)buffer->clip;

            data->ImportHook = &ImPlainHook;
            InsertText(data, clip, crsr_move);
            data->ImportHook = oldhook;
            MyFreePooled(data->mypool, clip);

            buffer->blk.x = data->CPos_X;
            buffer->blk.y = LineNr(data->actualline, data);

            if(!crsr_move)
            {
                data->CPos_X = buffer->x;
                data->actualline = LineNode(buffer->y, data);
            }
        }
        break;

        default:
            // nothing to do
            break;
        }

        ScrollIntoDisplay(data);

        if(data->flags & FLG_Active)
            SetCursor(data->CPos_X, data->actualline, TRUE, data);

        // if there are no further undo levels we
        // have to set UndoAvailable to FALSE
        if(data->undocur == 0)
        {
            set(data->object, MUIA_TextEditor_UndoAvailable, FALSE);

            if(!(data->flags & FLG_UndoLost))
                data->HasChanged = FALSE;
        }

        RETURN(TRUE);
        return(TRUE);
    }
    else
    {
        DoMethod(data->object, MUIM_TextEditor_HandleError, Error_NothingToUndo);

        RETURN(FALSE);
        return(FALSE);
    }
}
Esempio n. 7
0
long Redo(struct InstData *data)
{
    ENTER();

    D(DBF_UNDO, "undolevel: %ld undocur: %ld undofill: %ld", data->undolevel, data->undocur, data->undofill);

    // check if there something to redo at all
    if(data->undofill > 0 && data->undocur < data->undofill)
    {
        struct UserAction *buffer = (struct UserAction *)data->undopointer;

        if(Enabled(data))
        {
            data->blockinfo.enabled = FALSE;
            MarkText(data->blockinfo.startx, data->blockinfo.startline, data->blockinfo.stopx, data->blockinfo.stopline, data);
        }

        // in case undocur is equal zero then we have to
        // set the undoavailable attribute to true to signal
        // others that undo is available
        if(data->undocur == 0)
            set(data->object, MUIA_TextEditor_UndoAvailable, TRUE);

        data->undopointer = (APTR)((char *)data->undopointer + sizeof(struct UserAction));
        data->undocur++;

//    if(data->actualline != LineNode(buffer->y, data) || data->CPos_X != buffer->x)
        SetCursor(data->CPos_X, data->actualline, FALSE, data);
        data->CPos_X = buffer->x;
        data->actualline = LineNode(buffer->y, data);
        ScrollIntoDisplay(data);

        switch(buffer->type)
        {
        case ET_PASTECHAR:
            PasteChars(data->CPos_X++, data->actualline, 1, (char *)&buffer->del.character, buffer, data);
            break;

        case ET_BACKSPACECHAR:
        case ET_DELETECHAR:
            RemoveChars(data->CPos_X, data->actualline, 1, data);
            break;

        case ET_SPLITLINE:
            SplitLine(data->CPos_X, data->actualline, TRUE, NULL, data);
            break;

        case ET_MERGELINES:
        case ET_BACKSPACEMERGE:
            MergeLines(data->actualline, data);
            break;

        case ET_PASTEBLOCK:
        {
            struct Hook *oldhook = data->ImportHook;

            data->ImportHook = &ImPlainHook;
            InsertText(data, (char *)buffer->clip, TRUE);
            data->ImportHook = oldhook;
            MyFreePooled(data->mypool, buffer->clip);

            buffer->blk.x = data->CPos_X;
            buffer->blk.y = LineNr(data->actualline, data);
        }
        break;

        case ET_DELETEBLOCK_NOMOVE:
        case ET_DELETEBLOCK:
        {
            struct marking block =
            {
                TRUE,
                LineNode(buffer->y, data),
                buffer->x,
                LineNode(buffer->blk.y, data),
                buffer->blk.x
            };
            char  *clip = GetBlock(&block, data);

            CutBlock2(data, FALSE, FALSE, &block, TRUE);
            buffer->clip = (unsigned char *)clip;
        }
        break;

        default:
            // nothing to do
            break;
        }

        ScrollIntoDisplay(data);

        if(data->flags & FLG_Active)
            SetCursor(data->CPos_X, data->actualline, TRUE, data);

        // if undocur == undofill this signals that we
        // don't have any things to redo anymore.
        if(data->undocur == data->undofill)
            set(data->object, MUIA_TextEditor_RedoAvailable, FALSE);

        RETURN(TRUE);
        return(TRUE);
    }
    else
    {
        DoMethod(data->object, MUIM_TextEditor_HandleError, Error_NothingToRedo);

        RETURN(FALSE);
        return(FALSE);
    }
}
Esempio n. 8
0
/*----------------------*
 * Paste from Clipboard *
 *----------------------*/
BOOL PasteClip (LONG x, struct line_node *actline, struct InstData *data)
{
  struct line_node *line = NULL;
  struct line_node *startline = NULL;
  struct line_node *previous = NULL;
  UWORD   *styles = NULL;
  UWORD   *colors = NULL;
  STRPTR  textline;
  BOOL newline = TRUE;
  BOOL res = FALSE;

  ENTER();

  if(InitClipboard(data, IFFF_READ))
  {
    if(StopChunk(data->iff, ID_FTXT, ID_CHRS) == 0 &&
       StopChunk(data->iff, ID_FTXT, ID_FLOW) == 0 &&
       StopChunk(data->iff, ID_FTXT, ID_HIGH) == 0 &&
       StopChunk(data->iff, ID_FTXT, ID_SBAR) == 0 &&
       StopChunk(data->iff, ID_FTXT, ID_COLS) == 0 &&
       StopChunk(data->iff, ID_FTXT, ID_STYL) == 0 &&
       StopChunk(data->iff, ID_FTXT, ID_CSET) == 0)
    {
      LONG error, codeset = 0;
      UWORD flow = MUIV_TextEditor_Flow_Left;
      UWORD color = FALSE;
      UWORD separator = 0;
      BOOL ownclip = FALSE;
      LONG updatefrom;

      while(TRUE)
      {
        struct ContextNode *cn;

        error = ParseIFF(data->iff, IFFPARSE_SCAN);
        SHOWVALUE(DBF_CLIPBOARD, error);
        if(error == IFFERR_EOC)
          continue;
        else if(error)
          break;

        if((cn = CurrentChunk(data->iff)) != NULL)
        {
          switch (cn->cn_ID)
          {
            case ID_CSET:
              D(DBF_CLIPBOARD, "reading FLOW");
              SHOWVALUE(DBF_CLIPBOARD, cn->cn_Size);
              if(cn->cn_Size >= 4)
              {
                /* Only the first four bytes are interesting */
                if(ReadChunkBytes(data->iff, &codeset, 4) != 4)
                {
                  codeset = 0;
                }
                SHOWVALUE(DBF_CLIPBOARD, codeset);
              }
              break;

            case ID_FLOW:
              D(DBF_CLIPBOARD, "reading FLOW");
              SHOWVALUE(DBF_CLIPBOARD, cn->cn_Size);
              if(cn->cn_Size == 2)
              {
                if(ReadChunkBytes(data->iff, &flow, 2) == 2)
                  if(flow > MUIV_TextEditor_Flow_Right)
                    flow = MUIV_TextEditor_Flow_Left;
                SHOWVALUE(DBF_CLIPBOARD, flow);
              }
              break;

            case ID_HIGH:
              D(DBF_CLIPBOARD, "reading HIGH");
              SHOWVALUE(DBF_CLIPBOARD, cn->cn_Size);
              if (cn->cn_Size == 2)
              {
                error = ReadChunkBytes(data->iff, &color, 2);
                SHOWVALUE(DBF_CLIPBOARD, color);
                SHOWVALUE(DBF_CLIPBOARD, error);
              }
              break;

            case ID_SBAR:
              D(DBF_CLIPBOARD, "reading SBAR");
              SHOWVALUE(DBF_CLIPBOARD, cn->cn_Size);
              if (cn->cn_Size == 2)
              {
                error = ReadChunkBytes(data->iff, &separator, 2);
                SHOWVALUE(DBF_CLIPBOARD, separator);
                SHOWVALUE(DBF_CLIPBOARD, error);
              }
              break;

            case ID_COLS:
              D(DBF_CLIPBOARD, "reading COLS");
              SHOWVALUE(DBF_CLIPBOARD, cn->cn_Size);
              if(colors)
              {
                MyFreePooled(data->mypool, colors);
                colors = NULL;
              }
              // allocate one word more than the chunk tell us, because we terminate the array with an additional value
              if(cn->cn_Size > 0 && (colors = (UWORD *)MyAllocPooled(data->mypool, cn->cn_Size + sizeof(UWORD))) != NULL)
              {
                error = ReadChunkBytes(data->iff, colors, cn->cn_Size);
                SHOWVALUE(DBF_CLIPBOARD, error);
                colors[cn->cn_Size / 2] = 0xffff;
              }
              break;

            case ID_STYL:
              D(DBF_CLIPBOARD, "reading STYL");
              SHOWVALUE(DBF_CLIPBOARD, cn->cn_Size);
              ownclip = TRUE;
              if(styles)
              {
                MyFreePooled(data->mypool, styles);
                styles = NULL;
              }
              // allocate one word more than the chunk tell us, because we terminate the array with an additional value
              if(cn->cn_Size > 0 && (styles = (UWORD *)MyAllocPooled(data->mypool, cn->cn_Size + sizeof(UWORD))) != NULL)
              {
                error = ReadChunkBytes(data->iff, styles, cn->cn_Size);
                SHOWVALUE(DBF_CLIPBOARD, error);
                styles[cn->cn_Size / 2] = EOS;
              }
              break;

            case ID_CHRS:
            {
              D(DBF_CLIPBOARD, "reading CHRS");
              SHOWVALUE(DBF_CLIPBOARD, cn->cn_Size);

              data->HasChanged = TRUE;

              if(cn->cn_Size > 0 && !ownclip)
              {
                char *contents;
                ULONG length = cn->cn_Size;

                if((contents = (char *)MyAllocPooled(data->mypool, length + 1)) != NULL)
                {
                  error = ReadChunkBytes(data->iff, contents, length);
                  SHOWVALUE(DBF_CLIPBOARD, error);

                  if(contents[length - 1] != '\n')
                  {
                    newline = FALSE;
                  }
                  else
                  {
                    length--;
                  }
                  contents[length] = '\0';

                  #if defined(__MORPHOS__)
                  if (codeset == CODESET_UTF8)
                  {
                    if (IS_MORPHOS2)
                      contents = utf8_to_ansi(data, contents);
                  }
                  #endif

                  if((line = ImportText(contents, data, &ImPlainHook, data->ImportWrap)))
                  {
                    if(!startline)
                      startline = line;
                    if(previous)
                      previous->next  = line;

                    line->previous    = previous;
                    line->visual    = VisualHeight(line, data);
                    data->totallines += line->visual;
                    while(line->next)
                    {
                      line = line->next;
                      line->visual    = VisualHeight(line, data);
                      data->totallines += line->visual;
                    }
                    previous = line;
                  }
                  MyFreePooled(data->mypool, contents);
                }
              }
              else
              {
                ULONG length = cn->cn_Size;

                if(length > 0 && (textline = (char *)MyAllocPooled(data->mypool, length + 2)) != NULL)
                {
                  error = ReadChunkBytes(data->iff, textline, length);
                  SHOWVALUE(DBF_CLIPBOARD, error);

                  if (textline[length - 1] != '\n')
                  {
                    newline = FALSE;
                    textline[length] = '\n';
                    length++;
                  }
                  textline[length] = '\0';

                  if((line = AllocLine(data)))
                  {
                    line->next     = NULL;
                    line->previous   = previous;
                    line->line.Contents   = textline;
                    line->line.Length   = length;
                    line->visual   = VisualHeight(line, data);
                    line->line.Color    = color;
                    line->line.Flow     = flow;
                    line->line.Separator = separator;
                    line->line.Styles   = styles;
                    line->line.Colors   = colors;
                    data->totallines += line->visual;

                    if(!startline)
                      startline = line;
                    if(previous)
                      previous->next  = line;

                    previous = line;
                  }
                  else
                  {
                    if(styles)
                      MyFreePooled(data->mypool, (void *)styles);
                    if(colors)
                      MyFreePooled(data->mypool, (void *)colors);
                  }
                }
                else
                {
                  if(styles)
                    MyFreePooled(data->mypool, styles);
                  if(colors)
                    MyFreePooled(data->mypool, (void *)colors);
                }
                styles    = NULL;
                colors    = NULL;
                flow      = MUIV_TextEditor_Flow_Left;
                color     = FALSE;
                separator = 0;
                ownclip   = FALSE;
              }
            }
            break;
          }
        }
      }

      if(line)
      {
        BOOL oneline = FALSE;

        SplitLine(x, actline, FALSE, NULL, data);
        line->next = actline->next;
        actline->next->previous = line;
        actline->next = startline;
        startline->previous = actline;
        data->CPos_X = line->line.Length-1;
        if(actline->next == line)
        {
          data->CPos_X += actline->line.Length-1;
          oneline = TRUE;
        }
        if(!newline)
          MergeLines(line, data);
        MergeLines(actline, data);
        if(oneline)
          line = actline;
        if(newline)
        {
          line = line->next;
          data->CPos_X = 0;
        }
        data->actualline = line;
      }
      else
      {
        switch(error)
        {
          case IFFERR_MANGLED:
          case IFFERR_SYNTAX:
          case IFFERR_NOTIFF:
            D(DBF_CLIPBOARD, "no FTXT clip!");
            DoMethod(data->object, MUIM_TextEditor_HandleError, Error_ClipboardIsNotFTXT);
            break;
          default:
            D(DBF_CLIPBOARD, "clipboard is empty!");
            DoMethod(data->object, MUIM_TextEditor_HandleError, Error_ClipboardIsEmpty);
            break;
        }
      }
      data->update = TRUE;

      ScrollIntoDisplay(data);
      updatefrom = LineToVisual(actline, data)-1;
      if(updatefrom < 0)
        updatefrom = 0;
      DumpText(data->visual_y+updatefrom, updatefrom, data->maxlines, TRUE, data);

      if(data->update)
        res = TRUE;
      else
        data->update = TRUE;
    }

    EndClipSession(data);
  }

  RETURN(res);
  return res;
}
Esempio n. 9
0
/// Just concatenates the body into a single string and returns it.
String SIPPacket::GetBodyAsString(){
	return MergeLines(body, "\n\r");
}
Esempio n. 10
0
/// Returns the body as a single string.
String SIPInfoPacket::GetBodyAsString(){
	String string = MergeLines(body, "\r\n");
	return string;
}
Esempio n. 11
0
LONG CutBlock2(struct InstData *data, BOOL Clipboard, BOOL NoCut, struct marking *newblock, BOOL update)
{
  LONG  tvisual_y, error;
  LONG  startx, stopx;
  LONG  res = 0;
  struct  line_node *startline, *stopline;

  ENTER();

  startx    = newblock->startx;
  stopx     = newblock->stopx;
  startline = newblock->startline;
  stopline  = newblock->stopline;

  //D(DBF_STARTUP, "CutBlock2: %ld-%ld %lx-%lx %ld %ld", startx, stopx, startline, stopline, Clipboard, NoCut);

  if(startline != stopline)
  {
    struct line_node *c_startline = startline->next;

    data->update = FALSE;

    if(Clipboard == TRUE)
    {
      if(InitClipboard(data, IFFF_WRITE))
      {
        D(DBF_CLIPBOARD, "writing FORM");
        error = PushChunk(data->iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN);
        SHOWVALUE(DBF_CLIPBOARD, error);

        ClipChars(startx, startline, startline->line.Length-startx, data);
      }
      else
      {
        Clipboard = FALSE;
      }
    }

    while(c_startline != stopline)
    {
      if(Clipboard == TRUE)
      {
        ClipLine(c_startline, data);
      }

      if(NoCut == FALSE)
      {
        struct line_node *cc_startline = c_startline;

        MyFreePooled(data->mypool, c_startline->line.Contents);
        if(c_startline->line.Styles != NULL)
          MyFreePooled(data->mypool, c_startline->line.Styles);
        data->totallines -= c_startline->visual;
        c_startline = c_startline->next;

        //D(DBF_STARTUP, "FreeLine %08lx", cc_startline);

        FreeLine(cc_startline, data);
      }
      else
        c_startline = c_startline->next;
    }

    if(Clipboard == TRUE)
    {
      if(stopx != 0)
        ClipChars(0, stopline, stopx, data);

      EndClipSession(data);
    }

    if(NoCut == FALSE)
    {
      startline->next = stopline;
      stopline->previous = startline;

      //D(DBF_STARTUP, "RemoveChars: %ld %ld %08lx %ld", startx, stopx, startline, startline->line.Length);

      if(startline->line.Length-startx-1 > 0)
        RemoveChars(startx, startline, startline->line.Length-startx-1, data);

      if(stopx != 0)
        RemoveChars(0, stopline, stopx, data);

      data->CPos_X = startx;
      data->actualline = startline;
      MergeLines(startline, data);
    }
  }
  else
  {
    if(Clipboard == TRUE)
    {
      if(InitClipboard(data, IFFF_WRITE))
      {
        D(DBF_CLIPBOARD, "writing FORM");
        error = PushChunk(data->iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN);
        SHOWVALUE(DBF_CLIPBOARD, error);

        ClipChars(startx, startline, stopx-startx, data);
        EndClipSession(data);
      }

      if(update == TRUE && NoCut == TRUE)
      {
        MarkText(data->blockinfo.startx, data->blockinfo.startline, data->blockinfo.stopx, data->blockinfo.stopline, data);
          goto end;
      }
    }

    if(NoCut == FALSE)
    {
      data->CPos_X = startx;
      RemoveChars(startx, startline, stopx-startx, data);
      if(update == TRUE)
        goto end;
    }
  }

  tvisual_y = LineToVisual(startline, data)-1;
  if(tvisual_y < 0 || tvisual_y > data->maxlines)
  {
    //D(DBF_STARTUP, "ScrollIntoDisplay");
    ScrollIntoDisplay(data);
    tvisual_y = 0;
  }

  if(update == TRUE)
  {
    //D(DBF_STARTUP, "DumpText! %ld %ld %ld", data->visual_y, tvisual_y, data->maxlines);
    data->update = TRUE;
    DumpText(data->visual_y+tvisual_y, tvisual_y, data->maxlines, TRUE, data);
  }
  res = tvisual_y;

end:

  RETURN(res);
  return res;
}