Exemple #1
0
void RichTextLabel::_input_event(InputEvent p_event) {

	switch(p_event.type) {

		case InputEvent::MOUSE_BUTTON: {

			if (main->first_invalid_line<main->lines.size())
				return;

			const InputEventMouseButton& b = p_event.mouse_button;

			if (b.button_index==BUTTON_LEFT) {

				if (true) {


					if (b.pressed && !b.doubleclick) {
						int line=0;
						Item *item=NULL;

						bool outside;
						_find_click(main,Point2i(b.x,b.y),&item,&line,&outside);

						if (item) {

							Variant meta;
							if (!outside && _find_meta(item,&meta)) {
								//meta clicked

								emit_signal("meta_clicked",meta);
							} else if (selection.enabled) {

								selection.click=item;
								selection.click_char=line;

							}

						}

					} else if (!b.pressed) {

						selection.click=NULL;
					}
				}
			}

			if (b.button_index==BUTTON_WHEEL_UP) {

				if (scroll_active)
					vscroll->set_val( vscroll->get_val()-vscroll->get_page()/8 );
			}
			if (b.button_index==BUTTON_WHEEL_DOWN) {

				if (scroll_active)
					vscroll->set_val( vscroll->get_val()+vscroll->get_page()/8 );
			}
		} break;
		case InputEvent::KEY: {

			const InputEventKey &k=p_event.key;
			if (k.pressed && !k.mod.alt && !k.mod.shift && !k.mod.meta) {
				bool handled=true;
				switch(k.scancode) {
					case KEY_PAGEUP: {

						if (vscroll->is_visible())
							vscroll->set_val( vscroll->get_val() - vscroll->get_page() );
					} break;
					case KEY_PAGEDOWN: {

						if (vscroll->is_visible())
							vscroll->set_val( vscroll->get_val() + vscroll->get_page() );
					} break;
					case KEY_UP: {

						if (vscroll->is_visible())
							vscroll->set_val( vscroll->get_val() - get_font("normal_font")->get_height() );
					} break;
					case KEY_DOWN: {

						if (vscroll->is_visible())
							vscroll->set_val( vscroll->get_val() + get_font("normal_font")->get_height() );
					} break;
					case KEY_HOME: {

						if (vscroll->is_visible())
							vscroll->set_val( 0 );
					} break;
					case KEY_END: {

						if (vscroll->is_visible())
							vscroll->set_val( vscroll->get_max() );
					} break;
					case KEY_INSERT:
					case KEY_C: {

						if (k.mod.command) {
							selection_copy();
						} else {
							handled=false;
						}

					} break;
					default: handled=false;
				}


				if (handled)
					accept_event();
			}

		} break;
		case InputEvent::MOUSE_MOTION: {

			if (main->first_invalid_line<main->lines.size())
				return;

			const InputEventMouseMotion& m = p_event.mouse_motion;

			if (selection.click) {

				int line=0;
				Item *item=NULL;
				_find_click(main,Point2i(m.x,m.y),&item,&line);
				if (!item)
					return; // do not update


				selection.from=selection.click;
				selection.from_char=selection.click_char;

				selection.to=item;
				selection.to_char=line;

				bool swap=false;
				if (selection.from->index > selection.to->index )
					swap=true;
				else if (selection.from->index == selection.to->index) {
					if (selection.from_char > selection.to_char)
						swap=true;
					else if (selection.from_char == selection.to_char) {

						selection.active=false;
						return;
					}
				}

				if (swap) {
					SWAP( selection.from, selection.to );
					SWAP( selection.from_char, selection.to_char );
				}

				selection.active=true;
				update();

			}

		} break;
	}

}
Exemple #2
0
void RichTextLabel::_process_line(ItemFrame *p_frame,const Vector2& p_ofs,int &y, int p_width, int p_line, ProcessMode p_mode,const Ref<Font> &p_base_font,const Color &p_base_color,const Point2i& p_click_pos,Item **r_click_item,int *r_click_char,bool *r_outside,int p_char_count) {

	RID ci;
	if (r_outside)
		*r_outside=false;
	if (p_mode==PROCESS_DRAW) {
		ci=get_canvas_item();

		if (r_click_item)
			*r_click_item=NULL;

	}
	Line &l = p_frame->lines[p_line];
	Item *it = l.from;


	int line_ofs=0;
	int margin=_find_margin(it,p_base_font);
	Align align=_find_align(it);;
	int line=0;
	int spaces=0;


	if (p_mode!=PROCESS_CACHE) {

		ERR_FAIL_INDEX(line,l.offset_caches.size());
		line_ofs = l.offset_caches[line];
	}

	if (p_mode==PROCESS_CACHE) {
		l.offset_caches.clear();
		l.height_caches.clear();
		l.char_count=0;
		l.minimum_width=0;
	}

	int wofs=margin;
	int spaces_size=0;
	int align_ofs=0;


	if (p_mode!=PROCESS_CACHE && align!=ALIGN_FILL)
		wofs+=line_ofs;

	int begin=wofs;

	Ref<Font> cfont = _find_font(it);
	if (cfont.is_null())
		cfont=p_base_font;

	//line height should be the font height for the first time, this ensures that an empty line will never have zero height and succesive newlines are displayed
	int line_height=cfont->get_height();

	Variant meta;

#define NEW_LINE \
{\
	if (p_mode!=PROCESS_CACHE) {\
		line++;\
		if (line < l.offset_caches.size())\
			line_ofs=l.offset_caches[line];\
		wofs=margin;\
		if (align!=ALIGN_FILL)\
			wofs+=line_ofs;\
	} else {\
		int used=wofs-margin;\
		switch(align) {\
			case ALIGN_LEFT: l.offset_caches.push_back(0); break;\
			case ALIGN_CENTER: l.offset_caches.push_back(((p_width-margin)-used)/2); break;\
			case ALIGN_RIGHT: l.offset_caches.push_back(((p_width-margin)-used)); break;\
			case ALIGN_FILL: l.offset_caches.push_back((p_width-margin)-used/*+spaces_size*/); break;\
		}\
		l.height_caches.push_back(line_height);\
		l.space_caches.push_back(spaces);\
	}\
	y+=line_height+get_constant(SceneStringNames::get_singleton()->line_separation);\
	line_height=0;\
	spaces=0;\
	spaces_size=0;\
	wofs=begin;\
	align_ofs=0;\
	if (p_mode!=PROCESS_CACHE) {\
		lh=line<l.height_caches.size()?l.height_caches[line]:1;\
	}\
	if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh && p_click_pos.x<p_ofs.x+wofs) {\
		if (r_outside) *r_outside=true;\
		*r_click_item=it;\
		*r_click_char=rchar;\
		return;\
	}\
}


