Beispiel #1
0
void CCharBufferText::RemoveLinesAt (unsigned int line, unsigned int count)
{
    ASSERT (line <= GetLinesCount ());
    ASSERT (line + count <= GetLinesCount ());

    unsigned int start = line_start [line];
    unsigned int end_line = line + count;
    unsigned int end = end_line < lines_count ? line_start [end_line] : data.GetSize ();

    data.ReplaceCharsRange (start, end - start, 0, NULL);
}
Beispiel #2
0
void CText::FireOnChange (
    unsigned int start_line, unsigned int start_position, 
    unsigned int old_end_line, unsigned int old_end_position, 
    unsigned int new_end_line, unsigned int new_end_position)
{
    ASSERT (start_line < GetLinesCount ());
    ASSERT (start_position <= GetLineLength (start_line));
    ASSERT (new_end_line < GetLinesCount ());
    ASSERT (new_end_position <= GetLineLength (new_end_line));
    ASSERT (old_end_line > start_line || (old_end_line == start_line && old_end_position >= start_position));
    ASSERT (new_end_line > start_line || (new_end_line == start_line && new_end_position >= start_position));

    if (listener != NULL && (start_line != old_end_line || start_position != old_end_position || start_line != new_end_line || start_position != new_end_position))
        listener->OnChange (start_line, start_position, old_end_line, old_end_position, new_end_line, new_end_position);
}
Beispiel #3
0
bool SjMainFrame::DisplayBusyInfo(const wxString& text, bool forceUpdateForLongOp)
{
	long lineCount = GetLinesCount();

	if( lineCount )
	{
		SjSkinValue value;

		value.value = SJ_VFLAG_CENTER;
		value.string = SjTools::ShortenUrl(text);
		SetSkinTargetValue(IDT_DISPLAY_LINE_FIRST+lineCount/2, value);
	}

	return TRUE;
}
Beispiel #4
0
void CCharBufferText::InsertLinesAt (unsigned int line, unsigned int count, unsigned int length [], UNICHAR *characters [])
{
    static UNICHAR EOL [] = {'\r', '\n'};

    ASSERT (line <= GetLinesCount ());
    ASSERT (count == 0 || length != NULL);
    ASSERT (count == 0 || characters != NULL);

    unsigned int l = 0;
    for (unsigned int i = 0; i < count; i++)
        l += length [i] + 2;

    UNICHAR *buffer = new UNICHAR [l];

    if (line < lines_count)
    {
        unsigned int pos = 0;
        for (unsigned int i = 0; i < count; i++)
        {
            memcpy (&buffer [pos], characters [i], length [i] * sizeof (UNICHAR));
            pos += length [i];
            memcpy (&buffer [pos], EOL, 2 * sizeof (UNICHAR));
            pos += 2;
        }

        data.ReplaceCharsRange (line_start [line], 0, l, buffer);
    }
    else
    {
        unsigned int pos = 0;
        for (unsigned int i = 0; i < count; i++)
        {
            memcpy (&buffer [pos], EOL, 2 * sizeof (UNICHAR));
            pos += 2;
            memcpy (&buffer [pos], characters [i], length [i] * sizeof (UNICHAR));
            pos += length [i];
        }

        data.ReplaceCharsRange (data.GetSize (), 0, l, buffer);
    }

    delete [] buffer;
}
void CToolTipWnd::ObtainTextSize(PIX &pixMaxWidth, PIX &pixMaxHeight) 
{
  CDC *pDC = GetDC();
  if( pDC == NULL) return;

  pixMaxWidth = 0;
  _saPixLineHeights.Clear();
  PIX pixStartY = 0;
  INDEX ctLines = GetLinesCount();
  _saPixLineHeights.New( ctLines);
  for(INDEX iLine = 0; iLine<ctLines; iLine++)
  {
    CTString strLine = GetLine(iLine);
    CSize size = pDC->GetOutputTextExtent( CString(strLine));
    if( size.cx>pixMaxWidth)  pixMaxWidth = size.cx;
    _saPixLineHeights[iLine] = pixStartY;
    pixStartY += size.cy;
  }
  pixMaxHeight = pixStartY;
  ReleaseDC( pDC);
}
void CToolTipWnd::OnPaint() 
{
	CPaintDC dc(this);
  
  CRect rectWindow;
  GetClientRect(rectWindow);
  DWORD colPaper = GetSysColor( COLOR_INFOBK);
  DWORD colInk = GetSysColor( COLOR_INFOTEXT);
  dc.FillSolidRect( rectWindow, colPaper);
  dc.SetTextColor( colInk);
  
  CDC *pDC = GetDC();
  pDC->SelectObject( &theApp.m_FixedFont);
  if( pDC == NULL) return;
  
  INDEX ctLines = GetLinesCount();
  for(INDEX iLine = 0; iLine<ctLines; iLine++)
  {
    CTString strLine = GetLine(iLine);
    dc.TextOut( 0, _saPixLineHeights[iLine], CString(strLine));
  }
  ReleaseDC( pDC);
}
Beispiel #7
0
void SjMainFrame::UpdateDisplay()
{
	SjSkinValue     value;
	long            totalMs, elapsedMs;

	// get the number of visible lines
	long            visLineCount = GetLinesCount(),
	                visLine;
	wxASSERT(visLineCount>=0);

	// get the player
	SjPlayer*       recentPlayer = &g_mainFrame->m_player;

	// get the number of tracks in the queue
	long            queueTrackCount = recentPlayer->m_queue.GetCount(),
	                queuePlayingPos = recentPlayer->m_queue.GetCurrPos(),
	                queuePos;
	wxASSERT(queueTrackCount>=0);

	// current stuff
	SjPlaylistEntry&    urlInfo1 = recentPlayer->m_queue.GetInfo(-1);
	wxString            currTrackName = urlInfo1.GetTrackName();
	wxString            currArtistName = urlInfo1.GetLeadArtistName();

	unsigned long       currTimestamp = SjTools::GetMsTicks();

	// update cover
	{
		if( !m_display.m_showStartupDisplay && queuePlayingPos != -1 )
		{
			value.string = m_columnMixer.GetTrackCoverUrl(recentPlayer->m_queue.GetUrlByPos(-1));
			value.value = SJ_VFLAG_STRING_IS_IMAGE_URL;
			SetSkinTargetValue(IDT_DISPLAY_COVER, value);
		}
		else
		{
			SetSkinTargetValue(IDT_DISPLAY_COVER, value);
		}
	}

	// update seeker
	{
		totalMs = recentPlayer->GetTotalTime();
		value.string.Empty();
		if( !m_display.m_showStartupDisplay && !recentPlayer->IsStopped() && totalMs > 0 )
		{
			elapsedMs = recentPlayer->GetElapsedTime();

			value.vmax  = totalMs/SJ_SEEK_RESOLUTION; if( value.vmax==0 ) value.vmax = 1;
			value.value = elapsedMs/SJ_SEEK_RESOLUTION;
			SetSkinTargetValue(IDT_SEEK, value);

		}
		else
		{
			value.value = 0;
			SetSkinTargetValue(IDT_SEEK, value);
		}
	}

	// update main window title and the "currTrack" target
	if( !m_display.m_showStartupDisplay && !IsStopped() )
	{
		wxString title, titleArtistName(currArtistName), titleTrackName(currTrackName);
		if( recentPlayer != &m_player )
		{
			SjPlaylistEntry& urlInfo4 = m_player.m_queue.GetInfo(-1);
			titleTrackName = urlInfo4.GetTrackName();
			titleArtistName = urlInfo4.GetLeadArtistName();
		}

		// set windows title
		title = CombineArtistNTitle(titleArtistName, titleTrackName, wxT(" - "));
		title.Append(wxT(" - "));
		title.Append(SJ_PROGRAM_NAME);
		SetTitle(title);
	}
	else
	{
		// set windows title
		SetTitle(SJ_PROGRAM_NAME);
	}

	// update display lines...
	bool displayMsgInCurrNextTrack = false;

	if( (m_display.m_msgCancelAtTimestamp && !m_display.m_msg1.IsEmpty()) )
	{
		// ...display message
		wxString msg1(m_display.m_msg1), msg2(m_display.m_msg2);

		if( HasSkinTarget(IDT_DISPLAY_LINE_FIRST) )
		{
			m_display.m_backupedCurrLine = -1;
			value.value = SJ_VFLAG_CENTER;
			value.value |= queueTrackCount? SJ_VFLAG_IGNORECENTEROFFSET : 0;
			for( visLine = 0; visLine < visLineCount; visLine++ )
			{
				if( visLine==visLineCount/2 )
				{
					value.string = msg1;
				}
				else if( visLine==(visLineCount/2)+1 )
				{
					value.string = msg2;
				}
				else
				{
					value.string.Empty();
				}
				SetSkinTargetValue(IDT_DISPLAY_LINE_FIRST+visLine, value);
			}
		}
		else if( HasSkinTarget(IDT_CURR_TRACK) )
		{
			displayMsgInCurrNextTrack = true;
			value.value = 0;
			if( HasSkinTarget(IDT_NEXT_TRACK) )
			{
				value.string = msg1;
				SetSkinTargetValue(IDT_CURR_TRACK, value);
				value.string = msg2;
				SetSkinTargetValue(IDT_NEXT_TRACK, value);
			}
			else
			{
				value.string = msg1;
				if( !msg2.IsEmpty() ) value.string += wxT(" ") + msg2;
				SetSkinTargetValue(IDT_CURR_TRACK, value);
			}
		}
	}
	else if( m_display.m_showStartupDisplay || queueTrackCount == 0 )
	{
		// ...nothing in queue, display logo
		m_display.m_firstLineQueuePos = -1;

		value.thumbSize = 1;
		value.vmin  = 0;
		value.vmax  = 1;
		value.value = 0;
		SetSkinTargetValue(IDT_DISPLAY_V_SCROLL, value);

		value.value = SJ_VFLAG_CENTER;
		for( visLine = 0; visLine < visLineCount; visLine++ )
		{
			if( visLine==visLineCount/2 )
			{
				value.string = SJ_PROGRAM_NAME wxT(" ") SJ_VERSION_STR;
			}
			else
			{
				value.string.Empty();
			}
			SetSkinTargetValue(IDT_DISPLAY_LINE_FIRST+visLine, value);
		}
	}
	else
	{
		// ...sth. in queue, display queue...
		m_display.m_backupedCurrLine = -1;
		m_display.m_backupedCurrBold = FALSE;

		queuePos = m_display.m_scrollPos==-1? queuePlayingPos : m_display.m_scrollPos;
		if( queuePos < 0 ) queuePos = 0;
		if( queueTrackCount > visLineCount && queuePos > queueTrackCount-visLineCount ) queuePos = queueTrackCount-visLineCount;
		if( queueTrackCount <= visLineCount ) queuePos = 0;

		if( m_display.m_scrollPos != -1 ) m_display.m_scrollPos = queuePos; // correct the value

		value.thumbSize = visLineCount;
		value.vmin  = 0;
		value.vmax  = queueTrackCount;
		value.value = queuePos;
		SetSkinTargetValue(IDT_DISPLAY_V_SCROLL, value);

		value.thumbSize = 0;
		m_display.m_firstLineQueuePos = queuePos;
		for( visLine = 0; visLine < visLineCount; visLine++, queuePos++ )
		{
			value.vmax = 0;
			value.vmin = 0;
			if( queuePos >= 0 && queuePos < queueTrackCount )
			{
				// ...name
				SjPlaylistEntry& urlInfo2 = recentPlayer->m_queue.GetInfo(queuePos);
				value.string = CombineArtistNTitle(urlInfo2.GetLeadArtistName(), urlInfo2.GetTrackName());

				if( m_skinFlags&SJ_SKIN_SHOW_DISPLAY_AUTOPLAY )
				{
					if( recentPlayer->m_queue.GetFlags(queuePos)&SJ_PLAYLISTENTRY_AUTOPLAY )
					{
						value.string.Prepend(_("AutoPlay")+wxString(wxT(": ")));
					}
				}

				if( m_skinFlags&SJ_SKIN_SHOW_DISPLAY_TRACKNR )
				{
					value.string.Prepend(wxString::Format(wxT("%i. "), (int)queuePos+1));
				}

				// ...time / right icon
				value.value = 0;

				if( IsOpAvailable(SJ_OP_EDIT_QUEUE) )
				{
					value.value |= (SJ_VFLAG_MOVABLE);
				}

				if( IsOpAvailable(SJ_OP_UNQUEUE) )
				{
					value.value |= (SJ_VFLAG_ICONR_DELETE);
				}

				if( queuePos == queuePlayingPos && !recentPlayer->IsStopped() )
				{
					value.value |= SJ_VFLAG_VMIN_IS_TIME;
					if( IsOpAvailable(SJ_OP_TOGGLE_TIME_MODE) )
						value.value |= SJ_VFLAG_TIME_CLICKABLE;

					long totalTime = recentPlayer->GetTotalTime();
					if( m_showRemainingTime && totalTime > 0 )
					{
						value.value |= SJ_VFLAG_VMIN_MINUS;
						value.vmin = recentPlayer->GetRemainingTime();
						if( value.vmin <= 0 )
						{
							value.vmin = urlInfo2.GetPlaytimeMs();
						}
					}
					else
					{
						value.vmin = recentPlayer->GetElapsedTime();
					}

					if( m_skinFlags&SJ_SKIN_SHOW_DISPLAY_TOTAL_TIME )
					{
						value.vmax = totalTime;
						value.value |= value.vmax>0? SJ_VFLAG_VMAX_IS_TIME : 0;
					}
				}
				else
				{
					value.vmin = urlInfo2.GetPlaytimeMs();
					value.value |= value.vmin>0? SJ_VFLAG_VMIN_IS_TIME : 0;
				}

				// marked (or just added?)
				if( m_display.m_selectedIds.Lookup(urlInfo2.GetId()) )
				{
					value.value |= SJ_VFLAG_BOLD;
					if( queuePos == queuePlayingPos )
					{
						m_display.m_backupedCurrBold = TRUE;
					}
				}

				// ...left icon
				if( queuePos == queuePlayingPos )
				{
					if( recentPlayer->IsStopped() ) { value.value |= SJ_VFLAG_ICONL_STOP; }
					else if( recentPlayer->IsPaused() )  { value.value |= SJ_VFLAG_ICONL_PAUSE; }
					else                                 { value.value |= SJ_VFLAG_ICONL_PLAY; }

					m_display.m_backupedCurrTitle = value.string;
					m_display.m_backupedCurrLine = visLine;
				}
				else
				{
					long entryFlags     = recentPlayer->m_queue.GetFlags(queuePos);
					if( entryFlags & SJ_PLAYLISTENTRY_MOVED_DOWN )
					{
						if( !recentPlayer->m_queue.IsBoring(queuePos, currTimestamp) )
						{
							entryFlags &= ~SJ_PLAYLISTENTRY_MOVED_DOWN;
							recentPlayer->m_queue.SetFlags(queuePos, entryFlags);
						}
					}

					if( entryFlags&SJ_PLAYLISTENTRY_ERRONEOUS )        { value.value |= SJ_VFLAG_ICONL_ERRONEOUS; }
					else if( recentPlayer->m_queue.WasPlayed(queuePos) )    { value.value |= SJ_VFLAG_ICONL_PLAYED; }
					else if( entryFlags&SJ_PLAYLISTENTRY_MOVED_DOWN )       { value.value |= SJ_VFLAG_ICONL_MOVED_DOWN; }
					else                                                    { value.value |= SJ_VFLAG_ICONL_EMPTY; }
				}
			}
			else
			{
				// ...empty line
				value.value = SJ_VFLAG_ICONL_EMPTY;
				value.string.Empty();
			}

			SetSkinTargetValue(IDT_DISPLAY_LINE_FIRST+visLine, value);
		}
	}

	// set "currTrack", "nextTrack" and "currTime" targets
	if( !displayMsgInCurrNextTrack )
	{
		if( HasSkinTarget(IDT_CURR_TRACK) )
		{
			value.value = 0;
			value.string = SJ_PROGRAM_NAME;
			if( !IsStopped() && queuePlayingPos < queueTrackCount )
			{
				SjPlaylistEntry& urlInfo4 = m_player.m_queue.GetInfo(queuePlayingPos);
				value.string = CombineArtistNTitle(urlInfo4.GetLeadArtistName(), urlInfo4.GetTrackName());
			}
			SetSkinTargetValue(IDT_CURR_TRACK, value);
		}

		if( HasSkinTarget(IDT_NEXT_TRACK) )
		{
			value.value = 0;
			value.string.Empty();
			if( !IsStopped() )
			{
				if( GetShuffle() )
				{
					if( HasNextRegardAP() )
					{
						value.string = _("Shuffle");
					}
				}
				else
				{
					if( queuePlayingPos+1 < queueTrackCount )
					{
						SjPlaylistEntry& urlInfo4 = m_player.m_queue.GetInfo(queuePlayingPos+1);
						value.string = CombineArtistNTitle(urlInfo4.GetLeadArtistName(), urlInfo4.GetTrackName());
					}
				}
			}
			SetSkinTargetValue(IDT_NEXT_TRACK, value);
		}
	}

	if( HasSkinTarget(IDT_CURR_TIME) )
	{
		UpdateCurrTime();
	}

	// playing states
	value.vmin      = 0;
	value.vmax      = 255;
	value.thumbSize = 0;
	value.value     = m_player.GetMainVol();
	SetSkinTargetValue(IDT_MAIN_VOL_SLIDER, value);

	value.vmax  = 0;
	value.value = m_player.GetMainVolMute()? 1 : 0;
	SetSkinTargetValue(IDT_MAIN_VOL_MUTE, value);

	value.value = m_player.IsPlaying()? 1 : 0;
	SetSkinTargetValue(IDT_PLAY, value);

	value.value = m_player.IsPaused()? 1 : 0;
	SetSkinTargetValue(IDT_PAUSE, value);

	value.value = m_player.IsStopped()? 1 : 0;
	SetSkinTargetValue(IDT_STOP, value);

	value.value = HasPrev()? 0 : 2;
	SetSkinTargetValue(IDT_PREV, value);

	value.value = HasNextRegardAP()? 0 : 2;
	SetSkinTargetValue(IDT_NEXT, value);
	SetSkinTargetValue(IDT_FADE_TO_NEXT, value);

	value.value = m_player.StopAfterThisTrack()? 1 : (/*m_player.IsStopped()? 2 :*/ 0);
	SetSkinTargetValue(IDT_STOP_AFTER_THIS_TRACK, value);

	value.value = m_player.StopAfterEachTrack()? 1 : (/*m_player.IsStopped()? 2 :*/ 0);
	SetSkinTargetValue(IDT_STOP_AFTER_EACH_TRACK, value);

	value.value = m_player.m_queue.GetShuffle()? 1 : 0;
	SetSkinTargetValue(IDT_SHUFFLE, value);

	value.value = m_player.m_queue.GetRepeat(); /*0=off, 1=single, 2=all*/
	SetSkinTargetValue(IDT_REPEAT, value);

	value.value = queueTrackCount? 0/*enabled*/ : 2/*disabled*/;
	SetSkinTargetValue(IDT_APPEND_FILES, value);
	SetSkinTargetValue(IDT_SAVE_PLAYLIST, value);
	SetSkinTargetValue(IDT_UNQUEUE_ALL, value);
	SetSkinTargetValue(IDT_MORE_FROM_CURR_ALBUM, value);
	SetSkinTargetValue(IDT_MORE_FROM_CURR_ARTIST, value);
}
Beispiel #8
0
void SjMainFrame::OnSkinTargetMotion(int clickTargetId, int requestedMotionAmount)
{
	GotDisplayInputFromUser();

	if( m_display.m_selectedIds.GetCount() )
	{
		m_display.m_backupedCurrLine = -1; // stop timer display updating until UpdateDisplay() is called

		long visLineCount = GetLinesCount();
		long queueTrackCount = m_player.m_queue.GetCount();
		long queuePlayingPos = m_player.m_queue.GetCurrPos();

		long scrollPos = m_display.m_scrollPos==-1? queuePlayingPos : m_display.m_scrollPos;
		if( scrollPos < 0 ) scrollPos = 0;
		if( queueTrackCount > visLineCount && scrollPos > queueTrackCount-visLineCount )
		{
			scrollPos = queueTrackCount-visLineCount;
			#ifdef DEBUG_DISPLAY
			wxLogDebug("          vv corrected vv");
			#endif
		}
		if( queueTrackCount <= visLineCount ) scrollPos = 0;

		int realMotionAmount = m_player.m_queue.MoveByIds(m_display.m_selectedIds, requestedMotionAmount);

		int resumeTargetId = clickTargetId;
		if( realMotionAmount )
		{
			#ifdef DEBUG_DISPLAY
			wxLogDebug("oldScrollPos=%i (oldDisplayScrollPos=%i, queuePlayingPos=%i)", (int)scrollPos, (int)m_display.m_scrollPos, (int)queuePlayingPos);
			#endif

			resumeTargetId += realMotionAmount;
			if( resumeTargetId < IDT_DISPLAY_LINE_FIRST )
			{
				m_display.m_scrollPos = scrollPos + realMotionAmount;
				if( m_display.m_scrollPos < 0 ) m_display.m_scrollPos = 0;
				resumeTargetId = IDT_DISPLAY_LINE_FIRST;

				#ifdef DEBUG_DISPLAY
				wxLogDebug("  ^ newDisplayScrollPos=%i, resumeTargetLine=%i", (int)m_display.m_scrollPos, (int)resumeTargetId-IDT_DISPLAY_LINE_FIRST);
				#endif
			}
			else if( resumeTargetId >= IDT_DISPLAY_LINE_FIRST+visLineCount )
			{
				m_display.m_scrollPos = scrollPos + realMotionAmount;
				resumeTargetId = (IDT_DISPLAY_LINE_FIRST+visLineCount)-1;

				#ifdef DEBUG_DISPLAY
				wxLogDebug("  v newDisplayScrollPos=%i, resumeTargetLine=%i", (int)m_display.m_scrollPos, (int)resumeTargetId-IDT_DISPLAY_LINE_FIRST);
				#endif
			}
			else
			{
				m_display.m_scrollPos = scrollPos;

				#ifdef DEBUG_DISPLAY
				wxLogDebug("  * newDisplayScrollPos=%i, resumeTargetLine=%i", (int)m_display.m_scrollPos, (int)resumeTargetId-IDT_DISPLAY_LINE_FIRST);
				#endif
			}
		}

		ResumeSkinTargetMotion(clickTargetId, resumeTargetId);
		UpdateDisplay();

		#ifdef DEBUG_DISPLAY
		wxLogDebug("    resultingDisplayScrollPos=%i", (int)m_display.m_scrollPos);
		#endif

		m_display.m_selectedIdsTimestamp = SjTools::GetMsTicks();
		m_display.m_selectedIdsLockMs = SJ_DISPLAY_SELECTION_LOCK_MS;
	}
}
Beispiel #9
0
void GridDisplay::DrawText(Draw &w, int mx, int x, int y, int cx, int cy, int align, const wchar *s, const Font &font, const Color &fg, const Color &bg, bool found, int fs, int fe, bool wrap)
{
	if(*s == 0)
		return;

	int tcy = font.Info().GetHeight();

	const wchar *p = s;
	const wchar *t = s;

	int lines = 0;
//	int enters = 0;

	int ty = y;
	Size tsz;

	if((align & GD::VCENTER) || (align & GD::BOTTOM))
	{
		lines = GetLinesCount(cx, s, font, wrap);

		if(align & GD::VCENTER)
			ty = max(ty, y + (cy - tcy * lines) / 2);
		else
			ty = y + cy - tcy * lines;
	}

	//w.DrawText(x, y, Format("lines: %d, cx:%d, cy%d", lines, cx, cy));
	Size isz = GridImg::Dots2().GetSize();
	int gcx = cx - (wrap ? 0 : isz.cx);

	real_size.cy = lines > 1 ? lines * tcy : 0;

	while(true)
	{
		bool nextline = *p == '\n';
		bool caret    = *p == '\r';
		bool endtext  = *p == '\0';

		const wchar * pp = p;

		bool textbreak = false;
		
		if(nextline || endtext)
		{
			/*int kk = p - t;
			if(p - t <= 1)
			{
				//break;
				kk = 0;
			}*/

			int tx = x;
			tsz = GetTextSize(t, font, int(p - t));

			if(tsz.cx > gcx)
			{
				int size = 0;
				const wchar *e = t;
				while(e < p)
				{
					int tcx = GetTextSize(e, font, 1).cx;
					size += tcx;
					if(size > gcx)
						break;
					e++;
				}

				p = e;
				if(wrap)
				{
					textbreak = true;
					if(p == t)
						p = t + 1;
				}
			}

			if(align & GD::RIGHT)
				tx = x + cx - tsz.cx;
			else if(align & GD::HCENTER)
				tx = x + (cx - tsz.cx) / 2;

			Color tfg = fg;

			if(found)
			{
				int chs = int(t - s);
				int che = int(p - s - 1);

				if(fs <= che && fe >= chs)
				{
					int scx = GetTextSize(t, font, max(chs, fs) - chs).cx;
					int ecx = GetTextSize(t, font, min(che, fe) - chs + 1).cx;
					Color nbg(255 - fg.GetR(), 255 - fg.GetG(), 255 - fg.GetB());
					w.DrawRect(max(mx, tx) + scx, ty, ecx - scx, tcy, Blend(nbg, bg, 100));
				}
			}

			bool dots = !wrap && tsz.cx > gcx;
			if(dots)
			{
				real_size.cx = max(real_size.cx, tsz.cx);
				
				w.Clip(x, y, cx, cy);
				w.DrawImage(x + cx - isz.cx, ty + font.Info().GetAscent() - isz.cy, GridImg::Dots2, tfg);
				w.End();
				
				if(p > t)
				{
					w.Clip(x, y, cx - isz.cx , cy);
					w.DrawText(max(mx, tx), ty, t, font, tfg, int(p - t));
					w.End();
				}
			}
			else
				w.DrawText(max(mx, tx), ty, t, font, tfg, int(p - t));

			ty += tcy;
			t = textbreak ? p : (p = pp + 1);
		}
		
		if(caret)
			*(char *) p = ' ';

		if(!textbreak)
		{
			if(endtext)
				break;
			else
				p++;
		}
	}
}