diff options
93 files changed, 4154 insertions, 2403 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 513c7a9470..f50330447c 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -577,9 +577,9 @@ float _OS::get_frames_per_second() const { return OS::get_singleton()->get_frames_per_second(); } -Error _OS::native_video_play(String p_path, float p_volume) { +Error _OS::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) { - return OS::get_singleton()->native_video_play(p_path, p_volume); + return OS::get_singleton()->native_video_play(p_path, p_volume, p_audio_track, p_subtitle_track); }; bool _OS::native_video_is_playing() { diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index b3040a96ca..101dc1ab94 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -108,7 +108,7 @@ public: bool is_video_mode_resizable(int p_screen=0) const; Array get_fullscreen_mode_list(int p_screen=0) const; - Error native_video_play(String p_path, float p_volume); + Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track); bool native_video_is_playing(); void native_video_pause(); void native_video_stop(); diff --git a/core/image.cpp b/core/image.cpp index ae9fb0adc4..17ee569b6b 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -34,6 +34,7 @@ #include "print_string.h" #include <stdio.h> +SavePNGFunc Image::save_png_func = NULL; void Image::_put_pixel(int p_x,int p_y, const BColor& p_color, unsigned char *p_data) { @@ -1200,6 +1201,14 @@ Error Image::load(const String& p_path) { return ImageLoader::load_image(p_path, this); } +Error Image::save_png(const String& p_path) { + + if (save_png_func == NULL) + return ERR_UNAVAILABLE; + + return save_png_func(p_path, *this); +}; + bool Image::operator==(const Image& p_image) const { if (data.size() == 0 && p_image.data.size() == 0) diff --git a/core/image.h b/core/image.h index 0084a3616f..f4c96703b9 100644 --- a/core/image.h +++ b/core/image.h @@ -40,7 +40,9 @@ * Images can be loaded from a file, or registered into the Render object as textures. */ +class Image; +typedef Error (*SavePNGFunc)(const String &p_path, Image& p_img); class Image { @@ -50,6 +52,8 @@ class Image { }; public: + static SavePNGFunc save_png_func; + enum Format { FORMAT_GRAYSCALE, ///< one byte per pixel, 0-255 FORMAT_INTENSITY, ///< one byte per pixel, 0-255 @@ -278,6 +282,7 @@ public: DVector<uint8_t> get_data() const; Error load(const String& p_path); + Error save_png(const String& p_path); /** * create an empty image diff --git a/core/io/ip_address.h b/core/io/ip_address.h index cd39aa6c81..3cd8bb7733 100644 --- a/core/io/ip_address.h +++ b/core/io/ip_address.h @@ -39,6 +39,12 @@ struct IP_Address { }; //operator Variant() const; + bool operator==(const IP_Address& p_ip) const { + return host==p_ip.host; + } + bool operator!=(const IP_Address& p_ip) const { + return host!=p_ip.host; + } operator String() const; IP_Address(const String& p_string); IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d); diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 37fc9c4a0a..b566ce4b7b 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -111,7 +111,7 @@ Variant PacketPeer::_bnd_get_var() const { void PacketPeer::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_var"),&PacketPeer::_bnd_get_var); - ObjectTypeDB::bind_method(_MD("put_var", "var:Variant"),&PacketPeer::put_var); + ObjectTypeDB::bind_method(_MD("put_var", "var:var"),&PacketPeer::put_var); ObjectTypeDB::bind_method(_MD("get_available_packet_count"),&PacketPeer::get_available_packet_count); }; diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp new file mode 100644 index 0000000000..83217ffc41 --- /dev/null +++ b/core/io/packet_peer_udp.cpp @@ -0,0 +1,63 @@ +#include "packet_peer_udp.h" +#include "io/ip.h" + + +PacketPeerUDP* (*PacketPeerUDP::_create)()=NULL; + +int PacketPeerUDP::_get_packet_address() const { + + IP_Address ip = get_packet_address(); + return ip.host; +} + +String PacketPeerUDP::_get_packet_ip() const { + + return get_packet_address(); +} + +Error PacketPeerUDP::_set_send_address(const String& p_address,int p_port) { + + IP_Address ip; + if (p_address.is_valid_ip_address()) { + ip=p_address; + } else { + ip=IP::get_singleton()->resolve_hostname(p_address); + if (ip==IP_Address()) + return ERR_CANT_RESOLVE; + } + + set_send_address(ip,p_port); + return OK; +} + +void PacketPeerUDP::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("listen:Error","port","recv_buf_size"),&PacketPeerUDP::listen,DEFVAL(65536)); + ObjectTypeDB::bind_method(_MD("close"),&PacketPeerUDP::close); + ObjectTypeDB::bind_method(_MD("wait:Error"),&PacketPeerUDP::wait); + ObjectTypeDB::bind_method(_MD("is_listening"),&PacketPeerUDP::is_listening); + ObjectTypeDB::bind_method(_MD("get_packet_ip"),&PacketPeerUDP::_get_packet_ip); + ObjectTypeDB::bind_method(_MD("get_packet_address"),&PacketPeerUDP::_get_packet_address); + ObjectTypeDB::bind_method(_MD("get_packet_port"),&PacketPeerUDP::get_packet_port); + ObjectTypeDB::bind_method(_MD("set_send_address","host","port"),&PacketPeerUDP::_set_send_address); + + +} + +Ref<PacketPeerUDP> PacketPeerUDP::create_ref() { + + if (!_create) + return Ref<PacketPeerUDP>(); + return Ref<PacketPeerUDP>(_create()); +} + +PacketPeerUDP* PacketPeerUDP::create() { + + if (!_create) + return NULL; + return _create(); +} + +PacketPeerUDP::PacketPeerUDP() +{ +} diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h new file mode 100644 index 0000000000..73ff487b19 --- /dev/null +++ b/core/io/packet_peer_udp.h @@ -0,0 +1,37 @@ +#ifndef PACKET_PEER_UDP_H +#define PACKET_PEER_UDP_H + + +#include "io/packet_peer.h" + +class PacketPeerUDP : public PacketPeer { + OBJ_TYPE(PacketPeerUDP,PacketPeer); + +protected: + + static PacketPeerUDP* (*_create)(); + static void _bind_methods(); + + int _get_packet_address() const; + String _get_packet_ip() const; + + virtual Error _set_send_address(const String& p_address,int p_port); + +public: + + virtual Error listen(int p_port,int p_recv_buffer_size=65536)=0; + virtual void close()=0; + virtual Error wait()=0; + virtual bool is_listening() const=0; + virtual IP_Address get_packet_address() const=0; + virtual int get_packet_port() const=0; + virtual void set_send_address(const IP_Address& p_address,int p_port)=0; + + + static Ref<PacketPeerUDP> create_ref(); + static PacketPeerUDP* create(); + + PacketPeerUDP(); +}; + +#endif // PACKET_PEER_UDP_H diff --git a/core/os/os.cpp b/core/os/os.cpp index 53ca7c3a49..e56f4a4904 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -438,7 +438,7 @@ int OS::get_processor_count() const { return 1; } -Error OS::native_video_play(String p_path, float p_volume) { +Error OS::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) { return FAILED; }; diff --git a/core/os/os.h b/core/os/os.h index ed7e1e4324..5e084f6373 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -323,7 +323,7 @@ public: virtual String get_unique_ID() const; - virtual Error native_video_play(String p_path, float p_volume); + virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track); virtual bool native_video_is_playing() const; virtual void native_video_pause(); virtual void native_video_stop(); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 492068f604..2f16e31de6 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -29,6 +29,7 @@ #include "register_core_types.h" #include "io/tcp_server.h" +#include "io/packet_peer_udp.h" #include "io/config_file.h" #include "os/main_loop.h" #include "io/packet_peer.h" @@ -115,6 +116,7 @@ void register_core_types() { ObjectTypeDB::register_virtual_type<StreamPeer>(); ObjectTypeDB::register_create_type<StreamPeerTCP>(); ObjectTypeDB::register_create_type<TCP_Server>(); + ObjectTypeDB::register_create_type<PacketPeerUDP>(); ObjectTypeDB::register_create_type<StreamPeerSSL>(); ObjectTypeDB::register_virtual_type<IP>(); ObjectTypeDB::register_virtual_type<PacketPeer>(); diff --git a/core/script_language.cpp b/core/script_language.cpp index ad93fca4b5..81a9e2b062 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -85,6 +85,13 @@ void ScriptServer::register_language(ScriptLanguage *p_language) { _languages[_language_count++]=p_language; } +void ScriptServer::init_languages() { + + for(int i=0;i<_language_count;i++) { + _languages[i]->init(); + } +} + Variant ScriptInstance::call(const StringName& p_method,VARIANT_ARG_DECLARE) { VARIANT_ARGPTRS; diff --git a/core/script_language.h b/core/script_language.h index 1e0dcc678f..d62e9849a1 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -54,6 +54,8 @@ public: static int get_language_count(); static ScriptLanguage *get_language(int p_idx); static void register_language(ScriptLanguage *p_language); + + static void init_languages(); }; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 28953a535f..93a9e6475f 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -544,6 +544,7 @@ static void _call_##m_type##_##m_method(Variant& r_ret,Variant& p_self,const Var VCALL_PTR0R(Image,get_used_rect); VCALL_PTR3R(Image,brushed); VCALL_PTR1R(Image,load); + VCALL_PTR1R(Image,save_png); VCALL_PTR3(Image,brush_transfer); VCALL_PTR1R(Image,get_rect); VCALL_PTR1R(Image,compressed); @@ -1326,6 +1327,7 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl ADDFUNC4(IMAGE, NIL, Image, put_pixel, INT, "x", INT, "y", COLOR, "color", INT, "mipmap_level", varray(0)); ADDFUNC3(IMAGE, IMAGE, Image, brushed, IMAGE, "src", IMAGE, "brush", VECTOR2, "pos", varray(0)); ADDFUNC1(IMAGE, INT, Image, load, STRING, "path", varray(0)); + ADDFUNC1(IMAGE, INT, Image, save_png, STRING, "path", varray(0)); ADDFUNC3(IMAGE, NIL, Image, brush_transfer, IMAGE, "src", IMAGE, "brush", VECTOR2, "pos", varray(0)); ADDFUNC0(IMAGE, RECT2, Image, get_used_rect, varray(0)); ADDFUNC1(IMAGE, IMAGE, Image, get_rect, RECT2, "area", varray(0)); diff --git a/demos/2d/shower_of_bullets/bullets.gd b/demos/2d/shower_of_bullets/bullets.gd index f76fcc38ba..79f4faaae6 100644 --- a/demos/2d/shower_of_bullets/bullets.gd +++ b/demos/2d/shower_of_bullets/bullets.gd @@ -65,9 +65,9 @@ func _ready(): func _exit_tree(): for b in bullets: - Physics2DServer.free(b.body) + Physics2DServer.free_rid(b.body) - Physics2DServer.free(shape) + Physics2DServer.free_rid(shape) # Initalization here bullets.clear() diff --git a/demos/3d/navmesh/engine.cfg b/demos/3d/navmesh/engine.cfg new file mode 100644 index 0000000000..30af6ce5a5 --- /dev/null +++ b/demos/3d/navmesh/engine.cfg @@ -0,0 +1,9 @@ +[application] + +name="Navmesh Demo" +main_scene="res://navmesh.scn" +icon="res://icon.png" + +[rasterizer] + +shadow_filter=3 diff --git a/demos/3d/navmesh/navmesh.gd b/demos/3d/navmesh/navmesh.gd new file mode 100644 index 0000000000..a81a5e90d6 --- /dev/null +++ b/demos/3d/navmesh/navmesh.gd @@ -0,0 +1,110 @@ + +extends Navigation + +# member variables here, example: +# var a=2 +# var b="textvar" + +const SPEED=4.0 + +var camrot=0.0 + +var begin=Vector3() +var end=Vector3() +var m = FixedMaterial.new() + +var path=[] + +func _process(delta): + + + if (path.size()>1): + + var to_walk = delta*SPEED + var to_watch = Vector3(0,1,0) + while(to_walk>0 and path.size()>=2): + var pfrom = path[path.size()-1] + var pto = path[path.size()-2] + to_watch = (pto - pfrom).normalized() + var d = pfrom.distance_to(pto) + if (d<=to_walk): + path.remove(path.size()-1) + to_walk-=d + else: + path[path.size()-1] = pfrom.linear_interpolate(pto,to_walk/d) + to_walk=0 + + var atpos = path[path.size()-1] + var atdir = to_watch + atdir.y=0 + + var t = Transform() + t.origin=atpos + t=t.looking_at(atpos+atdir,Vector3(0,1,0)) + get_node("robot_base").set_transform(t) + + if (path.size()<2): + path=[] + set_process(false) + + else: + set_process(false) + +var draw_path=false + +func _update_path(): + + var p = get_simple_path(begin,end,true) + path=Array(p) # Vector3array to complex to use, convert to regular array + path.invert() + set_process(true) + + if (draw_path): + var im = get_node("draw") + im.set_material_override(m) + im.clear() + im.begin(Mesh.PRIMITIVE_POINTS,null) + im.add_vertex(begin) + im.add_vertex(end) + im.end() + im.begin(Mesh.PRIMITIVE_LINE_STRIP,null) + for x in p: + im.add_vertex(x) + im.end() + +func _input(ev): + + if (ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT and ev.pressed): + + var from = get_node("cambase/Camera").project_position(ev.pos) + var to = from+get_node("cambase/Camera").project_ray_normal(ev.pos)*100 + var p = get_closest_point_to_segment(from,to) + + begin=get_closest_point(get_node("robot_base").get_translation()) + end=p + + _update_path() + + if (ev.type==InputEvent.MOUSE_MOTION): + if (ev.button_mask&BUTTON_MASK_MIDDLE): + + camrot+=ev.relative_x*0.005 + get_node("cambase").set_rotation(Vector3(0,camrot,0)) + print("camrot ", camrot) + + + +func _ready(): + # Initalization here + set_process_input(true) + m.set_line_width(3) + m.set_point_size(3) + m.set_fixed_flag(FixedMaterial.FLAG_USE_POINT_SIZE,true) + m.set_flag(Material.FLAG_UNSHADED,true) + #begin = get_closest_point(get_node("start").get_translation()) + #end = get_closest_point(get_node("end").get_translation()) + #call_deferred("_update_path") + + pass + + diff --git a/demos/3d/navmesh/navmesh.scn b/demos/3d/navmesh/navmesh.scn Binary files differnew file mode 100644 index 0000000000..73df340b1e --- /dev/null +++ b/demos/3d/navmesh/navmesh.scn diff --git a/demos/3d/navmesh/particle.png b/demos/3d/navmesh/particle.png Binary files differnew file mode 100644 index 0000000000..18851c8c9d --- /dev/null +++ b/demos/3d/navmesh/particle.png diff --git a/demos/misc/udp_chat/chat.gd b/demos/misc/udp_chat/chat.gd new file mode 100644 index 0000000000..3270eebbfe --- /dev/null +++ b/demos/misc/udp_chat/chat.gd @@ -0,0 +1,72 @@ + +extends Panel + +# Really simple UDP chat client, not intended as a chat example!! +# (UDP can lose packets and you won't normally find out, so don't do a chat this way) +# This is just a demo that shows how to use the UDP class. + +var udp = PacketPeerUDP.new() + +func _process(delta): + + if (not udp.is_listening()): + return + + while(udp.get_available_packet_count()>0): + var packet = udp.get_var() + if (typeof(packet)==TYPE_STRING): + var host = udp.get_packet_ip() + var port = udp.get_packet_port() + get_node("chat/text").add_text("("+host+":"+str(port)+":) "+packet) + get_node("chat/text").newline() + + + +func _ready(): + # Initalization here + get_node("chat").add_style_override("panel",get_stylebox("bg","Tree")) + set_process(true) + + + +func send_message(text): + if (udp.is_listening()): + udp.put_var(text) + + +func _on_connect_toggled( pressed ): + + + if (pressed): + var err = udp.listen( get_node("listen_port").get_val() ) + if (err!=OK): + get_node("status").set_text("Error:\nCan't Listen.") + get_node("connect").set_pressed(false) + else: + get_node("status").set_text("Connected.") + get_node("connect").set_text("Disconnect") + err = udp.set_send_address(get_node("remote_host").get_text(),get_node("remote_port").get_val()) + if (err!=OK): + get_node("status").set_text("Error:\nCan't Resolve.") + get_node("connect").set_pressed(false) + else: + send_message("* "+get_node("user_name").get_text()+" entered chat.") + else: + + udp.close() + get_node("status").set_text("Disconnected.") + get_node("connect").set_text("Connect") + + + + +func _on_entry_line_text_entered( text ): + _on_entry_button_pressed(); + +func _on_entry_button_pressed(): + var msg = get_node("entry_line").get_text() + if (msg==""): + return + send_message(get_node("user_name").get_text()+"> "+msg) + + get_node("entry_line").set_text("") diff --git a/demos/misc/udp_chat/chat.scn b/demos/misc/udp_chat/chat.scn Binary files differnew file mode 100644 index 0000000000..efa4b799a0 --- /dev/null +++ b/demos/misc/udp_chat/chat.scn diff --git a/demos/misc/udp_chat/engine.cfg b/demos/misc/udp_chat/engine.cfg new file mode 100644 index 0000000000..584841ea83 --- /dev/null +++ b/demos/misc/udp_chat/engine.cfg @@ -0,0 +1,5 @@ +[application] + +name="UDP Chat" +main_scene="res://chat.scn" +icon="res://icon.png" diff --git a/demos/misc/udp_chat/icon.png b/demos/misc/udp_chat/icon.png Binary files differnew file mode 100644 index 0000000000..db6e21cce1 --- /dev/null +++ b/demos/misc/udp_chat/icon.png diff --git a/doc/base/classes.xml b/doc/base/classes.xml index b28c050a61..2c49926d66 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -2392,7 +2392,7 @@ <argument index="1" name="animation" type="Animation"> </argument> <description> - Add an animation resource to the player, which will be later referenced by the "name" arguemnt. + Add an animation resource to the player, which will be later referenced by the "name" argument. </description> </method> <method name="remove_animation" > @@ -2747,7 +2747,7 @@ types of nodes have different amount of inputs. </description> </method> - <method name="node_get_input_sourcre" qualifiers="const" > + <method name="node_get_input_source" qualifiers="const" > <return type="String"> </return> <argument index="0" name="id" type="String"> diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 4044496953..057de329df 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -4173,6 +4173,9 @@ void RasterizerGLES2::capture_viewport(Image* r_capture) { pixels.resize(viewport.width*viewport.height*4); DVector<uint8_t>::Write w = pixels.write(); glPixelStorei(GL_PACK_ALIGNMENT, 4); + +// uint64_t time = OS::get_singleton()->get_ticks_usec(); + if (current_rt) { #ifdef GLEW_ENABLED glReadBuffer(GL_COLOR_ATTACHMENT0); @@ -4182,10 +4185,25 @@ void RasterizerGLES2::capture_viewport(Image* r_capture) { // back? glReadPixels( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height,GL_RGBA,GL_UNSIGNED_BYTE,w.ptr()); } + + uint32_t *imgptr = (uint32_t*)w.ptr(); + for(int y=0;y<(viewport.height/2);y++) { + + uint32_t *ptr1 = &imgptr[y*viewport.width]; + uint32_t *ptr2 = &imgptr[(viewport.height-y-1)*viewport.width]; + + for(int x=0;x<viewport.width;x++) { + + uint32_t tmp = ptr1[x]; + ptr1[x]=ptr2[x]; + ptr2[x]=tmp; + } + } + w=DVector<uint8_t>::Write(); r_capture->create(viewport.width,viewport.height,0,Image::FORMAT_RGBA,pixels); - r_capture->flip_y(); + //r_capture->flip_y(); #endif diff --git a/drivers/png/resource_saver_png.cpp b/drivers/png/resource_saver_png.cpp index 9b394e8a8c..1fee50c8b5 100644 --- a/drivers/png/resource_saver_png.cpp +++ b/drivers/png/resource_saver_png.cpp @@ -31,6 +31,7 @@ #include "drivers/png/png.h" #include "os/file_access.h" #include "globals.h" +#include "core/image.h" static void _write_png_data(png_structp png_ptr,png_bytep data, png_size_t p_length) { @@ -46,12 +47,56 @@ Error ResourceSaverPNG::save(const String &p_path,const RES& p_resource,uint32_t ERR_EXPLAIN("Can't save empty texture as PNG"); ERR_FAIL_COND_V(!texture->get_width() || !texture->get_height(),ERR_INVALID_PARAMETER); + Image img = texture->get_data(); - if (img.get_format() > Image::FORMAT_INDEXED_ALPHA) - img.decompress(); + Error err = save_image(p_path, img); + + if (err == OK) { + + bool global_filter = Globals::get_singleton()->get("image_loader/filter"); + bool global_mipmaps = Globals::get_singleton()->get("image_loader/gen_mipmaps"); + bool global_repeat = Globals::get_singleton()->get("image_loader/repeat"); + + String text; + + if (global_filter!=bool(texture->get_flags()&Texture::FLAG_FILTER)) { + text+=bool(texture->get_flags()&Texture::FLAG_FILTER)?"filter=true\n":"filter=false\n"; + } + if (global_mipmaps!=bool(texture->get_flags()&Texture::FLAG_MIPMAPS)) { + text+=bool(texture->get_flags()&Texture::FLAG_FILTER)?"gen_mipmaps=true\n":"gen_mipmaps=false\n"; + } + if (global_repeat!=bool(texture->get_flags()&Texture::FLAG_REPEAT)) { + text+=bool(texture->get_flags()&Texture::FLAG_FILTER)?"repeat=true\n":"repeat=false\n"; + } + if (bool(texture->get_flags()&Texture::FLAG_ANISOTROPIC_FILTER)) { + text+="anisotropic=true\n"; + } + if (bool(texture->get_flags()&Texture::FLAG_CONVERT_TO_LINEAR)) { + text+="tolinear=true\n"; + } + + if (text!="" || FileAccess::exists(p_path+".flags")) { + + FileAccess* f = FileAccess::open(p_path+".flags",FileAccess::WRITE); + if (f) { + + f->store_string(text); + memdelete(f); + } + } + } + + + return err; +}; + +Error ResourceSaverPNG::save_image(const String &p_path, Image &p_img) { + + if (p_img.get_format() > Image::FORMAT_INDEXED_ALPHA) + p_img.decompress(); - ERR_FAIL_COND_V(img.get_format() > Image::FORMAT_INDEXED_ALPHA, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_img.get_format() > Image::FORMAT_INDEXED_ALPHA, ERR_INVALID_PARAMETER); png_structp png_ptr; png_infop info_ptr; @@ -89,7 +134,7 @@ Error ResourceSaverPNG::save(const String &p_path,const RES& p_resource,uint32_t int cs=0; - switch(img.get_format()) { + switch(p_img.get_format()) { case Image::FORMAT_GRAYSCALE: { @@ -113,14 +158,14 @@ Error ResourceSaverPNG::save(const String &p_path,const RES& p_resource,uint32_t } break; default: { - if (img.detect_alpha()) { + if (p_img.detect_alpha()) { - img.convert(Image::FORMAT_RGBA); + p_img.convert(Image::FORMAT_RGBA); pngf=PNG_COLOR_TYPE_RGB_ALPHA; cs=4; } else { - img.convert(Image::FORMAT_RGB); + p_img.convert(Image::FORMAT_RGB); pngf=PNG_COLOR_TYPE_RGB; cs=3; } @@ -128,8 +173,8 @@ Error ResourceSaverPNG::save(const String &p_path,const RES& p_resource,uint32_t } } - int w = img.get_width(); - int h = img.get_height(); + int w = p_img.get_width(); + int h = p_img.get_height(); png_set_IHDR(png_ptr, info_ptr, w,h, 8, pngf, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); @@ -144,7 +189,7 @@ Error ResourceSaverPNG::save(const String &p_path,const RES& p_resource,uint32_t } - DVector<uint8_t>::Read r = img.get_data().read(); + DVector<uint8_t>::Read r = p_img.get_data().read(); row_pointers = (png_bytep*)memalloc(sizeof(png_bytep)*h); for(int i=0;i<h;i++) { @@ -165,43 +210,6 @@ Error ResourceSaverPNG::save(const String &p_path,const RES& p_resource,uint32_t png_write_end(png_ptr, NULL); memdelete(f); - - if (true) { - - bool global_filter = Globals::get_singleton()->get("image_loader/filter"); - bool global_mipmaps = Globals::get_singleton()->get("image_loader/gen_mipmaps"); - bool global_repeat = Globals::get_singleton()->get("image_loader/repeat"); - - String text; - - if (global_filter!=bool(texture->get_flags()&Texture::FLAG_FILTER)) { - text+=bool(texture->get_flags()&Texture::FLAG_FILTER)?"filter=true\n":"filter=false\n"; - } - if (global_mipmaps!=bool(texture->get_flags()&Texture::FLAG_MIPMAPS)) { - text+=bool(texture->get_flags()&Texture::FLAG_FILTER)?"gen_mipmaps=true\n":"gen_mipmaps=false\n"; - } - if (global_repeat!=bool(texture->get_flags()&Texture::FLAG_REPEAT)) { - text+=bool(texture->get_flags()&Texture::FLAG_FILTER)?"repeat=true\n":"repeat=false\n"; - } - if (bool(texture->get_flags()&Texture::FLAG_ANISOTROPIC_FILTER)) { - text+="anisotropic=true\n"; - } - if (bool(texture->get_flags()&Texture::FLAG_CONVERT_TO_LINEAR)) { - text+="tolinear=true\n"; - } - - if (text!="" || FileAccess::exists(p_path+".flags")) { - - f = FileAccess::open(p_path+".flags",FileAccess::WRITE); - if (f) { - - f->store_string(text); - memdelete(f); - } - } - } - - /* cleanup heap allocation */ return OK; @@ -218,3 +226,8 @@ void ResourceSaverPNG::get_recognized_extensions(const RES& p_resource,List<Stri } } + +ResourceSaverPNG::ResourceSaverPNG() { + + Image::save_png_func = &save_image; +}; diff --git a/drivers/png/resource_saver_png.h b/drivers/png/resource_saver_png.h index 7da50150e2..116d425d24 100644 --- a/drivers/png/resource_saver_png.h +++ b/drivers/png/resource_saver_png.h @@ -6,9 +6,13 @@ class ResourceSaverPNG : public ResourceFormatSaver { public: + static Error save_image(const String &p_path, Image& p_img); + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); virtual bool recognize(const RES& p_resource) const; virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const; + + ResourceSaverPNG(); }; diff --git a/drivers/theoraplayer/SCsub b/drivers/theoraplayer/SCsub index 979ff2ed1b..d4218debb6 100644 --- a/drivers/theoraplayer/SCsub +++ b/drivers/theoraplayer/SCsub @@ -67,10 +67,13 @@ if env["platform"] == "iphone": env_theora = env.Clone() -env_theora.Append(CPPFLAGS=["-D_YUV_C", "-D__THEORA", "-D_LIB"]) +env_theora.Append(CPPFLAGS=["-D_YUV_C", "-D_LIB", "-D__THEORA"]) if env["platform"] == "iphone": env_theora.Append(CPPFLAGS=["-D__AVFOUNDATION"]) +else: + pass + #env_theora.Append(CPPFLAGS=["-D__FFMPEG"]) if env["platform"] == "android": env_theora.Append(CPPFLAGS=["-D_ANDROID"]) diff --git a/drivers/theoraplayer/include/theoraplayer/TheoraVideoClip.h b/drivers/theoraplayer/include/theoraplayer/TheoraVideoClip.h index b2987c01c4..fe71cf8566 100644 --- a/drivers/theoraplayer/include/theoraplayer/TheoraVideoClip.h +++ b/drivers/theoraplayer/include/theoraplayer/TheoraVideoClip.h @@ -87,6 +87,7 @@ protected: std::string mName; int mWidth, mHeight, mStride; int mNumFrames; + int audio_track; int mSubFrameWidth, mSubFrameHeight, mSubFrameOffsetX, mSubFrameOffsetY; float mAudioGain; //! multiplier for audio samples. between 0 and 1 @@ -233,6 +234,7 @@ public: bool getAutoRestart() { return mAutoRestart; } + void set_audio_track(int p_track) { audio_track=p_track; } /** TODO: user priority. Useful only when more than one video is being decoded diff --git a/drivers/theoraplayer/include/theoraplayer/TheoraVideoManager.h b/drivers/theoraplayer/include/theoraplayer/TheoraVideoManager.h index 3ff9b217cd..d94c51b4d4 100644 --- a/drivers/theoraplayer/include/theoraplayer/TheoraVideoManager.h +++ b/drivers/theoraplayer/include/theoraplayer/TheoraVideoManager.h @@ -67,8 +67,8 @@ public: //! search registered clips by name TheoraVideoClip* getVideoClipByName(std::string name); - TheoraVideoClip* createVideoClip(std::string filename,TheoraOutputMode output_mode=TH_RGB,int numPrecachedOverride=0,bool usePower2Stride=0); - TheoraVideoClip* createVideoClip(TheoraDataSource* data_source,TheoraOutputMode output_mode=TH_RGB,int numPrecachedOverride=0,bool usePower2Stride=0); + TheoraVideoClip* createVideoClip(std::string filename,TheoraOutputMode output_mode=TH_RGB,int numPrecachedOverride=0,bool usePower2Stride=0, int p_track=0); + TheoraVideoClip* createVideoClip(TheoraDataSource* data_source,TheoraOutputMode output_mode=TH_RGB,int numPrecachedOverride=0,bool usePower2Stride=0, int p_audio_track=0); void update(float timeDelta); diff --git a/drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm b/drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm index 8c3d2cc3b9..1b5cf0ab13 100644 --- a/drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm +++ b/drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm @@ -271,7 +271,10 @@ void TheoraVideoClip_AVFoundation::load(TheoraDataSource* source) AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; NSArray* audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio]; - AVAssetTrack *audioTrack = audioTracks.count > 0 ? [audioTracks objectAtIndex:0] : NULL; + if (audio_track >= audioTracks.count) + audio_track = 0; + AVAssetTrack *audioTrack = audioTracks.count > 0 ? [audioTracks objectAtIndex:audio_track] : NULL; + printf("*********** using audio track %i\n", audio_track); #ifdef _AVFOUNDATION_BGRX bool yuv_output = (mOutputMode != TH_BGRX && mOutputMode != TH_RGBA); diff --git a/drivers/theoraplayer/src/TheoraVideoClip.cpp b/drivers/theoraplayer/src/TheoraVideoClip.cpp index b71319e6a1..ed9f2c22da 100644 --- a/drivers/theoraplayer/src/TheoraVideoClip.cpp +++ b/drivers/theoraplayer/src/TheoraVideoClip.cpp @@ -51,6 +51,8 @@ TheoraVideoClip::TheoraVideoClip(TheoraDataSource* data_source, mWaitingForCache(false), mOutputMode(TH_UNDEFINED) { + + audio_track=0; mAudioMutex = NULL; mThreadAccessMutex = new TheoraMutex(); mTimer = mDefaultTimer = new TheoraTimer(); diff --git a/drivers/theoraplayer/src/TheoraVideoManager.cpp b/drivers/theoraplayer/src/TheoraVideoManager.cpp index 87696d12a9..53b211374a 100644 --- a/drivers/theoraplayer/src/TheoraVideoManager.cpp +++ b/drivers/theoraplayer/src/TheoraVideoManager.cpp @@ -35,6 +35,8 @@ extern "C" void initYUVConversionModule(); } +#include "core/os/memory.h" + //#define _DECODING_BENCHMARK //uncomment to test average decoding time on a given device @@ -184,16 +186,18 @@ TheoraAudioInterfaceFactory* TheoraVideoManager::getAudioInterfaceFactory() TheoraVideoClip* TheoraVideoManager::createVideoClip(std::string filename, TheoraOutputMode output_mode, int numPrecachedOverride, - bool usePower2Stride) + bool usePower2Stride, + int p_track) { - TheoraDataSource* src=new TheoraFileDataSource(filename); - return createVideoClip(src,output_mode,numPrecachedOverride,usePower2Stride); + TheoraDataSource* src=memnew(TheoraFileDataSource(filename)); + return createVideoClip(src,output_mode,numPrecachedOverride,usePower2Stride, p_track); } TheoraVideoClip* TheoraVideoManager::createVideoClip(TheoraDataSource* data_source, TheoraOutputMode output_mode, int numPrecachedOverride, - bool usePower2Stride) + bool usePower2Stride, + int p_audio_track) { mWorkMutex->lock(); @@ -226,6 +230,8 @@ TheoraVideoClip* TheoraVideoManager::createVideoClip(TheoraDataSource* data_sour #ifdef __FFMPEG clip = new TheoraVideoClip_FFmpeg(data_source, output_mode, nPrecached, usePower2Stride); #endif + + clip->set_audio_track(p_audio_track); clip->load(data_source); clip->decodeNextFrame(); // ensure the first frame is always preloaded and have the main thread do it to prevent potential thread starvatio diff --git a/drivers/theoraplayer/video_stream_theoraplayer.cpp b/drivers/theoraplayer/video_stream_theoraplayer.cpp index fdf612ff0f..62dee1336a 100644 --- a/drivers/theoraplayer/video_stream_theoraplayer.cpp +++ b/drivers/theoraplayer/video_stream_theoraplayer.cpp @@ -39,6 +39,8 @@ #include "core/ring_buffer.h" #include "core/os/thread_safe.h" +#include "core/globals.h" + static TheoraVideoManager* mgr = NULL; class TPDataFA : public TheoraDataSource { @@ -120,6 +122,7 @@ class AudioStreamInput : public AudioStreamResampled { int rb_power; int total_wrote; bool playing; + bool paused; public: @@ -131,6 +134,7 @@ public: AudioServer::get_singleton()->stream_set_active(stream_rid,true); AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,1); playing = true; + paused = false; }; virtual void stop() { @@ -141,10 +145,11 @@ public: playing=false; _clear(); }; + virtual bool is_playing() const { return true; }; - virtual void set_paused(bool p_paused) {}; - virtual bool is_paused(bool p_paused) const { return false; }; + virtual void set_paused(bool p_paused) { paused = p_paused; }; + virtual bool is_paused(bool p_paused) const { return paused; }; virtual void set_loop(bool p_enable) {}; virtual bool has_loop() const { return false; }; @@ -164,12 +169,16 @@ public: void input(float* p_data, int p_samples) { + _THREAD_SAFE_METHOD_; + //printf("input %i samples from %p\n", p_samples, p_data); if (rb.space_left() < p_samples) { rb_power += 1; rb.resize(rb_power); } rb.write(p_data, p_samples); + + update(); //update too here for less latency }; void update() { @@ -177,15 +186,16 @@ public: _THREAD_SAFE_METHOD_; int todo = get_todo(); int16_t* buffer = get_write_buffer(); - int samples = rb.data_left(); - const int to_write = MIN(todo, samples); + int frames = rb.data_left()/channels; + const int to_write = MIN(todo, frames); - for (int i=0; i<to_write; i++) { + for (int i=0; i<to_write*channels; i++) { - uint16_t sample = uint16_t(rb.read() * 32767); + int v = rb.read() * 32767; + int16_t sample = CLAMP(v,-32768,32767); buffer[i] = sample; }; - write(to_write/channels); + write(to_write); total_wrote += to_write; }; @@ -201,6 +211,7 @@ public: AudioStreamInput(int p_channels, int p_freq) { playing = false; + paused = true; channels = p_channels; freq = p_freq; total_wrote = 0; @@ -231,7 +242,7 @@ public: TPAudioGodot(TheoraVideoClip* owner, int nChannels, int p_freq) : TheoraAudioInterface(owner, nChannels, p_freq), TheoraTimer() { - printf("***************** audio interface constructor\n"); + printf("***************** audio interface constructor freq %i\n", p_freq); channels = nChannels; freq = p_freq; stream = Ref<AudioStreamInput>(memnew(AudioStreamInput(nChannels, p_freq))); @@ -247,12 +258,13 @@ public: void update(float time_increase) { - mTime = (float)(stream->get_total_wrote() / channels) / freq; + //mTime = (float)(stream->get_total_wrote()) / freq; + //mTime = MAX(0,mTime-AudioServer::get_singleton()->get_output_delay()); //mTime = (float)sample_count / channels / freq; - //mTime += time_increase; + mTime += time_increase; //float duration=mClip->getDuration(); //if (mTime > duration) mTime=duration; - //printf("time at timer is %f, samples %i\n", mTime, sample_count); + //printf("time at timer is %f, %f, samples %i\n", mTime, time_increase, sample_count); } }; @@ -276,12 +288,12 @@ void VideoStreamTheoraplayer::stop() { clip->stop(); clip->seek(0); }; + started = true; }; void VideoStreamTheoraplayer::play() { - - playing = true; - started = true; + if (clip) + playing = true; }; bool VideoStreamTheoraplayer::is_playing() const { @@ -291,7 +303,13 @@ bool VideoStreamTheoraplayer::is_playing() const { void VideoStreamTheoraplayer::set_paused(bool p_paused) { - playing = false; + paused = p_paused; + if (paused) { + clip->pause(); + } else { + if (clip && playing && !started) + clip->play(); + } }; bool VideoStreamTheoraplayer::is_paused(bool p_paused) const { @@ -346,6 +364,9 @@ int VideoStreamTheoraplayer::get_pending_frame_count() const { void VideoStreamTheoraplayer::pop_frame(Ref<ImageTexture> p_tex) { + if (!clip) + return; + TheoraVideoFrame* f = clip->getNextFrame(); if (!f) { return; @@ -358,13 +379,15 @@ void VideoStreamTheoraplayer::pop_frame(Ref<ImageTexture> p_tex) { #endif float w=clip->getWidth(),h=clip->getHeight(); - int imgsize = w * h * f->mBpp; + int imgsize = w * h * f->mBpp; int size = f->getStride() * f->getHeight() * f->mBpp; data.resize(imgsize); - DVector<uint8_t>::Write wr = data.write(); - uint8_t* ptr = wr.ptr(); - copymem(ptr, f->getBuffer(), imgsize); + { + DVector<uint8_t>::Write wr = data.write(); + uint8_t* ptr = wr.ptr(); + copymem(ptr, f->getBuffer(), imgsize); + } /* for (int i=0; i<h; i++) { int dstofs = i * w * f->mBpp; @@ -405,6 +428,12 @@ void VideoStreamTheoraplayer::update(float p_time) { if (!mgr) return; + if (!clip) + return; + + if (!playing || paused) + return; + //printf("video update!\n"); if (started) { if (clip->getNumReadyFrames() < 2) { @@ -421,6 +450,13 @@ void VideoStreamTheoraplayer::update(float p_time) { mgr->update(p_time); }; + +void VideoStreamTheoraplayer::set_audio_track(int p_idx) { + audio_track=p_idx; + if (clip) + clip->set_audio_track(audio_track); +} + void VideoStreamTheoraplayer::set_file(const String& p_file) { FileAccess* f = FileAccess::open(p_file, FileAccess::READ); @@ -436,10 +472,13 @@ void VideoStreamTheoraplayer::set_file(const String& p_file) { mgr->setAudioInterfaceFactory(audio_factory); }; + int track = GLOBAL_DEF("theora/audio_track", 0); // hack + if (p_file.find(".mp4") != -1) { std::string file = p_file.replace("res://", "").utf8().get_data(); - clip = mgr->createVideoClip(file, TH_BGRX, 16); + clip = mgr->createVideoClip(file, TH_RGBX, 2, false, track); + //clip->set_audio_track(audio_track); memdelete(f); } else { @@ -448,6 +487,7 @@ void VideoStreamTheoraplayer::set_file(const String& p_file) { try { clip = mgr->createVideoClip(ds); + clip->set_audio_track(audio_track); } catch (_TheoraGenericException e) { printf("exception ocurred! %s\n", e.repr().c_str()); clip = NULL; @@ -477,7 +517,9 @@ VideoStreamTheoraplayer::VideoStreamTheoraplayer() { clip = NULL; started = false; playing = false; + paused = false; loop = false; + audio_track=0; }; diff --git a/drivers/theoraplayer/video_stream_theoraplayer.h b/drivers/theoraplayer/video_stream_theoraplayer.h index d88f495032..d43c12609f 100644 --- a/drivers/theoraplayer/video_stream_theoraplayer.h +++ b/drivers/theoraplayer/video_stream_theoraplayer.h @@ -17,6 +17,9 @@ class VideoStreamTheoraplayer : public VideoStream { bool started; bool playing; bool loop; + bool paused; + + int audio_track; public: @@ -43,6 +46,7 @@ public: void update(float p_time); void set_file(const String& p_file); + void set_audio_track(int p_idx); ~VideoStreamTheoraplayer(); VideoStreamTheoraplayer(); diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index ef4cf644fd..e6458068ea 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -42,6 +42,7 @@ #include "dir_access_unix.h" #include "tcp_server_posix.h" #include "stream_peer_tcp_posix.h" +#include "packet_peer_udp_posix.h" #include <stdarg.h> @@ -115,6 +116,7 @@ void OS_Unix::initialize_core() { #ifndef NO_NETWORK TCPServerPosix::make_default(); StreamPeerTCPPosix::make_default(); + PacketPeerUDPPosix::make_default(); IP_Unix::make_default(); #endif mempool_static = new MemoryPoolStaticMalloc; diff --git a/drivers/unix/packet_peer_udp_posix.cpp b/drivers/unix/packet_peer_udp_posix.cpp new file mode 100644 index 0000000000..26a0b29228 --- /dev/null +++ b/drivers/unix/packet_peer_udp_posix.cpp @@ -0,0 +1,194 @@ +#include "packet_peer_udp_posix.h" + +#ifdef UNIX_ENABLED + + +#include <errno.h> +#include <unistd.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <stdio.h> + +#ifndef NO_FCNTL +#include <sys/fcntl.h> +#else +#include <sys/ioctl.h> +#endif + +#ifdef JAVASCRIPT_ENABLED +#include <arpa/inet.h> +#endif + + +int PacketPeerUDPPosix::get_available_packet_count() const { + + Error err = const_cast<PacketPeerUDPPosix*>(this)->_poll(false); + if (err!=OK) + return 0; + + return queue_count; +} + +Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size) const{ + + Error err = const_cast<PacketPeerUDPPosix*>(this)->_poll(false); + if (err!=OK) + return err; + if (queue_count==0) + return ERR_UNAVAILABLE; + + uint32_t size; + rb.read((uint8_t*)&packet_ip.host,4,true); + rb.read((uint8_t*)&packet_port,4,true); + rb.read((uint8_t*)&size,4,true); + rb.read(packet_buffer,size,true); + --queue_count; + *r_buffer=packet_buffer; + r_buffer_size=size; + return OK; + +} +Error PacketPeerUDPPosix::put_packet(const uint8_t *p_buffer,int p_buffer_size){ + + int sock = _get_socket(); + ERR_FAIL_COND_V( sock == -1, FAILED ); + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(peer_port); + addr.sin_addr = *((struct in_addr*)&peer_addr.host); + + errno = 0; + int err; + + while ( (err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) { + + if (errno != EAGAIN) { + return FAILED; + } + } + + return OK; +} + +int PacketPeerUDPPosix::get_max_packet_size() const{ + + return 512; // uhm maybe not +} + +Error PacketPeerUDPPosix::listen(int p_port, int p_recv_buffer_size){ + + close(); + int sock = _get_socket(); + if (sock == -1 ) + return ERR_CANT_CREATE; + sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_port = htons(p_port); + addr.sin_addr.s_addr = INADDR_ANY; + if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) { + close(); + return ERR_UNAVAILABLE; + } + printf("UDP Connection listening on port %i bufsize %i \n", p_port,p_recv_buffer_size); + rb.resize(nearest_shift(p_recv_buffer_size)); + return OK; +} + +void PacketPeerUDPPosix::close(){ + + if (sockfd != -1) + ::close(sockfd); + sockfd=-1; + rb.resize(8); + queue_count=0; +} + + +Error PacketPeerUDPPosix::wait() { + + return _poll(true); +} + +Error PacketPeerUDPPosix::_poll(bool p_wait) { + + struct sockaddr_in from = {0}; + socklen_t len = sizeof(struct sockaddr_in); + int ret; + while ( (ret = recvfrom(sockfd, recv_buffer, MIN(sizeof(recv_buffer),rb.data_left()-12), p_wait?0:MSG_DONTWAIT, (struct sockaddr*)&from, &len)) > 0) { + rb.write((uint8_t*)&from.sin_addr, 4); + uint32_t port = ntohs(from.sin_port); + rb.write((uint8_t*)&port, 4); + rb.write((uint8_t*)&ret, 4); + rb.write(recv_buffer, ret); + len = sizeof(struct sockaddr_in); + ++queue_count; + }; + + if (ret == 0 || (ret == -1 && errno != EAGAIN) ) { + close(); + return FAILED; + }; + + return OK; +} +bool PacketPeerUDPPosix::is_listening() const{ + + return sockfd!=-1; +} + +IP_Address PacketPeerUDPPosix::get_packet_address() const { + + return packet_ip; +} + +int PacketPeerUDPPosix::get_packet_port() const{ + + return packet_port; +} + +int PacketPeerUDPPosix::_get_socket() { + + if (sockfd != -1) + return sockfd; + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ERR_FAIL_COND_V( sockfd == -1, -1 ); + //fcntl(sockfd, F_SETFL, O_NONBLOCK); + + return sockfd; +} + + +void PacketPeerUDPPosix::set_send_address(const IP_Address& p_address,int p_port) { + + peer_addr=p_address; + peer_port=p_port; +} + +PacketPeerUDP* PacketPeerUDPPosix::_create() { + + return memnew(PacketPeerUDPPosix); +}; + +void PacketPeerUDPPosix::make_default() { + + PacketPeerUDP::_create = PacketPeerUDPPosix::_create; +}; + + +PacketPeerUDPPosix::PacketPeerUDPPosix() { + + sockfd=-1; + packet_port=0; + queue_count=0; + peer_port=0; +} + +PacketPeerUDPPosix::~PacketPeerUDPPosix() { + + close(); +} +#endif diff --git a/drivers/unix/packet_peer_udp_posix.h b/drivers/unix/packet_peer_udp_posix.h new file mode 100644 index 0000000000..b14568eb5f --- /dev/null +++ b/drivers/unix/packet_peer_udp_posix.h @@ -0,0 +1,57 @@ +#ifndef PACKET_PEER_UDP_POSIX_H +#define PACKET_PEER_UDP_POSIX_H + +#ifdef UNIX_ENABLED + +#include "io/packet_peer_udp.h" +#include "ring_buffer.h" + +class PacketPeerUDPPosix : public PacketPeerUDP { + + + enum { + PACKET_BUFFER_SIZE=65536 + }; + + mutable RingBuffer<uint8_t> rb; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + IP_Address packet_ip; + int packet_port; + mutable int queue_count; + int sockfd; + + IP_Address peer_addr; + int peer_port; + + _FORCE_INLINE_ int _get_socket(); + + static PacketPeerUDP* _create(); + virtual Error _poll(bool p_block); + +public: + + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const; + virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size); + + virtual int get_max_packet_size() const; + + virtual Error listen(int p_port,int p_recv_buffer_size=65536); + virtual void close(); + virtual Error wait(); + virtual bool is_listening() const; + + virtual IP_Address get_packet_address() const; + virtual int get_packet_port() const; + + virtual void set_send_address(const IP_Address& p_address,int p_port); + + static void make_default(); + + PacketPeerUDPPosix(); + ~PacketPeerUDPPosix(); +}; + +#endif // PACKET_PEER_UDP_POSIX_H +#endif diff --git a/main/main.cpp b/main/main.cpp index ac2a20fb68..36f5f2aec5 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -819,6 +819,8 @@ Error Main::setup2() { register_module_types(); register_driver_types(); + ScriptServer::init_languages(); + MAIN_PRINT("Main: Load Translations"); translation_server->setup(); //register translations, load them, etc. diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index 8738185d41..7085ae6a56 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -2464,6 +2464,8 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "assert", "yield", "static", + "float", + "int", 0}; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 543eecdf8b..4470b7ca0c 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -125,7 +125,7 @@ void register_gdscript_types() { ObjectTypeDB::register_virtual_type<GDFunctionState>(); script_language_gd=memnew( GDScriptLanguage ); - script_language_gd->init(); + //script_language_gd->init(); ScriptServer::register_language(script_language_gd); resource_loader_gd=memnew( ResourceFormatLoaderGDScript ); ResourceLoader::add_resource_format_loader(resource_loader_gd); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index bcc818dac8..89f121c3f6 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1533,7 +1533,7 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() { device_lock = Mutex::create(); quit_request=false; orientation=0; - remove_prev=false; + remove_prev=true; device_thread=Thread::create(_device_poll_thread,this); devices_changed=true; diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index 76c2d06080..e214b75bb0 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -286,6 +286,9 @@ static int frame_count = 0; if (OS::get_singleton()->get_main_loop()) OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); [view_controller.view startAnimation]; // FIXME: resume seems to be recommended elsewhere + if (OSIPhone::get_singleton()->native_video_is_playing()) { + OSIPhone::get_singleton()->native_video_unpause(); + }; } - (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration { diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index c25742a854..a5ce376b8f 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -21,7 +21,8 @@ def get_opts(): return [ ('IPHONEPLATFORM', 'name of the iphone platform', 'iPhoneOS'), ('IPHONEPATH', 'the path to iphone toolchain', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain'), - ('IOS_SDK_VERSION', 'The SDK version', 'iPhoneOS7.0'), + #('IOS_SDK_VERSION', 'The SDK version', 'iPhoneOS7.0'), + ('IOS_SDK_VERSION', 'The SDK version', 'iPhoneOS8.1'), ('IPHONESDK', 'path to the iphone SDK', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/${IOS_SDK_VERSION}.sdk/'), ('game_center', 'Support for game center', 'yes'), ('store_kit', 'Support for in-app store', 'yes'), @@ -79,6 +80,8 @@ def configure(env): '-framework', 'Security', #'-framework', 'AdSupport', '-framework', 'MediaPlayer', + '-framework', 'AVFoundation', + '-framework', 'CoreMedia', ]) if env['game_center'] == 'yes': diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h index 3e6181ab90..8ae7c2f87d 100755 --- a/platform/iphone/gl_view.h +++ b/platform/iphone/gl_view.h @@ -32,6 +32,7 @@ #import <OpenGLES/ES1/gl.h> #import <OpenGLES/ES1/glext.h> #import <MediaPlayer/MediaPlayer.h> +#import <AVFoundation/AVFoundation.h> @protocol GLViewDelegate; @@ -66,6 +67,13 @@ @property(nonatomic, assign) id<GLViewDelegate> delegate; +// AVPlayer-related properties +@property(strong, nonatomic) AVAsset *avAsset; +@property(strong, nonatomic) AVPlayerItem *avPlayerItem; +@property(strong, nonatomic) AVPlayer *avPlayer; +@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer; + +// Old videoplayer properties @property(strong, nonatomic) MPMoviePlayerController *moviePlayerController; @property(strong, nonatomic) UIWindow *backgroundWindow; diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 06b679c305..55b5eabacf 100755 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -52,6 +52,7 @@ static GLView* _instance = NULL; static bool video_found_error = false; static bool video_playing = false; static float video_previous_volume = 0.0f; +static CMTime video_current_time; void _show_keyboard(String p_existing) { keyboard_text = p_existing; @@ -65,6 +66,7 @@ void _hide_keyboard() { keyboard_text = ""; }; +/* bool _play_video(String p_path, float p_volume) { float player_volume = p_volume * AudioServer::get_singleton()->get_singleton()->get_stream_global_volume_scale(); @@ -96,24 +98,97 @@ bool _play_video(String p_path, float p_volume) { return true; } +*/ + +bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) { + p_path = Globals::get_singleton()->globalize_path(p_path); + + NSString* file_path = [[[NSString alloc] initWithUTF8String:p_path.utf8().get_data()] autorelease]; + //NSURL *file_url = [NSURL fileURLWithPath:file_path]; + + _instance.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:file_path]]; + _instance.avPlayerItem =[[AVPlayerItem alloc]initWithAsset:_instance.avAsset]; + [_instance.avPlayerItem addObserver:_instance forKeyPath:@"status" options:0 context:nil]; + + _instance.avPlayer = [[AVPlayer alloc]initWithPlayerItem:_instance.avPlayerItem]; + _instance.avPlayerLayer =[AVPlayerLayer playerLayerWithPlayer:_instance.avPlayer]; + + [_instance.avPlayer addObserver:_instance forKeyPath:@"status" options:0 context:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_instance + selector:@selector(playerItemDidReachEnd:) + name:AVPlayerItemDidPlayToEndTimeNotification + object:[_instance.avPlayer currentItem]]; + + [_instance.avPlayerLayer setFrame:_instance.bounds]; + [_instance.layer addSublayer:_instance.avPlayerLayer]; + [_instance.avPlayer play]; + + AVMediaSelectionGroup *audioGroup = [_instance.avAsset mediaSelectionGroupForMediaCharacteristic: AVMediaCharacteristicAudible]; + + for (id track in audioGroup.options) + { + NSString* language = [[track locale] localeIdentifier]; + NSLog(@"subtitle lang: %@", language); + + if ([language isEqualToString:[NSString stringWithUTF8String:p_audio_track.utf8()]]) + { + [_instance.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup: audioGroup]; + break; + } + } + + AVMediaSelectionGroup *subtitlesGroup = [_instance.avAsset mediaSelectionGroupForMediaCharacteristic: AVMediaCharacteristicLegible]; + NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]]; + + for (id track in useableTracks) + { + NSString* language = [[track locale] localeIdentifier]; + NSLog(@"subtitle lang: %@", language); + + if ([language isEqualToString:[NSString stringWithUTF8String:p_subtitle_track.utf8()]]) + { + [_instance.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup: subtitlesGroup]; + break; + } + } + + video_playing = true; + + return true; +} bool _is_video_playing() { //NSInteger playback_state = _instance.moviePlayerController.playbackState; - return video_playing || _instance.moviePlayerController.playbackState == MPMoviePlaybackStatePlaying; + //return video_playing || _instance.moviePlayerController.playbackState == MPMoviePlaybackStatePlaying; //if (video_found_error) // return false; //return (_instance.moviePlayerController.playbackState == MPMoviePlaybackStatePlaying); + + return video_playing || (_instance.avPlayer.rate > 0 && !_instance.avPlayer.error); } void _pause_video() { - [_instance.moviePlayerController pause]; + //[_instance.moviePlayerController pause]; + video_current_time = _instance.avPlayer.currentTime; + [_instance.avPlayer pause]; video_playing = false; } +void _unpause_video() { + [_instance.avPlayer play]; + video_playing = true; + + //video_current_time = kCMTimeZero; +}; + void _stop_video() { - [_instance.moviePlayerController stop]; - [_instance.moviePlayerController.view removeFromSuperview]; + //[_instance.moviePlayerController stop]; + //[_instance.moviePlayerController.view removeFromSuperview]; //[[MPMusicPlayerController applicationMusicPlayer] setVolume: video_previous_volume]; + + [_instance.avPlayer pause]; + [_instance.avPlayerLayer removeFromSuperlayer]; + _instance.avPlayer = nil; video_playing = false; } @@ -326,6 +401,11 @@ static void clear_touches() { active = TRUE; printf("start animation!\n"); animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES]; + + if (video_playing) + { + _unpause_video(); + } } - (void)stopAnimation @@ -337,6 +417,11 @@ static void clear_touches() { [animationTimer invalidate]; animationTimer = nil; clear_touches(); + + if (video_playing) + { + // save position + } } - (void)setAnimationInterval:(NSTimeInterval)interval @@ -375,9 +460,11 @@ static void clear_touches() { glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; +#ifdef DEBUG_ENABLED GLenum err = glGetError(); if(err) NSLog(@"%x error", err); +#endif } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event @@ -523,6 +610,32 @@ static void clear_touches() { [super dealloc]; } +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context { + + if (object == _instance.avPlayerItem && [keyPath isEqualToString:@"status"]) { + if (_instance.avPlayerItem.status == AVPlayerStatusFailed || _instance.avPlayer.status == AVPlayerStatusFailed) { + _stop_video(); + video_found_error = true; + } + + if(_instance.avPlayer.status == AVPlayerStatusReadyToPlay && + _instance.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && + CMTIME_COMPARE_INLINE(video_current_time, ==, kCMTimeZero)) { + + //NSLog(@"time: %@", video_current_time); + + [_instance.avPlayer seekToTime:video_current_time]; + video_current_time = kCMTimeZero; + } + } +} + +- (void)playerItemDidReachEnd:(NSNotification *)notification { + _stop_video(); +} + +/* - (void)moviePlayBackDidFinish:(NSNotification*)notification { @@ -557,5 +670,6 @@ static void clear_touches() { //[[MPMusicPlayerController applicationMusicPlayer] setVolume: video_previous_volume]; video_playing = false; } +*/ @end diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 2ef732183b..c8a132c39b 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -234,6 +234,7 @@ void OSIPhone::mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_ ev.mouse_button.x = ev.mouse_button.global_x = p_x; ev.mouse_button.y = ev.mouse_button.global_y = p_y; + input->set_mouse_pos(Point2(ev.mouse_motion.x,ev.mouse_motion.y)); ev.mouse_button.button_index = BUTTON_LEFT; ev.mouse_button.doubleclick = p_doubleclick; ev.mouse_button.pressed = p_pressed; @@ -485,13 +486,14 @@ String OSIPhone::get_locale() const { return locale_code; } -extern bool _play_video(String p_path, float p_volume); +extern bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track); extern bool _is_video_playing(); extern void _pause_video(); +extern void _unpause_video(); extern void _stop_video(); -Error OSIPhone::native_video_play(String p_path, float p_volume) { - if ( _play_video(p_path, p_volume) ) +Error OSIPhone::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) { + if ( _play_video(p_path, p_volume, p_audio_track, p_subtitle_track) ) return OK; return FAILED; } @@ -505,6 +507,11 @@ void OSIPhone::native_video_pause() { _pause_video(); } +void OSIPhone::native_video_unpause() { + _unpause_video(); +}; + + void OSIPhone::native_video_stop() { if (native_video_is_playing()) _stop_video(); diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 14b46816e9..bf8c5bb1c9 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -184,9 +184,10 @@ public: void set_unique_ID(String p_ID); String get_unique_ID() const; - virtual Error native_video_play(String p_path, float p_volume); + virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track); virtual bool native_video_is_playing() const; virtual void native_video_pause(); + virtual void native_video_unpause(); virtual void native_video_stop(); OSIPhone(int width, int height); diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 08a775e689..a77428e954 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -7,6 +7,7 @@ common_win=[ "ctxgl_procaddr.cpp", "key_mapping_win.cpp", "tcp_server_winsock.cpp", + "packet_peer_udp_winsock.cpp", "stream_peer_winsock.cpp", ] diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index be53d2d46a..8eb5746948 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -45,6 +45,7 @@ #include "servers/visual/visual_server_wrap_mt.h" #include "tcp_server_winsock.h" +#include "packet_peer_udp_winsock.h" #include "stream_peer_winsock.h" #include "os/pc_joystick_map.h" #include "lang_table.h" @@ -173,6 +174,7 @@ void OS_Windows::initialize_core() { TCPServerWinsock::make_default(); StreamPeerWinsock::make_default(); + PacketPeerUDPWinsock::make_default(); mempool_static = new MemoryPoolStaticMalloc; #if 1 @@ -1492,7 +1494,7 @@ OS::Date OS_Windows::get_date() const { OS::Time OS_Windows::get_time() const { SYSTEMTIME systemtime; - GetSystemTime(&systemtime); + GetLocalTime(&systemtime); Time time; time.hour=systemtime.wHour; diff --git a/platform/windows/packet_peer_udp_winsock.cpp b/platform/windows/packet_peer_udp_winsock.cpp new file mode 100644 index 0000000000..aff92b8fc8 --- /dev/null +++ b/platform/windows/packet_peer_udp_winsock.cpp @@ -0,0 +1,202 @@ +#include "packet_peer_udp_winsock.h" + +#include <winsock2.h> + +int PacketPeerUDPWinsock::get_available_packet_count() const { + + Error err = const_cast<PacketPeerUDPWinsock*>(this)->_poll(false); + if (err!=OK) + return 0; + + return queue_count; +} + +Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_size) const{ + + Error err = const_cast<PacketPeerUDPWinsock*>(this)->_poll(false); + if (err!=OK) + return err; + if (queue_count==0) + return ERR_UNAVAILABLE; + + uint32_t size; + rb.read((uint8_t*)&packet_ip.host,4,true); + rb.read((uint8_t*)&packet_port,4,true); + rb.read((uint8_t*)&size,4,true); + rb.read(packet_buffer,size,true); + --queue_count; + *r_buffer=packet_buffer; + r_buffer_size=size; + return OK; + +} +Error PacketPeerUDPWinsock::put_packet(const uint8_t *p_buffer,int p_buffer_size){ + + int sock = _get_socket(); + ERR_FAIL_COND_V( sock == -1, FAILED ); + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(peer_port); + addr.sin_addr = *((struct in_addr*)&peer_addr.host); + + + _set_blocking(true); + + errno = 0; + int err; + while ( (err = sendto(sock, (const char*)p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) { + + if (WSAGetLastError() != WSAEWOULDBLOCK) { + return FAILED; + }; + } + + return OK; +} + +int PacketPeerUDPWinsock::get_max_packet_size() const{ + + return 512; // uhm maybe not +} + + +void PacketPeerUDPWinsock::_set_blocking(bool p_blocking) { + //am no windows expert + //hope this is the right thing + + if (blocking==p_blocking) + return; + + blocking=p_blocking; + unsigned long par = blocking?0:1; + if (ioctlsocket(sockfd, FIONBIO, &par)) { + perror("setting non-block mode"); + //close(); + //return -1; + }; +} + +Error PacketPeerUDPWinsock::listen(int p_port, int p_recv_buffer_size){ + + close(); + int sock = _get_socket(); + if (sock == -1 ) + return ERR_CANT_CREATE; + sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_port = htons(p_port); + addr.sin_addr.s_addr = INADDR_ANY; + if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) { + close(); + return ERR_UNAVAILABLE; + } + + blocking=true; + + printf("UDP Connection listening on port %i\n", p_port); + rb.resize(nearest_shift(p_recv_buffer_size)); + return OK; +} + +void PacketPeerUDPWinsock::close(){ + + if (sockfd != -1) + ::closesocket(sockfd); + sockfd=-1; + rb.resize(8); + queue_count=0; +} + + +Error PacketPeerUDPWinsock::wait() { + + return _poll(true); +} +Error PacketPeerUDPWinsock::_poll(bool p_wait) { + + + _set_blocking(p_wait); + + + struct sockaddr_in from = {0}; + int len = sizeof(struct sockaddr_in); + int ret; + while ( (ret = recvfrom(sockfd, (char*)recv_buffer, MIN(sizeof(recv_buffer),rb.data_left()-12), 0, (struct sockaddr*)&from, &len)) > 0) { + rb.write((uint8_t*)&from.sin_addr, 4); + uint32_t port = ntohs(from.sin_port); + rb.write((uint8_t*)&port, 4); + rb.write((uint8_t*)&ret, 4); + rb.write(recv_buffer, ret); + + len = sizeof(struct sockaddr_in); + ++queue_count; + }; + + + if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) ) { + close(); + return FAILED; + }; + + + return OK; +} + +bool PacketPeerUDPWinsock::is_listening() const{ + + return sockfd!=-1; +} + +IP_Address PacketPeerUDPWinsock::get_packet_address() const { + + return packet_ip; +} + +int PacketPeerUDPWinsock::get_packet_port() const{ + + return packet_port; +} + +int PacketPeerUDPWinsock::_get_socket() { + + if (sockfd != -1) + return sockfd; + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ERR_FAIL_COND_V( sockfd == -1, -1 ); + //fcntl(sockfd, F_SETFL, O_NONBLOCK); + + return sockfd; +} + + +void PacketPeerUDPWinsock::set_send_address(const IP_Address& p_address,int p_port) { + + peer_addr=p_address; + peer_port=p_port; +} + +void PacketPeerUDPWinsock::make_default() { + + PacketPeerUDP::_create = PacketPeerUDPWinsock::_create; +}; + + +PacketPeerUDP* PacketPeerUDPWinsock::_create() { + + return memnew(PacketPeerUDPWinsock); +}; + + +PacketPeerUDPWinsock::PacketPeerUDPWinsock() { + + sockfd=-1; + packet_port=0; + queue_count=0; + peer_port=0; +} + +PacketPeerUDPWinsock::~PacketPeerUDPWinsock() { + + close(); +} diff --git a/platform/windows/packet_peer_udp_winsock.h b/platform/windows/packet_peer_udp_winsock.h new file mode 100644 index 0000000000..34dbcbee91 --- /dev/null +++ b/platform/windows/packet_peer_udp_winsock.h @@ -0,0 +1,56 @@ +#ifndef PACKET_PEER_UDP_WINSOCK_H +#define PACKET_PEER_UDP_WINSOCK_H + +#include "io/packet_peer_udp.h" +#include "ring_buffer.h" + +class PacketPeerUDPWinsock : public PacketPeerUDP { + + + enum { + PACKET_BUFFER_SIZE=65536 + }; + + mutable RingBuffer<uint8_t> rb; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + IP_Address packet_ip; + int packet_port; + mutable int queue_count; + int sockfd; + + IP_Address peer_addr; + int peer_port; + + _FORCE_INLINE_ int _get_socket(); + + static PacketPeerUDP* _create(); + + bool blocking; + void _set_blocking(bool p_blocking); + + Error _poll(bool p_wait); + +public: + + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const; + virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size); + + virtual int get_max_packet_size() const; + + virtual Error listen(int p_port,int p_recv_buffer_size=65536); + virtual void close(); + virtual Error wait(); + virtual bool is_listening() const; + + virtual IP_Address get_packet_address() const; + virtual int get_packet_port() const; + + virtual void set_send_address(const IP_Address& p_address,int p_port); + + static void make_default(); + PacketPeerUDPWinsock(); + ~PacketPeerUDPWinsock(); +}; +#endif // PACKET_PEER_UDP_WINSOCK_H diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 03de91fa2f..1c8d231d4a 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -123,7 +123,7 @@ def configure(env): if (is64 and env["bits"]=="32"): env.Append(CPPFLAGS=['-m32']) env.Append(LINKFLAGS=['-m32','-L/usr/lib/i386-linux-gnu']) - elif (not is64 and env["bits"]=="32"): + elif (not is64 and env["bits"]=="64"): env.Append(CPPFLAGS=['-m64']) env.Append(LINKFLAGS=['-m64','-L/usr/lib/i686-linux-gnu']) diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 33176c4f34..3114106235 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -263,6 +263,25 @@ bool Area2D::is_monitoring_enabled() const { return monitoring; } +Array Area2D::get_overlapping_bodies() const { + + ERR_FAIL_COND_V(!monitoring,Array()); + Array ret; + ret.resize(body_map.size()); + int idx=0; + for (const Map<ObjectID,BodyState>::Element *E=body_map.front();E;E=E->next()) { + Object *obj = ObjectDB::get_instance(E->key()); + if (!obj) { + ret.resize( ret.size() -1 ); //ops + } else { + ret[idx++]=obj; + } + + } + + return ret; +} + void Area2D::_bind_methods() { @@ -290,6 +309,8 @@ void Area2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_enable_monitoring","enable"),&Area2D::set_enable_monitoring); ObjectTypeDB::bind_method(_MD("is_monitoring_enabled"),&Area2D::is_monitoring_enabled); + ObjectTypeDB::bind_method(_MD("get_overlapping_bodies"),&Area2D::get_overlapping_bodies); + ObjectTypeDB::bind_method(_MD("_body_inout"),&Area2D::_body_inout); diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index 85f2b91582..c6210e7c9a 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -112,6 +112,8 @@ public: void set_enable_monitoring(bool p_enable); bool is_monitoring_enabled() const; + Array get_overlapping_bodies() const; //function for script + Area2D(); ~Area2D(); diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp index 7f492e743c..6e2cf5954b 100644 --- a/scene/2d/particles_2d.cpp +++ b/scene/2d/particles_2d.cpp @@ -355,6 +355,8 @@ void Particles2D::_process_particles(float p_delta) { p.rot=Math::deg2rad(param[PARAM_INITIAL_ANGLE]+param[PARAM_INITIAL_ANGLE]*randomness[PARAM_INITIAL_ANGLE]*_rand_from_seed(&rand_seed)); active_count++; + p.frame=Math::fmod(param[PARAM_ANIM_INITIAL_POS]+randomness[PARAM_ANIM_INITIAL_POS]*_rand_from_seed(&rand_seed),1.0); + } else { @@ -426,6 +428,8 @@ void Particles2D::_process_particles(float p_delta) { p.pos+=p.velocity*frame_time; p.rot+=Math::lerp(param[PARAM_SPIN_VELOCITY],param[PARAM_SPIN_VELOCITY]*randomness[PARAM_SPIN_VELOCITY]*_rand_from_seed(&rand_seed),randomness[PARAM_SPIN_VELOCITY])*frame_time; + float anim_spd=param[PARAM_ANIM_SPEED_SCALE]+param[PARAM_ANIM_SPEED_SCALE]*randomness[PARAM_ANIM_SPEED_SCALE]*_rand_from_seed(&rand_seed); + p.frame=Math::fposmod(p.frame+(frame_time/lifetime)*anim_spd,1.0); active_count++; @@ -474,9 +478,13 @@ void Particles2D::_notification(int p_what) { RID ci=get_canvas_item(); Size2 size(1,1); Point2 center; + int total_frames=1; if (!texture.is_null()) { size=texture->get_size(); + size.x/=h_frames; + size.y/=v_frames; + total_frames=h_frames*v_frames; } @@ -612,7 +620,17 @@ void Particles2D::_notification(int p_what) { if (texrid.is_valid()) { - texture->draw(ci,Point2(),color); + Rect2 src_rect; + src_rect.size=size; + + if (total_frames>1) { + int frame = Math::fast_ftoi(Math::floor(p.frame*total_frames)) % total_frames; + src_rect.pos.x = size.x * (frame%h_frames); + src_rect.pos.y = size.y * (frame/h_frames); + } + + + texture->draw_rect_region(ci,Rect2(Point2(),size),src_rect,color); //VisualServer::get_singleton()->canvas_item_add_texture_rect(ci,r,texrid,false,color); } else { VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2(),size),color); @@ -642,7 +660,9 @@ static const char* _particlesframe_property_names[Particles2D::PARAM_MAX]={ "params/initial_angle", "params/initial_size", "params/final_size", - "params/hue_variation" + "params/hue_variation", + "params/anim_speed_scale", + "params/anim_initial_pos", }; static const char* _particlesframe_property_rnames[Particles2D::PARAM_MAX]={ @@ -659,7 +679,9 @@ static const char* _particlesframe_property_rnames[Particles2D::PARAM_MAX]={ "randomness/initial_angle", "randomness/initial_size", "randomness/final_size", - "randomness/hue_variation" + "randomness/hue_variation", + "randomness/anim_speed_scale", + "randomness/anim_initial_pos", }; static const char* _particlesframe_property_ranges[Particles2D::PARAM_MAX]={ @@ -676,7 +698,9 @@ static const char* _particlesframe_property_ranges[Particles2D::PARAM_MAX]={ "0,360,0.01", "0,1024,0.01", "0,1024,0.01", - "0,1,0.01" + "0,1,0.01", + "0,128,0.01", + "0,1,0.01", }; @@ -909,6 +933,28 @@ bool Particles2D::is_flipped_v() const{ return flip_v; } +void Particles2D::set_h_frames(int p_frames) { + + ERR_FAIL_COND(p_frames<1); + h_frames=p_frames; +} + +int Particles2D::get_h_frames() const{ + + return h_frames; +} + +void Particles2D::set_v_frames(int p_frames){ + + ERR_FAIL_COND(p_frames<1); + v_frames=p_frames; +} +int Particles2D::get_v_frames() const{ + + return v_frames; +} + + void Particles2D::set_emission_points(const DVector<Vector2>& p_points) { @@ -958,6 +1004,12 @@ void Particles2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_flip_v","enable"),&Particles2D::set_flip_v); ObjectTypeDB::bind_method(_MD("is_flipped_v"),&Particles2D::is_flipped_v); + ObjectTypeDB::bind_method(_MD("set_h_frames","enable"),&Particles2D::set_h_frames); + ObjectTypeDB::bind_method(_MD("get_h_frames"),&Particles2D::get_h_frames); + + ObjectTypeDB::bind_method(_MD("set_v_frames","enable"),&Particles2D::set_v_frames); + ObjectTypeDB::bind_method(_MD("get_v_frames"),&Particles2D::get_v_frames); + ObjectTypeDB::bind_method(_MD("set_emission_half_extents","extents"),&Particles2D::set_emission_half_extents); ObjectTypeDB::bind_method(_MD("get_emission_half_extents"),&Particles2D::get_emission_half_extents); @@ -997,6 +1049,9 @@ void Particles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL,"config/flip_h"),_SCS("set_flip_h"),_SCS("is_flipped_h")); ADD_PROPERTY(PropertyInfo(Variant::BOOL,"config/flip_v"),_SCS("set_flip_v"),_SCS("is_flipped_v")); ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"config/texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"config/h_frames",PROPERTY_HINT_RANGE,"1,512,1"),_SCS("set_h_frames"),_SCS("get_h_frames")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"config/v_frames",PROPERTY_HINT_RANGE,"1,512,1"),_SCS("set_v_frames"),_SCS("get_v_frames")); + for(int i=0;i<PARAM_MAX;i++) { @@ -1054,6 +1109,7 @@ Particles2D::Particles2D() { set_param(PARAM_INITIAL_ANGLE,0.0); set_param(PARAM_INITIAL_SIZE,1.0); set_param(PARAM_FINAL_SIZE,1.0); + set_param(PARAM_ANIM_SPEED_SCALE,1.0); time=0; @@ -1081,6 +1137,8 @@ Particles2D::Particles2D() { flip_h=false; flip_v=false; + v_frames=1; + h_frames=1; emit_timeout = 0; time_to_live = 0; diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h index 47bd078a7a..6d91fcafc3 100644 --- a/scene/2d/particles_2d.h +++ b/scene/2d/particles_2d.h @@ -99,6 +99,8 @@ public: PARAM_INITIAL_SIZE, PARAM_FINAL_SIZE, PARAM_HUE_VARIATION, + PARAM_ANIM_SPEED_SCALE, + PARAM_ANIM_INITIAL_POS, PARAM_MAX }; @@ -117,8 +119,9 @@ private: Point2 pos; Vector2 velocity; float rot; + float frame; uint32_t seed; - Particle() { active=false; seed=123465789; rot=0;} + Particle() { active=false; seed=123465789; rot=0; frame=0;} }; Vector<Particle> particles; @@ -146,6 +149,8 @@ private: float time_scale; bool flip_h; bool flip_v; + int h_frames; + int v_frames; Point2 emissor_offset; Vector2 initial_velocity; Vector2 extents; @@ -206,6 +211,13 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; + + void set_h_frames(int p_frames); + int get_h_frames() const; + + void set_v_frames(int p_frames); + int get_v_frames() const; + void set_color_phases(int p_phases); int get_color_phases() const; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index f2f7a15e91..655c6e8bf6 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -618,6 +618,26 @@ RigidBody2D::CCDMode RigidBody2D::get_continuous_collision_detection_mode() cons } +Array RigidBody2D::get_colliding_bodies() const { + + ERR_FAIL_COND_V(!contact_monitor,Array()); + + Array ret; + ret.resize(contact_monitor->body_map.size()); + int idx=0; + for (const Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.front();E;E=E->next()) { + Object *obj = ObjectDB::get_instance(E->key()); + if (!obj) { + ret.resize( ret.size() -1 ); //ops + } else { + ret[idx++]=obj; + } + + } + + return ret; +} + void RigidBody2D::set_contact_monitor(bool p_enabled) { if (p_enabled==is_contact_monitor_enabled()) @@ -697,6 +717,8 @@ void RigidBody2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("_body_enter_tree"),&RigidBody2D::_body_enter_tree); ObjectTypeDB::bind_method(_MD("_body_exit_tree"),&RigidBody2D::_body_exit_tree); + ObjectTypeDB::bind_method(_MD("get_colliding_bodies"),&RigidBody2D::get_colliding_bodies); + BIND_VMETHOD(MethodInfo("_integrate_forces",PropertyInfo(Variant::OBJECT,"state:Physics2DDirectBodyState"))); ADD_PROPERTY( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Rigid,Static,Character,Kinematic"),_SCS("set_mode"),_SCS("get_mode")); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 77e909f105..ca7b757497 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -229,6 +229,8 @@ public: void set_applied_force(const Vector2& p_force); Vector2 get_applied_force() const; + Array get_colliding_bodies() const; //function for script + RigidBody2D(); ~RigidBody2D(); diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp index 407747fc0d..cb1df78fda 100644 --- a/scene/3d/area.cpp +++ b/scene/3d/area.cpp @@ -255,6 +255,24 @@ bool Area::is_monitoring_enabled() const { } +Array Area::get_overlapping_bodies() const { + + ERR_FAIL_COND_V(!monitoring,Array()); + Array ret; + ret.resize(body_map.size()); + int idx=0; + for (const Map<ObjectID,BodyState>::Element *E=body_map.front();E;E=E->next()) { + Object *obj = ObjectDB::get_instance(E->key()); + if (!obj) { + ret.resize( ret.size() -1 ); //ops + } else { + ret[idx++]=obj; + } + + } + + return ret; +} void Area::_bind_methods() { @@ -283,6 +301,8 @@ void Area::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_enable_monitoring","enable"),&Area::set_enable_monitoring); ObjectTypeDB::bind_method(_MD("is_monitoring_enabled"),&Area::is_monitoring_enabled); + ObjectTypeDB::bind_method(_MD("get_overlapping_bodies"),&Area::get_overlapping_bodies); + ObjectTypeDB::bind_method(_MD("_body_inout"),&Area::_body_inout); diff --git a/scene/3d/area.h b/scene/3d/area.h index 96b8585338..4707b73e1c 100644 --- a/scene/3d/area.h +++ b/scene/3d/area.h @@ -112,6 +112,8 @@ public: void set_enable_monitoring(bool p_enable); bool is_monitoring_enabled() const; + Array get_overlapping_bodies() const; + Area(); ~Area(); diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp index d22198d47e..ce002fb44b 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -182,6 +182,41 @@ void Navigation::navmesh_remove(int p_id){ } +void Navigation::_clip_path(Vector<Vector3>& path, Polygon *from_poly, const Vector3& p_to_point, Polygon* p_to_poly) { + + Vector3 from = path[path.size()-1]; + + if (from.distance_to(p_to_point)<CMP_EPSILON) + return; + Plane cut_plane; + cut_plane.normal = (from-p_to_point).cross(up); + if (cut_plane.normal==Vector3()) + return; + cut_plane.normal.normalize(); + cut_plane.d = cut_plane.normal.dot(from); + + + while(from_poly!=p_to_poly) { + + int pe = from_poly->prev_edge; + Vector3 a = _get_vertex(from_poly->edges[pe].point); + Vector3 b = _get_vertex(from_poly->edges[(pe+1)%from_poly->edges.size()].point); + + from_poly=from_poly->edges[pe].C; + ERR_FAIL_COND(!from_poly); + + if (a.distance_to(b)>CMP_EPSILON) { + + Vector3 inters; + if (cut_plane.intersects_segment(a,b,&inters)) { + if (inters.distance_to(p_to_point)>CMP_EPSILON && inters.distance_to(path[path.size()-1])>CMP_EPSILON) { + path.push_back(inters); + } + } + } + } +} + Vector<Vector3> Navigation::get_simple_path(const Vector3& p_start, const Vector3& p_end, bool p_optimize) { @@ -379,9 +414,12 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3& p_start, const Vector portal_left=left; } else { - apex_point=portal_right; + _clip_path(path,apex_poly,portal_right,right_poly); + + apex_point=portal_right; p=right_poly; left_poly=p; + apex_poly=p; portal_left=apex_point; portal_right=apex_point; path.push_back(apex_point); @@ -396,9 +434,12 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3& p_start, const Vector portal_right=right; } else { + _clip_path(path,apex_poly,portal_left,left_poly); + apex_point=portal_left; p=left_poly; right_poly=p; + apex_poly=p; portal_right=apex_point; portal_left=apex_point; path.push_back(apex_point); diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h index 9b6cf5fbc4..69d48531a7 100644 --- a/scene/3d/navigation.h +++ b/scene/3d/navigation.h @@ -118,6 +118,7 @@ class Navigation : public Spatial { int last_id; Vector3 up; + void _clip_path(Vector<Vector3>& path,Polygon *from_poly, const Vector3& p_to_point, Polygon* p_to_poly); protected: diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index a80fdce64c..21ecac6e3d 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -637,6 +637,27 @@ RigidBody::AxisLock RigidBody::get_axis_lock() const { } +Array RigidBody::get_colliding_bodies() const { + + ERR_FAIL_COND_V(!contact_monitor,Array()); + + Array ret; + ret.resize(contact_monitor->body_map.size()); + int idx=0; + for (const Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.front();E;E=E->next()) { + Object *obj = ObjectDB::get_instance(E->key()); + if (!obj) { + ret.resize( ret.size() -1 ); //ops + } else { + ret[idx++]=obj; + } + + } + + return ret; +} + + void RigidBody::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_mode","mode"),&RigidBody::set_mode); @@ -688,6 +709,8 @@ void RigidBody::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_axis_lock","axis_lock"),&RigidBody::set_axis_lock); ObjectTypeDB::bind_method(_MD("get_axis_lock"),&RigidBody::get_axis_lock); + ObjectTypeDB::bind_method(_MD("get_colliding_bodies"),&RigidBody::get_colliding_bodies); + BIND_VMETHOD(MethodInfo("_integrate_forces",PropertyInfo(Variant::OBJECT,"state:PhysicsDirectBodyState"))); ADD_PROPERTY( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Rigid,Static,Character,Kinematic"),_SCS("set_mode"),_SCS("get_mode")); diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index f9f028e20d..0d1de7f236 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -236,6 +236,7 @@ public: void set_axis_lock(AxisLock p_lock); AxisLock get_axis_lock() const; + Array get_colliding_bodies() const; void apply_impulse(const Vector3& p_pos, const Vector3& p_impulse); diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index b55cc0d1a3..ec72123c98 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -1662,7 +1662,7 @@ void AnimationTreePlayer::_bind_methods() { ObjectTypeDB::bind_method(_MD("node_get_type","id"),&AnimationTreePlayer::node_get_type); ObjectTypeDB::bind_method(_MD("node_get_input_count","id"),&AnimationTreePlayer::node_get_input_count); - ObjectTypeDB::bind_method(_MD("node_get_input_sourcre","id","idx"),&AnimationTreePlayer::node_get_input_source); + ObjectTypeDB::bind_method(_MD("node_get_input_source","id","idx"),&AnimationTreePlayer::node_get_input_source); ObjectTypeDB::bind_method(_MD("animation_node_set_animation","id","animation:Animation"),&AnimationTreePlayer::animation_node_set_animation); ObjectTypeDB::bind_method(_MD("animation_node_get_animation:Animation","id"),&AnimationTreePlayer::animation_node_get_animation); diff --git a/scene/audio/sound_room_params.cpp b/scene/audio/sound_room_params.cpp index 01d373ebff..f1067f12e0 100644 --- a/scene/audio/sound_room_params.cpp +++ b/scene/audio/sound_room_params.cpp @@ -54,7 +54,7 @@ void SoundRoomParams::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { -#if 0 +//#if 0 Node *n=this; Room *room_instance=NULL; while(n) { @@ -74,11 +74,11 @@ void SoundRoomParams::_notification(int p_what) { if (room_instance) { room=room_instance->get_sound_room(); } else { - room=get_scene()->get_default_world()->get_sound_space(); + room=get_viewport()->find_world()->get_sound_space(); } _update_sound_room(); -#endif +//#endif } break; case NOTIFICATION_EXIT_TREE: { diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 442fd286dd..cf3bef73ea 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -55,9 +55,9 @@ void BaseButton::_input_event(InputEvent p_event) { if (b.pressed) { if (!toggle_mode) { //mouse press attempt - - status.press_attempt=true; - status.pressing_inside=true; + + status.press_attempt=true; + status.pressing_inside=true; pressed(); emit_signal("pressed"); @@ -74,13 +74,13 @@ void BaseButton::_input_event(InputEvent p_event) { } - } else { - - if (status.press_attempt &&status.pressing_inside) { - pressed(); - emit_signal("pressed"); - } - status.press_attempt=false; + } else { + + if (status.press_attempt && status.pressing_inside) { +// released(); + emit_signal("released"); + } + status.press_attempt=false; } update(); break; @@ -95,14 +95,14 @@ void BaseButton::_input_event(InputEvent p_event) { if (status.press_attempt &&status.pressing_inside) { - + if (!toggle_mode) { //mouse press attempt - + pressed(); - emit_signal("pressed"); + emit_signal("pressed"); } else { - + status.pressed=!status.pressed; pressed(); @@ -110,11 +110,11 @@ void BaseButton::_input_event(InputEvent p_event) { toggled(status.pressed); emit_signal("toggled",status.pressed); - + } - + } - + status.press_attempt=false; } @@ -363,6 +363,7 @@ void BaseButton::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_draw_mode"),&BaseButton::get_draw_mode); ADD_SIGNAL( MethodInfo("pressed" ) ); + ADD_SIGNAL( MethodInfo("released" ) ); ADD_SIGNAL( MethodInfo("toggled", PropertyInfo( Variant::BOOL,"pressed") ) ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "disabled"), _SCS("set_disabled"), _SCS("is_disabled")); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "toggle_mode"), _SCS("set_toggle_mode"), _SCS("is_toggle_mode")); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 4a0a77e6db..b95d271394 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -40,7 +40,7 @@ #include "text_edit.h" #include "os/keyboard.h" #include "os/os.h" - + #include "globals.h" #include "message_queue.h" @@ -48,1550 +48,1561 @@ static bool _is_text_char(CharType c) { - return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; } static bool _is_symbol(CharType c) { - return c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t'); + return c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t'); } static bool _is_pair_right_symbol(CharType c) { - return - c == '"' || - c == '\'' || - c == ')' || - c == ']' || - c == '}'; + return + c == '"' || + c == '\'' || + c == ')' || + c == ']' || + c == '}'; } static bool _is_pair_left_symbol(CharType c) { - return - c == '"' || - c == '\'' || - c == '(' || - c == '[' || - c == '{'; + return + c == '"' || + c == '\'' || + c == '(' || + c == '[' || + c == '{'; } static bool _is_pair_symbol(CharType c) { - return _is_pair_left_symbol(c) || _is_pair_right_symbol(c); + return _is_pair_left_symbol(c) || _is_pair_right_symbol(c); } static CharType _get_right_pair_symbol(CharType c) { - if(c == '"') - return '"'; - if(c == '\'') - return '\''; - if(c == '(') - return ')'; - if(c == '[') - return ']'; - if(c == '{') - return '}'; - return 0; + if(c == '"') + return '"'; + if(c == '\'') + return '\''; + if(c == '(') + return ')'; + if(c == '[') + return ']'; + if(c == '{') + return '}'; + return 0; } void TextEdit::Text::set_font(const Ref<Font>& p_font) { - font=p_font; + font=p_font; } void TextEdit::Text::set_tab_size(int p_tab_size) { - tab_size=p_tab_size; + tab_size=p_tab_size; } void TextEdit::Text::_update_line_cache(int p_line) const { - int w =0; - int tab_w=font->get_char_size(' ').width; + int w =0; + int tab_w=font->get_char_size(' ').width; - int len = text[p_line].data.length(); - const CharType *str = text[p_line].data.c_str(); + int len = text[p_line].data.length(); + const CharType *str = text[p_line].data.c_str(); - //update width + //update width - for(int i=0;i<len;i++) { - if (str[i]=='\t') { + for(int i=0;i<len;i++) { + if (str[i]=='\t') { - int left = w%tab_w; - if (left==0) - w+=tab_w; - else - w+=tab_w-w%tab_w; // is right... + int left = w%tab_w; + if (left==0) + w+=tab_w; + else + w+=tab_w-w%tab_w; // is right... - } else { + } else { - w+=font->get_char_size(str[i],str[i+1]).width; - } - } + w+=font->get_char_size(str[i],str[i+1]).width; + } + } - text[p_line].width_cache=w; + text[p_line].width_cache=w; - //update regions + //update regions - text[p_line].region_info.clear(); + text[p_line].region_info.clear(); - for(int i=0;i<len;i++) { + for(int i=0;i<len;i++) { - if (!_is_symbol(str[i])) - continue; - if (str[i]=='\\') { - i++; //skip quoted anything - continue; - } + if (!_is_symbol(str[i])) + continue; + if (str[i]=='\\') { + i++; //skip quoted anything + continue; + } - int left=len-i; + int left=len-i; - for(int j=0;j<color_regions->size();j++) { + for(int j=0;j<color_regions->size();j++) { - const ColorRegion& cr=color_regions->operator [](j); + const ColorRegion& cr=color_regions->operator [](j); - /* BEGIN */ + /* BEGIN */ - int lr=cr.begin_key.length(); - if (lr==0 || lr>left) - continue; + int lr=cr.begin_key.length(); + if (lr==0 || lr>left) + continue; - const CharType* kc = cr.begin_key.c_str(); + const CharType* kc = cr.begin_key.c_str(); - bool match=true; + bool match=true; - for(int k=0;k<lr;k++) { - if (kc[k]!=str[i+k]) { - match=false; - break; - } - } + for(int k=0;k<lr;k++) { + if (kc[k]!=str[i+k]) { + match=false; + break; + } + } - if (match) { + if (match) { - ColorRegionInfo cri; - cri.end=false; - cri.region=j; - text[p_line].region_info[i]=cri; - i+=lr-1; - break; - } + ColorRegionInfo cri; + cri.end=false; + cri.region=j; + text[p_line].region_info[i]=cri; + i+=lr-1; + break; + } - /* END */ + /* END */ - lr=cr.end_key.length(); - if (lr==0 || lr>left) - continue; + lr=cr.end_key.length(); + if (lr==0 || lr>left) + continue; - kc = cr.end_key.c_str(); + kc = cr.end_key.c_str(); - match=true; + match=true; - for(int k=0;k<lr;k++) { - if (kc[k]!=str[i+k]) { - match=false; - break; - } - } + for(int k=0;k<lr;k++) { + if (kc[k]!=str[i+k]) { + match=false; + break; + } + } - if (match) { + if (match) { - ColorRegionInfo cri; - cri.end=true; - cri.region=j; - text[p_line].region_info[i]=cri; - i+=lr-1; - break; - } + ColorRegionInfo cri; + cri.end=true; + cri.region=j; + text[p_line].region_info[i]=cri; + i+=lr-1; + break; + } - } - } + } + } } const Map<int,TextEdit::Text::ColorRegionInfo>& TextEdit::Text::get_color_region_info(int p_line) { - Map<int,ColorRegionInfo> *cri=NULL; - ERR_FAIL_INDEX_V(p_line,text.size(),*cri); //enjoy your crash + Map<int,ColorRegionInfo> *cri=NULL; + ERR_FAIL_INDEX_V(p_line,text.size(),*cri); //enjoy your crash - if (text[p_line].width_cache==-1) { - _update_line_cache(p_line); - } + if (text[p_line].width_cache==-1) { + _update_line_cache(p_line); + } - return text[p_line].region_info; + return text[p_line].region_info; } int TextEdit::Text::get_line_width(int p_line) const { - ERR_FAIL_INDEX_V(p_line,text.size(),-1); + ERR_FAIL_INDEX_V(p_line,text.size(),-1); - if (text[p_line].width_cache==-1) { - _update_line_cache(p_line); - } + if (text[p_line].width_cache==-1) { + _update_line_cache(p_line); + } - return text[p_line].width_cache; + return text[p_line].width_cache; } void TextEdit::Text::clear_caches() { - for(int i=0;i<text.size();i++) - text[i].width_cache=-1; + for(int i=0;i<text.size();i++) + text[i].width_cache=-1; } void TextEdit::Text::clear() { - text.clear();; - insert(0,""); + text.clear();; + insert(0,""); } int TextEdit::Text::get_max_width() const { - //quite some work.. but should be fast enough. + //quite some work.. but should be fast enough. - int max = 0; + int max = 0; - for(int i=0;i<text.size();i++) - max=MAX(max,get_line_width(i)); - return max; + for(int i=0;i<text.size();i++) + max=MAX(max,get_line_width(i)); + return max; } void TextEdit::Text::set(int p_line,const String& p_text) { - ERR_FAIL_INDEX(p_line,text.size()); + ERR_FAIL_INDEX(p_line,text.size()); - text[p_line].width_cache=-1; - text[p_line].data=p_text; + text[p_line].width_cache=-1; + text[p_line].data=p_text; } void TextEdit::Text::insert(int p_at,const String& p_text) { - Line line; - line.marked=false; - line.breakpoint=false; - line.width_cache=-1; - line.data=p_text; - text.insert(p_at,line); + Line line; + line.marked=false; + line.breakpoint=false; + line.width_cache=-1; + line.data=p_text; + text.insert(p_at,line); } void TextEdit::Text::remove(int p_at) { - text.remove(p_at); + text.remove(p_at); } void TextEdit::_update_scrollbars() { - Size2 size = get_size(); - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); + Size2 size = get_size(); + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); - v_scroll->set_begin( Point2(size.width - vmin.width, cache.style_normal->get_margin(MARGIN_TOP)) ); - v_scroll->set_end( Point2(size.width, size.height - cache.style_normal->get_margin(MARGIN_TOP) - cache.style_normal->get_margin(MARGIN_BOTTOM)) ); + v_scroll->set_begin( Point2(size.width - vmin.width, cache.style_normal->get_margin(MARGIN_TOP)) ); + v_scroll->set_end( Point2(size.width, size.height - cache.style_normal->get_margin(MARGIN_TOP) - cache.style_normal->get_margin(MARGIN_BOTTOM)) ); - h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); - h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); + h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); + h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); - int hscroll_rows = ((hmin.height-1)/get_row_height())+1; - int visible_rows = get_visible_rows(); - int total_rows = text.size() * cache.line_spacing; + int hscroll_rows = ((hmin.height-1)/get_row_height())+1; + int visible_rows = get_visible_rows(); + int total_rows = text.size() * cache.line_spacing; - int vscroll_pixels = v_scroll->get_combined_minimum_size().width; - int visible_width = size.width - cache.style_normal->get_minimum_size().width; - int total_width = text.get_max_width(); + int vscroll_pixels = v_scroll->get_combined_minimum_size().width; + int visible_width = size.width - cache.style_normal->get_minimum_size().width; + int total_width = text.get_max_width(); - bool use_hscroll=true; - bool use_vscroll=true; + bool use_hscroll=true; + bool use_vscroll=true; - if (total_rows <= visible_rows && total_width <= visible_width) { - //thanks yessopie for this clever bit of logic - use_hscroll=false; - use_vscroll=false; + if (total_rows <= visible_rows && total_width <= visible_width) { + //thanks yessopie for this clever bit of logic + use_hscroll=false; + use_vscroll=false; - } else { + } else { - if (total_rows > visible_rows && total_width <= visible_width - vscroll_pixels) { - //thanks yessopie for this clever bit of logic - use_hscroll=false; - } + if (total_rows > visible_rows && total_width <= visible_width - vscroll_pixels) { + //thanks yessopie for this clever bit of logic + use_hscroll=false; + } - if (total_rows <= visible_rows - hscroll_rows && total_width > visible_width) { - //thanks yessopie for this clever bit of logic - use_vscroll=false; - } - } + if (total_rows <= visible_rows - hscroll_rows && total_width > visible_width) { + //thanks yessopie for this clever bit of logic + use_vscroll=false; + } + } - updating_scrolls=true; + updating_scrolls=true; - if (use_vscroll) { + if (use_vscroll) { - v_scroll->show(); - v_scroll->set_max(total_rows); - v_scroll->set_page(visible_rows); + v_scroll->show(); + v_scroll->set_max(total_rows); + v_scroll->set_page(visible_rows); - v_scroll->set_val(cursor.line_ofs); + v_scroll->set_val(cursor.line_ofs); - } else { - cursor.line_ofs = 0; - v_scroll->hide(); - } + } else { + cursor.line_ofs = 0; + v_scroll->hide(); + } - if (use_hscroll) { + if (use_hscroll) { - h_scroll->show(); - h_scroll->set_max(total_width); - h_scroll->set_page(visible_width); - h_scroll->set_val(cursor.x_ofs); - } else { + h_scroll->show(); + h_scroll->set_max(total_width); + h_scroll->set_page(visible_width); + h_scroll->set_val(cursor.x_ofs); + } else { - h_scroll->hide(); - } + h_scroll->hide(); + } - updating_scrolls=false; + updating_scrolls=false; } void TextEdit::_notification(int p_what) { - switch(p_what) { - case NOTIFICATION_ENTER_TREE: { + switch(p_what) { + case NOTIFICATION_ENTER_TREE: { - _update_caches(); - if (cursor_changed_dirty) - MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); - if (text_changed_dirty) - MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); + _update_caches(); + if (cursor_changed_dirty) + MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); + if (text_changed_dirty) + MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); - } break; - case NOTIFICATION_RESIZED: { + } break; + case NOTIFICATION_RESIZED: { - cache.size=get_size(); - adjust_viewport_to_cursor(); + cache.size=get_size(); + adjust_viewport_to_cursor(); - } break; - case NOTIFICATION_THEME_CHANGED: { + } break; + case NOTIFICATION_THEME_CHANGED: { - _update_caches(); - }; - case NOTIFICATION_DRAW: { + _update_caches(); + }; + case NOTIFICATION_DRAW: { - int line_number_char_count=0; + int line_number_char_count=0; - { - int lc=text.size()+1; - cache.line_number_w=0; - while(lc) { - cache.line_number_w+=1; - lc/=10; - }; + { + int lc=text.size()+1; + cache.line_number_w=0; + while(lc) { + cache.line_number_w+=1; + lc/=10; + }; - if (line_numbers) { + if (line_numbers) { - line_number_char_count=cache.line_number_w; - cache.line_number_w=(cache.line_number_w+1)*cache.font->get_char_size('0').width; - } else { - cache.line_number_w=0; - } - - - } - _update_scrollbars(); + line_number_char_count=cache.line_number_w; + cache.line_number_w=(cache.line_number_w+1)*cache.font->get_char_size('0').width; + } else { + cache.line_number_w=0; + } - RID ci = get_canvas_item(); - int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w; - int xmargin_end=cache.size.width-cache.style_normal->get_margin(MARGIN_RIGHT); - //let's do it easy for now: - cache.style_normal->draw(ci,Rect2(Point2(),cache.size)); - if (has_focus()) - cache.style_focus->draw(ci,Rect2(Point2(),cache.size)); + } + _update_scrollbars(); - int ascent=cache.font->get_ascent(); + RID ci = get_canvas_item(); + int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w; + int xmargin_end=cache.size.width-cache.style_normal->get_margin(MARGIN_RIGHT); + //let's do it easy for now: + cache.style_normal->draw(ci,Rect2(Point2(),cache.size)); + if (has_focus()) + cache.style_focus->draw(ci,Rect2(Point2(),cache.size)); - int visible_rows = get_visible_rows(); - int tab_w = cache.font->get_char_size(' ').width*tab_size; + int ascent=cache.font->get_ascent(); - Color color = cache.font_color; - int in_region=-1; + int visible_rows = get_visible_rows(); - if (syntax_coloring) { + int tab_w = cache.font->get_char_size(' ').width*tab_size; - if (custom_bg_color.a>0.01) { + Color color = cache.font_color; + int in_region=-1; - Point2i ofs = Point2i(cache.style_normal->get_offset())/2.0; - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(ofs, get_size()-cache.style_normal->get_minimum_size()+ofs),custom_bg_color); - } - //compute actual region to start (may be inside say, a comment). - //slow in very large documments :( but ok for source! + if (syntax_coloring) { - for(int i=0;i<cursor.line_ofs;i++) { + if (custom_bg_color.a>0.01) { - const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(i); + Point2i ofs = Point2i(cache.style_normal->get_offset())/2.0; + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(ofs, get_size()-cache.style_normal->get_minimum_size()+ofs),custom_bg_color); + } + //compute actual region to start (may be inside say, a comment). + //slow in very large documments :( but ok for source! - if (in_region>=0 && color_regions[in_region].line_only) { - in_region=-1; //reset regions that end at end of line - } + for(int i=0;i<cursor.line_ofs;i++) { - for( const Map<int,Text::ColorRegionInfo>::Element* E= cri_map.front();E;E=E->next() ) { + const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(i); - const Text::ColorRegionInfo &cri=E->get(); + if (in_region>=0 && color_regions[in_region].line_only) { + in_region=-1; //reset regions that end at end of line + } - if (in_region==-1) { + for( const Map<int,Text::ColorRegionInfo>::Element* E= cri_map.front();E;E=E->next() ) { - if (!cri.end) { + const Text::ColorRegionInfo &cri=E->get(); - in_region=cri.region; - } - } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise + if (in_region==-1) { - if (cri.end || color_regions[cri.region].eq) { + if (!cri.end) { - in_region=-1; - } - } - } - } - } + in_region=cri.region; + } + } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - int deregion=0; //force it to clear inrgion - Point2 cursor_pos; + if (cri.end || color_regions[cri.region].eq) { - for (int i=0;i<visible_rows;i++) { + in_region=-1; + } + } + } + } + } - int line=i+cursor.line_ofs; + int deregion=0; //force it to clear inrgion + Point2 cursor_pos; - if (line<0 || line>=(int)text.size()) - continue; + for (int i=0;i<visible_rows;i++) { + int line=i+cursor.line_ofs; + if (line<0 || line>=(int)text.size()) + continue; - const String &str=text[line]; + const String &str=text[line]; - int char_margin=xmargin_beg-cursor.x_ofs; - int char_ofs=0; - int ofs_y=i*get_row_height()+cache.line_spacing/2; - bool prev_is_char=false; - bool in_keyword=false; - Color keyword_color; + int char_margin=xmargin_beg-cursor.x_ofs; + int char_ofs=0; + int ofs_y=i*get_row_height()+cache.line_spacing/2; + bool prev_is_char=false; + bool in_keyword=false; + Color keyword_color; - if (cache.line_number_w) { - Color fcol = cache.font_color; - fcol.a*=0.4; - String fc = String::num(line+1); - while (fc.length() < line_number_char_count) { - fc="0"+fc; - } + if (cache.line_number_w) { + Color fcol = cache.font_color; + fcol.a*=0.4; + String fc = String::num(line+1); + while (fc.length() < line_number_char_count) { + fc="0"+fc; + } - cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT),ofs_y+cache.font->get_ascent()),fc,fcol); - } + cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT),ofs_y+cache.font->get_ascent()),fc,fcol); + } - const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(line); + const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(line); - if (text.is_marked(line)) { + if (text.is_marked(line)) { - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.mark_color); - } + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.mark_color); + } - if (text.is_breakpoint(line)) { + if (text.is_breakpoint(line)) { - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.breakpoint_color); - } + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.breakpoint_color); + } - if (line==cursor.line) { + if (line==cursor.line) { - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.current_line_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.current_line_color); - } - for (int j=0;j<str.length();j++) { + } + for (int j=0;j<str.length();j++) { - //look for keyword + //look for keyword - if (deregion>0) { - deregion--; - if (deregion==0) - in_region=-1; - } - if (syntax_coloring && deregion==0) { + if (deregion>0) { + deregion--; + if (deregion==0) + in_region=-1; + } + if (syntax_coloring && deregion==0) { - color = cache.font_color; //reset - //find keyword - bool is_char = _is_text_char(str[j]); - bool is_symbol=_is_symbol(str[j]); + color = cache.font_color; //reset + //find keyword + bool is_char = _is_text_char(str[j]); + bool is_symbol=_is_symbol(str[j]); - if (j==0 && in_region>=0 && color_regions[in_region].line_only) { - in_region=-1; //reset regions that end at end of line - } + if (j==0 && in_region>=0 && color_regions[in_region].line_only) { + in_region=-1; //reset regions that end at end of line + } - if (is_symbol && cri_map.has(j)) { + if (is_symbol && cri_map.has(j)) { - const Text::ColorRegionInfo &cri=cri_map[j]; + const Text::ColorRegionInfo &cri=cri_map[j]; - if (in_region==-1) { + if (in_region==-1) { - if (!cri.end) { + if (!cri.end) { - in_region=cri.region; - } - } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise + in_region=cri.region; + } + } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - if (cri.end || color_regions[cri.region].eq) { + if (cri.end || color_regions[cri.region].eq) { - deregion=color_regions[cri.region].eq?color_regions[cri.region].begin_key.length():color_regions[cri.region].end_key.length(); - } - } - } + deregion=color_regions[cri.region].eq?color_regions[cri.region].begin_key.length():color_regions[cri.region].end_key.length(); + } + } + } - if (!is_char) - in_keyword=false; + if (!is_char) + in_keyword=false; - if (in_region==-1 && !in_keyword && is_char && !prev_is_char) { + if (in_region==-1 && !in_keyword && is_char && !prev_is_char) { - int to=j; - while(_is_text_char(str[to]) && to<str.length()) - to++; + int to=j; + while(_is_text_char(str[to]) && to<str.length()) + to++; - uint32_t hash = String::hash(&str[j],to-j); - StrRange range(&str[j],to-j); + uint32_t hash = String::hash(&str[j],to-j); + StrRange range(&str[j],to-j); - const Color *col=keywords.custom_getptr(range,hash); + const Color *col=keywords.custom_getptr(range,hash); - if (col) { + if (col) { - in_keyword=true; - keyword_color=*col; - } - } + in_keyword=true; + keyword_color=*col; + } + } - if (in_region>=0) - color=color_regions[in_region].color; - else if (in_keyword) - color=keyword_color; - else if (is_symbol) - color=symbol_color; + if (in_region>=0) + color=color_regions[in_region].color; + else if (in_keyword) + color=keyword_color; + else if (is_symbol) + color=symbol_color; - prev_is_char=is_char; + prev_is_char=is_char; - } - int char_w; + } + int char_w; - //handle tabulator + //handle tabulator - if (str[j]=='\t') { - int left = char_ofs%tab_w; - if (left==0) - char_w=tab_w; - else - char_w=tab_w-char_ofs%tab_w; // is right... + if (str[j]=='\t') { + int left = char_ofs%tab_w; + if (left==0) + char_w=tab_w; + else + char_w=tab_w-char_ofs%tab_w; // is right... - } else { - char_w=cache.font->get_char_size(str[j],str[j+1]).width; - } + } else { + char_w=cache.font->get_char_size(str[j],str[j+1]).width; + } - if ( (char_ofs+char_margin)<xmargin_beg) { - char_ofs+=char_w; - continue; - } + if ( (char_ofs+char_margin)<xmargin_beg) { + char_ofs+=char_w; + continue; + } - if ( (char_ofs+char_margin+char_w)>=xmargin_end) { - if (syntax_coloring) - continue; - else - break; - } + if ( (char_ofs+char_margin+char_w)>=xmargin_end) { + if (syntax_coloring) + continue; + else + break; + } - bool in_selection = (selection.active && line>=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (line<selection.to_line || j<selection.to_column)); + bool in_selection = (selection.active && line>=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (line<selection.to_line || j<selection.to_column)); - if (in_selection) { - //inside selection! - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,get_row_height())),cache.selection_color); - } + if (in_selection) { + //inside selection! + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,get_row_height())),cache.selection_color); + } - if (str[j]>=32) - cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),str[j],str[j+1],in_selection?cache.font_selected_color:color); - else if (draw_tabs && str[j]=='\t') { - int yofs= (get_row_height() - cache.tab_icon->get_height())/2; - cache.tab_icon->draw(ci, Point2(char_ofs+char_margin,ofs_y+yofs),in_selection?cache.font_selected_color:color); - } + if (str[j]>=32) + cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),str[j],str[j+1],in_selection?cache.font_selected_color:color); + else if (draw_tabs && str[j]=='\t') { + int yofs= (get_row_height() - cache.tab_icon->get_height())/2; + cache.tab_icon->draw(ci, Point2(char_ofs+char_margin,ofs_y+yofs),in_selection?cache.font_selected_color:color); + } - if (cursor.column==j && cursor.line==line) { + if (cursor.column==j && cursor.line==line) { - cursor_pos = Point2i( char_ofs+char_margin, ofs_y ); - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); + cursor_pos = Point2i( char_ofs+char_margin, ofs_y ); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); - } - char_ofs+=char_w; + } + char_ofs+=char_w; - } + } - if (cursor.column==str.length() && cursor.line==line) { + if (cursor.column==str.length() && cursor.line==line) { - cursor_pos=Point2i( char_ofs+char_margin, ofs_y ); - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); + cursor_pos=Point2i( char_ofs+char_margin, ofs_y ); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); - } - } + } + } - if (completion_active) { - // code completion box - Ref<StyleBox> csb = get_stylebox("completion"); - Ref<StyleBox> csel = get_stylebox("completion_selected"); - int maxlines = get_constant("completion_lines"); - int cmax_width = get_constant("completion_max_width")*cache.font->get_char_size('x').x; - Color existing = get_color("completion_existing"); - int scrollw = get_constant("completion_scroll_width"); - Color scrollc = get_color("completion_scroll_color"); + if (completion_active) { + // code completion box + Ref<StyleBox> csb = get_stylebox("completion"); + Ref<StyleBox> csel = get_stylebox("completion_selected"); + int maxlines = get_constant("completion_lines"); + int cmax_width = get_constant("completion_max_width")*cache.font->get_char_size('x').x; + Color existing = get_color("completion_existing"); + int scrollw = get_constant("completion_scroll_width"); + Color scrollc = get_color("completion_scroll_color"); - int lines = MIN(completion_options.size(),maxlines); - int w=0; - int h=lines*get_row_height(); - int nofs = cache.font->get_string_size(completion_base).width; + int lines = MIN(completion_options.size(),maxlines); + int w=0; + int h=lines*get_row_height(); + int nofs = cache.font->get_string_size(completion_base).width; - if (completion_options.size() < 50) { - for(int i=0;i<completion_options.size();i++) { - int w2=MIN(cache.font->get_string_size(completion_options[i]).x,cmax_width); - if (w2>w) - w=w2; - } - } else { - w=cmax_width; - } + if (completion_options.size() < 50) { + for(int i=0;i<completion_options.size();i++) { + int w2=MIN(cache.font->get_string_size(completion_options[i]).x,cmax_width); + if (w2>w) + w=w2; + } + } else { + w=cmax_width; + } - int th = h + csb->get_minimum_size().y; - if (cursor_pos.y+get_row_height()+th > get_size().height) { - completion_rect.pos.y=cursor_pos.y-th; - } else { - completion_rect.pos.y=cursor_pos.y+get_row_height()+csb->get_offset().y; - } + int th = h + csb->get_minimum_size().y; + if (cursor_pos.y+get_row_height()+th > get_size().height) { + completion_rect.pos.y=cursor_pos.y-th; + } else { + completion_rect.pos.y=cursor_pos.y+get_row_height()+csb->get_offset().y; + } - if (cursor_pos.x-nofs+w+scrollw > get_size().width) { - completion_rect.pos.x=get_size().width-w-scrollw; - } else { - completion_rect.pos.x=cursor_pos.x-nofs; - } + if (cursor_pos.x-nofs+w+scrollw > get_size().width) { + completion_rect.pos.x=get_size().width-w-scrollw; + } else { + completion_rect.pos.x=cursor_pos.x-nofs; + } - completion_rect.size.width=w; - completion_rect.size.height=h; - if (completion_options.size()<=maxlines) - scrollw=0; + completion_rect.size.width=w; + completion_rect.size.height=h; + if (completion_options.size()<=maxlines) + scrollw=0; - draw_style_box(csb,Rect2(completion_rect.pos-csb->get_offset(),completion_rect.size+csb->get_minimum_size()+Size2(scrollw,0))); + draw_style_box(csb,Rect2(completion_rect.pos-csb->get_offset(),completion_rect.size+csb->get_minimum_size()+Size2(scrollw,0))); - int line_from = CLAMP(completion_index - lines/2, 0, completion_options.size() - lines); - draw_style_box(csel,Rect2(Point2(completion_rect.pos.x,completion_rect.pos.y+(completion_index-line_from)*get_row_height()),Size2(completion_rect.size.width,get_row_height()))); + int line_from = CLAMP(completion_index - lines/2, 0, completion_options.size() - lines); + draw_style_box(csel,Rect2(Point2(completion_rect.pos.x,completion_rect.pos.y+(completion_index-line_from)*get_row_height()),Size2(completion_rect.size.width,get_row_height()))); - draw_rect(Rect2(completion_rect.pos,Size2(nofs,completion_rect.size.height)),existing); + draw_rect(Rect2(completion_rect.pos,Size2(nofs,completion_rect.size.height)),existing); - for(int i=0;i<lines;i++) { + for(int i=0;i<lines;i++) { - int l = line_from + i; - ERR_CONTINUE( l < 0 || l>= completion_options.size()); - draw_string(cache.font,Point2(completion_rect.pos.x,completion_rect.pos.y+i*get_row_height()+cache.font->get_ascent()),completion_options[l],cache.font_color,completion_rect.size.width); - } + int l = line_from + i; + ERR_CONTINUE( l < 0 || l>= completion_options.size()); + draw_string(cache.font,Point2(completion_rect.pos.x,completion_rect.pos.y+i*get_row_height()+cache.font->get_ascent()),completion_options[l],cache.font_color,completion_rect.size.width); + } - if (scrollw) { - //draw a small scroll rectangle to show a position in the options - float r = maxlines / (float)completion_options.size(); - float o = line_from / (float)completion_options.size(); - draw_rect(Rect2(completion_rect.pos.x+completion_rect.size.width,completion_rect.pos.y+o*completion_rect.size.y,scrollw,completion_rect.size.y*r),scrollc); - } + if (scrollw) { + //draw a small scroll rectangle to show a position in the options + float r = maxlines / (float)completion_options.size(); + float o = line_from / (float)completion_options.size(); + draw_rect(Rect2(completion_rect.pos.x+completion_rect.size.width,completion_rect.pos.y+o*completion_rect.size.y,scrollw,completion_rect.size.y*r),scrollc); + } - completion_line_ofs=line_from; + completion_line_ofs=line_from; - } + } - } break; - case NOTIFICATION_FOCUS_ENTER: { + } break; + case NOTIFICATION_FOCUS_ENTER: { - if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->show_virtual_keyboard(get_text(),get_global_rect()); + if (OS::get_singleton()->has_virtual_keyboard()) + OS::get_singleton()->show_virtual_keyboard(get_text(),get_global_rect()); - } break; - case NOTIFICATION_FOCUS_EXIT: { + } break; + case NOTIFICATION_FOCUS_EXIT: { - if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->hide_virtual_keyboard(); + if (OS::get_singleton()->has_virtual_keyboard()) + OS::get_singleton()->hide_virtual_keyboard(); - } break; + } break; - } + } } void TextEdit::_consume_pair_symbol(CharType ch) { - - int cursor_position_to_move = cursor_get_column() + 1; - - CharType ch_single[2] = {ch, 0}; - CharType ch_single_pair[2] = {_get_right_pair_symbol(ch), 0}; - CharType ch_pair[3] = {ch, _get_right_pair_symbol(ch), 0}; - - if(is_selection_active()) { - - int new_column,new_line; - - _begin_compex_operation(); - _insert_text(get_selection_from_line(), get_selection_from_column(), - ch_single, - &new_line, &new_column); - - int to_col_offset = 0; - if(get_selection_from_line() == get_selection_to_line()) - to_col_offset = 1; - - _insert_text(get_selection_to_line(), - get_selection_to_column() + to_col_offset, - ch_single_pair, - &new_line,&new_column); - _end_compex_operation(); - - cursor_set_line(get_selection_to_line()); - cursor_set_column(get_selection_to_column() + to_col_offset); - - deselect(); - update(); - return; - } - - if( (ch == '\'' || ch == '"') && - cursor_get_column() > 0 && - _is_text_char(text[cursor.line][cursor_get_column() - 1]) - ) { - insert_text_at_cursor(ch_single); - cursor_set_column(cursor_position_to_move); - return; - } - - if(cursor_get_column() < text[cursor.line].length()) { - if(_is_text_char(text[cursor.line][cursor_get_column()])) { - insert_text_at_cursor(ch_single); - cursor_set_column(cursor_position_to_move); - return; - } - if( _is_pair_right_symbol(ch) && - text[cursor.line][cursor_get_column()] == ch - ) { - cursor_set_column(cursor_position_to_move); - return; - } - } - - - insert_text_at_cursor(ch_pair); - cursor_set_column(cursor_position_to_move); - return; - + + int cursor_position_to_move = cursor_get_column() + 1; + + CharType ch_single[2] = {ch, 0}; + CharType ch_single_pair[2] = {_get_right_pair_symbol(ch), 0}; + CharType ch_pair[3] = {ch, _get_right_pair_symbol(ch), 0}; + + if(is_selection_active()) { + + int new_column,new_line; + + _begin_compex_operation(); + _insert_text(get_selection_from_line(), get_selection_from_column(), + ch_single, + &new_line, &new_column); + + int to_col_offset = 0; + if(get_selection_from_line() == get_selection_to_line()) + to_col_offset = 1; + + _insert_text(get_selection_to_line(), + get_selection_to_column() + to_col_offset, + ch_single_pair, + &new_line,&new_column); + _end_compex_operation(); + + cursor_set_line(get_selection_to_line()); + cursor_set_column(get_selection_to_column() + to_col_offset); + + deselect(); + update(); + return; + } + + if( (ch == '\'' || ch == '"') && + cursor_get_column() > 0 && + _is_text_char(text[cursor.line][cursor_get_column() - 1]) + ) { + insert_text_at_cursor(ch_single); + cursor_set_column(cursor_position_to_move); + return; + } + + if(cursor_get_column() < text[cursor.line].length()) { + if(_is_text_char(text[cursor.line][cursor_get_column()])) { + insert_text_at_cursor(ch_single); + cursor_set_column(cursor_position_to_move); + return; + } + if( _is_pair_right_symbol(ch) && + text[cursor.line][cursor_get_column()] == ch + ) { + cursor_set_column(cursor_position_to_move); + return; + } + } + + + insert_text_at_cursor(ch_pair); + cursor_set_column(cursor_position_to_move); + return; + } void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column) { - - bool remove_right_symbol = false; - - if(cursor.column < text[cursor.line].length() && cursor.column > 0) { - - CharType left_char = text[cursor.line][cursor.column - 1]; - CharType right_char = text[cursor.line][cursor.column]; - - if(right_char == _get_right_pair_symbol(left_char)) { - remove_right_symbol = true; - } - - } - if(remove_right_symbol) { - _remove_text(prev_line,prev_column,cursor.line,cursor.column + 1); - } else { - _remove_text(prev_line,prev_column,cursor.line,cursor.column); - } - + + bool remove_right_symbol = false; + + if(cursor.column < text[cursor.line].length() && cursor.column > 0) { + + CharType left_char = text[cursor.line][cursor.column - 1]; + CharType right_char = text[cursor.line][cursor.column]; + + if(right_char == _get_right_pair_symbol(left_char)) { + remove_right_symbol = true; + } + + } + if(remove_right_symbol) { + _remove_text(prev_line,prev_column,cursor.line,cursor.column + 1); + } else { + _remove_text(prev_line,prev_column,cursor.line,cursor.column); + } + } void TextEdit::backspace_at_cursor() { - if (cursor.column==0 && cursor.line==0) - return; + if (cursor.column==0 && cursor.line==0) + return; - int prev_line = cursor.column?cursor.line:cursor.line-1; - int prev_column = cursor.column?(cursor.column-1):(text[cursor.line-1].length()); - if(auto_brace_completion_enabled && - cursor.column > 0 && - _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) { - _consume_backspace_for_pair_symbol(prev_line, prev_column); - } else { - _remove_text(prev_line,prev_column,cursor.line,cursor.column); - } + int prev_line = cursor.column?cursor.line:cursor.line-1; + int prev_column = cursor.column?(cursor.column-1):(text[cursor.line-1].length()); + if(auto_brace_completion_enabled && + cursor.column > 0 && + _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) { + _consume_backspace_for_pair_symbol(prev_line, prev_column); + } else { + _remove_text(prev_line,prev_column,cursor.line,cursor.column); + } - cursor_set_line(prev_line); - cursor_set_column(prev_column); + cursor_set_line(prev_line); + cursor_set_column(prev_column); } bool TextEdit::_get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const { - int row=p_mouse.y; - row-=cache.style_normal->get_margin(MARGIN_TOP); - row/=get_row_height(); + int row=p_mouse.y; + row-=cache.style_normal->get_margin(MARGIN_TOP); + row/=get_row_height(); - if (row<0 || row>=get_visible_rows()) - return false; + if (row<0 || row>=get_visible_rows()) + return false; - row+=cursor.line_ofs; - int col=0; + row+=cursor.line_ofs; + int col=0; - if (row>=text.size()) { + if (row>=text.size()) { - row=text.size()-1; - col=text[row].size(); - } else { + row=text.size()-1; + col=text[row].size(); + } else { - col=p_mouse.x-(cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w); - col+=cursor.x_ofs; - col=get_char_pos_for( col, get_line(row) ); - } + col=p_mouse.x-(cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w); + col+=cursor.x_ofs; + col=get_char_pos_for( col, get_line(row) ); + } - r_row=row; - r_col=col; - return true; + r_row=row; + r_col=col; + return true; } void TextEdit::_input_event(const InputEvent& p_input_event) { - switch(p_input_event.type) { - - case InputEvent::MOUSE_BUTTON: { + switch(p_input_event.type) { - const InputEventMouseButton &mb=p_input_event.mouse_button; + case InputEvent::MOUSE_BUTTON: { - if (completion_active && completion_rect.has_point(Point2(mb.x,mb.y))) { + const InputEventMouseButton &mb=p_input_event.mouse_button; - if (!mb.pressed) - return; + if (completion_active && completion_rect.has_point(Point2(mb.x,mb.y))) { - if (mb.button_index==BUTTON_WHEEL_UP) { - if (completion_index>0) { - completion_index--; - completion_current=completion_options[completion_index]; - update(); - } + if (!mb.pressed) + return; - } - if (mb.button_index==BUTTON_WHEEL_DOWN) { + if (mb.button_index==BUTTON_WHEEL_UP) { + if (completion_index>0) { + completion_index--; + completion_current=completion_options[completion_index]; + update(); + } - if (completion_index<completion_options.size()-1) { - completion_index++; - completion_current=completion_options[completion_index]; - update(); - } - } + } + if (mb.button_index==BUTTON_WHEEL_DOWN) { - if (mb.button_index==BUTTON_LEFT) { + if (completion_index<completion_options.size()-1) { + completion_index++; + completion_current=completion_options[completion_index]; + update(); + } + } - completion_index=CLAMP(completion_line_ofs+(mb.y-completion_rect.pos.y)/get_row_height(),0,completion_options.size()-1); + if (mb.button_index==BUTTON_LEFT) { - completion_current=completion_options[completion_index]; - update(); - if (mb.doubleclick) - _confirm_completion(); - } - return; - } else { - _cancel_completion(); - } + completion_index=CLAMP(completion_line_ofs+(mb.y-completion_rect.pos.y)/get_row_height(),0,completion_options.size()-1); - if (mb.pressed) { - if (mb.button_index==BUTTON_WHEEL_UP) { - v_scroll->set_val( v_scroll->get_val() -3 ); - } - if (mb.button_index==BUTTON_WHEEL_DOWN) { - v_scroll->set_val( v_scroll->get_val() +3 ); - } - if (mb.button_index==BUTTON_LEFT) { + completion_current=completion_options[completion_index]; + update(); + if (mb.doubleclick) + _confirm_completion(); + } + return; + } else { + _cancel_completion(); + } + + if (mb.pressed) { + if (mb.button_index==BUTTON_WHEEL_UP) { + v_scroll->set_val( v_scroll->get_val() -3 ); + } + if (mb.button_index==BUTTON_WHEEL_DOWN) { + v_scroll->set_val( v_scroll->get_val() +3 ); + } + if (mb.button_index==BUTTON_LEFT) { - int row,col; - if (!_get_mouse_pos(Point2i(mb.x,mb.y), row,col)) - return; + int row,col; + if (!_get_mouse_pos(Point2i(mb.x,mb.y), row,col)) + return; - int prev_col=cursor.column; - int prev_line=cursor.line; + int prev_col=cursor.column; + int prev_line=cursor.line; - cursor_set_line( row ); - cursor_set_column( col ); + cursor_set_line( row ); + cursor_set_column( col ); - if (mb.mod.shift && (cursor.column!=prev_col || cursor.line!=prev_line)) { + if (mb.mod.shift && (cursor.column!=prev_col || cursor.line!=prev_line)) { - selection.active=true; - selection.selecting_mode=Selection::MODE_POINTER; - selection.from_column=prev_col; - selection.from_line=prev_line; - selection.to_column=cursor.column; - selection.to_line=cursor.line; - if (selection.from_column>selection.to_column) { - SWAP(selection.from_column,selection.to_column); - SWAP(selection.from_line,selection.to_line); - } - selection.selecting_line=prev_line; - selection.selecting_column=prev_col; - update(); + selection.active=true; + selection.selecting_mode=Selection::MODE_POINTER; + selection.from_column=prev_col; + selection.from_line=prev_line; + selection.to_column=cursor.column; + selection.to_line=cursor.line; + if (selection.from_column>selection.to_column) { + SWAP(selection.from_column,selection.to_column); + SWAP(selection.from_line,selection.to_line); + } + selection.selecting_line=prev_line; + selection.selecting_column=prev_col; + update(); - } else { + } else { - //if sel active and dblick last time < something + //if sel active and dblick last time < something - //else - selection.active=false; - selection.selecting_mode=Selection::MODE_POINTER; - selection.selecting_line=row; - selection.selecting_column=col; - } + //else + selection.active=false; + selection.selecting_mode=Selection::MODE_POINTER; + selection.selecting_line=row; + selection.selecting_column=col; + } - if (!mb.doubleclick && (OS::get_singleton()->get_ticks_msec()-last_dblclk)<600) { - //tripleclick select line - select(cursor.line,0,cursor.line,text[cursor.line].length()); - last_dblclk=0; + if (!mb.doubleclick && (OS::get_singleton()->get_ticks_msec()-last_dblclk)<600) { + //tripleclick select line + select(cursor.line,0,cursor.line,text[cursor.line].length()); + last_dblclk=0; - } else if (mb.doubleclick && text[cursor.line].length()) { + } else if (mb.doubleclick && text[cursor.line].length()) { - //doubleclick select world - String s = text[cursor.line]; - int beg=CLAMP(cursor.column,0,s.length()); - int end=beg; + //doubleclick select world + String s = text[cursor.line]; + int beg=CLAMP(cursor.column,0,s.length()); + int end=beg; - if (s[beg]>32 || beg==s.length()) { + if (s[beg]>32 || beg==s.length()) { - bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this + bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this - while(beg>0 && s[beg-1]>32 && (symbol==_is_symbol(s[beg-1]))) { - beg--; - } - while(end<s.length() && s[end+1]>32 && (symbol==_is_symbol(s[end+1]))) { - end++; - } + while(beg>0 && s[beg-1]>32 && (symbol==_is_symbol(s[beg-1]))) { + beg--; + } + while(end<s.length() && s[end+1]>32 && (symbol==_is_symbol(s[end+1]))) { + end++; + } - if (end<s.length()) - end+=1; + if (end<s.length()) + end+=1; - select(cursor.line,beg,cursor.line,end); - } + select(cursor.line,beg,cursor.line,end); + } - last_dblclk = OS::get_singleton()->get_ticks_msec(); + last_dblclk = OS::get_singleton()->get_ticks_msec(); - } + } - update(); - } - } else { + update(); + } + } else { - selection.selecting_mode=Selection::MODE_NONE; + selection.selecting_mode=Selection::MODE_NONE; // notify to show soft keyboard notification(NOTIFICATION_FOCUS_ENTER); - } + } + + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_input_event.mouse_motion; + + if (mm.button_mask&BUTTON_MASK_LEFT) { - } break; - case InputEvent::MOUSE_MOTION: { + int row,col; + if (!_get_mouse_pos(Point2i(mm.x,mm.y), row,col)) + return; - const InputEventMouseMotion &mm=p_input_event.mouse_motion; + if (selection.selecting_mode==Selection::MODE_POINTER) { - if (mm.button_mask&BUTTON_MASK_LEFT) { + select(selection.selecting_line,selection.selecting_column,row,col); - int row,col; - if (!_get_mouse_pos(Point2i(mm.x,mm.y), row,col)) - return; + cursor_set_line( row ); + cursor_set_column( col ); + update(); + + } + + } + + } break; + + case InputEvent::KEY: { - if (selection.selecting_mode==Selection::MODE_POINTER) { + InputEventKey k=p_input_event.key; - select(selection.selecting_line,selection.selecting_column,row,col); + if (!k.pressed) + return; - cursor_set_line( row ); - cursor_set_column( col ); - update(); + if (completion_active) { - } + bool valid=true; + if (k.mod.command || k.mod.alt || k.mod.meta) + valid=false; + + if (valid) { + + if (k.scancode==KEY_UP) { + + if (completion_index>0) { + completion_index--; + completion_current=completion_options[completion_index]; + update(); + } + accept_event(); + return; + } - } - } break; + if (k.scancode==KEY_DOWN) { - case InputEvent::KEY: { + if (completion_index<completion_options.size()-1) { + completion_index++; + completion_current=completion_options[completion_index]; + update(); + } + accept_event(); + return; + } + + if (k.scancode==KEY_PAGEUP) { + + completion_index-=get_constant("completion_lines"); + if (completion_index<0) + completion_index=0; + completion_current=completion_options[completion_index]; + update(); + accept_event(); + return; + } + + + if (k.scancode==KEY_PAGEDOWN) { + + completion_index+=get_constant("completion_lines"); + if (completion_index>=completion_options.size()) + completion_index=completion_options.size()-1; + completion_current=completion_options[completion_index]; + update(); + accept_event(); + return; + } + + if (k.scancode==KEY_HOME) { + + completion_index=0; + completion_current=completion_options[completion_index]; + update(); + accept_event(); + return; + } + + if (k.scancode==KEY_END) { + + completion_index=completion_options.size()-1; + completion_current=completion_options[completion_index]; + update(); + accept_event(); + return; + } - InputEventKey k=p_input_event.key; - if (!k.pressed) - return; + if (k.scancode==KEY_DOWN) { - if (completion_active) { + if (completion_index<completion_options.size()-1) { + completion_index++; + completion_current=completion_options[completion_index]; + update(); + } + accept_event(); + return; + } + + if (k.scancode==KEY_RETURN || k.scancode==KEY_TAB) { + + _confirm_completion(); + accept_event(); + return; + } + + if (k.scancode==KEY_BACKSPACE) { + + backspace_at_cursor(); + _update_completion_candidates(); + accept_event(); + return; + } + + + if (k.scancode==KEY_SHIFT) { + accept_event(); + return; + } + + if (k.unicode>32) { + + if (cursor.column<text[cursor.line].length() && text[cursor.line][cursor.column]==k.unicode) { + //same char, move ahead + cursor_set_column(cursor.column+1); + + } else { + //different char, go back + const CharType chr[2] = {k.unicode, 0}; + if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { + _consume_pair_symbol(chr[0]); + } else { + _insert_text_at_cursor(chr); + } + } + + _update_completion_candidates(); + accept_event(); + + return; + } + } + + _cancel_completion(); + } + + /* TEST CONTROL FIRST!! */ + + // some remaps for duplicate functions.. + if (k.mod.command && !k.mod.shift && !k.mod.alt && !k.mod.meta && k.scancode==KEY_INSERT) { + + k.scancode=KEY_C; + } + if (!k.mod.command && k.mod.shift && !k.mod.alt && !k.mod.meta && k.scancode==KEY_INSERT) { + + k.scancode=KEY_V; + k.mod.command=true; + k.mod.shift=false; + } + + // stuff to do when selection is active.. + + if (selection.active) { + + bool clear=false; + bool unselect=false; + bool dobreak=false; + + switch(k.scancode) { + + case KEY_TAB: { + + String txt = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + String prev_txt=txt; + + if (k.mod.shift) { + + for(int i=0;i<txt.length();i++) { + if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/)) && (txt[i]=='\t' || txt[i]==' ')) { + txt.remove(i); + //i--; + } + } + } else { + + for(int i=0;i<txt.length();i++) { + + if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/))) { + txt=txt.insert(i,"\t"); + //i--; + } + } + } - bool valid=true; - if (k.mod.command || k.mod.alt || k.mod.meta) - valid=false; + if (txt!=prev_txt) { + + int sel_line=selection.from_line; + int sel_column=selection.from_column; + + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + _begin_compex_operation(); + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + _insert_text_at_cursor(txt); + _end_compex_operation(); + selection.active=true; + selection.from_column=sel_column; + selection.from_line=sel_line; + selection.to_column=cursor.column; + selection.to_line=cursor.line; + update(); + } - if (valid) { + dobreak=true; + accept_event(); - if (k.scancode==KEY_UP) { + } break; + case KEY_X: + case KEY_C: + //special keys often used with control, wait... + clear=(!k.mod.command || k.mod.shift || k.mod.alt ); + break; + case KEY_DELETE: + case KEY_BACKSPACE: + accept_event(); + clear=true; dobreak=true; + break; + case KEY_LEFT: + case KEY_RIGHT: + case KEY_UP: + case KEY_DOWN: + case KEY_PAGEUP: + case KEY_PAGEDOWN: + case KEY_HOME: + case KEY_END: + // ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys) + if (k.mod.command || k.mod.shift || k.mod.alt || k.mod.command) + break; + unselect=true; + break; + default: + if (k.unicode>=32 && !k.mod.command && !k.mod.alt && !k.mod.meta) + clear=true; + if (auto_brace_completion_enabled && _is_pair_left_symbol(k.unicode)) + clear=false; + } - if (completion_index>0) { - completion_index--; - completion_current=completion_options[completion_index]; - update(); - } - accept_event(); - return; - } + if (unselect) { + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; + update(); + } + if (clear) { + selection.active=false; + update(); + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + update(); + } + if (dobreak) + break; + } - if (k.scancode==KEY_DOWN) { + selection.selecting_test=false; - if (completion_index<completion_options.size()-1) { - completion_index++; - completion_current=completion_options[completion_index]; - update(); - } - accept_event(); - return; - } + bool scancode_handled=true; - if (k.scancode==KEY_PAGEUP) { + // special scancode test... - completion_index-=get_constant("completion_lines"); - if (completion_index<0) - completion_index=0; - completion_current=completion_options[completion_index]; - update(); - accept_event(); - return; - } + switch (k.scancode) { + case KEY_ENTER: + case KEY_RETURN: { - if (k.scancode==KEY_PAGEDOWN) { + String ins="\n"; - completion_index+=get_constant("completion_lines"); - if (completion_index>=completion_options.size()) - completion_index=completion_options.size()-1; - completion_current=completion_options[completion_index]; - update(); - accept_event(); - return; - } + //keep indentation + for(int i=0;i<text[cursor.line].length();i++) { + if (text[cursor.line][i]=='\t') + ins+="\t"; + else + break; + } - if (k.scancode==KEY_HOME) { + _insert_text_at_cursor(ins); + _push_current_op(); - completion_index=0; - completion_current=completion_options[completion_index]; - update(); - accept_event(); - return; - } + } break; + case KEY_TAB: { - if (k.scancode==KEY_END) { + if (readonly) + break; - completion_index=completion_options.size()-1; - completion_current=completion_options[completion_index]; - update(); - accept_event(); - return; - } + if (selection.active) { - if (k.scancode==KEY_DOWN) { + } else { + if (k.mod.shift) { - if (completion_index<completion_options.size()-1) { - completion_index++; - completion_current=completion_options[completion_index]; - update(); - } - accept_event(); - return; - } + int cc = cursor.column; + if (cc>0 && cc<=text[cursor.line].length() && text[cursor.line][cursor.column-1]=='\t') { + //simple unindent - if (k.scancode==KEY_RETURN || k.scancode==KEY_TAB) { + backspace_at_cursor(); + } + } else { + //simple indent + _insert_text_at_cursor("\t"); + } + } - _confirm_completion(); - accept_event(); - return; - } + } break; + case KEY_BACKSPACE: { + if (readonly) + break; + backspace_at_cursor(); - if (k.scancode==KEY_BACKSPACE) { + } break; + case KEY_LEFT: { - backspace_at_cursor(); - _update_completion_candidates(); - accept_event(); - return; - } - - - if (k.scancode==KEY_SHIFT) { - accept_event(); - return; - } - - if (k.unicode>32) { - - if (cursor.column<text[cursor.line].length() && text[cursor.line][cursor.column]==k.unicode) { - //same char, move ahead - cursor_set_column(cursor.column+1); - - } else { - //different char, go back - const CharType chr[2] = {k.unicode, 0}; - if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { - _consume_pair_symbol(chr[0]); - } else { - _insert_text_at_cursor(chr); - } - } - - _update_completion_candidates(); - accept_event(); - - return; - } - } - - _cancel_completion(); - } - - /* TEST CONTROL FIRST!! */ - - // some remaps for duplicate functions.. - if (k.mod.command && !k.mod.shift && !k.mod.alt && !k.mod.meta && k.scancode==KEY_INSERT) { - - k.scancode=KEY_C; - } - if (!k.mod.command && k.mod.shift && !k.mod.alt && !k.mod.meta && k.scancode==KEY_INSERT) { - - k.scancode=KEY_V; - k.mod.command=true; - k.mod.shift=false; - } - - // stuff to do when selection is active.. - - if (selection.active) { - - bool clear=false; - bool unselect=false; - bool dobreak=false; - - switch(k.scancode) { - - case KEY_TAB: { - - String txt = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - String prev_txt=txt; - - if (k.mod.shift) { - - for(int i=0;i<txt.length();i++) { - if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/)) && (txt[i]=='\t' || txt[i]==' ')) { - txt.remove(i); - //i--; - } - } - } else { - - for(int i=0;i<txt.length();i++) { - - if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/))) { - txt=txt.insert(i,"\t"); - //i--; - } - } - } - - if (txt!=prev_txt) { - - int sel_line=selection.from_line; - int sel_column=selection.from_column; - - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - _begin_compex_operation(); - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - _insert_text_at_cursor(txt); - _end_compex_operation(); - selection.active=true; - selection.from_column=sel_column; - selection.from_line=sel_line; - selection.to_column=cursor.column; - selection.to_line=cursor.line; - update(); - } - - dobreak=true; - accept_event(); - - } break; - case KEY_X: - case KEY_C: - //special keys often used with control, wait... - clear=(!k.mod.command || k.mod.shift || k.mod.alt ); - break; - case KEY_DELETE: - case KEY_BACKSPACE: - accept_event(); - clear=true; dobreak=true; - break; - case KEY_LEFT: - case KEY_RIGHT: - case KEY_UP: - case KEY_DOWN: - case KEY_PAGEUP: - case KEY_PAGEDOWN: - case KEY_HOME: - case KEY_END: - if (k.mod.shift) // if selecting ignore - break; - unselect=true; - break; - default: - if (k.unicode>=32 && !k.mod.command && !k.mod.alt && !k.mod.meta) - clear=true; - if (auto_brace_completion_enabled && _is_pair_left_symbol(k.unicode)) - clear=false; - } - - if (unselect) { - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; - update(); - } - if (clear) { - - selection.active=false; - update(); - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - update(); - } - if (dobreak) - break; - } - - selection.selecting_test=false; - - bool scancode_handled=true; - - // special scancode test... - - switch (k.scancode) { - - case KEY_ENTER: - case KEY_RETURN: { - - String ins="\n"; - - //keep indentation - for(int i=0;i<text[cursor.line].length();i++) { - if (text[cursor.line][i]=='\t') - ins+="\t"; - else - break; - } - - _insert_text_at_cursor(ins); - _push_current_op(); - - } break; - case KEY_TAB: { - - if (readonly) - break; - - if (selection.active) { - - - } else { - if (k.mod.shift) { - - int cc = cursor.column; - if (cc>0 && cc<=text[cursor.line].length() && text[cursor.line][cursor.column-1]=='\t') { - //simple unindent - - backspace_at_cursor(); - } - } else { - //simple indent - _insert_text_at_cursor("\t"); - } - } - - } break; - case KEY_BACKSPACE: { - if (readonly) - break; - backspace_at_cursor(); - - } break; - case KEY_LEFT: { - - if (k.mod.shift) - _pre_shift_selection(); + if (k.mod.shift) + _pre_shift_selection(); #ifdef APPLE_STYLE_KEYS - if (k.mod.command) { - cursor_set_column(0); - } else if (k.mod.alt) { + if (k.mod.command) { + cursor_set_column(0); + } else if (k.mod.alt) { #else - if (k.mod.command) { + if (k.mod.alt) { + scancode_handled=false; + break; + } else if (k.mod.command) { #endif - bool prev_char=false; - int cc=cursor.column; - while (cc>0) { + bool prev_char=false; + int cc=cursor.column; + while (cc>0) { - bool ischar=_is_text_char(text[cursor.line][cc-1]); + bool ischar=_is_text_char(text[cursor.line][cc-1]); - if (prev_char && !ischar) - break; + if (prev_char && !ischar) + break; - prev_char=ischar; - cc--; + prev_char=ischar; + cc--; - } + } - cursor_set_column(cc); + cursor_set_column(cc); } else if (cursor.column==0) { - if (cursor.line>0) { - cursor_set_line(cursor.line-1); - cursor_set_column(text[cursor.line].length()); - } + if (cursor.line>0) { + cursor_set_line(cursor.line-1); + cursor_set_column(text[cursor.line].length()); + } } else { - cursor_set_column(cursor_get_column()-1); - } + cursor_set_column(cursor_get_column()-1); + } - if (k.mod.shift) - _post_shift_selection(); + if (k.mod.shift) + _post_shift_selection(); - } break; - case KEY_RIGHT: { + } break; + case KEY_RIGHT: { - if (k.mod.shift) - _pre_shift_selection(); + if (k.mod.shift) + _pre_shift_selection(); #ifdef APPLE_STYLE_KEYS - if (k.mod.command) { - cursor_set_column(text[cursor.line].length()); - } else if (k.mod.alt) { + if (k.mod.command) { + cursor_set_column(text[cursor.line].length()); + } else if (k.mod.alt) { #else - if (k.mod.command) { + if (k.mod.alt) { + scancode_handled=false; + break; + } else if (k.mod.command) { #endif - bool prev_char=false; - int cc=cursor.column; - while (cc<text[cursor.line].length()) { + bool prev_char=false; + int cc=cursor.column; + while (cc<text[cursor.line].length()) { - bool ischar=_is_text_char(text[cursor.line][cc]); + bool ischar=_is_text_char(text[cursor.line][cc]); - if (prev_char && !ischar) - break; - prev_char=ischar; - cc++; - } + if (prev_char && !ischar) + break; + prev_char=ischar; + cc++; + } - cursor_set_column(cc); + cursor_set_column(cc); } else if (cursor.column==text[cursor.line].length()) { - if (cursor.line<text.size()-1) { - cursor_set_line(cursor.line+1); - cursor_set_column(0); - } + if (cursor.line<text.size()-1) { + cursor_set_line(cursor.line+1); + cursor_set_column(0); + } } else { - cursor_set_column(cursor_get_column()+1); - } - - if (k.mod.shift) - _post_shift_selection(); + cursor_set_column(cursor_get_column()+1); + } - } break; - case KEY_UP: { + if (k.mod.shift) + _post_shift_selection(); - if (k.mod.shift) - _pre_shift_selection(); + } break; + case KEY_UP: { + if (k.mod.shift) + _pre_shift_selection(); + if (k.mod.alt) { + scancode_handled=false; + break; + } #ifdef APPLE_STYLE_KEYS - if (k.mod.command) - cursor_set_line(0); - else + if (k.mod.command) + cursor_set_line(0); + else #endif - cursor_set_line(cursor_get_line()-1); - - if (k.mod.shift) - _post_shift_selection(); + cursor_set_line(cursor_get_line()-1); - } break; - case KEY_DOWN: { + if (k.mod.shift) + _post_shift_selection(); - if (k.mod.shift) - _pre_shift_selection(); + } break; + case KEY_DOWN: { + if (k.mod.shift) + _pre_shift_selection(); + if (k.mod.alt) { + scancode_handled=false; + break; + } #ifdef APPLE_STYLE_KEYS - if (k.mod.command) - cursor_set_line(text.size()-1); - else + if (k.mod.command) + cursor_set_line(text.size()-1); + else #endif - cursor_set_line(cursor_get_line()+1); + cursor_set_line(cursor_get_line()+1); - if (k.mod.shift) - _post_shift_selection(); + if (k.mod.shift) + _post_shift_selection(); - } break; + } break; - case KEY_DELETE: { + case KEY_DELETE: { - if (readonly) - break; - int curline_len = text[cursor.line].length(); + if (readonly) + break; + int curline_len = text[cursor.line].length(); - if (cursor.line==text.size()-1 && cursor.column==curline_len) - break; //nothing to do + if (cursor.line==text.size()-1 && cursor.column==curline_len) + break; //nothing to do - int next_line = cursor.column<curline_len?cursor.line:cursor.line+1; - int next_column = cursor.column<curline_len?(cursor.column+1):0; - _remove_text(cursor.line,cursor.column,next_line,next_column); - update(); - } break; + int next_line = cursor.column<curline_len?cursor.line:cursor.line+1; + int next_column = cursor.column<curline_len?(cursor.column+1):0; + _remove_text(cursor.line,cursor.column,next_line,next_column); + update(); + } break; #ifdef APPLE_STYLE_KEYS - case KEY_HOME: { + case KEY_HOME: { - if (k.mod.shift) - _pre_shift_selection(); + if (k.mod.shift) + _pre_shift_selection(); - cursor_set_line(0); + cursor_set_line(0); - if (k.mod.shift) - _post_shift_selection(); + if (k.mod.shift) + _post_shift_selection(); - } break; - case KEY_END: { + } break; + case KEY_END: { - if (k.mod.shift) - _pre_shift_selection(); + if (k.mod.shift) + _pre_shift_selection(); - cursor_set_line(text.size()-1); + cursor_set_line(text.size()-1); - if (k.mod.shift) - _post_shift_selection(); + if (k.mod.shift) + _post_shift_selection(); - } break; + } break; #else - case KEY_HOME: { - - - if (k.mod.shift) - _pre_shift_selection(); - - // compute whitespace symbols seq length - int current_line_whitespace_len = 0; - while(current_line_whitespace_len < text[cursor.line].length()) { - CharType c = text[cursor.line][current_line_whitespace_len]; - if(c != '\t' && c != ' ') - break; - current_line_whitespace_len++; - } - - if(cursor_get_column() == current_line_whitespace_len) - cursor_set_column(0); - else - cursor_set_column(current_line_whitespace_len); - - if (k.mod.command) - cursor_set_line(0); - - if (k.mod.shift) - _post_shift_selection(); - - } break; - case KEY_END: { - - if (k.mod.shift) - _pre_shift_selection(); - - if (k.mod.command) - cursor_set_line(text.size()-1); - cursor_set_column(text[cursor.line].length()); - - if (k.mod.shift) - _post_shift_selection(); - - } break; + case KEY_HOME: { + + + if (k.mod.shift) + _pre_shift_selection(); + + // compute whitespace symbols seq length + int current_line_whitespace_len = 0; + while(current_line_whitespace_len < text[cursor.line].length()) { + CharType c = text[cursor.line][current_line_whitespace_len]; + if(c != '\t' && c != ' ') + break; + current_line_whitespace_len++; + } + + if(cursor_get_column() == current_line_whitespace_len) + cursor_set_column(0); + else + cursor_set_column(current_line_whitespace_len); + + if (k.mod.command) + cursor_set_line(0); + + if (k.mod.shift) + _post_shift_selection(); + + } break; + case KEY_END: { + + if (k.mod.shift) + _pre_shift_selection(); + + if (k.mod.command) + cursor_set_line(text.size()-1); + cursor_set_column(text[cursor.line].length()); + + if (k.mod.shift) + _post_shift_selection(); + + } break; #endif - case KEY_PAGEUP: { + case KEY_PAGEUP: { - if (k.mod.shift) - _pre_shift_selection(); + if (k.mod.shift) + _pre_shift_selection(); - cursor_set_line(cursor_get_line()-get_visible_rows()); + cursor_set_line(cursor_get_line()-get_visible_rows()); - if (k.mod.shift) - _post_shift_selection(); + if (k.mod.shift) + _post_shift_selection(); - } break; - case KEY_PAGEDOWN: { + } break; + case KEY_PAGEDOWN: { - if (k.mod.shift) - _pre_shift_selection(); + if (k.mod.shift) + _pre_shift_selection(); - cursor_set_line(cursor_get_line()+get_visible_rows()); + cursor_set_line(cursor_get_line()+get_visible_rows()); - if (k.mod.shift) - _post_shift_selection(); + if (k.mod.shift) + _post_shift_selection(); - } break; - case KEY_A: { + } break; + case KEY_A: { - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } + if (!k.mod.command || k.mod.shift || k.mod.alt) { + scancode_handled=false; + break; + } - if (text.size()==1 && text[0].length()==0) - break; - selection.active=true; - selection.from_line=0; - selection.from_column=0; - selection.to_line=text.size()-1; - selection.to_column=text[selection.to_line].size(); - selection.selecting_mode=Selection::MODE_NONE; - update(); + if (text.size()==1 && text[0].length()==0) + break; + selection.active=true; + selection.from_line=0; + selection.from_column=0; + selection.to_line=text.size()-1; + selection.to_column=text[selection.to_line].size(); + selection.selecting_mode=Selection::MODE_NONE; + update(); - } break; - case KEY_X: { + } break; + case KEY_X: { - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } + if (!k.mod.command || k.mod.shift || k.mod.alt) { + scancode_handled=false; + break; + } if (!selection.active){ @@ -1623,13 +1634,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { cut_copy_line = false; } - } break; - case KEY_C: { + } break; + case KEY_C: { - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } + if (!k.mod.command || k.mod.shift || k.mod.alt) { + scancode_handled=false; + break; + } if (!selection.active){ String clipboard = _base_get_text(cursor.line,0,cursor.line,text[cursor.line].length()); @@ -1641,35 +1652,35 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { OS::get_singleton()->set_clipboard(clipboard); cut_copy_line = false; } - } break; - case KEY_Z: { - - if (!k.mod.command) { - scancode_handled=false; - break; - } - - if (k.mod.shift) - redo(); - else - undo(); - } break; - case KEY_V: { - - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } - - String clipboard = OS::get_singleton()->get_clipboard(); - - if (selection.active) { - selection.active=false; - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - - } + } break; + case KEY_Z: { + + if (!k.mod.command) { + scancode_handled=false; + break; + } + + if (k.mod.shift) + redo(); + else + undo(); + } break; + case KEY_V: { + + if (!k.mod.command || k.mod.shift || k.mod.alt) { + scancode_handled=false; + break; + } + + String clipboard = OS::get_singleton()->get_clipboard(); + + if (selection.active) { + selection.active=false; + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + + } else if (cut_copy_line) { cursor_set_column(0); @@ -1677,20 +1688,20 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { clipboard += ins; } - _insert_text_at_cursor(clipboard); + _insert_text_at_cursor(clipboard); - update(); - } break; - case KEY_SPACE: { - if (completion_enabled && k.mod.command) { + update(); + } break; + case KEY_SPACE: { + if (completion_enabled && k.mod.command) { - query_code_comple(); - scancode_handled=true; - } else { - scancode_handled=false; - } + query_code_comple(); + scancode_handled=true; + } else { + scancode_handled=false; + } - } break; + } break; case KEY_K:{ if (!k.mod.command || k.mod.shift || k.mod.alt) { @@ -1741,57 +1752,57 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { scancode_handled=false; } break; - } + } - if (scancode_handled) - accept_event(); + if (scancode_handled) + accept_event(); - if (!scancode_handled && !k.mod.command && !k.mod.alt) { + if (!scancode_handled && !k.mod.command && !k.mod.alt) { - if (k.unicode>=32) { + if (k.unicode>=32) { - if (readonly) - break; + if (readonly) + break; - accept_event(); - } else { + accept_event(); + } else { - break; - } - } - - if (!scancode_handled && !k.mod.command && !k.mod.alt) { - - if (k.unicode>=32) { + break; + } + } + + if (!scancode_handled && !k.mod.command && !k.mod.alt) { - if (readonly) - break; - - const CharType chr[2] = {k.unicode, 0}; - - if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { - _consume_pair_symbol(chr[0]); - } else { - _insert_text_at_cursor(chr); - } - - accept_event(); - } else { + if (k.unicode>=32) { - break; - } - } - + if (readonly) + break; - if (!selection.selecting_test) { + const CharType chr[2] = {k.unicode, 0}; - selection.selecting_mode=Selection::MODE_NONE; - } + if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { + _consume_pair_symbol(chr[0]); + } else { + _insert_text_at_cursor(chr); + } + + accept_event(); + } else { + + break; + } + } - return; - } break; - } + if (!selection.selecting_test) { + + selection.selecting_mode=Selection::MODE_NONE; + } + + return; + } break; + + } } @@ -1799,253 +1810,249 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { void TextEdit::_pre_shift_selection() { - if (!selection.active || selection.selecting_mode!=Selection::MODE_SHIFT) { + if (!selection.active || selection.selecting_mode!=Selection::MODE_SHIFT) { - selection.selecting_line=cursor.line; - selection.selecting_column=cursor.column; - selection.active=true; - selection.selecting_mode=Selection::MODE_SHIFT; - } + selection.selecting_line=cursor.line; + selection.selecting_column=cursor.column; + selection.active=true; + selection.selecting_mode=Selection::MODE_SHIFT; + } } void TextEdit::_post_shift_selection() { - if (selection.active && selection.selecting_mode==Selection::MODE_SHIFT) { + if (selection.active && selection.selecting_mode==Selection::MODE_SHIFT) { - select(selection.selecting_line,selection.selecting_column,cursor.line,cursor.column); - update(); - } + select(selection.selecting_line,selection.selecting_column,cursor.line,cursor.column); + update(); + } - selection.selecting_test=true; + selection.selecting_test=true; } /**** TEXT EDIT CORE API ****/ void TextEdit::_base_insert_text(int p_line, int p_char,const String& p_text,int &r_end_line,int &r_end_column) { - //save for undo... - ERR_FAIL_INDEX(p_line,text.size()); - ERR_FAIL_COND(p_char<0); + //save for undo... + ERR_FAIL_INDEX(p_line,text.size()); + ERR_FAIL_COND(p_char<0); - /* STEP 1 add spaces if the char is greater than the end of the line */ - while(p_char>text[p_line].length()) { + /* STEP 1 add spaces if the char is greater than the end of the line */ + while(p_char>text[p_line].length()) { - text.set(p_line,text[p_line]+String::chr(' ')); - } + text.set(p_line,text[p_line]+String::chr(' ')); + } - /* STEP 2 separate dest string in pre and post text */ + /* STEP 2 separate dest string in pre and post text */ - String preinsert_text = text[p_line].substr(0,p_char); - String postinsert_text = text[p_line].substr(p_char,text[p_line].size()); + String preinsert_text = text[p_line].substr(0,p_char); + String postinsert_text = text[p_line].substr(p_char,text[p_line].size()); - /* STEP 3 remove \r from source text and separate in substrings */ + /* STEP 3 remove \r from source text and separate in substrings */ - //buh bye \r and split - Vector<String> substrings = p_text.replace("\r","").split("\n"); + //buh bye \r and split + Vector<String> substrings = p_text.replace("\r","").split("\n"); - for(int i=0;i<substrings.size();i++) { - //insert the substrings + for(int i=0;i<substrings.size();i++) { + //insert the substrings - if (i==0) { + if (i==0) { - text.set(p_line,preinsert_text+substrings[i]); - } else { + text.set(p_line,preinsert_text+substrings[i]); + } else { - text.insert(p_line+i,substrings[i]); - } + text.insert(p_line+i,substrings[i]); + } - if (i==substrings.size()-1){ + if (i==substrings.size()-1){ - text.set(p_line+i,text[p_line+i]+postinsert_text); - } - } + text.set(p_line+i,text[p_line+i]+postinsert_text); + } + } - r_end_line=p_line+substrings.size()-1; - r_end_column=text[r_end_line].length()-postinsert_text.length(); + r_end_line=p_line+substrings.size()-1; + r_end_column=text[r_end_line].length()-postinsert_text.length(); - if (!text_changed_dirty && !setting_text) { - - if (is_inside_tree()) - MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); - text_changed_dirty=true; - } + if (!text_changed_dirty && !setting_text) { + if (is_inside_tree()) + MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); + text_changed_dirty=true; + } } String TextEdit::_base_get_text(int p_from_line, int p_from_column,int p_to_line,int p_to_column) const { - ERR_FAIL_INDEX_V(p_from_line,text.size(),String()); - ERR_FAIL_INDEX_V(p_from_column,text[p_from_line].length()+1,String()); - ERR_FAIL_INDEX_V(p_to_line,text.size(),String()); - ERR_FAIL_INDEX_V(p_to_column,text[p_to_line].length()+1,String()); - ERR_FAIL_COND_V(p_to_line < p_from_line ,String()); // from > to - ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column<p_from_column,String()); // from > to + ERR_FAIL_INDEX_V(p_from_line,text.size(),String()); + ERR_FAIL_INDEX_V(p_from_column,text[p_from_line].length()+1,String()); + ERR_FAIL_INDEX_V(p_to_line,text.size(),String()); + ERR_FAIL_INDEX_V(p_to_column,text[p_to_line].length()+1,String()); + ERR_FAIL_COND_V(p_to_line < p_from_line ,String()); // from > to + ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column<p_from_column,String()); // from > to - String ret; + String ret; - for(int i=p_from_line;i<=p_to_line;i++) { + for(int i=p_from_line;i<=p_to_line;i++) { - int begin = (i==p_from_line)?p_from_column:0; - int end = (i==p_to_line)?p_to_column:text[i].length(); + int begin = (i==p_from_line)?p_from_column:0; + int end = (i==p_to_line)?p_to_column:text[i].length(); - if (i>p_from_line) - ret+="\n"; - ret+=text[i].substr(begin,end-begin); - } + if (i>p_from_line) + ret+="\n"; + ret+=text[i].substr(begin,end-begin); + } - return ret; + return ret; } void TextEdit::_base_remove_text(int p_from_line, int p_from_column,int p_to_line,int p_to_column) { - ERR_FAIL_INDEX(p_from_line,text.size()); - ERR_FAIL_INDEX(p_from_column,text[p_from_line].length()+1); - ERR_FAIL_INDEX(p_to_line,text.size()); - ERR_FAIL_INDEX(p_to_column,text[p_to_line].length()+1); - ERR_FAIL_COND(p_to_line < p_from_line ); // from > to - ERR_FAIL_COND(p_to_line == p_from_line && p_to_column<p_from_column); // from > to - + ERR_FAIL_INDEX(p_from_line,text.size()); + ERR_FAIL_INDEX(p_from_column,text[p_from_line].length()+1); + ERR_FAIL_INDEX(p_to_line,text.size()); + ERR_FAIL_INDEX(p_to_column,text[p_to_line].length()+1); + ERR_FAIL_COND(p_to_line < p_from_line ); // from > to + ERR_FAIL_COND(p_to_line == p_from_line && p_to_column<p_from_column); // from > to - String pre_text = text[p_from_line].substr(0,p_from_column); - String post_text = text[p_to_line].substr(p_to_column,text[p_to_line].length()); - for(int i=p_from_line;i<p_to_line;i++) { + String pre_text = text[p_from_line].substr(0,p_from_column); + String post_text = text[p_to_line].substr(p_to_column,text[p_to_line].length()); - text.remove(p_from_line+1); - } + for(int i=p_from_line;i<p_to_line;i++) { - text.set(p_from_line,pre_text+post_text); + text.remove(p_from_line+1); + } - if (!text_changed_dirty && !setting_text) { + text.set(p_from_line,pre_text+post_text); - if (is_inside_tree()) - MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); - text_changed_dirty=true; - } + if (!text_changed_dirty && !setting_text) { + if (is_inside_tree()) + MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); + text_changed_dirty=true; + } } - - void TextEdit::_insert_text(int p_line, int p_char,const String& p_text,int *r_end_line,int *r_end_column) { - if (!setting_text) - idle_detect->start(); - - if (undo_enabled) { - _clear_redo(); - } - - int retline,retchar; - _base_insert_text(p_line,p_char,p_text,retline,retchar); - if (r_end_line) - *r_end_line=retline; - if (r_end_column) - *r_end_column=retchar; - - if (!undo_enabled) - return; - - /* UNDO!! */ - TextOperation op; - op.type=TextOperation::TYPE_INSERT; - op.from_line=p_line; - op.from_column=p_char; - op.to_line=retline; - op.to_column=retchar; - op.text=p_text; - op.version=++version; - op.chain_forward=false; - op.chain_backward=false; - - //see if it shold just be set as current op - if (current_op.type!=op.type) { - _push_current_op(); - current_op=op; - - return; //set as current op, return - } - //see if it can be merged - if (current_op.to_line!=p_line || current_op.to_column!=p_char) { - _push_current_op(); - current_op=op; - return; //set as current op, return - } - //merge current op - - current_op.text+=p_text; - current_op.to_column=retchar; - current_op.to_line=retline; - current_op.version=op.version; + if (!setting_text) + idle_detect->start(); + + if (undo_enabled) { + _clear_redo(); + } + + int retline,retchar; + _base_insert_text(p_line,p_char,p_text,retline,retchar); + if (r_end_line) + *r_end_line=retline; + if (r_end_column) + *r_end_column=retchar; + + if (!undo_enabled) + return; + + /* UNDO!! */ + TextOperation op; + op.type=TextOperation::TYPE_INSERT; + op.from_line=p_line; + op.from_column=p_char; + op.to_line=retline; + op.to_column=retchar; + op.text=p_text; + op.version=++version; + op.chain_forward=false; + op.chain_backward=false; + + //see if it shold just be set as current op + if (current_op.type!=op.type) { + _push_current_op(); + current_op=op; + + return; //set as current op, return + } + //see if it can be merged + if (current_op.to_line!=p_line || current_op.to_column!=p_char) { + _push_current_op(); + current_op=op; + return; //set as current op, return + } + //merge current op + + current_op.text+=p_text; + current_op.to_column=retchar; + current_op.to_line=retline; + current_op.version=op.version; } void TextEdit::_remove_text(int p_from_line, int p_from_column,int p_to_line,int p_to_column) { - if (!setting_text) - idle_detect->start(); - - String text; - if (undo_enabled) { - _clear_redo(); - text=_base_get_text(p_from_line,p_from_column,p_to_line,p_to_column); - } - - _base_remove_text(p_from_line,p_from_column,p_to_line,p_to_column); - - if (!undo_enabled) - return; - - /* UNDO!! */ - TextOperation op; - op.type=TextOperation::TYPE_REMOVE; - op.from_line=p_from_line; - op.from_column=p_from_column; - op.to_line=p_to_line; - op.to_column=p_to_column; - op.text=text; - op.version=++version; - op.chain_forward=false; - op.chain_backward=false; - - //see if it shold just be set as current op - if (current_op.type!=op.type) { - _push_current_op(); - current_op=op; - return; //set as current op, return - } - //see if it can be merged - if (current_op.from_line==p_to_line && current_op.from_column==p_to_column) { - //basckace or similar - current_op.text=text+current_op.text; - current_op.from_line=p_from_line; - current_op.from_column=p_from_column; - return; //update current op - } - if (current_op.from_line==p_from_line && current_op.from_column==p_from_column) { - - //current_op.text=text+current_op.text; - //current_op.from_line=p_from_line; - //current_op.from_column=p_from_column; - //return; //update current op - } - - _push_current_op(); - current_op=op; + if (!setting_text) + idle_detect->start(); + + String text; + if (undo_enabled) { + _clear_redo(); + text=_base_get_text(p_from_line,p_from_column,p_to_line,p_to_column); + } + + _base_remove_text(p_from_line,p_from_column,p_to_line,p_to_column); + + if (!undo_enabled) + return; + + /* UNDO!! */ + TextOperation op; + op.type=TextOperation::TYPE_REMOVE; + op.from_line=p_from_line; + op.from_column=p_from_column; + op.to_line=p_to_line; + op.to_column=p_to_column; + op.text=text; + op.version=++version; + op.chain_forward=false; + op.chain_backward=false; + + //see if it shold just be set as current op + if (current_op.type!=op.type) { + _push_current_op(); + current_op=op; + return; //set as current op, return + } + //see if it can be merged + if (current_op.from_line==p_to_line && current_op.from_column==p_to_column) { + //basckace or similar + current_op.text=text+current_op.text; + current_op.from_line=p_from_line; + current_op.from_column=p_from_column; + return; //update current op + } + if (current_op.from_line==p_from_line && current_op.from_column==p_from_column) { + + //current_op.text=text+current_op.text; + //current_op.from_line=p_from_line; + //current_op.from_column=p_from_column; + //return; //update current op + } + + _push_current_op(); + current_op=op; } void TextEdit::_insert_text_at_cursor(const String& p_text) { - int new_column,new_line; - _insert_text(cursor.line,cursor.column,p_text,&new_line,&new_column); - cursor_set_line(new_line); - cursor_set_column(new_column); + int new_column,new_line; + _insert_text(cursor.line,cursor.column,p_text,&new_line,&new_column); + cursor_set_line(new_line); + cursor_set_column(new_column); - update(); + update(); } @@ -2053,66 +2060,66 @@ void TextEdit::_insert_text_at_cursor(const String& p_text) { int TextEdit::get_char_count() { - int totalsize=0; + int totalsize=0; - for (int i=0;i<text.size();i++) { + for (int i=0;i<text.size();i++) { - if (i>0) - totalsize++; // incliude \n - totalsize+=text[i].length(); - } + if (i>0) + totalsize++; // incliude \n + totalsize+=text[i].length(); + } - return totalsize; // omit last \n + return totalsize; // omit last \n } Size2 TextEdit::get_minimum_size() { - return cache.style_normal->get_minimum_size(); + return cache.style_normal->get_minimum_size(); } int TextEdit::get_visible_rows() const { - int total=cache.size.height; - total-=cache.style_normal->get_minimum_size().height; - total/=get_row_height(); - return total; + int total=cache.size.height; + total-=cache.style_normal->get_minimum_size().height; + total/=get_row_height(); + return total; } void TextEdit::adjust_viewport_to_cursor() { - if (cursor.line_ofs>cursor.line) - cursor.line_ofs=cursor.line; + if (cursor.line_ofs>cursor.line) + cursor.line_ofs=cursor.line; - int visible_width=cache.size.width-cache.style_normal->get_minimum_size().width-cache.line_number_w; - if (v_scroll->is_visible()) - visible_width-=v_scroll->get_combined_minimum_size().width; - visible_width-=20; // give it a little more space + int visible_width=cache.size.width-cache.style_normal->get_minimum_size().width-cache.line_number_w; + if (v_scroll->is_visible()) + visible_width-=v_scroll->get_combined_minimum_size().width; + visible_width-=20; // give it a little more space - //printf("rowofs %i, visrows %i, cursor.line %i\n",cursor.line_ofs,get_visible_rows(),cursor.line); + //printf("rowofs %i, visrows %i, cursor.line %i\n",cursor.line_ofs,get_visible_rows(),cursor.line); - int visible_rows = get_visible_rows(); - if (h_scroll->is_visible()) - visible_rows-=((h_scroll->get_combined_minimum_size().height-1)/get_row_height()); + int visible_rows = get_visible_rows(); + if (h_scroll->is_visible()) + visible_rows-=((h_scroll->get_combined_minimum_size().height-1)/get_row_height()); - if (cursor.line>=(cursor.line_ofs+visible_rows)) - cursor.line_ofs=cursor.line-visible_rows+1; - if (cursor.line<cursor.line_ofs) - cursor.line_ofs=cursor.line; + if (cursor.line>=(cursor.line_ofs+visible_rows)) + cursor.line_ofs=cursor.line-visible_rows+1; + if (cursor.line<cursor.line_ofs) + cursor.line_ofs=cursor.line; - int cursor_x = get_column_x_offset( cursor.column, text[cursor.line] ); + int cursor_x = get_column_x_offset( cursor.column, text[cursor.line] ); - if (cursor_x>(cursor.x_ofs+visible_width)) - cursor.x_ofs=cursor_x-visible_width+1; + if (cursor_x>(cursor.x_ofs+visible_width)) + cursor.x_ofs=cursor_x-visible_width+1; - if (cursor_x < cursor.x_ofs) - cursor.x_ofs=cursor_x; + if (cursor_x < cursor.x_ofs) + cursor.x_ofs=cursor_x; - update(); + update(); /* - get_range()->set_max(text.size()); + get_range()->set_max(text.size()); - get_range()->set_page(get_visible_rows()); + get_range()->set_page(get_visible_rows()); - get_range()->set((int)cursor.line_ofs); + get_range()->set((int)cursor.line_ofs); */ @@ -2120,82 +2127,80 @@ void TextEdit::adjust_viewport_to_cursor() { void TextEdit::cursor_set_column(int p_col) { - if (p_col<0) - p_col=0; - - cursor.column=p_col; - if (cursor.column > get_line( cursor.line ).length()) - cursor.column=get_line( cursor.line ).length(); + if (p_col<0) + p_col=0; - cursor.last_fit_x=get_column_x_offset(cursor.column,get_line(cursor.line)); + cursor.column=p_col; + if (cursor.column > get_line( cursor.line ).length()) + cursor.column=get_line( cursor.line ).length(); - adjust_viewport_to_cursor(); + cursor.last_fit_x=get_column_x_offset(cursor.column,get_line(cursor.line)); - if (!cursor_changed_dirty) { + adjust_viewport_to_cursor(); - if (is_inside_tree()) - MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); - cursor_changed_dirty=true; - } + if (!cursor_changed_dirty) { + if (is_inside_tree()) + MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); + cursor_changed_dirty=true; + } } void TextEdit::cursor_set_line(int p_row) { - if (setting_row) - return; + if (setting_row) + return; - setting_row=true; - if (p_row<0) - p_row=0; + setting_row=true; + if (p_row<0) + p_row=0; - if (p_row>=(int)text.size()) - p_row=(int)text.size()-1; + if (p_row>=(int)text.size()) + p_row=(int)text.size()-1; - cursor.line=p_row; - cursor.column=get_char_pos_for( cursor.last_fit_x, get_line( cursor.line) ); + cursor.line=p_row; + cursor.column=get_char_pos_for( cursor.last_fit_x, get_line( cursor.line) ); - adjust_viewport_to_cursor(); + adjust_viewport_to_cursor(); - setting_row=false; + setting_row=false; - if (!cursor_changed_dirty) { - - if (is_inside_tree()) - MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); - cursor_changed_dirty=true; - } + if (!cursor_changed_dirty) { + if (is_inside_tree()) + MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); + cursor_changed_dirty=true; + } } int TextEdit::cursor_get_column() const { - return cursor.column; + return cursor.column; } int TextEdit::cursor_get_line() const { - return cursor.line; + return cursor.line; } void TextEdit::_scroll_moved(double p_to_val) { - if (updating_scrolls) - return; + if (updating_scrolls) + return; - if (h_scroll->is_visible()) - cursor.x_ofs=h_scroll->get_val(); - if (v_scroll->is_visible()) - cursor.line_ofs=v_scroll->get_val(); - update(); + if (h_scroll->is_visible()) + cursor.x_ofs=h_scroll->get_val(); + if (v_scroll->is_visible()) + cursor.line_ofs=v_scroll->get_val(); + update(); } @@ -2204,1110 +2209,1117 @@ void TextEdit::_scroll_moved(double p_to_val) { int TextEdit::get_row_height() const { - return cache.font->get_height()+cache.line_spacing; + return cache.font->get_height()+cache.line_spacing; } int TextEdit::get_char_pos_for(int p_px,String p_str) const { - int px=0; - int c=0; + int px=0; + int c=0; - int tab_w = cache.font->get_char_size(' ').width*tab_size; + int tab_w = cache.font->get_char_size(' ').width*tab_size; - while (c<p_str.length()) { + while (c<p_str.length()) { - int w=0; + int w=0; - if (p_str[c]=='\t') { + if (p_str[c]=='\t') { - int left = px%tab_w; - if (left==0) - w=tab_w; - else - w=tab_w-px%tab_w; // is right... + int left = px%tab_w; + if (left==0) + w=tab_w; + else + w=tab_w-px%tab_w; // is right... - } else { + } else { - w=cache.font->get_char_size(p_str[c],p_str[c+1]).width; - } + w=cache.font->get_char_size(p_str[c],p_str[c+1]).width; + } - if (p_px<(px+w/2)) - break; - px+=w; - c++; - } + if (p_px<(px+w/2)) + break; + px+=w; + c++; + } - return c; + return c; } int TextEdit::get_column_x_offset(int p_char,String p_str) { - int px=0; + int px=0; - int tab_w = cache.font->get_char_size(' ').width*tab_size; + int tab_w = cache.font->get_char_size(' ').width*tab_size; - for (int i=0;i<p_char;i++) { + for (int i=0;i<p_char;i++) { - if (i>=p_str.length()) - break; + if (i>=p_str.length()) + break; - if (p_str[i]=='\t') { + if (p_str[i]=='\t') { - int left = px%tab_w; - if (left==0) - px+=tab_w; - else - px+=tab_w-px%tab_w; // is right... + int left = px%tab_w; + if (left==0) + px+=tab_w; + else + px+=tab_w-px%tab_w; // is right... - } else { - px+=cache.font->get_char_size(p_str[i],p_str[i+1]).width; - } - } + } else { + px+=cache.font->get_char_size(p_str[i],p_str[i+1]).width; + } + } - return px; + return px; } void TextEdit::insert_text_at_cursor(const String& p_text) { - if (selection.active) { + if (selection.active) { - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; - } + } - _insert_text_at_cursor(p_text); - update(); + _insert_text_at_cursor(p_text); + update(); } Control::CursorShape TextEdit::get_cursor_shape(const Point2& p_pos) const { - if(completion_active && completion_rect.has_point(p_pos)) { - return CURSOR_ARROW; - } - return CURSOR_IBEAM; + if(completion_active && completion_rect.has_point(p_pos)) { + return CURSOR_ARROW; + } + return CURSOR_IBEAM; } void TextEdit::set_text(String p_text){ - setting_text=true; - _clear(); - _insert_text_at_cursor(p_text); - - cursor.column=0; - cursor.line=0; - cursor.x_ofs=0; - cursor.line_ofs=0; - cursor.last_fit_x=0; - cursor_set_line(0); - cursor_set_column(0); - update(); - setting_text=false; - - //get_range()->set(0); + setting_text=true; + _clear(); + _insert_text_at_cursor(p_text); + + cursor.column=0; + cursor.line=0; + cursor.x_ofs=0; + cursor.line_ofs=0; + cursor.last_fit_x=0; + cursor_set_line(0); + cursor_set_column(0); + update(); + setting_text=false; + + //get_range()->set(0); }; String TextEdit::get_text() { - String longthing; - int len = text.size(); - for (int i=0;i<len;i++) { + String longthing; + int len = text.size(); + for (int i=0;i<len;i++) { - longthing+=text[i]; - if (i!=len-1) - longthing+="\n"; - } + longthing+=text[i]; + if (i!=len-1) + longthing+="\n"; + } - return longthing; + return longthing; }; String TextEdit::get_line(int line) const { - if (line<0 || line>=text.size()) - return ""; + if (line<0 || line>=text.size()) + return ""; - return text[line]; + return text[line]; }; void TextEdit::_clear() { - clear_undo_history(); - text.clear(); - cursor.column=0; - cursor.line=0; - cursor.x_ofs=0; - cursor.line_ofs=0; - cursor.last_fit_x=0; + clear_undo_history(); + text.clear(); + cursor.column=0; + cursor.line=0; + cursor.x_ofs=0; + cursor.line_ofs=0; + cursor.last_fit_x=0; } void TextEdit::clear() { - setting_text=true; - _clear(); - setting_text=false; + setting_text=true; + _clear(); + setting_text=false; }; void TextEdit::set_readonly(bool p_readonly) { - readonly=p_readonly; + readonly=p_readonly; } void TextEdit::set_wrap(bool p_wrap) { - wrap=p_wrap; + wrap=p_wrap; } void TextEdit::set_max_chars(int p_max_chars) { - max_chars=p_max_chars; + max_chars=p_max_chars; } void TextEdit::_update_caches() { - cache.style_normal=get_stylebox("normal"); - cache.style_focus=get_stylebox("focus"); - cache.font=get_font("font"); - cache.font_color=get_color("font_color"); - cache.font_selected_color=get_color("font_selected_color"); - cache.keyword_color=get_color("keyword_color"); - cache.selection_color=get_color("selection_color"); - cache.mark_color=get_color("mark_color"); - cache.current_line_color=get_color("current_line_color"); - cache.breakpoint_color=get_color("breakpoint_color"); - cache.line_spacing=get_constant("line_spacing"); - cache.row_height = cache.font->get_height() + cache.line_spacing; - cache.tab_icon=get_icon("tab"); - text.set_font(cache.font); + cache.style_normal=get_stylebox("normal"); + cache.style_focus=get_stylebox("focus"); + cache.font=get_font("font"); + cache.font_color=get_color("font_color"); + cache.font_selected_color=get_color("font_selected_color"); + cache.keyword_color=get_color("keyword_color"); + cache.selection_color=get_color("selection_color"); + cache.mark_color=get_color("mark_color"); + cache.current_line_color=get_color("current_line_color"); + cache.breakpoint_color=get_color("breakpoint_color"); + cache.line_spacing=get_constant("line_spacing"); + cache.row_height = cache.font->get_height() + cache.line_spacing; + cache.tab_icon=get_icon("tab"); + text.set_font(cache.font); } void TextEdit::clear_colors() { - keywords.clear(); - color_regions.clear();; - text.clear_caches(); - custom_bg_color=Color(0,0,0,0); + keywords.clear(); + color_regions.clear();; + text.clear_caches(); + custom_bg_color=Color(0,0,0,0); } void TextEdit::set_custom_bg_color(const Color& p_color) { - custom_bg_color=p_color; - update(); + custom_bg_color=p_color; + update(); } void TextEdit::add_keyword_color(const String& p_keyword,const Color& p_color) { - keywords[p_keyword]=p_color; - update(); + keywords[p_keyword]=p_color; + update(); } void TextEdit::add_color_region(const String& p_begin_key,const String& p_end_key,const Color &p_color,bool p_line_only) { - color_regions.push_back(ColorRegion(p_begin_key,p_end_key,p_color,p_line_only)); - text.clear_caches(); - update(); - + color_regions.push_back(ColorRegion(p_begin_key,p_end_key,p_color,p_line_only)); + text.clear_caches(); + update(); + } void TextEdit::set_symbol_color(const Color& p_color) { - symbol_color=p_color; - update(); + symbol_color=p_color; + update(); } void TextEdit::set_syntax_coloring(bool p_enabled) { - syntax_coloring=p_enabled; - update(); + syntax_coloring=p_enabled; + update(); } bool TextEdit::is_syntax_coloring_enabled() const { - return syntax_coloring; + return syntax_coloring; } void TextEdit::cut() { - if (!selection.active) - return; + if (!selection.active) + return; - String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - OS::get_singleton()->set_clipboard(clipboard); + String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + OS::get_singleton()->set_clipboard(clipboard); - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; - update(); + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; + update(); } void TextEdit::copy() { - if (!selection.active) - return; + if (!selection.active) + return; - String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - OS::get_singleton()->set_clipboard(clipboard); + String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + OS::get_singleton()->set_clipboard(clipboard); } void TextEdit::paste() { - if (selection.active) { + if (selection.active) { - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; - } + } - String clipboard = OS::get_singleton()->get_clipboard(); - _insert_text_at_cursor(clipboard); - update(); + String clipboard = OS::get_singleton()->get_clipboard(); + _insert_text_at_cursor(clipboard); + update(); } void TextEdit::select_all() { - if (text.size()==1 && text[0].length()==0) - return; - selection.active=true; - selection.from_line=0; - selection.from_column=0; - selection.to_line=text.size()-1; - selection.to_column=text[selection.to_line].size(); - selection.selecting_mode=Selection::MODE_NONE; - update(); + if (text.size()==1 && text[0].length()==0) + return; + selection.active=true; + selection.from_line=0; + selection.from_column=0; + selection.to_line=text.size()-1; + selection.to_column=text[selection.to_line].size(); + selection.selecting_mode=Selection::MODE_NONE; + update(); } void TextEdit::deselect() { - selection.active=false; - update(); + selection.active=false; + update(); } void TextEdit::select(int p_from_line,int p_from_column,int p_to_line,int p_to_column) { - if (p_from_line>=text.size()) - p_from_line=text.size()-1; - if (p_from_column>=text[p_from_line].length()) - p_from_column=text[p_from_line].length(); + if (p_from_line>=text.size()) + p_from_line=text.size()-1; + if (p_from_column>=text[p_from_line].length()) + p_from_column=text[p_from_line].length(); - if (p_to_line>=text.size()) - p_to_line=text.size()-1; - if (p_to_column>=text[p_to_line].length()) - p_to_column=text[p_to_line].length(); + if (p_to_line>=text.size()) + p_to_line=text.size()-1; + if (p_to_column>=text[p_to_line].length()) + p_to_column=text[p_to_line].length(); - selection.from_line=p_from_line; - selection.from_column=p_from_column; - selection.to_line=p_to_line; - selection.to_column=p_to_column; + selection.from_line=p_from_line; + selection.from_column=p_from_column; + selection.to_line=p_to_line; + selection.to_column=p_to_column; - selection.active=true; + selection.active=true; - if (selection.from_line==selection.to_line) { + if (selection.from_line==selection.to_line) { - if (selection.from_column==selection.to_column) { + if (selection.from_column==selection.to_column) { - selection.active=false; + selection.active=false; - } else if (selection.from_column>selection.to_column) { + } else if (selection.from_column>selection.to_column) { - SWAP( selection.from_column, selection.to_column ); - } - } else if (selection.from_line>selection.to_line) { + SWAP( selection.from_column, selection.to_column ); + } + } else if (selection.from_line>selection.to_line) { - SWAP( selection.from_line, selection.to_line ); - SWAP( selection.from_column, selection.to_column ); - } + SWAP( selection.from_line, selection.to_line ); + SWAP( selection.from_column, selection.to_column ); + } - update(); + update(); } bool TextEdit::is_selection_active() const { - return selection.active; + return selection.active; } int TextEdit::get_selection_from_line() const { - ERR_FAIL_COND_V(!selection.active,-1); - return selection.from_line; + ERR_FAIL_COND_V(!selection.active,-1); + return selection.from_line; } int TextEdit::get_selection_from_column() const { - ERR_FAIL_COND_V(!selection.active,-1); - return selection.from_column; + ERR_FAIL_COND_V(!selection.active,-1); + return selection.from_column; } int TextEdit::get_selection_to_line() const { - ERR_FAIL_COND_V(!selection.active,-1); - return selection.to_line; + ERR_FAIL_COND_V(!selection.active,-1); + return selection.to_line; } int TextEdit::get_selection_to_column() const { - ERR_FAIL_COND_V(!selection.active,-1); - return selection.to_column; + ERR_FAIL_COND_V(!selection.active,-1); + return selection.to_column; } String TextEdit::get_selection_text() const { - if (!selection.active) - return ""; + if (!selection.active) + return ""; - return _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + return _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); } String TextEdit::get_word_under_cursor() const { - int prev_cc = cursor.column; - while(prev_cc >0) { - bool is_char = _is_text_char(text[cursor.line][prev_cc-1]); - if (!is_char) - break; - --prev_cc; - } - - int next_cc = cursor.column; - while(next_cc<text[cursor.line].length()) { - bool is_char = _is_text_char(text[cursor.line][next_cc]); - if(!is_char) - break; - ++ next_cc; - } - if (prev_cc == cursor.column || next_cc == cursor.column) - return ""; - return text[cursor.line].substr(prev_cc, next_cc-prev_cc); + int prev_cc = cursor.column; + while(prev_cc >0) { + bool is_char = _is_text_char(text[cursor.line][prev_cc-1]); + if (!is_char) + break; + --prev_cc; + } + + int next_cc = cursor.column; + while(next_cc<text[cursor.line].length()) { + bool is_char = _is_text_char(text[cursor.line][next_cc]); + if(!is_char) + break; + ++ next_cc; + } + if (prev_cc == cursor.column || next_cc == cursor.column) + return ""; + return text[cursor.line].substr(prev_cc, next_cc-prev_cc); } DVector<int> TextEdit::_search_bind(const String &p_key,uint32_t p_search_flags, int p_from_line,int p_from_column) const { - int col,line; - if (search(p_key,p_search_flags,p_from_line,p_from_column,col,line)) { - DVector<int> result; - result.resize(2); - result.set(0,line); - result.set(1,col); - return result; + int col,line; + if (search(p_key,p_search_flags,p_from_line,p_from_column,col,line)) { + DVector<int> result; + result.resize(2); + result.set(0,line); + result.set(1,col); + return result; - } else { + } else { - return DVector<int>(); - } + return DVector<int>(); + } } bool TextEdit::search(const String &p_key,uint32_t p_search_flags, int p_from_line, int p_from_column,int &r_line,int &r_column) const { - if (p_key.length()==0) - return false; - ERR_FAIL_INDEX_V(p_from_line,text.size(),false); - ERR_FAIL_INDEX_V(p_from_column,text[p_from_line].length()+1,false); + if (p_key.length()==0) + return false; + ERR_FAIL_INDEX_V(p_from_line,text.size(),false); + ERR_FAIL_INDEX_V(p_from_column,text[p_from_line].length()+1,false); - //search through the whole documment, but start by current line + //search through the whole documment, but start by current line - int line=-1; - int pos=-1; + int line=-1; + int pos=-1; - line=p_from_line; + line=p_from_line; - for(int i=0;i<text.size()+1;i++) { - //backwards is broken... - //int idx=(p_search_flags&SEARCH_BACKWARDS)?(text.size()-i):i; //do backwards seearch + for(int i=0;i<text.size()+1;i++) { + //backwards is broken... + //int idx=(p_search_flags&SEARCH_BACKWARDS)?(text.size()-i):i; //do backwards seearch - if (line<0) { - line=text.size()-1; - } - if (line==text.size()) { - line=0; - } + if (line<0) { + line=text.size()-1; + } + if (line==text.size()) { + line=0; + } - String text_line = text[line]; - int from_column=0; - if (line==p_from_line) { + String text_line = text[line]; + int from_column=0; + if (line==p_from_line) { - if (i==text.size()) { - //wrapped + if (i==text.size()) { + //wrapped - if (p_search_flags&SEARCH_BACKWARDS) { - text_line=text_line.substr(from_column,text_line.length()); - from_column=text_line.length(); - } else { - text_line=text_line.substr(0,from_column); - from_column=0; - } + if (p_search_flags&SEARCH_BACKWARDS) { + text_line=text_line.substr(from_column,text_line.length()); + from_column=text_line.length(); + } else { + text_line=text_line.substr(0,from_column); + from_column=0; + } - } else { + } else { - from_column=p_from_column; - } + from_column=p_from_column; + } - } else { - //text_line=text_line.substr(0,p_from_column); //wrap around for missing begining. - if (p_search_flags&SEARCH_BACKWARDS) - from_column=text_line.length()-1; - else - from_column=0; - } + } else { + //text_line=text_line.substr(0,p_from_column); //wrap around for missing begining. + if (p_search_flags&SEARCH_BACKWARDS) + from_column=text_line.length()-1; + else + from_column=0; + } - pos=-1; + pos=-1; - if (!(p_search_flags&SEARCH_BACKWARDS)) { + if (!(p_search_flags&SEARCH_BACKWARDS)) { - pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.find(p_key,from_column):text_line.findn(p_key,from_column); - } else { + pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.find(p_key,from_column):text_line.findn(p_key,from_column); + } else { - pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.rfind(p_key,from_column):text_line.rfindn(p_key,from_column); - } + pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.rfind(p_key,from_column):text_line.rfindn(p_key,from_column); + } - if (pos!=-1 && (p_search_flags&SEARCH_WHOLE_WORDS)) { - //validate for whole words - if (pos>0 && _is_text_char(text_line[pos-1])) - pos=-1; - else if (_is_text_char(text_line[pos+p_key.length()])) - pos=-1; - } + if (pos!=-1 && (p_search_flags&SEARCH_WHOLE_WORDS)) { + //validate for whole words + if (pos>0 && _is_text_char(text_line[pos-1])) + pos=-1; + else if (_is_text_char(text_line[pos+p_key.length()])) + pos=-1; + } - if (pos!=-1) - break; + if (pos!=-1) + break; - if (p_search_flags&SEARCH_BACKWARDS) - line--; - else - line++; + if (p_search_flags&SEARCH_BACKWARDS) + line--; + else + line++; - } + } - if (pos==-1) { - r_line=-1; - r_column=-1; - return false; - } + if (pos==-1) { + r_line=-1; + r_column=-1; + return false; + } - r_line=line; - r_column=pos; + r_line=line; + r_column=pos; - return true; + return true; } void TextEdit::_cursor_changed_emit() { - emit_signal("cursor_changed"); - cursor_changed_dirty=false; + emit_signal("cursor_changed"); + cursor_changed_dirty=false; } void TextEdit::_text_changed_emit() { - emit_signal("text_changed"); - text_changed_dirty=false; + emit_signal("text_changed"); + text_changed_dirty=false; } void TextEdit::set_line_as_marked(int p_line,bool p_marked) { - ERR_FAIL_INDEX(p_line,text.size()); - text.set_marked(p_line,p_marked); - update(); + ERR_FAIL_INDEX(p_line,text.size()); + text.set_marked(p_line,p_marked); + update(); } bool TextEdit::is_line_set_as_breakpoint(int p_line) const { - ERR_FAIL_INDEX_V(p_line,text.size(),false); - return text.is_breakpoint(p_line); + ERR_FAIL_INDEX_V(p_line,text.size(),false); + return text.is_breakpoint(p_line); } void TextEdit::set_line_as_breakpoint(int p_line,bool p_breakpoint) { - ERR_FAIL_INDEX(p_line,text.size()); - text.set_breakpoint(p_line,p_breakpoint); - update(); + ERR_FAIL_INDEX(p_line,text.size()); + text.set_breakpoint(p_line,p_breakpoint); + update(); } void TextEdit::get_breakpoints(List<int> *p_breakpoints) const { - for(int i=0;i<text.size();i++) { - if (text.is_breakpoint(i)) - p_breakpoints->push_back(i); - } + for(int i=0;i<text.size();i++) { + if (text.is_breakpoint(i)) + p_breakpoints->push_back(i); + } } int TextEdit::get_line_count() const { - return text.size(); + return text.size(); } void TextEdit::_do_text_op(const TextOperation& p_op, bool p_reverse) { - ERR_FAIL_COND(p_op.type==TextOperation::TYPE_NONE); + ERR_FAIL_COND(p_op.type==TextOperation::TYPE_NONE); - bool insert = p_op.type==TextOperation::TYPE_INSERT; - if (p_reverse) - insert=!insert; + bool insert = p_op.type==TextOperation::TYPE_INSERT; + if (p_reverse) + insert=!insert; - if (insert) { + if (insert) { - int check_line; - int check_column; - _base_insert_text(p_op.from_line,p_op.from_column,p_op.text,check_line,check_column); - ERR_FAIL_COND( check_line != p_op.to_line ); // BUG - ERR_FAIL_COND( check_column != p_op.to_column ); // BUG - } else { + int check_line; + int check_column; + _base_insert_text(p_op.from_line,p_op.from_column,p_op.text,check_line,check_column); + ERR_FAIL_COND( check_line != p_op.to_line ); // BUG + ERR_FAIL_COND( check_column != p_op.to_column ); // BUG + } else { - _base_remove_text(p_op.from_line,p_op.from_column,p_op.to_line,p_op.to_column); - } + _base_remove_text(p_op.from_line,p_op.from_column,p_op.to_line,p_op.to_column); + } } void TextEdit::_clear_redo() { - if (undo_stack_pos==NULL) - return; //nothing to clear + if (undo_stack_pos==NULL) + return; //nothing to clear - _push_current_op(); + _push_current_op(); - while (undo_stack_pos) { - List<TextOperation>::Element *elem = undo_stack_pos; - undo_stack_pos=undo_stack_pos->next(); - undo_stack.erase(elem); - } + while (undo_stack_pos) { + List<TextOperation>::Element *elem = undo_stack_pos; + undo_stack_pos=undo_stack_pos->next(); + undo_stack.erase(elem); + } } void TextEdit::undo() { - - _push_current_op(); - if (undo_stack_pos==NULL) { + _push_current_op(); + + if (undo_stack_pos==NULL) { - if (!undo_stack.size()) - return; //nothing to undo + if (!undo_stack.size()) + return; //nothing to undo - undo_stack_pos=undo_stack.back(); + undo_stack_pos=undo_stack.back(); - } else if (undo_stack_pos==undo_stack.front()) - return; // at the bottom of the undo stack - else - undo_stack_pos=undo_stack_pos->prev(); + } else if (undo_stack_pos==undo_stack.front()) + return; // at the bottom of the undo stack + else + undo_stack_pos=undo_stack_pos->prev(); - _do_text_op( undo_stack_pos->get(),true); - if(undo_stack_pos->get().chain_backward) { - do { - undo_stack_pos = undo_stack_pos->prev(); - _do_text_op(undo_stack_pos->get(), true); - } while(!undo_stack_pos->get().chain_forward); - } + _do_text_op( undo_stack_pos->get(),true); + if(undo_stack_pos->get().chain_backward) { + do { + undo_stack_pos = undo_stack_pos->prev(); + _do_text_op(undo_stack_pos->get(), true); + } while(!undo_stack_pos->get().chain_forward); + } - cursor_set_line(undo_stack_pos->get().from_line); - cursor_set_column(undo_stack_pos->get().from_column); - update(); + cursor_set_line(undo_stack_pos->get().from_line); + cursor_set_column(undo_stack_pos->get().from_column); + update(); } void TextEdit::redo() { - _push_current_op(); + _push_current_op(); - if (undo_stack_pos==NULL) - return; //nothing to do. + if (undo_stack_pos==NULL) + return; //nothing to do. - _do_text_op(undo_stack_pos->get(), false); - if(undo_stack_pos->get().chain_forward) { - do { - undo_stack_pos=undo_stack_pos->next(); - _do_text_op(undo_stack_pos->get(), false); - } while(!undo_stack_pos->get().chain_backward); - } - cursor_set_line(undo_stack_pos->get().from_line); - cursor_set_column(undo_stack_pos->get().from_column); - undo_stack_pos=undo_stack_pos->next(); - update(); + _do_text_op(undo_stack_pos->get(), false); + if(undo_stack_pos->get().chain_forward) { + do { + undo_stack_pos=undo_stack_pos->next(); + _do_text_op(undo_stack_pos->get(), false); + } while(!undo_stack_pos->get().chain_backward); + } + cursor_set_line(undo_stack_pos->get().from_line); + cursor_set_column(undo_stack_pos->get().from_column); + undo_stack_pos=undo_stack_pos->next(); + update(); } void TextEdit::clear_undo_history() { - saved_version=0; - current_op.type=TextOperation::TYPE_NONE; - undo_stack_pos=NULL; - undo_stack.clear(); + saved_version=0; + current_op.type=TextOperation::TYPE_NONE; + undo_stack_pos=NULL; + undo_stack.clear(); } void TextEdit::_begin_compex_operation() { - _push_current_op(); - next_operation_is_complex=true; + _push_current_op(); + next_operation_is_complex=true; } void TextEdit::_end_compex_operation() { - - _push_current_op(); - ERR_FAIL_COND(undo_stack.size() == 0); - - if(undo_stack.back()->get().chain_forward) { - undo_stack.back()->get().chain_forward=false; - return; - } - - undo_stack.back()->get().chain_backward=true; + + _push_current_op(); + ERR_FAIL_COND(undo_stack.size() == 0); + + if(undo_stack.back()->get().chain_forward) { + undo_stack.back()->get().chain_forward=false; + return; + } + + undo_stack.back()->get().chain_backward=true; } void TextEdit::_push_current_op() { - - if (current_op.type==TextOperation::TYPE_NONE) - return; // do nothing - - if(next_operation_is_complex) { - current_op.chain_forward=true; - next_operation_is_complex=false; - } - - undo_stack.push_back(current_op); - current_op.type=TextOperation::TYPE_NONE; - current_op.text=""; - current_op.chain_forward=false; - + + if (current_op.type==TextOperation::TYPE_NONE) + return; // do nothing + + if(next_operation_is_complex) { + current_op.chain_forward=true; + next_operation_is_complex=false; + } + + undo_stack.push_back(current_op); + current_op.type=TextOperation::TYPE_NONE; + current_op.text=""; + current_op.chain_forward=false; + } void TextEdit::set_draw_tabs(bool p_draw) { - draw_tabs=p_draw; + draw_tabs=p_draw; } bool TextEdit::is_drawing_tabs() const{ - return draw_tabs; + return draw_tabs; } uint32_t TextEdit::get_version() const { - return current_op.version; + return current_op.version; } uint32_t TextEdit::get_saved_version() const { - return saved_version; + return saved_version; } void TextEdit::tag_saved_version() { - saved_version=get_version(); + saved_version=get_version(); } int TextEdit::get_v_scroll() const { - return v_scroll->get_val(); + return v_scroll->get_val(); } void TextEdit::set_v_scroll(int p_scroll) { - v_scroll->set_val(p_scroll); - cursor.line_ofs=p_scroll; + v_scroll->set_val(p_scroll); + cursor.line_ofs=p_scroll; } int TextEdit::get_h_scroll() const { - return h_scroll->get_val(); + return h_scroll->get_val(); } void TextEdit::set_h_scroll(int p_scroll) { - h_scroll->set_val(p_scroll); + h_scroll->set_val(p_scroll); } void TextEdit::set_completion(bool p_enabled,const Vector<String>& p_prefixes) { - completion_prefixes.clear(); - completion_enabled=p_enabled; - for(int i=0;i<p_prefixes.size();i++) - completion_prefixes.insert(p_prefixes[i]); + completion_prefixes.clear(); + completion_enabled=p_enabled; + for(int i=0;i<p_prefixes.size();i++) + completion_prefixes.insert(p_prefixes[i]); } void TextEdit::_confirm_completion() { - String remaining=completion_current.substr(completion_base.length(),completion_current.length()-completion_base.length()); - String l = text[cursor.line]; - bool same=true; - //if what is going to be inserted is the same as what it is, don't change it - for(int i=0;i<remaining.length();i++) { - int c=i+cursor.column; - if (c>=l.length() || l[c]!=remaining[i]) { - same=false; - break; - } - } + String remaining=completion_current.substr(completion_base.length(),completion_current.length()-completion_base.length()); + String l = text[cursor.line]; + bool same=true; + //if what is going to be inserted is the same as what it is, don't change it + for(int i=0;i<remaining.length();i++) { + int c=i+cursor.column; + if (c>=l.length() || l[c]!=remaining[i]) { + same=false; + break; + } + } - if (same) - cursor_set_column(cursor.column+remaining.length()); - else - insert_text_at_cursor(remaining); + if (same) + cursor_set_column(cursor.column+remaining.length()); + else + insert_text_at_cursor(remaining); - _cancel_completion(); + _cancel_completion(); } void TextEdit::_cancel_completion() { - if (!completion_active) - return; + if (!completion_active) + return; - completion_active=false; - update(); + completion_active=false; + update(); } void TextEdit::_update_completion_candidates() { - String l = text[cursor.line]; - int cofs = CLAMP(cursor.column,0,l.length()); - - String s; - while(cofs>0 && l[cofs-1]>32 && !_is_symbol(l[cofs-1])) { - s=String::chr(l[cofs-1])+s; - cofs--; - } - - update(); - - if (s=="" && (cofs==0 || !completion_prefixes.has(String::chr(l[cofs-1])))) { - //none to complete, cancel - _cancel_completion(); - return; - } - - completion_options.clear(); - completion_index=0; - completion_base=s; - int ci_match=0; - for(int i=0;i<completion_strings.size();i++) { - if (completion_strings[i].begins_with(s)) { - completion_options.push_back(completion_strings[i]); - int m=0; - int max=MIN(completion_current.length(),completion_strings[i].length()); - if (max<ci_match) - continue; - for(int j=0;j<max;j++) { - - if (j>=completion_strings[i].length()) - break; - if (completion_current[j]!=completion_strings[i][j]) - break; - m++; - } - if (m>ci_match) { - ci_match=m; - completion_index=completion_options.size()-1; - } - - } - } - - - - if (completion_options.size()==0) { - //no options to complete, cancel - _cancel_completion(); - return; - - } - - completion_current=completion_options[completion_index]; + String l = text[cursor.line]; + int cofs = CLAMP(cursor.column,0,l.length()); + + String s; + while(cofs>0 && l[cofs-1]>32 && !_is_symbol(l[cofs-1])) { + s=String::chr(l[cofs-1])+s; + cofs--; + } + + update(); + + if (s=="" && (cofs==0 || !completion_prefixes.has(String::chr(l[cofs-1])))) { + //none to complete, cancel + _cancel_completion(); + return; + } + + completion_options.clear(); + completion_index=0; + completion_base=s; + int ci_match=0; + for(int i=0;i<completion_strings.size();i++) { + if (completion_strings[i].begins_with(s)) { + completion_options.push_back(completion_strings[i]); + int m=0; + int max=MIN(completion_current.length(),completion_strings[i].length()); + if (max<ci_match) + continue; + for(int j=0;j<max;j++) { + + if (j>=completion_strings[i].length()) + break; + if (completion_current[j]!=completion_strings[i][j]) + break; + m++; + } + if (m>ci_match) { + ci_match=m; + completion_index=completion_options.size()-1; + } -#if 0 // even there's only one option, user still get the chance to choose using it or not - if (completion_options.size()==1) { - //one option to complete, just complete it automagically - _confirm_completion(); -// insert_text_at_cursor(completion_options[0].substr(s.length(),completion_options[0].length()-s.length())); - _cancel_completion(); - return; + } + } - } -#endif - if (completion_options.size()==1 && s==completion_options[0]) - _cancel_completion(); - completion_enabled=true; + if (completion_options.size()==0) { + //no options to complete, cancel + _cancel_completion(); + return; + + } + + completion_current=completion_options[completion_index]; +#if 0 // even there's only one option, user still get the chance to choose using it or not + if (completion_options.size()==1) { + //one option to complete, just complete it automagically + _confirm_completion(); +// insert_text_at_cursor(completion_options[0].substr(s.length(),completion_options[0].length()-s.length())); + _cancel_completion(); + return; + } +#endif + if (completion_options.size()==1 && s==completion_options[0]) + _cancel_completion(); + + completion_enabled=true; } void TextEdit::query_code_comple() { - String l = text[cursor.line]; - int ofs = CLAMP(cursor.column,0,l.length()); - String cs; - while(ofs>0 && l[ofs-1]>32) { + String l = text[cursor.line]; + int ofs = CLAMP(cursor.column,0,l.length()); + String cs; + while(ofs>0 && l[ofs-1]>32) { - if (_is_symbol(l[ofs-1])) { - String s; - while(ofs>0 && l[ofs-1]>32 && _is_symbol(l[ofs-1])) { - s=String::chr(l[ofs-1])+s; - ofs--; - } - if (completion_prefixes.has(s)) - cs=s+cs; - else - break; - } else { + if (_is_symbol(l[ofs-1])) { + String s; + while(ofs>0 && l[ofs-1]>32 && _is_symbol(l[ofs-1])) { + s=String::chr(l[ofs-1])+s; + ofs--; + } + if (completion_prefixes.has(s)) + cs=s+cs; + else + break; + } else { - cs=String::chr(l[ofs-1])+cs; - ofs--; - } + cs=String::chr(l[ofs-1])+cs; + ofs--; + } - } + } - if (cs!="") { - emit_signal("request_completion",cs,cursor.line); + if (cs!="") { + emit_signal("request_completion",cs,cursor.line); - } + } } void TextEdit::code_complete(const Vector<String> &p_strings) { - completion_strings=p_strings; - completion_active=true; - completion_current=""; - completion_index=0; - _update_completion_candidates(); + completion_strings=p_strings; + completion_active=true; + completion_current=""; + completion_index=0; + _update_completion_candidates(); // } String TextEdit::get_tooltip(const Point2& p_pos) const { - if (!tooltip_obj) - return Control::get_tooltip(p_pos); - int row,col; - if (!_get_mouse_pos(p_pos, row,col)) { - return Control::get_tooltip(p_pos); - } + if (!tooltip_obj) + return Control::get_tooltip(p_pos); + int row,col; + if (!_get_mouse_pos(p_pos, row,col)) { + return Control::get_tooltip(p_pos); + } - String s = text[row]; - if (s.length()==0) - return Control::get_tooltip(p_pos); - int beg=CLAMP(col,0,s.length()); - int end=beg; + String s = text[row]; + if (s.length()==0) + return Control::get_tooltip(p_pos); + int beg=CLAMP(col,0,s.length()); + int end=beg; - if (s[beg]>32 || beg==s.length()) { + if (s[beg]>32 || beg==s.length()) { - bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this + bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this - while(beg>0 && s[beg-1]>32 && (symbol==_is_symbol(s[beg-1]))) { - beg--; - } - while(end<s.length() && s[end+1]>32 && (symbol==_is_symbol(s[end+1]))) { - end++; - } + while(beg>0 && s[beg-1]>32 && (symbol==_is_symbol(s[beg-1]))) { + beg--; + } + while(end<s.length() && s[end+1]>32 && (symbol==_is_symbol(s[end+1]))) { + end++; + } - if (end<s.length()) - end+=1; + if (end<s.length()) + end+=1; - String tt = tooltip_obj->call(tooltip_func,s.substr(beg,end-beg),tooltip_ud); + String tt = tooltip_obj->call(tooltip_func,s.substr(beg,end-beg),tooltip_ud); - return tt; + return tt; - } + } - return Control::get_tooltip(p_pos); + return Control::get_tooltip(p_pos); } void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName& p_function,const Variant& p_udata) { - tooltip_obj=p_obj; - tooltip_func=p_function; - tooltip_ud=p_udata; + tooltip_obj=p_obj; + tooltip_func=p_function; + tooltip_ud=p_udata; } +void TextEdit::set_line(int line, String new_text) +{ + if (line < 0 || line > text.size()) + return; + text.set(line, new_text); +} + +void TextEdit::insert_at(const String &p_text, int at) +{ + text.insert(at, p_text); +} void TextEdit::set_show_line_numbers(bool p_show) { - line_numbers=p_show; - update(); + line_numbers=p_show; + update(); } void TextEdit::_bind_methods() { - ObjectTypeDB::bind_method(_MD("_input_event"),&TextEdit::_input_event); - ObjectTypeDB::bind_method(_MD("_scroll_moved"),&TextEdit::_scroll_moved); - ObjectTypeDB::bind_method(_MD("_cursor_changed_emit"),&TextEdit::_cursor_changed_emit); - ObjectTypeDB::bind_method(_MD("_text_changed_emit"),&TextEdit::_text_changed_emit); - ObjectTypeDB::bind_method(_MD("_push_current_op"),&TextEdit::_push_current_op); + ObjectTypeDB::bind_method(_MD("_input_event"),&TextEdit::_input_event); + ObjectTypeDB::bind_method(_MD("_scroll_moved"),&TextEdit::_scroll_moved); + ObjectTypeDB::bind_method(_MD("_cursor_changed_emit"),&TextEdit::_cursor_changed_emit); + ObjectTypeDB::bind_method(_MD("_text_changed_emit"),&TextEdit::_text_changed_emit); + ObjectTypeDB::bind_method(_MD("_push_current_op"),&TextEdit::_push_current_op); - BIND_CONSTANT( SEARCH_MATCH_CASE ); - BIND_CONSTANT( SEARCH_WHOLE_WORDS ); - BIND_CONSTANT( SEARCH_BACKWARDS ); + BIND_CONSTANT( SEARCH_MATCH_CASE ); + BIND_CONSTANT( SEARCH_WHOLE_WORDS ); + BIND_CONSTANT( SEARCH_BACKWARDS ); /* - ObjectTypeDB::bind_method(_MD("delete_char"),&TextEdit::delete_char); - ObjectTypeDB::bind_method(_MD("delete_line"),&TextEdit::delete_line); + ObjectTypeDB::bind_method(_MD("delete_char"),&TextEdit::delete_char); + ObjectTypeDB::bind_method(_MD("delete_line"),&TextEdit::delete_line); */ - ObjectTypeDB::bind_method(_MD("set_text","text"),&TextEdit::set_text); - ObjectTypeDB::bind_method(_MD("insert_text_at_cursor","text"),&TextEdit::insert_text_at_cursor); + ObjectTypeDB::bind_method(_MD("set_text","text"),&TextEdit::set_text); + ObjectTypeDB::bind_method(_MD("insert_text_at_cursor","text"),&TextEdit::insert_text_at_cursor); - ObjectTypeDB::bind_method(_MD("get_line_count"),&TextEdit::get_line_count); - ObjectTypeDB::bind_method(_MD("get_text"),&TextEdit::get_text); - ObjectTypeDB::bind_method(_MD("get_line"),&TextEdit::get_line); + ObjectTypeDB::bind_method(_MD("get_line_count"),&TextEdit::get_line_count); + ObjectTypeDB::bind_method(_MD("get_text"),&TextEdit::get_text); + ObjectTypeDB::bind_method(_MD("get_line"),&TextEdit::get_line); - ObjectTypeDB::bind_method(_MD("cursor_set_column","column"),&TextEdit::cursor_set_column); - ObjectTypeDB::bind_method(_MD("cursor_set_line","line"),&TextEdit::cursor_set_line); + ObjectTypeDB::bind_method(_MD("cursor_set_column","column"),&TextEdit::cursor_set_column); + ObjectTypeDB::bind_method(_MD("cursor_set_line","line"),&TextEdit::cursor_set_line); - ObjectTypeDB::bind_method(_MD("cursor_get_column"),&TextEdit::cursor_get_column); - ObjectTypeDB::bind_method(_MD("cursor_get_line"),&TextEdit::cursor_get_line); + ObjectTypeDB::bind_method(_MD("cursor_get_column"),&TextEdit::cursor_get_column); + ObjectTypeDB::bind_method(_MD("cursor_get_line"),&TextEdit::cursor_get_line); - ObjectTypeDB::bind_method(_MD("set_readonly","enable"),&TextEdit::set_readonly); - ObjectTypeDB::bind_method(_MD("set_wrap","enable"),&TextEdit::set_wrap); - ObjectTypeDB::bind_method(_MD("set_max_chars","amount"),&TextEdit::set_max_chars); + ObjectTypeDB::bind_method(_MD("set_readonly","enable"),&TextEdit::set_readonly); + ObjectTypeDB::bind_method(_MD("set_wrap","enable"),&TextEdit::set_wrap); + ObjectTypeDB::bind_method(_MD("set_max_chars","amount"),&TextEdit::set_max_chars); - ObjectTypeDB::bind_method(_MD("cut"),&TextEdit::cut); - ObjectTypeDB::bind_method(_MD("copy"),&TextEdit::copy); - ObjectTypeDB::bind_method(_MD("paste"),&TextEdit::paste); - ObjectTypeDB::bind_method(_MD("select_all"),&TextEdit::select_all); - ObjectTypeDB::bind_method(_MD("select","from_line","from_column","to_line","to_column"),&TextEdit::select); + ObjectTypeDB::bind_method(_MD("cut"),&TextEdit::cut); + ObjectTypeDB::bind_method(_MD("copy"),&TextEdit::copy); + ObjectTypeDB::bind_method(_MD("paste"),&TextEdit::paste); + ObjectTypeDB::bind_method(_MD("select_all"),&TextEdit::select_all); + ObjectTypeDB::bind_method(_MD("select","from_line","from_column","to_line","to_column"),&TextEdit::select); - ObjectTypeDB::bind_method(_MD("is_selection_active"),&TextEdit::is_selection_active); - ObjectTypeDB::bind_method(_MD("get_selection_from_line"),&TextEdit::get_selection_from_line); - ObjectTypeDB::bind_method(_MD("get_selection_from_column"),&TextEdit::get_selection_from_column); - ObjectTypeDB::bind_method(_MD("get_selection_to_line"),&TextEdit::get_selection_to_line); - ObjectTypeDB::bind_method(_MD("get_selection_to_column"),&TextEdit::get_selection_to_column); - ObjectTypeDB::bind_method(_MD("get_selection_text"),&TextEdit::get_selection_text); - ObjectTypeDB::bind_method(_MD("get_word_under_cursor"),&TextEdit::get_word_under_cursor); - ObjectTypeDB::bind_method(_MD("search","flags","from_line","from_column","to_line","to_column"),&TextEdit::_search_bind); + ObjectTypeDB::bind_method(_MD("is_selection_active"),&TextEdit::is_selection_active); + ObjectTypeDB::bind_method(_MD("get_selection_from_line"),&TextEdit::get_selection_from_line); + ObjectTypeDB::bind_method(_MD("get_selection_from_column"),&TextEdit::get_selection_from_column); + ObjectTypeDB::bind_method(_MD("get_selection_to_line"),&TextEdit::get_selection_to_line); + ObjectTypeDB::bind_method(_MD("get_selection_to_column"),&TextEdit::get_selection_to_column); + ObjectTypeDB::bind_method(_MD("get_selection_text"),&TextEdit::get_selection_text); + ObjectTypeDB::bind_method(_MD("get_word_under_cursor"),&TextEdit::get_word_under_cursor); + ObjectTypeDB::bind_method(_MD("search","flags","from_line","from_column","to_line","to_column"),&TextEdit::_search_bind); - ObjectTypeDB::bind_method(_MD("undo"),&TextEdit::undo); - ObjectTypeDB::bind_method(_MD("redo"),&TextEdit::redo); - ObjectTypeDB::bind_method(_MD("clear_undo_history"),&TextEdit::clear_undo_history); - - ObjectTypeDB::bind_method(_MD("set_syntax_coloring","enable"),&TextEdit::set_syntax_coloring); - ObjectTypeDB::bind_method(_MD("is_syntax_coloring_enabled"),&TextEdit::is_syntax_coloring_enabled); + ObjectTypeDB::bind_method(_MD("undo"),&TextEdit::undo); + ObjectTypeDB::bind_method(_MD("redo"),&TextEdit::redo); + ObjectTypeDB::bind_method(_MD("clear_undo_history"),&TextEdit::clear_undo_history); - - ObjectTypeDB::bind_method(_MD("add_keyword_color","keyword","color"),&TextEdit::add_keyword_color); - ObjectTypeDB::bind_method(_MD("add_color_region","begin_key","end_key","color","line_only"),&TextEdit::add_color_region,DEFVAL(false)); - ObjectTypeDB::bind_method(_MD("set_symbol_color","color"),&TextEdit::set_symbol_color); - ObjectTypeDB::bind_method(_MD("set_custom_bg_color","color"),&TextEdit::set_custom_bg_color); - ObjectTypeDB::bind_method(_MD("clear_colors"),&TextEdit::clear_colors); + ObjectTypeDB::bind_method(_MD("set_syntax_coloring","enable"),&TextEdit::set_syntax_coloring); + ObjectTypeDB::bind_method(_MD("is_syntax_coloring_enabled"),&TextEdit::is_syntax_coloring_enabled); - ADD_SIGNAL(MethodInfo("cursor_changed")); - ADD_SIGNAL(MethodInfo("text_changed")); - ADD_SIGNAL(MethodInfo("request_completion",PropertyInfo(Variant::STRING,"keyword"),PropertyInfo(Variant::INT,"line"))); + ObjectTypeDB::bind_method(_MD("add_keyword_color","keyword","color"),&TextEdit::add_keyword_color); + ObjectTypeDB::bind_method(_MD("add_color_region","begin_key","end_key","color","line_only"),&TextEdit::add_color_region,DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("set_symbol_color","color"),&TextEdit::set_symbol_color); + ObjectTypeDB::bind_method(_MD("set_custom_bg_color","color"),&TextEdit::set_custom_bg_color); + ObjectTypeDB::bind_method(_MD("clear_colors"),&TextEdit::clear_colors); + + + ADD_SIGNAL(MethodInfo("cursor_changed")); + ADD_SIGNAL(MethodInfo("text_changed")); + ADD_SIGNAL(MethodInfo("request_completion",PropertyInfo(Variant::STRING,"keyword"),PropertyInfo(Variant::INT,"line"))); } TextEdit::TextEdit() { - readonly=false; - setting_row=false; - draw_tabs=false; - max_chars=0; - clear(); - wrap=false; - set_focus_mode(FOCUS_ALL); - _update_caches(); - cache.size=Size2(1,1); - tab_size=4; - text.set_tab_size(tab_size); - text.clear(); + readonly=false; + setting_row=false; + draw_tabs=false; + max_chars=0; + clear(); + wrap=false; + set_focus_mode(FOCUS_ALL); + _update_caches(); + cache.size=Size2(1,1); + tab_size=4; + text.set_tab_size(tab_size); + text.clear(); // text.insert(1,"Mongolia.."); // text.insert(2,"PAIS GENEROSO!!"); - text.set_color_regions(&color_regions); + text.set_color_regions(&color_regions); - h_scroll = memnew( HScrollBar ); - v_scroll = memnew( VScrollBar ); + h_scroll = memnew( HScrollBar ); + v_scroll = memnew( VScrollBar ); - add_child(h_scroll); - add_child(v_scroll); + add_child(h_scroll); + add_child(v_scroll); - updating_scrolls=false; - selection.active=false; + updating_scrolls=false; + selection.active=false; - h_scroll->connect("value_changed", this,"_scroll_moved"); - v_scroll->connect("value_changed", this,"_scroll_moved"); + h_scroll->connect("value_changed", this,"_scroll_moved"); + v_scroll->connect("value_changed", this,"_scroll_moved"); - cursor_changed_dirty=false; - text_changed_dirty=false; + cursor_changed_dirty=false; + text_changed_dirty=false; - selection.selecting_mode=Selection::MODE_NONE; - selection.selecting_line=0; - selection.selecting_column=0; - selection.selecting_test=false; - selection.active=false; - syntax_coloring=false; + selection.selecting_mode=Selection::MODE_NONE; + selection.selecting_line=0; + selection.selecting_column=0; + selection.selecting_test=false; + selection.active=false; + syntax_coloring=false; - custom_bg_color=Color(0,0,0,0); - idle_detect = memnew( Timer ); - add_child(idle_detect); - idle_detect->set_one_shot(true); - idle_detect->set_wait_time(GLOBAL_DEF("display/text_edit_idle_detect_sec",3)); - idle_detect->connect("timeout", this,"_push_current_op"); + custom_bg_color=Color(0,0,0,0); + idle_detect = memnew( Timer ); + add_child(idle_detect); + idle_detect->set_one_shot(true); + idle_detect->set_wait_time(GLOBAL_DEF("display/text_edit_idle_detect_sec",3)); + idle_detect->connect("timeout", this,"_push_current_op"); #if 0 - syntax_coloring=true; - keywords["void"]=Color(0.3,0.0,0.1); - keywords["int"]=Color(0.3,0.0,0.1); - keywords["function"]=Color(0.3,0.0,0.1); - keywords["class"]=Color(0.3,0.0,0.1); - keywords["extends"]=Color(0.3,0.0,0.1); - keywords["constructor"]=Color(0.3,0.0,0.1); - symbol_color=Color(0.1,0.0,0.3,1.0); - - color_regions.push_back(ColorRegion("/*","*/",Color(0.4,0.6,0,4))); - color_regions.push_back(ColorRegion("//","",Color(0.6,0.6,0.4))); - color_regions.push_back(ColorRegion("\"","\"",Color(0.4,0.7,0.7))); - color_regions.push_back(ColorRegion("'","'",Color(0.4,0.8,0.8))); - color_regions.push_back(ColorRegion("#","",Color(0.2,1.0,0.2))); + syntax_coloring=true; + keywords["void"]=Color(0.3,0.0,0.1); + keywords["int"]=Color(0.3,0.0,0.1); + keywords["function"]=Color(0.3,0.0,0.1); + keywords["class"]=Color(0.3,0.0,0.1); + keywords["extends"]=Color(0.3,0.0,0.1); + keywords["constructor"]=Color(0.3,0.0,0.1); + symbol_color=Color(0.1,0.0,0.3,1.0); + + color_regions.push_back(ColorRegion("/*","*/",Color(0.4,0.6,0,4))); + color_regions.push_back(ColorRegion("//","",Color(0.6,0.6,0.4))); + color_regions.push_back(ColorRegion("\"","\"",Color(0.4,0.7,0.7))); + color_regions.push_back(ColorRegion("'","'",Color(0.4,0.8,0.8))); + color_regions.push_back(ColorRegion("#","",Color(0.2,1.0,0.2))); #endif - current_op.type=TextOperation::TYPE_NONE; - undo_enabled=true; + current_op.type=TextOperation::TYPE_NONE; + undo_enabled=true; undo_stack_pos=NULL; - setting_text=false; - last_dblclk=0; - current_op.version=0; - version=0; - saved_version=0; + setting_text=false; + last_dblclk=0; + current_op.version=0; + version=0; + saved_version=0; - completion_enabled=false; - completion_active=false; - completion_line_ofs=0; - tooltip_obj=NULL; - line_numbers=false; - next_operation_is_complex=false; - auto_brace_completion_enabled=false; + completion_enabled=false; + completion_active=false; + completion_line_ofs=0; + tooltip_obj=NULL; + line_numbers=false; + next_operation_is_complex=false; + auto_brace_completion_enabled=false; } -TextEdit::~TextEdit(){ +TextEdit::~TextEdit() +{ } - - diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 15c289a87e..d70403a944 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -138,7 +138,7 @@ class TextEdit : public Control { int size() const { return text.size(); } void clear(); void clear_caches(); - _FORCE_INLINE_ const String& operator[](int p_line) const { return text[p_line].data; } + _FORCE_INLINE_ const String& operator[](int p_line) const { return text[p_line].data; } Text() { tab_size=4; } }; @@ -299,6 +299,7 @@ public: void set_text(String p_text); void insert_text_at_cursor(const String& p_text); + void insert_at(const String& p_text, int at); int get_line_count() const; void set_line_as_marked(int p_line,bool p_marked); void set_line_as_breakpoint(int p_line,bool p_breakpoint); @@ -306,6 +307,7 @@ public: void get_breakpoints(List<int> *p_breakpoints) const; String get_text(); String get_line(int line) const; + void set_line(int line, String new_text); void backspace_at_cursor(); inline void set_auto_brace_completion(bool p_enabled) { diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 857ea25d0f..050fd890f4 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -192,7 +192,7 @@ String VideoPlayer::get_stream_name() const { return stream->get_name(); }; -float VideoPlayer::get_pos() const { +float VideoPlayer::get_stream_pos() const { if (stream.is_null()) return 0; @@ -231,7 +231,7 @@ void VideoPlayer::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_stream_name"),&VideoPlayer::get_stream_name); - ObjectTypeDB::bind_method(_MD("get_pos"),&VideoPlayer::get_pos); + ObjectTypeDB::bind_method(_MD("get_stream_pos"),&VideoPlayer::get_stream_pos); ObjectTypeDB::bind_method(_MD("set_autoplay","enabled"),&VideoPlayer::set_autoplay); ObjectTypeDB::bind_method(_MD("has_autoplay"),&VideoPlayer::has_autoplay); diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h index db5f9a58a6..3eb629ced5 100644 --- a/scene/gui/video_player.h +++ b/scene/gui/video_player.h @@ -77,7 +77,7 @@ public: float get_volume_db() const; String get_stream_name() const; - float get_pos() const; + float get_stream_pos() const; void set_autoplay(bool p_vol); bool has_autoplay() const; diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 5561b5ef90..80993c7eaf 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1716,189 +1716,222 @@ void Animation::clear() { } -void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle) { - ERR_FAIL_INDEX(p_idx,tracks.size()); - ERR_FAIL_COND(tracks[p_idx]->type!=TYPE_TRANSFORM); - TransformTrack *tt= static_cast<TransformTrack*>(tracks[p_idx]); - for(int i=1;i<tt->transforms.size()-1;i++) { - TKey<TransformKey> &t0 = tt->transforms[i-1]; - TKey<TransformKey> &t1 = tt->transforms[i]; - TKey<TransformKey> &t2 = tt->transforms[i+1]; +bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0,const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle) { - real_t c = (t1.time-t0.time)/(t2.time-t0.time); - real_t t[3]={-1,-1,-1}; - { //translation + real_t c = (t1.time-t0.time)/(t2.time-t0.time); + real_t t[3]={-1,-1,-1}; - const Vector3 &v0=t0.value.loc; - const Vector3 &v1=t1.value.loc; - const Vector3 &v2=t2.value.loc; + { //translation - if (v0.distance_to(v2)<CMP_EPSILON) { - //0 and 2 are close, let's see if 1 is close - if (v0.distance_to(v1)>CMP_EPSILON) { - //not close, not optimizable - continue; - } + const Vector3 &v0=t0.value.loc; + const Vector3 &v1=t1.value.loc; + const Vector3 &v2=t2.value.loc; - } else { + if (v0.distance_to(v2)<CMP_EPSILON) { + //0 and 2 are close, let's see if 1 is close + if (v0.distance_to(v1)>CMP_EPSILON) { + //not close, not optimizable + return false; + } - Vector3 pd = (v2-v0); - float d0 = pd.dot(v0); - float d1 = pd.dot(v1); - float d2 = pd.dot(v2); - if (d1<d0 || d1>d2) { - continue; //beyond segment range - } + } else { - Vector3 s[2]={ v0, v2 }; - real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1); + Vector3 pd = (v2-v0); + float d0 = pd.dot(v0); + float d1 = pd.dot(v1); + float d2 = pd.dot(v2); + if (d1<d0 || d1>d2) { + return false; + } - if (d>pd.length()*p_alowed_linear_err) { - continue; //beyond allowed error for colinearity - } + Vector3 s[2]={ v0, v2 }; + real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1); - t[0] = (d1-d0)/(d2-d0); + if (d>pd.length()*p_alowed_linear_err) { + return false; //beyond allowed error for colinearity } - } - { //rotation + t[0] = (d1-d0)/(d2-d0); + } + } - const Quat &q0=t0.value.rot; - const Quat &q1=t1.value.rot; - const Quat &q2=t2.value.rot; + { //rotation - //localize both to rotation from q0 + const Quat &q0=t0.value.rot; + const Quat &q1=t1.value.rot; + const Quat &q2=t2.value.rot; - if ((q0-q2).length() < CMP_EPSILON) { + //localize both to rotation from q0 - if ((q0-q1).length() > CMP_EPSILON) - continue; + if ((q0-q2).length() < CMP_EPSILON) { - } else { + if ((q0-q1).length() > CMP_EPSILON) + return false; + } else { - Quat r02 = (q0.inverse() * q2).normalized(); - Quat r01 = (q0.inverse() * q1).normalized(); - Vector3 v02,v01; - real_t a02,a01; + Quat r02 = (q0.inverse() * q2).normalized(); + Quat r01 = (q0.inverse() * q1).normalized(); - r02.get_axis_and_angle(v02,a02); - r01.get_axis_and_angle(v01,a01); + Vector3 v02,v01; + real_t a02,a01; - if (Math::abs(a02)>p_max_optimizable_angle) - continue; + r02.get_axis_and_angle(v02,a02); + r01.get_axis_and_angle(v01,a01); - if (v01.dot(v02)<0) { - //make sure both rotations go the same way to compare - v02=-v02; - a02=-a02; - } + if (Math::abs(a02)>p_max_optimizable_angle) + return false; - real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized()))/Math_PI; - if (err_01>p_alowed_angular_err) { - //not rotating in the same axis - continue; - } + if (v01.dot(v02)<0) { + //make sure both rotations go the same way to compare + v02=-v02; + a02=-a02; + } - if (a01*a02 < 0 ) { - //not rotating in the same direction - continue; - } + real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized()))/Math_PI; + if (err_01>p_alowed_angular_err) { + //not rotating in the same axis + return false; + } - real_t tr = a01/a02; - if (tr<0 || tr>1) - continue; //rotating too much or too less + if (a01*a02 < 0 ) { + //not rotating in the same direction + return false; + } - t[1]=tr; + real_t tr = a01/a02; + if (tr<0 || tr>1) + return false; //rotating too much or too less - } + t[1]=tr; } - { //scale + } - const Vector3 &v0=t0.value.scale; - const Vector3 &v1=t1.value.scale; - const Vector3 &v2=t2.value.scale; + { //scale - if (v0.distance_to(v2)<CMP_EPSILON) { - //0 and 2 are close, let's see if 1 is close - if (v0.distance_to(v1)>CMP_EPSILON) { - //not close, not optimizable - continue; - } + const Vector3 &v0=t0.value.scale; + const Vector3 &v1=t1.value.scale; + const Vector3 &v2=t2.value.scale; - } else { + if (v0.distance_to(v2)<CMP_EPSILON) { + //0 and 2 are close, let's see if 1 is close + if (v0.distance_to(v1)>CMP_EPSILON) { + //not close, not optimizable + return false; + } - Vector3 pd = (v2-v0); - float d0 = pd.dot(v0); - float d1 = pd.dot(v1); - float d2 = pd.dot(v2); - if (d1<d0 || d1>d2) { - continue; //beyond segment range - } + } else { - Vector3 s[2]={ v0, v2 }; - real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1); + Vector3 pd = (v2-v0); + float d0 = pd.dot(v0); + float d1 = pd.dot(v1); + float d2 = pd.dot(v2); + if (d1<d0 || d1>d2) { + return false; //beyond segment range + } - if (d>pd.length()*p_alowed_linear_err) { - continue; //beyond allowed error for colinearity - } + Vector3 s[2]={ v0, v2 }; + real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1); - t[2] = (d1-d0)/(d2-d0); + if (d>pd.length()*p_alowed_linear_err) { + return false; //beyond allowed error for colinearity } + + t[2] = (d1-d0)/(d2-d0); } + } - bool erase=false; - if (t[0]==-1 && t[1]==-1 && t[2]==-1) { + bool erase=false; + if (t[0]==-1 && t[1]==-1 && t[2]==-1) { - erase=true; - } else { + erase=true; + } else { - erase=true; - real_t lt=-1; - for(int j=0;j<3;j++) { - //search for t on first, one must be it - if (t[j]!=-1) { - lt=t[j]; //official t - //validate rest - for(int k=j+1;k<3;k++) { - if (t[k]==-1) - continue; - - if (Math::abs(lt-t[k])>p_alowed_linear_err) { - erase=false; - break; - } + erase=true; + real_t lt=-1; + for(int j=0;j<3;j++) { + //search for t on first, one must be it + if (t[j]!=-1) { + lt=t[j]; //official t + //validate rest + for(int k=j+1;k<3;k++) { + if (t[k]==-1) + continue; + + if (Math::abs(lt-t[k])>p_alowed_linear_err) { + erase=false; + break; } - break; } + break; } + } - ERR_CONTINUE( lt==-1 ); + ERR_FAIL_COND_V( lt==-1,false ); - if (erase) { + if (erase) { - if (Math::abs(lt-c)>p_alowed_linear_err) { - //todo, evaluate changing the transition if this fails? - //this could be done as a second pass and would be - //able to optimize more - erase=false; - } else { + if (Math::abs(lt-c)>p_alowed_linear_err) { + //todo, evaluate changing the transition if this fails? + //this could be done as a second pass and would be + //able to optimize more + erase=false; + } else { - //print_line(itos(i)+"because of interp"); - } + //print_line(itos(i)+"because of interp"); } + } + + } + + + return erase; + + +} + +void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle) { + + ERR_FAIL_INDEX(p_idx,tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type!=TYPE_TRANSFORM); + TransformTrack *tt= static_cast<TransformTrack*>(tracks[p_idx]); + bool prev_erased=false; + TKey<TransformKey> first_erased; + + for(int i=1;i<tt->transforms.size()-1;i++) { + + TKey<TransformKey> &t0 = tt->transforms[i-1]; + TKey<TransformKey> &t1 = tt->transforms[i]; + TKey<TransformKey> &t2 = tt->transforms[i+1]; + + bool erase = _transform_track_optimize_key(t0,t1,t2,p_alowed_linear_err,p_alowed_angular_err,p_max_optimizable_angle); + + + if (prev_erased && !_transform_track_optimize_key(t0,first_erased,t2,p_alowed_linear_err,p_alowed_angular_err,p_max_optimizable_angle)) { + //avoid error to go beyond first erased key + erase=false; } + if (erase) { + + if (!prev_erased) { + first_erased=t1; + prev_erased=true; + } + tt->transforms.remove(i); i--; + + } else { + prev_erased=false; } diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 0c0290295a..bf87789e39 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -204,6 +204,7 @@ private: return idxr; } + bool _transform_track_optimize_key(const TKey<TransformKey> &t0,const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle); void _transform_track_optimize(int p_idx, float p_allowed_err=0.05, float p_alowed_angular_err=0.01,float p_max_optimizable_angle=Math_PI*0.125); protected: diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index 056be87e72..d6a9db690b 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -54,9 +54,7 @@ void CircleShape2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_radius","radius"),&CircleShape2D::set_radius); ObjectTypeDB::bind_method(_MD("get_radius"),&CircleShape2D::get_radius); - - - ADD_PROPERTY( PropertyInfo(Variant::REAL,"radius"),_SCS("set_radius"),_SCS("get_radius") ); + ADD_PROPERTY( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.5"),_SCS("set_radius"),_SCS("get_radius") ); } diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp index fffe1ad7fa..2bbae37510 100644 --- a/scene/resources/video_stream.cpp +++ b/scene/resources/video_stream.cpp @@ -33,6 +33,7 @@ void VideoStream::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_pending_frame_count"),&VideoStream::get_pending_frame_count); ObjectTypeDB::bind_method(_MD("pop_frame"),&VideoStream::pop_frame); ObjectTypeDB::bind_method(_MD("peek_frame"),&VideoStream::peek_frame); + ObjectTypeDB::bind_method(_MD("set_audio_track","idx"),&VideoStream::set_audio_track); }; diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index 1bc8a5e5bc..18f0cc3d05 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -62,6 +62,8 @@ public: virtual void pop_frame(Ref<ImageTexture> p_tex)=0; virtual Image peek_frame() const=0; + virtual void set_audio_track(int p_idx) =0; + virtual void update(float p_time)=0; VideoStream(); diff --git a/servers/audio/audio_server_sw.cpp b/servers/audio/audio_server_sw.cpp index f50813731e..55dde1b35b 100644 --- a/servers/audio/audio_server_sw.cpp +++ b/servers/audio/audio_server_sw.cpp @@ -332,6 +332,7 @@ void AudioServerSW::driver_process_chunk(int p_frames,int32_t *p_buffer) { void AudioServerSW::driver_process(int p_frames,int32_t *p_buffer) { + _output_delay=p_frames/double(AudioDriverSW::get_singleton()->get_mix_rate()); //process in chunks to make sure to never process more than INTERNAL_BUFFER_SIZE int todo=p_frames; while(todo) { @@ -795,6 +796,8 @@ void AudioServerSW::init() { mixer = memnew( AudioMixerSW( sample_manager, latency, AudioDriverSW::get_singleton()->get_mix_rate(),mix_chans,mixer_use_fx,mixer_interp,_mixer_callback,this ) ); mixer_step_usecs=mixer->get_step_usecs(); + _output_delay=0; + stream_volume=0.3; // start the audio driver if (AudioDriverSW::get_singleton()) @@ -911,6 +914,11 @@ float AudioServerSW::get_event_voice_global_volume_scale() const { return event_voice_volume_scale; } +double AudioServerSW::get_output_delay() const { + + return _output_delay; +} + double AudioServerSW::get_mix_time() const { return AudioDriverSW::get_singleton()->get_mix_time(); diff --git a/servers/audio/audio_server_sw.h b/servers/audio/audio_server_sw.h index d137c15633..d47c1b4b3f 100644 --- a/servers/audio/audio_server_sw.h +++ b/servers/audio/audio_server_sw.h @@ -92,6 +92,8 @@ class AudioServerSW : public AudioServer { float peak_left,peak_right; uint32_t max_peak; + double _output_delay; + VoiceRBSW voice_rb; bool exit_update_thread; @@ -206,6 +208,9 @@ public: virtual double get_mix_time() const; //useful for video -> audio sync + virtual double get_output_delay() const; + + AudioServerSW(SampleManagerSW *p_sample_manager); ~AudioServerSW(); diff --git a/servers/audio_server.h b/servers/audio_server.h index 85289de58a..511340678a 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -274,6 +274,7 @@ public: static AudioServer *get_singleton(); virtual double get_mix_time() const=0; //useful for video -> audio sync + virtual double get_output_delay() const=0; AudioServer(); virtual ~AudioServer(); diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp index 2760f9bea6..22fb4fc0a8 100644 --- a/servers/physics_2d_server.cpp +++ b/servers/physics_2d_server.cpp @@ -514,7 +514,7 @@ void Physics2DServer::_bind_methods() { ObjectTypeDB::bind_method(_MD("joint_get_type","joint"),&Physics2DServer::joint_get_type); - ObjectTypeDB::bind_method(_MD("free","rid"),&Physics2DServer::free); + ObjectTypeDB::bind_method(_MD("free_rid","rid"),&Physics2DServer::free); ObjectTypeDB::bind_method(_MD("set_active","active"),&Physics2DServer::set_active); diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp index e6b2927fb4..79de253d3b 100644 --- a/servers/physics_server.cpp +++ b/servers/physics_server.cpp @@ -655,7 +655,7 @@ void PhysicsServer::_bind_methods() { ObjectTypeDB::bind_method(_MD("joint_get_type","joint"),&PhysicsServer::joint_get_type); */ - ObjectTypeDB::bind_method(_MD("free","rid"),&PhysicsServer::free); + ObjectTypeDB::bind_method(_MD("free_rid","rid"),&PhysicsServer::free); ObjectTypeDB::bind_method(_MD("set_active","active"),&PhysicsServer::set_active); diff --git a/tools/docdump/class_list.xml b/tools/docdump/class_list.xml index 897477ff48..ab33cef7d7 100644 --- a/tools/docdump/class_list.xml +++ b/tools/docdump/class_list.xml @@ -319,7 +319,7 @@ <argument index="1" name="animation" type="Object"> </argument> <description> - Add an animation resource to the player, which will be later referenced by the "name" arguemnt. + Add an animation resource to the player, which will be later referenced by the "name" argument. </description> </method> <method name="remove_animation" > diff --git a/tools/editor/animation_editor.cpp b/tools/editor/animation_editor.cpp index 68bd051ee9..0d4f5a7b74 100644 --- a/tools/editor/animation_editor.cpp +++ b/tools/editor/animation_editor.cpp @@ -687,9 +687,7 @@ void AnimationKeyEditor::_menu_track(int p_type) { case TRACK_MENU_OPTIMIZE: { - animation->optimize(); - - track_editor->update(); + optimize_dialog->popup_centered(Size2(250,180)); } break; @@ -698,6 +696,18 @@ void AnimationKeyEditor::_menu_track(int p_type) { } + +void AnimationKeyEditor::_animation_optimize() { + + + print_line("OPTIMIZE!"); + animation->optimize(optimize_linear_error->get_val(),optimize_angular_error->get_val(),optimize_max_angle->get_val()); + track_editor->update(); + undo_redo->clear_history(); + +} + + float AnimationKeyEditor::_get_zoom_scale() const { float zv = zoom->get_val(); @@ -2335,11 +2345,12 @@ void AnimationKeyEditor::_notification(int p_what) { tpp->add_item("Out-In",TRACK_MENU_SET_ALL_TRANS_OUTIN); tpp->set_name("Transitions"); tpp->connect("item_pressed",this,"_menu_track"); + optimize_dialog->connect("confirmed",this,"_animation_optimize"); menu_track->get_popup()->add_child(tpp); menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions"); - //menu_track->get_popup()->add_separator(); - //menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE); + menu_track->get_popup()->add_separator(); + menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE); @@ -3099,6 +3110,7 @@ void AnimationKeyEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_animation_len_update"),&AnimationKeyEditor::_animation_len_update); ObjectTypeDB::bind_method(_MD("set_animation"),&AnimationKeyEditor::set_animation); + ObjectTypeDB::bind_method(_MD("_animation_optimize"),&AnimationKeyEditor::_animation_optimize); ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) ); @@ -3224,6 +3236,37 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h remove_button->set_disabled(true); remove_button->set_tooltip("Remove selected track."); + + optimize_dialog = memnew( ConfirmationDialog ); + add_child(optimize_dialog); + optimize_dialog->set_title("Anim. Optimizer"); + VBoxContainer *optimize_vb = memnew( VBoxContainer ); + optimize_dialog->add_child(optimize_vb); + optimize_dialog->set_child_rect(optimize_vb); + optimize_linear_error = memnew( SpinBox ); + optimize_linear_error->set_max(1.0); + optimize_linear_error->set_min(0.001); + optimize_linear_error->set_step(0.001); + optimize_linear_error->set_val(0.05); + optimize_vb->add_margin_child("Max. Linear Error:",optimize_linear_error); + optimize_angular_error = memnew( SpinBox ); + optimize_angular_error->set_max(1.0); + optimize_angular_error->set_min(0.001); + optimize_angular_error->set_step(0.001); + optimize_angular_error->set_val(0.01); + + optimize_vb->add_margin_child("Max. Angular Error:",optimize_angular_error); + optimize_max_angle = memnew( SpinBox ); + optimize_vb->add_margin_child("Max Optimizable Angle:",optimize_max_angle); + optimize_max_angle->set_max(360.0); + optimize_max_angle->set_min(0.0); + optimize_max_angle->set_step(0.1); + optimize_max_angle->set_val(22); + + optimize_dialog->get_ok()->set_text("Optimize"); + + + /*keying = memnew( Button ); keying->set_toggle_mode(true); //keying->set_text("Keys"); diff --git a/tools/editor/animation_editor.h b/tools/editor/animation_editor.h index 01f6e294cc..9d3e692fad 100644 --- a/tools/editor/animation_editor.h +++ b/tools/editor/animation_editor.h @@ -169,6 +169,11 @@ class AnimationKeyEditor : public VBoxContainer { ToolButton *move_down_button; ToolButton *remove_button; + ConfirmationDialog *optimize_dialog; + SpinBox *optimize_linear_error; + SpinBox *optimize_angular_error; + SpinBox *optimize_max_angle; + SpinBox *step; MenuButton *menu_track; @@ -257,6 +262,7 @@ class AnimationKeyEditor : public VBoxContainer { StringName alc; void _animation_changed(); + void _animation_optimize(); void _scroll_changed(double); diff --git a/tools/editor/editor_file_system.cpp b/tools/editor/editor_file_system.cpp index e0b743a929..5d72928e9c 100644 --- a/tools/editor/editor_file_system.cpp +++ b/tools/editor/editor_file_system.cpp @@ -992,6 +992,35 @@ void EditorFileSystem::_resource_saved(const String& p_path){ EditorFileSystem::get_singleton()->update_file(p_path); } +String EditorFileSystem::_find_first_from_source(EditorFileSystemDirectory* p_dir,const String &p_src) const { + + for(int i=0;i<p_dir->files.size();i++) { + for(int j=0;j<p_dir->files[i].meta.sources.size();j++) { + + if (p_dir->files[i].meta.sources[j].path==p_src) + return p_dir->get_file_path(i); + } + } + + for(int i=0;i<p_dir->subdirs.size();i++) { + + String ret = _find_first_from_source(p_dir->subdirs[i],p_src); + if (ret.length()>0) + return ret; + } + + return String(); +} + + +String EditorFileSystem::find_resource_from_source(const String& p_path) const { + + + if (filesystem) + return _find_first_from_source(filesystem,p_path); + return String(); +} + void EditorFileSystem::update_file(const String& p_file) { EditorFileSystemDirectory *fs=NULL; diff --git a/tools/editor/editor_file_system.h b/tools/editor/editor_file_system.h index c26fc02548..2d14f9012f 100644 --- a/tools/editor/editor_file_system.h +++ b/tools/editor/editor_file_system.h @@ -75,6 +75,7 @@ class EditorFileSystemDirectory : public Object { static void _bind_methods(); + friend class EditorFileSystem; public: @@ -180,6 +181,7 @@ class EditorFileSystem : public Node { List<String> sources_changed; static void _resource_saved(const String& p_path); + String _find_first_from_source(EditorFileSystemDirectory* p_dir,const String &p_src) const; protected: @@ -197,6 +199,7 @@ public: void scan_sources(); void get_changed_sources(List<String> *r_changed); void update_file(const String& p_file); + String find_resource_from_source(const String& p_path) const; EditorFileSystemDirectory *get_path(const String& p_path); String get_file_type(const String& p_file) const; EditorFileSystem(); diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp index 3fb45d1870..8fe7010760 100644 --- a/tools/editor/io_plugins/editor_import_collada.cpp +++ b/tools/editor/io_plugins/editor_import_collada.cpp @@ -1844,12 +1844,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones anim_length=collada.state.animation_clips[p_clip].end; while(f<anim_length) { - if (f>=anim_length) - f=anim_length; base_snapshots.push_back(f); f+=snapshot_interval; + + if (f>=anim_length) { + base_snapshots.push_back(anim_length); + } } + //print_line("anim len: "+rtos(anim_length)); animation->set_length(anim_length); @@ -1894,6 +1897,8 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones for(int i=0;i<at.keys.size();i++) snapshots.push_back(at.keys[i].time); + print_line("using anim snapshots"); + } @@ -2185,8 +2190,6 @@ Node* EditorSceneImporterCollada::import_scene(const String& p_path, uint32_t p_ else name=state.animations[i]->get_name(); - if (p_flags&IMPORT_ANIMATION_OPTIMIZE) - state.animations[i]->optimize(); if (p_flags&IMPORT_ANIMATION_DETECT_LOOP) { if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) { @@ -2232,8 +2235,6 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String& p_path } } - if (p_flags&IMPORT_ANIMATION_OPTIMIZE) - anim->optimize(); return anim; } diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.cpp b/tools/editor/io_plugins/editor_scene_import_plugin.cpp index f305564622..06780e4d8a 100644 --- a/tools/editor/io_plugins/editor_scene_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_scene_import_plugin.cpp @@ -80,17 +80,25 @@ class EditorImportAnimationOptions : public VBoxContainer { TreeItem *fps; + TreeItem* optimize_linear_error; + TreeItem* optimize_angular_error; + TreeItem* optimize_max_angle; + TreeItem *clips_base; + TextEdit *filters; Vector<TreeItem*> clips; Tree *flags; Tree *clips_tree; + Tree *optimization_tree; Vector<TreeItem*> items; bool updating; bool validating; + + void _changed(); void _item_edited(); void _button_action(Object *p_obj,int p_col,int p_id); @@ -107,6 +115,15 @@ public: void set_fps(int p_fps); int get_fps() const; + void set_optimize_linear_error(float p_error); + float get_optimize_linear_error() const; + + void set_optimize_angular_error(float p_error); + float get_optimize_angular_error() const; + + void set_optimize_max_angle(float p_error); + float get_optimize_max_angle() const; + void setup_clips(const Array& p_clips); Array get_clips() const; @@ -453,9 +470,41 @@ EditorImportAnimationOptions::EditorImportAnimationOptions() { fps = flags->create_item(fps_base); fps->set_cell_mode(0,TreeItem::CELL_MODE_RANGE); fps->set_editable(0,true); - fps->set_range(0,15); fps->set_range_config(0,1,120,1); + fps->set_range(0,15); + optimization_tree = memnew( Tree ); + optimization_tree->set_columns(2); + tab->add_child(optimization_tree); + optimization_tree->set_name("Optimizer"); + optimization_tree->set_column_expand(0,true); + optimization_tree->set_column_expand(1,false); + optimization_tree->set_column_min_width(1,80); + optimization_tree->set_hide_root(true); + + + TreeItem *optimize_root = optimization_tree->create_item(); + + optimize_linear_error = optimization_tree->create_item(optimize_root); + optimize_linear_error->set_text(0,"Max Linear Error"); + optimize_linear_error->set_cell_mode(1,TreeItem::CELL_MODE_RANGE); + optimize_linear_error->set_editable(1,true); + optimize_linear_error->set_range_config(1,0,1,0.001); + optimize_linear_error->set_range(1,0.05); + + optimize_angular_error = optimization_tree->create_item(optimize_root); + optimize_angular_error->set_text(0,"Max Angular Error"); + optimize_angular_error->set_cell_mode(1,TreeItem::CELL_MODE_RANGE); + optimize_angular_error->set_editable(1,true); + optimize_angular_error->set_range_config(1,0,1,0.001); + optimize_angular_error->set_range(1,0.01); + + optimize_max_angle = optimization_tree->create_item(optimize_root); + optimize_max_angle->set_text(0,"Max Angle"); + optimize_max_angle->set_cell_mode(1,TreeItem::CELL_MODE_RANGE); + optimize_max_angle->set_editable(1,true); + optimize_max_angle->set_range_config(1,0,360,0.001); + optimize_max_angle->set_range(1,int(180*0.125)); clips_tree = memnew( Tree ); clips_tree->set_hide_root(true); @@ -498,6 +547,38 @@ int EditorImportAnimationOptions::get_fps() const { return fps->get_range(0); } + +void EditorImportAnimationOptions::set_optimize_linear_error(float p_optimize_linear_error) { + + optimize_linear_error->set_range(1,p_optimize_linear_error); +} + +float EditorImportAnimationOptions::get_optimize_linear_error() const { + + return optimize_linear_error->get_range(1); +} + +void EditorImportAnimationOptions::set_optimize_angular_error(float p_optimize_angular_error) { + + optimize_angular_error->set_range(1,p_optimize_angular_error); +} + +float EditorImportAnimationOptions::get_optimize_angular_error() const { + + return optimize_angular_error->get_range(1); +} + +void EditorImportAnimationOptions::set_optimize_max_angle(float p_optimize_max_angle) { + + optimize_max_angle->set_range(1,p_optimize_max_angle); +} + +float EditorImportAnimationOptions::get_optimize_max_angle() const { + + return optimize_max_angle->get_range(1); +} + + void EditorImportAnimationOptions::set_filter(const String& p_filter) { filters->set_text(p_filter); @@ -544,6 +625,17 @@ void EditorSceneImportDialog::_choose_file(const String& p_path) { } #endif + if (p_path!=String()) { + + String from_path = EditorFileSystem::get_singleton()->find_resource_from_source(EditorImportPlugin::validate_source_path(p_path)); + print_line("from path.."+from_path); + if (from_path!=String()) { + popup_import(from_path); + + } + } + + import_path->set_text(p_path); } @@ -650,6 +742,9 @@ void EditorSceneImportDialog::_import(bool p_and_open) { rim->set_option("texture_quality",texture_options->get_quality()); rim->set_option("animation_flags",animation_options->get_flags()); rim->set_option("animation_bake_fps",animation_options->get_fps()); + rim->set_option("animation_optimizer_linear_error",animation_options->get_optimize_linear_error()); + rim->set_option("animation_optimizer_angular_error",animation_options->get_optimize_angular_error()); + rim->set_option("animation_optimizer_max_angle",animation_options->get_optimize_max_angle()); rim->set_option("animation_filters",animation_options->get_filter()); rim->set_option("animation_clips",animation_options->get_clips()); rim->set_option("post_import_script",script_path->get_text()!=String()?EditorImportPlugin::validate_source_path(script_path->get_text()):String()); @@ -791,6 +886,13 @@ void EditorSceneImportDialog::popup_import(const String &p_from) { animation_options->set_filter(rimd->get_option("animation_filters")); if (rimd->has_option("animation_bake_fps")) animation_options->set_fps(rimd->get_option("animation_bake_fps")); + if (rimd->has_option("animation_optimizer_linear_error")) + animation_options->set_optimize_linear_error(rimd->get_option("animation_optimizer_linear_error")); + if (rimd->has_option("animation_optimizer_angular_error")) + animation_options->set_optimize_angular_error(rimd->get_option("animation_optimizer_angular_error")); + if (rimd->has_option("animation_optimizer_max_angle")) + animation_options->set_optimize_max_angle(rimd->get_option("animation_optimizer_max_angle")); + script_path->set_text(rimd->get_option("post_import_script")); if (rimd->has_option("import_this_time")) this_import->select(rimd->get_option("import_this_time")); @@ -2223,6 +2325,8 @@ Error EditorSceneImportPlugin::import1(const Ref<ResourceImportMetadata>& p_from int fps = 24; if (from->has_option("animation_bake_fps")) fps=from->get_option("animation_bake_fps"); + + Array clips; if (from->has_option("animation_clips")) clips=from->get_option("animation_clips"); @@ -2503,6 +2607,26 @@ void EditorSceneImportPlugin::_filter_tracks(Node *scene, const String& p_text) } +void EditorSceneImportPlugin::_optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle) { + + if (!scene->has_node(String("AnimationPlayer"))) + return; + Node* n = scene->get_node(String("AnimationPlayer")); + ERR_FAIL_COND(!n); + AnimationPlayer *anim = n->cast_to<AnimationPlayer>(); + ERR_FAIL_COND(!anim); + + + List<StringName> anim_names; + anim->get_animation_list(&anim_names); + for(List<StringName>::Element *E=anim_names.front();E;E=E->next()) { + + Ref<Animation> a = anim->get_animation(E->get()); + a->optimize(p_max_lin_error,p_max_ang_error,Math::deg2rad(p_max_angle)); + } +} + + Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, const Ref<ResourceImportMetadata>& p_from) { Error err=OK; @@ -2512,6 +2636,16 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c Array animation_clips = p_from->get_option("animation_clips"); String animation_filter = p_from->get_option("animation_filters"); int scene_flags = from->get_option("flags"); + float anim_optimizer_linerr=0.05; + float anim_optimizer_angerr=0.01; + float anim_optimizer_maxang=22; + + if (from->has_option("animation_optimizer_linear_error")) + anim_optimizer_linerr=from->get_option("animation_optimizer_linear_error"); + if (from->has_option("animation_optimizer_angular_error")) + anim_optimizer_angerr=from->get_option("animation_optimizer_angular_error"); + if (from->has_option("animation_optimizer_max_angle")) + anim_optimizer_maxang=from->get_option("animation_optimizer_max_angle"); EditorProgress progress("import","Import Scene",104); progress.step("Importing Scene..",2); @@ -2536,6 +2670,8 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c Map< Ref<ImageTexture>,TextureRole > imagemap; scene=_fix_node(scene,scene,collision_map,scene_flags,imagemap); + if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE) + _optimize_animations(scene,anim_optimizer_linerr,anim_optimizer_angerr,anim_optimizer_maxang); if (animation_clips.size()) _create_clips(scene,animation_clips,animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS); diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.h b/tools/editor/io_plugins/editor_scene_import_plugin.h index c56be57aed..fa4730f7ee 100644 --- a/tools/editor/io_plugins/editor_scene_import_plugin.h +++ b/tools/editor/io_plugins/editor_scene_import_plugin.h @@ -114,6 +114,7 @@ class EditorSceneImportPlugin : public EditorImportPlugin { void _merge_existing_node(Node *p_node,Node *p_imported_scene,Set<Ref<Resource> >& checked_resources,Set<Node*> &checked_nodes); void _add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Set<Node*> &checked_nodes); + void _optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle); void _merge_scenes(Node *p_node, Node *p_imported); void _scan_materials(Node*p_base,Node *p_node,Map<String,Ref<Material> > &mesh_materials,Map<String,Ref<Material> >& override_materials); diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp index 2cb907f1c3..c0d773309c 100644 --- a/tools/editor/plugins/script_editor_plugin.cpp +++ b/tools/editor/plugins/script_editor_plugin.cpp @@ -237,7 +237,7 @@ void ScriptTextEditor::_load_theme_settings() { //colorize engine types Color type_color= EDITOR_DEF("text_editor/engine_type_color",Color(0.0,0.2,0.4)); - List<String> types; + List<String> types; ObjectTypeDB::get_type_list(&types); for(List<String>::Element *E=types.front();E;E=E->next()) { @@ -661,13 +661,10 @@ void ScriptEditor::_menu_option(int p_option) { } break; case EDIT_UNDO: { - - current->get_text_edit()->undo(); } break; case EDIT_REDO: { current->get_text_edit()->redo(); - } break; case EDIT_CUT: { @@ -686,6 +683,163 @@ void ScriptEditor::_menu_option(int p_option) { current->get_text_edit()->select_all(); } break; + case EDIT_MOVE_LINE_UP: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + int line_id = tx->cursor_get_line(); + int next_id = line_id - 1; + + if (line_id == 0 || next_id < 0) + return; + + String tmp = tx->get_line(line_id); + String tmp2 = tx->get_line(next_id); + tx->set_line(next_id, tmp); + tx->set_line(line_id, tmp2); + tx->update(); + tx->cursor_set_line(next_id); + + } break; + case EDIT_MOVE_LINE_DOWN: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + int line_id = tx->cursor_get_line(); + int next_id = line_id + 1; + + if (line_id == tx->get_line_count() || next_id > tx->get_line_count()) + return; + + String tmp = tx->get_line(line_id); + String tmp2 = tx->get_line(next_id); + tx->set_line(next_id, tmp); + tx->set_line(line_id, tmp2); + tx->update(); + tx->cursor_set_line(next_id); + + } break; + case EDIT_INDENT_LEFT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + int begin, end; + begin = tx->get_selection_from_line(); + if (tx->is_selection_active()) + { + end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + line_text = line_text.substr(1, line_text.length()); + tx->set_line(i, line_text); + } + } + else + { + begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + line_text = line_text.substr(1, line_text.length()); + tx->set_line(begin, line_text); + } + tx->update(); + tx->deselect(); + + } break; + case EDIT_INDENT_RIGHT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + int begin, end; + begin = tx->get_selection_from_line(); + if (tx->is_selection_active()) + { + end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + line_text = '\t' + line_text; + tx->set_line(i, line_text); + } + } + else + { + begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + line_text = '\t' + line_text; + tx->set_line(begin, line_text); + } + tx->update(); + tx->deselect(); + + } break; + case EDIT_CLONE_DOWN: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + int line = tx->cursor_get_line(); + int next_line = line + 1; + + if (line == tx->get_line_count() || next_line > tx->get_line_count()) + return; + + String line_clone = tx->get_line(line); + tx->insert_at(line_clone, next_line); + tx->update(); + + } break; + case EDIT_TOGGLE_COMMENT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + int begin, end; + begin = tx->get_selection_from_line(); + if (tx->is_selection_active()) + { + end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + + if (line_text.begins_with("#")) + line_text = line_text.strip_edges().substr(1, line_text.length()); + else + line_text = "#" + line_text; + tx->set_line(i, line_text); + } + } + else + { + begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + + if (line_text.begins_with("#")) + line_text = line_text.strip_edges().substr(1, line_text.length()); + else + line_text = "#" + line_text; + tx->set_line(begin, line_text); + } + tx->update(); + tx->deselect(); + + } break; case EDIT_COMPLETE: { current->get_text_edit()->query_code_comple(); @@ -1335,14 +1489,21 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { edit_menu = memnew( MenuButton ); menu_hb->add_child(edit_menu); edit_menu->set_text("Edit"); - edit_menu->get_popup()->add_item("Undo"); - edit_menu->get_popup()->add_item("Redo"); + edit_menu->get_popup()->add_item("Undo",EDIT_UNDO,KEY_MASK_CMD|KEY_Z); + edit_menu->get_popup()->add_item("Redo",EDIT_REDO,KEY_MASK_CMD|KEY_Y); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_item("Cut",EDIT_CUT,KEY_MASK_CMD|KEY_X); edit_menu->get_popup()->add_item("Copy",EDIT_COPY,KEY_MASK_CMD|KEY_C); edit_menu->get_popup()->add_item("Paste",EDIT_PASTE,KEY_MASK_CMD|KEY_V); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_item("Select All",EDIT_SELECT_ALL,KEY_MASK_CMD|KEY_A); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_item("Move Line Up",EDIT_MOVE_LINE_UP,KEY_MASK_ALT|KEY_UP); + edit_menu->get_popup()->add_item("Move Line Down",EDIT_MOVE_LINE_DOWN,KEY_MASK_ALT|KEY_DOWN); + edit_menu->get_popup()->add_item("Indent Left",EDIT_INDENT_LEFT,KEY_MASK_ALT|KEY_LEFT); + edit_menu->get_popup()->add_item("Indent Right",EDIT_INDENT_RIGHT,KEY_MASK_ALT|KEY_RIGHT); + edit_menu->get_popup()->add_item("Toggle Comment",EDIT_TOGGLE_COMMENT,KEY_MASK_CMD|KEY_SLASH); + edit_menu->get_popup()->add_item("Clone Down",EDIT_CLONE_DOWN,KEY_MASK_CMD|KEY_B); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CMD|KEY_SPACE); edit_menu->get_popup()->add_item("Auto Indent",EDIT_AUTO_INDENT,KEY_MASK_CMD|KEY_I); diff --git a/tools/editor/plugins/script_editor_plugin.h b/tools/editor/plugins/script_editor_plugin.h index 69b8739d67..62508253e3 100644 --- a/tools/editor/plugins/script_editor_plugin.h +++ b/tools/editor/plugins/script_editor_plugin.h @@ -123,6 +123,12 @@ class ScriptEditor : public VBoxContainer { EDIT_SELECT_ALL, EDIT_COMPLETE, EDIT_AUTO_INDENT, + EDIT_TOGGLE_COMMENT, + EDIT_MOVE_LINE_UP, + EDIT_MOVE_LINE_DOWN, + EDIT_INDENT_RIGHT, + EDIT_INDENT_LEFT, + EDIT_CLONE_DOWN, SEARCH_FIND, SEARCH_FIND_NEXT, SEARCH_REPLACE, diff --git a/tools/export/blender25/io_scene_dae/export_dae.py b/tools/export/blender25/io_scene_dae/export_dae.py index d3337033c8..f8e64452db 100644 --- a/tools/export/blender25/io_scene_dae/export_dae.py +++ b/tools/export/blender25/io_scene_dae/export_dae.py @@ -863,6 +863,20 @@ class DaeExporter: if (node.parent.type=="ARMATURE"): armature=node.parent + if (node.data.shape_keys!=None): + sk = node.data.shape_keys + if (sk.animation_data): + print("HAS ANIM") + print("DRIVERS: "+str(len(sk.animation_data.drivers))) + for d in sk.animation_data.drivers: + if (d.driver): + for v in d.driver.variables: + for t in v.targets: + if (t.id!=None and t.id.name in self.scene.objects): + print("LINKING "+str(node)+" WITH "+str(t.id.name)) + self.armature_for_morph[node]=self.scene.objects[t.id.name] + + meshdata = self.export_mesh(node,armature) close_controller=False @@ -1339,6 +1353,7 @@ class DaeExporter: if (node.type=="MESH" and node.data!=None and (node in self.armature_for_morph) and (self.armature_for_morph[node] in allowed)): pass #all good you pass with flying colors for morphs inside of action else: + #print("fail "+str((node in self.armature_for_morph))) continue if (node.type=="MESH" and node.data!=None and node.data.shape_keys!=None and (node.data in self.mesh_cache) and len(node.data.shape_keys.key_blocks)): target = self.mesh_cache[node.data]["morph_id"] @@ -1453,11 +1468,11 @@ class DaeExporter: allowed_skeletons.append(y) y.animation_data.action=x; - + print("allowed skeletons "+str(allowed_skeletons)) print(str(x)) - tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]),allowed_skeletons) + tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]+0.5),allowed_skeletons) framelen=(1.0/self.scene.render.fps) start = x.frame_range[0]*framelen end = x.frame_range[1]*framelen @@ -1563,6 +1578,7 @@ class DaeExporter: + def save(operator, context, filepath="", use_selection=False, |