#define ENSURE_WIDTH(m_width) \
	if (p_mode==PROCESS_CACHE) { \
		l.minimum_width=MAX(l.minimum_width,wofs+m_width);\
	}\
	if (wofs + m_width > p_width) {\
		if (p_mode==PROCESS_CACHE) {\
			if (spaces>0)	\
				spaces-=1;\
		}\
		if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh && p_click_pos.x>p_ofs.x+wofs) {\
			if (r_outside) *r_outside=true;	\
			*r_click_item=it;\
			*r_click_char=rchar;\
			return;\
		}\
		NEW_LINE\
	}


#define ADVANCE(m_width) \
{\
	if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh && p_click_pos.x>=p_ofs.x+wofs && p_click_pos.x<p_ofs.x+wofs+m_width) {\
		if (r_outside) *r_outside=false;	\
		*r_click_item=it;\
		*r_click_char=rchar;\
		return;\
	}\
	wofs+=m_width;\
}

#define CHECK_HEIGHT( m_height ) \
if (m_height > line_height) {\
	line_height=m_height;\
}

	Color selection_fg;
	Color selection_bg;

	if (p_mode==PROCESS_DRAW) {


		selection_fg = get_color("font_color_selected");
		selection_bg = get_color("selection_color");
	}
	int rchar=0;
	int lh=0;

	while (it) {

		switch(it->type) {

			case ITEM_TEXT: {

				ItemText *text = static_cast<ItemText*>(it);

				Ref<Font> font=_find_font(it);
				if (font.is_null())
					font=p_base_font;

				const CharType *c = text->text.c_str();
				const CharType *cf=c;
				int fh=font->get_height();
				int ascent = font->get_ascent();
				Color color;
				bool underline=false;

				if (p_mode==PROCESS_DRAW) {
					color=_find_color(text,p_base_color);
					underline=_find_underline(text);
					if (_find_meta(text,&meta)) {

						underline=true;
					}


				} else if (p_mode==PROCESS_CACHE) {
					l.char_count+=text->text.length();

				}

				rchar=0;

				while(*c) {

					int end=0;
					int w=0;
					int fw=0;

					lh=0;
					if (p_mode!=PROCESS_CACHE) {
						lh=line<l.height_caches.size()?l.height_caches[line]:1;
					}

					while (c[end]!=0 && !(end && c[end-1]==' ' && c[end]!=' ')) {

						int cw = font->get_char_size(c[end],c[end+1]).width;
						if (c[end]=='\t') {
							cw=tab_size*font->get_char_size(' ').width;
						}

						if (end>0 && w+cw+begin > p_width ) {
							break; //don't allow lines longer than assigned width
						}

						w+=cw;
						fw+=cw;

						end++;
					}

					ENSURE_WIDTH(w);

					if (end && c[end-1]==' ') {
						if (p_mode==PROCESS_CACHE) {
							spaces_size+=font->get_char_size(' ').width;
						} else if (align==ALIGN_FILL) {
							int ln = MIN(l.offset_caches.size()-1,line);
							if (l.space_caches[ln]) {
								align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln];
							}
						}
						spaces++;

					}


					{

						int ofs=0;

						for(int i=0;i<end;i++) {
							int pofs=wofs+ofs;


							if (p_mode==PROCESS_POINTER && r_click_char && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh) {
								//int o = (wofs+w)-p_click_pos.x;


								int cw=font->get_char_size(c[i],c[i+1]).x;

								if (c[i]=='\t') {
									cw=tab_size*font->get_char_size(' ').width;
								}


								if (p_click_pos.x-cw/2>p_ofs.x+align_ofs+pofs) {

									rchar=int((&c[i])-cf);
								}


								ofs+=cw;
							} else if (p_mode==PROCESS_DRAW) {

								bool selected=false;
								if (selection.active) {

									int cofs = (&c[i])-cf;
									if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >=selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <=selection.to_char))) {
										selected=true;
									}
								}

								int cw=0;

								bool visible = visible_characters<0 || p_char_count<visible_characters;
								if (c[i]=='\t')
									visible=false;

								if (selected) {

									cw = font->get_char_size(c[i],c[i+1]).x;
									draw_rect(Rect2(p_ofs.x+pofs,p_ofs.y+y,cw,lh),selection_bg);
									if (visible)
										font->draw_char(ci,p_ofs+Point2(align_ofs+pofs,y+lh-(fh-ascent)),c[i],c[i+1],selection_fg);

								} else {
									if (visible)
										cw=font->draw_char(ci,p_ofs+Point2(align_ofs+pofs,y+lh-(fh-ascent)),c[i],c[i+1],color);
								}

								p_char_count++;
								if (c[i]=='\t') {
									cw=tab_size*font->get_char_size(' ').width;
								}


								if (underline) {
									Color uc=color;
									uc.a*=0.5;
									int uy = y+lh-fh+ascent+2;
									VS::get_singleton()->canvas_item_add_line(ci,p_ofs+Point2(align_ofs+pofs,uy),p_ofs+Point2(align_ofs+pofs+cw,uy),uc);
								}
								ofs+=cw;
							}

						}
					}


					ADVANCE(fw);
					CHECK_HEIGHT(fh); //must be done somewhere
					c=&c[end];
				}


			} break;
			case ITEM_IMAGE: {

				lh=0;
				if (p_mode!=PROCESS_CACHE)
					lh = line<l.height_caches.size()?l.height_caches[line]:1;
				else
					l.char_count+=1; //images count as chars too

				ItemImage *img = static_cast<ItemImage*>(it);

				Ref<Font> font=_find_font(it);
				if (font.is_null())
					font=p_base_font;

				if (p_mode==PROCESS_POINTER && r_click_char)
					*r_click_char=0;

				ENSURE_WIDTH( img->image->get_width() );

				bool visible = visible_characters<0 || p_char_count<visible_characters;

				if (p_mode==PROCESS_DRAW && visible) {
					img->image->draw(ci,p_ofs+Point2(align_ofs+wofs,y+lh-font->get_descent()-img->image->get_height()));
				}
				p_char_count++;

				ADVANCE( img->image->get_width() );
				CHECK_HEIGHT( (img->image->get_height()+font->get_descent()) );

			} break;
			case ITEM_NEWLINE: {


				lh=0;
				if (p_mode!=PROCESS_CACHE)
					lh = line<l.height_caches.size()?l.height_caches[line]:1;

			} break;
			case ITEM_TABLE: {

				lh=0;
				ItemTable *table = static_cast<ItemTable*>(it);
				int hseparation=get_constant("table_hseparation");
				int vseparation=get_constant("table_vseparation");
				Color ccolor = _find_color(table,p_base_color);
				Vector2 draw_ofs = Point2(wofs,y);

				if (p_mode==PROCESS_CACHE) {

					int idx=0;
					//set minimums to zero
					for(int i=0;i<table->columns.size();i++) {
						table->columns[i].min_width=0;
						table->columns[i].width=0;
					}
					//compute minimum width for each cell
					for (List<Item*>::Element *E=table->subitems.front();E;E=E->next()) {
						ERR_CONTINUE(E->get()->type!=ITEM_FRAME); //children should all be frames
						ItemFrame *frame = static_cast<ItemFrame*>(E->get());

						int column = idx % table->columns.size();

						int ly=0;


						for(int i=0;i<frame->lines.size();i++) {

							_process_line(frame,Point2(),ly,p_width,i,PROCESS_CACHE,cfont,Color());
							table->columns[column].min_width=MAX( table->columns[i].min_width, frame->lines[i].minimum_width );
						}
						idx++;
					}

					//compute available width and total radio (for expanders)


					int total_ratio=0;
					int available_width=p_width - hseparation * (table->columns.size() -1);
					table->total_width=hseparation;

					for(int i=0;i<table->columns.size();i++) {
						available_width-=table->columns[i].min_width;
						if (table->columns[i].expand)
							total_ratio+=table->columns[i].expand_ratio;
					}

					//assign actual widths

					for(int i=0;i<table->columns.size();i++) {
						table->columns[i].width = table->columns[i].min_width;
						if (table->columns[i].expand)
							table->columns[i].width+=table->columns[i].expand_ratio*available_width/total_ratio;
						table->total_width+=table->columns[i].width+hseparation;
					}

					//compute caches properly again with the right width
					idx=0;
					for (List<Item*>::Element *E=table->subitems.front();E;E=E->next()) {
						ERR_CONTINUE(E->get()->type!=ITEM_FRAME); //children should all be frames
						ItemFrame *frame = static_cast<ItemFrame*>(E->get());

						int column = idx % table->columns.size();


						for(int i=0;i<frame->lines.size();i++) {

							int ly=0;
							_process_line(frame,Point2(),ly,table->columns[column].width,i,PROCESS_CACHE,cfont,Color());
							frame->lines[i].height_cache=ly; //actual height
							frame->lines[i].height_accum_cache=ly; //actual height
						}
						idx++;
					}

				}



				Point2 offset(align_ofs+hseparation,vseparation);

				int row_height=0;
				//draw using computed caches
				int idx=0;
				for (List<Item*>::Element *E=table->subitems.front();E;E=E->next()) {
					ERR_CONTINUE(E->get()->type!=ITEM_FRAME); //children should all be frames
					ItemFrame *frame = static_cast<ItemFrame*>(E->get());

					int column = idx % table->columns.size();

					int ly=0;
					int yofs=0;


					int lines_h = frame->lines[frame->lines.size()-1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache);
					int lines_ofs = p_ofs.y+offset.y+draw_ofs.y;

					bool visible = lines_ofs < get_size().height && lines_ofs+lines_h >=0;

					for(int i=0;i<frame->lines.size();i++) {


						if (visible) {
							if (p_mode==PROCESS_DRAW) {
								_process_line(frame,p_ofs+offset+draw_ofs+Vector2(0,yofs),ly,table->columns[column].width,i,PROCESS_DRAW,cfont,ccolor);
							} else if (p_mode==PROCESS_POINTER) {
								_process_line(frame,p_ofs+offset+draw_ofs+Vector2(0,yofs),ly,table->columns[column].width,i,PROCESS_POINTER,cfont,ccolor,p_click_pos,r_click_item,r_click_char,r_outside);
							}
						}

						yofs+=frame->lines[i].height_cache;
						if (p_mode==PROCESS_CACHE) {
							frame->lines[i].height_accum_cache=offset.y+draw_ofs.y+frame->lines[i].height_cache;
						}

					}

					row_height=MAX(yofs,row_height);
					offset.x+=table->columns[column].width+hseparation;

					if (column==table->columns.size()-1) {

						offset.y+=row_height+vseparation;
						offset.x=hseparation;
						row_height=0;
					}
					idx++;
				}

				int total_height = offset.y;
				if (row_height) {
					total_height=row_height+vseparation;
				}



				ADVANCE( table->total_width );
				CHECK_HEIGHT( total_height );

			} break;

			default: {}

		}


		Item *itp = it;

		it = _get_next_item(it);

		if (p_mode == PROCESS_POINTER && r_click_item && itp && !it && p_click_pos.y>p_ofs.y+y+lh) {
			//at the end of all, return this
			if (r_outside) *r_outside=true;
			*r_click_item=itp;
			*r_click_char=rchar;
			return;
		}

		if (it && (p_line+1 < p_frame->lines.size()) && p_frame->lines[p_line+1].from==it) {

			if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh) {
				//went to next line, but pointer was on the previous one
				if (r_outside) *r_outside=true;
				*r_click_item=itp;
				*r_click_char=rchar;
				return;
			}

			break;
		}
	}

	NEW_LINE;

