├── .gitmodules ├── src └── bin │ ├── v1541commander │ ├── gfx │ │ ├── icon.ico │ │ ├── icon.xcf │ │ ├── icon-d64.ico │ │ ├── icon-d64.xcf │ │ ├── icon-lynx.ico │ │ ├── icon-lynx.xcf │ │ ├── icon-prg.ico │ │ ├── icon-prg.xcf │ │ ├── statusled.xcf │ │ ├── icon-zipcode.ico │ │ ├── icon-zipcode.xcf │ │ ├── statusled_green.png │ │ ├── statusled_red.png │ │ └── statusled_yellow.png │ ├── C64_Pro_Mono-STYLE.ttf │ ├── icons │ │ ├── 16x16 │ │ │ ├── v1541commander.png │ │ │ ├── v1541commander-d64.png │ │ │ ├── v1541commander-lynx.png │ │ │ ├── v1541commander-prg.png │ │ │ └── v1541commander-zipcode.png │ │ ├── 32x32 │ │ │ ├── v1541commander.png │ │ │ ├── v1541commander-d64.png │ │ │ ├── v1541commander-lynx.png │ │ │ ├── v1541commander-prg.png │ │ │ └── v1541commander-zipcode.png │ │ ├── 48x48 │ │ │ ├── v1541commander.png │ │ │ ├── v1541commander-d64.png │ │ │ ├── v1541commander-lynx.png │ │ │ ├── v1541commander-prg.png │ │ │ └── v1541commander-zipcode.png │ │ └── 256x256 │ │ │ ├── v1541commander.png │ │ │ ├── v1541commander-d64.png │ │ │ ├── v1541commander-lynx.png │ │ │ ├── v1541commander-prg.png │ │ │ └── v1541commander-zipcode.png │ ├── qtl10n.qrc │ ├── icon.qrc │ ├── cbmdosfsstatuswidget.h │ ├── petsciiconvert.h │ ├── aboutbox.h │ ├── logwindow.h │ ├── resources.qrc │ ├── skippablequestion.h │ ├── cbmdosfslistview.h │ ├── v1541commander.desktop │ ├── petsciistr.h │ ├── editoperationcheck.h │ ├── petsciibutton.h │ ├── settingsdialog.h │ ├── fsoptoverridesdialog.h │ ├── petsciiwindow.h │ ├── utils.h │ ├── cbmdosfswidget.h │ ├── petsciiedit.h │ ├── petsciibutton.cpp │ ├── skippablequestion.cpp │ ├── cbmdosfilemimedata.h │ ├── cbmdosfsoptionsdialog.h │ ├── cbmdosfilewidget.h │ ├── editoperationcheck.cpp │ ├── petsciistr.cpp │ ├── settings.h │ ├── logwindow.cpp │ ├── v1541imgwidget.h │ ├── cbmdosfsstatuswidget.cpp │ ├── cbmdosfsmodel.h │ ├── cbmdosfslistview.cpp │ ├── petsciiconvert.cpp │ ├── windres.rc │ ├── mainwindow.h │ ├── petsciiwindow.cpp │ ├── v1541commander.mk │ ├── mime │ │ └── v1541commander.xml │ ├── v1541commander.h │ ├── aboutbox.cpp │ ├── petsciiedit.cpp │ ├── settingsdialog.cpp │ ├── cbmdosfilemimedata.cpp │ ├── settings.cpp │ ├── main.cpp │ ├── cbmdosfswidget.cpp │ ├── cbmdosfsmodel.cpp │ ├── mainwindow.cpp │ ├── fsoptoverridesdialog.cpp │ ├── cbmdosfilewidget.cpp │ └── cbmdosfsoptionsdialog.cpp │ ├── setup │ ├── setup.mk │ ├── setup.exe.manifest │ └── windres.rc │ └── uninstall │ ├── uninstall.mk │ ├── uninstall.exe.manifest │ ├── windres.rc │ └── main.c ├── Makefile ├── .gitignore ├── CHANGES.txt ├── LICENSE-font.txt ├── LICENSE.txt ├── make-binreleases.sh ├── BUILDING.md ├── scripts ├── uninstall.sh └── setup.sh └── FAQ.txt /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "zimk"] 2 | path = zimk 3 | url = https://github.com/Zirias/zimk 4 | -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon.ico -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon.xcf -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon-d64.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon-d64.ico -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon-d64.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon-d64.xcf -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon-lynx.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon-lynx.ico -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon-lynx.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon-lynx.xcf -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon-prg.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon-prg.ico -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon-prg.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon-prg.xcf -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/statusled.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/statusled.xcf -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon-zipcode.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon-zipcode.ico -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/icon-zipcode.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/icon-zipcode.xcf -------------------------------------------------------------------------------- /src/bin/v1541commander/C64_Pro_Mono-STYLE.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/C64_Pro_Mono-STYLE.ttf -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/statusled_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/statusled_green.png -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/statusled_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/statusled_red.png -------------------------------------------------------------------------------- /src/bin/v1541commander/gfx/statusled_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/gfx/statusled_yellow.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/16x16/v1541commander.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/16x16/v1541commander.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/32x32/v1541commander.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/32x32/v1541commander.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/48x48/v1541commander.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/48x48/v1541commander.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/256x256/v1541commander.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/256x256/v1541commander.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/16x16/v1541commander-d64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/16x16/v1541commander-d64.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/16x16/v1541commander-lynx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/16x16/v1541commander-lynx.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/16x16/v1541commander-prg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/16x16/v1541commander-prg.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/32x32/v1541commander-d64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/32x32/v1541commander-d64.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/32x32/v1541commander-lynx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/32x32/v1541commander-lynx.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/32x32/v1541commander-prg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/32x32/v1541commander-prg.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/48x48/v1541commander-d64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/48x48/v1541commander-d64.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/48x48/v1541commander-lynx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/48x48/v1541commander-lynx.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/48x48/v1541commander-prg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/48x48/v1541commander-prg.png -------------------------------------------------------------------------------- /src/bin/v1541commander/qtl10n.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | qtbase_de.qm 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/16x16/v1541commander-zipcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/16x16/v1541commander-zipcode.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/256x256/v1541commander-d64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/256x256/v1541commander-d64.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/256x256/v1541commander-lynx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/256x256/v1541commander-lynx.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/256x256/v1541commander-prg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/256x256/v1541commander-prg.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/32x32/v1541commander-zipcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/32x32/v1541commander-zipcode.png -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/48x48/v1541commander-zipcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/48x48/v1541commander-zipcode.png -------------------------------------------------------------------------------- /src/bin/setup/setup.mk: -------------------------------------------------------------------------------- 1 | setup_MODULES:= main 2 | setup_LDFLAGS:= -mwindows 3 | setup_LIBS:= gdi32 comctl32 shlwapi 4 | setup_win32_RES:= windres 5 | 6 | $(call binrules, setup) 7 | 8 | -------------------------------------------------------------------------------- /src/bin/v1541commander/icons/256x256/v1541commander-zipcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excess-c64/v1541commander/HEAD/src/bin/v1541commander/icons/256x256/v1541commander-zipcode.png -------------------------------------------------------------------------------- /src/bin/uninstall/uninstall.mk: -------------------------------------------------------------------------------- 1 | uninstall_MODULES:= main 2 | uninstall_LDFLAGS:= -mwindows 3 | uninstall_LIBS:= gdi32 comctl32 4 | uninstall_win32_RES:= windres 5 | 6 | $(call binrules, uninstall) 7 | 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include zimk/zimk.mk 2 | $(call zinc,src/bin/v1541commander/v1541commander.mk) 3 | ifeq ($(PLATFORM),win32) 4 | $(call zinc,src/bin/setup/setup.mk) 5 | $(call zinc,src/bin/uninstall/uninstall.mk) 6 | endif 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .*.cfg 3 | .*.pyc 4 | *.o 5 | *.d 6 | *.qm 7 | *.exe 8 | *.core 9 | defaults.mk 10 | *.files 11 | *.config 12 | *.includes 13 | *.creator 14 | *.user 15 | *.bin 16 | *.exo 17 | *.prg 18 | /*.cfg 19 | /bin/ 20 | /lib/ 21 | /obj/ 22 | /test/ 23 | /dist/ 24 | /doc/ 25 | 26 | -------------------------------------------------------------------------------- /src/bin/v1541commander/icon.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/48x48/v1541commander.png 4 | icons/32x32/v1541commander.png 5 | icons/16x16/v1541commander.png 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfsstatuswidget.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_CBMDOSFSSTATUSWIDGET_H 2 | #define V1541C_CBMDOSFSSTATUSWIDGET_H 3 | 4 | #include 5 | 6 | #include <1541img/cbmdosfs.h> 7 | 8 | class CbmdosFsStatusWidget : public QLabel 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | CbmdosFsStatusWidget(QWidget *parent = 0); 14 | void setStatus(CbmdosFsStatus status); 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciiconvert.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_PETSCIICONVERT_H 2 | #define V1541C_PETSCIICONVERT_H 3 | 4 | #include 5 | 6 | namespace PetsciiConvert 7 | { 8 | QChar unicodeToFont(const QChar &uc, bool lc, bool reverse = false); 9 | QChar petsciiToFont(unsigned char petscii, bool lc, bool reverse = false); 10 | unsigned char fontToPetscii(const QChar &fc, bool reverse = false); 11 | } 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/bin/v1541commander/aboutbox.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_ABOUTBOX_H 2 | #define V1541C_ABOUTBOX_H 3 | 4 | #include 5 | 6 | class QFont; 7 | 8 | class AboutBox: public QDialog 9 | { 10 | Q_OBJECT 11 | 12 | private: 13 | class priv; 14 | priv *d; 15 | 16 | protected: 17 | virtual void showEvent(QShowEvent *event); 18 | 19 | public: 20 | AboutBox(const QFont &c64font); 21 | virtual ~AboutBox(); 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/bin/v1541commander/logwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_LOGWINDOW_H 2 | #define V1541C_LOGWINDOW_H 3 | 4 | #include 5 | 6 | class LogWindow : public QPlainTextEdit 7 | { 8 | Q_OBJECT 9 | 10 | public: 11 | LogWindow(QWidget *parent = 0); 12 | 13 | protected: 14 | virtual QSize sizeHint() const; 15 | virtual QSize minimumSizeHint() const; 16 | 17 | signals: 18 | void logLineAppended(const QString &line); 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/bin/v1541commander/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | C64_Pro_Mono-STYLE.ttf 4 | icons/256x256/v1541commander.png 5 | gfx/statusled_red.png 6 | gfx/statusled_yellow.png 7 | gfx/statusled_green.png 8 | qm/v1541commander-de.qm 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/bin/v1541commander/skippablequestion.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_SKIPPABLEQUESTION_H 2 | #define V1541C_SKIPPABLEQUESTION_H 3 | 4 | #include 5 | 6 | class SkippableQuestion: public QMessageBox 7 | { 8 | Q_OBJECT 9 | 10 | private: 11 | class priv; 12 | priv *d; 13 | 14 | public: 15 | SkippableQuestion(const QString &title, const QString &caption, 16 | QWidget *parent = nullptr); 17 | ~SkippableQuestion(); 18 | bool skip() const; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfslistview.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class QDragEnterEvent; 4 | class QDragMoveEvent; 5 | class QDropEvent; 6 | 7 | class CbmdosFsListView : public QListView 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | CbmdosFsListView(); 13 | 14 | protected: 15 | virtual void dragEnterEvent(QDragEnterEvent *event); 16 | virtual void dragMoveEvent(QDragMoveEvent *event); 17 | virtual void dropEvent(QDropEvent *event); 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /src/bin/v1541commander/v1541commander.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=V1541Commander 3 | GenericName=D64 Disk Image Editor 4 | Comment=Virtual 1541 disk commander 5 | Exec=v1541commander %F 6 | Icon=v1541commander 7 | StartupNotify=true 8 | Terminal=false 9 | Type=Application 10 | Categories=Utility;Archiving; 11 | MimeType=application/vnd.cbm.d64-disk-image;application/x.willcorley.lynx-archive;application/x.c64.zip-code; 12 | GenericName[de]=Editor für D64 Diskettenabbilder 13 | Comment[de]=Virtueller 1541 disk commander 14 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciistr.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_PETSCIISTR_H 2 | #define V1541C_PETSCIISTR_H 3 | 4 | #include 5 | #include 6 | 7 | class PetsciiStr 8 | { 9 | private: 10 | char *raw; 11 | uint8_t len; 12 | 13 | public: 14 | PetsciiStr(const char *raw, uint8_t len); 15 | PetsciiStr(const QString &str, bool reverse = false); 16 | ~PetsciiStr(); 17 | QString toString(bool lc, bool reverse = false) const; 18 | const char *petscii() const; 19 | uint8_t length() const; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/bin/v1541commander/editoperationcheck.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_EDITOPERATIONCHECK_H 2 | #define V1541C_EDITOPERATIONCHECK_H 3 | 4 | struct CbmdosFile; 5 | 6 | class EditOperationCheck 7 | { 8 | private: 9 | class priv; 10 | priv *d; 11 | 12 | public: 13 | EditOperationCheck(const CbmdosFile *newFile, 14 | const CbmdosFile *oldFile = nullptr); 15 | ~EditOperationCheck(); 16 | 17 | const CbmdosFile *newFile() const; 18 | const CbmdosFile *oldFile() const; 19 | 20 | void setAllowed(bool allowed); 21 | bool allowed() const; 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciibutton.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_PETSCIIBUTTON_H 2 | #define V1541C_PETSCIIBUTTON_H 3 | 4 | #include 5 | 6 | class QEvent; 7 | class QMouseEvent; 8 | 9 | class PetsciiButton : public QLabel 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | PetsciiButton( 15 | ushort petscii, const QFont &c64font, QWidget *parent = 0); 16 | 17 | signals: 18 | void clicked(ushort val); 19 | 20 | protected: 21 | void enterEvent(QEvent *ev); 22 | void leaveEvent(QEvent *ev); 23 | void mousePressEvent(QMouseEvent *ev); 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/bin/v1541commander/settingsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_SETTINGSDIALOG_H 2 | #define V1541C_SETTINGSDIALOG_H 3 | 4 | #include 5 | 6 | class QAbstractButton; 7 | class QShowEvent; 8 | 9 | class SettingsDialog: public QDialog 10 | { 11 | Q_OBJECT 12 | 13 | private: 14 | class priv; 15 | priv *d; 16 | 17 | public: 18 | SettingsDialog(); 19 | virtual ~SettingsDialog(); 20 | 21 | protected: 22 | virtual void showEvent(QShowEvent *event); 23 | 24 | private slots: 25 | virtual void save(); 26 | void buttonPressed(QAbstractButton *button); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | v1541commander changelog 2 | ======================== 3 | 4 | 5 | v1.1 6 | ---- 7 | 8 | * Allow to disable automatic image rewrite when opening an image with broken 9 | filesystem 10 | * Images with broken filesystems are read-only 11 | * Allow saving an image with broken filesystem 12 | * Warn before overflowing disk capacity or directory 13 | * Add a "settings" dialog 14 | * Full drag&drop support (files and text) 15 | * fix: set correct window icon on win32 16 | * several UI improvements: calculate sizes more reliably. Especially the 17 | directory list should now look ok on any screen resolution (DPI). 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/bin/v1541commander/fsoptoverridesdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_FSOPTOVERRIDESDIALOG_H 2 | #define V1541C_FSOPTOVERRIDESDIALOG_H 3 | 4 | #include 5 | 6 | struct CbmdosFsOptOverrides; 7 | class QAbstractButton; 8 | class QShowEvent; 9 | 10 | class FsOptOverridesDialog: public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | private: 15 | class priv; 16 | priv *d; 17 | 18 | public: 19 | FsOptOverridesDialog(CbmdosFsOptOverrides *overrides, 20 | QWidget *parent = nullptr); 21 | ~FsOptOverridesDialog(); 22 | 23 | protected: 24 | virtual void showEvent(QShowEvent *event); 25 | 26 | public slots: 27 | virtual void accept(); 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciiwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_PETSCIIWINDOW_H 2 | #define V1541C_PETSCIIWINDOW_H 3 | 4 | #include 5 | 6 | class PetsciiButton; 7 | class QShowEvent; 8 | 9 | class PetsciiWindow : public QWidget 10 | { 11 | Q_OBJECT 12 | 13 | private: 14 | PetsciiButton *buttons[0x100]; 15 | 16 | signals: 17 | void petsciiInput(ushort val); 18 | 19 | private slots: 20 | void buttonClicked(ushort val); 21 | 22 | public: 23 | PetsciiWindow(const QFont &c64font, QWidget *parent = 0); 24 | void setLowercase(bool lowerCase); 25 | 26 | protected: 27 | virtual void showEvent(QShowEvent *event); 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/bin/v1541commander/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_UTILS_H 2 | #define V1541C_UTILS_H 3 | 4 | #ifdef _WIN32 5 | # include 6 | # include 7 | #endif 8 | 9 | #define wstr_int(s) L ## s 10 | #define wstr(s) wstr_int(s) 11 | 12 | #ifdef _WIN32 13 | # define qfopen(n,m) _wfopen(\ 14 | reinterpret_cast((n).utf16()), wstr(m)) 15 | # define qfname(n) ((n).toUtf8().data()) 16 | # define qfnsan(n) ((n).replace(QRegularExpression("[|.<>/\\\":]"), "_")) 17 | #else 18 | # define qfopen(n,m) fopen((n).toLocal8Bit().data(), m) 19 | # define qfname(n) ((n).toLocal8Bit().data()) 20 | # define qfnsan(n) ((n).replace('/', '_')) 21 | #endif 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfswidget.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_CBMDOSFSWIDGET_H 2 | #define V1541C_CBMDOSFSWIDGET_H 3 | 4 | #include 5 | 6 | struct CbmdosFs; 7 | struct CbmdosVfsEventArgs; 8 | class PetsciiStr; 9 | 10 | class CbmdosFsWidget : public QGroupBox 11 | { 12 | Q_OBJECT 13 | 14 | private: 15 | class priv; 16 | priv *d; 17 | 18 | private slots: 19 | void nameChanged(const PetsciiStr &name); 20 | void idChanged(const PetsciiStr &id); 21 | void dosVerChanged(int val); 22 | void dosVerReset(); 23 | 24 | public: 25 | CbmdosFsWidget(QWidget *parent = 0); 26 | ~CbmdosFsWidget(); 27 | 28 | CbmdosFs *fs() const; 29 | void setFs(CbmdosFs *fs); 30 | void fsChanged(const CbmdosVfsEventArgs *args); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciiedit.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_PETSCIIEDIT_H 2 | #define V1541C_PETSCIIEDIT_H 3 | 4 | #include 5 | 6 | class PetsciiStr; 7 | class QKeyEvent; 8 | 9 | class PetsciiEdit : public QLineEdit 10 | { 11 | Q_OBJECT 12 | 13 | public slots: 14 | void petsciiInput(ushort val); 15 | void updateCase(bool lowerCase); 16 | 17 | private slots: 18 | void editText(const QString &text); 19 | 20 | signals: 21 | void petsciiEdited(const PetsciiStr &petscii); 22 | 23 | public: 24 | PetsciiEdit(QWidget *parent = 0); 25 | void setPetscii(const PetsciiStr &petscii, bool keepCursorPos = false); 26 | virtual void setMaxLength(int length); 27 | 28 | protected: 29 | virtual void keyPressEvent(QKeyEvent *event); 30 | virtual bool event(QEvent *event); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciibutton.cpp: -------------------------------------------------------------------------------- 1 | #include "petsciibutton.h" 2 | 3 | PetsciiButton::PetsciiButton( 4 | ushort petscii, const QFont &c64font, QWidget *parent) : 5 | QLabel(parent) 6 | { 7 | setFont(c64font); 8 | setText(QString(petscii)); 9 | setStyleSheet("QLabel { padding: 4px 2px; background-color: white; }"); 10 | } 11 | 12 | void PetsciiButton::enterEvent(QEvent *ev) 13 | { 14 | (void) ev; 15 | setStyleSheet("QLabel { padding: 4px 2px; " 16 | "background-color : lightskyblue; color: blue; }"); 17 | } 18 | 19 | void PetsciiButton::leaveEvent(QEvent *ev) 20 | { 21 | (void) ev; 22 | setStyleSheet("QLabel { padding: 4px 2px; background-color: white; }"); 23 | } 24 | 25 | void PetsciiButton::mousePressEvent(QMouseEvent *ev) 26 | { 27 | (void) ev; 28 | emit clicked(text().begin()->unicode()); 29 | } 30 | -------------------------------------------------------------------------------- /src/bin/v1541commander/skippablequestion.cpp: -------------------------------------------------------------------------------- 1 | #include "skippablequestion.h" 2 | 3 | #include 4 | #include 5 | 6 | class SkippableQuestion::priv 7 | { 8 | public: 9 | priv(); 10 | QCheckBox skipCheckBox; 11 | }; 12 | 13 | SkippableQuestion::priv::priv() : 14 | skipCheckBox(tr("Don't ask this question again")) 15 | {} 16 | 17 | SkippableQuestion::SkippableQuestion(const QString &title, 18 | const QString &caption, QWidget *parent) : 19 | QMessageBox(QMessageBox::Question, title, caption, 20 | QMessageBox::Yes|QMessageBox::No, parent) 21 | { 22 | d = new priv(); 23 | setCheckBox(&d->skipCheckBox); 24 | } 25 | 26 | SkippableQuestion::~SkippableQuestion() 27 | { 28 | delete d; 29 | } 30 | 31 | bool SkippableQuestion::skip() const 32 | { 33 | return d->skipCheckBox.isChecked(); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/bin/setup/setup.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | V1541Commander Setup 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/bin/uninstall/uninstall.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | V1541Commander Uninstall 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfilemimedata.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_CBMDOSFILEMIMEDATA_H 2 | #define V1541C_CBMDOSFILEMIMEDATA_H 3 | 4 | #include 5 | 6 | struct CbmdosFile; 7 | class CbmdosFsModel; 8 | 9 | class CbmdosFileMimeData: public QMimeData 10 | { 11 | Q_OBJECT 12 | 13 | private: 14 | class priv; 15 | priv *d; 16 | 17 | public: 18 | CbmdosFileMimeData(const CbmdosFsModel *model); 19 | virtual ~CbmdosFileMimeData(); 20 | virtual bool hasFormat(const QString &mimeType) const; 21 | virtual QStringList formats() const; 22 | void addFile(const CbmdosFile *file, int pos); 23 | const QList &files() const; 24 | const QList &filePositions() const; 25 | 26 | static const QString &internalFormat(); 27 | 28 | protected: 29 | virtual QVariant retrieveData( 30 | const QString &mimeType, QVariant::Type type) const; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfsoptionsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_CBMDOSFSOPTIONSDIALOG_H 2 | #define V1541C_CBMDOSFSOPTIONSDIALOG_H 3 | 4 | #include 5 | 6 | struct CbmdosFsOptions; 7 | class QAbstractButton; 8 | class QShowEvent; 9 | 10 | class CbmdosFsOptionsDialog: public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | private: 15 | class priv; 16 | priv *d; 17 | 18 | public: 19 | CbmdosFsOptionsDialog(CbmdosFsOptions *options, 20 | QWidget *parent = nullptr, bool canCancel = true); 21 | ~CbmdosFsOptionsDialog(); 22 | void disable35Tracks(); 23 | void disable40Tracks(); 24 | void disable42Tracks(); 25 | void disableZeroFree(); 26 | void reset(); 27 | bool wantRewrite(); 28 | 29 | protected: 30 | virtual void showEvent(QShowEvent *event); 31 | 32 | public slots: 33 | virtual void accept(); 34 | 35 | private slots: 36 | void buttonPressed(QAbstractButton *button); 37 | void optionClicked(bool checked); 38 | void valueChanged(int i); 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /LICENSE-font.txt: -------------------------------------------------------------------------------- 1 | This software package includes the "C64 Pro Mono" font by STYLE from 2 | . Here's the original license text: 3 | 4 | You MAY NOT: sell this font; include/redistribute this font in any font 5 | collection regardless of pricing; provide the font for direct download from 6 | any web site. You MAY: link to "http://style64.org/c64-truetype" in order for 7 | others to download and install the font; embed this font and or its .eot and 8 | .woff variants without any modification and using the same filenames they were 9 | provided with for display on any web site using @font-face rules; use this 10 | font in static images and vector art; include this font without any 11 | modification and using the same filenames they were provided with as part of a 12 | software package but ONLY if said software package is freely provided to end 13 | users. You may also contact us to negotiate a (possibly commercial) license 14 | for your use outside of these guidelines at "http://style64.org/contact-style". 15 | 16 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfilewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_CBMDOSFILEWIDGET_H 2 | #define V1541C_CBMDOSFILEWIDGET_H 3 | 4 | #include 5 | 6 | struct CbmdosFile; 7 | struct CbmdosFileEventArgs; 8 | class EditOperationCheck; 9 | class PetsciiStr; 10 | 11 | class CbmdosFileWidget: public QGroupBox 12 | { 13 | Q_OBJECT 14 | 15 | private: 16 | class priv; 17 | priv *d; 18 | 19 | private slots: 20 | void nameChanged(const PetsciiStr &name); 21 | void typeChanged(int typeIndex); 22 | void lockedChanged(int lockedState); 23 | void closedChanged(int closedState); 24 | void recordLengthChanged(int value); 25 | void forcedBlocksActiveChanged(int activeState); 26 | void forcedBlocksValueChanged(int value); 27 | void importFile(); 28 | void exportFile(); 29 | 30 | public: 31 | CbmdosFileWidget(QWidget *parent = 0); 32 | ~CbmdosFileWidget(); 33 | 34 | CbmdosFile *file() const; 35 | void setFile(CbmdosFile *file); 36 | void fileChanged(const CbmdosFileEventArgs *args); 37 | 38 | signals: 39 | void checkEditOperation(EditOperationCheck &check); 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/bin/v1541commander/editoperationcheck.cpp: -------------------------------------------------------------------------------- 1 | #include "editoperationcheck.h" 2 | 3 | class EditOperationCheck::priv 4 | { 5 | public: 6 | priv(const CbmdosFile *newFile, const CbmdosFile *oldFile); 7 | const CbmdosFile *newFile; 8 | const CbmdosFile *oldFile; 9 | bool allowed; 10 | }; 11 | 12 | EditOperationCheck::priv::priv(const CbmdosFile *newFile, 13 | const CbmdosFile *oldFile) : 14 | newFile(newFile), 15 | oldFile(oldFile), 16 | allowed(false) 17 | {} 18 | 19 | EditOperationCheck::EditOperationCheck(const CbmdosFile *newFile, 20 | const CbmdosFile *oldFile) 21 | { 22 | d = new priv(newFile, oldFile); 23 | } 24 | 25 | EditOperationCheck::~EditOperationCheck() 26 | { 27 | delete d; 28 | } 29 | 30 | const CbmdosFile *EditOperationCheck::newFile() const 31 | { 32 | return d->newFile; 33 | } 34 | 35 | const CbmdosFile *EditOperationCheck::oldFile() const 36 | { 37 | return d->oldFile; 38 | } 39 | 40 | void EditOperationCheck::setAllowed(bool allowed) 41 | { 42 | d->allowed = allowed; 43 | } 44 | 45 | bool EditOperationCheck::allowed() const 46 | { 47 | return d->allowed; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciistr.cpp: -------------------------------------------------------------------------------- 1 | #include "petsciistr.h" 2 | #include "petsciiconvert.h" 3 | 4 | #include 5 | 6 | #include 7 | 8 | PetsciiStr::PetsciiStr(const char *raw, uint8_t len) 9 | { 10 | this->raw = new char[len]; 11 | memcpy(this->raw, raw, len); 12 | this->len = len; 13 | } 14 | 15 | PetsciiStr::PetsciiStr(const QString &str, bool reverse) 16 | { 17 | raw = new char[str.count()]; 18 | int i = 0; 19 | for (QString::const_iterator j = str.begin(); j != str.end(); ++i, ++j) 20 | { 21 | raw[i] = PetsciiConvert::fontToPetscii(*j, reverse); 22 | } 23 | len = i; 24 | } 25 | 26 | PetsciiStr::~PetsciiStr() 27 | { 28 | delete[] raw; 29 | } 30 | 31 | QString PetsciiStr::toString(bool lc, bool reverse) const 32 | { 33 | QString result; 34 | for (int i = 0; i < len; ++i) 35 | { 36 | result.append(PetsciiConvert::petsciiToFont(raw[i], lc, reverse)); 37 | } 38 | 39 | return result; 40 | } 41 | 42 | const char *PetsciiStr::petscii() const 43 | { 44 | return raw; 45 | } 46 | 47 | uint8_t PetsciiStr::length() const 48 | { 49 | return len; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/bin/v1541commander/settings.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_SETTINGS_H 2 | #define V1541C_SETTINGS_H 3 | 4 | class QByteArray; 5 | 6 | #include <1541img/cbmdosfile.h> 7 | 8 | class Settings 9 | { 10 | private: 11 | class priv; 12 | priv *d; 13 | 14 | public: 15 | Settings(); 16 | ~Settings(); 17 | 18 | bool rememberWindowPositions() const; 19 | void setRememberWindowPositions(bool remember); 20 | 21 | const QByteArray &mainGeometry() const; 22 | void setMainGeometry(const QByteArray &geometry); 23 | 24 | const QByteArray &petsciiGeometry() const; 25 | void setPetsciiGeometry(const QByteArray &geometry); 26 | 27 | const QByteArray &logGeometry() const; 28 | void setLogGeometry(const QByteArray &geometry); 29 | 30 | bool lowercase() const; 31 | void setLowercase(bool lowercase); 32 | 33 | bool automapPetsciiToLc() const; 34 | void setAutomapPetsciiToLc(bool automap); 35 | 36 | bool exportAsPc64() const; 37 | void setExportAsPc64(bool exportPc64); 38 | 39 | CbmdosFileType defaultImportType() const; 40 | void setDefaultImportType(CbmdosFileType type); 41 | 42 | bool warnDiskCapacity() const; 43 | void setWarnDiskCapacity(bool warn); 44 | 45 | bool warnDirCapacity() const; 46 | void setWarnDirCapacity(bool warn); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/bin/v1541commander/logwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "logwindow.h" 2 | 3 | #include 4 | 5 | #include <1541img/log.h> 6 | 7 | static const QString levels[] = { 8 | QString("[FATAL] %1"), 9 | QString("[ERROR] %1"), 10 | QString("[WARN ] %1"), 11 | QString("[INFO ] %1"), 12 | QString("[DEBUG] %1") 13 | }; 14 | 15 | static void appendlog(LogLevel level, const char *message, void *data) 16 | { 17 | LogWindow *logWindow = static_cast(data); 18 | QString logmsg = levels[level].arg(message); 19 | logWindow->appendPlainText(logmsg); 20 | emit logWindow->logLineAppended(logmsg); 21 | } 22 | 23 | LogWindow::LogWindow(QWidget *parent) : 24 | QPlainTextEdit(parent) 25 | { 26 | setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 27 | setReadOnly(true); 28 | setMaximumBlockCount(1024); 29 | setWindowTitle(tr("lib1541img log viewer")); 30 | appendlog(L_INFO, "V1541Commander started, waiting for log messages from " 31 | "lib1541img ...", this); 32 | setCustomLogger(appendlog, this); 33 | adjustSize(); 34 | } 35 | 36 | QSize LogWindow::sizeHint() const 37 | { 38 | QFontMetricsF fm(font()); 39 | return QSize(82 * fm.averageCharWidth(), 16 * fm.height()); 40 | } 41 | 42 | QSize LogWindow::minimumSizeHint() const 43 | { 44 | return sizeHint(); 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Excess 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /src/bin/setup/windres.rc: -------------------------------------------------------------------------------- 1 | 1 24 "setup.exe.manifest" 2 | 1 VERSIONINFO 3 | FILEVERSION 1,1,0,0 4 | PRODUCTVERSION 1,1,0,0 5 | BEGIN 6 | BLOCK "StringFileInfo" 7 | BEGIN 8 | BLOCK "040904E4" 9 | BEGIN 10 | VALUE "CompanyName", "Excess" 11 | VALUE "FileDescription", "V1541Commander Setup" 12 | VALUE "FileVersion", "1.1.0" 13 | VALUE "InternalName", "setup" 14 | VALUE "LegalCopyright", "Zirias/Excess " 15 | VALUE "OriginalFilename", "setup.exe" 16 | VALUE "ProductName", "V1541Commander Setup" 17 | VALUE "ProductVersion", "1.1.0" 18 | END 19 | END 20 | BLOCK "VarFileInfo" 21 | BEGIN 22 | VALUE "Translation", 0x409, 1252 23 | END 24 | END 25 | LANGUAGE 0x0407, 0 26 | 1 VERSIONINFO 27 | FILEVERSION 1,1,0,0 28 | PRODUCTVERSION 1,1,0,0 29 | BEGIN 30 | BLOCK "StringFileInfo" 31 | BEGIN 32 | BLOCK "040704E4" 33 | BEGIN 34 | VALUE "CompanyName", "Excess" 35 | VALUE "FileDescription", "V1541Commander Einrichtung" 36 | VALUE "FileVersion", "1.1.0" 37 | VALUE "InternalName", "setup" 38 | VALUE "LegalCopyright", "Zirias/Excess " 39 | VALUE "OriginalFilename", "setup.exe" 40 | VALUE "ProductName", "V1541Commander Setup" 41 | VALUE "ProductVersion", "1.1.0" 42 | END 43 | END 44 | BLOCK "VarFileInfo" 45 | BEGIN 46 | VALUE "Translation", 0x407, 1252 47 | END 48 | END 49 | 50 | -------------------------------------------------------------------------------- /src/bin/uninstall/windres.rc: -------------------------------------------------------------------------------- 1 | 1 24 "uninstall.exe.manifest" 2 | 1 VERSIONINFO 3 | FILEVERSION 1,1,0,0 4 | PRODUCTVERSION 1,1,0,0 5 | BEGIN 6 | BLOCK "StringFileInfo" 7 | BEGIN 8 | BLOCK "040904E4" 9 | BEGIN 10 | VALUE "CompanyName", "Excess" 11 | VALUE "FileDescription", "V1541Commander Uninstall" 12 | VALUE "FileVersion", "1.1.0" 13 | VALUE "InternalName", "uninstall" 14 | VALUE "LegalCopyright", "Zirias/Excess " 15 | VALUE "OriginalFilename", "uninstall.exe" 16 | VALUE "ProductName", "V1541Commander Uninstall" 17 | VALUE "ProductVersion", "1.1.0" 18 | END 19 | END 20 | BLOCK "VarFileInfo" 21 | BEGIN 22 | VALUE "Translation", 0x409, 1252 23 | END 24 | END 25 | LANGUAGE 0x0407, 0 26 | 1 VERSIONINFO 27 | FILEVERSION 1,1,0,0 28 | PRODUCTVERSION 1,1,0,0 29 | BEGIN 30 | BLOCK "StringFileInfo" 31 | BEGIN 32 | BLOCK "040704E4" 33 | BEGIN 34 | VALUE "CompanyName", "Excess" 35 | VALUE "FileDescription", "V1541Commander entfernen" 36 | VALUE "FileVersion", "1.1.0" 37 | VALUE "InternalName", "uninstall" 38 | VALUE "LegalCopyright", "Zirias/Excess " 39 | VALUE "OriginalFilename", "uninstall.exe" 40 | VALUE "ProductName", "V1541Commander Uninstall" 41 | VALUE "ProductVersion", "1.1.0" 42 | END 43 | END 44 | BLOCK "VarFileInfo" 45 | BEGIN 46 | VALUE "Translation", 0x407, 1252 47 | END 48 | END 49 | 50 | -------------------------------------------------------------------------------- /src/bin/v1541commander/v1541imgwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_V1541IMGWIDGET_H 2 | #define V1541C_V1541IMGWIDGET_H 3 | 4 | #include 5 | 6 | class EditOperationCheck; 7 | class QKeyEvent; 8 | class QModelIndex; 9 | 10 | struct CbmdosVfs; 11 | 12 | class V1541ImgWidget: public QWidget 13 | { 14 | Q_OBJECT 15 | 16 | private: 17 | class priv; 18 | priv *d; 19 | 20 | private slots: 21 | void selected(const QModelIndex ¤t, const QModelIndex &previous); 22 | void modelModified(); 23 | void checkEditOperation(EditOperationCheck &check); 24 | 25 | protected: 26 | virtual void keyPressEvent(QKeyEvent *event); 27 | 28 | public slots: 29 | void newImage(); 30 | void open(const QString& filename); 31 | void openVfs(CbmdosVfs *vfs); 32 | void save(const QString& filename); 33 | void exportZipcode(const QString &filename); 34 | void exportLynx(const QString &filename); 35 | void fsOptions(); 36 | void rewriteImage(); 37 | void mapToLc(); 38 | void newFile(); 39 | void deleteFile(bool skipConfirmation = false); 40 | void fileOverrides(); 41 | 42 | public: 43 | V1541ImgWidget(QWidget *parent = nullptr); 44 | ~V1541ImgWidget(); 45 | QWidget *statusWidget() const; 46 | bool hasValidImage() const; 47 | bool hasValidSelection() const; 48 | bool isReadOnly() const; 49 | CbmdosVfs *exportZipcodeVfs(); 50 | 51 | signals: 52 | void selectionChanged(); 53 | void modified(); 54 | void saved(); 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfsstatuswidget.cpp: -------------------------------------------------------------------------------- 1 | #include "cbmdosfsstatuswidget.h" 2 | #include "v1541commander.h" 3 | 4 | CbmdosFsStatusWidget::CbmdosFsStatusWidget(QWidget *parent) : 5 | QLabel(parent) 6 | { 7 | setPixmap(cmdr.statusLedGreen()); 8 | setToolTip(tr("Filesystem OK")); 9 | } 10 | 11 | void CbmdosFsStatusWidget::setStatus(CbmdosFsStatus status) 12 | { 13 | if (status & ( 14 | CbmdosFsStatus::CFS_DISKFULL | 15 | CbmdosFsStatus::CFS_DIRFULL | 16 | CbmdosFsStatus::CFS_BROKEN )) 17 | { 18 | setPixmap(cmdr.statusLedRed()); 19 | } 20 | else if (status & CbmdosFsStatus::CFS_INVALIDBAM) 21 | { 22 | setPixmap(cmdr.statusLedYellow()); 23 | } 24 | else 25 | { 26 | setPixmap(cmdr.statusLedGreen()); 27 | } 28 | 29 | if (status == CbmdosFsStatus::CFS_OK) 30 | { 31 | setToolTip(tr("Filesystem OK")); 32 | } 33 | else 34 | { 35 | QStringList stmsgs; 36 | if (status & CbmdosFsStatus::CFS_BROKEN) 37 | { 38 | stmsgs << tr("Broken Filesystem"); 39 | } 40 | if (status & CbmdosFsStatus::CFS_DIRFULL) 41 | { 42 | stmsgs << tr("Directory full"); 43 | } 44 | if (status & CbmdosFsStatus::CFS_DISKFULL) 45 | { 46 | stmsgs << tr("Disk full"); 47 | } 48 | if (status & CbmdosFsStatus::CFS_INVALIDBAM) 49 | { 50 | stmsgs << tr("BAM is invalid"); 51 | } 52 | setToolTip(stmsgs.join('\n')); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfsmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_CBMDOSFSMODEL_H 2 | #define V1541C_CBMDOSFSMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct CbmdosFile; 9 | struct CbmdosFs; 10 | struct CbmdosVfsEventArgs; 11 | class EditOperationCheck; 12 | class QTemporaryDir; 13 | 14 | class CbmdosFsModel: public QAbstractListModel 15 | { 16 | Q_OBJECT 17 | 18 | private: 19 | class priv; 20 | priv *d; 21 | 22 | public: 23 | CbmdosFsModel(QObject *parent = nullptr); 24 | ~CbmdosFsModel(); 25 | CbmdosFs *fs() const; 26 | void setFs(CbmdosFs *fs); 27 | void fsChanged(const CbmdosVfsEventArgs *args); 28 | void deleteAt(const QModelIndex &at); 29 | void addFile(const QModelIndex &at, CbmdosFile *newFile); 30 | void setItemSize(QSizeF size); 31 | const QTemporaryDir *tmpDir() const; 32 | virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; 33 | virtual QVariant data(const QModelIndex &index, 34 | int role = Qt::DisplayRole) const; 35 | virtual Qt::ItemFlags flags(const QModelIndex &index) const; 36 | virtual QStringList mimeTypes() const; 37 | virtual QMimeData *mimeData(const QModelIndexList &indexes) const; 38 | virtual Qt::DropActions supportedDropActions() const; 39 | virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, 40 | int row, int column, const QModelIndex &parent); 41 | 42 | signals: 43 | void selectedIndexChanged(const QModelIndex &to, 44 | QItemSelectionModel::SelectionFlags command); 45 | void modified(); 46 | void checkEditOperation(EditOperationCheck &check); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfslistview.cpp: -------------------------------------------------------------------------------- 1 | #include "cbmdosfslistview.h" 2 | #include "cbmdosfsmodel.h" 3 | #include "v1541commander.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | CbmdosFsListView::CbmdosFsListView() : QListView() 10 | { 11 | setMinimumHeight(200); 12 | setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 13 | setDragEnabled(true); 14 | setAcceptDrops(true); 15 | setDragDropMode(QAbstractItemView::DragDrop); 16 | setDefaultDropAction(Qt::CopyAction); 17 | addAction(&cmdr.newFileAction()); 18 | addAction(&cmdr.deleteFileAction()); 19 | addAction(&cmdr.fileOverridesAction()); 20 | setContextMenuPolicy(Qt::ActionsContextMenu); 21 | setSelectionMode(QAbstractItemView::ExtendedSelection); 22 | } 23 | 24 | void CbmdosFsListView::dragEnterEvent(QDragEnterEvent *event) 25 | { 26 | if (event->source() == this) 27 | { 28 | event->setDropAction(Qt::MoveAction); 29 | } 30 | QListView::dragEnterEvent(event); 31 | if (event->source() == this) 32 | { 33 | event->setDropAction(Qt::MoveAction); 34 | } 35 | } 36 | 37 | void CbmdosFsListView::dragMoveEvent(QDragMoveEvent *event) 38 | { 39 | if (event->source() == this) 40 | { 41 | event->setDropAction(Qt::MoveAction); 42 | } 43 | QListView::dragMoveEvent(event); 44 | if (event->source() == this) 45 | { 46 | event->setDropAction(Qt::MoveAction); 47 | } 48 | } 49 | 50 | void CbmdosFsListView::dropEvent(QDropEvent *event) 51 | { 52 | if (event->source() == this) 53 | { 54 | event->setDropAction(Qt::MoveAction); 55 | } 56 | QListView::dropEvent(event); 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciiconvert.cpp: -------------------------------------------------------------------------------- 1 | #include "petsciiconvert.h" 2 | 3 | QChar PetsciiConvert::unicodeToFont(const QChar &uc, bool lc, bool reverse) 4 | { 5 | if (uc.isNull()) return QChar(); 6 | ushort val = uc.unicode(); 7 | if (val < 0x20) return QChar(); 8 | if (val > 0xff && val < 0xe000) return QChar(); 9 | if (val == 0xefa3) val = 0xe0a0; 10 | if (val > 0xe3ff) return QChar(); 11 | val &= 0xfeff; 12 | if (reverse) 13 | { 14 | if (!(val & 0x200)) 15 | { 16 | if (val < 0xe040 17 | || (val >= 0xe060 && val < 0xe0c0) 18 | || val >= 0xe0e0) 19 | val |= 0x200; 20 | } 21 | } 22 | else if (val & 0x200) 23 | { 24 | if (val < 0xe240 25 | || (val >= 0xe260 && val < 0xe2c0) 26 | || val >= 0xe2e0) 27 | val &= ~0x200; 28 | } 29 | if (val < 0x100) 30 | { 31 | if (val > 0x40 && val < 0x5b) val += 0x20; 32 | else if (val > 0x60 && val < 0x7b) val -= 0x20; 33 | val |= 0xe000; 34 | if (reverse) val |= 0x200; 35 | } 36 | if (lc) val |= 0x100; 37 | return QChar(val); 38 | } 39 | 40 | QChar PetsciiConvert::petsciiToFont(unsigned char petscii, 41 | bool lc, bool reverse) 42 | { 43 | ushort val; 44 | if (petscii < 0x20 || (petscii >= 0x80 && petscii < 0xa0)) 45 | val = 0xe240 + petscii; 46 | else 47 | val = 0xe000 + petscii; 48 | 49 | if (reverse) val ^= 0x200; 50 | if (lc) val |= 0x100; 51 | return QChar(val); 52 | } 53 | 54 | unsigned char PetsciiConvert::fontToPetscii(const QChar &fc, bool reverse) 55 | { 56 | ushort val = fc.unicode(); 57 | if (reverse) val ^= 0x200; 58 | if (val == 0xefa3) val = 0xe0a0; 59 | val &= 0xfeff; 60 | if (val < 0xe000 || val > 0xe2ff) return '?'; 61 | if (val > 0xe200) 62 | { 63 | val -= 0xe200; 64 | if ((val >= 0x40 && val < 0x60) || (val >= 0xc0 && val < 0xe0)) 65 | val += (0xe000 - 0x40); 66 | else return '?'; 67 | } 68 | return val & 0xff; 69 | } 70 | -------------------------------------------------------------------------------- /src/bin/v1541commander/windres.rc: -------------------------------------------------------------------------------- 1 | 1000 ICON DISCARDABLE "gfx/icon.ico" 2 | 1001 ICON DISCARDABLE "gfx/icon-d64.ico" 3 | 1002 ICON DISCARDABLE "gfx/icon-lynx.ico" 4 | 1003 ICON DISCARDABLE "gfx/icon-zipcode.ico" 5 | 1004 ICON DISCARDABLE "gfx/icon-prg.ico" 6 | 1 VERSIONINFO 7 | FILEVERSION 1,1,0,0 8 | PRODUCTVERSION 1,1,0,0 9 | BEGIN 10 | BLOCK "StringFileInfo" 11 | BEGIN 12 | BLOCK "040904E4" 13 | BEGIN 14 | VALUE "CompanyName", "Excess" 15 | VALUE "FileDescription", "virtual 1541 disk commander" 16 | VALUE "FileVersion", "1.1.0" 17 | VALUE "InternalName", "v1541commander" 18 | VALUE "LegalCopyright", "Zirias/Excess " 19 | VALUE "OriginalFilename", "v1541commander.exe" 20 | VALUE "ProductName", "V1541Commander" 21 | VALUE "ProductVersion", "1.1.0" 22 | END 23 | END 24 | BLOCK "VarFileInfo" 25 | BEGIN 26 | VALUE "Translation", 0x409, 1252 27 | END 28 | END 29 | STRINGTABLE 30 | BEGIN 31 | 1 L"D64 disk image" 32 | 2 L"C64 LyNX archive" 33 | 3 L"C64 Zip-Code archive file" 34 | 4 L"Commodore program" 35 | END 36 | LANGUAGE 0x0407, 0 37 | 1 VERSIONINFO 38 | FILEVERSION 1,1,0,0 39 | PRODUCTVERSION 1,1,0,0 40 | BEGIN 41 | BLOCK "StringFileInfo" 42 | BEGIN 43 | BLOCK "040704E4" 44 | BEGIN 45 | VALUE "CompanyName", "Excess" 46 | VALUE "FileDescription", "virtueller 1541 disk commander" 47 | VALUE "FileVersion", "1.1.0" 48 | VALUE "InternalName", "v1541commander" 49 | VALUE "LegalCopyright", "Zirias/Excess " 50 | VALUE "OriginalFilename", "v1541commander.exe" 51 | VALUE "ProductName", "V1541Commander" 52 | VALUE "ProductVersion", "1.1.0" 53 | END 54 | END 55 | BLOCK "VarFileInfo" 56 | BEGIN 57 | VALUE "Translation", 0x407, 1252 58 | END 59 | END 60 | STRINGTABLE 61 | BEGIN 62 | 1 L"D64 Diskettenabbild" 63 | 2 L"C64 LyNX Archiv" 64 | 3 L"C64 Zip-Code Archivdatei" 65 | 4 L"Commodore Programm" 66 | END 67 | -------------------------------------------------------------------------------- /src/bin/v1541commander/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_MAINWINDOW_H 2 | #define V1541C_MAINWINDOW_H 3 | 4 | #include 5 | 6 | class QDragEnterEvent; 7 | class QDragMoveEvent; 8 | class QDropEvent; 9 | class QEvent; 10 | class QWidget; 11 | 12 | struct CbmdosVfs; 13 | 14 | class MainWindow: public QMainWindow 15 | { 16 | Q_OBJECT 17 | 18 | private: 19 | class priv; 20 | priv *d; 21 | 22 | private slots: 23 | void contentSelectionChanged(); 24 | void contentModified(); 25 | void contentSaved(); 26 | 27 | public: 28 | enum Content 29 | { 30 | None, 31 | Image 32 | }; 33 | 34 | MainWindow(); 35 | virtual ~MainWindow(); 36 | Content content() const; 37 | const QString &filename() const; 38 | bool hasValidContent() const; 39 | bool hasValidSelection() const; 40 | bool isReadOnly() const; 41 | CbmdosVfs *exportZipcodeVfs(); 42 | virtual bool event(QEvent *e); 43 | virtual void closeEvent(QCloseEvent *e); 44 | virtual QSize sizeHint() const; 45 | 46 | protected: 47 | virtual void dragEnterEvent(QDragEnterEvent *event); 48 | virtual void dragMoveEvent(QDragMoveEvent *event); 49 | virtual void dropEvent(QDropEvent *event); 50 | 51 | signals: 52 | void activated(); 53 | void closed(); 54 | void contentChanged(); 55 | void modifiedChanged(); 56 | void selectionChanged(); 57 | 58 | public slots: 59 | void newImage(); 60 | void openImage(const QString &imgFile); 61 | void openVfs(CbmdosVfs *vfs); 62 | void save(const QString &imgFile = QString()); 63 | void exportZipcode(const QString &zcFile); 64 | void exportLynx(const QString &lynxFile); 65 | void closeDocument(); 66 | void fsOptions(); 67 | void rewriteImage(); 68 | void mapToLc(); 69 | void newFile(); 70 | void deleteFile(); 71 | void fileOverrides(); 72 | void showStatusLine(const QString &line); 73 | }; 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciiwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "petsciiwindow.h" 2 | #include "petsciibutton.h" 3 | 4 | #include 5 | 6 | #ifdef _WIN32 7 | #include 8 | #endif 9 | 10 | PetsciiWindow::PetsciiWindow(const QFont &c64font, QWidget *parent) : 11 | QWidget(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint 12 | | Qt::WindowCloseButtonHint | Qt::WindowDoesNotAcceptFocus 13 | | Qt::CustomizeWindowHint | Qt::MSWindowsFixedSizeDialogHint) 14 | { 15 | QGridLayout *layout = new QGridLayout(this); 16 | layout->setContentsMargins(0,0,0,0); 17 | layout->setSpacing(1); 18 | for (ushort row = 0; row < 0x10; ++row) 19 | { 20 | for (ushort col = 0; col < 0x10; ++col) 21 | { 22 | ushort petscii = (row << 4) | col; 23 | if (petscii < 0x20 || (petscii >= 0x80 && petscii < 0xa0)) 24 | { 25 | petscii += 0xe240; 26 | } 27 | else 28 | { 29 | petscii += 0xe000; 30 | } 31 | PetsciiButton *button = new PetsciiButton(petscii, c64font, this); 32 | connect(button, &PetsciiButton::clicked, 33 | this, &PetsciiWindow::buttonClicked); 34 | layout->addWidget(button, row, col); 35 | buttons[(row<<4)|col] = button; 36 | } 37 | } 38 | setLayout(layout); 39 | setWindowTitle(tr("PETSCII Input")); 40 | setAttribute(Qt::WA_ShowWithoutActivating); 41 | } 42 | 43 | void PetsciiWindow::setLowercase(bool lowerCase) 44 | { 45 | for (ushort i = 0; i < 0x100; ++i) 46 | { 47 | #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 48 | ushort val = buttons[i]->text().front().unicode(); 49 | #else 50 | ushort val = buttons[i]->text()[0].unicode(); 51 | #endif 52 | if (lowerCase) val |= 0x100; 53 | else val &= 0xfeff; 54 | buttons[i]->setText(QString(QChar(val))); 55 | } 56 | } 57 | 58 | void PetsciiWindow::buttonClicked(ushort val) 59 | { 60 | emit petsciiInput(val); 61 | } 62 | 63 | void PetsciiWindow::showEvent(QShowEvent *event) 64 | { 65 | QWidget::showEvent(event); 66 | #ifdef _WIN32 67 | SetWindowLongPtr(HWND(winId()), GWL_EXSTYLE, WS_EX_NOACTIVATE); 68 | #endif 69 | setFixedSize(minimumSize()); 70 | } 71 | -------------------------------------------------------------------------------- /src/bin/v1541commander/v1541commander.mk: -------------------------------------------------------------------------------- 1 | v1541commander_PREPROCMODULES:= v1541commander mainwindow v1541imgwidget \ 2 | cbmdosfsmodel cbmdosfswidget cbmdosfsoptionsdialog cbmdosfilewidget \ 3 | cbmdosfsstatuswidget petsciiedit petsciibutton petsciiwindow \ 4 | logwindow aboutbox cbmdosfslistview cbmdosfilemimedata \ 5 | fsoptoverridesdialog skippablequestion settingsdialog 6 | v1541commander_CXXMODULES:= $(v1541commander_PREPROCMODULES) main petsciistr \ 7 | petsciiconvert settings editoperationcheck 8 | v1541commander_QRC:= resources 9 | ifneq ($(PLATFORM),win32) 10 | v1541commander_QRC+= icon 11 | endif 12 | v1541commander_PREPROC:= MOC 13 | v1541commander_PREPROCFLAGS:= -p. 14 | v1541commander_posix_CXXFLAGS?= -fPIC 15 | v1541commander_INCLUDES= -I$(v1541commander_PPSRCDIR) 16 | v1541commander_CXXFLAGS= -fno-exceptions -fno-rtti 17 | v1541commander_PKGDEPS:= Qt5Core Qt5Gui Qt5Widgets Qt5Network \ 18 | 1541img >= 1.1 1541img < 2 19 | v1541commander_ICONSIZES:= 16x16 32x32 48x48 256x256 20 | v1541commander_MIMEICONS:= v1541commander-d64 v1541commander-zipcode \ 21 | v1541commander-lynx v1541commander-prg 22 | v1541commander_DESKTOPFILE:= v1541commander 23 | v1541commander_SHAREDMIMEINFO:= v1541commander 24 | v1541commander_DOCS:= README.md BUILDING.md LICENSE.txt LICENSE-font.txt \ 25 | CHANGES.txt FAQ.txt 26 | v1541commander_win32_LDFLAGS:= -mwindows 27 | v1541commander_win32_RES:= windres 28 | ifneq ($(EMBEDQTL10N),) 29 | v1541commander_QRC+= qtl10n 30 | endif 31 | 32 | $(call binrules, v1541commander) 33 | 34 | ifneq ($(EMBEDQTL10N),) 35 | $(v1541commander_OBJDIR)$(PSEP)qtl10n_qrc.cpp: \ 36 | $(v1541commander_SRCDIR)$(PSEP)qtl10n.qrc 37 | cp $(EMBEDQTL10N)$(PSEP)qtbase_de.qm $(v1541commander_SRCDIR) 38 | $(RCC) -o $@ --name qtl10n $< 39 | $(RMF) $(v1541commander_SRCDIR)$(PSEP)qtbase_de.qm 40 | 41 | endif 42 | 43 | 44 | v1541commander_LANGS:= de 45 | 46 | $(v1541commander_OBJDIR)$(PSEP)resources_qrc.cpp: $(addsuffix .qm,$(addprefix \ 47 | $(v1541commander_SRCDIR)$(PSEP)qm$(PSEP)v1541commander-, \ 48 | $(v1541commander_LANGS))) 49 | 50 | LRELEASE?= lrelease 51 | 52 | $(v1541commander_SRCDIR)$(PSEP)qm$(PSEP)%.qm: \ 53 | $(v1541commander_SRCDIR)$(PSEP)%.ts 54 | @$(MDP) $(v1541commander_SRCDIR)$(PSEP)qm 55 | $(LRELEASE) $< -qm $@ 56 | 57 | clean:: 58 | rm -fr $(v1541commander_SRCDIR)$(PSEP)qm 59 | 60 | -------------------------------------------------------------------------------- /make-binreleases.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | VERSION=${1:-1.1} 4 | NAME=v1541commander 5 | 6 | WINTARGET=i686-w64-mingw32.static 7 | LINTARGET=x86_64-unknown-linux-gnu 8 | WINPKGDIR=${NAME}-${VERSION} 9 | WINPKG=${NAME}-win32-${VERSION}.zip 10 | WINXPPKG=${NAME}-win32-xp-vista-${VERSION}.zip 11 | LINPKGDIR=${NAME}-linux-x86_64-${VERSION} 12 | LINPKG=${LINPKGDIR}.tar.xz 13 | DOCS="README.md BUILDING.md LICENSE.txt LICENSE-font.txt CHANGES.txt FAQ.txt" 14 | ICONS=src/bin/${NAME}/icons 15 | DESKTOP=src/bin/${NAME}/${NAME}.desktop 16 | MIMETYPES=src/bin/${NAME}/mime 17 | 18 | rm -fr ${WINPKGDIR} ${WINPKG} ${LINPKGDIR} ${LINPKG} 19 | 20 | ./build-static-mxe-win32.sh clean 21 | ./build-static-mxe-win32.sh 22 | 23 | mkdir ${WINPKGDIR} 24 | cp bin/${WINTARGET}/release/${NAME}.exe ${WINPKGDIR} 25 | cp bin/${WINTARGET}/release/setup.exe ${WINPKGDIR} 26 | cp bin/${WINTARGET}/release/uninstall.exe ${WINPKGDIR} 27 | cp ${DOCS} ${WINPKGDIR} 28 | for D in ${DOCS}; do 29 | vim -c "set ff=dos" -c ":wq" ${WINPKGDIR}/${D} 30 | done 31 | upx --lzma --best --compress-icons=0 --compress-resources=0 \ 32 | ${WINPKGDIR}/${NAME}.exe 33 | zip -9r ${WINPKG} ${WINPKGDIR} 34 | rm -fr ${WINPKGDIR} 35 | 36 | ./build-static-mxe-winxp.sh clean 37 | ./build-static-mxe-winxp.sh 38 | 39 | mkdir ${WINPKGDIR} 40 | cp bin/${WINTARGET}/release/${NAME}.exe ${WINPKGDIR} 41 | cp bin/${WINTARGET}/release/setup.exe ${WINPKGDIR} 42 | cp bin/${WINTARGET}/release/uninstall.exe ${WINPKGDIR} 43 | cp ${DOCS} ${WINPKGDIR} 44 | for D in ${DOCS}; do 45 | vim -c "set ff=dos" -c ":wq" ${WINPKGDIR}/${D} 46 | done 47 | upx --lzma --best --compress-icons=0 --compress-resources=0 \ 48 | ${WINPKGDIR}/${NAME}.exe 49 | zip -9r ${WINXPPKG} ${WINPKGDIR} 50 | rm -fr ${WINPKGDIR} 51 | 52 | ./build-static-linux64.sh clean 53 | ./build-static-linux64.sh 54 | 55 | mkdir ${LINPKGDIR} 56 | cp bin/${LINTARGET}/release/${NAME} ${LINPKGDIR} 57 | cp scripts/setup.sh ${LINPKGDIR} 58 | cp scripts/uninstall.sh ${LINPKGDIR} 59 | chmod 755 ${LINPKGDIR}/setup.sh 60 | chmod 755 ${LINPKGDIR}/uninstall.sh 61 | cp ${DOCS} ${LINPKGDIR} 62 | for D in ${DOCS}; do 63 | vim -c "set ff=unix" -c ":wq" ${LINPKGDIR}/${D} 64 | done 65 | cp ${DESKTOP} ${LINPKGDIR} 66 | cp -R ${ICONS} ${LINPKGDIR}/ 67 | cp -R ${MIMETYPES} ${LINPKGDIR}/ 68 | upx --lzma --best ${LINPKGDIR}/${NAME} 69 | tar cJvf ${LINPKG} ${LINPKGDIR} 70 | rm -fr ${LINPKGDIR} 71 | 72 | -------------------------------------------------------------------------------- /src/bin/v1541commander/mime/v1541commander.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | D64 disk image 5 | D64 Diskettenabbild 6 | 7 | 8 | 9 | 10 | C64 Zip-Code archive file 11 | C64 Zip-Code Archivdatei 12 | 13 | 14 | 15 | 16 | Commodore program 17 | Commodore Programm 18 | 19 | 20 | 21 | 22 | Commodore sequential file 23 | Commodore sequentielle Datei 24 | 25 | 26 | 27 | Commodore relative file data 28 | Commodore relative Datei (Inhalt) 29 | 30 | 31 | 32 | Commodore user format file 33 | Commodore Datei (benutzerspezifisch) 34 | 35 | 36 | 37 | PC64 Container for C64 file 38 | PC64 Container für C64 Datei 39 | 40 | 41 | 43 | 44 | 45 | 46 | C64 LyNX archive 47 | C64 LyNX Archiv 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building V1541Commander from source 2 | 3 | ## Dependencies 4 | 5 | The following libraries are needed for building: 6 | 7 | * Qt5 (recent versions preferred) 8 | * lib1541img (see below) 9 | 10 | To get the most recent source, you will need `git`. 11 | 12 | ## Getting and installing lib1541img 13 | 14 | To get the source from git, type the following 15 | 16 | git clone https://github.com/excess-c64/lib1541img 17 | cd lib1541img 18 | git submodule update --init 19 | 20 | If you want a specific version, e.g. 1.0, do the following 21 | 22 | git clone https://github.com/excess-c64/lib1541img 23 | cd lib1541img 24 | git checkout v1.0 25 | git submodule update --init 26 | 27 | To build and install lib1541img, the following commands should be enough: 28 | 29 | make -j4 strip 30 | make install 31 | 32 | This will install to `/usr/local`, so for `make install`, you will need root 33 | privileges. 34 | 35 | The build system will understand standard variables like `prefix` and 36 | `DESTDIR`. 37 | 38 | ## Getting and installing V1541Commander 39 | 40 | To get the source from git, type the following 41 | 42 | git clone https://github.com/excess-c64/v1541commander 43 | cd v1541commander 44 | git submodule update --init 45 | 46 | If you want a specific version, e.g. 1.0, do the following 47 | 48 | git clone https://github.com/excess-c64/v1541commander 49 | cd v1541commander 50 | git checkout v1.0 51 | git submodule update --init 52 | 53 | To build and install v1541commander, the following commands should be enough: 54 | 55 | make -j4 strip 56 | make install 57 | 58 | This will install to `/usr/local`, so for `make install`, you will need root 59 | privileges. 60 | 61 | The build system will understand standard variables like `prefix` and 62 | `DESTDIR`. 63 | 64 | ## Notes on non-GNU systems like FreeBSD 65 | 66 | The build system needs GNU make to work, so make sure you have it installed. 67 | On systems where the default `make` isn't GNU make, GNU make is typically 68 | called `gmake`. Therefore, replace all invocations of `make` with `gmake`. 69 | 70 | ## Notes on Windows 71 | 72 | To build the software yourself for Windows, it's recommended to either use a 73 | cross-toolchain like [MXE](https://mxe.cc/) or install 74 | [MSYS2](https://www.msys2.org/) on your Windows machine. With MSYS2, building 75 | should work like described above. You will have to place all the required 76 | DLLs (Qt, its dependencies, and lib1541img) in the application directory for 77 | it to work standalone. 78 | 79 | Static linking is possible as well, for an idea where to start, see the 80 | `build-static-mxe-win32.sh` script I use to create a static build with MXE. 81 | 82 | -------------------------------------------------------------------------------- /src/bin/v1541commander/v1541commander.h: -------------------------------------------------------------------------------- 1 | #ifndef V1541C_V1541COMMANDER_H 2 | #define V1541C_V1541COMMANDER_H 3 | 4 | #include 5 | 6 | class QAction; 7 | class QPixmap; 8 | class QTranslator; 9 | class Settings; 10 | 11 | #define cmdr (V1541Commander::instance()) 12 | 13 | class V1541Commander: public QApplication 14 | { 15 | Q_OBJECT 16 | 17 | private: 18 | class priv; 19 | priv *d; 20 | 21 | private slots: 22 | void newImage(); 23 | void open(); 24 | void save(); 25 | void exportZipcode(); 26 | void exportZipcodeD64(); 27 | void exportLynx(); 28 | void close(); 29 | void showSettings(); 30 | void about(); 31 | void exit(); 32 | void windowActivated(); 33 | void windowClosed(); 34 | void windowContentChanged(); 35 | void windowSelectionChanged(); 36 | void showPetsciiWindow(); 37 | void showLogWindow(); 38 | void petsciiInput(ushort val); 39 | void fsOptions(); 40 | void rewriteImage(); 41 | void mapToLc(); 42 | void newFile(); 43 | void deleteFile(); 44 | void fileOverrides(); 45 | void logLineAppended(const QString &line); 46 | void newConnection(); 47 | void disconnected(); 48 | void readyRead(); 49 | 50 | public: 51 | V1541Commander(int &argc, char **argv, QTranslator *translator); 52 | ~V1541Commander(); 53 | const QFont &c64font() const; 54 | const QFont &menufont() const; 55 | const QFont &statusfont() const; 56 | const QPixmap &statusLedRed() const; 57 | const QPixmap &statusLedYellow() const; 58 | const QPixmap &statusLedGreen() const; 59 | QAction &newAction(); 60 | QAction &openAction(); 61 | QAction &saveAction(); 62 | QAction &saveAsAction(); 63 | QAction &exportZipcodeAction(); 64 | QAction &exportZipcodeD64Action(); 65 | QAction &exportLynxAction(); 66 | QAction &closeAction(); 67 | QAction &settingsAction(); 68 | QAction &aboutAction(); 69 | QAction &exitAction(); 70 | QAction &petsciiWindowAction(); 71 | QAction &logWindowAction(); 72 | QAction &fsOptionsAction(); 73 | QAction &rewriteImageAction(); 74 | QAction &autoMapLcAction(); 75 | QAction &mapLcAction(); 76 | QAction &newFileAction(); 77 | QAction &deleteFileAction(); 78 | QAction &fileOverridesAction(); 79 | QAction &lowerCaseAction(); 80 | const QString &instanceServerName() const; 81 | bool isPrimaryInstance() const; 82 | Settings &settings(); 83 | 84 | static V1541Commander &instance(); 85 | 86 | public slots: 87 | void open(const QString &filename); 88 | void saveAs(); 89 | void show(); 90 | 91 | signals: 92 | void lowerCaseChanged(bool lowerCase); 93 | void autoMapToLcChanged(bool autoMap); 94 | }; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/bin/v1541commander/aboutbox.cpp: -------------------------------------------------------------------------------- 1 | #include "aboutbox.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class AboutBox::priv 10 | { 11 | public: 12 | priv(); 13 | QVBoxLayout mainLayout; 14 | QHBoxLayout contentLayout; 15 | QVBoxLayout textLayout; 16 | QHBoxLayout titleLayout; 17 | QLabel appLogo; 18 | QLabel excessLogo; 19 | QLabel aboutText; 20 | QLabel titleText; 21 | QDialogButtonBox buttons; 22 | }; 23 | 24 | static const QChar logo[] = {0xa, 25 | 0xe0b0, 0xe0d5, 0xe0c0, 0xe0b2, 0xe0b2, 0xe0b2, 0xe0c0, 0xe0b2, 26 | 0xe0c0, 0xe0b2, 0xe0c0, 0xe0b2, 0xe0c0, 0xe0b2, 0xe0c9, 0xe0ae, 0xa, 27 | 0xe0b0, 0xe0b3, 0xe0dd, 0xe0dd, 0xe0dd, 0xe0dd, 0xe0dd, 0xe0dd, 28 | 0xe0dd, 0xe0dd, 0xe0ad, 0xe0b3, 0xe0ad, 0xe0b3, 0xe0ab, 0xe0ae, 0xa, 29 | 0xe0dd, 0xe0dd, 0xe0b0, 0xe0db, 0xe020, 0xe0b3, 0xe0ab, 0xe0b3, 30 | 0xe0b0, 0xe0db, 0xe0ae, 0xe0ab, 0xe0ae, 0xe0dd, 0xe0dd, 0xe0dd, 0xa, 31 | 0xe0ad, 0xe0b3, 0xe0dd, 0xe0dd, 0xe0dd, 0xe0dd, 0xe0dd, 0xe0dd, 32 | 0xe0dd, 0xe0dd, 0xe0dd, 0xe0dd, 0xe0dd, 0xe0ab, 0xe0db, 0xe0bd, 0xa, 33 | 0xe0ad, 0xe0ca, 0xe0c0, 0xe0b1, 0xe0b1, 0xe0b1, 0xe0c0, 0xe0b1, 34 | 0xe0c0, 0xe0b1, 0xe0c0, 0xe0b1, 0xe0c0, 0xe0b1, 0xe0cb, 0xe0bd, 0xa 35 | }; 36 | 37 | AboutBox::priv::priv() : 38 | mainLayout(), 39 | contentLayout(), 40 | textLayout(), 41 | titleLayout(), 42 | appLogo(), 43 | excessLogo(QString::fromRawData(logo, sizeof logo / sizeof *logo)), 44 | aboutText(tr( 45 | "

