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 |
--------------------------------------------------------------------------------