#undef NEW_LINE
#undef ENSURE_WIDTH
#undef ADVANCE
#undef CHECK_HEIGHT

}
Exemple #3
0
void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p_mode,const Ref<Font> &p_base_font,const Color &p_base_color,const Point2i& p_click_pos,Item **r_click_item,int *r_click_char,bool *r_outside,int p_char_count) {

    RID ci;
    if (r_outside)
        *r_outside=false;
    if (p_mode==PROCESS_DRAW) {
        ci=get_canvas_item();

        if (r_click_item)
            *r_click_item=NULL;

    }
    Line &l = lines[p_line];
    Item *it = l.from;


    int line_ofs=0;
    int margin=_find_margin(it,p_base_font);
    Align align=_find_align(it);;
    int line=0;
    int spaces=0;


    if (p_mode!=PROCESS_CACHE) {

        ERR_FAIL_INDEX(line,l.offset_caches.size());
        line_ofs = l.offset_caches[line];
    }

    if (p_mode==PROCESS_CACHE) {
        l.offset_caches.clear();
        l.height_caches.clear();
        l.char_count=0;
    }

    int wofs=margin;
    int spaces_size=0;

    if (p_mode!=PROCESS_CACHE && align!=ALIGN_FILL)
        wofs+=line_ofs;

    int begin=wofs;

    Ref<Font> cfont = _find_font(it);
    if (cfont.is_null())
        cfont=p_base_font;

    //line height should be the font height for the first time, this ensures that an empty line will never have zero height and succesive newlines are displayed
    int line_height=cfont->get_height();

    Variant meta;

#define NEW_LINE \
{\
	if (p_mode!=PROCESS_CACHE) {\
		line++;\
		if (line < l.offset_caches.size())\
			line_ofs=l.offset_caches[line];\
		wofs=margin;\
		if (align!=ALIGN_FILL)\
			wofs+=line_ofs;\
	} else {\
		int used=wofs-margin;\
		switch(align) {\
			case ALIGN_LEFT: l.offset_caches.push_back(0); break;\
			case ALIGN_CENTER: l.offset_caches.push_back(((p_width-margin)-used)/2); break;\
			case ALIGN_RIGHT: l.offset_caches.push_back(((p_width-margin)-used)); break;\
			case ALIGN_FILL: l.offset_caches.push_back((p_width-margin)-used+spaces_size); break;\
		}\
		l.height_caches.push_back(line_height);\
		l.space_caches.push_back(spaces);\
	}\
	y+=line_height+get_constant(SceneStringNames::get_singleton()->line_separation);\
	line_height=0;\
	spaces=0;\
	spaces_size=0;\
	wofs=begin;\
	if (p_mode!=PROCESS_CACHE) {\
		lh=line<l.height_caches.size()?l.height_caches[line]:1;\
	}\
	if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh && p_click_pos.x<wofs) {\
		if (r_outside) *r_outside=true;\
		*r_click_item=it;\
		*r_click_char=rchar;\
		return;\
	}\
}