This is free software provided under BSD 2clause license, " 46 | "with no warranties whatsoever, see LICENSE.txt included with the " 47 | "package.

" 48 | "

About the C64 font used, see LICENSE-font.txt, also " 49 | "included with the package.

" 50 | "

The UI is driven by Qt, using LGPL licensing.

" 51 | "

If you received a statically linked package, see " 52 | "" 53 | "https://github.com/excess-c64/v1541commander for " 54 | "instructions to build your own executable, possibly linking " 55 | "different library versions.

")), 56 | titleText(QString(tr("

%1 v%2

" 57 | "

virtual 1541 disk commander
" 58 | "by Zirias/Excess

")) 59 | .arg(QCoreApplication::applicationName()) 60 | .arg(QCoreApplication::applicationVersion())), 61 | buttons(QDialogButtonBox::Ok) 62 | { 63 | appLogo.setPixmap(QPixmap(":/icon_256.png")); 64 | } 65 | 66 | AboutBox::AboutBox(const QFont &c64font) : 67 | QDialog(0, Qt::WindowSystemMenuHint | Qt::WindowTitleHint 68 | | Qt::WindowCloseButtonHint | Qt::MSWindowsFixedSizeDialogHint) 69 | { 70 | d = new priv(); 71 | d->excessLogo.setFont(c64font); 72 | d->aboutText.setWordWrap(true); 73 | d->titleLayout.addWidget(&d->titleText); 74 | d->titleLayout.addWidget(&d->excessLogo); 75 | d->textLayout.addLayout(&d->titleLayout); 76 | d->textLayout.addWidget(&d->aboutText); 77 | d->contentLayout.addWidget(&d->appLogo); 78 | d->contentLayout.addLayout(&d->textLayout); 79 | d->mainLayout.addLayout(&d->contentLayout); 80 | d->mainLayout.addWidget(&d->buttons); 81 | d->mainLayout.setSizeConstraint(QLayout::SetFixedSize); 82 | setLayout(&d->mainLayout); 83 | connect(&d->buttons, &QDialogButtonBox::accepted, this, &AboutBox::hide); 84 | } 85 | 86 | AboutBox::~AboutBox() 87 | { 88 | delete d; 89 | } 90 | 91 | void AboutBox::showEvent(QShowEvent *event) 92 | { 93 | QDialog::showEvent(event); 94 | setFixedSize(minimumSize()); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /src/bin/v1541commander/petsciiedit.cpp: -------------------------------------------------------------------------------- 1 | #include "petsciiedit.h" 2 | #include "petsciiconvert.h" 3 | #include "petsciistr.h" 4 | #include "settings.h" 5 | #include "v1541commander.h" 6 | 7 | #include 8 | 9 | static const ushort cbmLetterChars[] = 10 | { 11 | 0xe0b0, 12 | 0xe0bf, 13 | 0xe0bc, 14 | 0xe0ac, 15 | 0xe0b1, 16 | 0xe0bb, 17 | 0xe0a5, 18 | 0xe0b4, 19 | 0xe0a2, 20 | 0xe0b5, 21 | 0xe0a1, 22 | 0xe0b6, 23 | 0xe0a7, 24 | 0xe0aa, 25 | 0xe0b9, 26 | 0xe0af, 27 | 0xe0ab, 28 | 0xe0b2, 29 | 0xe0ae, 30 | 0xe0a3, 31 | 0xe0b8, 32 | 0xe0be, 33 | 0xe0b3, 34 | 0xe0bd, 35 | 0xe0b7, 36 | 0xe0ad 37 | }; 38 | 39 | PetsciiEdit::PetsciiEdit(QWidget *parent) : 40 | QLineEdit(QString(), parent) 41 | { 42 | setFont(cmdr.c64font()); 43 | connect(this, &QLineEdit::textEdited, this, &PetsciiEdit::editText); 44 | } 45 | 46 | void PetsciiEdit::editText(const QString &text) 47 | { 48 | int pos = cursorPosition(); 49 | QString translated; 50 | for (QString::const_iterator i = text.cbegin(); i != text.cend(); ++i) 51 | { 52 | QChar tc = PetsciiConvert::unicodeToFont(*i, 53 | cmdr.settings().lowercase()); 54 | if (tc.isNull()) 55 | { 56 | if (pos > 0) --pos; 57 | } 58 | else 59 | { 60 | if (tc == 0xe0a0 || tc == 0xe1a0) tc = 0xefa3; 61 | translated.append(tc); 62 | } 63 | } 64 | setCursorPosition(pos); 65 | PetsciiStr petscii(translated); 66 | emit petsciiEdited(petscii); 67 | } 68 | 69 | void PetsciiEdit::setPetscii(const PetsciiStr &petscii, bool keepCursorPos) 70 | { 71 | int pos = cursorPosition(); 72 | bool lc = cmdr.settings().lowercase(); 73 | setText(petscii.toString(lc).replace(lc ? 0xe1a0 : 0xe0a0, 0xefa3)); 74 | if (keepCursorPos) setCursorPosition(pos); 75 | } 76 | 77 | void PetsciiEdit::updateCase(bool lowerCase) 78 | { 79 | int pos = cursorPosition(); 80 | QString translated; 81 | for (QString::const_iterator i = text().cbegin(); i != text().cend(); ++i) 82 | { 83 | QChar tc = PetsciiConvert::unicodeToFont(*i, lowerCase); 84 | if (tc == 0xe0a0 || tc == 0xe1a0) tc = 0xefa3; 85 | translated.append(tc); 86 | } 87 | setText(translated); 88 | setCursorPosition(pos); 89 | } 90 | 91 | void PetsciiEdit::petsciiInput(ushort val) 92 | { 93 | insert(QString(val)); 94 | } 95 | 96 | void PetsciiEdit::setMaxLength(int length) 97 | { 98 | QFontMetricsF fm(font()); 99 | setMinimumWidth(minimumSizeHint().width() - fm.maxWidth() 100 | + length * fm.boundingRect(0xe220).width()); 101 | QLineEdit::setMaxLength(length); 102 | } 103 | 104 | void PetsciiEdit::keyPressEvent(QKeyEvent *event) 105 | { 106 | Qt::KeyboardModifiers mods = QApplication::keyboardModifiers(); 107 | int key = event->key(); 108 | if (mods == Qt::AltModifier) 109 | { 110 | if (key >= Qt::Key_A && key <= Qt::Key_Z) 111 | { 112 | petsciiInput(cbmLetterChars[key - Qt::Key_A]); 113 | } 114 | } 115 | else if (mods == Qt::ShiftModifier && key == Qt::Key_Space) 116 | { 117 | petsciiInput(0xefa3); 118 | } 119 | else QLineEdit::keyPressEvent(event); 120 | } 121 | 122 | bool PetsciiEdit::event(QEvent *event) 123 | { 124 | if (event->type() == QEvent::ShortcutOverride) 125 | { 126 | Qt::KeyboardModifiers mods = QApplication::keyboardModifiers(); 127 | if (mods == Qt::AltModifier) 128 | { 129 | int key = static_cast(event)->key(); 130 | if (key >= Qt::Key_A && key <= Qt::Key_Z) 131 | { 132 | event->accept(); 133 | } 134 | } 135 | } 136 | return QLineEdit::event(event); 137 | } 138 | -------------------------------------------------------------------------------- /scripts/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo 4 | echo V1541Commander uninstall 5 | echo 6 | echo This script will uninstall icons, menu entry and filetype associations 7 | echo installed by \`setup.sh\'. 8 | echo 9 | 10 | if test $(id -u) = 0 ; then 11 | echo This script must not be run as root, exiting. 12 | echo 13 | exit 1; 14 | fi 15 | 16 | missingtool=n 17 | 18 | if ! tool="$(type xdg-icon-resource 2>/dev/null)" || test -z "$tool"; then 19 | echo ERROR: required tool \`xdg-icon-resource\' not found in path. 20 | missingtool=y 21 | fi 22 | 23 | if ! tool="$(type xdg-desktop-menu 2>/dev/null)" || test -z "$tool"; then 24 | echo ERROR: required tool \`xdg-desktop-menu\' not found in path. 25 | missingtool=y 26 | fi 27 | 28 | if ! tool="$(type xdg-mime 2>/dev/null)" || test -z "$tool"; then 29 | echo ERROR: required tool \`xdg-mime\' not found in path. 30 | missingtool=y 31 | fi 32 | 33 | if ! tool="$(type dirname 2>/dev/null)" || test -z "$tool"; then 34 | echo ERROR: required tool \`dirname\' not found. 35 | missingtesttool=y 36 | fi 37 | 38 | if test "$missingtool" = "y"; then 39 | exit 1; 40 | fi 41 | 42 | V1541DIR=$(dirname $0) 43 | 44 | read -p "Do you want to continue (y/n)? " yn 45 | case $yn in 46 | [Yy]* ) break;; 47 | * ) exit;; 48 | esac 49 | 50 | cd "$V1541DIR" 51 | echo Uninstalling file type associations ... 52 | xdg-mime uninstall --mode user mime/v1541commander.xml 53 | echo Uninstalling desktop icon ... 54 | xdg-desktop-menu uninstall --mode user v1541commander-local.desktop 55 | echo Uninstalling icons ... 56 | xdg-icon-resource uninstall --mode user \ 57 | --size 16 icons/16x16/v1541commander.png 58 | xdg-icon-resource uninstall --context mimetypes --mode user \ 59 | --size 16 icons/16x16/v1541commander-d64.png v1541commander-d64 60 | xdg-icon-resource uninstall --context mimetypes --mode user \ 61 | --size 16 icons/16x16/v1541commander-zipcode.png v1541commander-zipcode 62 | xdg-icon-resource uninstall --context mimetypes --mode user \ 63 | --size 16 icons/16x16/v1541commander-lynx.png v1541commander-lynx 64 | xdg-icon-resource uninstall --context mimetypes --mode user \ 65 | --size 16 icons/16x16/v1541commander-prg.png v1541commander-prg 66 | xdg-icon-resource uninstall --mode user \ 67 | --size 32 icons/32x32/v1541commander.png 68 | xdg-icon-resource uninstall --context mimetypes --mode user \ 69 | --size 32 icons/32x32/v1541commander-d64.png v1541commander-d64 70 | xdg-icon-resource uninstall --context mimetypes --mode user \ 71 | --size 32 icons/32x32/v1541commander-zipcode.png v1541commander-zipcode 72 | xdg-icon-resource uninstall --context mimetypes --mode user \ 73 | --size 32 icons/32x32/v1541commander-lynx.png v1541commander-lynx 74 | xdg-icon-resource uninstall --context mimetypes --mode user \ 75 | --size 32 icons/32x32/v1541commander-prg.png v1541commander-prg 76 | xdg-icon-resource uninstall --mode user \ 77 | --size 48 icons/48x48/v1541commander.png 78 | xdg-icon-resource uninstall --context mimetypes --mode user \ 79 | --size 48 icons/48x48/v1541commander-d64.png v1541commander-d64 80 | xdg-icon-resource uninstall --context mimetypes --mode user \ 81 | --size 48 icons/48x48/v1541commander-zipcode.png v1541commander-zipcode 82 | xdg-icon-resource uninstall --context mimetypes --mode user \ 83 | --size 48 icons/48x48/v1541commander-lynx.png v1541commander-lynx 84 | xdg-icon-resource uninstall --context mimetypes --mode user \ 85 | --size 48 icons/48x48/v1541commander-prg.png v1541commander-prg 86 | xdg-icon-resource uninstall --mode user \ 87 | --size 256 icons/256x256/v1541commander.png 88 | xdg-icon-resource uninstall --context mimetypes --mode user \ 89 | --size 256 icons/256x256/v1541commander-d64.png v1541commander-d64 90 | xdg-icon-resource uninstall --context mimetypes --mode user \ 91 | --size 256 icons/256x256/v1541commander-zipcode.png \ 92 | v1541commander-zipcode 93 | xdg-icon-resource uninstall --context mimetypes --mode user \ 94 | --size 256 icons/256x256/v1541commander-lynx.png v1541commander-lynx 95 | xdg-icon-resource uninstall --context mimetypes --mode user \ 96 | --size 256 icons/256x256/v1541commander-prg.png v1541commander-prg 97 | echo All done. 98 | 99 | -------------------------------------------------------------------------------- /src/bin/v1541commander/settingsdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "settingsdialog.h" 2 | #include "settings.h" 3 | #include "v1541commander.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class SettingsDialog::priv 13 | { 14 | public: 15 | priv(); 16 | void load(); 17 | void save(); 18 | QVBoxLayout mainLayout; 19 | QCheckBox rememberWindowPositions; 20 | QCheckBox exportAsPc64; 21 | QHBoxLayout defaultImportTypeLayout; 22 | QLabel defaultImportTypeLabel; 23 | QComboBox defaultImportType; 24 | QCheckBox warnDiskCapacity; 25 | QCheckBox warnDirCapacity; 26 | QDialogButtonBox buttons; 27 | }; 28 | 29 | SettingsDialog::priv::priv() : 30 | mainLayout(), 31 | rememberWindowPositions(tr("remember window positions")), 32 | exportAsPc64(tr("drag && drop exports as PC64 file")), 33 | defaultImportTypeLayout(), 34 | defaultImportTypeLabel(tr("import unknown dropped files as: ")), 35 | defaultImportType(), 36 | warnDiskCapacity(tr("warn when disk would overflow")), 37 | warnDirCapacity(tr("warn when directory would overflow")), 38 | buttons(QDialogButtonBox::Ok|QDialogButtonBox::Cancel 39 | |QDialogButtonBox::Apply) 40 | {} 41 | 42 | void SettingsDialog::priv::load() 43 | { 44 | rememberWindowPositions.setChecked( 45 | cmdr.settings().rememberWindowPositions()); 46 | exportAsPc64.setChecked(cmdr.settings().exportAsPc64()); 47 | defaultImportType.setCurrentIndex(defaultImportType.findData( 48 | cmdr.settings().defaultImportType())); 49 | warnDiskCapacity.setChecked(cmdr.settings().warnDiskCapacity()); 50 | warnDirCapacity.setChecked(cmdr.settings().warnDirCapacity()); 51 | } 52 | 53 | void SettingsDialog::priv::save() 54 | { 55 | cmdr.settings().setRememberWindowPositions( 56 | rememberWindowPositions.isChecked()); 57 | cmdr.settings().setExportAsPc64(exportAsPc64.isChecked()); 58 | cmdr.settings().setDefaultImportType((CbmdosFileType) 59 | defaultImportType.currentData().toInt()); 60 | cmdr.settings().setWarnDiskCapacity(warnDiskCapacity.isChecked()); 61 | cmdr.settings().setWarnDirCapacity(warnDirCapacity.isChecked()); 62 | } 63 | 64 | SettingsDialog::SettingsDialog() : 65 | QDialog(0, Qt::WindowSystemMenuHint 66 | | Qt::WindowTitleHint | Qt::WindowCloseButtonHint 67 | | Qt::CustomizeWindowHint | Qt::MSWindowsFixedSizeDialogHint) 68 | { 69 | d = new priv(); 70 | d->mainLayout.addWidget(&d->rememberWindowPositions); 71 | d->mainLayout.addWidget(&d->exportAsPc64); 72 | d->defaultImportType.addItem("SEQ", CFT_SEQ); 73 | d->defaultImportType.addItem("PRG", CFT_PRG); 74 | d->defaultImportType.addItem("USR", CFT_USR); 75 | d->defaultImportType.addItem("REL", CFT_REL); 76 | d->defaultImportTypeLayout.addWidget(&d->defaultImportTypeLabel); 77 | d->defaultImportTypeLayout.addWidget(&d->defaultImportType); 78 | d->mainLayout.addLayout(&d->defaultImportTypeLayout); 79 | d->mainLayout.addWidget(&d->warnDiskCapacity); 80 | d->mainLayout.addWidget(&d->warnDirCapacity); 81 | d->mainLayout.addWidget(&d->buttons); 82 | setLayout(&d->mainLayout); 83 | setWindowTitle(tr("V1541Commander settings")); 84 | 85 | connect(&d->buttons, &QDialogButtonBox::accepted, 86 | this, &SettingsDialog::save); 87 | connect(&d->buttons, &QDialogButtonBox::rejected, 88 | this, &QDialog::reject); 89 | connect(&d->buttons, &QDialogButtonBox::clicked, 90 | this, &SettingsDialog::buttonPressed); 91 | } 92 | 93 | SettingsDialog::~SettingsDialog() 94 | { 95 | delete d; 96 | } 97 | 98 | void SettingsDialog::showEvent(QShowEvent *event) 99 | { 100 | d->load(); 101 | QDialog::showEvent(event); 102 | setFixedSize(size()); 103 | } 104 | 105 | void SettingsDialog::save() 106 | { 107 | d->save(); 108 | hide(); 109 | } 110 | 111 | void SettingsDialog::buttonPressed(QAbstractButton *button) 112 | { 113 | if (d->buttons.buttonRole(button) == QDialogButtonBox::ApplyRole) 114 | { 115 | d->save(); 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfilemimedata.cpp: -------------------------------------------------------------------------------- 1 | #include "cbmdosfilemimedata.h" 2 | #include "cbmdosfsmodel.h" 3 | #include "settings.h" 4 | #include "utils.h" 5 | #include "v1541commander.h" 6 | 7 | #include 8 | #include 9 | 10 | #include <1541img/cbmdosfile.h> 11 | #include <1541img/filedata.h> 12 | #include <1541img/petscii.h> 13 | 14 | static const QString internalType = "application/x.v1541c.cbmdosfile"; 15 | 16 | class CbmdosFileMimeData::priv 17 | { 18 | public: 19 | priv(const CbmdosFsModel *model); 20 | void addFile(const CbmdosFile *file, int pos); 21 | const CbmdosFsModel *model; 22 | QList files; 23 | QList positions; 24 | QStringList fileNames; 25 | bool haveContent; 26 | bool tmpSaved; 27 | }; 28 | 29 | CbmdosFileMimeData::priv::priv(const CbmdosFsModel *model) : 30 | model(model), files(), positions(), fileNames(), 31 | haveContent(false), tmpSaved(false) 32 | {} 33 | 34 | void CbmdosFileMimeData::priv::addFile(const CbmdosFile *file, int pos) 35 | { 36 | files.append(file); 37 | positions.append(pos); 38 | uint8_t namelen; 39 | const char *name = CbmdosFile_name(file, &namelen); 40 | char utf8name[65]; 41 | petscii_toUtf8(utf8name, 65, name, namelen, 42 | cmdr.settings().lowercase(), 1, 0, 0); 43 | QString hostFileName = qfnsan(QString::fromUtf8(utf8name)); 44 | fileNames.append(hostFileName); 45 | if (!haveContent) 46 | { 47 | const FileData *data = CbmdosFile_rdata(file); 48 | haveContent = data && FileData_size(data); 49 | } 50 | } 51 | 52 | CbmdosFileMimeData::CbmdosFileMimeData(const CbmdosFsModel *model) 53 | { 54 | d = new priv(model); 55 | } 56 | 57 | CbmdosFileMimeData::~CbmdosFileMimeData() 58 | { 59 | delete d; 60 | } 61 | 62 | bool CbmdosFileMimeData::hasFormat(const QString &mimeType) const 63 | { 64 | if (mimeType == internalType) return true; 65 | if (mimeType == "text/uri-list") return d->haveContent; 66 | return QMimeData::hasFormat(mimeType); 67 | } 68 | 69 | QStringList CbmdosFileMimeData::formats() const 70 | { 71 | QStringList formats = QMimeData::formats(); 72 | if (d->haveContent && !d->tmpSaved) 73 | { 74 | formats.prepend("text/uri-list"); 75 | } 76 | return formats; 77 | } 78 | 79 | void CbmdosFileMimeData::addFile(const CbmdosFile *file, int pos) 80 | { 81 | d->addFile(file, pos); 82 | setText(d->fileNames.join('\n')); 83 | } 84 | 85 | const QList &CbmdosFileMimeData::files() const 86 | { 87 | return d->files; 88 | } 89 | 90 | const QList &CbmdosFileMimeData::filePositions() const 91 | { 92 | return d->positions; 93 | } 94 | 95 | /* static */ 96 | const QString &CbmdosFileMimeData::internalFormat() 97 | { 98 | return internalType; 99 | } 100 | 101 | QVariant CbmdosFileMimeData::retrieveData( 102 | const QString &mimeType, QVariant::Type type) const 103 | { 104 | if (d->haveContent && mimeType == "text/uri-list" && !d->tmpSaved) 105 | { 106 | QList uris; 107 | const QTemporaryDir *td = d->model->tmpDir(); 108 | for (int i = 0; i < d->files.size(); ++i) 109 | { 110 | #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) 111 | QString fullName = td->filePath(d->fileNames.at(i)); 112 | #else 113 | QString fullName = QDir::cleanPath(td->path() 114 | + "/" + d->fileNames.at(i)); 115 | #endif 116 | bool pc64 = cmdr.settings().exportAsPc64(); 117 | switch (CbmdosFile_type(d->files.at(i))) 118 | { 119 | case CbmdosFileType::CFT_DEL: 120 | continue; 121 | case CbmdosFileType::CFT_SEQ: 122 | fullName.append(pc64 ? ".s00" : ".seq"); 123 | break; 124 | case CbmdosFileType::CFT_USR: 125 | fullName.append(pc64 ? ".u00" : ".usr"); 126 | break; 127 | case CbmdosFileType::CFT_PRG: 128 | fullName.append(pc64 ? ".p00" : ".prg"); 129 | break; 130 | case CbmdosFileType::CFT_REL: 131 | fullName.append(pc64 ? ".r00" : ".rel"); 132 | break; 133 | } 134 | FILE *f = qfopen(fullName, "wb"); 135 | if (f) 136 | { 137 | int rc; 138 | if (pc64) 139 | { 140 | rc = CbmdosFile_exportPC64(d->files.at(i), f); 141 | } 142 | else 143 | { 144 | rc = CbmdosFile_exportRaw(d->files.at(i), f); 145 | } 146 | fclose(f); 147 | if (rc >= 0) 148 | { 149 | uris.append(QUrl::fromLocalFile(fullName)); 150 | } 151 | } 152 | } 153 | if (!uris.empty()) 154 | { 155 | const_cast(this)->setUrls(uris); 156 | d->tmpSaved = true; 157 | } 158 | } 159 | 160 | return QMimeData::retrieveData(mimeType, type); 161 | } 162 | -------------------------------------------------------------------------------- /src/bin/v1541commander/settings.cpp: -------------------------------------------------------------------------------- 1 | #include "settings.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class Settings::priv 8 | { 9 | public: 10 | priv(); 11 | ~priv(); 12 | QSettings qsettings; 13 | 14 | bool rememberWindowPositions; 15 | QByteArray mainGeometry; 16 | QByteArray petsciiGeometry; 17 | QByteArray logGeometry; 18 | bool lowercase; 19 | bool automapPetsciiToLc; 20 | bool exportAsPc64; 21 | CbmdosFileType defaultImportType; 22 | bool warnDiskCapacity; 23 | bool warnDirCapacity; 24 | }; 25 | 26 | Settings::priv::priv() : 27 | qsettings(QCoreApplication::organizationName(), 28 | QCoreApplication::applicationName()) 29 | { 30 | rememberWindowPositions = qsettings.value("rememberPos", true).toBool(); 31 | mainGeometry = qsettings.value("geometry").toByteArray(); 32 | petsciiGeometry = qsettings.value("petsciiGeometry").toByteArray(); 33 | logGeometry = qsettings.value("logGeometry").toByteArray(); 34 | lowercase = qsettings.value("lowercase", false).toBool(); 35 | automapPetsciiToLc = qsettings.value("automap", false).toBool(); 36 | exportAsPc64 = qsettings.value("exportPc64", false).toBool(); 37 | defaultImportType = (CbmdosFileType) 38 | qsettings.value("importType", CFT_USR).toInt(); 39 | if (defaultImportType <= CFT_DEL || defaultImportType > CFT_REL) 40 | defaultImportType = CFT_USR; 41 | warnDiskCapacity = qsettings.value("warnDiskCapacity", true).toBool(); 42 | warnDirCapacity = qsettings.value("warnDirCapacity", true).toBool(); 43 | } 44 | 45 | Settings::priv::~priv() 46 | { 47 | qsettings.setValue("rememberPos", rememberWindowPositions); 48 | qsettings.setValue("geometry", mainGeometry); 49 | qsettings.setValue("petsciiGeometry", petsciiGeometry); 50 | qsettings.setValue("logGeometry", logGeometry); 51 | qsettings.setValue("lowercase", lowercase); 52 | qsettings.setValue("automap", automapPetsciiToLc); 53 | qsettings.setValue("exportPc64", exportAsPc64); 54 | qsettings.setValue("importType", (int)defaultImportType); 55 | qsettings.setValue("warnDiskCapacity", warnDiskCapacity); 56 | qsettings.setValue("warnDirCapacity", warnDirCapacity); 57 | } 58 | 59 | Settings::Settings() 60 | { 61 | d = new priv(); 62 | } 63 | 64 | Settings::~Settings() 65 | { 66 | delete d; 67 | } 68 | 69 | bool Settings::rememberWindowPositions() const 70 | { 71 | return d->rememberWindowPositions; 72 | } 73 | 74 | void Settings::setRememberWindowPositions(bool remember) 75 | { 76 | d->rememberWindowPositions = remember; 77 | } 78 | 79 | const QByteArray &Settings::mainGeometry() const 80 | { 81 | return d->mainGeometry; 82 | } 83 | 84 | void Settings::setMainGeometry(const QByteArray &geometry) 85 | { 86 | d->mainGeometry = geometry; 87 | } 88 | 89 | const QByteArray &Settings::petsciiGeometry() const 90 | { 91 | return d->petsciiGeometry; 92 | } 93 | 94 | void Settings::setPetsciiGeometry(const QByteArray &geometry) 95 | { 96 | d->petsciiGeometry = geometry; 97 | } 98 | 99 | const QByteArray &Settings::logGeometry() const 100 | { 101 | return d->logGeometry; 102 | } 103 | 104 | void Settings::setLogGeometry(const QByteArray &geometry) 105 | { 106 | d->logGeometry = geometry; 107 | } 108 | 109 | bool Settings::lowercase() const 110 | { 111 | return d->lowercase; 112 | } 113 | 114 | void Settings::setLowercase(bool lowercase) 115 | { 116 | d->lowercase = lowercase; 117 | } 118 | 119 | bool Settings::automapPetsciiToLc() const 120 | { 121 | return d->automapPetsciiToLc; 122 | } 123 | 124 | void Settings::setAutomapPetsciiToLc(bool automap) 125 | { 126 | d->automapPetsciiToLc = automap; 127 | } 128 | 129 | bool Settings::exportAsPc64() const 130 | { 131 | return d->exportAsPc64; 132 | } 133 | 134 | void Settings::setExportAsPc64(bool exportPc64) 135 | { 136 | d->exportAsPc64 = exportPc64; 137 | } 138 | 139 | CbmdosFileType Settings::defaultImportType() const 140 | { 141 | return d->defaultImportType; 142 | } 143 | 144 | void Settings::setDefaultImportType(CbmdosFileType type) 145 | { 146 | if (type > CFT_DEL && type <= CFT_REL) 147 | { 148 | d->defaultImportType = type; 149 | } 150 | } 151 | 152 | bool Settings::warnDiskCapacity() const 153 | { 154 | return d->warnDiskCapacity; 155 | } 156 | 157 | void Settings::setWarnDiskCapacity(bool warn) 158 | { 159 | d->warnDiskCapacity = warn; 160 | } 161 | 162 | bool Settings::warnDirCapacity() const 163 | { 164 | return d->warnDirCapacity; 165 | } 166 | 167 | void Settings::setWarnDirCapacity(bool warn) 168 | { 169 | d->warnDirCapacity = warn; 170 | } 171 | 172 | -------------------------------------------------------------------------------- /scripts/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo 4 | echo V1541Commander setup 5 | echo 6 | echo This script will install icons, a menu entry and filetype associations for 7 | echo V1541Commander, which will be used by desktop environments conforming to 8 | echo freedesktop.org, like KDE, GNOME and many others. 9 | echo 10 | 11 | if test $(id -u) = 0 ; then 12 | echo This script must not be run as root, exiting. 13 | echo 14 | exit 1; 15 | fi 16 | 17 | missingtool=n 18 | missingtesttool=n 19 | 20 | if ! tool="$(type xdg-icon-resource 2>/dev/null)" || test -z "$tool"; then 21 | echo ERROR: required tool \`xdg-icon-resource\' not found in path. 22 | missingtool=y 23 | fi 24 | 25 | if ! tool="$(type xdg-desktop-menu 2>/dev/null)" || test -z "$tool"; then 26 | echo ERROR: required tool \`xdg-desktop-menu\' not found in path. 27 | missingtool=y 28 | fi 29 | 30 | if ! tool="$(type xdg-mime 2>/dev/null)" || test -z "$tool"; then 31 | echo ERROR: required tool \`xdg-mime\' not found in path. 32 | missingtool=y 33 | fi 34 | 35 | if ! tool="$(type sed 2>/dev/null)" || test -z "$tool"; then 36 | echo ERROR: required tool \`sed\' not found. 37 | missingtool=y 38 | fi 39 | 40 | if ! tool="$(type dirname 2>/dev/null)" || test -z "$tool"; then 41 | echo ERROR: required tool \`dirname\' not found. 42 | missingtesttool=y 43 | fi 44 | 45 | if ! tool="$(type realpath 2>/dev/null)" || test -z "$tool"; then 46 | echo ERROR: required tool \`realpath\' not found. 47 | missingtesttool=y 48 | fi 49 | 50 | if test "$missingtesttool" = "y"; then 51 | exit 1; 52 | fi 53 | 54 | V1541DIR=$(dirname $0) 55 | V1541CMD=$(realpath $V1541DIR)/v1541commander 56 | cd "$V1541DIR" 57 | 58 | if test ! -x $V1541CMD; then 59 | echo ERROR: v1541commander not found. Please make sure to place this 60 | echo " script in the directory where v1541commander is located." 61 | missingtool=y 62 | fi 63 | 64 | if test "$missingtool" = "y"; then 65 | exit 1; 66 | fi 67 | 68 | read -p "Do you want to continue (y/n)? " yn 69 | case $yn in 70 | [Yy]* ) break;; 71 | * ) exit;; 72 | esac 73 | 74 | echo Updating desktop file ... 75 | sed -e "s:^Exec=v1541commander:Exec=$V1541CMD:" \ 76 | v1541commander-local.desktop 77 | echo Installing icons ... 78 | xdg-icon-resource install --novendor --mode user \ 79 | --size 16 icons/16x16/v1541commander.png 80 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 81 | --size 16 icons/16x16/v1541commander-d64.png v1541commander-d64 82 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 83 | --size 16 icons/16x16/v1541commander-zipcode.png v1541commander-zipcode 84 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 85 | --size 16 icons/16x16/v1541commander-lynx.png v1541commander-lynx 86 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 87 | --size 16 icons/16x16/v1541commander-prg.png v1541commander-prg 88 | xdg-icon-resource install --novendor --mode user \ 89 | --size 32 icons/32x32/v1541commander.png 90 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 91 | --size 32 icons/32x32/v1541commander-d64.png v1541commander-d64 92 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 93 | --size 32 icons/32x32/v1541commander-zipcode.png v1541commander-zipcode 94 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 95 | --size 32 icons/32x32/v1541commander-lynx.png v1541commander-lynx 96 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 97 | --size 32 icons/32x32/v1541commander-prg.png v1541commander-prg 98 | xdg-icon-resource install --novendor --mode user \ 99 | --size 48 icons/48x48/v1541commander.png 100 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 101 | --size 48 icons/48x48/v1541commander-d64.png v1541commander-d64 102 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 103 | --size 48 icons/48x48/v1541commander-zipcode.png v1541commander-zipcode 104 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 105 | --size 48 icons/48x48/v1541commander-lynx.png v1541commander-lynx 106 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 107 | --size 48 icons/48x48/v1541commander-prg.png v1541commander-prg 108 | xdg-icon-resource install --novendor --mode user \ 109 | --size 256 icons/256x256/v1541commander.png 110 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 111 | --size 256 icons/256x256/v1541commander-d64.png v1541commander-d64 112 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 113 | --size 256 icons/256x256/v1541commander-zipcode.png \ 114 | v1541commander-zipcode 115 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 116 | --size 256 icons/256x256/v1541commander-lynx.png v1541commander-lynx 117 | xdg-icon-resource install --context mimetypes --novendor --mode user \ 118 | --size 256 icons/256x256/v1541commander-prg.png v1541commander-prg 119 | echo Installing menu entry ... 120 | xdg-desktop-menu install --novendor --mode user v1541commander-local.desktop 121 | echo Installing file type associations ... 122 | xdg-mime install --novendor --mode user mime/v1541commander.xml 123 | echo All done. 124 | 125 | -------------------------------------------------------------------------------- /src/bin/v1541commander/main.cpp: -------------------------------------------------------------------------------- 1 | #include "v1541commander.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef QT_STATICPLUGIN 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #ifdef _WIN32 16 | #include 17 | #include 18 | #else 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #endif 25 | 26 | #ifndef _WIN32 27 | void handlesig(int sig) 28 | { 29 | (void) sig; 30 | _exit(0); 31 | } 32 | #endif 33 | 34 | static int guimain(int argc, char **argv); 35 | 36 | int main(int argc, char **argv) 37 | { 38 | #ifndef _WIN32 39 | #ifndef DEBUG 40 | pid_t pid = fork(); 41 | if (pid < 0) return 1; 42 | if (pid) 43 | { 44 | signal(SIGHUP, handlesig); 45 | for (;;) 46 | { 47 | int crc; 48 | if (wait(&crc) > 0) 49 | { 50 | if (WIFEXITED(crc)) return 0; 51 | else return WEXITSTATUS(crc); 52 | } 53 | } 54 | } 55 | setsid(); 56 | #endif 57 | #endif 58 | return guimain(argc, argv); 59 | } 60 | 61 | static int guimain(int argc, char **argv) 62 | { 63 | #ifdef QT_STATICPLUGIN 64 | #ifdef _WIN32 65 | Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); 66 | #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) 67 | Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin); 68 | #endif 69 | #else 70 | Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); 71 | #endif 72 | #endif 73 | 74 | QCoreApplication::setOrganizationName("Excess"); 75 | QCoreApplication::setApplicationName("V1541Commander"); 76 | QCoreApplication::setApplicationVersion("1.1"); 77 | 78 | QTranslator translator; 79 | QString qmsuffix = QLocale::system().name(); 80 | if (!translator.load(":/qm/v1541commander-"+qmsuffix)) 81 | { 82 | qmsuffix = QLocale::languageToString(QLocale::system().language()); 83 | translator.load(":/qm/v1541commander-"+qmsuffix); 84 | } 85 | V1541Commander commander(argc, argv, &translator); 86 | 87 | #ifdef QT_STATICPLUGIN 88 | QString qttransloc(":/qm"); 89 | #else 90 | QString qttransloc = QLibraryInfo::location( 91 | QLibraryInfo::TranslationsPath); 92 | #endif 93 | QTranslator qttrans; 94 | qmsuffix = QLocale::system().name(); 95 | if (!qttrans.load(qttransloc+"/qtbase_"+qmsuffix)) 96 | { 97 | qmsuffix = QLocale::languageToString(QLocale::system().language()); 98 | qttrans.load(qttransloc+":/qtbase_"+qmsuffix); 99 | } 100 | commander.installTranslator(&qttrans); 101 | 102 | #ifdef _WIN32 103 | #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) \ 104 | && QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 105 | // work around a QSpinBox layout bug in Qt 5.13: 106 | commander.setStyleSheet("QFoo{}"); 107 | #endif 108 | #endif 109 | 110 | QCommandLineParser parser; 111 | parser.setApplicationDescription(QCoreApplication::translate("main", 112 | "virtual 1541 disk image commander")); 113 | parser.addHelpOption(); 114 | parser.addVersionOption(); 115 | parser.addPositionalArgument(QCoreApplication::translate("main", "file"), 116 | QCoreApplication::translate("main", "file(s) to open (D64 disk " 117 | "images) or import (ZipCode, LyNX)."), 118 | QCoreApplication::translate("main", "[file ...]")); 119 | parser.process(commander); 120 | 121 | const QStringList &positionalArgs = parser.positionalArguments(); 122 | 123 | if (commander.isPrimaryInstance()) 124 | { 125 | #ifndef _WIN32 126 | #ifndef DEBUG 127 | freopen("/dev/null", "r", stdin); 128 | freopen("/dev/null", "w", stdout); 129 | freopen("/dev/null", "w", stderr); 130 | kill(getppid(), SIGHUP); 131 | #endif 132 | #endif 133 | 134 | commander.show(); 135 | 136 | for (QStringList::const_iterator i = positionalArgs.constBegin(); 137 | i != positionalArgs.constEnd(); ++i) 138 | { 139 | const QString &path = QFileInfo(*i).canonicalFilePath(); 140 | if (!path.isEmpty()) commander.open(path); 141 | } 142 | 143 | return commander.exec(); 144 | } 145 | else 146 | { 147 | QLocalSocket sock; 148 | sock.connectToServer(commander.instanceServerName()); 149 | if (sock.state() == QLocalSocket::ConnectedState 150 | || sock.waitForConnected(5000)) 151 | { 152 | QDataStream stream(&sock); 153 | if (sock.waitForReadyRead(5000)) 154 | { 155 | qint64 mainpid; 156 | #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) 157 | stream.startTransaction(); 158 | #endif 159 | stream >> mainpid; 160 | #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) 161 | #ifdef _WIN32 162 | if (stream.commitTransaction()) 163 | { 164 | AllowSetForegroundWindow(DWORD(mainpid)); 165 | } 166 | #else 167 | stream.commitTransaction(); 168 | #endif 169 | #else 170 | #ifdef _WIN32 171 | AllowSetForegroundWindow(DWORD(mainpid)); 172 | #endif 173 | #endif 174 | } 175 | for (QStringList::const_iterator i = positionalArgs.constBegin(); 176 | i != positionalArgs.constEnd(); ++i) 177 | { 178 | const QString &path = QFileInfo(*i).canonicalFilePath(); 179 | if (!path.isEmpty()) stream << path; 180 | } 181 | sock.flush(); 182 | sock.disconnectFromServer(); 183 | sock.state() == QLocalSocket::UnconnectedState || 184 | sock.waitForDisconnected(1000); 185 | } 186 | return 0; 187 | } 188 | } 189 | 190 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfswidget.cpp: -------------------------------------------------------------------------------- 1 | #include "cbmdosfswidget.h" 2 | #include "petsciistr.h" 3 | #include "petsciiedit.h" 4 | #include "v1541commander.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include <1541img/cbmdosfs.h> 14 | #include <1541img/cbmdosvfs.h> 15 | #include <1541img/cbmdosvfseventargs.h> 16 | #include <1541img/event.h> 17 | 18 | static void evhdl(void *receiver, int id, const void *sender, const void *args) 19 | { 20 | (void) id; 21 | (void) sender; 22 | 23 | CbmdosFsWidget *widget = (CbmdosFsWidget *)receiver; 24 | const CbmdosVfsEventArgs *eventArgs = (const CbmdosVfsEventArgs *)args; 25 | widget->fsChanged(eventArgs); 26 | } 27 | 28 | class CbmdosFsWidget::priv 29 | { 30 | public: 31 | priv(); 32 | CbmdosFs *fs; 33 | QVBoxLayout layout; 34 | QHBoxLayout nameLayout; 35 | QLabel nameLabel; 36 | PetsciiEdit name; 37 | QHBoxLayout idLayout; 38 | QLabel idLabel; 39 | PetsciiEdit id; 40 | QLabel dosVerLabel; 41 | QSpinBox dosVer; 42 | QPushButton dosVerReset; 43 | }; 44 | 45 | CbmdosFsWidget::priv::priv() : 46 | fs(0), 47 | layout(), 48 | nameLayout(), 49 | nameLabel(tr("Name: ")), 50 | name(), 51 | idLayout(), 52 | idLabel(tr("ID: ")), 53 | id(), 54 | dosVerLabel(tr("DOS Version:")), 55 | dosVer(), 56 | dosVerReset(tr("reset")) 57 | { 58 | } 59 | 60 | CbmdosFsWidget::CbmdosFsWidget(QWidget *parent) 61 | : QGroupBox(tr("Disk properties"), parent) 62 | { 63 | d = new priv(); 64 | d->name.setMaxLength(16); 65 | d->nameLayout.addWidget(&d->nameLabel); 66 | d->nameLayout.addWidget(&d->name); 67 | d->id.setMaxLength(5); 68 | d->dosVer.setMinimum(0); 69 | d->dosVer.setMaximum(0xff); 70 | d->dosVer.setDisplayIntegerBase(16); 71 | QFont dosVerFont = d->dosVer.font(); 72 | dosVerFont.setCapitalization(QFont::AllUppercase); 73 | d->dosVer.setFont(dosVerFont); 74 | d->idLayout.addWidget(&d->idLabel); 75 | d->idLayout.addWidget(&d->id); 76 | d->idLayout.addWidget(&d->dosVerLabel); 77 | d->idLayout.addWidget(&d->dosVer); 78 | d->idLayout.addWidget(&d->dosVerReset); 79 | d->layout.addLayout(&d->nameLayout); 80 | d->layout.addLayout(&d->idLayout); 81 | setLayout(&d->layout); 82 | setEnabled(false); 83 | connect(&d->name, &PetsciiEdit::petsciiEdited, 84 | this, &CbmdosFsWidget::nameChanged); 85 | connect(&d->id, &PetsciiEdit::petsciiEdited, 86 | this, &CbmdosFsWidget::idChanged); 87 | connect(&d->dosVer, SIGNAL(valueChanged(int)), 88 | this, SLOT(dosVerChanged(int))); 89 | connect(&d->dosVerReset, &QPushButton::clicked, 90 | this, &CbmdosFsWidget::dosVerReset); 91 | 92 | QShortcut *sf2 = new QShortcut(QKeySequence(Qt::SHIFT+Qt::Key_F2), this); 93 | connect(sf2, &QShortcut::activated, this, [this](){ 94 | d->name.selectAll(); 95 | d->name.setFocus(); 96 | }); 97 | d->name.setToolTip(tr("The name of the disk (Shift+F2)")); 98 | d->nameLabel.setToolTip(tr("The name of the disk (Shift+F2)")); 99 | QShortcut *sf3 = new QShortcut(QKeySequence(Qt::SHIFT+Qt::Key_F3), this); 100 | connect(sf3, &QShortcut::activated, this, [this](){ 101 | d->id.selectAll(); 102 | d->id.setFocus(); 103 | }); 104 | d->id.setToolTip(tr("The ID of the disk (Shift+F3)")); 105 | d->idLabel.setToolTip(tr("The ID of the disk (Shift+F3)")); 106 | QShortcut *f4 = new QShortcut(QKeySequence(Qt::Key_F4), this); 107 | connect(f4, &QShortcut::activated, this, [this](){ 108 | d->dosVer.selectAll(); 109 | d->dosVer.setFocus(); 110 | }); 111 | d->dosVer.setToolTip(tr("DOS version, non-default value is \"soft " 112 | "write protection\" (F4)")); 113 | d->dosVerLabel.setToolTip(tr("DOS version, non-default value is \"soft " 114 | "write protection\" (F4)")); 115 | QShortcut *sf4 = new QShortcut(QKeySequence(Qt::SHIFT+Qt::Key_F4), this); 116 | connect(sf4, &QShortcut::activated, &d->dosVerReset, &QPushButton::click); 117 | d->dosVerReset.setToolTip(tr("Reset DOS version to default value " 118 | "(Shift+F4)")); 119 | 120 | connect(&cmdr, &V1541Commander::lowerCaseChanged, 121 | &d->name, &PetsciiEdit::updateCase); 122 | connect(&cmdr, &V1541Commander::lowerCaseChanged, 123 | &d->id, &PetsciiEdit::updateCase); 124 | } 125 | 126 | CbmdosFsWidget::~CbmdosFsWidget() 127 | { 128 | delete d; 129 | } 130 | 131 | void CbmdosFsWidget::nameChanged(const PetsciiStr &name) 132 | { 133 | if (d->fs) 134 | { 135 | CbmdosVfs *vfs = CbmdosFs_vfs(d->fs); 136 | CbmdosVfs_setName(vfs, name.petscii(), name.length()); 137 | } 138 | } 139 | 140 | void CbmdosFsWidget::idChanged(const PetsciiStr &id) 141 | { 142 | if (d->fs) 143 | { 144 | CbmdosVfs *vfs = CbmdosFs_vfs(d->fs); 145 | CbmdosVfs_setId(vfs, id.petscii(), id.length()); 146 | } 147 | } 148 | 149 | static uint8_t defaultDosVer(const CbmdosFs *fs) 150 | { 151 | if (!fs) return 0x41; 152 | if (CbmdosFs_options(fs).flags & CbmdosFsFlags::CFF_PROLOGICDOSBAM) 153 | { 154 | return 0x50; 155 | } 156 | else return 0x41; 157 | } 158 | 159 | void CbmdosFsWidget::dosVerChanged(int val) 160 | { 161 | if (d->fs) 162 | { 163 | d->dosVerReset.setEnabled(val != defaultDosVer(d->fs)); 164 | CbmdosVfs *vfs = CbmdosFs_vfs(d->fs); 165 | CbmdosVfs_setDosver(vfs, val); 166 | } 167 | } 168 | 169 | void CbmdosFsWidget::dosVerReset() 170 | { 171 | d->dosVer.setValue(defaultDosVer(d->fs)); 172 | } 173 | 174 | CbmdosFs *CbmdosFsWidget::fs() const 175 | { 176 | return d->fs; 177 | } 178 | 179 | void CbmdosFsWidget::setFs(CbmdosFs *fs) 180 | { 181 | if (d->fs) 182 | { 183 | Event_unregister( 184 | CbmdosVfs_changedEvent(CbmdosFs_vfs(d->fs)), this, evhdl); 185 | } 186 | d->fs = 0; 187 | if (fs) 188 | { 189 | setEnabled(true); 190 | const CbmdosVfs *vfs = CbmdosFs_rvfs(fs); 191 | uint8_t len; 192 | const char *str = CbmdosVfs_name(vfs, &len); 193 | d->name.setPetscii(PetsciiStr(str, len)); 194 | str = CbmdosVfs_id(vfs, &len); 195 | d->id.setPetscii(PetsciiStr(str, len)); 196 | uint8_t dosver = CbmdosVfs_dosver(vfs); 197 | d->dosVer.setValue(dosver); 198 | d->dosVerReset.setEnabled(dosver != defaultDosVer(fs)); 199 | Event_register(CbmdosVfs_changedEvent(CbmdosFs_vfs(fs)), this, evhdl); 200 | } 201 | else 202 | { 203 | setEnabled(false); 204 | d->name.setText(QString()); 205 | d->id.setText(QString()); 206 | d->dosVer.setValue(0); 207 | } 208 | d->fs = fs; 209 | } 210 | 211 | void CbmdosFsWidget::fsChanged(const CbmdosVfsEventArgs *args) 212 | { 213 | uint8_t len; 214 | const char *str; 215 | switch (args->what) 216 | { 217 | case CbmdosVfsEventArgs::CVE_NAMECHANGED: 218 | str = CbmdosVfs_name(CbmdosFs_rvfs(d->fs), &len); 219 | d->name.setPetscii(PetsciiStr(str, len), true); 220 | break; 221 | 222 | case CbmdosVfsEventArgs::CVE_IDCHANGED: 223 | str = CbmdosVfs_id(CbmdosFs_rvfs(d->fs), &len); 224 | d->id.setPetscii(PetsciiStr(str, len), true); 225 | break; 226 | 227 | default: 228 | break; 229 | } 230 | } 231 | 232 | -------------------------------------------------------------------------------- /FAQ.txt: -------------------------------------------------------------------------------- 1 | v1541commander -- frequently asked questions 2 | ============================================ 3 | 4 | This document contains actual questions asked by the users and might be 5 | updated from time to time. 6 | 7 | 8 | 1. About the BAM 9 | ---------------- 10 | 11 | Q: What does an "invalid BAM" mean? 12 | 13 | A: On a 1541 disk, the BAM (block availability map) stores which sectors (or 14 | blocks) are used by files and which are available. It also stores the 15 | number of available blocks per track. 16 | 17 | When V1541Commander opens a disk, it tries to read a CBM DOS filesystem 18 | from it and doing this, the BAM is validated. First, it is checked whether 19 | the available blocks per track actually match the BAM data. It is also 20 | acceptable if this number is 0 for *all* tracks, because there are disks 21 | using this to report a fake "0 blocks free." line. 22 | 23 | Then, the BAM data is compared to the blocks actually used by the 24 | directory and files found on the disk. This check will fail if there are 25 | things on the disk V1541Commander doesn't know, like for example 26 | trackloaders, C128 boot sectors, GEOS files, etc. 27 | 28 | When the BAM is found to be invalid, V1541Commander suggests to treat the 29 | image as a "new" image, so you don't accidentally overwrite your original 30 | image when saving after making some changes. 31 | 32 | 33 | Q: What happens when I edit a disk with an "invalid BAM"? 34 | 35 | A: This depends on how the BAM is invalid. If there are some additional blocks 36 | marked as used, you just can't use these when writing files. If some blocks 37 | actually used by files are marked "available", writing files to this disk 38 | *could* overwrite these blocks, destroying other files. 39 | 40 | It's probably best to save your edited disk as a new image and thoroughly 41 | test it. 42 | 43 | 44 | Q: How can I fix an "invalid BAM"? 45 | 46 | A: Use "rewrite image" from the "CBM DOS" menu. But be warned that this 47 | creates a new disk image from the current filesystem. Any data 48 | V1541Commander doesn't understand (like trackloaders, C128 boot sectors, 49 | etc) will be missing on the disk after rewriting the image. 50 | 51 | 52 | 2. Broken filesystem and recovery mode 53 | -------------------------------------- 54 | 55 | Q: What is a broken filesystem? 56 | 57 | A: A filesystem stored on a 1541 disk can have some severe consistency 58 | problems. A typical problem would be a sector that is used twice (e.g. 59 | by the directory and a file, or by two different files). If such a problem 60 | is found, V1541Commander tells you the filesystem is broken and will be 61 | opened in recovery mode. The default action is to rewrite the image 62 | directly after opening, so you have a clean image with the data that could 63 | be recovered, but you can disable this. 64 | 65 | 66 | Q: What does open in recovery mode mean? 67 | 68 | A: Recovery mode is automatically used to open a filesystem from a disk image 69 | when severe problems with the filesystem are found. In recovery mode, 70 | V1541Commander tries to read as much file data as possible, while avoiding 71 | to read a sector twice. There is no guarantee that everything can be read. 72 | 73 | 74 | Q: Can I edit a broken filesystem? 75 | 76 | A: Short answer: no. A broken filesystem will be read-only. But you can copy 77 | files from it to another disk image or to your file manager using 78 | drag&drop. Of course, you can rewrite the disk image to get a new, clean 79 | image from whatever was successfully read, and then you can fully edit 80 | the image. Be aware you might lose data doing this. 81 | 82 | 83 | Q: I downloaded a Zipcode compressed disk and V1541Commander tells me it has 84 | a broken filesystem, what should I do? 85 | 86 | A: In most cases, your download is probably fine. Even a broken filesystem 87 | *could* work flawlessly for reading on a 1541 drive. To save the extracted 88 | image without any changes, do the following: 89 | - In the options dialog popping up, uncheck the checkbox saying to rewrite 90 | the image after recovery. 91 | - Then, save the disk from the file menu (or by hitting Ctrl+S). 92 | - Answer yes to the safety question asking you whether you really want to 93 | save an image with a broken filesystem. 94 | This process will save the exact image that was found in the Zipcode files. 95 | 96 | 97 | 3. Official builds 98 | ------------------ 99 | 100 | Q: My virus scanner reports V1541Commander to be infected / be malware / eat 101 | my cat / ... ? 102 | 103 | A: Virus scanners use heuristics, so "false positives" happen all the time. 104 | If you downloaded your copy from csdb.dk (released by "Excess"), you can be 105 | very sure there is no virus. The only risk would be someone hacked csdb and 106 | tampered with the files there ... 107 | 108 | Official releases are built on a FreeBSD machine using GNU GCC-based cross- 109 | compilers for Windows and Linux, you can see the scripts used in the git 110 | repository. Still, if you don't trust our official builds, you can always 111 | build your own from the source :) 112 | 113 | 114 | Q: But why does a virus scanner think there is some malware? 115 | 116 | A: Most likely, one of the heuristics used by the scanner found a pattern that 117 | resembles something used in malware before. Unfortunately, this can't be 118 | fully avoided. At the time of the V1.1 release, the official windows build 119 | was checked at virustotal.com, with one out of 70 virus scanners (falsely!) 120 | reporting some malware. 121 | 122 | If you use an older virus scanner, it's also possible that your scanner 123 | thinks the cruncher used on the official builds (UPX) is suspicious. This 124 | isn't an issue any more with recent AV software, they all know UPX and will 125 | scan the decrunched executable. 126 | 127 | 128 | Q: Why are the official builds crunched? 129 | 130 | A: The official builds use static linking. This means all libraries used are 131 | embedded in the executable. Static linking is a good way to ensure the 132 | program runs on any system without problems and doesn't require 133 | installation. An alternative would be to ship all required libraries (DLLs 134 | on Windows) with the executable, but this would even take up more space. 135 | Static linking also ensures the executable is self-contained, so it will 136 | work no matter where you put it, not requiring any additional files. 137 | 138 | Still, a statically linked executable is quite large and a cruncher can 139 | drastically reduce the size. At the time of writing, the Windows build of 140 | V1541Commander has around 14.5 MB uncrunched, but only 5.1 MB crunched. 141 | Given today's standards, this doesn't sound like much, but imagine you want 142 | to carry your copy of V1541Commander on your USB key ... 143 | 144 | 145 | Q: Doesn't crunching have drawbacks? 146 | 147 | A: In short: no, not for V1541Commander. There are several typical drawbacks: 148 | 149 | - decrunching takes time: Well, this isn't an issue nowadays. Decrunching 150 | a few MB happens faster than you can look on your typical PC, you won't 151 | be able to notice. 152 | 153 | - decrunching needs more RAM: This depends on the cruncher. UPX decrunches 154 | "in place" (just like the famous exomizer for the C64 does), so it 155 | doesn't need any additional RAM. 156 | 157 | - multiple instances of crunched programs can't share the memory pages for 158 | code: This is actually true. If you run multiple copies of a program on 159 | a modern OS, the OS knows the code is the same and keeps it in RAM only 160 | once. This won't work with decrunched code because it's changed compared 161 | to the code on disk. But V1541Commander is designed as a "single 162 | instance" application: Any second instance will just talk to the already 163 | running one and immediately quit. So, this isn't a drawback for 164 | V1541Commander. 165 | 166 | - virus scanners consider crunched programs suspicious: At least for 167 | programs crunched with UPX, this is a thing from the past. Nowadays, all 168 | major virus scanners know UPX and decrunch before scanning. If your virus 169 | scanner is unhappy with V1541Commander, consider to upgrade. 170 | 171 | -------------------------------------------------------------------------------- /src/bin/uninstall/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define OEMRESOURCE 4 | #include 5 | #include 6 | #include 7 | 8 | #define WC_mainWindow L"V1541CommanderUninstall" 9 | #define CID_uninstall 0x101 10 | #define CID_cancel 0x102 11 | 12 | static HINSTANCE instance; 13 | static HWND mainWindow; 14 | static NONCLIENTMETRICSW ncm; 15 | static HFONT messageFont; 16 | static TEXTMETRICW messageFontMetrics; 17 | static int buttonWidth; 18 | static int buttonHeight; 19 | 20 | static const WCHAR *locales[] = { 21 | L"C", 22 | L"de", 23 | }; 24 | 25 | enum textid { 26 | TID_title, 27 | TID_success_title, 28 | TID_success_message, 29 | TID_failure_title, 30 | TID_failure_message, 31 | TID_message, 32 | TID_uninstall, 33 | TID_cancel, 34 | 35 | TID_N_texts 36 | }; 37 | 38 | static const WCHAR *locale_texts[][TID_N_texts] = { 39 | { 40 | L"V1541Commander Uninstall", 41 | L"Uninstall completed", 42 | L"All filetype associations for V1541Commander\n" 43 | L"were removed successfully.", 44 | L"Uninstall failed", 45 | L"There was an unexpected error removing\n" 46 | L"file type associations for V1541Commander.", 47 | L"This tool unregisters V1541Commander with windows\n" 48 | L"and removes all file type associations created by\n" 49 | L"setup.exe.", 50 | L"Uninstall", 51 | L"Cancel", 52 | }, 53 | { 54 | L"V1541Commander entfernen", 55 | L"Deinstallation erfolgreich", 56 | L"Alle Zuordnungen von Dateitypen für V1541Commander\n" 57 | L"wurden erfolgreich entfernt.", 58 | L"Deinstallation fehlgeschlagen", 59 | L"Beim Entfernen von Dateityp-Zuordnungen für V1541Commander\n" 60 | L"ist ein unerwarteter Fehler aufgetreten.", 61 | L"Dieses Programm deregistriert V1541Commander in Windows\n" 62 | L"und entfernt alle Dateityp-Zuordnungen, die von setup.exe\n" 63 | L"erstellt wurden.\n", 64 | L"Entfernen", 65 | L"Abbrechen", 66 | }, 67 | }; 68 | 69 | static const WCHAR **texts = locale_texts[0]; 70 | 71 | static void init(void) 72 | { 73 | INITCOMMONCONTROLSEX icx; 74 | icx.dwSize = sizeof icx; 75 | icx.dwICC = ICC_WIN95_CLASSES; 76 | InitCommonControlsEx(&icx); 77 | ncm.cbSize = sizeof ncm; 78 | SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0); 79 | messageFont = CreateFontIndirectW(&ncm.lfMessageFont); 80 | HDC dc = GetDC(0); 81 | SelectObject(dc, (HGDIOBJ) messageFont); 82 | GetTextMetricsW(dc, &messageFontMetrics); 83 | SIZE sampleSize; 84 | GetTextExtentExPointW(dc, 85 | L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 86 | 52, 0, 0, 0, &sampleSize); 87 | ReleaseDC(0, dc); 88 | buttonWidth = MulDiv(sampleSize.cx, 50, 4 * 52); 89 | buttonHeight = MulDiv(messageFontMetrics.tmHeight, 14, 8); 90 | instance = GetModuleHandleW(0); 91 | 92 | WCHAR lang[10]; 93 | if (GetLocaleInfoW(LOCALE_USER_DEFAULT, 94 | LOCALE_SISO639LANGNAME, lang, 10) > 0) 95 | { 96 | for (size_t i = 1; i < sizeof locales / sizeof *locales; ++i) 97 | { 98 | if (!wcscmp(locales[i], lang)) 99 | { 100 | texts = locale_texts[i]; 101 | break; 102 | } 103 | } 104 | } 105 | } 106 | 107 | static void regTreeDel(HKEY key, LPCWSTR subKey) 108 | { 109 | HKEY sub; 110 | if (RegOpenKeyExW(key, subKey, 0, DELETE|KEY_ENUMERATE_SUB_KEYS, &sub) 111 | == ERROR_SUCCESS) 112 | { 113 | WCHAR subName[256]; 114 | DWORD subNameLen; 115 | while (subNameLen = 256, 116 | RegEnumKeyExW(sub, 0, subName, &subNameLen, 0, 0, 0, 0) 117 | == ERROR_SUCCESS) 118 | { 119 | regTreeDel(sub, subName); 120 | } 121 | RegCloseKey(sub); 122 | RegDeleteKeyW(key, subKey); 123 | } 124 | } 125 | 126 | static int unregisterType(HKEY classes, LPCWSTR ext, LPCWSTR name) 127 | { 128 | int success = 1; 129 | 130 | HKEY ekey; 131 | 132 | if (RegOpenKeyExW(classes, ext, 0, KEY_WRITE|KEY_QUERY_VALUE, &ekey) 133 | == ERROR_SUCCESS) 134 | { 135 | WCHAR value[128]; 136 | DWORD len = 128; 137 | DWORD valueType; 138 | if (RegQueryValueExW(ekey, 0, 0, &valueType, (LPBYTE)&value, &len) 139 | == ERROR_SUCCESS) 140 | { 141 | if (valueType != REG_SZ) 142 | { 143 | success = 0; 144 | } 145 | else 146 | { 147 | value[len] = L'\0'; 148 | if (!wcscmp(value, name)) 149 | { 150 | if (RegDeleteValueW(ekey, 0) != ERROR_SUCCESS) 151 | { 152 | success = 0; 153 | } 154 | } 155 | } 156 | } 157 | 158 | HKEY owkey; 159 | if (RegOpenKeyExW(ekey, L"OpenWithProgids", 0, KEY_WRITE, &owkey) 160 | == ERROR_SUCCESS) 161 | { 162 | RegDeleteValueW(owkey, name); 163 | RegCloseKey(owkey); 164 | } 165 | RegCloseKey(ekey); 166 | } 167 | 168 | regTreeDel(classes, name); 169 | 170 | return success; 171 | } 172 | 173 | static void unregister(HWND w) 174 | { 175 | int success = 1; 176 | 177 | HKEY key; 178 | if (RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\" 179 | "CurrentVersion\\App Paths", 0, 180 | DELETE|KEY_ENUMERATE_SUB_KEYS, &key) == ERROR_SUCCESS) 181 | { 182 | regTreeDel(key, L"v1541commander.exe"); 183 | RegCloseKey(key); 184 | } 185 | if (RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\Applications", 186 | 0, DELETE|KEY_ENUMERATE_SUB_KEYS, &key) == ERROR_SUCCESS) 187 | { 188 | regTreeDel(key, L"v1541commander.exe"); 189 | RegCloseKey(key); 190 | } 191 | if (RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Classes", 0, 192 | KEY_WRITE|KEY_ENUMERATE_SUB_KEYS, &key) == ERROR_SUCCESS) 193 | { 194 | if (!unregisterType(key, L".prg", L"V1541Commander.Zipcode")) 195 | { 196 | success = 0; 197 | } 198 | if (!unregisterType(key, L".prg", L"V1541Commander.PRG")) 199 | { 200 | success = 0; 201 | } 202 | if (!unregisterType(key, L".lnx", L"V1541Commander.LyNX")) 203 | { 204 | success = 0; 205 | } 206 | if (!unregisterType(key, L".d64", L"V1541Commander.D64")) 207 | { 208 | success = 0; 209 | } 210 | RegCloseKey(key); 211 | } 212 | else success = 0; 213 | 214 | SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0); 215 | if (success) 216 | { 217 | MessageBoxW(w, texts[TID_success_message], 218 | texts[TID_success_title], MB_OK|MB_ICONINFORMATION); 219 | } 220 | else 221 | { 222 | MessageBoxW(w, texts[TID_failure_message], 223 | texts[TID_failure_title], MB_OK|MB_ICONERROR); 224 | } 225 | } 226 | 227 | static LRESULT CALLBACK wproc(HWND w, UINT msg, WPARAM wp, LPARAM lp) 228 | { 229 | switch (msg) 230 | { 231 | case WM_CREATE: 232 | { 233 | HDC dc = GetDC(0); 234 | SelectObject(dc, (HGDIOBJ) messageFont); 235 | RECT textrect = {0, 0, 0, 0}; 236 | int padding = messageFontMetrics.tmAveCharWidth * 3 / 2; 237 | int ypos = padding; 238 | 239 | const WCHAR *text = texts[TID_message]; 240 | DrawTextExW(dc, (WCHAR *)text, -1, &textrect, DT_CALCRECT, 0); 241 | int fullwidth = textrect.right; 242 | HWND ctrl = CreateWindowExW(0, L"Static", text, 243 | WS_CHILD|WS_VISIBLE, padding, ypos, 244 | textrect.right, textrect.bottom, w, 0, instance, 0); 245 | SendMessageW(ctrl, WM_SETFONT, (WPARAM)messageFont, 0); 246 | ypos += textrect.bottom + padding; 247 | 248 | ctrl = CreateWindowExW(0, L"Button", texts[TID_uninstall], 249 | WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON, 250 | fullwidth - 2*buttonWidth, ypos, 251 | buttonWidth, buttonHeight, 252 | w, (HMENU)CID_uninstall, instance, 0); 253 | SendMessageW(ctrl, WM_SETFONT, (WPARAM)messageFont, 0); 254 | ctrl = CreateWindowExW(0, L"Button", texts[TID_cancel], 255 | WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 256 | padding + fullwidth - buttonWidth, ypos, 257 | buttonWidth, buttonHeight, 258 | w, (HMENU)CID_cancel, instance, 0); 259 | SendMessageW(ctrl, WM_SETFONT, (WPARAM)messageFont, 0); 260 | ypos += buttonHeight + padding; 261 | 262 | ReleaseDC(0, dc); 263 | RECT winRect = {0, 0, fullwidth + 2*padding, ypos}; 264 | AdjustWindowRect(&winRect, WS_CAPTION|WS_SYSMENU, 0); 265 | SetWindowPos(w, HWND_TOP, 0, 0, 266 | winRect.right - winRect.left, 267 | winRect.bottom - winRect.top, SWP_NOMOVE); 268 | } 269 | break; 270 | 271 | case WM_DESTROY: 272 | PostQuitMessage(0); 273 | break; 274 | 275 | case WM_KEYDOWN: 276 | if (wp == VK_RETURN) 277 | { 278 | unregister(w); 279 | DestroyWindow(w); 280 | } 281 | else if (wp == VK_ESCAPE) 282 | { 283 | DestroyWindow(w); 284 | } 285 | break; 286 | 287 | case WM_COMMAND: 288 | switch (LOWORD(wp)) 289 | { 290 | case CID_uninstall: 291 | unregister(w); 292 | /* fall through */ 293 | case CID_cancel: 294 | DestroyWindow(w); 295 | break; 296 | } 297 | break; 298 | } 299 | return DefWindowProcW(w, msg, wp, lp); 300 | } 301 | 302 | int main(void) 303 | { 304 | init(); 305 | 306 | WNDCLASSEXW wc; 307 | memset(&wc, 0, sizeof wc); 308 | wc.cbSize = sizeof wc; 309 | wc.hInstance = instance; 310 | wc.lpszClassName = WC_mainWindow; 311 | wc.lpfnWndProc = wproc; 312 | wc.hbrBackground = (HBRUSH) COLOR_WINDOW; 313 | wc.hCursor = (HCURSOR) LoadImageW(0, MAKEINTRESOURCEW(OCR_NORMAL), 314 | IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED); 315 | RegisterClassExW(&wc); 316 | 317 | mainWindow = CreateWindowExW(0, WC_mainWindow, texts[TID_title], 318 | WS_CAPTION|WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, 319 | 320, 100, 0, 0, instance, 0); 320 | ShowWindow(mainWindow, SW_SHOWNORMAL); 321 | 322 | MSG msg; 323 | while (GetMessageW(&msg, 0, 0, 0) > 0) 324 | { 325 | TranslateMessage(&msg); 326 | DispatchMessageW(&msg); 327 | } 328 | return (int)msg.wParam; 329 | } 330 | 331 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfsmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "cbmdosfsmodel.h" 2 | #include "cbmdosfilemimedata.h" 3 | #include "editoperationcheck.h" 4 | #include "v1541commander.h" 5 | #include "petsciistr.h" 6 | #include "settings.h" 7 | #include "utils.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include <1541img/cbmdosfs.h> 16 | #include <1541img/cbmdosvfs.h> 17 | #include <1541img/cbmdosvfseventargs.h> 18 | #include <1541img/cbmdosfile.h> 19 | #include <1541img/event.h> 20 | #include <1541img/petscii.h> 21 | 22 | static void evhdl(void *receiver, int id, const void *sender, const void *args) 23 | { 24 | (void) id; 25 | (void) sender; 26 | 27 | CbmdosFsModel *model = (CbmdosFsModel *)receiver; 28 | const CbmdosVfsEventArgs *eventArgs = (const CbmdosVfsEventArgs *)args; 29 | model->fsChanged(eventArgs); 30 | } 31 | 32 | class CbmdosFsModel::priv 33 | { 34 | public: 35 | priv(); 36 | ~priv(); 37 | QTemporaryDir *tmpDir; 38 | CbmdosFs *fs; 39 | QSizeF itemSize; 40 | }; 41 | 42 | CbmdosFsModel::priv::priv() : 43 | tmpDir(0), fs(0), itemSize() 44 | {} 45 | 46 | CbmdosFsModel::priv::~priv() 47 | { 48 | delete tmpDir; 49 | } 50 | 51 | CbmdosFsModel::CbmdosFsModel(QObject *parent) 52 | : QAbstractListModel(parent) 53 | { 54 | d = new priv(); 55 | 56 | connect(&cmdr, &V1541Commander::lowerCaseChanged, 57 | this, [this](bool lowerCase){ 58 | (void) lowerCase; 59 | emit dataChanged(createIndex(0, 0), 60 | createIndex(rowCount()-1, 0), 61 | QVector(Qt::DisplayRole)); 62 | }); 63 | } 64 | 65 | CbmdosFsModel::~CbmdosFsModel() 66 | { 67 | if (d->fs) 68 | { 69 | CbmdosVfs *vfs = CbmdosFs_vfs(d->fs); 70 | Event_unregister(CbmdosVfs_changedEvent(vfs), this, evhdl); 71 | } 72 | delete d; 73 | } 74 | 75 | void CbmdosFsModel::fsChanged(const CbmdosVfsEventArgs *args) 76 | { 77 | emit modified(); 78 | switch (args->what) 79 | { 80 | case CbmdosVfsEventArgs::CVE_FILECHANGED: 81 | { 82 | QModelIndex pos = createIndex(args->filepos + 1, 0); 83 | emit dataChanged(pos, pos, QVector(Qt::DisplayRole)); 84 | } 85 | break; 86 | 87 | case CbmdosVfsEventArgs::CVE_FILEMOVED: 88 | if (args->targetpos > args->filepos) 89 | { 90 | QModelIndex from = createIndex(args->filepos + 1, 0); 91 | QModelIndex to = createIndex(args->targetpos + 1, 0); 92 | emit dataChanged(from, to, QVector(Qt::DisplayRole)); 93 | emit selectedIndexChanged(to, 94 | QItemSelectionModel::ClearAndSelect); 95 | } 96 | else 97 | { 98 | QModelIndex from = createIndex(args->targetpos + 1, 0); 99 | QModelIndex to = createIndex(args->filepos + 1, 0); 100 | emit dataChanged(from, to, QVector(Qt::DisplayRole)); 101 | emit selectedIndexChanged(from, 102 | QItemSelectionModel::ClearAndSelect); 103 | } 104 | break; 105 | 106 | case CbmdosVfsEventArgs::CVE_NAMECHANGED: 107 | case CbmdosVfsEventArgs::CVE_IDCHANGED: 108 | case CbmdosVfsEventArgs::CVE_DOSVERCHANGED: 109 | { 110 | QModelIndex pos = createIndex(0, 0); 111 | emit dataChanged(pos, pos, QVector(Qt::DisplayRole)); 112 | } 113 | break; 114 | 115 | case CbmdosVfsEventArgs::CVE_FILEDELETED: 116 | { 117 | endRemoveRows(); 118 | QModelIndex last = createIndex(rowCount() - 1, 0); 119 | emit dataChanged(last, last, QVector(Qt::DisplayRole)); 120 | } 121 | break; 122 | 123 | case CbmdosVfsEventArgs::CVE_FILEADDED: 124 | { 125 | endInsertRows(); 126 | QModelIndex last = createIndex(rowCount() - 1, 0); 127 | emit dataChanged(last, last, QVector(Qt::DisplayRole)); 128 | QModelIndex at = createIndex(args->filepos + 1, 0); 129 | emit selectedIndexChanged(at, 130 | QItemSelectionModel::ClearAndSelect); 131 | } 132 | break; 133 | 134 | default: 135 | break; 136 | } 137 | } 138 | 139 | CbmdosFs *CbmdosFsModel::fs() const 140 | { 141 | return d->fs; 142 | } 143 | 144 | void CbmdosFsModel::setFs(CbmdosFs *fs) 145 | { 146 | if (d->fs) 147 | { 148 | CbmdosVfs *vfs = CbmdosFs_vfs(d->fs); 149 | Event_unregister(CbmdosVfs_changedEvent(vfs), this, evhdl); 150 | beginRemoveRows(QModelIndex(), 0, CbmdosVfs_fileCount(vfs)+1); 151 | d->fs = 0; 152 | endRemoveRows(); 153 | } 154 | if (fs) 155 | { 156 | CbmdosVfs *vfs = CbmdosFs_vfs(fs); 157 | beginInsertRows(QModelIndex(), 0, CbmdosVfs_fileCount(vfs)+1); 158 | d->fs = fs; 159 | Event_register(CbmdosVfs_changedEvent(vfs), this, evhdl); 160 | endInsertRows(); 161 | } 162 | } 163 | 164 | void CbmdosFsModel::deleteAt(const QModelIndex &at) 165 | { 166 | if (at.row() > 0 && at.row() < rowCount() - 1) 167 | { 168 | beginRemoveRows(QModelIndex(), at.row(), at.row()); 169 | CbmdosVfs_deleteAt(CbmdosFs_vfs(d->fs), at.row() - 1); 170 | } 171 | } 172 | 173 | void CbmdosFsModel::addFile(const QModelIndex &at, CbmdosFile *newFile) 174 | { 175 | EditOperationCheck check(newFile); 176 | emit checkEditOperation(check); 177 | if (!check.allowed()) 178 | { 179 | CbmdosFile_destroy(newFile); 180 | return; 181 | } 182 | if (at.isValid() && at.row() > 0) 183 | { 184 | beginInsertRows(QModelIndex(), at.row(), at.row()); 185 | CbmdosVfs_insert(CbmdosFs_vfs(d->fs), newFile, at.row() - 1); 186 | } 187 | else 188 | { 189 | beginInsertRows(QModelIndex(), rowCount() - 1, rowCount() - 1); 190 | CbmdosVfs_append(CbmdosFs_vfs(d->fs), newFile); 191 | } 192 | } 193 | 194 | void CbmdosFsModel::setItemSize(QSizeF size) 195 | { 196 | d->itemSize = size; 197 | } 198 | 199 | const QTemporaryDir *CbmdosFsModel::tmpDir() const 200 | { 201 | if (!d->tmpDir) d->tmpDir = new QTemporaryDir(); 202 | return d->tmpDir; 203 | } 204 | 205 | int CbmdosFsModel::rowCount(const QModelIndex &parent) const 206 | { 207 | (void) parent; 208 | if (!d->fs) return 0; 209 | const CbmdosVfs *vfs = CbmdosFs_rvfs(d->fs); 210 | return CbmdosVfs_fileCount(vfs) + 2; 211 | } 212 | 213 | QVariant CbmdosFsModel::data(const QModelIndex &index, int role) const 214 | { 215 | uint8_t buffer[28]; 216 | 217 | if (role == Qt::FontRole) 218 | { 219 | return cmdr.c64font(); 220 | } 221 | 222 | if (role == Qt::SizeHintRole) 223 | { 224 | return d->itemSize; 225 | } 226 | 227 | if (!d->fs) return QVariant(); 228 | 229 | if (role != Qt::DisplayRole) 230 | { 231 | return QVariant(); 232 | } 233 | 234 | int row = index.row(); 235 | int rowcount = rowCount(); 236 | const CbmdosVfs *vfs = CbmdosFs_rvfs(d->fs); 237 | 238 | if (row > 0 && row < rowcount - 1) 239 | { 240 | const CbmdosFile *file = CbmdosVfs_rfile(vfs, row-1); 241 | CbmdosFile_getDirLine(file, buffer); 242 | PetsciiStr dirLine((char *)buffer, 28); 243 | return dirLine.toString(cmdr.settings().lowercase()); 244 | } 245 | 246 | if (row == 0) 247 | { 248 | QString heading("0 "); 249 | CbmdosVfs_getDirHeader(vfs, buffer); 250 | PetsciiStr dirHeader((char *)buffer, 24); 251 | heading.append(dirHeader.toString(cmdr.settings().lowercase(), true)); 252 | return heading; 253 | } 254 | 255 | CbmdosFs_getFreeBlocksLine(d->fs, buffer); 256 | PetsciiStr freeLine((char *)buffer, 16); 257 | return freeLine.toString(cmdr.settings().lowercase()); 258 | } 259 | 260 | Qt::ItemFlags CbmdosFsModel::flags(const QModelIndex &index) const 261 | { 262 | if (!d->fs) return Qt::ItemNeverHasChildren; 263 | if (!index.isValid()) return Qt::ItemIsDropEnabled; 264 | 265 | int row = index.row(); 266 | int rowcount = rowCount(); 267 | if (row > 0 && row < rowcount - 1) 268 | { 269 | return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled 270 | | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; 271 | } 272 | 273 | return Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; 274 | } 275 | 276 | QStringList CbmdosFsModel::mimeTypes() const 277 | { 278 | return QStringList({ 279 | CbmdosFileMimeData::internalFormat(), 280 | "text/uri-list"}); 281 | } 282 | 283 | QMimeData *CbmdosFsModel::mimeData(const QModelIndexList &indexes) const 284 | { 285 | if (indexes.empty()) return 0; 286 | CbmdosFileMimeData *mimeData = new CbmdosFileMimeData(this); 287 | CbmdosVfs *vfs = CbmdosFs_vfs(d->fs); 288 | for (QModelIndexList::const_iterator i = indexes.cbegin(); 289 | i != indexes.cend(); ++i) 290 | { 291 | if (i->isValid()) 292 | { 293 | int row = i->row(); 294 | if (row > 0 && row < rowCount() - 1) 295 | { 296 | mimeData->addFile(CbmdosVfs_rfile(vfs, row-1), row-1); 297 | } 298 | } 299 | } 300 | if (mimeData->files().empty()) 301 | { 302 | delete mimeData; 303 | return 0; 304 | } 305 | return mimeData; 306 | } 307 | 308 | Qt::DropActions CbmdosFsModel::supportedDropActions() const 309 | { 310 | return Qt::MoveAction|Qt::CopyAction; 311 | } 312 | 313 | bool CbmdosFsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, 314 | int row, int column, const QModelIndex &parent) 315 | { 316 | (void) parent; 317 | if (!d->fs) return false; 318 | if (CbmdosFs_status(d->fs) & CFS_BROKEN) return false; 319 | const CbmdosFileMimeData *fileData = 320 | qobject_cast(data); 321 | if (fileData) 322 | { 323 | if (action == Qt::MoveAction) 324 | { 325 | if (row < 0 || column < 0) return false; 326 | if (row < 1) ++row; 327 | int to = row - 1; 328 | QList fromRows; 329 | for (int r = 0; r < rowCount() - 2; ++r) fromRows.append(r); 330 | CbmdosVfs *vfs = CbmdosFs_vfs(d->fs); 331 | for (int i = 0; i < fileData->filePositions().size(); ++i) 332 | { 333 | int from = fromRows.at(fileData->filePositions().at(i)); 334 | if (to > from) --to; 335 | CbmdosVfs_move(vfs, to, from); 336 | int oldfrom = fromRows.at(to); 337 | fromRows.removeAt(to); 338 | fromRows.insert(from, oldfrom); 339 | ++to; 340 | } 341 | return true; 342 | } 343 | if (action == Qt::CopyAction) 344 | { 345 | if (fileData->files().empty()) return false; 346 | for (int i = 0; i < fileData->files().size(); ++i) 347 | { 348 | CbmdosFile *copy = CbmdosFile_clone(fileData->files().at(i)); 349 | addFile(createIndex(row+i, column), copy); 350 | } 351 | return true; 352 | } 353 | } 354 | else if (data->hasUrls()) 355 | { 356 | QList urls = data->urls(); 357 | int fileno = 0; 358 | for (QList::const_iterator i = urls.cbegin(); 359 | i != urls.cend(); ++i) 360 | { 361 | if (i->isLocalFile()) 362 | { 363 | QFileInfo file(i->toLocalFile()); 364 | FILE *fp = qfopen(file.canonicalFilePath(), "rb"); 365 | if (fp) 366 | { 367 | CbmdosFile *newFile = CbmdosFile_create(); 368 | char name[17]; 369 | QByteArray basename = file.completeBaseName().toUtf8(); 370 | size_t nameLen = petscii_fromUtf8(name, 17, 371 | basename.data(), basename.size(), PC_UPPER, 1, 0); 372 | if(--nameLen > 16) nameLen = 16; 373 | CbmdosFile_setName(newFile, name, nameLen); 374 | QString ext = file.suffix().toLower(); 375 | if (ext == "seq" || QRegularExpression("^s\\d{2}$") 376 | .match(ext).hasMatch()) 377 | { 378 | CbmdosFile_setType(newFile, CbmdosFileType::CFT_SEQ); 379 | } 380 | else if (ext == "usr" || QRegularExpression("^u\\d{2}$"). 381 | match(ext).hasMatch()) 382 | { 383 | CbmdosFile_setType(newFile, CbmdosFileType::CFT_USR); 384 | } 385 | else if (ext == "prg" || QRegularExpression("^p\\d{2}$"). 386 | match(ext).hasMatch()) 387 | { 388 | CbmdosFile_setType(newFile, CbmdosFileType::CFT_PRG); 389 | } 390 | else if (ext == "rel" || QRegularExpression("^r\\d{2}$"). 391 | match(ext).hasMatch()) 392 | { 393 | CbmdosFile_setType(newFile, CbmdosFileType::CFT_REL); 394 | } 395 | else 396 | { 397 | CbmdosFile_setType(newFile, 398 | cmdr.settings().defaultImportType()); 399 | } 400 | int rc = CbmdosFile_import(newFile, fp); 401 | fclose(fp); 402 | if (rc < 0) 403 | { 404 | CbmdosFile_destroy(newFile); 405 | break; 406 | } 407 | addFile(createIndex(row + (fileno++), column), newFile); 408 | } 409 | } 410 | } 411 | return fileno > 0; 412 | } 413 | return false; 414 | } 415 | -------------------------------------------------------------------------------- /src/bin/v1541commander/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "v1541commander.h" 3 | #include "v1541imgwidget.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | class MainWindow::priv 17 | { 18 | public: 19 | priv(); 20 | Content content; 21 | QString filename; 22 | }; 23 | 24 | MainWindow::priv::priv() : 25 | content(None), 26 | filename() 27 | {} 28 | 29 | MainWindow::MainWindow() 30 | { 31 | d = new priv(); 32 | QMenu *fileMenu = menuBar()->addMenu(tr("&File")); 33 | fileMenu->addAction(&cmdr.newAction()); 34 | fileMenu->addAction(&cmdr.openAction()); 35 | fileMenu->addAction(&cmdr.saveAction()); 36 | fileMenu->addAction(&cmdr.saveAsAction()); 37 | QMenu *exportMenu = fileMenu->addMenu(tr("&Export")); 38 | exportMenu->addAction(&cmdr.exportZipcodeAction()); 39 | exportMenu->addAction(&cmdr.exportZipcodeD64Action()); 40 | exportMenu->addAction(&cmdr.exportLynxAction()); 41 | fileMenu->addAction(&cmdr.closeAction()); 42 | fileMenu->addSeparator(); 43 | fileMenu->addAction(&cmdr.settingsAction()); 44 | fileMenu->addAction(&cmdr.aboutAction()); 45 | fileMenu->addAction(&cmdr.exitAction()); 46 | QMenu *displayMenu = menuBar()->addMenu(tr("&View")); 47 | displayMenu->addAction(&cmdr.lowerCaseAction()); 48 | QMenu *cbmdosMenu = menuBar()->addMenu(tr("&CBM DOS")); 49 | cbmdosMenu->addAction(&cmdr.fsOptionsAction()); 50 | cbmdosMenu->addAction(&cmdr.rewriteImageAction()); 51 | QMenu *petsciiMenu = cbmdosMenu->addMenu(tr("&Map UC Gfx to LC")); 52 | petsciiMenu->addAction(&cmdr.autoMapLcAction()); 53 | petsciiMenu->addAction(&cmdr.mapLcAction()); 54 | cbmdosMenu->addSeparator(); 55 | cbmdosMenu->addAction(&cmdr.newFileAction()); 56 | cbmdosMenu->addAction(&cmdr.deleteFileAction()); 57 | cbmdosMenu->addAction(&cmdr.fileOverridesAction()); 58 | QMenu *windowsMenu = menuBar()->addMenu(tr("&Windows")); 59 | windowsMenu->addAction(&cmdr.petsciiWindowAction()); 60 | windowsMenu->addAction(&cmdr.logWindowAction()); 61 | menuBar()->setFont(cmdr.menufont()); 62 | statusBar()->setStyleSheet("QStatusBar::item {border: none;}"); 63 | statusBar()->setFont(cmdr.statusfont()); 64 | 65 | setAcceptDrops(true); 66 | setWindowTitle(tr("V1541Commander: virtual 1541 disk commander[*]")); 67 | } 68 | 69 | MainWindow::~MainWindow() 70 | { 71 | delete d; 72 | } 73 | 74 | void MainWindow::contentSelectionChanged() 75 | { 76 | emit selectionChanged(); 77 | } 78 | 79 | void MainWindow::contentModified() 80 | { 81 | setWindowModified(true); 82 | emit modifiedChanged(); 83 | } 84 | 85 | void MainWindow::contentSaved() 86 | { 87 | setWindowModified(false); 88 | emit modifiedChanged(); 89 | } 90 | 91 | MainWindow::Content MainWindow::content() const 92 | { 93 | return d->content; 94 | } 95 | 96 | const QString &MainWindow::filename() const 97 | { 98 | return d->filename; 99 | } 100 | 101 | bool MainWindow::hasValidContent() const 102 | { 103 | switch (d->content) 104 | { 105 | V1541ImgWidget *imgWidget; 106 | 107 | case Content::Image: 108 | imgWidget = static_cast(centralWidget()); 109 | return imgWidget->hasValidImage(); 110 | 111 | default: 112 | return false; 113 | } 114 | } 115 | 116 | bool MainWindow::hasValidSelection() const 117 | { 118 | switch (d->content) 119 | { 120 | V1541ImgWidget *imgWidget; 121 | 122 | case Content::Image: 123 | imgWidget = static_cast(centralWidget()); 124 | return imgWidget->hasValidSelection(); 125 | 126 | default: 127 | return false; 128 | } 129 | } 130 | 131 | bool MainWindow::isReadOnly() const 132 | { 133 | switch (d->content) 134 | { 135 | V1541ImgWidget *imgWidget; 136 | 137 | case Content::Image: 138 | imgWidget = static_cast(centralWidget()); 139 | return imgWidget->isReadOnly(); 140 | 141 | default: 142 | return true; 143 | } 144 | } 145 | 146 | bool MainWindow::event(QEvent *e) 147 | { 148 | if (e->type() == QEvent::WindowActivate) 149 | { 150 | emit activated(); 151 | } 152 | return QMainWindow::event(e); 153 | } 154 | 155 | void MainWindow::closeEvent(QCloseEvent *e) 156 | { 157 | closeDocument(); 158 | if (isWindowModified()) e->ignore(); 159 | else emit closed(); 160 | } 161 | 162 | QSize MainWindow::sizeHint() const 163 | { 164 | if (d->content == Content::None) 165 | { 166 | return QSize(360 * logicalDpiX() / 72, 150 * logicalDpiY() / 72); 167 | } 168 | else 169 | { 170 | return QWidget::sizeHint(); 171 | } 172 | } 173 | 174 | void MainWindow::dragEnterEvent(QDragEnterEvent *event) 175 | { 176 | if (event->source()) 177 | { 178 | event->ignore(); 179 | } 180 | else if (event->mimeData()->hasUrls()) 181 | { 182 | bool ok = false; 183 | QList urls = event->mimeData()->urls(); 184 | for (QList::const_iterator i = urls.cbegin(); 185 | i != urls.cend(); ++i) 186 | { 187 | if (i->isLocalFile()) ok = true; 188 | } 189 | if (ok) 190 | { 191 | event->setDropAction(Qt::CopyAction); 192 | event->accept(); 193 | } 194 | else 195 | { 196 | event->ignore(); 197 | } 198 | } 199 | else 200 | { 201 | event->ignore(); 202 | } 203 | } 204 | 205 | void MainWindow::dragMoveEvent(QDragMoveEvent *event) 206 | { 207 | if (event->source()) 208 | { 209 | event->ignore(); 210 | } 211 | else if (event->mimeData()->hasUrls()) 212 | { 213 | bool ok = false; 214 | QList urls = event->mimeData()->urls(); 215 | for (QList::const_iterator i = urls.cbegin(); 216 | i != urls.cend(); ++i) 217 | { 218 | if (i->isLocalFile()) ok = true; 219 | } 220 | if (ok) 221 | { 222 | event->setDropAction(Qt::CopyAction); 223 | event->accept(); 224 | } 225 | else 226 | { 227 | event->ignore(); 228 | } 229 | } 230 | else 231 | { 232 | event->ignore(); 233 | } 234 | } 235 | 236 | void MainWindow::dropEvent(QDropEvent *event) 237 | { 238 | if (event->source()) 239 | { 240 | event->ignore(); 241 | } 242 | else if (event->mimeData()->hasUrls()) 243 | { 244 | bool ok = false; 245 | QList urls = event->mimeData()->urls(); 246 | for (QList::const_iterator i = urls.cbegin(); 247 | i != urls.cend(); ++i) 248 | { 249 | if (i->isLocalFile()) 250 | { 251 | ok = true; 252 | cmdr.open(QFileInfo(i->toLocalFile()).canonicalFilePath()); 253 | } 254 | } 255 | if (ok) 256 | { 257 | event->setDropAction(Qt::CopyAction); 258 | event->accept(); 259 | } 260 | else 261 | { 262 | event->ignore(); 263 | } 264 | } 265 | else 266 | { 267 | event->ignore(); 268 | } 269 | } 270 | 271 | void MainWindow::newImage() 272 | { 273 | V1541ImgWidget *imgWidget = new V1541ImgWidget(this); 274 | imgWidget->newImage(); 275 | if (imgWidget->hasValidImage()) 276 | { 277 | QWidget *current = centralWidget(); 278 | if (current) current->setParent(0); 279 | setCentralWidget(imgWidget); 280 | delete current; 281 | d->filename = QString(); 282 | setWindowTitle(tr("[*]")); 283 | d->content = Content::Image; 284 | connect(imgWidget, &V1541ImgWidget::selectionChanged, 285 | this, &MainWindow::contentSelectionChanged); 286 | connect(imgWidget, &V1541ImgWidget::modified, 287 | this, &MainWindow::contentModified); 288 | connect(imgWidget, &V1541ImgWidget::saved, 289 | this, &MainWindow::contentSaved); 290 | emit contentChanged(); 291 | setWindowModified(false); 292 | emit modifiedChanged(); 293 | statusBar()->addPermanentWidget(imgWidget->statusWidget()); 294 | adjustSize(); 295 | } 296 | else 297 | { 298 | delete imgWidget; 299 | } 300 | } 301 | 302 | void MainWindow::openImage(const QString &imgFile) 303 | { 304 | if (!imgFile.isEmpty()) 305 | { 306 | bool modified = isWindowModified(); 307 | setWindowModified(false); 308 | V1541ImgWidget *imgWidget = new V1541ImgWidget(this); 309 | connect(imgWidget, &V1541ImgWidget::modified, 310 | this, &MainWindow::contentModified); 311 | imgWidget->open(imgFile); 312 | if (imgWidget->hasValidImage()) 313 | { 314 | QWidget *current = centralWidget(); 315 | if (current) current->setParent(0); 316 | setCentralWidget(imgWidget); 317 | delete current; 318 | d->content = Content::Image; 319 | if (isWindowModified()) 320 | { 321 | d->filename = QString(); 322 | setWindowTitle(tr("[*]")); 323 | } 324 | else 325 | { 326 | d->filename = imgFile; 327 | setWindowTitle(QString(imgFile).append("[*]")); 328 | } 329 | connect(imgWidget, &V1541ImgWidget::selectionChanged, 330 | this, &MainWindow::contentSelectionChanged); 331 | connect(imgWidget, &V1541ImgWidget::saved, 332 | this, &MainWindow::contentSaved); 333 | emit contentChanged(); 334 | statusBar()->addPermanentWidget(imgWidget->statusWidget()); 335 | adjustSize(); 336 | } 337 | else 338 | { 339 | delete imgWidget; 340 | setWindowModified(modified); 341 | } 342 | } 343 | } 344 | 345 | void MainWindow::openVfs(CbmdosVfs *vfs) 346 | { 347 | V1541ImgWidget *imgWidget = new V1541ImgWidget(this); 348 | imgWidget->openVfs(vfs); 349 | if (imgWidget->hasValidImage()) 350 | { 351 | QWidget *current = centralWidget(); 352 | if (current) current->setParent(0); 353 | setCentralWidget(imgWidget); 354 | delete current; 355 | d->content = Content::Image; 356 | d->filename = QString(); 357 | setWindowTitle(tr("[*]")); 358 | connect(imgWidget, &V1541ImgWidget::selectionChanged, 359 | this, &MainWindow::contentSelectionChanged); 360 | connect(imgWidget, &V1541ImgWidget::modified, 361 | this, &MainWindow::contentModified); 362 | connect(imgWidget, &V1541ImgWidget::saved, 363 | this, &MainWindow::contentSaved); 364 | setWindowModified(true); 365 | emit contentChanged(); 366 | statusBar()->addPermanentWidget(imgWidget->statusWidget()); 367 | adjustSize(); 368 | } 369 | else 370 | { 371 | delete imgWidget; 372 | } 373 | } 374 | 375 | void MainWindow::save(const QString &imgFile) 376 | { 377 | switch (d->content) 378 | { 379 | V1541ImgWidget *imgWidget; 380 | 381 | case Content::Image: 382 | imgWidget = static_cast(centralWidget()); 383 | imgWidget->save(imgFile.isEmpty() ? d->filename : imgFile); 384 | if (!imgFile.isEmpty() && !isWindowModified()) 385 | { 386 | d->filename = imgFile; 387 | setWindowTitle(QString(imgFile).append("[*]")); 388 | } 389 | break; 390 | 391 | default: 392 | break; 393 | } 394 | } 395 | 396 | void MainWindow::exportZipcode(const QString &zcFile) 397 | { 398 | switch (d->content) 399 | { 400 | V1541ImgWidget *imgWidget; 401 | 402 | case Content::Image: 403 | imgWidget = static_cast(centralWidget()); 404 | imgWidget->exportZipcode(zcFile); 405 | break; 406 | 407 | default: 408 | break; 409 | } 410 | } 411 | 412 | CbmdosVfs *MainWindow::exportZipcodeVfs() 413 | { 414 | switch (d->content) 415 | { 416 | V1541ImgWidget *imgWidget; 417 | 418 | case Content::Image: 419 | imgWidget = static_cast(centralWidget()); 420 | return imgWidget->exportZipcodeVfs(); 421 | 422 | default: 423 | return 0; 424 | } 425 | } 426 | 427 | void MainWindow::exportLynx(const QString &lynxFile) 428 | { 429 | switch (d->content) 430 | { 431 | V1541ImgWidget *imgWidget; 432 | 433 | case Content::Image: 434 | imgWidget = static_cast(centralWidget()); 435 | imgWidget->exportLynx(lynxFile); 436 | break; 437 | 438 | default: 439 | break; 440 | } 441 | } 442 | 443 | void MainWindow::closeDocument() 444 | { 445 | if (isWindowModified()) 446 | { 447 | QMessageBox::StandardButton btn = QMessageBox::question(this, 448 | tr("Discard unsaved changes?"), QString(tr("%1 has unsaved " 449 | "changes. \nDo you want to save now?")) 450 | .arg(d->filename.isEmpty() ? 451 | tr("") : d->filename), 452 | QMessageBox::Save|QMessageBox::Cancel|QMessageBox::Discard, 453 | QMessageBox::Save); 454 | if (btn == QMessageBox::Cancel) return; 455 | if (btn == QMessageBox::Save) 456 | { 457 | if (d->filename.isEmpty()) 458 | { 459 | emit activated(); 460 | cmdr.saveAs(); 461 | } 462 | else 463 | { 464 | save(QString()); 465 | } 466 | if (isWindowModified()) return; 467 | } 468 | } 469 | QWidget *current = centralWidget(); 470 | if (current) current->setParent(0); 471 | setCentralWidget(0); 472 | delete current; 473 | d->content = Content::None; 474 | d->filename = QString(); 475 | emit contentChanged(); 476 | setWindowModified(false); 477 | emit modifiedChanged(); 478 | adjustSize(); 479 | setWindowTitle(tr("V1541Commander: virtual 1541 disk commander[*]")); 480 | } 481 | 482 | void MainWindow::fsOptions() 483 | { 484 | if (d->content != Content::Image) return; 485 | static_cast(centralWidget())->fsOptions(); 486 | } 487 | 488 | void MainWindow::rewriteImage() 489 | { 490 | if (d->content != Content::Image) return; 491 | static_cast(centralWidget())->rewriteImage(); 492 | } 493 | 494 | void MainWindow::mapToLc() 495 | { 496 | if (d->content != Content::Image) return; 497 | static_cast(centralWidget())->mapToLc(); 498 | } 499 | 500 | void MainWindow::newFile() 501 | { 502 | if (d->content != Content::Image) return; 503 | static_cast(centralWidget())->newFile(); 504 | } 505 | 506 | void MainWindow::deleteFile() 507 | { 508 | if (d->content != Content::Image) return; 509 | static_cast(centralWidget())->deleteFile(); 510 | } 511 | 512 | void MainWindow::fileOverrides() 513 | { 514 | if (d->content != Content::Image) return; 515 | static_cast(centralWidget())->fileOverrides(); 516 | } 517 | 518 | void MainWindow::showStatusLine(const QString &line) 519 | { 520 | statusBar()->showMessage(line, 10000); 521 | } 522 | -------------------------------------------------------------------------------- /src/bin/v1541commander/fsoptoverridesdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "fsoptoverridesdialog.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include <1541img/cbmdosfsoptions.h> 15 | 16 | class FsOptOverridesDialog::priv 17 | { 18 | public: 19 | priv(CbmdosFsOptOverrides *overrides); 20 | CbmdosFsOptOverrides *overrides; 21 | CbmdosFsOptOverrides ovrd; 22 | QVBoxLayout mainLayout; 23 | QGridLayout ovrdLayout; 24 | QCheckBox ovrdInterleaveCheckBox; 25 | QLabel interleaveLabel; 26 | QSpinBox interleaveSpinBox; 27 | QCheckBox ovrdStrategyCheckBox; 28 | QRadioButton allocOriginalButton; 29 | QRadioButton allocTrackloaderButton; 30 | QRadioButton allocSimpleButton; 31 | QCheckBox simpleInterleaveCheckBox; 32 | QCheckBox prefDirTrackCheckBox; 33 | QCheckBox chainInterlvCheckBox; 34 | QDialogButtonBox buttons; 35 | 36 | void reset(); 37 | void clicked(QObject *sender); 38 | void changed(int value); 39 | }; 40 | 41 | FsOptOverridesDialog::priv::priv(CbmdosFsOptOverrides *overrides) : 42 | overrides(overrides), 43 | ovrd(), 44 | mainLayout(), 45 | ovrdLayout(), 46 | ovrdInterleaveCheckBox(tr("Override interleave")), 47 | interleaveLabel(tr("interleave: ")), 48 | interleaveSpinBox(), 49 | ovrdStrategyCheckBox(tr("Override strategy")), 50 | allocOriginalButton(tr("CBM DOS strategy")), 51 | allocTrackloaderButton(tr("Trackloader strategy")), 52 | allocSimpleButton(tr("Simple strategy")), 53 | simpleInterleaveCheckBox(tr("Use simple interleave")), 54 | prefDirTrackCheckBox(tr("Prefer dir track for files")), 55 | chainInterlvCheckBox(tr("Apply interleave on track change")), 56 | buttons(QDialogButtonBox::Ok|QDialogButtonBox::Cancel 57 | |QDialogButtonBox::Reset) 58 | { 59 | interleaveLabel.setAlignment(Qt::AlignRight); 60 | interleaveSpinBox.setMinimum(1); 61 | interleaveSpinBox.setMaximum(20); 62 | simpleInterleaveCheckBox.setTristate(true); 63 | prefDirTrackCheckBox.setTristate(true); 64 | chainInterlvCheckBox.setTristate(true); 65 | ovrdInterleaveCheckBox.setToolTip( 66 | tr("Override interleave when writing this file")); 67 | QString interleaveToolTip( 68 | tr("Interleave value to use for blocks of this file " 69 | "(default: 10)")); 70 | interleaveLabel.setToolTip(interleaveToolTip); 71 | interleaveSpinBox.setToolTip(interleaveToolTip); 72 | ovrdStrategyCheckBox.setToolTip( 73 | tr("Override the base track/block allocation strategy")); 74 | allocOriginalButton.setToolTip( 75 | tr("Use the original CBM DOS strategy for allocating blocks.\n" 76 | "For starting a new file, a track as close as possible to " 77 | "track 18 is used.\n" 78 | "While writing a file, when the current track is full, the " 79 | "next track in the direction away from track 18 is used.\n" 80 | "Good for random access to different files, with the files " 81 | "at the top of the directory preferred.")); 82 | allocTrackloaderButton.setToolTip( 83 | tr("Use an allocation strategy often used with trackloaders.\n" 84 | "Tracks are used starting at track 1 and strictly " 85 | "incrementing.\n" 86 | "Good for a set of files that's always read in the same " 87 | "order.")); 88 | allocSimpleButton.setToolTip( 89 | tr("Use a simplified allocation strategy.\n" 90 | "Tracks are used starting at track 19 and strictly " 91 | "incrementing up to track 40, then wrapping around to " 92 | "track 1.\n" 93 | "On 42-track images, tracks 41 and 42 are still used last.\n" 94 | "This is a compromise between the other strategies, with " 95 | "the first files close to track 18, and the files " 96 | "\"following\" each other on disk.")); 97 | simpleInterleaveCheckBox.setToolTip( 98 | tr("When adding interleave, use a simple \"modulo\" to get a " 99 | "valid sector number on the current track.\n" 100 | "If this is not checked, interleave is applied like the " 101 | "original CBM DOS does it:\n" 102 | "When wrapping over sector 0, 1 is subtracted from " 103 | "the result.\n" 104 | "Partially checked means not to override this flag.")); 105 | prefDirTrackCheckBox.setToolTip( 106 | tr("Put files on the directory track first.\n" 107 | "This has only an effect if files on the directory track " 108 | "are allowed.\n" 109 | "Partially checked means not to override this flag.")); 110 | chainInterlvCheckBox.setToolTip( 111 | tr("When having to switch to another track, still apply " 112 | "interleave to the sector number.\n" 113 | "If this is not checked, looking for a free sector on a new " 114 | "track\n" 115 | "always starts at sector 0 (original CBM DOS behavior).\n" 116 | "Partially checked means not to override this flag.")); 117 | reset(); 118 | 119 | } 120 | 121 | void FsOptOverridesDialog::priv::reset() 122 | { 123 | memcpy(&ovrd, overrides, sizeof ovrd); 124 | if (ovrd.mask & CbmdosFsFlags::CFF_OVERRIDE_INTERLEAVE) 125 | { 126 | ovrdInterleaveCheckBox.setChecked(true); 127 | interleaveSpinBox.setEnabled(true); 128 | int interlv = ovrd.flags & CbmdosFsFlags::CFF_OVERRIDE_INTERLEAVE; 129 | if (interlv < 1) interlv = 1; 130 | else if (interlv > 20) interlv = 20; 131 | interleaveSpinBox.setValue(interlv); 132 | } 133 | else 134 | { 135 | ovrdInterleaveCheckBox.setChecked(false); 136 | interleaveSpinBox.setEnabled(false); 137 | interleaveSpinBox.setValue(10); 138 | } 139 | 140 | if (ovrd.mask & ( 141 | CbmdosFsFlags::CFF_TALLOC_TRACKLOAD | 142 | CbmdosFsFlags::CFF_TALLOC_SIMPLE )) 143 | { 144 | ovrdStrategyCheckBox.setChecked(true); 145 | allocOriginalButton.setEnabled(true); 146 | allocTrackloaderButton.setEnabled(true); 147 | allocSimpleButton.setEnabled(true); 148 | if (ovrd.flags & CbmdosFsFlags::CFF_TALLOC_TRACKLOAD) 149 | { 150 | allocTrackloaderButton.setChecked(true); 151 | } 152 | else if (ovrd.flags & CbmdosFsFlags::CFF_TALLOC_SIMPLE) 153 | { 154 | allocSimpleButton.setChecked(true); 155 | } 156 | else 157 | { 158 | allocOriginalButton.setChecked(true); 159 | } 160 | } 161 | else 162 | { 163 | ovrdStrategyCheckBox.setChecked(false); 164 | allocOriginalButton.setChecked(true); 165 | allocOriginalButton.setEnabled(false); 166 | allocTrackloaderButton.setEnabled(false); 167 | allocSimpleButton.setEnabled(false); 168 | } 169 | 170 | if (ovrd.mask & CbmdosFsFlags::CFF_SIMPLEINTERLEAVE) 171 | { 172 | if (ovrd.flags & CbmdosFsFlags::CFF_SIMPLEINTERLEAVE) 173 | { 174 | simpleInterleaveCheckBox.setCheckState(Qt::Checked); 175 | } 176 | else 177 | { 178 | simpleInterleaveCheckBox.setCheckState(Qt::Unchecked); 179 | } 180 | } 181 | else 182 | { 183 | simpleInterleaveCheckBox.setCheckState(Qt::PartiallyChecked); 184 | } 185 | 186 | if (ovrd.mask & CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK) 187 | { 188 | if (ovrd.flags & CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK) 189 | { 190 | prefDirTrackCheckBox.setCheckState(Qt::Checked); 191 | } 192 | else 193 | { 194 | prefDirTrackCheckBox.setCheckState(Qt::Unchecked); 195 | } 196 | } 197 | else 198 | { 199 | prefDirTrackCheckBox.setCheckState(Qt::PartiallyChecked); 200 | } 201 | 202 | if (ovrd.mask & CbmdosFsFlags::CFF_TALLOC_CHAININTERLV) 203 | { 204 | if (ovrd.flags & CbmdosFsFlags::CFF_TALLOC_CHAININTERLV) 205 | { 206 | chainInterlvCheckBox.setCheckState(Qt::Checked); 207 | } 208 | else 209 | { 210 | chainInterlvCheckBox.setCheckState(Qt::Unchecked); 211 | } 212 | } 213 | else 214 | { 215 | chainInterlvCheckBox.setCheckState(Qt::PartiallyChecked); 216 | } 217 | } 218 | 219 | void FsOptOverridesDialog::priv::clicked(QObject *sender) 220 | { 221 | if (sender == &ovrdInterleaveCheckBox) 222 | { 223 | interleaveSpinBox.setValue(10); 224 | ovrd.flags = (CbmdosFsFlags) 225 | (ovrd.flags & ~CbmdosFsFlags::CFF_OVERRIDE_INTERLEAVE); 226 | ovrd.flags = (CbmdosFsFlags) 227 | (ovrd.flags | 10); 228 | if (ovrdInterleaveCheckBox.isChecked()) 229 | { 230 | interleaveSpinBox.setEnabled(true); 231 | ovrd.mask = (CbmdosFsFlags) (ovrd.mask 232 | | CbmdosFsFlags::CFF_OVERRIDE_INTERLEAVE); 233 | } 234 | else 235 | { 236 | interleaveSpinBox.setEnabled(false); 237 | ovrd.mask = (CbmdosFsFlags) (ovrd.mask 238 | & ~CbmdosFsFlags::CFF_OVERRIDE_INTERLEAVE); 239 | } 240 | } 241 | else if (sender == &ovrdStrategyCheckBox) 242 | { 243 | allocOriginalButton.setChecked(true); 244 | ovrd.flags = (CbmdosFsFlags) (ovrd.flags & ~( 245 | CbmdosFsFlags::CFF_TALLOC_TRACKLOAD | 246 | CbmdosFsFlags::CFF_TALLOC_SIMPLE 247 | )); 248 | if (ovrdStrategyCheckBox.isChecked()) 249 | { 250 | allocOriginalButton.setEnabled(true); 251 | allocTrackloaderButton.setEnabled(true); 252 | allocSimpleButton.setEnabled(true); 253 | ovrd.mask = (CbmdosFsFlags) (ovrd.mask 254 | | CbmdosFsFlags::CFF_TALLOC_TRACKLOAD 255 | | CbmdosFsFlags::CFF_TALLOC_SIMPLE); 256 | } 257 | else 258 | { 259 | allocOriginalButton.setEnabled(false); 260 | allocTrackloaderButton.setEnabled(false); 261 | allocSimpleButton.setEnabled(false); 262 | ovrd.mask = (CbmdosFsFlags) (ovrd.mask & ~( 263 | CbmdosFsFlags::CFF_TALLOC_TRACKLOAD | 264 | CbmdosFsFlags::CFF_TALLOC_SIMPLE 265 | )); 266 | } 267 | } 268 | else if (sender == &allocOriginalButton) 269 | { 270 | ovrd.flags = (CbmdosFsFlags) (ovrd.flags & ~( 271 | CbmdosFsFlags::CFF_TALLOC_TRACKLOAD | 272 | CbmdosFsFlags::CFF_TALLOC_SIMPLE 273 | )); 274 | } 275 | else if (sender == &allocTrackloaderButton) 276 | { 277 | ovrd.flags = (CbmdosFsFlags) 278 | (ovrd.flags & ~CbmdosFsFlags::CFF_TALLOC_SIMPLE); 279 | ovrd.flags = (CbmdosFsFlags) 280 | (ovrd.flags | CbmdosFsFlags::CFF_TALLOC_TRACKLOAD); 281 | } 282 | else if (sender == &allocSimpleButton) 283 | { 284 | ovrd.flags = (CbmdosFsFlags) 285 | (ovrd.flags & ~CbmdosFsFlags::CFF_TALLOC_TRACKLOAD); 286 | ovrd.flags = (CbmdosFsFlags) 287 | (ovrd.flags | CbmdosFsFlags::CFF_TALLOC_SIMPLE); 288 | } 289 | else if (sender == &simpleInterleaveCheckBox) 290 | { 291 | switch (simpleInterleaveCheckBox.checkState()) 292 | { 293 | case Qt::Unchecked: 294 | ovrd.mask = (CbmdosFsFlags) 295 | (ovrd.mask | CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 296 | ovrd.flags = (CbmdosFsFlags) 297 | (ovrd.flags & ~CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 298 | break; 299 | case Qt::PartiallyChecked: 300 | ovrd.mask = (CbmdosFsFlags) 301 | (ovrd.mask & ~CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 302 | ovrd.flags = (CbmdosFsFlags) 303 | (ovrd.flags & ~CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 304 | break; 305 | case Qt::Checked: 306 | ovrd.mask = (CbmdosFsFlags) 307 | (ovrd.mask | CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 308 | ovrd.flags = (CbmdosFsFlags) 309 | (ovrd.flags | CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 310 | break; 311 | } 312 | } 313 | else if (sender == &prefDirTrackCheckBox) 314 | { 315 | switch (prefDirTrackCheckBox.checkState()) 316 | { 317 | case Qt::Unchecked: 318 | ovrd.mask = (CbmdosFsFlags) 319 | (ovrd.mask | CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 320 | ovrd.flags = (CbmdosFsFlags) 321 | (ovrd.flags & ~CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 322 | break; 323 | case Qt::PartiallyChecked: 324 | ovrd.mask = (CbmdosFsFlags) 325 | (ovrd.mask & ~CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 326 | ovrd.flags = (CbmdosFsFlags) 327 | (ovrd.flags & ~CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 328 | break; 329 | case Qt::Checked: 330 | ovrd.mask = (CbmdosFsFlags) 331 | (ovrd.mask | CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 332 | ovrd.flags = (CbmdosFsFlags) 333 | (ovrd.flags | CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 334 | break; 335 | } 336 | } 337 | else if (sender == &chainInterlvCheckBox) 338 | { 339 | switch (chainInterlvCheckBox.checkState()) 340 | { 341 | case Qt::Unchecked: 342 | ovrd.mask = (CbmdosFsFlags) 343 | (ovrd.mask | CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 344 | ovrd.flags = (CbmdosFsFlags) 345 | (ovrd.flags & ~CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 346 | break; 347 | case Qt::PartiallyChecked: 348 | ovrd.mask = (CbmdosFsFlags) 349 | (ovrd.mask & ~CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 350 | ovrd.flags = (CbmdosFsFlags) 351 | (ovrd.flags & ~CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 352 | break; 353 | case Qt::Checked: 354 | ovrd.mask = (CbmdosFsFlags) 355 | (ovrd.mask | CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 356 | ovrd.flags = (CbmdosFsFlags) 357 | (ovrd.flags | CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 358 | break; 359 | } 360 | } 361 | } 362 | 363 | void FsOptOverridesDialog::priv::changed(int value) 364 | { 365 | if (ovrd.mask & CFF_OVERRIDE_INTERLEAVE) 366 | { 367 | ovrd.flags = (CbmdosFsFlags) 368 | (ovrd.flags & ~CbmdosFsFlags::CFF_OVERRIDE_INTERLEAVE); 369 | ovrd.flags = (CbmdosFsFlags) 370 | (ovrd.flags | value); 371 | } 372 | } 373 | 374 | FsOptOverridesDialog::FsOptOverridesDialog(CbmdosFsOptOverrides *overrides, 375 | QWidget *parent) : 376 | QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | 377 | Qt::CustomizeWindowHint | Qt::MSWindowsFixedSizeDialogHint) 378 | { 379 | d = new priv(overrides); 380 | d->ovrdLayout.addWidget(&d->ovrdInterleaveCheckBox, 0, 0, 1, 2); 381 | d->ovrdLayout.addWidget(&d->interleaveLabel, 0, 2); 382 | d->ovrdLayout.addWidget(&d->interleaveSpinBox, 0, 3); 383 | d->ovrdLayout.addWidget(&d->ovrdStrategyCheckBox, 1, 0, 1, 4); 384 | d->ovrdLayout.addWidget(&d->allocOriginalButton, 2, 0, 1, 2); 385 | d->ovrdLayout.addWidget(&d->allocTrackloaderButton, 3, 0, 1, 2); 386 | d->ovrdLayout.addWidget(&d->allocSimpleButton, 4, 0, 1, 2); 387 | d->ovrdLayout.addWidget(&d->simpleInterleaveCheckBox, 2, 2, 1, 2); 388 | d->ovrdLayout.addWidget(&d->prefDirTrackCheckBox, 3, 2, 1, 2); 389 | d->ovrdLayout.addWidget(&d->chainInterlvCheckBox, 4, 2, 1, 2); 390 | d->mainLayout.addLayout(&d->ovrdLayout); 391 | d->mainLayout.addWidget(&d->buttons); 392 | setLayout(&d->mainLayout); 393 | 394 | connect(&d->buttons, &QDialogButtonBox::accepted, 395 | this, &FsOptOverridesDialog::accept); 396 | connect(&d->buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); 397 | connect(&d->buttons, &QDialogButtonBox::clicked, this, 398 | [this](QAbstractButton *button) 399 | { 400 | if (d->buttons.buttonRole(button) 401 | == QDialogButtonBox::ResetRole) 402 | { 403 | d->reset(); 404 | } 405 | }); 406 | 407 | auto handleChange = [this](int){ d->clicked(sender()); }; 408 | connect(&d->ovrdInterleaveCheckBox, &QCheckBox::stateChanged, 409 | this, handleChange); 410 | connect(&d->interleaveSpinBox, 411 | static_cast(&QSpinBox::valueChanged), 412 | this, [this](int i){d->changed(i);}); 413 | connect(&d->ovrdStrategyCheckBox, &QCheckBox::stateChanged, 414 | this, handleChange); 415 | connect(&d->allocOriginalButton, &QAbstractButton::clicked, 416 | this, handleChange); 417 | connect(&d->allocTrackloaderButton, &QAbstractButton::clicked, 418 | this, handleChange); 419 | connect(&d->allocSimpleButton, &QAbstractButton::clicked, 420 | this, handleChange); 421 | connect(&d->simpleInterleaveCheckBox, &QCheckBox::stateChanged, 422 | this, handleChange); 423 | connect(&d->prefDirTrackCheckBox, &QCheckBox::stateChanged, 424 | this, handleChange); 425 | connect(&d->chainInterlvCheckBox, &QCheckBox::stateChanged, 426 | this, handleChange); 427 | } 428 | 429 | FsOptOverridesDialog::~FsOptOverridesDialog() 430 | { 431 | delete d; 432 | } 433 | 434 | void FsOptOverridesDialog::accept() 435 | { 436 | memcpy(d->overrides, &d->ovrd, sizeof d->ovrd); 437 | QDialog::accept(); 438 | } 439 | 440 | void FsOptOverridesDialog::showEvent(QShowEvent *event) 441 | { 442 | QCoreApplication::processEvents(); 443 | QDialog::showEvent(event); 444 | adjustSize(); 445 | setFixedSize(size()); 446 | QCoreApplication::processEvents(); 447 | 448 | QRect dlgRect = frameGeometry(); 449 | QRect mainWinRect = parentWidget()->window()->frameGeometry(); 450 | dlgRect.moveCenter(mainWinRect.center()); 451 | 452 | const QScreen *screen = 0; 453 | const QWindow *currentWin = parentWidget()->windowHandle(); 454 | if (currentWin) 455 | { 456 | screen = currentWin->screen(); 457 | } 458 | if (screen) 459 | { 460 | QRect screenRect = screen->availableGeometry(); 461 | if (dlgRect.right() > screenRect.right()) 462 | { 463 | dlgRect.moveRight(screenRect.right()); 464 | } 465 | if (dlgRect.bottom() > screenRect.bottom()) 466 | { 467 | dlgRect.moveBottom(screenRect.bottom()); 468 | } 469 | if (dlgRect.top() < screenRect.top()) 470 | { 471 | dlgRect.moveTop(screenRect.top()); 472 | } 473 | if (dlgRect.left() < screenRect.left()) 474 | { 475 | dlgRect.moveLeft(screenRect.left()); 476 | } 477 | } 478 | move(dlgRect.topLeft()); 479 | } 480 | 481 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfilewidget.cpp: -------------------------------------------------------------------------------- 1 | #include "cbmdosfilewidget.h" 2 | #include "editoperationcheck.h" 3 | #include "petsciistr.h" 4 | #include "petsciiedit.h" 5 | #include "settings.h" 6 | #include "utils.h" 7 | #include "v1541commander.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #ifdef _WIN32 14 | #include 15 | #endif 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include <1541img/cbmdosfile.h> 25 | #include <1541img/cbmdosfileeventargs.h> 26 | #include <1541img/event.h> 27 | #include <1541img/filedata.h> 28 | #include <1541img/petscii.h> 29 | 30 | static void evhdl(void *receiver, int id, const void *sender, const void *args) 31 | { 32 | (void) id; 33 | (void) sender; 34 | 35 | CbmdosFileWidget *widget = (CbmdosFileWidget *)receiver; 36 | const CbmdosFileEventArgs *eventArgs = (const CbmdosFileEventArgs *)args; 37 | widget->fileChanged(eventArgs); 38 | } 39 | 40 | class CbmdosFileWidget::priv 41 | { 42 | public: 43 | priv(); 44 | CbmdosFile *file; 45 | QVBoxLayout layout; 46 | QHBoxLayout nameLayout; 47 | QLabel nameLabel; 48 | PetsciiEdit name; 49 | QHBoxLayout optionsLayout; 50 | QLabel typeLabel; 51 | QComboBox type; 52 | QCheckBox locked; 53 | QCheckBox closed; 54 | QHBoxLayout recordLengthLayout; 55 | QLabel recordLengthLabel; 56 | QSpinBox recordLength; 57 | QHBoxLayout forcedBlocksLayout; 58 | QCheckBox forcedBlocksActive; 59 | QSpinBox forcedBlocks; 60 | QHBoxLayout dataLayout; 61 | QLabel dataLabel; 62 | QLabel dataSizeLabel; 63 | QPushButton importButton; 64 | QPushButton exportButton; 65 | int lastNameCursorPos; 66 | bool ignoreNameCursorPos; 67 | 68 | void setDataSize(size_t size); 69 | }; 70 | 71 | CbmdosFileWidget::priv::priv() : 72 | file(0), 73 | layout(), 74 | nameLayout(), 75 | nameLabel(tr("Name: ")), 76 | name(), 77 | optionsLayout(), 78 | typeLabel(tr("Type: ")), 79 | type(), 80 | locked(tr("locked")), 81 | closed(tr("closed")), 82 | recordLengthLayout(), 83 | recordLengthLabel(tr("record length (for REL files): ")), 84 | recordLength(), 85 | forcedBlocksLayout(), 86 | forcedBlocksActive(tr("forced block size: ")), 87 | forcedBlocks(), 88 | dataLayout(), 89 | dataLabel(tr("Content: ")), 90 | dataSizeLabel(tr("")), 91 | importButton(tr("import")), 92 | exportButton(tr("export")), 93 | lastNameCursorPos(0), 94 | ignoreNameCursorPos(false) 95 | { 96 | } 97 | 98 | void CbmdosFileWidget::priv::setDataSize(size_t size) 99 | { 100 | dataSizeLabel.setText(QString("%1 KiB") 101 | .arg(QLocale::system().toString(size/1024.0, 'f', 3), -5, '0')); 102 | } 103 | 104 | CbmdosFileWidget::CbmdosFileWidget(QWidget *parent) 105 | : QGroupBox(tr("File properties"), parent) 106 | { 107 | d = new priv(); 108 | d->nameLayout.addWidget(&d->nameLabel); 109 | d->nameLayout.addWidget(&d->name); 110 | d->name.setMaxLength(16); 111 | d->type.addItem("DEL", CbmdosFileType::CFT_DEL); 112 | d->type.addItem("SEQ", CbmdosFileType::CFT_SEQ); 113 | d->type.addItem("PRG", CbmdosFileType::CFT_PRG); 114 | d->type.addItem("USR", CbmdosFileType::CFT_USR); 115 | d->type.addItem("REL", CbmdosFileType::CFT_REL); 116 | d->optionsLayout.addWidget(&d->typeLabel); 117 | d->optionsLayout.addWidget(&d->type); 118 | d->optionsLayout.addWidget(&d->locked); 119 | d->optionsLayout.addWidget(&d->closed); 120 | d->recordLength.setMinimum(1); 121 | d->recordLength.setMaximum(254); 122 | d->recordLength.setValue(254); 123 | d->recordLengthLayout.addWidget(&d->recordLengthLabel); 124 | d->recordLengthLayout.addWidget(&d->recordLength); 125 | d->forcedBlocks.setMinimum(0); 126 | d->forcedBlocks.setMaximum(0xfffe); 127 | d->forcedBlocks.setValue(0); 128 | d->forcedBlocks.setEnabled(false); 129 | d->forcedBlocksLayout.addWidget(&d->forcedBlocksActive); 130 | d->forcedBlocksLayout.addWidget(&d->forcedBlocks); 131 | d->dataLayout.addWidget(&d->dataLabel); 132 | d->dataLayout.addWidget(&d->dataSizeLabel); 133 | d->dataLayout.addWidget(&d->importButton); 134 | d->dataLayout.addWidget(&d->exportButton); 135 | d->layout.addLayout(&d->nameLayout); 136 | d->layout.addLayout(&d->optionsLayout); 137 | d->layout.addLayout(&d->recordLengthLayout); 138 | d->layout.addLayout(&d->forcedBlocksLayout); 139 | d->layout.addLayout(&d->dataLayout); 140 | setLayout(&d->layout); 141 | setEnabled(false); 142 | connect(&d->name, &PetsciiEdit::petsciiEdited, 143 | this, &CbmdosFileWidget::nameChanged); 144 | connect(&d->type, SIGNAL(currentIndexChanged(int)), 145 | this, SLOT(typeChanged(int))); 146 | connect(&d->locked, &QCheckBox::stateChanged, 147 | this, &CbmdosFileWidget::lockedChanged); 148 | connect(&d->closed, &QCheckBox::stateChanged, 149 | this, &CbmdosFileWidget::closedChanged); 150 | connect(&d->recordLength, SIGNAL(valueChanged(int)), 151 | this, SLOT(recordLengthChanged(int))); 152 | connect(&d->forcedBlocksActive, &QCheckBox::stateChanged, 153 | this, &CbmdosFileWidget::forcedBlocksActiveChanged); 154 | connect(&d->forcedBlocks, SIGNAL(valueChanged(int)), 155 | this, SLOT(forcedBlocksValueChanged(int))); 156 | connect(&d->importButton, &QPushButton::clicked, 157 | this, &CbmdosFileWidget::importFile); 158 | connect(&d->exportButton, &QPushButton::clicked, 159 | this, &CbmdosFileWidget::exportFile); 160 | 161 | QShortcut *f2 = new QShortcut(QKeySequence(Qt::Key_F2), this); 162 | connect(f2, &QShortcut::activated, this, [this](){ 163 | d->name.selectAll(); 164 | d->name.setFocus(); 165 | }); 166 | d->name.setToolTip(tr("The name of the file (F2)")); 167 | d->nameLabel.setToolTip(tr("The name of the file (F2)")); 168 | QShortcut *f8 = new QShortcut(QKeySequence(Qt::Key_F8), this); 169 | connect(f8, &QShortcut::activated, this, [this](){ 170 | d->type.setCurrentIndex(0);}); 171 | QShortcut *f9 = new QShortcut(QKeySequence(Qt::Key_F9), this); 172 | connect(f9, &QShortcut::activated, this, [this](){ 173 | d->type.setCurrentIndex(1);}); 174 | QShortcut *f10 = new QShortcut(QKeySequence(Qt::Key_F10), this); 175 | connect(f10, &QShortcut::activated, this, [this](){ 176 | d->type.setCurrentIndex(2);}); 177 | QShortcut *f11 = new QShortcut(QKeySequence(Qt::Key_F11), this); 178 | connect(f11, &QShortcut::activated, this, [this](){ 179 | d->type.setCurrentIndex(3);}); 180 | QShortcut *f12 = new QShortcut(QKeySequence(Qt::Key_F12), this); 181 | connect(f12, &QShortcut::activated, this, [this](){ 182 | d->type.setCurrentIndex(4);}); 183 | d->type.setToolTip(tr("The type of the file (F8-F12)")); 184 | d->typeLabel.setToolTip(tr("The type of the file (F8-F12)")); 185 | QShortcut *csl = new QShortcut( 186 | QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_L), this); 187 | connect(csl, &QShortcut::activated, &d->locked, &QCheckBox::click); 188 | d->locked.setToolTip(tr("File locked flag (Ctrl+Shift+L)")); 189 | QShortcut *csc = new QShortcut( 190 | QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_C), this); 191 | connect(csc, &QShortcut::activated, &d->closed, &QCheckBox::click); 192 | d->closed.setToolTip(tr("File closed flag (Ctrl+Shift+C)")); 193 | QShortcut *f7 = new QShortcut(QKeySequence(Qt::Key_F7), this); 194 | connect(f7, &QShortcut::activated, this, [this](){ 195 | d->recordLength.selectAll(); 196 | d->recordLength.setFocus(); 197 | }); 198 | d->recordLength.setToolTip( 199 | tr("The length of a record in a REL file (F7)")); 200 | d->recordLengthLabel.setToolTip( 201 | tr("The length of a record in a REL file (F7)")); 202 | QShortcut *sf6 = new QShortcut(QKeySequence(Qt::SHIFT+Qt::Key_F6), this); 203 | connect(sf6, &QShortcut::activated, 204 | &d->forcedBlocksActive, &QCheckBox::click); 205 | d->forcedBlocksActive.setToolTip( 206 | tr("Enable fake display of block size (Shift+F6)")); 207 | QShortcut *f6 = new QShortcut(QKeySequence(Qt::Key_F6), this); 208 | connect(f6, &QShortcut::activated, this, [this](){ 209 | if (!d->forcedBlocksActive.isChecked()) 210 | { 211 | d->forcedBlocksActive.click(); 212 | } 213 | d->forcedBlocks.selectAll(); 214 | d->forcedBlocks.setFocus(); 215 | }); 216 | d->forcedBlocks.setToolTip(tr("Fake block size to display (F6)")); 217 | QShortcut *csi = new QShortcut( 218 | QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_I), this); 219 | connect(csi, &QShortcut::activated, &d->importButton, &QPushButton::click); 220 | d->importButton.setToolTip(tr("Import file contents (Ctrl+Shift+I)")); 221 | QShortcut *cse = new QShortcut( 222 | QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_E), this); 223 | connect(cse, &QShortcut::activated, &d->exportButton, &QPushButton::click); 224 | d->exportButton.setToolTip(tr("Export file contents (Ctrl+Shift+E)")); 225 | 226 | connect(&d->name, &QLineEdit::cursorPositionChanged, 227 | this, [this](int oldPos, int newPos){ 228 | if (!d->ignoreNameCursorPos) 229 | { 230 | (void)oldPos; 231 | d->lastNameCursorPos = newPos; 232 | } 233 | }); 234 | 235 | connect(&cmdr, &V1541Commander::lowerCaseChanged, 236 | this, [this](bool lowerCase){ 237 | d->ignoreNameCursorPos = true; 238 | d->name.updateCase(lowerCase); 239 | d->ignoreNameCursorPos = false; 240 | }); 241 | } 242 | 243 | CbmdosFileWidget::~CbmdosFileWidget() 244 | { 245 | delete d; 246 | } 247 | 248 | void CbmdosFileWidget::nameChanged(const PetsciiStr &name) 249 | { 250 | if (d->file) 251 | { 252 | CbmdosFile_setName(d->file, name.petscii(), name.length()); 253 | } 254 | } 255 | 256 | void CbmdosFileWidget::typeChanged(int typeIndex) 257 | { 258 | if (typeIndex >= 0 && d->file) 259 | { 260 | CbmdosFileType type = (CbmdosFileType) d->type.currentData().toInt(); 261 | if (type == CbmdosFileType::CFT_DEL) 262 | { 263 | const FileData *data = CbmdosFile_rdata(d->file); 264 | size_t size = FileData_size(data); 265 | if (size) 266 | { 267 | QMessageBox::StandardButton reply = QMessageBox::question( 268 | this, tr("Erase file contents?"), tr("Changing the " 269 | "file type to DEL will erase all file contents. " 270 | "Proceed anyways?"), 271 | QMessageBox::Ok|QMessageBox::Cancel); 272 | if (reply != QMessageBox::Ok) 273 | { 274 | d->type.setCurrentIndex(d->type.findData( 275 | CbmdosFile_type(d->file))); 276 | return; 277 | } 278 | } 279 | } 280 | CbmdosFile_setType(d->file, type); 281 | if (type == CbmdosFileType::CFT_DEL) 282 | { 283 | d->dataSizeLabel.setText(tr("")); 284 | d->importButton.setEnabled(false); 285 | d->exportButton.setEnabled(false); 286 | } 287 | else 288 | { 289 | const FileData *data = CbmdosFile_rdata(d->file); 290 | size_t size = FileData_size(data); 291 | if (!size) 292 | { 293 | d->dataSizeLabel.setText(tr("")); 294 | d->importButton.setEnabled(true); 295 | d->exportButton.setEnabled(false); 296 | } 297 | else 298 | { 299 | d->setDataSize(size); 300 | d->importButton.setEnabled(true); 301 | d->exportButton.setEnabled(true); 302 | } 303 | } 304 | } 305 | } 306 | 307 | void CbmdosFileWidget::lockedChanged(int lockedState) 308 | { 309 | if (d->file) 310 | { 311 | CbmdosFile_setLocked(d->file, !!lockedState); 312 | } 313 | } 314 | 315 | void CbmdosFileWidget::closedChanged(int closedState) 316 | { 317 | if (d->file) 318 | { 319 | CbmdosFile_setClosed(d->file, !!closedState); 320 | } 321 | } 322 | 323 | void CbmdosFileWidget::recordLengthChanged(int value) 324 | { 325 | if (d->file) 326 | { 327 | CbmdosFile_setRecordLength(d->file, value); 328 | } 329 | } 330 | 331 | void CbmdosFileWidget::forcedBlocksActiveChanged(int activeState) 332 | { 333 | if (d->file) 334 | { 335 | if (activeState) 336 | { 337 | d->forcedBlocks.setEnabled(true); 338 | d->forcedBlocks.setValue(0); 339 | CbmdosFile_setForcedBlocks(d->file, 0); 340 | } 341 | else 342 | { 343 | d->forcedBlocks.setEnabled(false); 344 | d->forcedBlocks.setValue(0); 345 | CbmdosFile_setForcedBlocks(d->file, 0xffff); 346 | } 347 | } 348 | } 349 | 350 | void CbmdosFileWidget::forcedBlocksValueChanged(int value) 351 | { 352 | if (d->file && d->forcedBlocks.isEnabled()) 353 | { 354 | CbmdosFile_setForcedBlocks(d->file, value); 355 | } 356 | } 357 | 358 | static QString getFilterForType(CbmdosFileType type) 359 | { 360 | switch (type) 361 | { 362 | case CbmdosFileType::CFT_PRG: 363 | #ifdef _WIN32 364 | return QCoreApplication::translate("CbmdosFileWidget", 365 | "PRG files (*.prg);;P00 files (*.p00);;all files (*)"); 366 | #else 367 | return QCoreApplication::translate("CbmdosFileWidget", 368 | "PRG files (*.prg);;" 369 | "P00 files (*.p00 *.p[0-9][0-9]);;all files (*)"); 370 | #endif 371 | case CbmdosFileType::CFT_SEQ: 372 | #ifdef _WIN32 373 | return QCoreApplication::translate("CbmdosFileWidget", 374 | "SEQ files (*.seq);;S00 files (*.s00);;all files (*)"); 375 | #else 376 | return QCoreApplication::translate("CbmdosFileWidget", 377 | "SEQ files (*.seq);;" 378 | "S00 files (*.s00 *.s[0-9][0-9]);;all files (*)"); 379 | #endif 380 | case CbmdosFileType::CFT_USR: 381 | #ifdef _WIN32 382 | return QCoreApplication::translate("CbmdosFileWidget", 383 | "USR files (*.usr);;U00 files (*.u00);;all files (*)"); 384 | #else 385 | return QCoreApplication::translate("CbmdosFileWidget", 386 | "USR files (*.usr);;" 387 | "U00 files (*.u00 *.u[0-9][0-9]);;all files (*)"); 388 | #endif 389 | case CbmdosFileType::CFT_REL: 390 | #ifdef _WIN32 391 | return QCoreApplication::translate("CbmdosFileWidget", 392 | "R00 files (*.r00);;all files (*)"); 393 | #else 394 | return QCoreApplication::translate("CbmdosFileWidget", 395 | "R00 files (*.r00 *.r[0-9][0-9]);;all files (*)"); 396 | #endif 397 | default: 398 | return QCoreApplication::translate("CbmdosFileWidget", 399 | "all files (*)"); 400 | } 401 | } 402 | 403 | void CbmdosFileWidget::importFile() 404 | { 405 | if (CbmdosFile_type(d->file) == CbmdosFileType::CFT_DEL) return; 406 | QString hostFile = QFileDialog::getOpenFileName(this, tr("Import file"), 407 | QString(), getFilterForType(CbmdosFile_type(d->file))); 408 | if (!hostFile.isEmpty()) 409 | { 410 | FILE *f = qfopen(hostFile, "rb"); 411 | if (f) 412 | { 413 | if (cmdr.settings().warnDiskCapacity()) 414 | { 415 | CbmdosFile *newFile = CbmdosFile_clone(d->file); 416 | if (CbmdosFile_import(newFile, f) < 0) 417 | { 418 | CbmdosFile_destroy(newFile); 419 | fclose(f); 420 | QMessageBox::critical(this, tr("Error reading file"), 421 | tr("The selected file couldn't be read.")); 422 | return; 423 | } 424 | rewind(f); 425 | EditOperationCheck check(newFile, d->file); 426 | emit checkEditOperation(check); 427 | CbmdosFile_destroy(newFile); 428 | if (!check.allowed()) 429 | { 430 | fclose(f); 431 | return; 432 | } 433 | } 434 | if (CbmdosFile_import(d->file, f) >= 0) 435 | { 436 | d->setDataSize(FileData_size(CbmdosFile_rdata(d->file))); 437 | d->exportButton.setEnabled(true); 438 | uint8_t nameLength; 439 | const char *name = CbmdosFile_name(d->file, &nameLength); 440 | d->name.setPetscii(PetsciiStr(name, nameLength)); 441 | d->recordLength.setValue(CbmdosFile_recordLength(d->file)); 442 | } 443 | else 444 | { 445 | QMessageBox::critical(this, tr("Error reading file"), 446 | tr("The selected file couldn't be read.")); 447 | } 448 | fclose(f); 449 | } 450 | else 451 | { 452 | QMessageBox::critical(this, tr("Error opening file"), 453 | tr("The selected file couldn't be opened for reading.")); 454 | } 455 | } 456 | } 457 | 458 | void CbmdosFileWidget::exportFile() 459 | { 460 | if (CbmdosFile_type(d->file) == CbmdosFileType::CFT_DEL) return; 461 | const FileData *data = CbmdosFile_rdata(d->file); 462 | if (!data || !FileData_size(data)) return; 463 | uint8_t namelen; 464 | const char *name = CbmdosFile_name(d->file, &namelen); 465 | char utf8name[65]; 466 | petscii_toUtf8(utf8name, 65, name, namelen, 467 | cmdr.settings().lowercase(), 1, 0, 0); 468 | QString hostFile = QFileDialog::getSaveFileName(this, tr("Export file"), 469 | qfnsan(QString::fromUtf8(utf8name)), 470 | getFilterForType(CbmdosFile_type(d->file))); 471 | if (!hostFile.isEmpty()) 472 | { 473 | #ifdef _WIN32 474 | QFileInfo fileInfo(hostFile); 475 | if (fileInfo.completeSuffix().contains('.')) 476 | { 477 | QFileInfo corrected(fileInfo.dir(), fileInfo.completeBaseName()); 478 | hostFile = corrected.filePath(); 479 | } 480 | #endif 481 | QRegularExpression pc64pat("\\.[PpSsUuRr]\\d{2}$"); 482 | bool ispc64 = pc64pat.match(hostFile).hasMatch(); 483 | if (!ispc64 && CbmdosFile_type(d->file) == CbmdosFileType::CFT_REL 484 | && QMessageBox::question(this, 485 | tr("Export REL file as raw data?"), 486 | tr("You are about to export a REL file as raw data. To " 487 | "export it as PC64 R00 file instead, specify a " 488 | "matching file extension (like .R00, .R01, .R02, " 489 | "etc.)\n" 490 | "Exporting as raw data will lose the record length. " 491 | "Do you still want to export it as raw data?"), 492 | QMessageBox::Yes|QMessageBox::No) != QMessageBox::Yes) 493 | { 494 | return; 495 | } 496 | FILE *f = qfopen(hostFile, "wb"); 497 | if (f) 498 | { 499 | int rc = 0; 500 | if (ispc64) rc = CbmdosFile_exportPC64(d->file, f); 501 | else rc = CbmdosFile_exportRaw(d->file, f); 502 | fclose(f); 503 | if (rc < 0) 504 | { 505 | QMessageBox::critical(this, tr("Error writing file"), 506 | tr("The selected file couldn't be written.")); 507 | } 508 | } 509 | else 510 | { 511 | QMessageBox::critical(this, tr("Error opening file"), 512 | tr("The selected file couldn't be opened for writing.")); 513 | } 514 | } 515 | } 516 | 517 | CbmdosFile *CbmdosFileWidget::file() const 518 | { 519 | return d->file; 520 | } 521 | 522 | void CbmdosFileWidget::setFile(CbmdosFile *file) 523 | { 524 | if (d->file) 525 | { 526 | Event_unregister(CbmdosFile_changedEvent(d->file), this, evhdl); 527 | } 528 | d->file = 0; 529 | bool hadSelectedText = d->name.hasSelectedText(); 530 | d->ignoreNameCursorPos = true; 531 | if (file) 532 | { 533 | setEnabled(true); 534 | uint8_t nameLength; 535 | const char *name = CbmdosFile_name(file, &nameLength); 536 | d->name.setPetscii(PetsciiStr(name, nameLength)); 537 | CbmdosFileType type = CbmdosFile_type(file); 538 | d->type.setCurrentIndex(d->type.findData(type)); 539 | d->locked.setChecked(CbmdosFile_locked(file)); 540 | d->closed.setChecked(CbmdosFile_closed(file)); 541 | d->recordLength.setValue(CbmdosFile_recordLength(file)); 542 | uint16_t forcedBlocks = CbmdosFile_forcedBlocks(file); 543 | if (forcedBlocks == 0xffff) 544 | { 545 | d->forcedBlocksActive.setChecked(false); 546 | d->forcedBlocks.setEnabled(false); 547 | d->forcedBlocks.setValue(0); 548 | } 549 | else 550 | { 551 | d->forcedBlocksActive.setChecked(true); 552 | d->forcedBlocks.setEnabled(true); 553 | d->forcedBlocks.setValue(forcedBlocks); 554 | } 555 | const FileData *fd = CbmdosFile_rdata(file); 556 | size_t size = FileData_size(fd); 557 | if (type == CbmdosFileType::CFT_DEL) 558 | { 559 | d->dataSizeLabel.setText(tr("")); 560 | d->importButton.setEnabled(false); 561 | d->exportButton.setEnabled(false); 562 | } 563 | else if (!size) 564 | { 565 | d->dataSizeLabel.setText(tr("")); 566 | d->importButton.setEnabled(true); 567 | d->exportButton.setEnabled(false); 568 | } 569 | else 570 | { 571 | d->setDataSize(size); 572 | d->importButton.setEnabled(true); 573 | d->exportButton.setEnabled(true); 574 | } 575 | Event_register(CbmdosFile_changedEvent(file), this, evhdl); 576 | } 577 | else 578 | { 579 | setEnabled(false); 580 | d->name.setText(QString()); 581 | d->dataSizeLabel.setText(tr("")); 582 | } 583 | d->file = file; 584 | if (hadSelectedText) 585 | { 586 | d->name.selectAll(); 587 | } 588 | else 589 | { 590 | d->name.setCursorPosition(d->lastNameCursorPos); 591 | } 592 | d->ignoreNameCursorPos = false; 593 | } 594 | 595 | void CbmdosFileWidget::fileChanged(const CbmdosFileEventArgs *args) 596 | { 597 | if (args->what == CbmdosFileEventArgs::CFE_NAMECHANGED) 598 | { 599 | d->ignoreNameCursorPos = true; 600 | uint8_t nameLength; 601 | const char *name = CbmdosFile_name(d->file, &nameLength); 602 | d->name.setPetscii(PetsciiStr(name, nameLength), true); 603 | d->ignoreNameCursorPos = false; 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /src/bin/v1541commander/cbmdosfsoptionsdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "cbmdosfsoptionsdialog.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include <1541img/cbmdosfsoptions.h> 16 | 17 | class CbmdosFsOptionsDialog::priv 18 | { 19 | public: 20 | priv(CbmdosFsOptions *options, bool canCancel); 21 | CbmdosFsOptions *options; 22 | CbmdosFsOptions opts; 23 | QVBoxLayout mainLayout; 24 | QGridLayout optionsLayout; 25 | QRadioButton tracks35Button; 26 | QRadioButton tracks40Button; 27 | QRadioButton tracks42Button; 28 | QCheckBox allowLongDirCheckBox; 29 | QCheckBox filesOnDirTrackCheckBox; 30 | QCheckBox dolphinDosBamCheckBox; 31 | QCheckBox speedDosBamCheckBox; 32 | QCheckBox prologicDosBamCheckBox; 33 | QCheckBox zeroFreeCheckBox; 34 | QLabel dirInterleaveLabel; 35 | QSpinBox dirInterleaveSpinBox; 36 | QLabel fileInterleaveLabel; 37 | QSpinBox fileInterleaveSpinBox; 38 | QGroupBox allocationOptions; 39 | QGridLayout allocationLayout; 40 | QRadioButton allocOriginalButton; 41 | QRadioButton allocTrackloaderButton; 42 | QRadioButton allocSimpleButton; 43 | QCheckBox simpleInterleaveCheckBox; 44 | QCheckBox prefDirTrackCheckBox; 45 | QCheckBox chainInterlvCheckBox; 46 | QLabel recoverWarningLabel; 47 | QCheckBox rewriteAfterRecover; 48 | QDialogButtonBox buttons; 49 | 50 | void reset(); 51 | void clicked(QObject *sender, bool checked); 52 | void changed(QObject *sender, int value); 53 | }; 54 | 55 | CbmdosFsOptionsDialog::priv::priv(CbmdosFsOptions *options, bool canCancel) : 56 | options(options), 57 | opts(), 58 | mainLayout(), 59 | optionsLayout(), 60 | tracks35Button(tr("35 tracks")), 61 | tracks40Button(tr("40 tracks")), 62 | tracks42Button(tr("42 tracks")), 63 | allowLongDirCheckBox(tr("allow long directories")), 64 | filesOnDirTrackCheckBox(tr("place files on dir track")), 65 | dolphinDosBamCheckBox(tr("DolphinDOS BAM")), 66 | speedDosBamCheckBox(tr("SpeedDOS BAM")), 67 | prologicDosBamCheckBox(tr("PrologicDOS BAM")), 68 | zeroFreeCheckBox(tr("report 0 blocks free")), 69 | dirInterleaveLabel(tr("directory interleave: ")), 70 | dirInterleaveSpinBox(), 71 | fileInterleaveLabel(tr("default file interleave: ")), 72 | fileInterleaveSpinBox(), 73 | allocationOptions(tr("Track/block allocation options")), 74 | allocationLayout(), 75 | allocOriginalButton(tr("CBM DOS strategy")), 76 | allocTrackloaderButton(tr("Trackloader strategy")), 77 | allocSimpleButton(tr("Simple strategy")), 78 | simpleInterleaveCheckBox(tr("Use simple interleave")), 79 | prefDirTrackCheckBox(tr("Prefer dir track for files")), 80 | chainInterlvCheckBox(tr("Apply interleave on track change")), 81 | recoverWarningLabel(tr("WARNING: this disk image is broken, trying to " 82 | "recover data from it. It will be treated like a new disk " 83 | "image. It's recommended to rewrite the disk image after " 84 | "recovery, so you can edit it.")), 85 | rewriteAfterRecover(tr("Rewrite image after recovery")), 86 | buttons(canCancel ? 87 | QDialogButtonBox::Ok|QDialogButtonBox::Cancel 88 | |QDialogButtonBox::Reset 89 | : QDialogButtonBox::Ok|QDialogButtonBox::Reset) 90 | { 91 | dirInterleaveSpinBox.setMinimum(1); 92 | dirInterleaveSpinBox.setMaximum(20); 93 | fileInterleaveSpinBox.setMinimum(1); 94 | fileInterleaveSpinBox.setMaximum(20); 95 | recoverWarningLabel.setWordWrap(true); 96 | tracks35Button.setToolTip( 97 | tr("Filesystem uses 35 tracks on disk (default).")); 98 | tracks40Button.setToolTip( 99 | tr("Filesystem uses 40 tracks on disk, like some speeder DOS " 100 | "versions do.")); 101 | tracks42Button.setToolTip( 102 | tr("Filesystem uses 42 tracks on disk.\nNOT RECOMMENDED, not all " 103 | "drives can physically access these tracks.")); 104 | allowLongDirCheckBox.setToolTip( 105 | tr("Allow directories longer than 144 files.\n" 106 | "Directory blocks will be allocated on tracks other than " 107 | "track 18.\n" 108 | "The original 1541 DOS can read this, but corrupts the " 109 | "directory on write.")); 110 | filesOnDirTrackCheckBox.setToolTip( 111 | tr("Allow to use track 18 also for files when all other tracks " 112 | "are full.\n" 113 | "The original 1541 DOS can still read such a filesystem.")); 114 | dolphinDosBamCheckBox.setToolTip( 115 | tr("Write BAM entries for tracks 36-40 in the format " 116 | "DolphinDOS uses.")); 117 | speedDosBamCheckBox.setToolTip( 118 | tr("Write BAM entries for tracks 36-40 in the format " 119 | "SpeedDOS uses.")); 120 | prologicDosBamCheckBox.setToolTip( 121 | tr("Write BAM entries for tracks 36-40 in the format " 122 | "Prologic DOS uses.\n" 123 | "This is NOT compatible with the other extended " 124 | "BAM formats.")); 125 | zeroFreeCheckBox.setToolTip( 126 | tr("Write 0 as the number of free blocks per track for every " 127 | "track in the BAM.\n" 128 | "As a result, the directory listing shows " 129 | "\"0 blocks free.\"")); 130 | QString dirInterleaveToolTip( 131 | tr("Interleave value to use for blocks of the directory " 132 | "(default: 3)")); 133 | QString fileInterleaveToolTip( 134 | tr("Interleave value to use by default for blocks of files " 135 | "(default: 10)")); 136 | dirInterleaveLabel.setToolTip(dirInterleaveToolTip); 137 | dirInterleaveSpinBox.setToolTip(dirInterleaveToolTip); 138 | fileInterleaveLabel.setToolTip(fileInterleaveToolTip); 139 | fileInterleaveSpinBox.setToolTip(fileInterleaveToolTip); 140 | allocOriginalButton.setToolTip( 141 | tr("Use the original CBM DOS strategy for allocating blocks.\n" 142 | "For starting a new file, a track as close as possible to " 143 | "track 18 is used.\n" 144 | "While writing a file, when the current track is full, the " 145 | "next track in the direction away from track 18 is used.\n" 146 | "Good for random access to different files, with the files " 147 | "at the top of the directory preferred.")); 148 | allocTrackloaderButton.setToolTip( 149 | tr("Use an allocation strategy often used with trackloaders.\n" 150 | "Tracks are used starting at track 1 and strictly " 151 | "incrementing.\n" 152 | "Good for a set of files that's always read in the same " 153 | "order.")); 154 | allocSimpleButton.setToolTip( 155 | tr("Use a simplified allocation strategy.\n" 156 | "Tracks are used starting at track 19 and strictly " 157 | "incrementing up to track 40, then wrapping around to " 158 | "track 1.\n" 159 | "On 42-track images, tracks 41 and 42 are still used last.\n" 160 | "This is a compromise between the other strategies, with " 161 | "the first files close to track 18, and the files " 162 | "\"following\" each other on disk.")); 163 | simpleInterleaveCheckBox.setToolTip( 164 | tr("When adding interleave, use a simple \"modulo\" to get a " 165 | "valid sector number on the current track.\n" 166 | "If this is not checked, interleave is applied like the " 167 | "original CBM DOS does it:\n" 168 | "When wrapping over sector 0, 1 is subtracted from " 169 | "the result.")); 170 | prefDirTrackCheckBox.setToolTip( 171 | tr("Put files on the directory track first.\n" 172 | "This has only an effect if files on the directory track " 173 | "are allowed.\n" 174 | "When this option is set, you should \"rewrite\" your image " 175 | "from time to time,\n" 176 | "to ensure exactly the sectors on track 18 not used by the " 177 | "directory are allocated.")); 178 | chainInterlvCheckBox.setToolTip( 179 | tr("When having to switch to another track, still apply " 180 | "interleave to the sector number.\n" 181 | "If this is not checked, looking for a free sector on a new " 182 | "track\n" 183 | "always starts at sector 0 (original CBM DOS behavior).")); 184 | reset(); 185 | } 186 | 187 | void CbmdosFsOptionsDialog::priv::reset() 188 | { 189 | memcpy(&opts, options, sizeof opts); 190 | if (opts.flags & CbmdosFsFlags::CFF_42TRACK) 191 | { 192 | tracks42Button.setChecked(true); 193 | dolphinDosBamCheckBox.setEnabled(true); 194 | speedDosBamCheckBox.setEnabled(true); 195 | prologicDosBamCheckBox.setEnabled(true); 196 | } 197 | else if (opts.flags & CbmdosFsFlags::CFF_40TRACK) 198 | { 199 | tracks40Button.setChecked(true); 200 | dolphinDosBamCheckBox.setEnabled(true); 201 | speedDosBamCheckBox.setEnabled(true); 202 | prologicDosBamCheckBox.setEnabled(true); 203 | } 204 | else 205 | { 206 | tracks35Button.setChecked(true); 207 | dolphinDosBamCheckBox.setEnabled(false); 208 | speedDosBamCheckBox.setEnabled(false); 209 | prologicDosBamCheckBox.setEnabled(false); 210 | } 211 | allowLongDirCheckBox.setChecked( 212 | opts.flags & CbmdosFsFlags::CFF_ALLOWLONGDIR); 213 | if (opts.flags & CbmdosFsFlags::CFF_FILESONDIRTRACK) 214 | { 215 | filesOnDirTrackCheckBox.setChecked(true); 216 | prefDirTrackCheckBox.setChecked( 217 | opts.flags & CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 218 | prefDirTrackCheckBox.setEnabled(true); 219 | } 220 | else 221 | { 222 | filesOnDirTrackCheckBox.setChecked(false); 223 | prefDirTrackCheckBox.setChecked(false); 224 | prefDirTrackCheckBox.setEnabled(false); 225 | } 226 | dolphinDosBamCheckBox.setChecked( 227 | opts.flags & CbmdosFsFlags::CFF_DOLPHINDOSBAM); 228 | speedDosBamCheckBox.setChecked( 229 | opts.flags & CbmdosFsFlags::CFF_SPEEDDOSBAM); 230 | prologicDosBamCheckBox.setChecked( 231 | opts.flags & CbmdosFsFlags::CFF_PROLOGICDOSBAM); 232 | zeroFreeCheckBox.setChecked( 233 | opts.flags & CbmdosFsFlags::CFF_ZEROFREE); 234 | dirInterleaveSpinBox.setValue(opts.dirInterleave); 235 | fileInterleaveSpinBox.setValue(opts.fileInterleave); 236 | if (opts.flags & CbmdosFsFlags::CFF_TALLOC_TRACKLOAD) 237 | { 238 | allocTrackloaderButton.setChecked(true); 239 | } 240 | else if (opts.flags & CbmdosFsFlags::CFF_TALLOC_SIMPLE) 241 | { 242 | allocSimpleButton.setChecked(true); 243 | } 244 | else 245 | { 246 | allocOriginalButton.setChecked(true); 247 | } 248 | simpleInterleaveCheckBox.setChecked( 249 | opts.flags & CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 250 | chainInterlvCheckBox.setChecked( 251 | opts.flags & CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 252 | } 253 | 254 | void CbmdosFsOptionsDialog::priv::clicked(QObject *sender, bool checked) 255 | { 256 | if (sender == &tracks35Button) 257 | { 258 | opts.flags = (CbmdosFsFlags) (opts.flags & ~( 259 | CbmdosFsFlags::CFF_40TRACK | 260 | CbmdosFsFlags::CFF_42TRACK | 261 | CbmdosFsFlags::CFF_DOLPHINDOSBAM | 262 | CbmdosFsFlags::CFF_SPEEDDOSBAM | 263 | CbmdosFsFlags::CFF_PROLOGICDOSBAM 264 | )); 265 | dolphinDosBamCheckBox.setChecked(false); 266 | dolphinDosBamCheckBox.setEnabled(false); 267 | speedDosBamCheckBox.setChecked(false); 268 | speedDosBamCheckBox.setEnabled(false); 269 | prologicDosBamCheckBox.setChecked(false); 270 | prologicDosBamCheckBox.setEnabled(false); 271 | 272 | } 273 | else if (sender == &tracks40Button) 274 | { 275 | opts.flags = (CbmdosFsFlags) 276 | (opts.flags & ~CbmdosFsFlags::CFF_42TRACK); 277 | opts.flags = (CbmdosFsFlags) 278 | (opts.flags | CbmdosFsFlags::CFF_40TRACK); 279 | dolphinDosBamCheckBox.setEnabled(true); 280 | speedDosBamCheckBox.setEnabled(true); 281 | prologicDosBamCheckBox.setEnabled(true); 282 | } 283 | else if (sender == &tracks42Button) 284 | { 285 | opts.flags = (CbmdosFsFlags) 286 | (opts.flags & ~CbmdosFsFlags::CFF_40TRACK); 287 | opts.flags = (CbmdosFsFlags) 288 | (opts.flags | CbmdosFsFlags::CFF_42TRACK); 289 | dolphinDosBamCheckBox.setEnabled(true); 290 | speedDosBamCheckBox.setEnabled(true); 291 | prologicDosBamCheckBox.setEnabled(true); 292 | } 293 | else if (sender == &dolphinDosBamCheckBox) 294 | { 295 | if (checked) 296 | { 297 | opts.flags = (CbmdosFsFlags) 298 | (opts.flags & ~CbmdosFsFlags::CFF_PROLOGICDOSBAM); 299 | opts.flags = (CbmdosFsFlags) 300 | (opts.flags | CbmdosFsFlags::CFF_DOLPHINDOSBAM); 301 | prologicDosBamCheckBox.setChecked(false); 302 | } 303 | else 304 | { 305 | opts.flags = (CbmdosFsFlags) 306 | (opts.flags & ~CbmdosFsFlags::CFF_DOLPHINDOSBAM); 307 | } 308 | } 309 | else if (sender == &speedDosBamCheckBox) 310 | { 311 | if (checked) 312 | { 313 | opts.flags = (CbmdosFsFlags) 314 | (opts.flags & ~CbmdosFsFlags::CFF_PROLOGICDOSBAM); 315 | opts.flags = (CbmdosFsFlags) 316 | (opts.flags | CbmdosFsFlags::CFF_SPEEDDOSBAM); 317 | prologicDosBamCheckBox.setChecked(false); 318 | } 319 | else 320 | { 321 | opts.flags = (CbmdosFsFlags) 322 | (opts.flags & ~CbmdosFsFlags::CFF_SPEEDDOSBAM); 323 | } 324 | } 325 | else if (sender == &prologicDosBamCheckBox) 326 | { 327 | if (checked) 328 | { 329 | opts.flags = (CbmdosFsFlags) (opts.flags & ~( 330 | CbmdosFsFlags::CFF_SPEEDDOSBAM | 331 | CbmdosFsFlags::CFF_DOLPHINDOSBAM 332 | )); 333 | opts.flags = (CbmdosFsFlags) 334 | (opts.flags | CbmdosFsFlags::CFF_PROLOGICDOSBAM); 335 | speedDosBamCheckBox.setChecked(false); 336 | dolphinDosBamCheckBox.setChecked(false); 337 | } 338 | else 339 | { 340 | opts.flags = (CbmdosFsFlags) 341 | (opts.flags & ~CbmdosFsFlags::CFF_PROLOGICDOSBAM); 342 | } 343 | } 344 | else if (sender == &allowLongDirCheckBox) 345 | { 346 | if (checked) 347 | { 348 | opts.flags = (CbmdosFsFlags) 349 | (opts.flags | CbmdosFsFlags::CFF_ALLOWLONGDIR); 350 | } 351 | else 352 | { 353 | opts.flags = (CbmdosFsFlags) 354 | (opts.flags & ~CbmdosFsFlags::CFF_ALLOWLONGDIR); 355 | } 356 | } 357 | else if (sender == &filesOnDirTrackCheckBox) 358 | { 359 | if (checked) 360 | { 361 | opts.flags = (CbmdosFsFlags) 362 | (opts.flags | CbmdosFsFlags::CFF_FILESONDIRTRACK); 363 | prefDirTrackCheckBox.setEnabled(true); 364 | } 365 | else 366 | { 367 | opts.flags = (CbmdosFsFlags) 368 | (opts.flags & ~CbmdosFsFlags::CFF_FILESONDIRTRACK); 369 | prefDirTrackCheckBox.setEnabled(false); 370 | } 371 | } 372 | else if (sender == &zeroFreeCheckBox) 373 | { 374 | if (checked) 375 | { 376 | opts.flags = (CbmdosFsFlags) 377 | (opts.flags | CbmdosFsFlags::CFF_ZEROFREE); 378 | } 379 | else 380 | { 381 | opts.flags = (CbmdosFsFlags) 382 | (opts.flags & ~CbmdosFsFlags::CFF_ZEROFREE); 383 | } 384 | } 385 | else if (sender == &allocOriginalButton) 386 | { 387 | opts.flags = (CbmdosFsFlags) (opts.flags & ~( 388 | CbmdosFsFlags::CFF_TALLOC_TRACKLOAD | 389 | CbmdosFsFlags::CFF_TALLOC_SIMPLE 390 | )); 391 | } 392 | else if (sender == &allocTrackloaderButton) 393 | { 394 | opts.flags = (CbmdosFsFlags) 395 | (opts.flags & ~CbmdosFsFlags::CFF_TALLOC_SIMPLE); 396 | opts.flags = (CbmdosFsFlags) 397 | (opts.flags | CbmdosFsFlags::CFF_TALLOC_TRACKLOAD); 398 | } 399 | else if (sender == &allocSimpleButton) 400 | { 401 | opts.flags = (CbmdosFsFlags) 402 | (opts.flags & ~CbmdosFsFlags::CFF_TALLOC_TRACKLOAD); 403 | opts.flags = (CbmdosFsFlags) 404 | (opts.flags | CbmdosFsFlags::CFF_TALLOC_SIMPLE); 405 | } 406 | else if (sender == &simpleInterleaveCheckBox) 407 | { 408 | if (checked) 409 | { 410 | opts.flags = (CbmdosFsFlags) 411 | (opts.flags | CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 412 | } 413 | else 414 | { 415 | opts.flags = (CbmdosFsFlags) 416 | (opts.flags & ~CbmdosFsFlags::CFF_SIMPLEINTERLEAVE); 417 | } 418 | } 419 | else if (sender == &prefDirTrackCheckBox) 420 | { 421 | if (checked) 422 | { 423 | opts.flags = (CbmdosFsFlags) 424 | (opts.flags | CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 425 | } 426 | else 427 | { 428 | opts.flags = (CbmdosFsFlags) 429 | (opts.flags & ~CbmdosFsFlags::CFF_TALLOC_PREFDIRTRACK); 430 | } 431 | } 432 | else if (sender == &chainInterlvCheckBox) 433 | { 434 | if (checked) 435 | { 436 | opts.flags = (CbmdosFsFlags) 437 | (opts.flags | CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 438 | } 439 | else 440 | { 441 | opts.flags = (CbmdosFsFlags) 442 | (opts.flags & ~CbmdosFsFlags::CFF_TALLOC_CHAININTERLV); 443 | } 444 | } 445 | } 446 | 447 | void CbmdosFsOptionsDialog::priv::changed(QObject *sender, int value) 448 | { 449 | if (sender == &dirInterleaveSpinBox) 450 | { 451 | opts.dirInterleave = value; 452 | } 453 | else if (sender == &fileInterleaveSpinBox) 454 | { 455 | opts.fileInterleave = value; 456 | } 457 | } 458 | 459 | CbmdosFsOptionsDialog::CbmdosFsOptionsDialog(CbmdosFsOptions *options, 460 | QWidget *parent, bool canCancel) : 461 | QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | 462 | Qt::CustomizeWindowHint | Qt::MSWindowsFixedSizeDialogHint) 463 | { 464 | d = new priv(options, canCancel); 465 | d->optionsLayout.addWidget(&d->tracks35Button, 0, 0, 1, 2); 466 | d->optionsLayout.addWidget(&d->tracks40Button, 1, 0, 1, 2); 467 | d->optionsLayout.addWidget(&d->tracks42Button, 2, 0, 1, 2); 468 | d->optionsLayout.addWidget(&d->dirInterleaveLabel, 4, 0); 469 | d->optionsLayout.addWidget(&d->dirInterleaveSpinBox, 4, 1); 470 | d->optionsLayout.addWidget(&d->fileInterleaveLabel, 5, 0); 471 | d->optionsLayout.addWidget(&d->fileInterleaveSpinBox, 5, 1); 472 | d->optionsLayout.addWidget(&d->dolphinDosBamCheckBox, 0, 2); 473 | d->optionsLayout.addWidget(&d->speedDosBamCheckBox, 1, 2); 474 | d->optionsLayout.addWidget(&d->prologicDosBamCheckBox, 2, 2); 475 | d->optionsLayout.addWidget(&d->allowLongDirCheckBox, 3, 2); 476 | d->optionsLayout.addWidget(&d->filesOnDirTrackCheckBox, 4, 2); 477 | d->optionsLayout.addWidget(&d->zeroFreeCheckBox, 5, 2); 478 | d->allocationLayout.addWidget(&d->allocOriginalButton, 0, 0); 479 | d->allocationLayout.addWidget(&d->allocTrackloaderButton, 1, 0); 480 | d->allocationLayout.addWidget(&d->allocSimpleButton, 2, 0); 481 | d->allocationLayout.addWidget(&d->simpleInterleaveCheckBox, 0, 1); 482 | d->allocationLayout.addWidget(&d->prefDirTrackCheckBox, 1, 1); 483 | d->allocationLayout.addWidget(&d->chainInterlvCheckBox, 2, 1); 484 | d->allocationOptions.setLayout(&d->allocationLayout); 485 | d->optionsLayout.addWidget(&d->allocationOptions, 6, 0, 1, 3); 486 | if (options->flags & CbmdosFsFlags::CFF_RECOVER) 487 | { 488 | d->optionsLayout.addWidget(&d->recoverWarningLabel, 7, 0, 1, 3); 489 | d->rewriteAfterRecover.setChecked(true); 490 | d->optionsLayout.addWidget(&d->rewriteAfterRecover, 8, 0, 1, 3); 491 | } 492 | d->mainLayout.addLayout(&d->optionsLayout); 493 | d->mainLayout.addWidget(&d->buttons); 494 | setLayout(&d->mainLayout); 495 | 496 | connect(&d->buttons, &QDialogButtonBox::accepted, 497 | this, &CbmdosFsOptionsDialog::accept); 498 | connect(&d->buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); 499 | connect(&d->buttons, &QDialogButtonBox::clicked, this, 500 | &CbmdosFsOptionsDialog::buttonPressed); 501 | 502 | connect(&d->tracks35Button, &QAbstractButton::clicked, 503 | this, &CbmdosFsOptionsDialog::optionClicked); 504 | connect(&d->tracks40Button, &QAbstractButton::clicked, 505 | this, &CbmdosFsOptionsDialog::optionClicked); 506 | connect(&d->tracks42Button, &QAbstractButton::clicked, 507 | this, &CbmdosFsOptionsDialog::optionClicked); 508 | connect(&d->dolphinDosBamCheckBox, &QAbstractButton::clicked, 509 | this, &CbmdosFsOptionsDialog::optionClicked); 510 | connect(&d->speedDosBamCheckBox, &QAbstractButton::clicked, 511 | this, &CbmdosFsOptionsDialog::optionClicked); 512 | connect(&d->prologicDosBamCheckBox, &QAbstractButton::clicked, 513 | this, &CbmdosFsOptionsDialog::optionClicked); 514 | connect(&d->allowLongDirCheckBox, &QAbstractButton::clicked, 515 | this, &CbmdosFsOptionsDialog::optionClicked); 516 | connect(&d->filesOnDirTrackCheckBox, &QAbstractButton::clicked, 517 | this, &CbmdosFsOptionsDialog::optionClicked); 518 | connect(&d->zeroFreeCheckBox, &QAbstractButton::clicked, 519 | this, &CbmdosFsOptionsDialog::optionClicked); 520 | connect(&d->allocOriginalButton, &QAbstractButton::clicked, 521 | this, &CbmdosFsOptionsDialog::optionClicked); 522 | connect(&d->allocTrackloaderButton, &QAbstractButton::clicked, 523 | this, &CbmdosFsOptionsDialog::optionClicked); 524 | connect(&d->allocSimpleButton, &QAbstractButton::clicked, 525 | this, &CbmdosFsOptionsDialog::optionClicked); 526 | connect(&d->simpleInterleaveCheckBox, &QAbstractButton::clicked, 527 | this, &CbmdosFsOptionsDialog::optionClicked); 528 | connect(&d->prefDirTrackCheckBox, &QAbstractButton::clicked, 529 | this, &CbmdosFsOptionsDialog::optionClicked); 530 | connect(&d->chainInterlvCheckBox, &QAbstractButton::clicked, 531 | this, &CbmdosFsOptionsDialog::optionClicked); 532 | 533 | connect(&d->dirInterleaveSpinBox, SIGNAL(valueChanged(int)), 534 | this, SLOT(valueChanged(int))); 535 | connect(&d->fileInterleaveSpinBox, SIGNAL(valueChanged(int)), 536 | this, SLOT(valueChanged(int))); 537 | } 538 | 539 | CbmdosFsOptionsDialog::~CbmdosFsOptionsDialog() 540 | { 541 | delete d; 542 | } 543 | 544 | void CbmdosFsOptionsDialog::accept() 545 | { 546 | memcpy(d->options, &d->opts, sizeof d->opts); 547 | QDialog::accept(); 548 | } 549 | 550 | void CbmdosFsOptionsDialog::buttonPressed(QAbstractButton *button) 551 | { 552 | if (d->buttons.buttonRole(button) == QDialogButtonBox::ResetRole) 553 | { 554 | d->reset(); 555 | } 556 | } 557 | 558 | void CbmdosFsOptionsDialog::optionClicked(bool checked) 559 | { 560 | d->clicked(sender(), checked); 561 | } 562 | 563 | void CbmdosFsOptionsDialog::valueChanged(int i) 564 | { 565 | d->changed(sender(), i); 566 | } 567 | 568 | void CbmdosFsOptionsDialog::disable35Tracks() 569 | { 570 | d->tracks35Button.setEnabled(false); 571 | } 572 | 573 | void CbmdosFsOptionsDialog::disable40Tracks() 574 | { 575 | d->tracks40Button.setEnabled(false); 576 | } 577 | 578 | void CbmdosFsOptionsDialog::disable42Tracks() 579 | { 580 | d->tracks42Button.setEnabled(false); 581 | } 582 | 583 | void CbmdosFsOptionsDialog::disableZeroFree() 584 | { 585 | d->zeroFreeCheckBox.setEnabled(false); 586 | } 587 | 588 | void CbmdosFsOptionsDialog::reset() 589 | { 590 | d->reset(); 591 | } 592 | 593 | bool CbmdosFsOptionsDialog::wantRewrite() 594 | { 595 | return d->rewriteAfterRecover.isChecked(); 596 | } 597 | 598 | void CbmdosFsOptionsDialog::showEvent(QShowEvent *event) 599 | { 600 | QCoreApplication::processEvents(); 601 | QDialog::showEvent(event); 602 | adjustSize(); 603 | setFixedSize(size()); 604 | QCoreApplication::processEvents(); 605 | 606 | QRect dlgRect = frameGeometry(); 607 | QRect mainWinRect = parentWidget()->window()->frameGeometry(); 608 | dlgRect.moveCenter(mainWinRect.center()); 609 | 610 | const QScreen *screen = 0; 611 | const QWindow *currentWin = parentWidget()->windowHandle(); 612 | if (currentWin) 613 | { 614 | screen = currentWin->screen(); 615 | } 616 | if (screen) 617 | { 618 | QRect screenRect = screen->availableGeometry(); 619 | if (dlgRect.right() > screenRect.right()) 620 | { 621 | dlgRect.moveRight(screenRect.right()); 622 | } 623 | if (dlgRect.bottom() > screenRect.bottom()) 624 | { 625 | dlgRect.moveBottom(screenRect.bottom()); 626 | } 627 | if (dlgRect.top() < screenRect.top()) 628 | { 629 | dlgRect.moveTop(screenRect.top()); 630 | } 631 | if (dlgRect.left() < screenRect.left()) 632 | { 633 | dlgRect.moveLeft(screenRect.left()); 634 | } 635 | } 636 | move(dlgRect.topLeft()); 637 | } 638 | 639 | --------------------------------------------------------------------------------