File indexing completed on 2024-04-21 14:44:03

0001 /*
0002     SPDX-FileCopyrightText: 2012 Rishab Arora <ra.rishab@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "ksuserdb.h"
0008 
0009 #include "artificialhorizoncomponent.h"
0010 #include "kspaths.h"
0011 #include "kstarsdata.h"
0012 #include "linelist.h"
0013 #include "version.h"
0014 #include "oal/dslrlens.h"
0015 #include "imageoverlaycomponent.h"
0016 
0017 #include <QSqlQuery>
0018 #include <QSqlRecord>
0019 #include <QSqlTableModel>
0020 
0021 #include <QJsonDocument>
0022 
0023 #include <kstars_debug.h>
0024 
0025 /*
0026  * TODO (spacetime):
0027  * The database supports storing logs. But it needs to be implemented.
0028  *
0029  * One of the unresolved problems was the creation of a unique identifier
0030  * for each object (DSO,planet,star etc) for use in the database.
0031 */
0032 
0033 KSUserDB::~KSUserDB()
0034 {
0035     // Backup
0036     QString current_dbfile = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("userdb.sqlite");
0037     QString backup_dbfile = QDir(KSPaths::writableLocation(
0038                                      QStandardPaths::AppLocalDataLocation)).filePath("userdb.sqlite.backup");
0039     QFile::remove(backup_dbfile);
0040     QFile::copy(current_dbfile, backup_dbfile);
0041 }
0042 
0043 bool KSUserDB::Initialize()
0044 {
0045     // If the database file does not exist, look for a backup
0046     // If the database file exists and is empty, look for a backup
0047     // If the database file exists and has data, use it.
0048     // If the backup file does not exist, start fresh.
0049     // If the backup file exists and has data, replace and use it.
0050     // If the database file exists and has no data and the backup file exists, use it.
0051     // If the database file exists and has no data and no backup file exists, start fresh.
0052 
0053     QFileInfo dbfile(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("userdb.sqlite"));
0054     QFileInfo backup_file(QDir(KSPaths::writableLocation(
0055                                    QStandardPaths::AppLocalDataLocation)).filePath("userdb.sqlite.backup"));
0056 
0057     bool const first_run = !dbfile.exists() && !backup_file.exists();
0058     m_ConnectionName = dbfile.filePath();
0059 
0060     // Every logged in user has their own db.
0061     auto db = QSqlDatabase::addDatabase("QSQLITE", m_ConnectionName);
0062     // This would load the SQLITE file
0063     db.setDatabaseName(m_ConnectionName);
0064 
0065     if (!db.isValid())
0066     {
0067         qCCritical(KSTARS) << "Unable to prepare database of type sqlite!";
0068         return false;
0069     }
0070 
0071     // If main fails to open and we have no backup, fail
0072     if (!db.open())
0073     {
0074         if (!backup_file.exists())
0075         {
0076             qCCritical(KSTARS) << QString("Failed opening user database '%1'.").arg(dbfile.filePath());
0077             qCCritical(KSTARS) << db.lastError();
0078             return false;
0079         }
0080     }
0081 
0082     // If no main nor backup existed before opening, rebuild
0083     if (db.isOpen() && first_run)
0084     {
0085         qCInfo(KSTARS) << "User DB does not exist. New User DB will be created.";
0086         FirstRun();
0087     }
0088 
0089     // If main appears empty/corrupted, restore if possible or rebuild
0090     if (db.tables().empty())
0091     {
0092         if (backup_file.exists())
0093         {
0094 
0095             qCWarning(KSTARS) << "Detected corrupted database. Attempting to recover from backup...";
0096             QFile::remove(dbfile.filePath());
0097             QFile::copy(backup_file.filePath(), dbfile.filePath());
0098             QFile::remove(backup_file.filePath());
0099             return Initialize();
0100         }
0101         else if (!FirstRun())
0102         {
0103             qCCritical(KSTARS) << QString("Failed initializing user database '%1.").arg(dbfile.filePath());
0104             return false;
0105         }
0106     }
0107 
0108     qCDebug(KSTARS) << "Opened the User DB. Ready.";
0109 
0110     // Update table if previous version exists
0111     QSqlTableModel version(nullptr, db);
0112     version.setTable("Version");
0113     version.select();
0114     QSqlRecord record = version.record(0);
0115     version.clear();
0116 
0117     // Old version had 2.9.5 ..etc, so we remove them
0118     // Starting with 2.9.7, we are using SCHEMA_VERSION which now decoupled from KStars Version and starts at 300
0119     int currentDBVersion = record.value("Version").toString().remove(".").toInt();
0120 
0121     // Update database version to current KStars version
0122     if (currentDBVersion != SCHEMA_VERSION)
0123     {
0124         QSqlQuery query(db);
0125         QString versionQuery = QString("UPDATE Version SET Version=%1").arg(SCHEMA_VERSION);
0126         if (!query.exec(versionQuery))
0127             qCWarning(KSTARS) << query.lastError();
0128     }
0129 
0130     // If prior to 2.9.4 extend filters table
0131     if (currentDBVersion < 294)
0132     {
0133         QSqlQuery query(db);
0134 
0135         qCWarning(KSTARS) << "Detected old format filter table, re-creating...";
0136         if (!query.exec("DROP table filter"))
0137             qCWarning(KSTARS) << query.lastError();
0138         if (!query.exec("CREATE TABLE filter ( "
0139                         "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0140                         "Vendor TEXT DEFAULT NULL, "
0141                         "Model TEXT DEFAULT NULL, "
0142                         "Type TEXT DEFAULT NULL, "
0143                         "Color TEXT DEFAULT NULL,"
0144                         "Exposure REAL DEFAULT 1.0,"
0145                         "Offset INTEGER DEFAULT 0,"
0146                         "UseAutoFocus INTEGER DEFAULT 0,"
0147                         "LockedFilter TEXT DEFAULT '--',"
0148                         "AbsoluteFocusPosition INTEGER DEFAULT 0)"))
0149             qCWarning(KSTARS) << query.lastError();
0150     }
0151 
0152     // If prior to 2.9.5 create fov table
0153     if (currentDBVersion < 295)
0154     {
0155         QSqlQuery query(db);
0156 
0157         if (!query.exec("CREATE TABLE effectivefov ( "
0158                         "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0159                         "Profile TEXT DEFAULT NULL, "
0160                         "Width INTEGER DEFAULT NULL, "
0161                         "Height INTEGER DEFAULT NULL, "
0162                         "PixelW REAL DEFAULT 5.0,"
0163                         "PixelH REAL DEFAULT 5.0,"
0164                         "FocalLength REAL DEFAULT 0.0,"
0165                         "FovW REAL DEFAULT 0.0,"
0166                         "FovH REAL DEFAULT 0.0)"))
0167             qCWarning(KSTARS) << query.lastError();
0168     }
0169 
0170     if (currentDBVersion < 300)
0171     {
0172         QSqlQuery query(db);
0173         QString columnQuery = QString("ALTER TABLE profile ADD COLUMN remotedrivers TEXT DEFAULT NULL");
0174         if (!query.exec(columnQuery))
0175             qCWarning(KSTARS) << query.lastError();
0176 
0177         if (db.tables().contains("customdrivers") == false)
0178         {
0179             if (!query.exec("CREATE TABLE customdrivers ( "
0180                             "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0181                             "Name TEXT DEFAULT NULL, "
0182                             "Label TEXT DEFAULT NULL UNIQUE, "
0183                             "Manufacturer TEXT DEFAULT NULL, "
0184                             "Family TEXT DEFAULT NULL, "
0185                             "Exec TEXT DEFAULT NULL, "
0186                             "Version TEXT DEFAULT 1.0)"))
0187                 qCWarning(KSTARS) << query.lastError();
0188         }
0189     }
0190 
0191     // Add manufacturer
0192     if (currentDBVersion < 305)
0193     {
0194         QSqlQuery query(db);
0195         QString columnQuery = QString("ALTER TABLE customdrivers ADD COLUMN Manufacturer TEXT DEFAULT NULL");
0196         if (!query.exec(columnQuery))
0197             qCWarning(KSTARS) << query.lastError();
0198     }
0199 
0200     // Add indihub
0201     if (currentDBVersion < 306)
0202     {
0203         QSqlQuery query(db);
0204         QString columnQuery = QString("ALTER TABLE profile ADD COLUMN indihub INTEGER DEFAULT 0");
0205         if (!query.exec(columnQuery))
0206             qCWarning(KSTARS) << query.lastError();
0207     }
0208 
0209     // Add Defect Map
0210     if (currentDBVersion < 307)
0211     {
0212         QSqlQuery query(db);
0213         // If we are upgrading, remove all previous entries.
0214         QString clearQuery = QString("DELETE FROM darkframe");
0215         if (!query.exec(clearQuery))
0216             qCWarning(KSTARS) << query.lastError();
0217         QString columnQuery = QString("ALTER TABLE darkframe ADD COLUMN defectmap TEXT DEFAULT NULL");
0218         if (!query.exec(columnQuery))
0219             qCWarning(KSTARS) << query.lastError();
0220     }
0221 
0222     // Add port selector
0223     if (currentDBVersion < 308)
0224     {
0225         QSqlQuery query(db);
0226         QString columnQuery = QString("ALTER TABLE profile ADD COLUMN portselector INTEGER DEFAULT 0");
0227         if (!query.exec(columnQuery))
0228             qCWarning(KSTARS) << query.lastError();
0229     }
0230 
0231     // Add Gain/ISO to Dark Library
0232     if (currentDBVersion < 309)
0233     {
0234         QSqlQuery query(db);
0235         QString columnQuery = QString("ALTER TABLE darkframe ADD COLUMN gain INTEGER DEFAULT -1");
0236         if (!query.exec(columnQuery))
0237             qCWarning(KSTARS) << query.lastError();
0238         columnQuery = QString("ALTER TABLE darkframe ADD COLUMN iso TEXT DEFAULT NULL");
0239         if (!query.exec(columnQuery))
0240             qCWarning(KSTARS) << query.lastError();
0241     }
0242 
0243     // Add scripts to profile
0244     if (currentDBVersion < 310)
0245     {
0246         QSqlQuery query(db);
0247         QString columnQuery = QString("ALTER TABLE profile ADD COLUMN scripts TEXT DEFAULT NULL");
0248         if (!query.exec(columnQuery))
0249             qCWarning(KSTARS) << query.lastError();
0250     }
0251 
0252     // Add optical trains
0253     if (currentDBVersion < 311)
0254     {
0255         QSqlQuery query(db);
0256         if (!query.exec("CREATE TABLE opticaltrains ( "
0257                         "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0258                         "profile INTEGER DEFAULT NULL, "
0259                         "name TEXT DEFAULT NULL, "
0260                         "mount TEXT DEFAULT NULL, "
0261                         "dustcap TEXT DEFAULT NULL, "
0262                         "lightbox TEXT DEFAULT NULL, "
0263                         "scope TEXT DEFAULT NULL, "
0264                         "reducer REAL DEFAULT 1, "
0265                         "rotator TEXT DEFAULT NULL, "
0266                         "focuser TEXT DEFAULT NULL, "
0267                         "filterwheel TEXT DEFAULT NULL, "
0268                         "camera TEXT DEFAULT NULL, "
0269                         "guider TEXT DEFAULT NULL)"))
0270             qCWarning(KSTARS) << query.lastError();
0271 
0272         if (!query.exec("CREATE TABLE profilesettings ( "
0273                         "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0274                         "profile INTEGER DEFAULT NULL, "
0275                         "settings TEXT DEFAULT NULL)"))
0276             qCWarning(KSTARS) << query.lastError();
0277 
0278         if (!query.exec("CREATE TABLE opticaltrainsettings ( "
0279                         "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0280                         "opticaltrain INTEGER DEFAULT NULL, "
0281                         "settings TEXT DEFAULT NULL)"))
0282             qCWarning(KSTARS) << query.lastError();
0283 
0284 
0285         // Add DSLR lenses table
0286         if (!query.exec("CREATE TABLE dslrlens ( "
0287                         "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0288                         "Vendor TEXT DEFAULT NULL, "
0289                         "Model TEXT DEFAULT NULL, "
0290                         "FocalLength REAL DEFAULT NULL, "
0291                         "FocalRatio REAL DEFAULT NULL)"))
0292             qCWarning(KSTARS) << query.lastError();
0293 
0294         // Need to offset primary key by 100,000 to differential it from scopes and keep it backward compatible.
0295         if (!query.exec("UPDATE SQLITE_SEQUENCE SET seq = 100000 WHERE name ='dslrlens'"))
0296             qCWarning(KSTARS) << query.lastError();
0297     }
0298 
0299     // Adjust effective FOV
0300     if (currentDBVersion < 312)
0301     {
0302         QSqlQuery query(db);
0303 
0304         if (!query.exec("ALTER TABLE effectivefov ADD COLUMN Train TEXT DEFAULT NULL"))
0305             qCWarning(KSTARS) << query.lastError();
0306         if (!query.exec("ALTER TABLE effectivefov ADD COLUMN FocalReducer REAL DEFAULT 0.0"))
0307             qCWarning(KSTARS) << query.lastError();
0308         if (!query.exec("ALTER TABLE effectivefov ADD COLUMN FocalRatio REAL DEFAULT 0.0"))
0309             qCWarning(KSTARS) << query.lastError();
0310     }
0311 
0312     // Add focusTemperature, focusAltitude, focusTicksPerTemp, focusTicksPerAlt and wavelength to filter table
0313     if (currentDBVersion < 313)
0314     {
0315         QSqlQuery query(db);
0316 
0317         if (!query.exec("ALTER TABLE filter ADD COLUMN FocusTemperature REAL DEFAULT NULL"))
0318             qCWarning(KSTARS) << query.lastError();
0319         if (!query.exec("ALTER TABLE filter ADD COLUMN FocusAltitude REAL DEFAULT NULL"))
0320             qCWarning(KSTARS) << query.lastError();
0321         if (!query.exec("ALTER TABLE filter ADD COLUMN FocusTicksPerTemp REAL DEFAULT 0.0"))
0322             qCWarning(KSTARS) << query.lastError();
0323         if (!query.exec("ALTER TABLE filter ADD COLUMN FocusTicksPerAlt REAL DEFAULT 0.0"))
0324             qCWarning(KSTARS) << query.lastError();
0325         if (!query.exec("ALTER TABLE filter ADD COLUMN Wavelength REAL DEFAULT 500.0"))
0326             qCWarning(KSTARS) << query.lastError();
0327     }
0328 
0329     // Add collimationoverlayelements table
0330     if (currentDBVersion < 314)
0331     {
0332         QSqlQuery query(db);
0333 
0334         if (!query.exec("CREATE TABLE collimationoverlayelements ( "
0335                         "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0336                         "Name TEXT DEFAULT NULL, "
0337                         "Enabled INTEGER DEFAULT 0, "
0338                         "Type INTEGER DEFAULT NULL, "
0339                         "SizeX INTEGER DEFAULT NULL, "
0340                         "SizeY INTEGER DEFAULT NULL, "
0341                         "OffsetX INTEGER DEFAULT NULL, "
0342                         "OffsetY INTEGER DEFAULT NULL, "
0343                         "Count INTEGER DEFAULT 1, "
0344                         "PCD INTEGER DEFAULT 100, "
0345                         "Rotation REAL DEFAULT 0.0, "
0346                         "Colour TEXT DEFAULT NULL, "
0347                         "Thickness INTEGER DEFAULT 1)"))
0348             qCWarning(KSTARS) << query.lastError();
0349     }
0350     return true;
0351 }
0352 
0353 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0354 ///
0355 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0356 bool KSUserDB::FirstRun()
0357 {
0358     if (!RebuildDB())
0359         return false;
0360     return true;
0361 }
0362 
0363 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0364 ///
0365 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0366 bool KSUserDB::RebuildDB()
0367 {
0368     auto db = QSqlDatabase::database(m_ConnectionName);
0369     qCInfo(KSTARS) << "Rebuilding User Database";
0370 
0371     QVector<QString> tables;
0372     tables.append("CREATE TABLE Version ("
0373                   "Version CHAR DEFAULT NULL)");
0374     tables.append("INSERT INTO Version VALUES (\"" KSTARS_VERSION "\")");
0375     tables.append("CREATE TABLE user ( "
0376                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0377                   "Name TEXT NOT NULL  DEFAULT 'NULL', "
0378                   "Surname TEXT NOT NULL  DEFAULT 'NULL', "
0379                   "Contact TEXT DEFAULT NULL)");
0380 
0381     tables.append("CREATE TABLE telescope ( "
0382                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0383                   "Vendor TEXT DEFAULT NULL, "
0384                   "Aperture REAL NOT NULL DEFAULT NULL, "
0385                   "Model TEXT DEFAULT NULL, "
0386                   "Type TEXT DEFAULT NULL, "
0387                   "FocalLength REAL DEFAULT NULL)");
0388 
0389     tables.append("INSERT INTO telescope (Vendor, Aperture, Model, Type, FocalLength) VALUES "
0390                   "('Sample', 120, 'Primary', 'Refractor', 700)");
0391 
0392     tables.append("INSERT INTO telescope (Vendor, Aperture, Model, Type, FocalLength) VALUES "
0393                   "('Sample', 50, 'Guide', 'Refractor', 300)");
0394 
0395     tables.append("CREATE TABLE flags ( "
0396                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0397                   "RA TEXT NOT NULL  DEFAULT NULL, "
0398                   "Dec TEXT NOT NULL  DEFAULT NULL, "
0399                   "Icon TEXT NOT NULL  DEFAULT 'NULL', "
0400                   "Label TEXT NOT NULL  DEFAULT 'NULL', "
0401                   "Color TEXT DEFAULT NULL, "
0402                   "Epoch TEXT DEFAULT NULL)");
0403 
0404     tables.append("CREATE TABLE lens ( "
0405                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0406                   "Vendor TEXT NOT NULL  DEFAULT 'NULL', "
0407                   "Model TEXT DEFAULT NULL, "
0408                   "Factor REAL NOT NULL  DEFAULT NULL)");
0409 
0410     tables.append("CREATE TABLE dslrlens ( "
0411                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0412                   "Vendor TEXT DEFAULT NULL, "
0413                   "Model TEXT DEFAULT NULL, "
0414                   "FocalLength REAL DEFAULT NULL, "
0415                   "FocalRatio REAL DEFAULT NULL)");
0416 
0417     tables.append("CREATE TABLE eyepiece ( "
0418                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0419                   "Vendor TEXT DEFAULT NULL, "
0420                   "Model TEXT DEFAULT NULL, "
0421                   "FocalLength REAL NOT NULL  DEFAULT NULL, "
0422                   "ApparentFOV REAL NOT NULL  DEFAULT NULL, "
0423                   "FOVUnit TEXT NOT NULL  DEFAULT NULL)");
0424 
0425     tables.append("CREATE TABLE filter ( "
0426                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0427                   "Vendor TEXT DEFAULT NULL, "
0428                   "Model TEXT DEFAULT NULL, "
0429                   "Type TEXT DEFAULT NULL, "
0430                   "Color TEXT DEFAULT NULL,"
0431                   "Exposure REAL DEFAULT 1.0,"
0432                   "Offset INTEGER DEFAULT 0,"
0433                   "UseAutoFocus INTEGER DEFAULT 0,"
0434                   "LockedFilter TEXT DEFAULT '--',"
0435                   "AbsoluteFocusPosition INTEGER DEFAULT 0,"
0436                   "FocusTemperature REAL DEFAULT NULL,"
0437                   "FocusAltitude REAL DEFAULT NULL,"
0438                   "FocusTicksPerTemp REAL DEFAULT 0.0,"
0439                   "FocusTicksPerAlt REAL DEFAULT 0.0,"
0440                   "Wavelength INTEGER DEFAULT 500)");
0441 
0442     tables.append("CREATE TABLE wishlist ( "
0443                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0444                   "Date NUMERIC NOT NULL  DEFAULT NULL, "
0445                   "Type TEXT DEFAULT NULL, "
0446                   "UIUD TEXT DEFAULT NULL)");
0447 
0448     tables.append("CREATE TABLE fov ( "
0449                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0450                   "name TEXT NOT NULL  DEFAULT 'NULL', "
0451                   "color TEXT DEFAULT NULL, "
0452                   "sizeX NUMERIC DEFAULT NULL, "
0453                   "sizeY NUMERIC DEFAULT NULL, "
0454                   "shape TEXT DEFAULT NULL)");
0455 
0456     tables.append("CREATE TABLE logentry ( "
0457                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0458                   "content TEXT NOT NULL  DEFAULT 'NULL', "
0459                   "UIUD TEXT DEFAULT NULL, "
0460                   "DateTime NUMERIC NOT NULL  DEFAULT NULL, "
0461                   "User INTEGER DEFAULT NULL REFERENCES user (id), "
0462                   "Location TEXT DEFAULT NULL, "
0463                   "Telescope INTEGER DEFAULT NULL REFERENCES telescope (id),"
0464                   "Filter INTEGER DEFAULT NULL REFERENCES filter (id), "
0465                   "lens INTEGER DEFAULT NULL REFERENCES lens (id), "
0466                   "Eyepiece INTEGER DEFAULT NULL REFERENCES eyepiece (id), "
0467                   "FOV INTEGER DEFAULT NULL REFERENCES fov (id))");
0468 
0469     // Note: enabled now encodes both a bool enabled value as well
0470     // as another bool indicating if this is a horizon line or a ceiling line.
0471     tables.append("CREATE TABLE horizons ( "
0472                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0473                   "name TEXT NOT NULL,"
0474                   "label TEXT NOT NULL,"
0475                   "enabled INTEGER NOT NULL)");
0476 
0477     tables.append("CREATE TABLE profile (id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, host "
0478                   "TEXT, port INTEGER, city TEXT, province TEXT, country TEXT, indiwebmanagerport INTEGER DEFAULT "
0479                   "NULL, autoconnect INTEGER DEFAULT 1, guidertype INTEGER DEFAULT 0, guiderhost TEXT, guiderport INTEGER,"
0480                   "indihub INTEGER DEFAULT 0, portselector INTEGER DEFAULT 1, remotedrivers TEXT DEFAULT NULL, "
0481                   "scripts TEXT DEFAULT NULL)");
0482 
0483 #ifdef Q_OS_WIN
0484     tables.append("INSERT INTO profile (name, host, port) VALUES ('Simulators', 'localhost', 7624)");
0485 #else
0486     tables.append("INSERT INTO profile (name, portselector) VALUES ('Simulators', 0)");
0487 #endif
0488 
0489     tables.append("CREATE TABLE driver (id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, label TEXT NOT NULL, role "
0490                   "TEXT NOT NULL, profile INTEGER NOT NULL, FOREIGN KEY(profile) REFERENCES profile(id))");
0491     //tables.append("CREATE TABLE custom_driver (id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, drivers TEXT NOT NULL, profile INTEGER NOT NULL, FOREIGN KEY(profile) REFERENCES profile(id))");
0492 
0493     tables.append("INSERT INTO driver (label, role, profile) VALUES ('Telescope Simulator', 'Mount', 1)");
0494     tables.append("INSERT INTO driver (label, role, profile) VALUES ('CCD Simulator', 'CCD', 1)");
0495     tables.append("INSERT INTO driver (label, role, profile) VALUES ('Focuser Simulator', 'Focuser', 1)");
0496 
0497     tables.append("CREATE TABLE profilesettings (id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0498                   "profile INTEGER DEFAULT NULL, settings TEXT DEFAULT NULL)");
0499 
0500     tables.append("CREATE TABLE opticaltrains (id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0501                   "profile INTEGER DEFAULT NULL, name TEXT DEFAULT NULL, mount TEXT DEFAULT NULL, "
0502                   "dustcap TEXT DEFAULT NULL, lightbox TEXT DEFAULT NULL, scope TEXT DEFAULT NULL, reducer REAL DEFAULT 1, "
0503                   "rotator TEXT DEFAULT NULL, focuser TEXT DEFAULT NULL, filterwheel TEXT DEFAULT NULL, camera TEXT DEFAULT NULL, "
0504                   "guider TEXT DEFAULT NULL)");
0505 
0506     tables.append("CREATE TABLE opticaltrainsettings (id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0507                   "opticaltrain INTEGER DEFAULT NULL, settings TEXT DEFAULT NULL)");
0508 
0509     tables.append("CREATE TABLE IF NOT EXISTS darkframe (id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, ccd TEXT "
0510                   "NOT NULL, chip INTEGER DEFAULT 0, binX INTEGER, binY INTEGER, temperature REAL, gain INTEGER DEFAULT -1, "
0511                   "iso TEXT DEFAULT NULL, duration REAL, filename TEXT NOT NULL, defectmap TEXT DEFAULT NULL, timestamp "
0512                   "DATETIME DEFAULT CURRENT_TIMESTAMP)");
0513 
0514     tables.append("CREATE TABLE IF NOT EXISTS hips (ID TEXT NOT NULL UNIQUE,"
0515                   "obs_title TEXT NOT NULL, obs_description TEXT NOT NULL, hips_order TEXT NOT NULL,"
0516                   "hips_frame TEXT NOT NULL, hips_tile_width TEXT NOT NULL, hips_tile_format TEXT NOT NULL,"
0517                   "hips_service_url TEXT NOT NULL, moc_sky_fraction TEXT NOT NULL)");
0518 
0519     tables.append("INSERT INTO hips (ID, obs_title, obs_description, hips_order, hips_frame, hips_tile_width, hips_tile_format, hips_service_url, moc_sky_fraction)"
0520                   "VALUES ('CDS/P/DSS2/color', 'DSS Colored', 'Color composition generated by CDS. This HiPS survey is based on 2 others HiPS surveys,"
0521                   " respectively DSS2-red and DSS2-blue HiPS, both of them directly generated from original scanned plates downloaded"
0522                   " from STScI site. The red component has been built from POSS-II F, AAO-SES,SR and SERC-ER plates. The blue component"
0523                   " has been build from POSS-II J and SERC-J,EJ. The green component is based on the mean of other components. Three"
0524                   " missing plates from red survey (253, 260, 359) has been replaced by pixels from the DSSColor STScI jpeg survey."
0525                   " The 11 missing blue plates (mainly in galactic plane) have not been replaced (only red component).',"
0526                   "'9', 'equatorial', '512', 'jpeg fits', 'http://alasky.u-strasbg.fr/DSS/DSSColor','1')");
0527 
0528     tables.append("INSERT INTO hips (ID, obs_title, obs_description, hips_order, hips_frame, hips_tile_width, hips_tile_format, hips_service_url, moc_sky_fraction)"
0529                   "VALUES ('CDS/P/2MASS/color', '2MASS Color J (1.23 microns), H (1.66 microns), K (2.16 microns)',"
0530                   "'2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources"
0531                   " brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of"
0532                   " 2.0\". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two"
0533                   " highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with"
0534                   " a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the"
0535                   " sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts"
0536                   " (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and"
0537                   " on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible"
0538                   " for all data processing through the Production Pipeline, and construction and distribution of the data products."
0539                   " Funding is provided primarily by NASA and the NSF',"
0540                   "'9', 'equatorial', '512', 'jpeg fits', 'http://alaskybis.u-strasbg.fr/2MASS/Color', '1')");
0541 
0542     tables.append("INSERT INTO hips (ID, obs_title, obs_description, hips_order, hips_frame, hips_tile_width, hips_tile_format, hips_service_url, moc_sky_fraction)"
0543                   "VALUES ('CDS/P/Fermi/color', 'Fermi Color HEALPix Survey', 'Launched on June 11, 2008, the Fermi Gamma-ray Space Telescope observes the cosmos using the"
0544                   " highest-energy form of light. This survey sums all data observed by the Fermi mission up to week 396. This version"
0545                   " of the Fermi survey are intensity maps where the summed counts maps are divided by the exposure for each pixel"
0546                   ". We anticipate using the HEASARC Hera capabilities to update this survey on a roughly quarterly basis. Data is"
0547                   " broken into 5 energy bands : 30-100 MeV Band 1, 100-300 MeV Band 2, 300-1000 MeV Band 3, 1-3 GeV Band 4 ,"
0548                   " 3-300 GeV Band 5. The SkyView data are based upon a Cartesian projection of the counts divided by the exposure maps."
0549                   " In the Cartesian projection pixels near the pole have a much smaller area than pixels on the equator, so these"
0550                   " pixels have smaller integrated flux. When creating large scale images in other projections users may wish to make"
0551                   " sure to compensate for this effect the flux conserving clip-resampling option.', '9', 'equatorial', '512', 'jpeg fits',"
0552                   "'http://alaskybis.u-strasbg.fr/Fermi/Color', '1')");
0553 
0554 
0555     tables.append("CREATE TABLE dslr (id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0556                   "Model TEXT DEFAULT NULL, "
0557                   "Width INTEGER DEFAULT NULL, "
0558                   "Height INTEGER DEFAULT NULL, "
0559                   "PixelW REAL DEFAULT 5.0,"
0560                   "PixelH REAL DEFAULT 5.0)");
0561 
0562 
0563     tables.append("CREATE TABLE effectivefov ( "
0564                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0565                   "Train TEXT DEFAULT NULL, "
0566                   "Profile TEXT DEFAULT NULL, "
0567                   "Width INTEGER DEFAULT NULL, "
0568                   "Height INTEGER DEFAULT NULL, "
0569                   "PixelW REAL DEFAULT 5.0,"
0570                   "PixelH REAL DEFAULT 5.0,"
0571                   "FocalLength REAL DEFAULT 0.0,"
0572                   "FocalReducer REAL DEFAULT 0.0,"
0573                   "FocalRatio REAL DEFAULT 0.0,"
0574                   "FovW REAL DEFAULT 0.0,"
0575                   "FovH REAL DEFAULT 0.0)");
0576 
0577     tables.append("CREATE TABLE customdrivers ( "
0578                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0579                   "Name TEXT DEFAULT NULL, "
0580                   "Label TEXT DEFAULT NULL UNIQUE, "
0581                   "Manufacturer TEXT DEFAULT NULL, "
0582                   "Family TEXT DEFAULT NULL, "
0583                   "Exec TEXT DEFAULT NULL, "
0584                   "Version TEXT DEFAULT 1.0)");
0585 
0586     tables.append("CREATE TABLE IF NOT EXISTS collimationoverlayelements ( "
0587                   "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
0588                   "Name TEXT DEFAULT NULL, "
0589                   "Enabled INTEGER DEFAULT 0, "
0590                   "Type INTEGER DEFAULT NULL, "
0591                   "SizeX INTEGER DEFAULT NULL, "
0592                   "SizeY INTEGER DEFAULT NULL, "
0593                   "OffsetX INTEGER DEFAULT NULL, "
0594                   "OffsetY INTEGER DEFAULT NULL, "
0595                   "Count INTEGER DEFAULT 1, "
0596                   "PCD INTEGER DEFAULT 100, "
0597                   "Rotation REAL DEFAULT 0.0, "
0598                   "Colour TEXT DEFAULT NULL, "
0599                   "Thickness INTEGER DEFAULT 1)");
0600 
0601     // Need to offset primary key by 100,000 to differential it from scopes and keep it backward compatible.
0602     tables.append("UPDATE SQLITE_SEQUENCE SET seq = 100000 WHERE name ='dslrlens'");
0603 
0604     for (int i = 0; i < tables.count(); ++i)
0605     {
0606         QSqlQuery query(db);
0607         if (!query.exec(tables[i]))
0608         {
0609             qCDebug(KSTARS) << query.lastError();
0610             qCDebug(KSTARS) << query.executedQuery();
0611         }
0612     }
0613 
0614     return true;
0615 }
0616 
0617 /*
0618  * Observer Section
0619 */
0620 
0621 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0622 ///
0623 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0624 bool KSUserDB::AddObserver(const QString &name, const QString &surname, const QString &contact)
0625 {
0626     auto db = QSqlDatabase::database(m_ConnectionName);
0627     if (!db.isValid())
0628     {
0629         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0630         return false;
0631     }
0632 
0633     QSqlTableModel users(nullptr, db);
0634     users.setTable("user");
0635     users.setFilter("Name LIKE \'" + name + "\' AND Surname LIKE \'" + surname + "\'");
0636     users.select();
0637 
0638     if (users.rowCount() > 0)
0639     {
0640         QSqlRecord record = users.record(0);
0641         record.setValue("Name", name);
0642         record.setValue("Surname", surname);
0643         record.setValue("Contact", contact);
0644         users.setRecord(0, record);
0645         users.submitAll();
0646     }
0647     else
0648     {
0649         int row = 0;
0650         users.insertRows(row, 1);
0651         users.setData(users.index(row, 1), name); // row0 is autoincerement ID
0652         users.setData(users.index(row, 2), surname);
0653         users.setData(users.index(row, 3), contact);
0654         users.submitAll();
0655     }
0656 
0657     return true;
0658 }
0659 
0660 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0661 ///
0662 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0663 bool KSUserDB::FindObserver(const QString &name, const QString &surname)
0664 {
0665     auto db = QSqlDatabase::database(m_ConnectionName);
0666     if (!db.isValid())
0667     {
0668         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0669         return false;
0670     }
0671 
0672     QSqlTableModel users(nullptr, db);
0673     users.setTable("user");
0674     users.setFilter("Name LIKE \'" + name + "\' AND Surname LIKE \'" + surname + "\'");
0675     users.select();
0676 
0677     int observer_count = users.rowCount();
0678 
0679     users.clear();
0680     return (observer_count > 0);
0681 }
0682 
0683 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0684 ///
0685 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0686 bool KSUserDB::DeleteObserver(const QString &id)
0687 {
0688     auto db = QSqlDatabase::database(m_ConnectionName);
0689     if (!db.isValid())
0690     {
0691         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0692         return false;
0693     }
0694 
0695     QSqlTableModel users(nullptr, db);
0696     users.setTable("user");
0697     users.setFilter("id = \'" + id + "\'");
0698     users.select();
0699 
0700     users.removeRows(0, 1);
0701     users.submitAll();
0702 
0703     int observer_count = users.rowCount();
0704 
0705     users.clear();
0706     return (observer_count > 0);
0707 }
0708 
0709 #ifndef KSTARS_LITE
0710 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0711 ///
0712 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0713 bool KSUserDB::GetAllObservers(QList<Observer *> &observer_list)
0714 {
0715     auto db = QSqlDatabase::database(m_ConnectionName);
0716     if (!db.isValid())
0717     {
0718         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0719         return false;
0720     }
0721 
0722     observer_list.clear();
0723     QSqlTableModel users(nullptr, db);
0724     users.setTable("user");
0725     users.select();
0726 
0727     for (int i = 0; i < users.rowCount(); ++i)
0728     {
0729         QSqlRecord record = users.record(i);
0730         QString id        = record.value("id").toString();
0731         QString name      = record.value("Name").toString();
0732         QString surname   = record.value("Surname").toString();
0733         QString contact   = record.value("Contact").toString();
0734         OAL::Observer *o  = new OAL::Observer(id, name, surname, contact);
0735         observer_list.append(o);
0736     }
0737 
0738     users.clear();
0739     return true;
0740 }
0741 #endif
0742 
0743 /* Dark Library Section */
0744 
0745 /**
0746  * @brief KSUserDB::AddDarkFrame Saves a new dark frame data to the database
0747  * @param oneFrame Map that contains 1 to 1 correspondence with the database table, except for primary key and timestamp.
0748  */
0749 bool KSUserDB::AddDarkFrame(const QVariantMap &oneFrame)
0750 {
0751     auto db = QSqlDatabase::database(m_ConnectionName);
0752     if (!db.isValid())
0753     {
0754         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0755         return false;
0756     }
0757 
0758     QSqlTableModel darkframe(nullptr, db);
0759     darkframe.setTable("darkframe");
0760     darkframe.select();
0761 
0762     QSqlRecord record = darkframe.record();
0763     // Remove PK so that it gets auto-incremented later
0764     record.remove(0);
0765 
0766     for (QVariantMap::const_iterator iter = oneFrame.begin(); iter != oneFrame.end(); ++iter)
0767         record.setValue(iter.key(), iter.value());
0768 
0769     darkframe.insertRecord(-1, record);
0770     darkframe.submitAll();
0771     return true;
0772 }
0773 
0774 /**
0775  * @brief KSUserDB::UpdateDarkFrame Updates an existing dark frame record in the data, replace all values matching the supplied ID
0776  * @param oneFrame dark frame to update. The ID should already exist in the database.
0777  */
0778 bool KSUserDB::UpdateDarkFrame(const QVariantMap &oneFrame)
0779 {
0780     auto db = QSqlDatabase::database(m_ConnectionName);
0781     if (!db.isValid())
0782     {
0783         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0784         return false;
0785     }
0786 
0787     QSqlTableModel darkframe(nullptr, db);
0788     darkframe.setTable("darkframe");
0789     darkframe.setFilter(QString("id=%1").arg(oneFrame["id"].toInt()));
0790     darkframe.select();
0791 
0792     QSqlRecord record = darkframe.record(0);
0793     for (QVariantMap::const_iterator iter = oneFrame.begin(); iter != oneFrame.end(); ++iter)
0794         record.setValue(iter.key(), iter.value());
0795 
0796     darkframe.setRecord(0, record);
0797     darkframe.submitAll();
0798 
0799     return true;
0800 }
0801 
0802 /**
0803  * @brief KSUserDB::DeleteDarkFrame Delete from database a dark frame record that matches the filename field.
0804  * @param filename filename of dark frame to delete from database.
0805  */
0806 bool KSUserDB::DeleteDarkFrame(const QString &filename)
0807 {
0808     auto db = QSqlDatabase::database(m_ConnectionName);
0809     if (!db.isValid())
0810     {
0811         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0812         return false;
0813     }
0814 
0815     QSqlTableModel darkframe(nullptr, db);
0816     darkframe.setTable("darkframe");
0817     darkframe.setFilter("filename = \'" + filename + "\'");
0818 
0819     darkframe.select();
0820 
0821     darkframe.removeRows(0, 1);
0822     darkframe.submitAll();
0823 
0824     return true;
0825 }
0826 
0827 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0828 ///
0829 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0830 bool KSUserDB::GetAllDarkFrames(QList<QVariantMap> &darkFrames)
0831 {
0832     auto db = QSqlDatabase::database(m_ConnectionName);
0833     if (!db.isValid())
0834     {
0835         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0836         return false;
0837     }
0838 
0839     darkFrames.clear();
0840 
0841     QSqlTableModel darkframe(nullptr, db);
0842     darkframe.setTable("darkframe");
0843     darkframe.select();
0844 
0845     for (int i = 0; i < darkframe.rowCount(); ++i)
0846     {
0847         QVariantMap recordMap;
0848         QSqlRecord record = darkframe.record(i);
0849         for (int j = 0; j < record.count(); j++)
0850             recordMap[record.fieldName(j)] = record.value(j);
0851 
0852         darkFrames.append(recordMap);
0853     }
0854 
0855     return true;
0856 }
0857 
0858 
0859 /* Effective FOV Section */
0860 
0861 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0862 ///
0863 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0864 bool KSUserDB::AddEffectiveFOV(const QVariantMap &oneFOV)
0865 {
0866     auto db = QSqlDatabase::database(m_ConnectionName);
0867     if (!db.isValid())
0868     {
0869         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0870         return false;
0871     }
0872 
0873     QSqlTableModel effectivefov(nullptr, db);
0874     effectivefov.setTable("effectivefov");
0875     effectivefov.select();
0876 
0877     QSqlRecord record = effectivefov.record();
0878 
0879     // Remove PK so that it gets auto-incremented later
0880     record.remove(0);
0881 
0882     for (QVariantMap::const_iterator iter = oneFOV.begin(); iter != oneFOV.end(); ++iter)
0883         record.setValue(iter.key(), iter.value());
0884 
0885     effectivefov.insertRecord(-1, record);
0886 
0887     effectivefov.submitAll();
0888 
0889     return true;
0890 }
0891 
0892 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0893 ///
0894 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0895 bool KSUserDB::DeleteEffectiveFOV(const QString &id)
0896 {
0897     auto db = QSqlDatabase::database(m_ConnectionName);
0898     if (!db.isValid())
0899     {
0900         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0901         return false;
0902     }
0903 
0904     QSqlTableModel effectivefov(nullptr, db);
0905     effectivefov.setTable("effectivefov");
0906     effectivefov.setFilter("id = \'" + id + "\'");
0907 
0908     effectivefov.select();
0909 
0910     effectivefov.removeRows(0, 1);
0911     effectivefov.submitAll();
0912     return true;
0913 }
0914 
0915 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0916 ///
0917 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0918 bool KSUserDB::GetAllEffectiveFOVs(QList<QVariantMap> &effectiveFOVs)
0919 {
0920     auto db = QSqlDatabase::database(m_ConnectionName);
0921     if (!db.isValid())
0922     {
0923         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0924         return false;
0925     }
0926 
0927     effectiveFOVs.clear();
0928 
0929     QSqlTableModel effectivefov(nullptr, db);
0930     effectivefov.setTable("effectivefov");
0931     effectivefov.select();
0932 
0933     for (int i = 0; i < effectivefov.rowCount(); ++i)
0934     {
0935         QVariantMap recordMap;
0936         QSqlRecord record = effectivefov.record(i);
0937         for (int j = 0; j < record.count(); j++)
0938             recordMap[record.fieldName(j)] = record.value(j);
0939 
0940         effectiveFOVs.append(recordMap);
0941     }
0942 
0943     return true;
0944 }
0945 
0946 /* Optical Trains Section */
0947 
0948 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0949 ///
0950 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0951 bool KSUserDB::AddOpticalTrain(const QVariantMap &oneTrain)
0952 {
0953     auto db = QSqlDatabase::database(m_ConnectionName);
0954     if (!db.isValid())
0955     {
0956         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0957         return false;
0958     }
0959 
0960     QSqlTableModel opticalTrain(nullptr, db);
0961     opticalTrain.setTable("opticaltrains");
0962     opticalTrain.select();
0963 
0964     QSqlRecord record = opticalTrain.record();
0965 
0966     // Remove PK so that it gets auto-incremented later
0967     record.remove(0);
0968 
0969     for (QVariantMap::const_iterator iter = oneTrain.begin(); iter != oneTrain.end(); ++iter)
0970         record.setValue(iter.key(), iter.value());
0971 
0972     opticalTrain.insertRecord(-1, record);
0973 
0974     if (!opticalTrain.submitAll())
0975     {
0976         qCWarning(KSTARS) << opticalTrain.lastError();
0977         return false;
0978     }
0979 
0980     return true;
0981 }
0982 
0983 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0984 ///
0985 ////////////////////////////////////////////////////////////////////////////////////////////////////////
0986 bool KSUserDB::UpdateOpticalTrain(const QVariantMap &oneTrain, int id)
0987 {
0988     auto db = QSqlDatabase::database(m_ConnectionName);
0989     if (!db.isValid())
0990     {
0991         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
0992         return false;
0993     }
0994 
0995     QSqlTableModel opticalTrain(nullptr, db);
0996     opticalTrain.setTable("opticaltrains");
0997     opticalTrain.setFilter(QString("id=%1").arg(id));
0998     opticalTrain.select();
0999 
1000     QSqlRecord record = opticalTrain.record(0);
1001 
1002     for (QVariantMap::const_iterator iter = oneTrain.begin(); iter != oneTrain.end(); ++iter)
1003         record.setValue(iter.key(), iter.value());
1004 
1005     opticalTrain.setRecord(0, record);
1006 
1007     if (!opticalTrain.submitAll())
1008     {
1009         qCWarning(KSTARS) << opticalTrain.lastError();
1010         return false;
1011     }
1012 
1013     return true;
1014 }
1015 
1016 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1017 ///
1018 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1019 bool KSUserDB::DeleteOpticalTrain(int id)
1020 {
1021     auto db = QSqlDatabase::database(m_ConnectionName);
1022     if (!db.isValid())
1023     {
1024         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1025         return false;
1026     }
1027 
1028     QSqlTableModel opticalTrain(nullptr, db);
1029     opticalTrain.setTable("opticaltrains");
1030     opticalTrain.setFilter(QString("id=%1").arg(id));
1031 
1032     opticalTrain.select();
1033 
1034     opticalTrain.removeRows(0, 1);
1035     opticalTrain.submitAll();
1036     return true;
1037 }
1038 
1039 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1040 ///
1041 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1042 bool KSUserDB::GetOpticalTrains(uint32_t profileID, QList<QVariantMap> &opticalTrains)
1043 {
1044     auto db = QSqlDatabase::database(m_ConnectionName);
1045     if (!db.isValid())
1046     {
1047         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1048         return false;
1049     }
1050 
1051     opticalTrains.clear();
1052 
1053     QSqlTableModel opticalTrain(nullptr, db);
1054     opticalTrain.setTable("opticaltrains");
1055     opticalTrain.setFilter(QString("profile=%1").arg(profileID));
1056     opticalTrain.select();
1057 
1058     for (int i = 0; i < opticalTrain.rowCount(); ++i)
1059     {
1060         QVariantMap recordMap;
1061         QSqlRecord record = opticalTrain.record(i);
1062         for (int j = 0; j < record.count(); j++)
1063             recordMap[record.fieldName(j)] = record.value(j);
1064 
1065         opticalTrains.append(recordMap);
1066     }
1067 
1068     return true;
1069 }
1070 
1071 /* Driver Alias Section */
1072 
1073 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1074 ///
1075 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1076 bool KSUserDB::AddCustomDriver(const QVariantMap &oneDriver)
1077 {
1078     auto db = QSqlDatabase::database(m_ConnectionName);
1079     if (!db.isValid())
1080     {
1081         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1082         return false;
1083     }
1084 
1085     QSqlTableModel CustomDriver(nullptr, db);
1086     CustomDriver.setTable("customdrivers");
1087     CustomDriver.select();
1088 
1089     QSqlRecord record = CustomDriver.record();
1090 
1091     // Remove PK so that it gets auto-incremented later
1092     record.remove(0);
1093 
1094     for (QVariantMap::const_iterator iter = oneDriver.begin(); iter != oneDriver.end(); ++iter)
1095         record.setValue(iter.key(), iter.value());
1096 
1097     bool rc = CustomDriver.insertRecord(-1, record);
1098     if (rc == false)
1099         return rc;
1100 
1101     rc = CustomDriver.submitAll();
1102 
1103     return rc;
1104 }
1105 
1106 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1107 ///
1108 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1109 bool KSUserDB::DeleteCustomDriver(const QString &id)
1110 {
1111     auto db = QSqlDatabase::database(m_ConnectionName);
1112     if (!db.isValid())
1113     {
1114         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1115         return false;
1116     }
1117 
1118     QSqlTableModel CustomDriver(nullptr, db);
1119     CustomDriver.setTable("customdrivers");
1120     CustomDriver.setFilter("id = \'" + id + "\'");
1121 
1122     CustomDriver.select();
1123 
1124     CustomDriver.removeRows(0, 1);
1125     CustomDriver.submitAll();
1126 
1127     return true;
1128 }
1129 
1130 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1131 ///
1132 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1133 bool KSUserDB::GetAllCustomDrivers(QList<QVariantMap> &CustomDrivers)
1134 {
1135     auto db = QSqlDatabase::database(m_ConnectionName);
1136     if (!db.isValid())
1137     {
1138         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1139         return false;
1140     }
1141 
1142     CustomDrivers.clear();
1143     QSqlTableModel CustomDriver(nullptr, db);
1144     CustomDriver.setTable("customdrivers");
1145     CustomDriver.select();
1146 
1147     for (int i = 0; i < CustomDriver.rowCount(); ++i)
1148     {
1149         QVariantMap recordMap;
1150         QSqlRecord record = CustomDriver.record(i);
1151         for (int j = 0; j < record.count(); j++)
1152             recordMap[record.fieldName(j)] = record.value(j);
1153 
1154         CustomDrivers.append(recordMap);
1155     }
1156 
1157     return true;
1158 }
1159 
1160 /* HiPS Section */
1161 
1162 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1163 ///
1164 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1165 bool KSUserDB::AddHIPSSource(const QMap<QString, QString> &oneSource)
1166 {
1167     auto db = QSqlDatabase::database(m_ConnectionName);
1168     if (!db.isValid())
1169     {
1170         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1171         return false;
1172     }
1173 
1174     QSqlTableModel HIPSSource(nullptr, db);
1175     HIPSSource.setTable("hips");
1176     HIPSSource.select();
1177 
1178     QSqlRecord record = HIPSSource.record();
1179 
1180     for (QMap<QString, QString>::const_iterator iter = oneSource.begin(); iter != oneSource.end(); ++iter)
1181         record.setValue(iter.key(), iter.value());
1182 
1183     HIPSSource.insertRecord(-1, record);
1184     HIPSSource.submitAll();
1185 
1186     return true;
1187 }
1188 
1189 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1190 ///
1191 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1192 bool KSUserDB::DeleteHIPSSource(const QString &ID)
1193 {
1194     auto db = QSqlDatabase::database(m_ConnectionName);
1195     if (!db.isValid())
1196     {
1197         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1198         return false;
1199     }
1200 
1201     QSqlTableModel HIPSSource(nullptr, db);
1202     HIPSSource.setTable("hips");
1203     HIPSSource.setFilter("ID = \'" + ID + "\'");
1204 
1205     HIPSSource.select();
1206 
1207     HIPSSource.removeRows(0, 1);
1208     HIPSSource.submitAll();
1209 
1210     return true;
1211 }
1212 
1213 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1214 ///
1215 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1216 bool KSUserDB::GetAllHIPSSources(QList<QMap<QString, QString>> &HIPSSources)
1217 {
1218     auto db = QSqlDatabase::database(m_ConnectionName);
1219     if (!db.isValid())
1220     {
1221         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1222         return false;
1223     }
1224 
1225     HIPSSources.clear();
1226     QSqlTableModel HIPSSource(nullptr, db);
1227     HIPSSource.setTable("hips");
1228     HIPSSource.select();
1229 
1230     for (int i = 0; i < HIPSSource.rowCount(); ++i)
1231     {
1232         QMap<QString, QString> recordMap;
1233         QSqlRecord record = HIPSSource.record(i);
1234         for (int j = 1; j < record.count(); j++)
1235             recordMap[record.fieldName(j)] = record.value(j).toString();
1236 
1237         HIPSSources.append(recordMap);
1238     }
1239 
1240     return true;
1241 }
1242 
1243 
1244 /* DSLR Section */
1245 
1246 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1247 ///
1248 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1249 bool KSUserDB::AddDSLRInfo(const QMap<QString, QVariant> &oneInfo)
1250 {
1251     auto db = QSqlDatabase::database(m_ConnectionName);
1252     if (!db.isValid())
1253     {
1254         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1255         return false;
1256     }
1257 
1258     QSqlTableModel DSLRInfo(nullptr, db);
1259     DSLRInfo.setTable("dslr");
1260     DSLRInfo.select();
1261 
1262     QSqlRecord record = DSLRInfo.record();
1263 
1264     for (QMap<QString, QVariant>::const_iterator iter = oneInfo.begin(); iter != oneInfo.end(); ++iter)
1265         record.setValue(iter.key(), iter.value());
1266 
1267     DSLRInfo.insertRecord(-1, record);
1268     DSLRInfo.submitAll();
1269 
1270     return true;
1271 }
1272 
1273 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1274 ///
1275 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1276 bool KSUserDB::DeleteAllDSLRInfo()
1277 {
1278     auto db = QSqlDatabase::database(m_ConnectionName);
1279     if (!db.isValid())
1280     {
1281         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1282         return false;
1283     }
1284 
1285     QSqlTableModel DSLRInfo(nullptr, db);
1286     DSLRInfo.setTable("dslr");
1287     DSLRInfo.select();
1288 
1289     DSLRInfo.removeRows(0, DSLRInfo.rowCount());
1290     DSLRInfo.submitAll();
1291 
1292     return true;
1293 }
1294 
1295 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1296 ///
1297 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1298 bool KSUserDB::DeleteDSLRInfo(const QString &model)
1299 {
1300     auto db = QSqlDatabase::database(m_ConnectionName);
1301     if (!db.isValid())
1302     {
1303         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1304         return false;
1305     }
1306 
1307     QSqlTableModel DSLRInfo(nullptr, db);
1308     DSLRInfo.setTable("dslr");
1309     DSLRInfo.setFilter("model = \'" + model + "\'");
1310 
1311     DSLRInfo.select();
1312 
1313     DSLRInfo.removeRows(0, 1);
1314     DSLRInfo.submitAll();
1315 
1316     return true;
1317 }
1318 
1319 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1320 ///
1321 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1322 bool KSUserDB::GetAllDSLRInfos(QList<QMap<QString, QVariant>> &DSLRInfos)
1323 {
1324     auto db = QSqlDatabase::database(m_ConnectionName);
1325     if (!db.isValid())
1326     {
1327         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1328         return false;
1329     }
1330 
1331     DSLRInfos.clear();
1332 
1333     QSqlTableModel DSLRInfo(nullptr, db);
1334     DSLRInfo.setTable("dslr");
1335     DSLRInfo.select();
1336 
1337     for (int i = 0; i < DSLRInfo.rowCount(); ++i)
1338     {
1339         QMap<QString, QVariant> recordMap;
1340         QSqlRecord record = DSLRInfo.record(i);
1341         for (int j = 1; j < record.count(); j++)
1342             recordMap[record.fieldName(j)] = record.value(j);
1343 
1344         DSLRInfos.append(recordMap);
1345     }
1346 
1347     return true;
1348 }
1349 
1350 /*
1351  * Flag Section
1352 */
1353 
1354 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1355 ///
1356 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1357 bool KSUserDB::DeleteAllFlags()
1358 {
1359     auto db = QSqlDatabase::database(m_ConnectionName);
1360     if (!db.isValid())
1361     {
1362         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1363         return false;
1364     }
1365 
1366     QSqlTableModel flags(nullptr, db);
1367     flags.setEditStrategy(QSqlTableModel::OnManualSubmit);
1368     flags.setTable("flags");
1369     flags.select();
1370 
1371     flags.removeRows(0, flags.rowCount());
1372     flags.submitAll();
1373 
1374     flags.clear();
1375 
1376     return true;
1377 }
1378 
1379 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1380 ///
1381 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1382 bool KSUserDB::AddFlag(const QString &ra, const QString &dec, const QString &epoch, const QString &image_name,
1383                        const QString &label, const QString &labelColor)
1384 {
1385     auto db = QSqlDatabase::database(m_ConnectionName);
1386     if (!db.isValid())
1387     {
1388         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1389         return false;
1390     }
1391 
1392     QSqlTableModel flags(nullptr, db);
1393     flags.setTable("flags");
1394 
1395     int row = 0;
1396     flags.insertRows(row, 1);
1397     flags.setData(flags.index(row, 1), ra); // row,0 is autoincerement ID
1398     flags.setData(flags.index(row, 2), dec);
1399     flags.setData(flags.index(row, 3), image_name);
1400     flags.setData(flags.index(row, 4), label);
1401     flags.setData(flags.index(row, 5), labelColor);
1402     flags.setData(flags.index(row, 6), epoch);
1403     flags.submitAll();
1404 
1405     flags.clear();
1406     return true;
1407 }
1408 
1409 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1410 ///
1411 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1412 bool KSUserDB::GetAllFlags(QList<QStringList> &flagList)
1413 {
1414     auto db = QSqlDatabase::database(m_ConnectionName);
1415     if (!db.isValid())
1416     {
1417         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1418         return false;
1419     }
1420 
1421     QSqlTableModel flags(nullptr, db);
1422     flags.setTable("flags");
1423     flags.select();
1424 
1425     for (int i = 0; i < flags.rowCount(); ++i)
1426     {
1427         QStringList flagEntry;
1428         QSqlRecord record = flags.record(i);
1429         /* flagEntry order description
1430          * The variation in the order is due to variation
1431          * in flag entry description order and flag database
1432          * description order.
1433          * flag (database): ra, dec, icon, label, color, epoch
1434          * flag (object):  ra, dec, epoch, icon, label, color
1435         */
1436         flagEntry.append(record.value(1).toString());
1437         flagEntry.append(record.value(2).toString());
1438         flagEntry.append(record.value(6).toString());
1439         flagEntry.append(record.value(3).toString());
1440         flagEntry.append(record.value(4).toString());
1441         flagEntry.append(record.value(5).toString());
1442         flagList.append(flagEntry);
1443     }
1444 
1445     flags.clear();
1446     return true;
1447 }
1448 
1449 /*
1450  * Generic Section
1451  */
1452 
1453 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1454 ///
1455 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1456 bool KSUserDB::DeleteEquipment(const QString &type, const QString &id)
1457 {
1458     auto db = QSqlDatabase::database(m_ConnectionName);
1459     if (!db.isValid())
1460     {
1461         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1462         return false;
1463     }
1464 
1465     QSqlTableModel equip(nullptr, db);
1466     equip.setTable(type);
1467     equip.setFilter("id = " + id);
1468     equip.select();
1469 
1470     equip.removeRows(0, equip.rowCount());
1471     equip.submitAll();
1472     equip.clear();
1473 
1474     return true;
1475 }
1476 
1477 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1478 ///
1479 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1480 bool KSUserDB::DeleteAllEquipment(const QString &type)
1481 {
1482     auto db = QSqlDatabase::database(m_ConnectionName);
1483     if (!db.isValid())
1484     {
1485         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1486         return false;
1487     }
1488 
1489     QSqlTableModel equip(nullptr, db);
1490     equip.setEditStrategy(QSqlTableModel::OnManualSubmit);
1491     equip.setTable(type);
1492     equip.setFilter("id >= 1");
1493     equip.select();
1494     equip.removeRows(0, equip.rowCount());
1495     equip.submitAll();
1496     equip.clear();
1497 
1498     return true;
1499 }
1500 
1501 /*
1502  * Telescope section
1503  */
1504 
1505 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1506 ///
1507 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1508 bool KSUserDB::AddScope(const QString &model, const QString &vendor, const QString &type, const double &aperture,
1509                         const double &focalLength)
1510 {
1511     auto db = QSqlDatabase::database(m_ConnectionName);
1512     if (!db.isValid())
1513     {
1514         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1515         return false;
1516     }
1517 
1518     QSqlTableModel equip(nullptr, db);
1519     equip.setTable("telescope");
1520 
1521     QSqlRecord record = equip.record();
1522     record.setValue("Vendor", vendor);
1523     record.setValue("Aperture", aperture);
1524     record.setValue("Model", model);
1525     record.setValue("Type", type);
1526     record.setValue("FocalLength", focalLength);
1527 
1528     equip.insertRecord(-1, record);
1529 
1530     if (!equip.submitAll())
1531         qCWarning(KSTARS) << equip.lastError().text();
1532 
1533     equip.clear();
1534 
1535     return true;
1536 }
1537 
1538 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1539 ///
1540 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1541 bool KSUserDB::AddScope(const QString &model, const QString &vendor, const QString &type,
1542                         const double &aperture, const double &focalLength, const QString &id)
1543 {
1544     auto db = QSqlDatabase::database(m_ConnectionName);
1545     if (!db.isValid())
1546     {
1547         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1548         return false;
1549     }
1550 
1551     QSqlTableModel equip(nullptr, db);
1552     equip.setTable("telescope");
1553     equip.setFilter("id = " + id);
1554     equip.select();
1555 
1556     if (equip.rowCount() > 0)
1557     {
1558         QSqlRecord record = equip.record(0);
1559         record.setValue("Vendor", vendor);
1560         record.setValue("Aperture", aperture);
1561         record.setValue("Model", model);
1562         record.setValue("Type", type);
1563         record.setValue("FocalLength", focalLength);
1564         equip.setRecord(0, record);
1565         equip.submitAll();
1566     }
1567 
1568     return true;
1569 }
1570 
1571 #ifndef KSTARS_LITE
1572 
1573 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1574 ///
1575 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1576 bool KSUserDB::GetAllScopes(QList<Scope *> &scope_list)
1577 {
1578     auto db = QSqlDatabase::database(m_ConnectionName);
1579     if (!db.isValid())
1580     {
1581         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1582         return false;
1583     }
1584 
1585     qDeleteAll(scope_list);
1586     scope_list.clear();
1587 
1588     QSqlTableModel equip(nullptr, db);
1589     equip.setTable("telescope");
1590     equip.select();
1591 
1592     for (int i = 0; i < equip.rowCount(); ++i)
1593     {
1594         QSqlRecord record  = equip.record(i);
1595         QString id         = record.value("id").toString();
1596         QString vendor     = record.value("Vendor").toString();
1597         double aperture    = record.value("Aperture").toDouble();
1598         QString model      = record.value("Model").toString();
1599         QString type       = record.value("Type").toString();
1600         double focalLength = record.value("FocalLength").toDouble();
1601         OAL::Scope *o      = new OAL::Scope(id, model, vendor, type, focalLength, aperture);
1602         scope_list.append(o);
1603     }
1604 
1605     equip.clear();
1606     return true;
1607 }
1608 #endif
1609 /*
1610  * Eyepiece section
1611  */
1612 
1613 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1614 ///
1615 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1616 bool KSUserDB::AddEyepiece(const QString &vendor, const QString &model, const double &focalLength, const double &fov,
1617                            const QString &fovunit)
1618 {
1619     auto db = QSqlDatabase::database(m_ConnectionName);
1620     if (!db.isValid())
1621     {
1622         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1623         return false;
1624     }
1625 
1626     QSqlTableModel equip(nullptr, db);
1627     equip.setTable("eyepiece");
1628 
1629     int row = 0;
1630     equip.insertRows(row, 1);
1631     equip.setData(equip.index(row, 1), vendor); // row,0 is autoincerement ID
1632     equip.setData(equip.index(row, 2), model);
1633     equip.setData(equip.index(row, 3), focalLength);
1634     equip.setData(equip.index(row, 4), fov);
1635     equip.setData(equip.index(row, 5), fovunit);
1636     equip.submitAll();
1637     equip.clear();
1638 
1639     return true;
1640 }
1641 
1642 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1643 ///
1644 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1645 bool KSUserDB::AddEyepiece(const QString &vendor, const QString &model, const double &focalLength, const double &fov,
1646                            const QString &fovunit, const QString &id)
1647 {
1648     auto db = QSqlDatabase::database(m_ConnectionName);
1649     if (!db.isValid())
1650     {
1651         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1652         return false;
1653     }
1654 
1655     QSqlTableModel equip(nullptr, db);
1656     equip.setTable("eyepiece");
1657     equip.setFilter("id = " + id);
1658     equip.select();
1659 
1660     if (equip.rowCount() > 0)
1661     {
1662         QSqlRecord record = equip.record(0);
1663         record.setValue(1, vendor);
1664         record.setValue(2, model);
1665         record.setValue(3, focalLength);
1666         record.setValue(4, fov);
1667         record.setValue(5, fovunit);
1668         equip.setRecord(0, record);
1669         equip.submitAll();
1670     }
1671 
1672     return true;
1673 }
1674 
1675 #ifndef KSTARS_LITE
1676 
1677 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1678 ///
1679 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1680 bool KSUserDB::GetAllEyepieces(QList<OAL::Eyepiece *> &eyepiece_list)
1681 {
1682     auto db = QSqlDatabase::database(m_ConnectionName);
1683     if (!db.isValid())
1684     {
1685         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1686         return false;
1687     }
1688 
1689     eyepiece_list.clear();
1690 
1691     QSqlTableModel equip(nullptr, db);
1692     equip.setTable("eyepiece");
1693     equip.select();
1694 
1695     for (int i = 0; i < equip.rowCount(); ++i)
1696     {
1697         QSqlRecord record  = equip.record(i);
1698         QString id         = record.value("id").toString();
1699         QString vendor     = record.value("Vendor").toString();
1700         QString model      = record.value("Model").toString();
1701         double focalLength = record.value("FocalLength").toDouble();
1702         double fov         = record.value("ApparentFOV").toDouble();
1703         QString fovUnit    = record.value("FOVUnit").toString();
1704 
1705         OAL::Eyepiece *o = new OAL::Eyepiece(id, model, vendor, fov, fovUnit, focalLength);
1706         eyepiece_list.append(o);
1707     }
1708 
1709     equip.clear();
1710     return true;
1711 }
1712 #endif
1713 /*
1714  * lens section
1715  */
1716 
1717 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1718 ///
1719 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1720 bool KSUserDB::AddLens(const QString &vendor, const QString &model, const double &factor)
1721 {
1722     auto db = QSqlDatabase::database(m_ConnectionName);
1723     if (!db.isValid())
1724     {
1725         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1726         return false;
1727     }
1728 
1729     QSqlTableModel equip(nullptr, db);
1730     equip.setTable("lens");
1731 
1732     int row = 0;
1733     equip.insertRows(row, 1);
1734     equip.setData(equip.index(row, 1), vendor); // row,0 is autoincerement ID
1735     equip.setData(equip.index(row, 2), model);
1736     equip.setData(equip.index(row, 3), factor);
1737     equip.submitAll();
1738 
1739     equip.clear();
1740     return true;
1741 }
1742 
1743 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1744 ///
1745 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1746 bool KSUserDB::AddLens(const QString &vendor, const QString &model, const double &factor, const QString &id)
1747 {
1748     auto db = QSqlDatabase::database(m_ConnectionName);
1749     if (!db.isValid())
1750     {
1751         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1752         return false;
1753     }
1754 
1755     QSqlTableModel equip(nullptr, db);
1756     equip.setTable("lens");
1757     equip.setFilter("id = " + id);
1758     equip.select();
1759 
1760     if (equip.rowCount() > 0)
1761     {
1762         QSqlRecord record = equip.record(0);
1763         record.setValue(1, vendor);
1764         record.setValue(2, model);
1765         record.setValue(3, factor);
1766         equip.submitAll();
1767     }
1768 
1769     return true;
1770 }
1771 #ifndef KSTARS_LITE
1772 
1773 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1774 ///
1775 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1776 bool KSUserDB::GetAllLenses(QList<OAL::Lens *> &lens_list)
1777 {
1778     auto db = QSqlDatabase::database(m_ConnectionName);
1779     if (!db.isValid())
1780     {
1781         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1782         return false;
1783     }
1784 
1785     qDeleteAll(lens_list);
1786     lens_list.clear();
1787 
1788     QSqlTableModel equip(nullptr, db);
1789     equip.setTable("lens");
1790     equip.select();
1791 
1792     for (int i = 0; i < equip.rowCount(); ++i)
1793     {
1794         QSqlRecord record = equip.record(i);
1795         QString id        = record.value("id").toString();
1796         QString vendor    = record.value("Vendor").toString();
1797         QString model     = record.value("Model").toString();
1798         double factor     = record.value("Factor").toDouble();
1799         OAL::Lens *o      = new OAL::Lens(id, model, vendor, factor);
1800         lens_list.append(o);
1801     }
1802 
1803     equip.clear();
1804     return true;
1805 }
1806 #endif
1807 /*
1808  *  filter section
1809  */
1810 
1811 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1812 ///
1813 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1814 bool KSUserDB::AddFilter(const filterProperties *fp)
1815 {
1816     auto db = QSqlDatabase::database(m_ConnectionName);
1817     if (!db.isValid())
1818     {
1819         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1820         return false;
1821     }
1822 
1823     QSqlTableModel equip(nullptr, db);
1824     equip.setTable("filter");
1825 
1826     QSqlRecord record = equip.record();
1827     record.setValue("Vendor", fp->vendor);
1828     record.setValue("Model", fp->model);
1829     record.setValue("Type", fp->type);
1830     record.setValue("Color", fp->color);
1831     record.setValue("Offset", fp->offset);
1832     record.setValue("Exposure", fp->exposure);
1833     record.setValue("UseAutoFocus", fp->useAutoFocus ? 1 : 0);
1834     record.setValue("LockedFilter", fp->lockedFilter);
1835     record.setValue("AbsoluteFocusPosition", fp->absFocusPos);
1836     record.setValue("FocusTemperature", fp->focusTemperature);
1837     record.setValue("FocusAltitude", fp->focusAltitude);
1838     record.setValue("FocusTicksPerTemp", fp->focusTicksPerTemp);
1839     record.setValue("FocusTicksPerAlt", fp->focusTicksPerAlt);
1840     record.setValue("Wavelength", fp->wavelength);
1841 
1842     if (equip.insertRecord(-1, record) == false)
1843         qCritical() << __FUNCTION__ << equip.lastError();
1844 
1845     if (equip.submitAll() == false)
1846         qCritical() << "AddFilter:" << equip.lastError();
1847 
1848     equip.clear();
1849     return true;
1850 }
1851 
1852 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1853 ///
1854 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1855 bool KSUserDB::AddFilter(const filterProperties *fp, const QString &id)
1856 {
1857     auto db = QSqlDatabase::database(m_ConnectionName);
1858     if (!db.isValid())
1859     {
1860         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1861         return false;
1862     }
1863 
1864     QSqlTableModel equip(nullptr, db);
1865     equip.setTable("filter");
1866     equip.setFilter("id = " + id);
1867     equip.select();
1868 
1869     if (equip.rowCount() > 0)
1870     {
1871         QSqlRecord record = equip.record(0);
1872         record.setValue("Vendor", fp->vendor);
1873         record.setValue("Model", fp->model);
1874         record.setValue("Type", fp->type);
1875         record.setValue("Color", fp->color);
1876         record.setValue("Offset", fp->offset);
1877         record.setValue("Exposure", fp->exposure);
1878         record.setValue("UseAutoFocus", fp->useAutoFocus ? 1 : 0);
1879         record.setValue("LockedFilter", fp->lockedFilter);
1880         record.setValue("AbsoluteFocusPosition", fp->absFocusPos);
1881         record.setValue("FocusTemperature", fp->focusTemperature);
1882         record.setValue("FocusAltitude", fp->focusAltitude);
1883         record.setValue("FocusTicksPerTemp", fp->focusTicksPerTemp);
1884         record.setValue("FocusTicksPerAlt", fp->focusTicksPerAlt);
1885         record.setValue("Wavelength", fp->wavelength);
1886         equip.setRecord(0, record);
1887         if (equip.submitAll() == false)
1888             qCritical() << "AddFilter:" << equip.lastError();
1889     }
1890 
1891     return true;
1892 }
1893 
1894 #ifndef KSTARS_LITE
1895 
1896 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1897 ///
1898 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1899 bool KSUserDB::GetAllFilters(QList<OAL::Filter *> &filter_list)
1900 {
1901     auto db = QSqlDatabase::database(m_ConnectionName);
1902     if (!db.isValid())
1903     {
1904         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
1905         return false;
1906     }
1907 
1908     filter_list.clear();
1909     QSqlTableModel equip(nullptr, db);
1910     equip.setTable("filter");
1911     equip.select();
1912 
1913     filterProperties *fp = new filterProperties("", "", "", "");
1914 
1915     for (int i = 0; i < equip.rowCount(); ++i)
1916     {
1917         QSqlRecord record     = equip.record(i);
1918         QString id            = record.value("id").toString();
1919         fp->vendor            = record.value("Vendor").toString();
1920         fp->model             = record.value("Model").toString();
1921         fp->type              = record.value("Type").toString();
1922         fp->color             = record.value("Color").toString();
1923         fp->offset            = record.value("Offset").toInt();
1924         fp->exposure          = record.value("Exposure").toDouble();
1925         fp->lockedFilter      = record.value("LockedFilter").toString();
1926         fp->useAutoFocus      = record.value("UseAutoFocus").toInt() == 1;
1927         fp->absFocusPos       = record.value("AbsoluteFocusPosition").toInt();
1928         fp->focusTemperature  = record.value("FocusTemperature").toDouble();
1929         fp->focusAltitude     = record.value("FocusAltitude").toDouble();
1930         fp->focusTicksPerTemp = record.value("FocusTicksPerTemp").toDouble();
1931         fp->focusTicksPerAlt  = record.value("FocusTicksPerAlt").toDouble();
1932         fp->wavelength        = record.value("Wavelength").toDouble();
1933         OAL::Filter *o        = new OAL::Filter(id, fp);
1934         filter_list.append(o);
1935     }
1936 
1937     equip.clear();
1938     return true;
1939 }
1940 #endif
1941 
1942 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1943 ///
1944 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1945 void KSUserDB::readScopes()
1946 {
1947     while (!reader_->atEnd())
1948     {
1949         reader_->readNext();
1950 
1951         if (reader_->isEndElement())
1952             break;
1953 
1954         if (reader_->isStartElement())
1955         {
1956             if (reader_->name() == "scope")
1957                 readScope();
1958         }
1959     }
1960 }
1961 
1962 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1963 ///
1964 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1965 void KSUserDB::readEyepieces()
1966 {
1967     while (!reader_->atEnd())
1968     {
1969         reader_->readNext();
1970 
1971         if (reader_->isEndElement())
1972             break;
1973 
1974         if (reader_->isStartElement())
1975         {
1976             if (reader_->name() == "eyepiece")
1977                 readEyepiece();
1978         }
1979     }
1980 }
1981 
1982 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1983 ///
1984 ////////////////////////////////////////////////////////////////////////////////////////////////////////
1985 void KSUserDB::readLenses()
1986 {
1987     while (!reader_->atEnd())
1988     {
1989         reader_->readNext();
1990 
1991         if (reader_->isEndElement())
1992             break;
1993 
1994         if (reader_->isStartElement())
1995         {
1996             if (reader_->name() == "lens")
1997                 readLens();
1998         }
1999     }
2000 }
2001 
2002 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2003 ///
2004 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2005 void KSUserDB::readFilters()
2006 {
2007     while (!reader_->atEnd())
2008     {
2009         reader_->readNext();
2010 
2011         if (reader_->isEndElement())
2012             break;
2013 
2014         if (reader_->isStartElement())
2015         {
2016             if (reader_->name() == "filter")
2017                 readFilter();
2018         }
2019     }
2020 }
2021 
2022 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2023 ///
2024 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2025 void KSUserDB::readScope()
2026 {
2027     QString model, vendor, type;
2028     double aperture = 0, focalLength = 0;
2029 
2030     while (!reader_->atEnd())
2031     {
2032         reader_->readNext();
2033 
2034         if (reader_->isEndElement())
2035             break;
2036 
2037         if (reader_->isStartElement())
2038         {
2039             if (reader_->name() == "model")
2040             {
2041                 model = reader_->readElementText();
2042             }
2043             else if (reader_->name() == "vendor")
2044             {
2045                 vendor = reader_->readElementText();
2046             }
2047             else if (reader_->name() == "type")
2048             {
2049                 type = reader_->readElementText();
2050                 if (type == "N")
2051                     type = "Newtonian";
2052                 if (type == "R")
2053                     type = "Refractor";
2054                 if (type == "M")
2055                     type = "Maksutov";
2056                 if (type == "S")
2057                     type = "Schmidt-Cassegrain";
2058                 if (type == "K")
2059                     type = "Kutter (Schiefspiegler)";
2060                 if (type == "C")
2061                     type = "Cassegrain";
2062                 if (type == "RC")
2063                     type = "Ritchey-Chretien";
2064             }
2065             else if (reader_->name() == "focalLength")
2066             {
2067                 focalLength = (reader_->readElementText()).toDouble();
2068             }
2069             else if (reader_->name() == "aperture")
2070                 aperture = (reader_->readElementText()).toDouble();
2071         }
2072     }
2073 
2074     AddScope(model, vendor, type, focalLength, aperture);
2075 }
2076 
2077 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2078 ///
2079 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2080 void KSUserDB::readEyepiece()
2081 {
2082     QString model, focalLength, vendor, fov, fovUnit;
2083     while (!reader_->atEnd())
2084     {
2085         reader_->readNext();
2086 
2087         if (reader_->isEndElement())
2088             break;
2089 
2090         if (reader_->isStartElement())
2091         {
2092             if (reader_->name() == "model")
2093             {
2094                 model = reader_->readElementText();
2095             }
2096             else if (reader_->name() == "vendor")
2097             {
2098                 vendor = reader_->readElementText();
2099             }
2100             else if (reader_->name() == "apparentFOV")
2101             {
2102                 fov     = reader_->readElementText();
2103                 fovUnit = reader_->attributes().value("unit").toString();
2104             }
2105             else if (reader_->name() == "focalLength")
2106             {
2107                 focalLength = reader_->readElementText();
2108             }
2109         }
2110     }
2111 
2112     AddEyepiece(vendor, model, focalLength.toDouble(), fov.toDouble(), fovUnit);
2113 }
2114 
2115 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2116 ///
2117 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2118 void KSUserDB::readLens()
2119 {
2120     QString model, factor, vendor;
2121     while (!reader_->atEnd())
2122     {
2123         reader_->readNext();
2124 
2125         if (reader_->isEndElement())
2126             break;
2127 
2128         if (reader_->isStartElement())
2129         {
2130             if (reader_->name() == "model")
2131             {
2132                 model = reader_->readElementText();
2133             }
2134             else if (reader_->name() == "vendor")
2135             {
2136                 vendor = reader_->readElementText();
2137             }
2138             else if (reader_->name() == "factor")
2139             {
2140                 factor = reader_->readElementText();
2141             }
2142         }
2143     }
2144 
2145     AddLens(vendor, model, factor.toDouble());
2146 }
2147 
2148 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2149 ///
2150 ////////////////////////////////////////////////////////////////////////////////////////////////////////
2151 void KSUserDB::readFilter()
2152 {
2153     filterProperties *fp = new filterProperties("", "", "", "");
2154 
2155     while (!reader_->atEnd())
2156     {
2157         reader_->readNext();
2158 
2159         if (reader_->isEndElement())
2160             break;
2161 
2162         if (reader_->isStartElement())
2163         {
2164             if (reader_->name() == "model")
2165             {
2166                 fp->model = reader_->readElementText();
2167             }
2168             else if (reader_->name() == "vendor")
2169             {
2170                 fp->vendor = reader_->readElementText();
2171             }
2172             else if (reader_->name() == "type")
2173             {
2174                 fp->type = reader_->readElementText();
2175             }
2176             else if (reader_->name() == "offset")
2177             {
2178                 fp->offset = reader_->readElementText().toInt();
2179             }
2180             else if (reader_->name() == "color")
2181             {
2182                 fp->color = reader_->readElementText();
2183             }
2184             else if (reader_->name() == "exposure")
2185             {
2186                 fp->exposure = reader_->readElementText().toDouble();
2187             }
2188             else if (reader_->name() == "lockedFilter")
2189             {
2190                 fp->lockedFilter = reader_->readElementText();
2191             }
2192             else if (reader_->name() == "useAutoFocus")
2193             {
2194                 fp->useAutoFocus = (reader_->readElementText() == "1");
2195             }
2196             else if (reader_->name() == "AbsoluteAutoFocus")
2197             {
2198                 fp->absFocusPos = (reader_->readElementText().toInt());
2199             }
2200             else if (reader_->name() == "FocusTemperature")
2201             {
2202                 fp->focusTemperature = (reader_->readElementText().toDouble());
2203             }
2204             else if (reader_->name() == "FocusAltitude")
2205             {
2206                 fp->focusAltitude = (reader_->readElementText().toDouble());
2207             }
2208             else if (reader_->name() == "FocusTicksPerTemp")
2209             {
2210                 fp->focusTicksPerTemp = (reader_->readElementText().toDouble());
2211             }
2212             else if (reader_->name() == "FocusTicksPerAlt")
2213             {
2214                 fp->focusTicksPerAlt = (reader_->readElementText().toDouble());
2215             }
2216             else if (reader_->name() == "Wavelength")
2217             {
2218                 fp->wavelength = (reader_->readElementText().toDouble());
2219             }
2220         }
2221     }
2222     AddFilter(fp);
2223 }
2224 
2225 bool KSUserDB::GetAllHorizons(QList<ArtificialHorizonEntity *> &horizonList)
2226 {
2227     auto db = QSqlDatabase::database(m_ConnectionName);
2228     if (!db.isValid())
2229     {
2230         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2231         return false;
2232     }
2233 
2234     qDeleteAll(horizonList);
2235     horizonList.clear();
2236     QSqlTableModel regions(nullptr, db);
2237     regions.setTable("horizons");
2238     regions.select();
2239 
2240     QSqlTableModel points(nullptr, db);
2241 
2242     for (int i = 0; i < regions.rowCount(); ++i)
2243     {
2244         QSqlRecord record         = regions.record(i);
2245         const QString regionTable = record.value("name").toString();
2246         const QString regionName  = record.value("label").toString();
2247 
2248         const int flags           = record.value("enabled").toInt();
2249         const bool enabled        = flags & 0x1 ? true : false;
2250         const bool ceiling        = flags & 0x2 ? true : false;
2251 
2252         points.setTable(regionTable);
2253         points.select();
2254 
2255         std::shared_ptr<LineList> skyList(new LineList());
2256 
2257         ArtificialHorizonEntity *horizon = new ArtificialHorizonEntity;
2258 
2259         horizon->setRegion(regionName);
2260         horizon->setEnabled(enabled);
2261         horizon->setCeiling(ceiling);
2262         horizon->setList(skyList);
2263 
2264         horizonList.append(horizon);
2265 
2266         for (int j = 0; j < points.rowCount(); j++)
2267         {
2268             std::shared_ptr<SkyPoint> p(new SkyPoint());
2269 
2270             record = points.record(j);
2271             p->setAz(record.value(0).toDouble());
2272             p->setAlt(record.value(1).toDouble());
2273             p->HorizontalToEquatorial(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
2274             skyList->append(std::move(p));
2275         }
2276 
2277         points.clear();
2278     }
2279 
2280     regions.clear();
2281 
2282     return true;
2283 }
2284 
2285 bool KSUserDB::DeleteAllHorizons()
2286 {
2287     auto db = QSqlDatabase::database(m_ConnectionName);
2288     if (!db.isValid())
2289     {
2290         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2291         return false;
2292     }
2293 
2294     QSqlTableModel regions(nullptr, db);
2295     regions.setEditStrategy(QSqlTableModel::OnManualSubmit);
2296     regions.setTable("horizons");
2297     regions.select();
2298 
2299     QSqlQuery query(db);
2300 
2301     for (int i = 0; i < regions.rowCount(); ++i)
2302     {
2303         QSqlRecord record  = regions.record(i);
2304         QString tableQuery = QString("DROP TABLE %1").arg(record.value("name").toString());
2305         if (!query.exec(tableQuery))
2306             qCWarning(KSTARS) << query.lastError().text();
2307     }
2308 
2309     regions.removeRows(0, regions.rowCount());
2310     regions.submitAll();
2311 
2312     regions.clear();
2313     return true;
2314 }
2315 
2316 bool KSUserDB::AddHorizon(ArtificialHorizonEntity *horizon)
2317 {
2318     auto db = QSqlDatabase::database(m_ConnectionName);
2319     if (!db.isValid())
2320     {
2321         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2322         return false;
2323     }
2324 
2325     QSqlTableModel regions(nullptr, db);
2326     regions.setTable("horizons");
2327 
2328     regions.select();
2329     QString tableName = QString("horizon_%1").arg(regions.rowCount() + 1);
2330 
2331     regions.insertRow(0);
2332     regions.setData(regions.index(0, 1), tableName);
2333     regions.setData(regions.index(0, 2), horizon->region());
2334     int flags = 0;
2335     if (horizon->enabled()) flags |= 0x1;
2336     if (horizon->ceiling()) flags |= 0x2;
2337     regions.setData(regions.index(0, 3), flags);
2338     regions.submitAll();
2339     regions.clear();
2340 
2341     QString tableQuery = QString("CREATE TABLE %1 (Az REAL NOT NULL, Alt REAL NOT NULL)").arg(tableName);
2342     QSqlQuery query(db);
2343     query.exec(tableQuery);
2344 
2345     QSqlTableModel points(nullptr, db);
2346 
2347     points.setTable(tableName);
2348 
2349     SkyList *skyList = horizon->list()->points();
2350 
2351     for (const auto &item : *skyList)
2352     {
2353         points.select();
2354         QSqlRecord rec(points.record());
2355 
2356         rec.setValue("Az", item->az().Degrees());
2357         rec.setValue("Alt", item->alt().Degrees());
2358         points.insertRecord(-1, rec);
2359     }
2360 
2361     points.submitAll();
2362     points.clear();
2363     return true;
2364 }
2365 
2366 void KSUserDB::CreateImageOverlayTableIfNecessary()
2367 {
2368     auto db = QSqlDatabase::database(m_ConnectionName);
2369     QString command = "CREATE TABLE IF NOT EXISTS imageOverlays ( "
2370                       "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT, "
2371                       "filename TEXT NOT NULL,"
2372                       "enabled INTEGER DEFAULT 0,"
2373                       "nickname TEXT DEFAULT NULL,"
2374                       "status INTEGER DEFAULT 0,"
2375                       "orientation REAL DEFAULT 0.0,"
2376                       "ra REAL DEFAULT 0.0,"
2377                       "dec REAL DEFAULT 0.0,"
2378                       "pixelsPerArcsec REAL DEFAULT 0.0,"
2379                       "eastToTheRight INTEGER DEFAULT 0,"
2380                       "width INTEGER DEFAULT 0,"
2381                       "height INTEGER DEFAULT 0)";
2382     QSqlQuery query(db);
2383     if (!query.exec(command))
2384     {
2385         qCDebug(KSTARS) << query.lastError();
2386         qCDebug(KSTARS) << query.executedQuery();
2387     }
2388 }
2389 
2390 bool KSUserDB::DeleteAllImageOverlays()
2391 {
2392     CreateImageOverlayTableIfNecessary();
2393     auto db = QSqlDatabase::database(m_ConnectionName);
2394     if (!db.isValid())
2395     {
2396         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2397         return false;
2398     }
2399 
2400     QSqlTableModel overlays(nullptr, db);
2401     overlays.setTable("imageOverlays");
2402     overlays.setFilter("id >= 1");
2403     overlays.select();
2404     overlays.removeRows(0, overlays.rowCount());
2405     overlays.submitAll();
2406 
2407     QSqlQuery query(db);
2408     QString dropQuery = QString("DROP TABLE imageOverlays");
2409     if (!query.exec(dropQuery))
2410         qCWarning(KSTARS) << query.lastError().text();
2411 
2412     return true;
2413 }
2414 
2415 bool KSUserDB::AddImageOverlay(const ImageOverlay &overlay)
2416 {
2417     CreateImageOverlayTableIfNecessary();
2418     auto db = QSqlDatabase::database(m_ConnectionName);
2419     if (!db.isValid())
2420     {
2421         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2422         return false;
2423     }
2424 
2425     QSqlTableModel overlays(nullptr, db);
2426     overlays.setTable("imageOverlays");
2427     overlays.setFilter("filename LIKE \'" + overlay.m_Filename + "\'");
2428     overlays.select();
2429 
2430     if (overlays.rowCount() > 0)
2431     {
2432         QSqlRecord record = overlays.record(0);
2433         record.setValue("filename", overlay.m_Filename);
2434         record.setValue("enabled", static_cast<int>(overlay.m_Enabled));
2435         record.setValue("nickname", overlay.m_Nickname);
2436         record.setValue("status", static_cast<int>(overlay.m_Status));
2437         record.setValue("orientation", overlay.m_Orientation);
2438         record.setValue("ra", overlay.m_RA);
2439         record.setValue("dec", overlay.m_DEC);
2440         record.setValue("pixelsPerArcsec", overlay.m_ArcsecPerPixel);
2441         record.setValue("eastToTheRight", static_cast<int>(overlay.m_EastToTheRight));
2442         record.setValue("width", overlay.m_Width);
2443         record.setValue("height", overlay.m_Height);
2444         overlays.setRecord(0, record);
2445         overlays.submitAll();
2446     }
2447     else
2448     {
2449         int row = 0;
2450         overlays.insertRows(row, 1);
2451 
2452         overlays.setData(overlays.index(row, 1), overlay.m_Filename);  // row,0 is autoincerement ID
2453         overlays.setData(overlays.index(row, 2), static_cast<int>(overlay.m_Enabled));
2454         overlays.setData(overlays.index(row, 3), overlay.m_Nickname);
2455         overlays.setData(overlays.index(row, 4), static_cast<int>(overlay.m_Status));
2456         overlays.setData(overlays.index(row, 5), overlay.m_Orientation);
2457         overlays.setData(overlays.index(row, 6), overlay.m_RA);
2458         overlays.setData(overlays.index(row, 7), overlay.m_DEC);
2459         overlays.setData(overlays.index(row, 8), overlay.m_ArcsecPerPixel);
2460         overlays.setData(overlays.index(row, 9), static_cast<int>(overlay.m_EastToTheRight));
2461         overlays.setData(overlays.index(row, 10), overlay.m_Width);
2462         overlays.setData(overlays.index(row, 11), overlay.m_Height);
2463         overlays.submitAll();
2464     }
2465     return true;
2466 }
2467 
2468 bool KSUserDB::GetAllImageOverlays(QList<ImageOverlay> *imageOverlayList)
2469 {
2470     CreateImageOverlayTableIfNecessary();
2471     auto db = QSqlDatabase::database(m_ConnectionName);
2472     if (!db.isValid())
2473     {
2474         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2475         return false;
2476     }
2477 
2478     imageOverlayList->clear();
2479     QSqlTableModel overlays(nullptr, db);
2480     overlays.setTable("imageOverlays");
2481     overlays.select();
2482 
2483     for (int i = 0; i < overlays.rowCount(); ++i)
2484     {
2485         QSqlRecord record         = overlays.record(i);
2486 
2487         const QString filename        = record.value("filename").toString();
2488         const bool    enabled         = static_cast<bool>(record.value("enabled").toInt());
2489         const QString nickname        = record.value("nickname").toString();
2490         const ImageOverlay::Status status
2491             = static_cast<ImageOverlay::Status>(record.value("status").toInt());
2492         const double  orientation     = record.value("orientation").toDouble();
2493         const double  ra              = record.value("ra").toDouble();
2494         const double  dec             = record.value("dec").toDouble();
2495         const double  pixelsPerArcsec = record.value("pixelsPerArcsec").toDouble();
2496         const bool    eastToTheRight  = static_cast<bool>(record.value("eastToTheRight").toInt());
2497         const int     width           = record.value("width").toInt();
2498         const int     height          = record.value("height").toInt();
2499         ImageOverlay o(filename, enabled, nickname, status, orientation, ra, dec, pixelsPerArcsec,
2500                        eastToTheRight, width, height);
2501         imageOverlayList->append(o);
2502     }
2503 
2504     overlays.clear();
2505     return true;
2506 }
2507 
2508 int KSUserDB::AddProfile(const QString &name)
2509 {
2510     auto db = QSqlDatabase::database(m_ConnectionName);
2511     if (!db.isValid())
2512     {
2513         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2514         return -1;
2515     }
2516 
2517     int id = -1;
2518 
2519     QSqlQuery query(db);
2520     bool rc = query.exec(QString("INSERT INTO profile (name) VALUES('%1')").arg(name));
2521 
2522     if (rc == false)
2523         qCWarning(KSTARS) << query.lastQuery() << query.lastError().text();
2524     else
2525         id = query.lastInsertId().toInt();
2526 
2527     return id;
2528 }
2529 
2530 bool KSUserDB::DeleteProfile(const QSharedPointer<ProfileInfo> &pi)
2531 {
2532     auto db = QSqlDatabase::database(m_ConnectionName);
2533     if (!db.isValid())
2534     {
2535         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2536         return false;
2537     }
2538 
2539     QSqlQuery query(db);
2540     bool rc;
2541 
2542     rc = query.exec("DELETE FROM profile WHERE id=" + QString::number(pi->id));
2543 
2544     if (rc == false)
2545         qCWarning(KSTARS) << query.lastQuery() << query.lastError().text();
2546 
2547 
2548 
2549     return rc;
2550 }
2551 
2552 bool KSUserDB::PurgeProfile(const QSharedPointer<ProfileInfo> &pi)
2553 {
2554     auto db = QSqlDatabase::database(m_ConnectionName);
2555     if (!db.isValid())
2556     {
2557         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2558         return false;
2559     }
2560 
2561     QSqlQuery query(db);
2562     bool rc;
2563 
2564     rc = query.exec("DELETE FROM profile WHERE id=" + QString::number(pi->id));
2565     if (rc == false)
2566         qCWarning(KSTARS) << query.lastQuery() << query.lastError().text();
2567     rc = query.exec("DELETE FROM profilesettings WHERE profile=" + QString::number(pi->id));
2568     if (rc == false)
2569         qCWarning(KSTARS) << query.lastQuery() << query.lastError().text();
2570     rc = query.exec("DELETE FROM opticaltrainsettings WHERE opticaltrain IN (select id FROM opticaltrains WHERE profile=" +
2571                     QString::number(pi->id) + ")");
2572     if (rc == false)
2573         qCWarning(KSTARS) << query.lastQuery() << query.lastError().text();
2574     rc = query.exec("DELETE FROM opticaltrains WHERE profile=" + QString::number(pi->id));
2575     if (rc == false)
2576         qCWarning(KSTARS) << query.lastQuery() << query.lastError().text();
2577 
2578     return rc;
2579 }
2580 
2581 bool KSUserDB::SaveProfile(const QSharedPointer<ProfileInfo> &pi)
2582 {
2583     auto db = QSqlDatabase::database(m_ConnectionName);
2584     if (!db.isValid())
2585     {
2586         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2587         return false;
2588     }
2589 
2590     // Remove all drivers
2591     DeleteProfileDrivers(pi);
2592 
2593     QSqlQuery query(db);
2594 
2595     // Clear data
2596     if (!query.exec(QString("UPDATE profile SET "
2597                             "host=null,port=null,city=null,province=null,country=null,indiwebmanagerport=NULL,"
2598                             "autoconnect=NULL,portselector=NULL,indihub=0 WHERE id=%1")
2599                     .arg(pi->id)))
2600         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2601 
2602     // Update Name
2603     if (!query.exec(QString("UPDATE profile SET name='%1' WHERE id=%2").arg(pi->name).arg(pi->id)))
2604         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2605 
2606     // Update Remote Data
2607     if (pi->host.isEmpty() == false)
2608     {
2609         if (!query.exec(
2610                     QString("UPDATE profile SET host='%1',port=%2 WHERE id=%3").arg(pi->host).arg((pi->port)).arg(pi->id)))
2611             qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2612 
2613         if (pi->INDIWebManagerPort != -1)
2614         {
2615             if (!query.exec(QString("UPDATE profile SET indiwebmanagerport='%1' WHERE id=%2")
2616                             .arg(pi->INDIWebManagerPort)
2617                             .arg(pi->id)))
2618                 qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2619         }
2620     }
2621 
2622     // Update City Info
2623     if (pi->city.isEmpty() == false)
2624     {
2625         if (!query.exec(QString("UPDATE profile SET city='%1',province='%2',country='%3' WHERE id=%4")
2626                         .arg(pi->city, pi->province, pi->country)
2627                         .arg(pi->id)))
2628         {
2629             qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2630         }
2631     }
2632 
2633     // Update Auto Connect Info
2634     if (!query.exec(QString("UPDATE profile SET autoconnect=%1 WHERE id=%2").arg(pi->autoConnect ? 1 : 0).arg(pi->id)))
2635         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2636 
2637     // Update Port Selector Info
2638     if (!query.exec(QString("UPDATE profile SET portselector=%1 WHERE id=%2").arg(pi->portSelector ? 1 : 0).arg(pi->id)))
2639         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2640 
2641     // Update Guide Application Info
2642     if (!query.exec(QString("UPDATE profile SET guidertype=%1 WHERE id=%2").arg(pi->guidertype).arg(pi->id)))
2643         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2644 
2645     // Update INDI Hub
2646     if (!query.exec(QString("UPDATE profile SET indihub=%1 WHERE id=%2").arg(pi->indihub).arg(pi->id)))
2647         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2648 
2649     // If using external guider
2650     if (pi->guidertype != 0)
2651     {
2652         if (!query.exec(QString("UPDATE profile SET guiderhost='%1' WHERE id=%2").arg(pi->guiderhost).arg(pi->id)))
2653             qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2654         if (!query.exec(QString("UPDATE profile SET guiderport=%1 WHERE id=%2").arg(pi->guiderport).arg(pi->id)))
2655             qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2656     }
2657 
2658     // Update remote drivers
2659     if (!query.exec(QString("UPDATE profile SET remotedrivers='%1' WHERE id=%2").arg(pi->remotedrivers).arg(pi->id)))
2660         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2661 
2662     // Update scripts
2663     if (!query.exec(QString("UPDATE profile SET scripts='%1' WHERE id=%2").arg(QString::fromLocal8Bit(pi->scripts)).arg(
2664                         pi->id)))
2665         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2666 
2667     QMapIterator<QString, QString> i(pi->drivers);
2668     while (i.hasNext())
2669     {
2670         i.next();
2671         if (!query.exec(QString("INSERT INTO driver (label, role, profile) VALUES('%1','%2',%3)")
2672                         .arg(i.value(), i.key())
2673                         .arg(pi->id)))
2674         {
2675             qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2676         }
2677     }
2678 
2679     /*if (pi->customDrivers.isEmpty() == false && !query.exec(QString("INSERT INTO custom_driver (drivers, profile) VALUES('%1',%2)").arg(pi->customDrivers).arg(pi->id)))
2680         qDebug()  << query.lastQuery() << query.lastError().text();*/
2681 
2682 
2683     return true;
2684 }
2685 
2686 bool KSUserDB::GetAllProfiles(QList<QSharedPointer<ProfileInfo>> &profiles)
2687 {
2688     auto db = QSqlDatabase::database(m_ConnectionName);
2689     if (!db.isValid())
2690     {
2691         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2692         return false;
2693     }
2694 
2695     profiles.clear();
2696     QSqlTableModel profile(nullptr, db);
2697     profile.setTable("profile");
2698     profile.select();
2699 
2700     for (int i = 0; i < profile.rowCount(); ++i)
2701     {
2702         QSqlRecord record = profile.record(i);
2703 
2704         int id       = record.value("id").toInt();
2705         QString name = record.value("name").toString();
2706         QSharedPointer<ProfileInfo> pi(new ProfileInfo(id, name));
2707 
2708         // Add host and port
2709         pi->host = record.value("host").toString();
2710         pi->port = record.value("port").toInt();
2711 
2712         // City info
2713         pi->city     = record.value("city").toString();
2714         pi->province = record.value("province").toString();
2715         pi->country  = record.value("country").toString();
2716 
2717         pi->INDIWebManagerPort = record.value("indiwebmanagerport").toInt();
2718         pi->autoConnect        = (record.value("autoconnect").toInt() == 1);
2719         pi->portSelector       = (record.value("portselector").toInt() == 1);
2720 
2721         pi->indihub = record.value("indihub").toInt();
2722 
2723         pi->guidertype = record.value("guidertype").toInt();
2724         if (pi->guidertype != 0)
2725         {
2726             pi->guiderhost = record.value("guiderhost").toString();
2727             pi->guiderport = record.value("guiderport").toInt();
2728         }
2729 
2730         pi->remotedrivers = record.value("remotedrivers").toString();
2731 
2732         pi->scripts = record.value("scripts").toByteArray();
2733 
2734         GetProfileDrivers(pi);
2735 
2736         profiles.append(std::move(pi));
2737     }
2738 
2739     return true;
2740 }
2741 
2742 bool KSUserDB::GetProfileDrivers(const QSharedPointer<ProfileInfo> &pi)
2743 {
2744     auto db = QSqlDatabase::database(m_ConnectionName);
2745     if (!db.isValid())
2746     {
2747         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2748         return false;
2749     }
2750 
2751     QSqlTableModel driver(nullptr, db);
2752     driver.setTable("driver");
2753     driver.setFilter("profile=" + QString::number(pi->id));
2754     if (driver.select() == false)
2755         qCWarning(KSTARS) << "Driver select error:" << driver.lastError().text();
2756 
2757     for (int i = 0; i < driver.rowCount(); ++i)
2758     {
2759         QSqlRecord record = driver.record(i);
2760         QString label     = record.value("label").toString();
2761         QString role      = record.value("role").toString();
2762 
2763         pi->drivers[role] = label;
2764     }
2765 
2766     driver.clear();
2767     return true;
2768 }
2769 
2770 /*void KSUserDB::GetProfileCustomDrivers(ProfileInfo* pi)
2771 {
2772     userdb_.open();
2773     QSqlTableModel custom_driver(0, userdb_);
2774     custom_driver.setTable("driver");
2775     custom_driver.setFilter("profile=" + QString::number(pi->id));
2776     if (custom_driver.select() == false)
2777         qDebug() << Q_FUNC_INFO << "custom driver select error: " << custom_driver.query().lastQuery() << custom_driver.lastError().text();
2778 
2779     QSqlRecord record = custom_driver.record(0);
2780     pi->customDrivers   = record.value("drivers").toString();
2781 
2782     custom_driver.clear();
2783     userdb_.close();
2784 }*/
2785 
2786 bool KSUserDB::DeleteProfileDrivers(const QSharedPointer<ProfileInfo> &pi)
2787 {
2788     auto db = QSqlDatabase::database(m_ConnectionName);
2789     if (!db.isValid())
2790     {
2791         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2792         return false;
2793     }
2794 
2795     QSqlQuery query(db);
2796 
2797     /*if (!query.exec("DELETE FROM custom_driver WHERE profile=" + QString::number(pi->id)))
2798         qDebug() << Q_FUNC_INFO << query.lastQuery() << query.lastError().text();*/
2799 
2800     if (!query.exec("DELETE FROM driver WHERE profile=" + QString::number(pi->id)))
2801         qCWarning(KSTARS) << query.executedQuery() << query.lastError().text();
2802 
2803     return true;
2804 }
2805 
2806 /*
2807  * DSLR Lens Section
2808 */
2809 bool KSUserDB::AddDSLRLens(const QString &model, const QString &vendor, const double focalLength, const double focalRatio)
2810 {
2811     auto db = QSqlDatabase::database(m_ConnectionName);
2812     if (!db.isValid())
2813     {
2814         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2815         return false;
2816     }
2817 
2818     QSqlTableModel equip(nullptr, db);
2819     equip.setTable("dslrlens");
2820 
2821     QSqlRecord record = equip.record();
2822     record.setValue("Vendor", vendor);
2823     record.setValue("Model", model);
2824     record.setValue("FocalLength", focalLength);
2825     record.setValue("FocalRatio", focalRatio);
2826 
2827     if (equip.insertRecord(-1, record) == false)
2828         qCritical() << __FUNCTION__ << equip.lastError();
2829     equip.submitAll();
2830     equip.clear();
2831 
2832     return true;
2833 
2834 }
2835 
2836 bool KSUserDB::AddDSLRLens(const QString &model, const QString &vendor, const double focalLength, const double focalRatio,
2837                            const QString &id)
2838 {
2839     auto db = QSqlDatabase::database(m_ConnectionName);
2840     if (!db.isValid())
2841     {
2842         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2843         return false;
2844     }
2845 
2846     QSqlTableModel equip(nullptr, db);
2847     equip.setTable("dslrlens");
2848     equip.setFilter("id = " + id);
2849     equip.select();
2850 
2851     if (equip.rowCount() > 0)
2852     {
2853         QSqlRecord record = equip.record(0);
2854         record.setValue("Vendor", vendor);
2855         record.setValue("Model", model);
2856         record.setValue("FocalLength", focalLength);
2857         record.setValue("FocalRatio", focalRatio);
2858         equip.setRecord(0, record);
2859         equip.submitAll();
2860     }
2861 
2862     return true;
2863 }
2864 
2865 #ifndef KSTARS_LITE
2866 bool KSUserDB::GetAllDSLRLenses(QList<OAL::DSLRLens *> &dslrlens_list)
2867 {
2868     dslrlens_list.clear();
2869 
2870     auto db = QSqlDatabase::database(m_ConnectionName);
2871     if (!db.isValid())
2872     {
2873         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
2874         return false;
2875     }
2876 
2877     QSqlTableModel equip(nullptr, db);
2878     equip.setTable("dslrlens");
2879     equip.select();
2880 
2881     for (int i = 0; i < equip.rowCount(); ++i)
2882     {
2883         QSqlRecord record  = equip.record(i);
2884         QString id         = record.value("id").toString();
2885         QString vendor     = record.value("Vendor").toString();
2886         QString model      = record.value("Model").toString();
2887         double focalLength = record.value("FocalLength").toDouble();
2888         double focalRatio  = record.value("FocalRatio").toDouble();
2889         OAL::DSLRLens *o   = new OAL::DSLRLens(id, model, vendor, focalLength, focalRatio);
2890         dslrlens_list.append(o);
2891     }
2892 
2893     equip.clear();
2894 
2895     return true;
2896 }
2897 #endif
2898 
2899 bool KSUserDB::getOpticalElementByID(int id, QJsonObject &element)
2900 {
2901     // Get all OAL equipment filter list
2902     QList<OAL::Scope *> scopeList;
2903     QList<OAL::DSLRLens *> dslrlensList;
2904     KStarsData::Instance()->userdb()->GetAllScopes(scopeList);
2905     KStarsData::Instance()->userdb()->GetAllDSLRLenses(dslrlensList);
2906 
2907     for (auto &oneScope : scopeList)
2908     {
2909         if (oneScope->id().toInt() == id)
2910         {
2911             element = oneScope->toJson();
2912             return true;
2913         }
2914     }
2915 
2916     for (auto &oneLens : dslrlensList)
2917     {
2918         if (oneLens->id().toInt() == id)
2919         {
2920             element = oneLens->toJson();
2921             return true;
2922         }
2923     }
2924 
2925     return false;
2926 }
2927 
2928 bool KSUserDB::getLastOpticalElement(QJsonObject &element)
2929 {
2930     // Get all OAL equipment filter list
2931     QList<OAL::Scope *> scopeList;
2932     QList<OAL::DSLRLens *> dslrlensList;
2933     KStarsData::Instance()->userdb()->GetAllScopes(scopeList);
2934     KStarsData::Instance()->userdb()->GetAllDSLRLenses(dslrlensList);
2935 
2936     if (!scopeList.empty())
2937     {
2938         element = scopeList.last()->toJson();
2939         return true;
2940     }
2941 
2942     if (!dslrlensList.empty())
2943     {
2944         element = dslrlensList.last()->toJson();
2945         return true;
2946     }
2947 
2948     return false;
2949 }
2950 
2951 bool KSUserDB::getOpticalElementByName(const QString &name, QJsonObject &element)
2952 {
2953     // Get all OAL equipment filter list
2954     QList<OAL::Scope *> scopeList;
2955     QList<OAL::DSLRLens *> dslrlensList;
2956     KStarsData::Instance()->userdb()->GetAllScopes(scopeList);
2957     KStarsData::Instance()->userdb()->GetAllDSLRLenses(dslrlensList);
2958 
2959     for (auto &oneScope : scopeList)
2960     {
2961         if (oneScope->name() == name)
2962         {
2963             element = oneScope->toJson();
2964             return true;
2965         }
2966     }
2967 
2968     for (auto &oneLens : dslrlensList)
2969     {
2970         if (oneLens->name() == name)
2971         {
2972             element = oneLens->toJson();
2973             return true;
2974         }
2975     }
2976 
2977     return false;
2978 }
2979 
2980 QStringList KSUserDB::getOpticalElementNames()
2981 {
2982     QStringList names;
2983 
2984     // Get all OAL equipment filter list
2985     QList<OAL::Scope *> scopeList;
2986     QList<OAL::DSLRLens *> dslrlensList;
2987     KStarsData::Instance()->userdb()->GetAllScopes(scopeList);
2988     KStarsData::Instance()->userdb()->GetAllDSLRLenses(dslrlensList);
2989 
2990     for (auto &oneValue : scopeList)
2991         names << oneValue->name();
2992 
2993     for (auto &oneValue : dslrlensList)
2994         names << oneValue->name();
2995 
2996     return names;
2997 }
2998 
2999 void KSUserDB::AddProfileSettings(uint32_t profile, const QByteArray &settings)
3000 {
3001     auto db = QSqlDatabase::database(m_ConnectionName);
3002     if (!db.isValid())
3003         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3004 
3005     QSqlTableModel profileSettings(nullptr, db);
3006     profileSettings.setTable("profilesettings");
3007     profileSettings.select();
3008 
3009     QSqlRecord record = profileSettings.record();
3010     record.setValue("profile", profile);
3011     record.setValue("settings", settings);
3012     profileSettings.insertRecord(-1, record);
3013 
3014     if (!profileSettings.submitAll())
3015         qCWarning(KSTARS) << profileSettings.lastError();
3016 
3017 
3018 }
3019 
3020 void KSUserDB::UpdateProfileSettings(uint32_t profile, const QByteArray &settings)
3021 {
3022     auto db = QSqlDatabase::database(m_ConnectionName);
3023     if (!db.isValid())
3024         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3025 
3026     QSqlTableModel profileSettings(nullptr, db);
3027     profileSettings.setTable("profilesettings");
3028     profileSettings.setFilter(QString("profile=%1").arg(profile));
3029     profileSettings.select();
3030 
3031     QSqlRecord record = profileSettings.record(0);
3032     record.setValue("settings", settings);
3033     profileSettings.setRecord(0, record);
3034 
3035     if (!profileSettings.submitAll())
3036         qCWarning(KSTARS) << profileSettings.lastError();
3037 }
3038 
3039 
3040 void KSUserDB::DeleteProfileSettings(uint32_t profile)
3041 {
3042     auto db = QSqlDatabase::database(m_ConnectionName);
3043     if (!db.isValid())
3044         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3045 
3046     QSqlTableModel profileSettings(nullptr, db);
3047     profileSettings.setTable("profilesettings");
3048     profileSettings.setFilter(QString("profile=%1").arg(profile));
3049 
3050     profileSettings.select();
3051     profileSettings.removeRows(0, profileSettings.rowCount() - 1);
3052     profileSettings.submitAll();
3053 }
3054 
3055 bool KSUserDB::GetProfileSettings(uint32_t profile, QVariantMap &settings)
3056 {
3057     auto db = QSqlDatabase::database(m_ConnectionName);
3058     if (!db.isValid())
3059     {
3060         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3061         return false;
3062     }
3063 
3064     settings.clear();
3065 
3066     QSqlTableModel profileSettings(nullptr, db);
3067     profileSettings.setTable("profilesettings");
3068     profileSettings.setFilter(QString("profile=%1").arg(profile));
3069     profileSettings.select();
3070 
3071     if (profileSettings.rowCount() > 0)
3072     {
3073         QSqlRecord record = profileSettings.record(0);
3074         auto settingsField = record.value("settings").toByteArray();
3075         QJsonParseError parserError;
3076         auto doc = QJsonDocument::fromJson(settingsField, &parserError);
3077         if (parserError.error == QJsonParseError::NoError)
3078         {
3079             settings = doc.object().toVariantMap();
3080 
3081             return true;
3082         }
3083     }
3084 
3085     return false;
3086 }
3087 
3088 bool KSUserDB::AddOpticalTrainSettings(uint32_t train, const QByteArray &settings)
3089 {
3090     auto db = QSqlDatabase::database(m_ConnectionName);
3091     if (!db.isValid())
3092     {
3093         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3094         return false;
3095     }
3096 
3097     QSqlTableModel OpticalTrainSettings(nullptr, db);
3098     OpticalTrainSettings.setTable("opticaltrainsettings");
3099     OpticalTrainSettings.select();
3100 
3101     QSqlRecord record = OpticalTrainSettings.record();
3102     record.setValue("opticaltrain", train);
3103     record.setValue("settings", settings);
3104     OpticalTrainSettings.insertRecord(-1, record);
3105 
3106     if (!OpticalTrainSettings.submitAll())
3107     {
3108         qCWarning(KSTARS) << OpticalTrainSettings.lastError();
3109         return false;
3110     }
3111 
3112     return true;
3113 }
3114 
3115 bool KSUserDB::UpdateOpticalTrainSettings(uint32_t train, const QByteArray &settings)
3116 {
3117     auto db = QSqlDatabase::database(m_ConnectionName);
3118     if (!db.isValid())
3119     {
3120         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3121         return false;
3122     }
3123 
3124     QSqlTableModel OpticalTrainSettings(nullptr, db);
3125     OpticalTrainSettings.setTable("opticaltrainsettings");
3126     OpticalTrainSettings.setFilter(QString("opticaltrain=%1").arg(train));
3127     OpticalTrainSettings.select();
3128 
3129     QSqlRecord record = OpticalTrainSettings.record(0);
3130     record.setValue("settings", settings);
3131     OpticalTrainSettings.setRecord(0, record);
3132 
3133     if (!OpticalTrainSettings.submitAll())
3134         qCWarning(KSTARS) << OpticalTrainSettings.lastError();
3135 
3136     return true;
3137 }
3138 
3139 bool KSUserDB::DeleteOpticalTrainSettings(uint32_t train)
3140 {
3141     auto db = QSqlDatabase::database(m_ConnectionName);
3142     if (!db.isValid())
3143     {
3144         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3145         return false;
3146     }
3147 
3148     QSqlQuery query(db);
3149     return query.exec(QString("DELETE FROM opticaltrainsettings WHERE opticaltrain=%1").arg(train));
3150 }
3151 
3152 bool KSUserDB::GetOpticalTrainSettings(uint32_t train, QVariantMap &settings)
3153 {
3154     auto db = QSqlDatabase::database(m_ConnectionName);
3155     if (!db.isValid())
3156     {
3157         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3158         return false;
3159     }
3160 
3161     settings.clear();
3162 
3163     QSqlTableModel OpticalTrainSettings(nullptr, db);
3164     OpticalTrainSettings.setTable("OpticalTrainsettings");
3165     OpticalTrainSettings.setFilter(QString("opticaltrain=%1").arg(train));
3166     OpticalTrainSettings.select();
3167 
3168     if (OpticalTrainSettings.rowCount() > 0)
3169     {
3170         QSqlRecord record = OpticalTrainSettings.record(0);
3171         auto settingsField = record.value("settings").toByteArray();
3172         QJsonParseError parserError;
3173         auto doc = QJsonDocument::fromJson(settingsField, &parserError);
3174         if (parserError.error == QJsonParseError::NoError)
3175         {
3176             settings = doc.object().toVariantMap();
3177 
3178             return true;
3179         }
3180     }
3181 
3182     return false;
3183 }
3184 
3185 /* Collimation Overlay Elements Section */
3186 
3187 ////////////////////////////////////////////////////////////////////////////////////////////////////////
3188 ///
3189 ////////////////////////////////////////////////////////////////////////////////////////////////////////
3190 bool KSUserDB::AddCollimationOverlayElement(const QVariantMap &oneElement)
3191 {
3192     auto db = QSqlDatabase::database(m_ConnectionName);
3193     if (!db.isValid())
3194     {
3195         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3196         return false;
3197     }
3198 
3199     QSqlTableModel collimationOverlayElement(nullptr, db);
3200     collimationOverlayElement.setTable("collimationoverlayelements");
3201     collimationOverlayElement.select();
3202 
3203     QSqlRecord record = collimationOverlayElement.record();
3204 
3205     // Remove PK so that it gets auto-incremented later
3206     record.remove(0);
3207 
3208     for (QVariantMap::const_iterator iter = oneElement.begin(); iter != oneElement.end(); ++iter)
3209         record.setValue(iter.key(), iter.value());
3210 
3211     collimationOverlayElement.insertRecord(-1, record);
3212 
3213     if (!collimationOverlayElement.submitAll())
3214     {
3215         qCWarning(KSTARS) << collimationOverlayElement.lastError();
3216         return false;
3217     }
3218 
3219     return true;
3220 }
3221 
3222 ////////////////////////////////////////////////////////////////////////////////////////////////////////
3223 ///
3224 ////////////////////////////////////////////////////////////////////////////////////////////////////////
3225 bool KSUserDB::UpdateCollimationOverlayElement(const QVariantMap &oneElement, int id)
3226 {
3227     auto db = QSqlDatabase::database(m_ConnectionName);
3228     if (!db.isValid())
3229     {
3230         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3231         return false;
3232     }
3233 
3234     QSqlTableModel collimationOverlayElement(nullptr, db);
3235     collimationOverlayElement.setTable("collimationoverlayelements");
3236     collimationOverlayElement.setFilter(QString("id=%1").arg(id));
3237     collimationOverlayElement.select();
3238 
3239     QSqlRecord record = collimationOverlayElement.record(0);
3240 
3241     for (QVariantMap::const_iterator iter = oneElement.begin(); iter != oneElement.end(); ++iter)
3242         record.setValue(iter.key(), iter.value());
3243 
3244     collimationOverlayElement.setRecord(0, record);
3245 
3246     if (!collimationOverlayElement.submitAll())
3247     {
3248         qCWarning(KSTARS) << collimationOverlayElement.lastError();
3249         return false;
3250     }
3251 
3252     return true;
3253 }
3254 
3255 ////////////////////////////////////////////////////////////////////////////////////////////////////////
3256 ///
3257 ////////////////////////////////////////////////////////////////////////////////////////////////////////
3258 bool KSUserDB::DeleteCollimationOverlayElement(int id)
3259 {
3260     auto db = QSqlDatabase::database(m_ConnectionName);
3261     if (!db.isValid())
3262     {
3263         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3264         return false;
3265     }
3266 
3267     QSqlTableModel collimationOverlayElement(nullptr, db);
3268     collimationOverlayElement.setTable("collimationoverlayelements");
3269     collimationOverlayElement.setFilter(QString("id=%1").arg(id));
3270 
3271     collimationOverlayElement.select();
3272 
3273     collimationOverlayElement.removeRows(0, 1);
3274     collimationOverlayElement.submitAll();
3275     return true;
3276 }
3277 
3278 ////////////////////////////////////////////////////////////////////////////////////////////////////////
3279 ///
3280 ////////////////////////////////////////////////////////////////////////////////////////////////////////
3281 bool KSUserDB::GetCollimationOverlayElements(QList<QVariantMap> &collimationOverlayElements)
3282 {
3283     auto db = QSqlDatabase::database(m_ConnectionName);
3284     if (!db.isValid())
3285     {
3286         qCCritical(KSTARS) << "Failed to open database:" << db.lastError();
3287         return false;
3288     }
3289 
3290     collimationOverlayElements.clear();
3291 
3292     QSqlTableModel collimationOverlayElement(nullptr, db);
3293     collimationOverlayElement.setTable("collimationoverlayelements");
3294     collimationOverlayElement.select();
3295 
3296     for (int i = 0; i < collimationOverlayElement.rowCount(); ++i)
3297     {
3298         QVariantMap recordMap;
3299         QSqlRecord record = collimationOverlayElement.record(i);
3300         for (int j = 0; j < record.count(); j++)
3301             recordMap[record.fieldName(j)] = record.value(j);
3302 
3303         collimationOverlayElements.append(recordMap);
3304     }
3305 
3306     return true;
3307 }