#define ENSURE_WIDTH(m_width) \
	if (wofs + m_width > p_width) {\
		if (p_mode==PROCESS_CACHE) {\
			if (spaces>0)	\
				spaces-=1;\
		}\
		if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh && p_click_pos.x>wofs) {\
			if (r_outside) *r_outside=true;	\
			*r_click_item=it;\
			*r_click_char=rchar;\
			return;\
		}\
		NEW_LINE\
	}


#define ADVANCE(m_width) \
{\
	if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh && p_click_pos.x>=wofs && p_click_pos.x<wofs+m_width) {\
		if (r_outside) *r_outside=false;	\
		*r_click_item=it;\
		*r_click_char=rchar;\
		return;\
	}\
	wofs+=m_width;\
}

#define CHECK_HEIGHT( m_height ) \
if (m_height > line_height) {\
	line_height=m_height;\
}

    Color selection_fg;
    Color selection_bg;

    if (p_mode==PROCESS_DRAW) {


        selection_fg = get_color("font_color_selected");
        selection_bg = get_color("selection_color");
    }
    int rchar=0;
    int lh=0;

    while (it) {

        switch(it->type) {

        case ITEM_TEXT: {

            ItemText *text = static_cast<ItemText*>(it);

            Ref<Font> font=_find_font(it);
            if (font.is_null())
                font=p_base_font;

            const CharType *c = text->text.c_str();
            const CharType *cf=c;
            int fh=font->get_height();
            int ascent = font->get_ascent();
            Color color;
            bool underline=false;

            if (p_mode==PROCESS_DRAW) {
                color=_find_color(text,p_base_color);
                underline=_find_underline(text);
                if (_find_meta(text,&meta)) {

                    underline=true;
                }

            } else if (p_mode==PROCESS_CACHE) {
                l.char_count+=text->text.length();
            }

            rchar=0;

            while(*c) {

                int end=0;
                int w=0;
                int fw=0;

                lh=0;
                if (p_mode!=PROCESS_CACHE) {
                    lh=line<l.height_caches.size()?l.height_caches[line]:1;
                }
                bool found_space=false;

                while (c[end]!=0 && !(end && c[end-1]==' ' && c[end]!=' ')) {

                    int cw = font->get_char_size(c[end],c[end+1]).width;
                    if (c[end]=='\t') {
                        cw=tab_size*font->get_char_size(' ').width;
                    }
                    w+=cw;

                    if (c[end]==' ') {

                        if (p_mode==PROCESS_CACHE) {
                            fw+=cw;
                        } else if (align==ALIGN_FILL && line<l.space_caches.size() && l.space_caches[line]>0) {
                            //print_line(String(c,end)+": "+itos(l.offset_caches[line])+"/"+itos(l.space_caches[line]));
                            //sub_space=cw;
                            found_space=true;
                        } else {
                            fw+=cw;
                        }
                    } else {
                        fw+=cw;
                    }

                    end++;
                }



                ENSURE_WIDTH(w);


                //print_line("END: "+String::chr(c[end])+".");
                if (end && c[end-1]==' ') {
                    spaces++;
                    if (p_mode==PROCESS_CACHE) {
                        spaces_size+=font->get_char_size(' ').width;
                    }

                    if (found_space) {
                        int ln = MIN(l.offset_caches.size()-1,line);

                        fw+=l.offset_caches[ln]/l.space_caches[ln];
                    }

                }


                {


                    int ofs=0;

                    for(int i=0; i<end; i++) {
                        int pofs=wofs+ofs;




                        if (p_mode==PROCESS_POINTER && r_click_char && p_click_pos.y>=y && p_click_pos.y<=y+lh) {
                            //int o = (wofs+w)-p_click_pos.x;


                            int cw=font->get_char_size(c[i],c[i+1]).x;
                            if (c[i]=='\t') {
                                cw=tab_size*font->get_char_size(' ').width;
                            }

                            if (p_click_pos.x-cw/2>pofs) {

                                rchar=int((&c[i])-cf);
                                //print_line("GOT: "+itos(rchar));


                                //if (i==end-1 && p_click_pos.x+cw/2 > pofs)
                                //	rchar++;
                                //int o = (wofs+w)-p_click_pos.x;

                                //	if (o>cw/2)
                                //		rchar++;
                            }


                            ofs+=cw;
                        } else if (p_mode==PROCESS_DRAW) {

                            bool selected=false;
                            if (selection.active) {

                                int cofs = (&c[i])-cf;
                                if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >=selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <=selection.to_char))) {
                                    selected=true;
                                }
                            }

                            int cw=0;

                            bool visible = visible_characters<0 || p_char_count<visible_characters;

                            if (selected) {

                                cw = font->get_char_size(c[i],c[i+1]).x;
                                draw_rect(Rect2(pofs,y,cw,lh),selection_bg);
                                if (visible)
                                    font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],selection_fg);

                            } else {
                                if (visible)
                                    cw=font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],color);
                            }

                            p_char_count++;
                            if (c[i]=='\t') {
                                cw=tab_size*font->get_char_size(' ').width;
                            }


                            //print_line("draw char: "+String::chr(c[i]));

                            if (underline) {
                                Color uc=color;
                                uc.a*=0.5;
                                //VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,y+ascent+2),Point2(pofs+cw,y+ascent+2),uc);
                                int uy = y+lh-fh+ascent+2;
                                VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,uy),Point2(pofs+cw,uy),uc);
                            }
                            ofs+=cw;
                        }

                    }
                }


                ADVANCE(fw);
                CHECK_HEIGHT(fh); //must be done somewhere
                c=&c[end];
            }


        }
        break;
        case ITEM_IMAGE: {

            lh=0;
            if (p_mode!=PROCESS_CACHE)
                lh = line<l.height_caches.size()?l.height_caches[line]:1;
            else
                l.char_count+=1; //images count as chars too

            ItemImage *img = static_cast<ItemImage*>(it);

            Ref<Font> font=_find_font(it);
            if (font.is_null())
                font=p_base_font;

            if (p_mode==PROCESS_POINTER && r_click_char)
                *r_click_char=0;

            ENSURE_WIDTH( img->image->get_width() );

            bool visible = visible_characters<0 || p_char_count<visible_characters;

            if (p_mode==PROCESS_DRAW && visible) {
                img->image->draw(ci,Point2(wofs,y+lh-font->get_descent()-img->image->get_height()));
            }
            p_char_count++;

            ADVANCE( img->image->get_width() );
            CHECK_HEIGHT( (img->image->get_height()+font->get_descent()) );

        }
        break;
        case ITEM_NEWLINE: {


            lh=0;
            if (p_mode!=PROCESS_CACHE)
                lh = line<l.height_caches.size()?l.height_caches[line]:1;


#if 0
            if (p_mode==PROCESS_POINTER && r_click_item ) {
                //previous last "wrapped" line
                int pl = line-1;
                if (pl<0 || lines[pl].height_caches.size()==0)
                    break;
                int py=lines[pl].offset_caches[ lines[pl].offset_caches.size() -1 ];
                int ph=lines[pl].height_caches[ lines[pl].height_caches.size() -1 ];
                print_line("py: "+itos(py));
                print_line("ph: "+itos(ph));

                rchar=0;
                if (p_click_pos.y>=py && p_click_pos.y<=py+ph) {
                    if (r_outside) *r_outside=true;
                    *r_click_item=it;
                    *r_click_char=rchar;
                    return;
                }
            }

#endif
        }
        break;

        default:
        {}

        }


        Item *itp = it;

        it = _get_next_item(it);

        if (p_mode == PROCESS_POINTER && r_click_item && itp && !it && p_click_pos.y>y+lh) {
            //at the end of all, return this
            if (r_outside) *r_outside=true;
            *r_click_item=itp;
            *r_click_char=rchar;
            return;
        }

        if (it && (p_line+1 < lines.size()) && lines[p_line+1].from==it) {

            if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh) {
                //went to next line, but pointer was on the previous one
                if (r_outside) *r_outside=true;
                *r_click_item=itp;
                *r_click_char=rchar;
                return;
            }

            break;
        }
    }

    NEW_LINE;

#undef NEW_LINE
#undef ENSURE_WIDTH
#undef ADVANCE
#undef CHECK_HEIGHT

}