123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- PortConnector::PortConnector(View* parent) : PanelItem(parent, Size{~0, ~0}) {
- setCollapsible().setVisible(false);
- renameAction.setIcon(Icon::Application::TextEditor).setText("Rename ...").onActivate([&] { eventRename(); });
- removeAction.setIcon(Icon::Action::Remove).setText("Delete ...").onActivate([&] { eventRemove(); });
- locationLabel.setMouseCursor(MouseCursor::Hand);
- locationLabel.setFont(Font().setBold());
- locationLabel.setForegroundColor({0, 0, 240});
- locationLabel.onMousePress([&](auto button) {
- if(button == Mouse::Button::Left) eventBrowse();
- });
- peripheralList.onActivate([&] { eventActivate(); });
- peripheralList.onChange([&] { eventChange(); });
- peripheralList.onContext([&] { eventContext(); });
- importButton.setText("Import ...").onActivate([&] { eventImport(); });
- acceptButton.setText("Create").onActivate([&] { eventActivate(); });
- }
- auto PortConnector::show() -> void {
- setVisible(true);
- }
- auto PortConnector::hide() -> void {
- setVisible(false);
- port = {};
- }
- auto PortConnector::isMiaType() const -> bool {
- if(!port) return false; //should never occur
- if(port->type() == "Cartridge") return true;
- if(port->type() == "Floppy Disk") return true;
- if(port->type() == "Compact Disc") return true;
- return false;
- }
- auto PortConnector::refresh(higan::Node::Port port) -> void {
- this->port = port;
- importButton.setVisible(isMiaType());
- auto path = port->attribute("path");
- //is this the first time refreshing this port?
- if(!path) {
- //see if there's a shared icarus path for it
- if(isMiaType()) {
- path = {Path::user(), "Emulation/", port->family(), "/"};
- }
- //if not, use a local path for it
- if(!directory::exists(path)) {
- path = {emulator.system.data, port->family(), " ", port->type(), "/"};
- }
- port->setAttribute("path", path);
- }
- //if this is a user path, replace the home directory with ~ for brevity
- if(path.beginsWith(Path::user())) {
- locationLabel.setText(string{path}.trimLeft(Path::user(), 1L).prepend("~/"));
- } else {
- locationLabel.setText(path);
- }
- peripheralList.reset();
- ListViewItem item{&peripheralList};
- item.setAttribute("type", "nothing");
- item.setIcon(Icon::Action::Remove).setText("Nothing");
- if(!port->connected()) item.setSelected().setFocused();
- if(string location = {emulator.system.templates, port->type(), "/"}) {
- for(auto& name : directory::folders(location)) {
- //if(!port->supported().find(name.trimRight("/", 1L))) continue;
- ListViewItem item{&peripheralList};
- item.setAttribute("type", "template");
- item.setAttribute("location", {location, name, "/"});
- item.setAttribute("path", location);
- item.setAttribute("name", name.trimRight("/", 1L));
- item.setIcon(Icon::Action::Add).setText(name);
- }
- }
- //for(auto& name : port->supported()) {
- // ListViewItem item{&peripheralList};
- // item.setAttribute("type", "template");
- // item.setAttribute("name", name);
- // item.setIcon(Icon::Action::Add).setText(name);
- //}
- for(auto& name : directory::folders(path)) {
- ListViewItem item{&peripheralList};
- item.setAttribute("type", "peripheral");
- item.setAttribute("location", {path, name});
- item.setAttribute("path", path);
- item.setAttribute("name", name.trimRight("/", 1L));
- item.setIcon(Icon::Emblem::Folder).setText(name);
- //set a currently connected peripheral as the active item
- if(auto peripheral = port->connected()) {
- if(name == peripheral->attribute("name")) {
- item.setSelected().setFocused();
- }
- }
- }
- peripheralList.doChange();
- }
- auto PortConnector::eventImport() -> void {
- if(auto location = execute("icarus", "--system", interface->name(), "--import").output) {
- refresh(port);
- }
- }
- auto PortConnector::eventActivate() -> void {
- if(auto item = peripheralList.selected()) {
- if(!port->hotSwappable() && emulator.system.power) {
- auto response = MessageDialog()
- .setText("The peripheral currently connected to this port isn't hot-swappable.\n"
- "Removing it anyway may crash the emulated system.\n"
- "What would you like to do?")
- .setTitle("Warning").setAlignment(program).question({"Force", "Power Off", "Cancel"});
- if(response == "Cancel") return;
- if(response == "Power Off") emulator.power(false);
- }
- auto name = item.attribute("name");
- if(item.attribute("type") == "nothing") {
- port->disconnect();
- program.refreshPanelList();
- }
- else if(item.attribute("type") == "template") {
- auto label = NameDialog()
- .setTitle({"Create New ", name})
- .setAlignment(program)
- .create(item.text());
- if(!label) return;
- auto source = item.attribute("location");
- auto target = string{emulator.system.data, port->family(), " ", port->type(), "/", label, "/"};
- if(directory::exists(target)) return (void)MessageDialog()
- .setText("A directory by this name already exists.")
- .setTitle("Error").setAlignment(program).error();
- //if(directory::create(target)) {
- if(directory::copy(source, target)) {
- if(auto peripheral = port->allocate(name)) {
- peripheral->setAttribute("location", target);
- peripheral->setAttribute("name", label);
- port->connect();
- inputManager.bind(); //bind any inputs this peripheral may contain
- program.refreshPanelList();
- refresh(port);
- }
- }
- }
- else if(item.attribute("type") == "peripheral") {
- auto location = item.attribute("location");
- auto connected = emulator.connected(location);
- //treat selecting the already-connected device as a no-op that aborts the port connection dialog
- if(connected && connected == port) return;
- if(connected) return (void)MessageDialog()
- .setText({"This peripheral is already connected to another port:\n\n", connected->name()})
- .setTitle("Error").setAlignment(program).error();
- if(auto markup = file::read({location, "settings.bml"})) {
- auto node = higan::Node::unserialize(markup);
- //update the location and name here, in case the folder moved since it was last connected
- node->setAttribute("location", location);
- node->setAttribute("name", item.attribute("name"));
- if(auto peripheral = port->allocate(node->name())) {
- peripheral->copy(node);
- port->connect();
- peripheral->copy(node);
- }
- } else {
- if(auto peripheral = port->allocate()) {
- peripheral->setAttribute("location", location);
- peripheral->setAttribute("name", item.attribute("name"));
- port->connect();
- }
- }
- inputManager.bind(); //bind any inputs this peripheral may contain
- program.refreshPanelList();
- }
- }
- }
- auto PortConnector::eventChange() -> void {
- acceptButton.setVisible(false);
- acceptButton.setEnabled(true);
- if(auto item = peripheralList.selected()) {
- if(item.attribute("type") == "nothing") {
- if(!port->connected()) {
- acceptButton.setText("Disconnect").setEnabled(false).setVisible();
- } else {
- acceptButton.setText("Disconnect").setVisible();
- }
- }
- if(item.attribute("type") == "template") {
- acceptButton.setText("Create").setVisible();
- }
- if(item.attribute("type") == "peripheral") {
- auto peripheral = port->connected();
- if(peripheral && peripheral->attribute("name") == item.text()) {
- acceptButton.setText("Connect").setEnabled(false).setVisible();
- } else {
- acceptButton.setText("Connect").setVisible();
- }
- }
- }
- controlLayout.resize();
- }
- auto PortConnector::eventContext() -> void {
- if(auto item = peripheralList.selected()) {
- if(item.attribute("type") == "peripheral") {
- contextMenu.setVisible();
- }
- }
- }
- auto PortConnector::eventBrowse() -> void {
- auto path = port->attribute("path");
- if(auto location = BrowserDialog()
- .setTitle({port->name(), " Location"})
- .setPath(path ? path : Path::user())
- .setAlignment(program).selectFolder()
- ) {
- port->setAttribute("path", location);
- refresh(port);
- }
- }
- auto PortConnector::eventRename() -> void {
- if(auto item = peripheralList.selected()) {
- if(item.attribute("type") == "peripheral") {
- auto location = item.attribute("location");
- auto path = item.attribute("path");
- auto name = item.attribute("name");
- if(auto rename = NameDialog()
- .setAlignment(program)
- .rename(name)
- ) {
- if(name == rename) return;
- if(directory::exists({path, rename})) return (void)MessageDialog()
- .setText("A directory by this name already exists.")
- .setTitle("Error").setAlignment(program).error();
- if(!directory::rename({path, name}, {path, rename})) return (void)MessageDialog()
- .setText("Failed to rename directory.")
- .setTitle("Error").setAlignment(program).error();
- //the location must be updated if this peripheral is already connected ...
- if(auto connector = emulator.connected(location)) {
- if(auto connected = connector->connected()) {
- connected->setAttribute("location", {path, rename, "/"});
- connected->setAttribute("name", rename);
- //the name will be updated in the system tree, so it must be refreshed:
- program.refreshPanelList();
- }
- }
- return refresh(port);
- }
- }
- }
- }
- auto PortConnector::eventRemove() -> void {
- if(auto item = peripheralList.selected()) {
- if(item.attribute("type") == "peripheral") {
- auto location = item.attribute("location");
- auto connected = emulator.connected(location);
- if(!port->hotSwappable() && emulator.system.power && emulator.connected(location)) return (void)MessageDialog()
- .setText({"This peripheral is not hot-swappable and is already connected to a port:\n\n", connected->name()})
- .setTitle("Error").setAlignment(program).error();
- if(MessageDialog()
- .setText("Are you really sure you want to delete this peripheral?\n"
- "All data will be permanently lost!")
- .setTitle("Warning").setAlignment(program).question() == "No"
- ) return;
- //must disconnect device before removing it, as disconnect will attempt to save data to disk
- if(connected) {
- port->disconnect();
- program.refreshPanelList();
- }
- if(!directory::remove(location)) return (void)MessageDialog()
- .setText("Failed to remove directory.")
- .setTitle("Error").setAlignment(program).error();
- refresh(port);
- }
- }
- }
|