1 #ifndef NO_EDITOR
2 #include <boost/bind.hpp>
3
4 #include <algorithm>
5
6 #include "border_widget.hpp"
7 #include "button.hpp"
8 #include "code_editor_dialog.hpp"
9 #include "code_editor_widget.hpp"
10 #include "custom_object.hpp"
11 #include "custom_object_callable.hpp"
12 #include "custom_object_type.hpp"
13 #include "drag_widget.hpp"
14 #include "filesystem.hpp"
15 #include "font.hpp"
16 #include "foreach.hpp"
17 #include "formatter.hpp"
18 #include "formula_function_registry.hpp"
19 #include "frame.hpp"
20 #include "image_widget.hpp"
21 #include "json_parser.hpp"
22 #include "label.hpp"
23 #include "level.hpp"
24 #include "module.hpp"
25 #include "object_events.hpp"
26 #include "raster.hpp"
27 #include "surface_cache.hpp"
28 #include "text_editor_widget.hpp"
29 #include "tile_map.hpp"
30 #include "tileset_editor_dialog.hpp"
31 #include "unit_test.hpp"
32
33 std::set<level*>& get_all_levels_set();
34
code_editor_dialog(const rect & r)35 code_editor_dialog::code_editor_dialog(const rect& r)
36 : dialog(r.x(), r.y(), r.w(), r.h()), invalidated_(0), modified_(false),
37 suggestions_prefix_(-1)
38 {
39 init();
40 }
41
init()42 void code_editor_dialog::init()
43 {
44 clear();
45
46 using namespace gui;
47
48 if(!editor_) {
49 editor_.reset(new code_editor_widget(width() - 40, height() - 60));
50 }
51
52 button* save_button = new button("Save", boost::bind(&code_editor_dialog::save, this));
53 button* increase_font = new button("+", boost::bind(&code_editor_dialog::change_font_size, this, 1));
54 button* decrease_font = new button("-", boost::bind(&code_editor_dialog::change_font_size, this, -1));
55
56 //std::cerr << "CED: " << x() << "," << y() << "; " << width() << "," << height() << std::endl;
57 drag_widget* dragger = new drag_widget(x(), y(), width(), height(),
58 drag_widget::DRAG_HORIZONTAL, [](int, int){},
59 boost::bind(&code_editor_dialog::on_drag_end, this, _1, _2),
60 boost::bind(&code_editor_dialog::on_drag, this, _1, _2));
61
62 search_ = new text_editor_widget(120);
63 replace_ = new text_editor_widget(120);
64 const SDL_Color col = {255,255,255,255};
65 widget_ptr find_label(label::create("Find: ", col));
66 replace_label_ = label::create("Replace: ", col);
67 status_label_ = label::create("Ok", col);
68 error_label_ = label::create("", col);
69 add_widget(find_label, 42, 12, MOVE_RIGHT);
70 add_widget(widget_ptr(search_), MOVE_RIGHT);
71 add_widget(replace_label_, MOVE_RIGHT);
72 add_widget(widget_ptr(replace_), MOVE_RIGHT);
73 add_widget(widget_ptr(save_button), MOVE_RIGHT);
74 add_widget(widget_ptr(increase_font), MOVE_RIGHT);
75 add_widget(widget_ptr(decrease_font), MOVE_RIGHT);
76 add_widget(editor_, find_label->x(), find_label->y() + save_button->height() + 2);
77 add_widget(status_label_);
78 add_widget(error_label_, status_label_->x() + 480, status_label_->y());
79 add_widget(widget_ptr(dragger));
80
81 replace_label_->set_visible(false);
82 replace_->set_visible(false);
83
84 search_->set_on_tab_handler(boost::bind(&code_editor_dialog::on_tab, this));
85 replace_->set_on_tab_handler(boost::bind(&code_editor_dialog::on_tab, this));
86
87 search_->set_on_change_handler(boost::bind(&code_editor_dialog::on_search_changed, this));
88 search_->set_on_enter_handler(boost::bind(&code_editor_dialog::on_search_enter, this));
89 replace_->set_on_enter_handler(boost::bind(&code_editor_dialog::on_replace_enter, this));
90
91
92 init_files_grid();
93 }
94
init_files_grid()95 void code_editor_dialog::init_files_grid()
96 {
97 if(files_grid_) {
98 remove_widget(files_grid_);
99 }
100
101 if(files_.empty()) {
102 return;
103 }
104
105 using namespace gui;
106
107 files_grid_.reset(new grid(1));
108 files_grid_->allow_selection();
109 files_grid_->register_selection_callback(boost::bind(&code_editor_dialog::select_file, this, _1));
110 foreach(const KnownFile& f, files_) {
111 if(f.anim) {
112 image_widget* img = new image_widget(f.anim->img());
113 img->set_dim(42, 42);
114 img->set_area(f.anim->area());
115
116 files_grid_->add_col(widget_ptr(img));
117 } else {
118 std::string fname = f.fname;
119 while(std::find(fname.begin(), fname.end(), '/') != fname.end()) {
120 fname.erase(fname.begin(), std::find(fname.begin(), fname.end(), '/')+1);
121 }
122 if(fname.size() > 6) {
123 fname.resize(6);
124 }
125
126 files_grid_->add_col(label::create(fname, graphics::color_white()));
127 }
128 }
129
130 add_widget(files_grid_, 2, 2);
131 }
132
load_file(std::string fname,bool focus)133 void code_editor_dialog::load_file(std::string fname, bool focus)
134 {
135 if(fname_ == fname) {
136 return;
137 }
138
139 using namespace gui;
140
141 int index = 0;
142 foreach(const KnownFile& f, files_) {
143 if(f.fname == fname) {
144 break;
145 }
146
147 ++index;
148 }
149
150 if(index == files_.size()) {
151 KnownFile f;
152 f.fname = fname;
153 f.editor.reset(new code_editor_widget(width() - 40, height() - 60));
154 std::string text = json::get_file_contents(fname);
155 try {
156 variant doc = json::parse(text, json::JSON_NO_PREPROCESSOR);
157 std::cerr << "CHECKING FOR PROTOTYPES: " << doc["prototype"].write_json() << "\n";
158 if(doc["prototype"].is_list()) {
159 std::map<std::string,std::string> paths;
160 module::get_unique_filenames_under_dir("data/object_prototypes", &paths);
161 foreach(variant proto, doc["prototype"].as_list()) {
162 std::string name = proto.as_string() + ".cfg";
163 std::map<std::string,std::string>::const_iterator itor = module::find(paths, name);
164 if(itor != paths.end()) {
165 load_file(itor->second, false);
166 }
167 }
168 }
169 } catch(...) {
170 }
171
172 f.editor->set_text(json::get_file_contents(fname));
173 f.editor->set_on_change_handler(boost::bind(&code_editor_dialog::on_code_changed, this));
174 f.editor->set_on_move_cursor_handler(boost::bind(&code_editor_dialog::on_move_cursor, this));
175
176 foreach(const std::string& obj_type, custom_object_type::get_all_ids()) {
177 const std::string* path = custom_object_type::get_object_path(obj_type + ".cfg");
178 if(path && *path == fname) {
179 f.anim.reset(new frame(custom_object_type::get(obj_type)->default_frame()));
180 break;
181 }
182 }
183
184 //in case any files have been inserted, update the index.
185 index = files_.size();
186 files_.push_back(f);
187 }
188
189 KnownFile f = files_[index];
190
191 if(editor_) {
192 f.editor->set_font_size(editor_->get_font_size());
193 }
194
195 if(!focus) {
196 return;
197 }
198
199 files_.erase(files_.begin() + index);
200 files_.insert(files_.begin(), f);
201
202 add_widget(f.editor, editor_->x(), editor_->y());
203 remove_widget(editor_);
204
205 editor_ = f.editor;
206 editor_->set_focus(true);
207
208 init_files_grid();
209
210 fname_ = fname;
211
212 modified_ = editor_->text() != sys::read_file(module::map_file(fname));
213 on_move_cursor();
214 }
215
select_file(int index)216 void code_editor_dialog::select_file(int index)
217 {
218 if(index < 0 || index >= files_.size()) {
219 return;
220 }
221
222 std::cerr << "select file " << index << " -> " << files_[index].fname << "\n";
223
224 load_file(files_[index].fname);
225 }
226
has_keyboard_focus() const227 bool code_editor_dialog::has_keyboard_focus() const
228 {
229 return editor_->has_focus() || search_->has_focus() || replace_->has_focus();
230 }
231
handle_event(const SDL_Event & event,bool claimed)232 bool code_editor_dialog::handle_event(const SDL_Event& event, bool claimed)
233 {
234 if(animation_preview_) {
235 claimed = animation_preview_->process_event(event, claimed) || claimed;
236 if(claimed) {
237 return claimed;
238 }
239 }
240
241 if(suggestions_grid_) {
242 //since an event could cause removal of the suggestions grid,
243 //make sure the object doesn't get cleaned up until after the event.
244 gui::widget_ptr suggestions = suggestions_grid_;
245 claimed = suggestions->process_event(event, claimed) || claimed;
246 if(claimed) {
247 return claimed;
248 }
249 }
250
251 claimed = claimed || dialog::handle_event(event, claimed);
252 if(claimed) {
253 return claimed;
254 }
255
256 if(has_keyboard_focus()) {
257 switch(event.type) {
258 case SDL_KEYDOWN: {
259 if(event.key.keysym.sym == SDLK_f && (event.key.keysym.mod&KMOD_CTRL)) {
260 search_->set_focus(true);
261 replace_->set_focus(false);
262 editor_->set_focus(false);
263 return true;
264 } else if(event.key.keysym.sym == SDLK_s && (event.key.keysym.mod&KMOD_CTRL)) {
265 save();
266 return true;
267 } else if(event.key.keysym.sym == SDLK_TAB && (event.key.keysym.mod&KMOD_CTRL) && files_grid_) {
268 if(!files_grid_->has_must_select()) {
269 files_grid_->must_select(true, 1);
270 } else {
271 files_grid_->must_select(true, (files_grid_->selection()+1)%files_.size());
272 }
273 }
274 break;
275 }
276 case SDL_KEYUP: {
277 if(files_grid_ && files_grid_->has_must_select() && (event.key.keysym.sym == SDLK_LCTRL || event.key.keysym.sym == SDLK_RCTRL)) {
278 select_file(files_grid_->selection());
279 }
280 break;
281 }
282 }
283 }
284
285 return claimed;
286 }
287
handle_draw_children() const288 void code_editor_dialog::handle_draw_children() const
289 {
290 dialog::handle_draw_children();
291 if(animation_preview_) {
292 animation_preview_->draw();
293 }
294
295 if(suggestions_grid_) {
296 suggestions_grid_->draw();
297 }
298 }
299
change_font_size(int amount)300 void code_editor_dialog::change_font_size(int amount)
301 {
302 if(editor_) {
303 editor_->change_font_size(amount);
304 }
305 }
306
process()307 void code_editor_dialog::process()
308 {
309 using namespace gui;
310
311 if(invalidated_ && SDL_GetTicks() > invalidated_ + 200) {
312 try {
313 custom_object::reset_current_debug_error();
314
315 #if defined(USE_GLES2)
316 gles2::shader::get_and_clear_runtime_error();
317 #endif
318
319
320 if(strstr(fname_.c_str(), "/tiles/")) {
321 std::cerr << "INIT TILE MAP\n";
322
323 const std::string old_contents = json::get_file_contents(fname_);
324
325 //verify the text is parseable before we bother setting it.
326 json::parse(editor_->text());
327 json::set_file_contents(fname_, editor_->text());
328 const variant tiles_data = json::parse_from_file("data/tiles.cfg");
329 tile_map::prepare_rebuild_all();
330 try {
331 std::cerr << "tile_map::init()\n";
332 tile_map::init(tiles_data);
333 tile_map::rebuild_all();
334 std::cerr << "done tile_map::init()\n";
335 editor_dialogs::tileset_editor_dialog::global_tile_update();
336 foreach(level* lvl, get_all_levels_set()) {
337 lvl->rebuild_tiles();
338 }
339 } catch(...) {
340 json::set_file_contents(fname_, old_contents);
341 const variant tiles_data = json::parse_from_file("data/tiles.cfg");
342 tile_map::init(tiles_data);
343 tile_map::rebuild_all();
344 editor_dialogs::tileset_editor_dialog::global_tile_update();
345 foreach(level* lvl, get_all_levels_set()) {
346 lvl->rebuild_tiles();
347 }
348 throw;
349 }
350 std::cerr << "INIT TILE MAP OK\n";
351 #if defined(USE_GLES2)
352 } else if(strstr(fname_.c_str(), "data/shaders.cfg")) {
353 std::cerr << "CODE_EDIT_DIALOG FILE: " << fname_ << std::endl;
354 gles2::program::load_shaders(editor_->text());
355 foreach(level* lvl, get_all_levels_set()) {
356 lvl->shaders_updated();
357 }
358 #endif
359 } else {
360 std::cerr << "SET FILE: " << fname_ << "\n";
361 custom_object_type::set_file_contents(fname_, editor_->text());
362 }
363 error_label_->set_text("Ok");
364 error_label_->set_tooltip("");
365 } catch(validation_failure_exception& e) {
366 error_label_->set_text("Error");
367 error_label_->set_tooltip(e.msg);
368 } catch(...) {
369 error_label_->set_text("Error");
370 error_label_->set_tooltip("Unknown error");
371 }
372 invalidated_ = 0;
373 } else if(custom_object::current_debug_error()) {
374 error_label_->set_text("Runtime Error");
375 error_label_->set_tooltip(*custom_object::current_debug_error());
376 }
377
378 #if defined(USE_GLES2)
379 const std::string shader_error = gles2::shader::get_and_clear_runtime_error();
380 if(shader_error != "") {
381 error_label_->set_text("Runtime Shader Error");
382 error_label_->set_tooltip(shader_error);
383 }
384 #endif
385
386 const bool show_replace = editor_->has_search_matches();
387 replace_label_->set_visible(show_replace);
388 replace_->set_visible(show_replace);
389
390 const int cursor_pos = editor_->row_col_to_text_pos(editor_->cursor_row(), editor_->cursor_col());
391 const std::string& text = editor_->current_text();
392
393 const gui::code_editor_widget::ObjectInfo info = editor_->get_current_object();
394 const json::Token* selected_token = NULL;
395 int token_pos = 0;
396 foreach(const json::Token& token, info.tokens) {
397 const int begin_pos = token.begin - text.c_str();
398 const int end_pos = token.end - text.c_str();
399 if(cursor_pos >= begin_pos && cursor_pos <= end_pos) {
400 token_pos = cursor_pos - begin_pos;
401 selected_token = &token;
402 break;
403 }
404 }
405
406 std::vector<Suggestion> suggestions;
407 if(selected_token != NULL) {
408
409 const bool at_end = token_pos == selected_token->end - selected_token->begin;
410 std::string str(selected_token->begin, selected_token->end);
411 suggestions_prefix_ = 0;
412 if(str.size() >= 3 && std::string(str.begin(), str.begin()+3) == "on_" && at_end) {
413 const std::string id(str.begin()+3, str.end());
414 for(int i = 0; i != NUM_OBJECT_BUILTIN_EVENT_IDS; ++i) {
415 const std::string& event_str = get_object_event_str(i);
416 if(event_str.size() >= id.size() && std::equal(id.begin(), id.end(), event_str.begin())) {
417 Suggestion s = { "on_" + event_str, "", ": \"\",", 3 };
418 suggestions.push_back(s);
419 }
420 }
421
422 static std::vector<std::string> animations;
423
424 if(info.obj.is_map() && info.obj["animation"].is_list()) {
425 animations.clear();
426 foreach(variant anim, info.obj["animation"].as_list()) {
427 if(anim.is_map() && anim["id"].is_string()) {
428 animations.push_back(anim["id"].as_string());
429 }
430 }
431 }
432
433 foreach(const std::string& str, animations) {
434 static const std::string types[] = {"enter", "end", "leave", "process"};
435 foreach(const std::string& type, types) {
436 const std::string event_str = type + "_" + str + (type == "process" ? "" : "_anim");
437 if(event_str.size() >= id.size() && std::equal(id.begin(), id.end(), event_str.begin())) {
438 Suggestion s = { "on_" + event_str, "", ": \"\",", 3 };
439 suggestions.push_back(s);
440 }
441 }
442 }
443
444 suggestions_prefix_ = str.size();
445 } else if(selected_token->type == json::Token::TYPE_STRING) {
446 try {
447 const std::string formula_str(selected_token->begin, selected_token->end);
448 std::vector<formula_tokenizer::token> tokens;
449 std::string::const_iterator i1 = formula_str.begin();
450 formula_tokenizer::token t = formula_tokenizer::get_token(i1, formula_str.end());
451 while(t.type != formula_tokenizer::TOKEN_INVALID) {
452 tokens.push_back(t);
453 if(i1 == formula_str.end()) {
454 break;
455 }
456
457 t = formula_tokenizer::get_token(i1, formula_str.end());
458 }
459
460 const formula_tokenizer::token* selected = NULL;
461 const std::string::const_iterator itor = formula_str.begin() + token_pos;
462
463 foreach(const formula_tokenizer::token& tok, tokens) {
464 if(tok.end == itor) {
465 selected = &tok;
466 break;
467 }
468 }
469
470 if(selected && selected->type == formula_tokenizer::TOKEN_IDENTIFIER) {
471 const std::string identifier(selected->begin, selected->end);
472
473 static const custom_object_callable obj_definition;
474 for(int n = 0; n != obj_definition.num_slots(); ++n) {
475 const std::string id = obj_definition.get_entry(n)->id;
476 if(id.size() > identifier.size() && std::equal(identifier.begin(), identifier.end(), id.begin())) {
477 Suggestion s = { id, "", "", 0 };
478 suggestions.push_back(s);
479 }
480 }
481
482 std::vector<std::string> helpstrings;
483 foreach(const std::string& s, function_helpstrings("core")) {
484 helpstrings.push_back(s);
485 }
486 foreach(const std::string& s, function_helpstrings("custom_object")) {
487 helpstrings.push_back(s);
488 }
489
490 foreach(const std::string& str, helpstrings) {
491 std::string::const_iterator paren = std::find(str.begin(), str.end(), '(');
492 std::string::const_iterator colon = std::find(paren, str.end(), ':');
493 if(colon == str.end()) {
494 continue;
495 }
496
497 const std::string id(str.begin(), paren);
498 const std::string text(str.begin(), colon);
499 if(id.size() > identifier.size() && std::equal(identifier.begin(), identifier.end(), id.begin())) {
500 Suggestion s = { id, text, "()", 1 };
501 suggestions.push_back(s);
502 }
503 }
504
505 suggestions_prefix_ = identifier.size();
506 }
507 } catch(formula_tokenizer::token_error&) {
508 }
509 }
510
511 }
512
513 std::sort(suggestions.begin(), suggestions.end());
514
515 if(suggestions != suggestions_) {
516 suggestions_ = suggestions;
517 suggestions_grid_.reset();
518
519 if(suggestions_.empty() == false) {
520 grid_ptr suggestions_grid(new grid(1));
521 suggestions_grid->register_selection_callback(boost::bind(&code_editor_dialog::select_suggestion, this, _1));
522 suggestions_grid->swallow_clicks();
523 suggestions_grid->allow_selection(true);
524 suggestions_grid->set_show_background(true);
525 suggestions_grid->set_max_height(160);
526 foreach(const Suggestion& s, suggestions_) {
527 suggestions_grid->add_col(widget_ptr(new label(s.suggestion_text.empty() ? s.suggestion : s.suggestion_text)));
528 }
529
530 suggestions_grid_.reset(new border_widget(suggestions_grid, graphics::color(255,255,255,255)));
531 }
532 std::cerr << "SUGGESTIONS: " << suggestions_.size() << ":\n";
533 foreach(const Suggestion& suggestion, suggestions_) {
534 std::cerr << " - " << suggestion.suggestion << "\n";
535 }
536 }
537
538 if(suggestions_grid_) {
539 const std::pair<int,int> cursor_pos = editor_->char_position_on_screen(editor_->cursor_row(), editor_->cursor_col());
540 suggestions_grid_->set_loc(x() + editor_->x() + cursor_pos.second, y() + editor_->y() + cursor_pos.first - suggestions_grid_->height());
541
542 if(suggestions_grid_->y() < 10) {
543 suggestions_grid_->set_loc(suggestions_grid_->x(), suggestions_grid_->y() + suggestions_grid_->height() + 14);
544 }
545
546 if(suggestions_grid_->x() + suggestions_grid_->width() + 20 > graphics::screen_width()) {
547 suggestions_grid_->set_loc(graphics::screen_width() - suggestions_grid_->width() - 20, suggestions_grid_->y());
548 }
549 }
550
551 try {
552 editor_->set_highlight_current_object(false);
553 if(gui::animation_preview_widget::is_animation(info.obj)) {
554 if(!animation_preview_) {
555 animation_preview_.reset(new gui::animation_preview_widget(info.obj));
556 animation_preview_->set_rect_handler(boost::bind(&code_editor_dialog::set_animation_rect, this, _1));
557 animation_preview_->set_solid_handler(boost::bind(&code_editor_dialog::move_solid_rect, this, _1, _2));
558 animation_preview_->set_pad_handler(boost::bind(&code_editor_dialog::set_integer_attr, this, "pad", _1));
559 animation_preview_->set_num_frames_handler(boost::bind(&code_editor_dialog::set_integer_attr, this, "frames", _1));
560 animation_preview_->set_frames_per_row_handler(boost::bind(&code_editor_dialog::set_integer_attr, this, "frames_per_row", _1));
561 animation_preview_->set_loc(x() - 520, y() + 100);
562 animation_preview_->set_dim(500, 400);
563 animation_preview_->init();
564 } else {
565 animation_preview_->set_object(info.obj);
566 }
567
568 editor_->set_highlight_current_object(true);
569 } else {
570 animation_preview_.reset();
571 }
572 } catch(type_error&) {
573 if(animation_preview_) {
574 animation_preview_.reset();
575 }
576 } catch(frame::error&) {
577 // skip
578 } catch(validation_failure_exception&) {
579 if(animation_preview_) {
580 animation_preview_.reset();
581 }
582 } catch(graphics::load_image_error&) {
583 if(animation_preview_) {
584 animation_preview_.reset();
585 }
586 }
587
588 if(animation_preview_) {
589 animation_preview_->process();
590 }
591
592 }
593
change_width(int amount)594 void code_editor_dialog::change_width(int amount)
595 {
596 int new_width = width() + amount;
597 if(new_width < 200) {
598 new_width = 200;
599 }
600
601 if(new_width > 1000) {
602 new_width = 1000;
603 }
604
605 amount = new_width - width();
606 set_loc(x() - amount, y());
607 set_dim(new_width, height());
608
609
610 foreach(KnownFile& f, files_) {
611 f.editor->set_dim(width() - 40, height() - 60);
612 }
613 init();
614 }
615
on_drag(int dx,int dy)616 void code_editor_dialog::on_drag(int dx, int dy)
617 {
618 int new_width = width() + dx;
619 int min_width = int(graphics::screen_width() * 0.17);
620 int max_width = int(graphics::screen_width() * 0.83);
621 //std::cerr << "ON_DRAG: " << dx << ", " << min_width << ", " << max_width << std::endl;
622 if(new_width < min_width) {
623 new_width = min_width;
624 }
625
626 if(new_width > max_width) {
627 new_width = max_width;
628 }
629
630 dx = new_width - width();
631 set_loc(x() - dx, y());
632 set_dim(new_width, height());
633
634
635 foreach(KnownFile& f, files_) {
636 f.editor->set_dim(width() - 40, height() - 60);
637 }
638 //init();
639 }
640
on_drag_end(int x,int y)641 void code_editor_dialog::on_drag_end(int x, int y)
642 {
643 init();
644 }
645
on_tab()646 void code_editor_dialog::on_tab()
647 {
648 if(search_->has_focus()) {
649 search_->set_focus(false);
650 if(editor_->has_search_matches()) {
651 replace_->set_focus(true);
652 } else {
653 editor_->set_focus(true);
654 }
655 } else if(replace_->has_focus()) {
656 replace_->set_focus(false);
657 editor_->set_focus(true);
658 }
659 }
660
on_search_changed()661 void code_editor_dialog::on_search_changed()
662 {
663 editor_->set_search(search_->text());
664 }
665
on_search_enter()666 void code_editor_dialog::on_search_enter()
667 {
668 editor_->next_search_match();
669 }
670
on_replace_enter()671 void code_editor_dialog::on_replace_enter()
672 {
673 editor_->replace(replace_->text());
674 }
675
on_code_changed()676 void code_editor_dialog::on_code_changed()
677 {
678 if(!modified_) {
679 modified_ = true;
680 on_move_cursor();
681 }
682
683 if(!invalidated_) {
684 invalidated_ = SDL_GetTicks();
685 error_label_->set_text("Processing...");
686 }
687 }
688
on_move_cursor()689 void code_editor_dialog::on_move_cursor()
690 {
691 status_label_->set_text(formatter() << "Line " << (editor_->cursor_row()+1) << " Col " << (editor_->cursor_col()+1) << (modified_ ? " (Modified)" : ""));
692 }
693
set_animation_rect(rect r)694 void code_editor_dialog::set_animation_rect(rect r)
695 {
696 const gui::code_editor_widget::ObjectInfo info = editor_->get_current_object();
697 variant v = info.obj;
698 if(v.is_null() == false) {
699 v.add_attr(variant("rect"), r.write());
700 editor_->modify_current_object(v);
701 try {
702 animation_preview_->set_object(v);
703 } catch(frame::error& e) {
704 }
705 }
706 }
707
move_solid_rect(int dx,int dy)708 void code_editor_dialog::move_solid_rect(int dx, int dy)
709 {
710 const gui::code_editor_widget::ObjectInfo info = editor_->get_current_object();
711 variant v = info.obj;
712 if(v.is_null() == false) {
713 variant solid_area = v["solid_area"];
714 if(!solid_area.is_list() || solid_area.num_elements() != 4) {
715 return;
716 }
717
718 foreach(const variant& num, solid_area.as_list()) {
719 if(!num.is_int()) {
720 return;
721 }
722 }
723
724 rect area(solid_area);
725 area = rect(area.x() + dx, area.y() + dy, area.w(), area.h());
726 v.add_attr(variant("solid_area"), area.write());
727 editor_->modify_current_object(v);
728 try {
729 animation_preview_->set_object(v);
730 } catch(frame::error& e) {
731 }
732 }
733 }
734
set_integer_attr(const char * attr,int value)735 void code_editor_dialog::set_integer_attr(const char* attr, int value)
736 {
737 const gui::code_editor_widget::ObjectInfo info = editor_->get_current_object();
738 variant v = info.obj;
739 if(v.is_null() == false) {
740 v.add_attr(variant(attr), variant(value));
741 editor_->modify_current_object(v);
742 try {
743 animation_preview_->set_object(v);
744 } catch(frame::error& e) {
745 }
746 }
747 }
748
save()749 void code_editor_dialog::save()
750 {
751 sys::write_file(module::map_file(fname_), editor_->text());
752 status_label_->set_text(formatter() << "Saved " << fname_);
753 modified_ = false;
754 }
755
select_suggestion(int index)756 void code_editor_dialog::select_suggestion(int index)
757 {
758 if(index >= 0 && index < suggestions_.size()) {
759 std::cerr << "SELECT " << suggestions_[index].suggestion << "\n";
760 const std::string& str = suggestions_[index].suggestion;
761 if(suggestions_prefix_ >= 0 && suggestions_prefix_ < str.size()) {
762 const int col = editor_->cursor_col();
763 const std::string insert(str.begin() + suggestions_prefix_, str.end());
764 const std::string postfix = suggestions_[index].postfix;
765 const std::string row = editor_->get_data()[editor_->cursor_row()];
766 const std::string new_row = std::string(row.begin(), row.begin() + editor_->cursor_col()) + insert + postfix + std::string(row.begin() + editor_->cursor_col(), row.end());
767 editor_->set_row_contents(editor_->cursor_row(), new_row);
768 editor_->set_cursor(editor_->cursor_row(), col + insert.size() + suggestions_[index].postfix_index);
769 }
770 } else {
771 suggestions_grid_.reset();
772 }
773 }
774
COMMAND_LINE_UTILITY(codeedit)775 COMMAND_LINE_UTILITY(codeedit)
776 {
777 SDL_Init(SDL_INIT_VIDEO);
778 SDL_SetVideoMode(600, 600, 0, SDL_OPENGL|SDL_RESIZABLE);
779 #ifdef USE_GLES2
780 glViewport(0, 0, 600, 600);
781 glEnable(GL_BLEND);
782 glEnable(GL_TEXTURE_2D);
783 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
784 #else
785 glShadeModel(GL_SMOOTH);
786 glEnable(GL_BLEND);
787 glEnable(GL_TEXTURE_2D);
788 glEnableClientState(GL_VERTEX_ARRAY);
789 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
790 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
791 #endif
792
793 const font::manager font_manager;
794 graphics::texture::manager texture_manager;
795
796 variant gui_node = json::parse_from_file("data/gui.cfg");
797 gui_section::init(gui_node);
798
799 framed_gui_element::init(gui_node);
800
801 code_editor_dialog d(rect(0,0,600,600));
802 std::cerr << "CREATE DIALOG\n";
803 if(args.empty() == false) {
804 d.load_file(args[0]);
805 }
806
807 d.show_modal();
808
809
810 SDL_Quit();
811 }
812
813 #endif // NO_EDITOR
814