|
| 1 | +#include <gtkmm.h> |
| 2 | +#include <glibmm.h> |
| 3 | +#include <iostream> |
| 4 | +#include <thread> |
| 5 | +#include <string> |
| 6 | +#include <sigc++/sigc++.h> |
| 7 | +#include "../lib/ArduinoInterface.h" |
| 8 | +#include "../lib/ADFWriter.h" |
| 9 | + |
| 10 | +class MainWindow : public Gtk::ApplicationWindow |
| 11 | +{ |
| 12 | +public: |
| 13 | + MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &builder) |
| 14 | + : Gtk::ApplicationWindow(obj), builder{builder} |
| 15 | + { |
| 16 | + std::vector<std::wstring> portList; |
| 17 | + ArduinoFloppyReader::ArduinoInterface::enumeratePorts(portList); |
| 18 | + Gtk::ComboBoxText *portsCombo = nullptr; |
| 19 | + builder->get_widget("serialPortsCombo", portsCombo); |
| 20 | + for (std::wstring port : portList) |
| 21 | + { |
| 22 | + const std::string portString(port.begin(), port.end()); |
| 23 | + Glib::ustring text = Glib::ustring(portString.c_str(), port.size()); |
| 24 | + portsCombo->append(text); |
| 25 | + } |
| 26 | + Gtk::Button *diagnosticsButton = nullptr; |
| 27 | + builder->get_widget("diagnosticsButton", diagnosticsButton); |
| 28 | + if (portList.size() > 0) |
| 29 | + { |
| 30 | + portsCombo->set_active(0); |
| 31 | + diagnosticsButton->set_sensitive(true); |
| 32 | + } |
| 33 | + diagnosticsButton->signal_clicked().connect([this, portsCombo]() |
| 34 | + { |
| 35 | + Glib::ustring serial = portsCombo->get_active_text(); |
| 36 | + run_diagnostics(serial); |
| 37 | + }); |
| 38 | + Gtk::Button *copyButton = nullptr; |
| 39 | + builder->get_widget("copyButton", copyButton); |
| 40 | + Gtk::Label *cylinderCount = nullptr; |
| 41 | + builder->get_widget("cylinderCount", cylinderCount); |
| 42 | + Gtk::Label *sideLabel = nullptr; |
| 43 | + builder->get_widget("sideLabel", sideLabel); |
| 44 | + Gtk::Label *goodCount = nullptr; |
| 45 | + builder->get_widget("goodCount", goodCount); |
| 46 | + Gtk::Label *partialCount = nullptr; |
| 47 | + builder->get_widget("partialCount", partialCount); |
| 48 | + |
| 49 | + auto callback = [this, cylinderCount, sideLabel, goodCount, partialCount](const int currentTrack, const ArduinoFloppyReader::DiskSurface currentSide, const int retryCounter, const int sectorsFound, const int badSectorsFound) -> ArduinoFloppyReader::WriteResponse |
| 50 | + { |
| 51 | + std::string side = (currentSide == ArduinoFloppyReader::DiskSurface::dsUpper ? "Upper" : "Lower"); |
| 52 | + std::cout << "currentTrack: " << currentTrack << " side: " << side << "retryCount: " << retryCounter << std::endl; |
| 53 | + cylinderCount->set_text(Glib::ustring::sprintf("%i", currentTrack)); |
| 54 | + goodCount->set_text(Glib::ustring::sprintf("%i", sectorsFound)); |
| 55 | + partialCount->set_text(Glib::ustring::sprintf("%i", badSectorsFound)); |
| 56 | + sideLabel->set_text(side); |
| 57 | + while (gtk_events_pending()) |
| 58 | + gtk_main_iteration(); |
| 59 | + |
| 60 | + if (retryCounter > 20) |
| 61 | + { |
| 62 | + // Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Aborted", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK); |
| 63 | + // dialog.run(); |
| 64 | + // dialog.hide(); |
| 65 | + |
| 66 | + return ArduinoFloppyReader::WriteResponse::wrSkipBadChecksums; |
| 67 | + } |
| 68 | + |
| 69 | + // Just continue |
| 70 | + return ArduinoFloppyReader::WriteResponse::wrContinue; |
| 71 | + }; |
| 72 | + |
| 73 | + Gtk::Entry *fileEntry = nullptr; |
| 74 | + builder->get_widget("fileNameEntry", fileEntry); |
| 75 | + Gtk::FileChooserButton *folderButton = nullptr; |
| 76 | + builder->get_widget("folderSelector", folderButton); |
| 77 | + Gtk::ComboBoxText *typeSelector = nullptr; |
| 78 | + builder->get_widget("typeSelector", typeSelector); |
| 79 | + fileEntry->signal_changed().connect([fileEntry, copyButton]() |
| 80 | + { |
| 81 | + if (fileEntry->get_text_length() > 0) |
| 82 | + { |
| 83 | + copyButton->set_sensitive(true); |
| 84 | + } |
| 85 | + else |
| 86 | + { |
| 87 | + copyButton->set_sensitive(false); |
| 88 | + } |
| 89 | + }); |
| 90 | + Gtk::RadioButton *trackButton = nullptr; |
| 91 | + builder->get_widget("radio80", trackButton); |
| 92 | + copyButton->signal_clicked().connect([this, fileEntry, folderButton, typeSelector, trackButton, callback, portsCombo]() |
| 93 | + { |
| 94 | + Glib::ustring folder = folderButton->get_filename(); |
| 95 | + Glib::ustring filename = folder + "/" + fileEntry->get_buffer()->get_text(); |
| 96 | + ArduinoFloppyReader::ADFWriter writer; |
| 97 | + Glib::ustring serial = portsCombo->get_active_text(); |
| 98 | + writer.openDevice(std::wstring(serial.begin(), serial.end())); |
| 99 | + bool tracks80 = trackButton->get_active(); |
| 100 | + ArduinoFloppyReader::ADFResult readerResult; |
| 101 | + if (typeSelector->get_active_text() == "ADF") |
| 102 | + { |
| 103 | + readerResult = writer.DiskToADF(std::wstring(filename.begin(), filename.end()), tracks80 ? 80 : 82, callback); |
| 104 | + } |
| 105 | + else |
| 106 | + { |
| 107 | + readerResult = writer.DiskToSCP(std::wstring(filename.begin(), filename.end()), tracks80 ? 80 : 82, 3, callback); |
| 108 | + } |
| 109 | + // Handle the result |
| 110 | + handleResult(writer, readerResult); |
| 111 | + }); |
| 112 | + } |
| 113 | + |
| 114 | + virtual ~MainWindow() = default; |
| 115 | + |
| 116 | +private |
| 117 | + : |
| 118 | + Glib::RefPtr<Gtk::Builder> |
| 119 | + builder; |
| 120 | + |
| 121 | + void handleResult(ArduinoFloppyReader::ADFWriter writer, ArduinoFloppyReader::ADFResult readerResult) |
| 122 | + { |
| 123 | + switch (readerResult) |
| 124 | + { |
| 125 | + case ArduinoFloppyReader::ADFResult::adfrComplete: |
| 126 | + { |
| 127 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Completed", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK); |
| 128 | + dialog.run(); |
| 129 | + dialog.hide(); |
| 130 | + break; |
| 131 | + } |
| 132 | + case ArduinoFloppyReader::ADFResult::adfrAborted: |
| 133 | + { |
| 134 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Aborted", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); |
| 135 | + dialog.run(); |
| 136 | + dialog.hide(); |
| 137 | + break; |
| 138 | + } |
| 139 | + case ArduinoFloppyReader::ADFResult::adfrFileError: |
| 140 | + { |
| 141 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Unable to open the specified file to write to it.\nOutput File Error", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); |
| 142 | + dialog.run(); |
| 143 | + dialog.hide(); |
| 144 | + break; |
| 145 | + } |
| 146 | + case ArduinoFloppyReader::ADFResult::adfrFileIOError: |
| 147 | + { |
| 148 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "An error occured writing to the specified file.\nOutput File Error", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); |
| 149 | + dialog.run(); |
| 150 | + dialog.hide(); |
| 151 | + break; |
| 152 | + } |
| 153 | + case ArduinoFloppyReader::ADFResult::adfrFirmwareTooOld: |
| 154 | + { |
| 155 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "This requires firmware V1.8 or newer.\nFirmware out of date", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); |
| 156 | + dialog.run(); |
| 157 | + dialog.hide(); |
| 158 | + break; |
| 159 | + } |
| 160 | + case ArduinoFloppyReader::ADFResult::adfrDiskWriteProtected: |
| 161 | + { |
| 162 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "The disk is write protected", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); |
| 163 | + dialog.run(); |
| 164 | + dialog.hide(); |
| 165 | + break; |
| 166 | + } |
| 167 | + case ArduinoFloppyReader::ADFResult::adfrCompletedWithErrors: |
| 168 | + { |
| 169 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Completed with some errors", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK); |
| 170 | + dialog.run(); |
| 171 | + dialog.hide(); |
| 172 | + break; |
| 173 | + } |
| 174 | + case ArduinoFloppyReader::ADFResult::adfrDriveError: |
| 175 | + { |
| 176 | + std::string msg = "An error occured communicating with the Arduino interface:\r\n\r\n"; |
| 177 | + msg += writer.getLastError(); |
| 178 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, msg.c_str(), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); |
| 179 | + dialog.run(); |
| 180 | + dialog.hide(); |
| 181 | + break; |
| 182 | + } |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + bool showQuestion(bool isQuestion, const std::string question) |
| 187 | + { |
| 188 | + Gtk::ButtonsType buttons = Gtk::BUTTONS_OK_CANCEL; |
| 189 | + if (isQuestion) |
| 190 | + { |
| 191 | + buttons = Gtk::BUTTONS_YES_NO; |
| 192 | + } |
| 193 | + |
| 194 | + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, question, false, Gtk::MESSAGE_QUESTION, buttons); |
| 195 | + int Answer = dialog.run(); |
| 196 | + dialog.hide(); |
| 197 | + |
| 198 | + // Process user choice |
| 199 | + bool isYes = false; |
| 200 | + switch (Answer) |
| 201 | + { |
| 202 | + case (Gtk::RESPONSE_OK): |
| 203 | + isYes = true; |
| 204 | + break; |
| 205 | + case (Gtk::RESPONSE_CANCEL): |
| 206 | + std::cout << "Cancel clicked." << std::endl; |
| 207 | + break; |
| 208 | + case (Gtk::RESPONSE_YES): |
| 209 | + std::cout << "Yes clicked." << std::endl; |
| 210 | + isYes = true; |
| 211 | + break; |
| 212 | + case (Gtk::RESPONSE_NO): |
| 213 | + std::cout << "No clicked." << std::endl; |
| 214 | + break; |
| 215 | + default: |
| 216 | + std::cout << "Unexpected button clicked." << std::endl; |
| 217 | + break; |
| 218 | + } |
| 219 | + return isYes; |
| 220 | + } |
| 221 | + |
| 222 | + void showStatus(bool isError, std::string status) |
| 223 | + { |
| 224 | + std::string strLine; |
| 225 | + |
| 226 | + if (isError) |
| 227 | + strLine = "DIAGNOSTICS FAILED: "; |
| 228 | + strLine += status.c_str(); |
| 229 | + std::cerr << strLine << std::endl; |
| 230 | + Gtk::Statusbar *statusBar = nullptr; |
| 231 | + builder->get_widget("statusBar", statusBar); |
| 232 | + statusBar->pop(); |
| 233 | + statusBar->push(strLine); |
| 234 | + } |
| 235 | + |
| 236 | + void run_diagnostics(Glib::ustring serial) |
| 237 | + { |
| 238 | + ArduinoFloppyReader::ADFWriter writer; |
| 239 | + writer.runDiagnostics( |
| 240 | + std::wstring(serial.begin(), serial.end()), [this, serial](bool isError, const std::string message) -> void |
| 241 | + { |
| 242 | + while (gtk_events_pending()) |
| 243 | + gtk_main_iteration(); |
| 244 | + this->showStatus(isError, message); |
| 245 | + }, |
| 246 | + [this, serial](bool isQuestion, const std::string question) -> bool |
| 247 | + { |
| 248 | + while (gtk_events_pending()) |
| 249 | + gtk_main_iteration(); |
| 250 | + return this->showQuestion(isQuestion, question); |
| 251 | + }); |
| 252 | + } |
| 253 | +}; |
| 254 | + |
| 255 | +int main(int argc, char *argv[]) |
| 256 | +{ |
| 257 | + auto app = Gtk::Application::create(argc, argv, "be.sourcery.Garduino"); |
| 258 | + auto builder = Gtk::Builder::create(); |
| 259 | + |
| 260 | + builder->add_from_file("Garduino.glade"); |
| 261 | + |
| 262 | + MainWindow *wnd = nullptr; |
| 263 | + |
| 264 | + builder->get_widget_derived("MainWindow", wnd); |
| 265 | + |
| 266 | + auto r = app->run(*wnd); |
| 267 | + |
| 268 | + delete wnd; |
| 269 | + |
| 270 | + return r; |
| 271 | +} |
0 commit comments