diff --git a/src/editor/main.rs b/src/editor/main.rs index 3a002812..b747eb56 100644 --- a/src/editor/main.rs +++ b/src/editor/main.rs @@ -3,17 +3,135 @@ #[macro_use] extern crate qt_extras as qt; #[macro_use] extern crate glium; +extern crate dreammaker as dm; +extern crate dmm_tools; + mod map_renderer; +use std::cell::RefCell; +use std::path::PathBuf; + +use dm::objtree::{ObjectTree, TypeRef}; + use qt::widgets; use qt::widgets::application::Application; +use qt::widgets::file_dialog::FileDialog; +use qt::widgets::tree_widget::TreeWidget; +use qt::widgets::tree_widget_item::TreeWidgetItem; use qt::core::connection::Signal; +use qt::core::slots::SlotNoArgs; +use qt::cpp_utils::static_cast_mut; +use qt::gui::key_sequence::KeySequence; + +unsafe fn show_error(message: &str) { + qt::widgets::message_box::MessageBox::critical(( + 0 as *mut qt::widgets::widget::Widget, + qstr!("Error"), + qstr!(message), + qt::core::flags::Flags::from_enum(qt::widgets::message_box::StandardButton::Ok), + )); +} + +struct State { + environment_file: Option, + objtree: Option, +} + +impl State { + fn new() -> State { + State { + environment_file: None, + objtree: None, + } + } + + unsafe fn load_env(&mut self, path: String, widget: &mut TreeWidget) { + let path = PathBuf::from(path); + + let mut preprocessor; + match dm::preprocessor::Preprocessor::new(path.clone()) { + Err(_) => return show_error(&format!("Could not open for reading:\n{}", path.display())), + Ok(pp) => preprocessor = pp, + }; + + let objtree; + match dm::parser::parse(dm::indents::IndentProcessor::new(&mut preprocessor)) { + Err(e) => { + let mut message = format!("\ + Could not parse the environment:\n\ + {}\n\n\ + This may be caused by incorrect or unusual code, but is typically a parser bug. \ + Change the code to use a more common form, or report the parsing problem.\n\ + ", path.display()); + let mut message_buf = Vec::new(); + let _ = dm::pretty_print_error(&mut message_buf, &preprocessor, &e); + message.push_str(&String::from_utf8_lossy(&message_buf[..])); + return show_error(&message); + }, + Ok(t) => objtree = t, + } + + self.environment_file = Some(path); + { + widget.clear(); + let root = objtree.root(); + for &root_child in ["area", "turf", "obj", "mob"].iter() { + let ty = root.child(root_child, &objtree).expect("builtins missing"); + + let mut root_item = TreeWidgetItem::new(()); + root_item.set_text(0, qstr!(&ty.name)); + add_children(&mut root_item, ty, &objtree); + widget.add_top_level_item(qt_own!(root_item)); + } + } + self.objtree = Some(objtree); + } +} + +unsafe fn add_children(parent: &mut TreeWidgetItem, ty: TypeRef, tree: &ObjectTree) { + let mut children = ty.children(tree); + children.sort_unstable_by(|a, b| a.name.cmp(&b.name)); + for each in children { + let mut child = TreeWidgetItem::new(()); + child.set_text(0, qstr!(&each.name)); + add_children(&mut child, each, tree); + parent.add_child(qt_own!(child)); + } +} + +macro_rules! action { + (@[$it:ident] (tip = $text:expr)) => { + $it.set_status_tip(&qstr!($text)); + }; + (@[$it:ident] (key = $(^$m:ident)* $k:ident)) => { + $it.set_shortcut(&KeySequence::new( qt::core::qt::Key::$k as i32 $(+ qt::core::qt::Modifier::$m as i32)* )); + }; + (@[$it:ident] (slot = $slot:expr)) => { + $it.signals().triggered().connect(&$slot); + }; + (@[$it:ident] $closure:block) => { + let slot = SlotNoArgs::new(|| $closure); + $it.signals().triggered().connect(&slot); + }; + ($add_to:expr, $name:expr $(, $x:tt)*) => { + let it = &mut *$add_to.add_action(qstr!($name)); + $(action!(@[it] $x);)* + } +} #[allow(unused_mut)] fn main() { + let mut state = RefCell::new(State::new()); + Application::create_and_exit(|_app| unsafe { + let mut window = widgets::main_window::MainWindow::new(); + let window_ptr = window.as_mut_ptr(); + // object tree - let mut tree_view = widgets::tree_view::TreeView::new(); + let mut tree_widget = widgets::tree_widget::TreeWidget::new(); + let tree_widget_ptr = tree_widget.as_mut_ptr(); + tree_widget.set_column_count(1); + tree_widget.set_header_hidden(true); // minimap let mut minimap_widget = qt::glium_widget::create(map_renderer::GliumTest); @@ -46,7 +164,7 @@ fn main() { // root splitter let mut splitter = widgets::splitter::Splitter::new(()); splitter.set_children_collapsible(false); - splitter.add_widget(qt_own!(tree_view)); + splitter.add_widget(qt_own!(tree_widget)); splitter.add_widget(qt_own!(h_layout_widget)); splitter.set_stretch_factor(0, 0); splitter.set_stretch_factor(1, 1); @@ -54,19 +172,29 @@ fn main() { // menus let mut menu_bar = widgets::menu_bar::MenuBar::new(); let mut menu_file = &mut *menu_bar.add_menu(qstr!("File")); - menu_file.add_action(qstr!("Open Environment")); - menu_file.add_action(qstr!("Recent Environments")); + action!(menu_file, "Open Environment", (tip = "Load a DME file."), { + let file = FileDialog::get_open_file_name_unsafe(( + static_cast_mut(window_ptr), + qstr!("Open File"), + qstr!("."), + qstr!("Environments (*.dme)"), + )).to_std_string(); + if !file.is_empty() { + state.borrow_mut().load_env(file, &mut *tree_widget_ptr); + } + }); + menu_file.add_menu(qstr!("Recent Environments")); menu_file.add_separator(); - menu_file.add_action(qstr!("New")); - menu_file.add_action(qstr!("Open")); - menu_file.add_action(qstr!("Close")); + action!(menu_file, "New", (key = ^CTRL KeyN), (tip = "Create a new map.")); + action!(menu_file, "Open", (key = ^CTRL KeyO), (tip = "Open a map.")); + action!(menu_file, "Close", (key = ^CTRL KeyW), (tip = "Close the current map.")); menu_file.add_separator(); + action!(menu_file, "Exit", (key = ^ALT KeyF4), (slot = window.slots().close())); // status bar let mut status_bar = widgets::status_bar::StatusBar::new(); // build main window - let mut window = widgets::main_window::MainWindow::new(); window.set_window_title(qstr!("SpacemanDMM")); window.resize((1400, 768)); @@ -74,9 +202,6 @@ fn main() { window.set_status_bar(qt_own!(status_bar)); window.set_central_widget(qt_own!(splitter)); - // connect signals - (*menu_file.add_action(qstr!("Exit"))).signals().triggered().connect(&window.slots().close()); - // cede control window.show(); Application::exec()