kexi

KexiStartup.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "KexiStartup.h"
00021 #ifdef Q_WS_WIN
00022 # include "KexiStartup_p_win.h"
00023 #else
00024 # include "KexiStartup_p.h"
00025 #endif
00026 
00027 #include "kexiproject.h"
00028 #include "kexiprojectdata.h"
00029 #include "kexiprojectset.h"
00030 #include "kexiguimsghandler.h"
00031 
00032 #include <kexidb/driver.h>
00033 #include <kexidb/drivermanager.h>
00034 #include "KexiStartupDialog.h"
00035 #include "KexiConnSelector.h"
00036 #include "KexiProjectSelectorBase.h"
00037 #include "KexiProjectSelector.h"
00038 #include "KexiNewProjectWizard.h"
00039 #include <kexidbconnectionwidget.h>
00040 #include <kexidbshortcutfile.h>
00041 
00042 #include <kdebug.h>
00043 #include <klocale.h>
00044 #include <kmimetype.h>
00045 #include <kmessagebox.h>
00046 #include <kcmdlineargs.h>
00047 #include <kdeversion.h>
00048 #include <kprogress.h>
00049 #include <ktextedit.h>
00050 #include <kstaticdeleter.h>
00051 
00052 #include <unistd.h>
00053 
00054 #if KDE_IS_VERSION(3,1,9)
00055 # include <kuser.h>
00056 #endif
00057 
00058 #include <qcstring.h>
00059 #include <qapplication.h>
00060 #include <qlayout.h>
00061 
00062 namespace Kexi {
00063     static KStaticDeleter<KexiStartupHandler> Kexi_startupHandlerDeleter;
00064     KexiStartupHandler* _startupHandler = 0;
00065 
00066     KexiStartupHandler& startupHandler()
00067     {
00068         if (!_startupHandler)
00069             Kexi_startupHandlerDeleter.setObject( _startupHandler, new KexiStartupHandler() );
00070         return *_startupHandler; 
00071     }
00072 }
00073 
00074 //---------------------------------
00075 
00077 class KexiStartupHandlerPrivate
00078 {
00079     public:
00080         KexiStartupHandlerPrivate()
00081          : passwordDialog(0)//, showConnectionDetailsExecuted(false)
00082             , shortcutFile(0), connShortcutFile(0), connDialog(0), startupDialog(0)
00083         {
00084         }
00085 
00086         ~KexiStartupHandlerPrivate()
00087         {
00088             delete passwordDialog;
00089             delete connDialog;
00090             delete startupDialog;
00091         }
00092 
00093         KexiDBPasswordDialog* passwordDialog;
00094 //      bool showConnectionDetailsExecuted : 1;
00095         KexiDBShortcutFile *shortcutFile;
00096         KexiDBConnShortcutFile *connShortcutFile;
00097         KexiDBConnectionDialog *connDialog;
00098         QString shortcutFileGroupKey;
00099         KexiStartupDialog *startupDialog;
00100 };
00101 
00102 //---------------------------------
00103 
00104 static bool stripQuotes(const QString &item, QString &name)
00105 {
00106     if (item.left(1)=="\"" && item.right(1)=="\"") {
00107         name = item.mid(1, item.length()-2);
00108         return true;
00109     }
00110     name = item;
00111     return false;
00112 }
00113 
00114 void updateProgressBar(KProgressDialog *pd, char *buffer, int buflen)
00115 {
00116     char *p = buffer;
00117     QCString line(80);
00118     for (int i=0; i<buflen; i++, p++) {
00119         if ((i==0 || buffer[i-1]=='\n') && buffer[i]=='%') {
00120             bool ok;
00121             int j=0;
00122 //          char *q=++p;
00123             ++i;
00124             line="";
00125             for (;i<buflen && *p>='0' && *p<='9'; j++, i++, p++)
00126                 line+=QChar(*p);
00127             --i; --p;
00128             int percent = line.toInt(&ok);
00129             if (ok && percent>=0 && percent<=100 && pd->progressBar()->progress()<percent) {
00130 //              kdDebug() << percent << endl;
00131                 pd->progressBar()->setProgress(percent);
00132                 qApp->processEvents(100);
00133             }
00134         }
00135     }
00136 }
00137 
00138 //---------------------------------
00139 
00140 KexiDBPasswordDialog::KexiDBPasswordDialog(QWidget *parent, KexiDB::ConnectionData& cdata, bool showDetailsButton)
00141  : KPasswordDialog( KPasswordDialog::Password, false/*keep*/, 
00142     showDetailsButton ? (int)KDialogBase::User1 : 0, parent )
00143  , m_cdata(&cdata)
00144  , m_showConnectionDetailsRequested(false)
00145 {
00146     QString msg = "<H2>" + i18n("Opening database") + "</H2><p>"
00147      + i18n("Please enter the password.") + "</p>";
00148 /*      msg += cdata.userName.isEmpty() ?
00149             "<p>"+i18n("Please enter the password.")
00150             : "<p>"+i18n("Please enter the password for user.").arg("<b>"+cdata.userName+"</b>");*/
00151 
00152     QString srv = cdata.serverInfoString(false);
00153     if (srv.isEmpty() || srv.lower()=="localhost")
00154         srv = i18n("local database server");
00155 
00156     msg += ("</p><p>"+i18n("Database server: %1").arg(QString("<nobr>")+srv+"</nobr>")+"</p>");
00157         
00158     QString usr;
00159     if (cdata.userName.isEmpty())
00160         usr = i18n("unspecified user", "(unspecified)");
00161     else
00162         usr = cdata.userName;
00163     
00164     msg += ("<p>"+i18n("Username: %1").arg(usr)+"</p>");
00165 
00166     setPrompt( msg );
00167     if (showDetailsButton) {
00168         connect( this, SIGNAL(user1Clicked()), 
00169             this, SLOT(slotShowConnectionDetails()) );
00170         setButtonText(KDialogBase::User1, i18n("&Details")+ " >>");
00171     }
00172     setButtonOK(KGuiItem(i18n("&Open"), "fileopen"));
00173 }
00174 
00175 KexiDBPasswordDialog::~KexiDBPasswordDialog()
00176 {
00177 }
00178 
00179 void KexiDBPasswordDialog::done(int r)
00180 {
00181     if (r == QDialog::Accepted) {
00182         m_cdata->password = QString::fromLatin1(password());
00183     }
00184 //  if (d->showConnectionDetailsExecuted || ret == QDialog::Accepted) {
00185 /*          } else {
00186                 m_action = Exit;
00187                 return true;
00188             }
00189         }*/
00190     KPasswordDialog::done(r);
00191 }
00192 
00193 void KexiDBPasswordDialog::slotShowConnectionDetails()
00194 {
00195     m_showConnectionDetailsRequested = true;
00196     close();
00197 }
00198 
00199 //---------------------------------
00200 KexiStartupHandler::KexiStartupHandler()
00201  : QObject(0,"KexiStartupHandler")
00202  , KexiStartupData()
00203  , d( new KexiStartupHandlerPrivate() )
00204 {
00205 }
00206 
00207 KexiStartupHandler::~KexiStartupHandler()
00208 {
00209     delete d;
00210 }
00211 
00212 bool KexiStartupHandler::getAutoopenObjects(KCmdLineArgs *args, const QCString &action_name)
00213 {
00214     QCStringList list = args->getOptionList(action_name);
00215     QCStringList::ConstIterator it;
00216     bool atLeastOneFound = false;
00217     for ( it = list.constBegin(); it!=list.constEnd(); ++it) {
00218         QString type_name, obj_name, item=*it;
00219         int idx;
00220         bool name_required = true;
00221         if (action_name=="new") {
00222             obj_name = "";
00223             stripQuotes(item, type_name);
00224             name_required = false;
00225         }
00226         else {//open, design, text...
00227             QString defaultType;
00228             if (action_name=="execute")
00229                 defaultType = "macro";
00230             else
00231                 defaultType = "table";
00232 
00233             //option with " " (set default type)
00234             if (stripQuotes(item, obj_name)) {
00235                 type_name = defaultType;
00236             }
00237             else if ((idx = item.find(':'))!=-1) {
00238                 //option with type name specified:
00239                 type_name = item.left(idx).lower();
00240                 obj_name = item.mid(idx+1);
00241                 //optional: remove ""
00242                 if (obj_name.left(1)=="\"" && obj_name.right(1)=="\"")
00243                     obj_name = obj_name.mid(1, obj_name.length()-2);
00244             }
00245             else {
00246                 //just obj. name: set default type name
00247                 obj_name = item;
00248                 type_name = defaultType;
00249             }
00250         }
00251         if (type_name.isEmpty())
00252             continue;
00253         if (name_required && obj_name.isEmpty())
00254             continue;
00255 
00256         KexiProjectData::ObjectInfo info;
00257         info["name"]=obj_name;
00258         info["type"]=type_name;
00259         info["action"]=action_name;
00260         //ok, now add info for this object
00261         atLeastOneFound = true;
00262         if (projectData())
00263             projectData()->autoopenObjects.append( info );
00264         else
00265             return true; //no need to find more because we do not have projectData() anyway
00266     } //for
00267     return atLeastOneFound;
00268 }
00269 
00270 tristate KexiStartupHandler::init(int /*argc*/, char ** /*argv*/)
00271 {
00272     m_action = DoNothing;
00273 //  d->showConnectionDetailsExecuted = false;
00274     KCmdLineArgs *args = KCmdLineArgs::parsedArgs(0);
00275     if (!args)
00276         return true;
00277 
00278     KexiDB::ConnectionData cdata;
00279 
00280     const QString connectionShortcutFileName( args->getOption("connection") );
00281     if (!connectionShortcutFileName.isEmpty()) {
00282         KexiDBConnShortcutFile connectionShortcut( connectionShortcutFileName );
00283         if (!connectionShortcut.loadConnectionData(cdata)) {
00286             KMessageBox::sorry( 0, "<qt>"
00287                 +i18n("Could not read connection information from connection shortcut "
00288                 "file <nobr>\"%1\"</nobr>.<br><br>Check whether the file has valid contents.")
00289                 .arg(QDir::convertSeparators(connectionShortcut.fileName())));
00290             return false;
00291         }
00292     }
00293     
00294     if (!args->getOption("dbdriver").isEmpty())
00295         cdata.driverName = args->getOption("dbdriver");
00296 
00297     QString fileType( args->getOption("type").lower() );
00298     if (args->count()>0 && (!fileType.isEmpty() && fileType!="project" && fileType!="shortcut" && fileType!="connection")) {
00299         KMessageBox::sorry( 0, 
00300             i18n("You have specified invalid argument (\"%1\") for \"type\" command-line option.")
00301             .arg(fileType));
00302         return false;
00303     }
00304 
00305 //  if (cdata.driverName.isEmpty())
00306 //      cdata.driverName = KexiDB::Driver::defaultFileBasedDriverName();
00307 #ifdef KEXI_SERVER_SUPPORT
00308     if (!args->getOption("host").isEmpty())
00309         cdata.hostName = args->getOption("host");
00310     if (!args->getOption("local-socket").isEmpty())
00311         cdata.localSocketFileName = args->getOption("local-socket");
00312     if (!args->getOption("user").isEmpty())
00313         cdata.userName = args->getOption("user");
00314 #endif
00315 //  cdata.password = args->getOption("password");
00316     bool fileDriverSelected;
00317     if (cdata.driverName.isEmpty())
00318         fileDriverSelected = true;
00319     else {
00320         KexiDB::DriverManager dm;
00321         KexiDB::Driver::Info dinfo = dm.driverInfo(cdata.driverName);
00322         if (dinfo.name.isEmpty()) {
00323             //driver name provided explicity, but not found
00324             KMessageBox::sorry(0, dm.errorMsg());
00325             return false;
00326         }
00327         fileDriverSelected = dinfo.fileBased;
00328     }
00329     bool projectFileExists = false;
00330 
00331     //obfuscate the password, if present
00332 //removed
00333 /*
00334     for (int i=1; i<(argc-1); i++) {
00335         if (qstrcmp("--password",argv[i])==0
00336             || qstrcmp("-password",argv[i])==0)
00337         {
00338             QCString pwd(argv[i+1]);
00339             if (!pwd.isEmpty()) {
00340                 pwd.fill(' ');
00341                 pwd[0]='x';
00342                 qstrcpy(argv[i+1], (const char*)pwd);
00343             }
00344             break;
00345         }
00346     }
00347     */
00348     
00349 #ifdef KEXI_SERVER_SUPPORT
00350     const QString portStr = args->getOption("port");
00351     if (!portStr.isEmpty()) {
00352         bool ok;
00353         const int p = portStr.toInt(&ok);
00354         if (ok && p > 0)
00355             cdata.port = p;
00356         else {
00357             KMessageBox::sorry( 0, 
00358                 i18n("You have specified invalid port number \"%1\"."));
00359             return false;
00360         }
00361     }
00362 #endif
00363 
00364 #ifdef KEXI_SHOW_UNIMPLEMENTED
00365     m_forcedFinalMode = args->isSet("final-mode");
00366     m_forcedDesignMode = args->isSet("design-mode");
00367 #else
00368     m_forcedFinalMode = false;
00369     m_forcedDesignMode = false;
00370 #endif
00371     bool createDB = args->isSet("createdb");
00372     const bool alsoOpenDB = args->isSet("create-opendb");
00373     if (alsoOpenDB)
00374         createDB = true;
00375     const bool dropDB = args->isSet("dropdb");
00376     const bool openExisting = !createDB && !dropDB;
00377     const bool readOnly = args->isSet("readonly");
00378     const QString couldnotMsg = QString::fromLatin1("\n")
00379         +i18n("Could not start Kexi application this way.");
00380 
00381     if (createDB && dropDB) {
00382         KMessageBox::sorry( 0, i18n(
00383             "You have used both \"createdb\" and \"dropdb\" startup options.")+couldnotMsg);
00384         return false;
00385     };
00386 
00387     if (createDB || dropDB) {
00388         if (args->count()<1) {
00389             KMessageBox::sorry( 0, i18n("No project name specified.") );
00390             return false;
00391         }
00392         m_action = Exit;
00393     }
00394 
00395 //TODO: add option for non-gui; integrate with KWallet; 
00396 //      move to static KexiProject method
00397     if (!fileDriverSelected && !cdata.driverName.isEmpty() && cdata.password.isEmpty()) {
00398 
00399         if (cdata.password.isEmpty()) {
00400             delete d->passwordDialog;
00401             d->passwordDialog = new KexiDBPasswordDialog(0, cdata, true);
00402 //          connect( d->passwordDialog, SIGNAL(user1Clicked()), 
00403 //              this, SLOT(slotShowConnectionDetails()) );
00404             const int ret = d->passwordDialog->exec();
00405             if (d->passwordDialog->showConnectionDetailsRequested() || ret == QDialog::Accepted) {
00406 //              if ( ret == QDialog::Accepted ) {
00407         //      if (QDialog::Accepted == KPasswordDialog::getPassword(pwd, msg)) {
00408 //moved             cdata.password = QString(pwd);
00409 //              }
00410             } else {
00411                 m_action = Exit;
00412                 return true;
00413             }
00414         }
00415     }
00416 
00417 /*  kdDebug() << "ARGC==" << args->count() << endl;
00418     for (int i=0;i<args->count();i++) {
00419         kdDebug() << "ARG" <<i<< "= " << args->arg(i) <<endl;
00420     }*/
00421 
00422     if (m_forcedFinalMode && m_forcedDesignMode) {
00423         KMessageBox::sorry( 0, i18n(
00424         "You have used both \"final-mode\" and \"design-mode\" startup options.")+couldnotMsg);
00425         return false;
00426     }
00427 
00428     //database filenames, shortcut filenames or db names on a server
00429     if (args->count()>=1) {
00430         QString prjName;
00431         QString fileName;
00432         if (fileDriverSelected) {
00433             fileName = QFile::decodeName(args->arg(0));
00434         }
00435         else {
00436             prjName = QString::fromLocal8Bit(args->arg(0));
00437         }
00438         
00439         if (fileDriverSelected) {
00440             QFileInfo finfo(fileName);
00441             prjName = finfo.fileName(); //filename only, to avoid messy names like when Kexi is started with "../../db" arg
00442             cdata.setFileName( finfo.absFilePath() );
00443             projectFileExists = finfo.exists();
00444 
00445             if (dropDB && !projectFileExists) {
00446                 KMessageBox::sorry(0, 
00447                     i18n("Could not remove project.\nThe file \"%1\" does not exist.")
00448                     .arg(QDir::convertSeparators(cdata.dbFileName())));
00449                 return 0;
00450             }
00451         }
00452 
00453         if (createDB) {
00454             if (cdata.driverName.isEmpty())
00455                 cdata.driverName = KexiDB::Driver::defaultFileBasedDriverName();
00456             m_projectData = new KexiProjectData(cdata, prjName); //dummy
00457         }
00458         else {
00459             if (fileDriverSelected) {
00460                 int detectOptions = 0;
00461                 if (fileType=="project")
00462                     detectOptions |= ThisIsAProjectFile;
00463                 else if (fileType=="shortcut")
00464                     detectOptions |= ThisIsAShortcutToAProjectFile;
00465                 else if (fileType=="connection")
00466                     detectOptions |= ThisIsAShortcutToAConnectionData;
00467 
00468                 if (dropDB)
00469                     detectOptions |= DontConvert;
00470 
00471                 QString detectedDriverName;
00472                 const tristate res = detectActionForFile( m_importActionData, detectedDriverName, 
00473                     cdata.driverName, cdata.fileName(), 0, detectOptions );
00474                 if (true != res)
00475                     return res;
00476 
00477                 if (m_importActionData) { //importing action
00478                     m_action = ImportProject;
00479                     return true;
00480                 }
00481 
00482                 //opening action
00483                 cdata.driverName = detectedDriverName;
00484                 if (cdata.driverName=="shortcut") {
00485                     //get information for a shortcut file
00486                     d->shortcutFile = new KexiDBShortcutFile(cdata.fileName());
00487                     m_projectData = new KexiProjectData();
00488                     if (!d->shortcutFile->loadProjectData(*m_projectData, &d->shortcutFileGroupKey)) {
00489                         KMessageBox::sorry(0, i18n("Could not open shortcut file\n\"%1\".")
00490                             .arg(QDir::convertSeparators(cdata.fileName())));
00491                         delete m_projectData;
00492                         m_projectData = 0;
00493                         delete d->shortcutFile;
00494                         d->shortcutFile = 0;
00495                         return false;
00496                     }
00497                     d->connDialog = new KexiDBConnectionDialog(
00498                         *m_projectData, d->shortcutFile->fileName());
00499                     connect(d->connDialog, SIGNAL(saveChanges()), 
00500                         this, SLOT(slotSaveShortcutFileChanges()));
00501                     int res = d->connDialog->exec();
00502                     if (res == QDialog::Accepted) {
00503                         //get (possibly changed) prj data
00504                         *m_projectData = d->connDialog->currentProjectData();
00505                     }
00506 
00507                     delete d->connDialog;
00508                     d->connDialog = 0;
00509                     delete d->shortcutFile;
00510                     d->shortcutFile = 0;
00511 
00512                     if (res == QDialog::Rejected) {
00513                         delete m_projectData;
00514                         m_projectData = 0;
00515                         return cancelled;
00516                     }
00517                 }
00518                 else if (cdata.driverName=="connection") {
00519                     //get information for a connection file
00520                     d->connShortcutFile = new KexiDBConnShortcutFile(cdata.fileName());
00521                     if (!d->connShortcutFile->loadConnectionData(cdata, &d->shortcutFileGroupKey)) {
00522                         KMessageBox::sorry(0, i18n("Could not open connection data file\n\"%1\".")
00523                             .arg(QDir::convertSeparators(cdata.fileName())));
00524                         delete d->connShortcutFile;
00525                         d->connShortcutFile = 0;
00526                         return false;
00527                     }
00528                     bool cancel = false;
00529                     const bool showConnectionDialog = !args->isSet("skip-conn-dialog");
00530                     while (true) {
00531                         if (showConnectionDialog) {
00532                             //show connection dialog, so user can change parameters
00533                             if (!d->connDialog) {
00534                                 d->connDialog = new KexiDBConnectionDialog(
00535                                     cdata, d->connShortcutFile->fileName());
00536                                 connect(d->connDialog, SIGNAL(saveChanges()), 
00537                                     this, SLOT(slotSaveShortcutFileChanges()));
00538                             }
00539                             const int res = d->connDialog->exec();
00540                             if (res == QDialog::Accepted) {
00541                                 //get (possibly changed) prj data
00542                                 cdata = *d->connDialog->currentProjectData().constConnectionData();
00543                             }
00544                             else {
00545                                 cancel = true;
00546                                 break;
00547                             }
00548                         }
00549                         m_projectData = selectProject(&cdata, cancel);
00550                         if (m_projectData || cancel || !showConnectionDialog)
00551                             break;
00552                     }
00553 
00554                     delete d->connShortcutFile;
00555                     d->connShortcutFile = 0;
00556                     delete d->connDialog;
00557                     d->connDialog = 0;
00558 
00559                     if (cancel)
00560                         return cancelled;
00561                 }
00562                 else
00563                     m_projectData = new KexiProjectData(cdata, prjName);
00564             }
00565             else
00566                 m_projectData = new KexiProjectData(cdata, prjName);
00567 
00568         }
00569 //      if (!m_projectData)
00570 //          return false;
00571     }
00572     if (args->count()>1) {
00573         //TODO: KRun another Kexi instances
00574     }
00575 
00576     //let's show connection details, user asked for that in the "password dialog"
00577     if (d->passwordDialog && d->passwordDialog->showConnectionDetailsRequested()) {
00578         d->connDialog = new KexiDBConnectionDialog(*m_projectData);
00579 //      connect(d->connDialog->tabWidget->mainWidget, SIGNAL(saveChanges()), 
00580 //          this, SLOT(slotSaveShortcutFileChanges()));
00581         int res = d->connDialog->exec();
00582 
00583         if (res == QDialog::Accepted) {
00584             //get (possibly changed) prj data
00585             *m_projectData = d->connDialog->currentProjectData();
00586         }
00587 
00588         delete d->connDialog;
00589         d->connDialog = 0;
00590 
00591         if (res == QDialog::Rejected) {
00592             delete m_projectData;
00593             m_projectData = 0;
00594             return cancelled;
00595         }
00596     }
00597 
00598     //---autoopen objects:
00599     const bool atLeastOneAOOFound = getAutoopenObjects(args, "open")
00600         || getAutoopenObjects(args, "design")
00601         || getAutoopenObjects(args, "edittext")
00602         || getAutoopenObjects(args, "execute")
00603         || getAutoopenObjects(args, "new")
00604         || getAutoopenObjects(args, "print")
00605         || getAutoopenObjects(args, "print-preview");
00606 
00607     if (atLeastOneAOOFound && !openExisting) {
00608         KMessageBox::information( 0, 
00609             i18n("You have specified a few database objects to be opened automatically, "
00610                 "using startup options.\n"
00611                 "These options will be ignored because it is not available while creating "
00612                 "or dropping projects."));
00613     }
00614 
00615     if (createDB) {
00616         bool creationNancelled;
00617         KexiGUIMessageHandler gui;
00618         KexiProject *prj = KexiProject::createBlankProject(creationNancelled, projectData(), &gui);
00619         bool ok = prj!=0;
00620         delete prj;
00621         if (creationNancelled)
00622             return cancelled;
00623         if (!alsoOpenDB) {
00624             if (ok) {
00625                 KMessageBox::information( 0, i18n("Project \"%1\" created successfully.")
00626                     .arg( QDir::convertSeparators(projectData()->databaseName()) ));
00627             }
00628             return ok;
00629         }
00630     }
00631     else if (dropDB) {
00632         KexiGUIMessageHandler gui;
00633         tristate res = KexiProject::dropProject(projectData(), &gui, false/*ask*/);
00634         if (res==true)
00635             KMessageBox::information( 0, i18n("Project \"%1\" dropped successfully.")
00636                 .arg( QDir::convertSeparators(projectData()->databaseName()) ));
00637         return res!=false;
00638     }
00639 
00640     //------
00641 
00642 /*  if (m_forcedFinalMode || (m_projectData && projectData->finalMode())) {
00643         //TODO: maybe also auto allow to open objects...
00644         KexiMainWindowImpl::initFinal(m_projectData);
00645         return;
00646     }*/
00647 
00648     if (!m_projectData) {
00649         cdata = KexiDB::ConnectionData(); //clear
00650 
00651         if (args->isSet("skip-startup-dialog") || !KexiStartupDialog::shouldBeShown())
00652             return true;
00653 
00654         if (!d->startupDialog) {
00655             //create d->startupDialog for reuse because it can be used again after conn err.
00656             d->startupDialog = new KexiStartupDialog(
00657                 KexiStartupDialog::Everything, KexiStartupDialog::CheckBoxDoNotShowAgain,
00658                 Kexi::connset(), Kexi::recentProjects(), 0, "KexiStartupDialog");
00659         }
00660         if (d->startupDialog->exec()!=QDialog::Accepted)
00661             return true;
00662 
00663         int r = d->startupDialog->result();
00664         if (r==KexiStartupDialog::TemplateResult) {
00665 //          kdDebug() << "Template key == " << d->startupDialog->selectedTemplateKey() << endl;
00666             QString selectedTemplateKey( d->startupDialog->selectedTemplateKey() );
00667             if (selectedTemplateKey=="blank") {
00668                 m_action = CreateBlankProject;
00669                 return true;
00670             }
00671             else if (selectedTemplateKey=="import") {
00672                 m_action = ImportProject;
00673                 return true;
00674             }
00675             
00677             return true;
00678         }
00679         else if (r==KexiStartupDialog::OpenExistingResult) {
00680 //          kdDebug() << "Existing project --------" << endl;
00681             QString selFile = d->startupDialog->selectedExistingFile();
00682             if (!selFile.isEmpty()) {
00683                 //file-based project
00684 //              kdDebug() << "Project File: " << selFile << endl;
00685                 cdata.setFileName( selFile );
00686                 QString detectedDriverName;
00687                 const tristate res = detectActionForFile( m_importActionData, detectedDriverName, 
00688                     cdata.driverName, selFile );
00689                 if (true != res)
00690                     return res;
00691                 if (m_importActionData) { //importing action
00692                     m_action = ImportProject;
00693                     return true;
00694                 }
00695 
00696                 if (detectedDriverName.isEmpty())
00697                     return false;
00698                 cdata.driverName = detectedDriverName;
00699                 m_projectData = new KexiProjectData(cdata, selFile);
00700             }
00701             else if (d->startupDialog->selectedExistingConnection()) {
00702 //              kdDebug() << "Existing connection: " <<
00703 //                  d->startupDialog->selectedExistingConnection()->serverInfoString() << endl;
00704                 KexiDB::ConnectionData *cdata = d->startupDialog->selectedExistingConnection();
00705                 //ok, now we will try to show projects for this connection to the user
00706                 bool cancelled;
00707                 m_projectData = selectProject( cdata, cancelled );
00708                 if (!m_projectData && !cancelled || cancelled) {
00709                         //try again
00710                         return init(0, 0);
00711                 }
00712                 //not needed anymore
00713                 delete d->startupDialog;
00714                 d->startupDialog = 0;
00715             }
00716         }
00717         else if (r==KexiStartupDialog::OpenRecentResult) {
00718 //          kdDebug() << "Recent project --------" << endl;
00719             const KexiProjectData *data = d->startupDialog->selectedProjectData();
00720             if (data) {
00721 //              kdDebug() << "Selected project: database=" << data->databaseName()
00722 //                  << " connection=" << data->constConnectionData()->serverInfoString() << endl;
00723             }
00725             return data!=0;
00726         }
00727 
00728         if (!m_projectData)
00729             return true;
00730     }
00731     
00732     if (m_projectData && (openExisting || (createDB && alsoOpenDB))) {
00733         m_projectData->setReadOnly( readOnly );
00734         m_action = OpenProject;
00735     }
00736     //show if wasn't show yet
00737 //  importantInfo(true);
00738     
00739     return true;
00740 }
00741 
00742 tristate KexiStartupHandler::detectActionForFile( 
00743     KexiStartupData::Import& detectedImportAction, QString& detectedDriverName,
00744     const QString& _suggestedDriverName, const QString &dbFileName, QWidget *parent, int options )
00745 {
00746     detectedImportAction = KexiStartupData::Import(); //clear
00747     QString suggestedDriverName(_suggestedDriverName); //safe
00748     detectedDriverName = QString::null;
00749     QFileInfo finfo(dbFileName);
00750     if (dbFileName.isEmpty() || !finfo.isReadable()) {
00751         if (!(options & SkipMessages))
00752             KMessageBox::sorry(parent, i18n("<p>Could not open project.</p>")
00753                 +i18n("<p>The file <nobr>\"%1\"</nobr> does not exist or is not readable.</p>")
00754                 .arg(QDir::convertSeparators(dbFileName))
00755                 +i18n("Check the file's permissions and whether it is already opened "
00756                 "and locked by another application."));
00757         return false;
00758     }
00759 
00760     KMimeType::Ptr ptr;
00761     QString mimename;
00762 
00763     const bool thisIsShortcut = (options & ThisIsAShortcutToAProjectFile) 
00764         || (options & ThisIsAShortcutToAConnectionData);
00765 
00766     if ((options & ThisIsAProjectFile) || !thisIsShortcut) {
00767         //try this detection if "project file" mode is forced or no type is forced:
00768         ptr = KMimeType::findByFileContent(dbFileName);
00769         mimename = ptr.data()->name();
00770         kdDebug() << "KexiStartupHandler::detectActionForFile(): found mime is: " 
00771             << mimename << endl;
00772         if (mimename.isEmpty() || mimename=="application/octet-stream" || mimename=="text/plain") {
00773             //try by URL:
00774             ptr = KMimeType::findByURL(dbFileName);
00775             mimename = ptr.data()->name();
00776         }
00777     }
00778     if (mimename.isEmpty() || mimename=="application/octet-stream") {
00779         // perhaps the file is locked
00780         QFile f(dbFileName);
00781         if (!f.open(IO_ReadOnly)) {
00782             // BTW: similar error msg is provided in SQLiteConnection::drv_useDatabase()
00783             if (!(options & SkipMessages))
00784                 KMessageBox::sorry(parent, i18n("<p>Could not open project.</p>")
00785                     +i18n("<p>The file <nobr>\"%1\"</nobr> is not readable.</p>")
00786                     .arg(QDir::convertSeparators(dbFileName))
00787                     +i18n("Check the file's permissions and whether it is already opened "
00788                         "and locked by another application."));
00789             return false;
00790         }
00791     }
00792     if ((options & ThisIsAShortcutToAProjectFile) || mimename=="application/x-kexiproject-shortcut") {
00793         detectedDriverName = "shortcut";
00794         return true;
00795     }
00796 
00797     if ((options & ThisIsAShortcutToAConnectionData) || mimename=="application/x-kexi-connectiondata") {
00798         detectedDriverName = "connection";
00799         return true;
00800     }
00801 
00804     if (ptr.data()) {
00805         if (mimename=="application/x-msaccess") {
00806             if ((options & SkipMessages) || KMessageBox::Yes != KMessageBox::questionYesNo(
00807                 parent, i18n("\"%1\" is an external file of type:\n\"%2\".\n"
00808                 "Do you want to import the file as a Kexi project?")
00809                 .arg(QDir::convertSeparators(dbFileName)).arg(ptr.data()->comment()),
00810                 i18n("Open External File"), KGuiItem(i18n("Import...")), KStdGuiItem::cancel() ) )
00811             {
00812                 return cancelled;
00813             }
00814             detectedImportAction.mimeType = mimename;
00815             detectedImportAction.fileName = dbFileName;
00816             return true;
00817         }
00818     }
00819 
00820     if (!finfo.isWritable()) {
00822     }
00823 
00824     // "application/x-kexiproject-sqlite", etc.:
00825     QString tmpDriverName = Kexi::driverManager().lookupByMime(mimename).latin1();
00826 //@todo What about trying to reuse KOFFICE FILTER CHAINS here?
00827     bool useDetectedDriver = suggestedDriverName.isEmpty() || suggestedDriverName.lower()==detectedDriverName.lower();
00828     if (!useDetectedDriver) {
00829         int res = KMessageBox::Yes;
00830         if (!(options & SkipMessages))
00831             res = KMessageBox::warningYesNoCancel(parent, i18n(
00832              "The project file \"%1\" is recognized as compatible with \"%2\" database driver, "
00833              "while you have asked for \"%3\" database driver to be used.\n"
00834              "Do you want to use \"%4\" database driver?")
00835              .arg(QDir::convertSeparators(dbFileName))
00836              .arg(tmpDriverName).arg(suggestedDriverName).arg(tmpDriverName));
00837         if (KMessageBox::Yes == res)
00838             useDetectedDriver = true;
00839         else if (KMessageBox::Cancel == res)
00840             return cancelled;
00841     }
00842     if (useDetectedDriver) {
00843         detectedDriverName = tmpDriverName;
00844     }
00845     else {//use suggested driver
00846         detectedDriverName = suggestedDriverName;
00847     }
00848 //  kdDebug() << "KexiStartupHandler::detectActionForFile(): driver name: " << detectedDriverName << endl;
00849 //hardcoded for convenience:
00850     const QString newFileFormat = "SQLite3";
00851     if (!(options & DontConvert || options & SkipMessages) 
00852         && detectedDriverName.lower()=="sqlite2" && detectedDriverName.lower()!=suggestedDriverName.lower()
00853         && KMessageBox::Yes == KMessageBox::questionYesNo(parent, i18n(
00854             "Previous version of database file format (\"%1\") is detected in the \"%2\" "
00855             "project file.\nDo you want to convert the project to a new \"%3\" format (recommended)?")
00856             .arg(detectedDriverName).arg(QDir::convertSeparators(dbFileName)).arg(newFileFormat)) )
00857     {
00858 //      SQLite2ToSQLite3Migration *migr = new 
00859         SQLite2ToSQLite3Migration migr( finfo.absFilePath() );
00860         tristate res = migr.run();
00861 //      kdDebug() << "--- migr.run() END ---" <<endl;
00862         if (!res) {
00863             //TODO msg
00864             KMessageBox::sorry(parent, i18n(
00865                 "Failed to convert project file \"%1\" to a new \"%2\" format.\n"
00866                 "The file format remains unchanged.")
00867                 .arg(QDir::convertSeparators(dbFileName)).arg(newFileFormat) );
00868             //continue...
00869         }
00870         if (res==true)
00871             detectedDriverName = newFileFormat;
00872     }
00873 //  action.driverName = detectedDriverName;
00874     if (detectedDriverName.isEmpty()) {
00875         QString possibleProblemsInfoMsg( Kexi::driverManager().possibleProblemsInfoMsg() );
00876         if (!possibleProblemsInfoMsg.isEmpty()) {
00877             possibleProblemsInfoMsg.prepend(QString::fromLatin1("<p>")+i18n("Possible problems:"));
00878             possibleProblemsInfoMsg += QString::fromLatin1("</p>");
00879         }
00880         if (!(options & SkipMessages))
00881             KMessageBox::detailedSorry(parent, 
00882                 i18n( "The file \"%1\" is not recognized as being supported by Kexi.")
00883                     .arg(QDir::convertSeparators(dbFileName)),
00884                 QString::fromLatin1("<p>")
00885                 +i18n("Database driver for this file type not found.\nDetected MIME type: %1")
00886                     .arg(mimename)
00887                 +(ptr.data()->comment().isEmpty() 
00888                     ? QString::fromLatin1(".") : QString::fromLatin1(" (%1).").arg(ptr.data()->comment()))
00889                 +QString::fromLatin1("</p>")
00890                 +possibleProblemsInfoMsg);
00891         return false;
00892     }
00893     return true;
00894 }
00895 
00896 KexiProjectData*
00897 KexiStartupHandler::selectProject(KexiDB::ConnectionData *cdata, bool& cancelled, QWidget *parent)
00898 {
00899     clearStatus();
00900     cancelled = false;
00901     if (!cdata)
00902         return 0;
00903     if (!cdata->savePassword && cdata->password.isEmpty()) {
00904         if (!d->passwordDialog)
00905             d->passwordDialog = new KexiDBPasswordDialog(0, *cdata, false);
00906         const int ret = d->passwordDialog->exec();
00907         if (d->passwordDialog->showConnectionDetailsRequested() || ret == QDialog::Accepted) {
00908 
00909         } else {
00910             cancelled = true;
00911             return 0;
00912         }
00913     }
00914     KexiProjectData* projectData = 0;
00915     //dialog for selecting a project
00916     KexiProjectSelectorDialog prjdlg( parent, "prjdlg", cdata, true, false );
00917     if (!prjdlg.projectSet() || prjdlg.projectSet()->error()) {
00918         KexiGUIMessageHandler msgh;
00919         if (prjdlg.projectSet())
00920             msgh.showErrorMessage(prjdlg.projectSet(), 
00921                 i18n("Could not load list of available projects for <b>%1</b> database server.")
00922                 .arg(cdata->serverInfoString(true)));
00923         else
00924             msgh.showErrorMessage(
00925                 i18n("Could not load list of available projects for <b>%1</b> database server.")
00926                 .arg(cdata->serverInfoString(true)));
00927 //      setStatus(i18n("Could not load list of available projects for database server \"%1\"")
00928 //      .arg(cdata->serverInfoString(true)), prjdlg.projectSet()->errorMsg());
00929         return 0;
00930     }
00931     if (prjdlg.exec()!=QDialog::Accepted) {
00932         cancelled = true;
00933         return 0;
00934     }
00935     if (prjdlg.selectedProjectData()) {
00936         //deep copy
00937         projectData = new KexiProjectData(*prjdlg.selectedProjectData());
00938     }
00939     return projectData;
00940 }
00941 
00942 void KexiStartupHandler::slotSaveShortcutFileChanges()
00943 {
00944     bool ok = true;
00945     if (d->shortcutFile)
00946         ok = d->shortcutFile->saveProjectData(d->connDialog->currentProjectData(), 
00947             d->connDialog->savePasswordOptionSelected(), 
00948             &d->shortcutFileGroupKey );
00949     else if (d->connShortcutFile)
00950         ok = d->connShortcutFile->saveConnectionData(
00951             *d->connDialog->currentProjectData().connectionData(), 
00952             d->connDialog->savePasswordOptionSelected(), 
00953             &d->shortcutFileGroupKey );
00954 
00955     if (!ok) {
00956         KMessageBox::sorry(0, i18n("Failed saving connection data to\n\"%1\" file.")
00957             .arg(QDir::convertSeparators(d->shortcutFile->fileName())));
00958     }
00959 }
00960 
00961 /*void KexiStartupHandler::slotShowConnectionDetails()
00962 {
00963     d->passwordDialog->close();
00964     d->showConnectionDetailsExecuted = true;
00965 }*/
00966 
00967 #include "KexiStartup.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys